# Info sul corso & setup ## Slides 0- [Introduzione](https://docs.google.com/presentation/d/1md_fAuSIPrLf_gItNSB9pvqehKNrR9hojglHqSXzZKQ/edit?usp=sharing) 1 - [Installazione](https://docs.google.com/presentation/d/1pYVaASF576BkbHNEK-DCQqTv6iXZsMBtl642P1GCK2U/edit?usp=sharing) 2 - [Shell](https://docs.google.com/presentation/d/1O200ptoHUnmJl7mYjikpeFQHWu7EzMQcSIXVHVhhtKc/edit?usp=sharing) 3 - [CRUD](https://docs.google.com/presentation/d/1NT1aT9nEjHShMGJR4TomtXvl9XtQl7-6_4taFzSEMxc/edit?usp=sharing) 6 - [NO-ACID](https://docs.google.com/presentation/d/12FSX_942Wsu-XklzpK_cST4Y2wFSAlmxYQ56B9v7al0/edit?usp=sharing) ## Organizzazione si inizia alle 9:00 e si finisce alle 17:00.s pausa 15 min alle 11:00. pausa 15 min alle 16:00. Pausa pranzo 13-14. Altri due giorni di corso settimana prossima giovedi e venerdi. Argomento di giovedi: Cluster replica + sharding + riferimenti per locking Argomento di venerdi: LDAP auth + tsl + riferimenti OpManager https://docs.opsmanager.mongodb.com/current/ ## Ambiente di lab (35.180.47.33) Noi lavoreremo su container https://hub.docker.com/_/mongo al quale e' stato aggiunto un servizio sshd per potercisi connettere. Quindi e' gia' tutto installato. Per installare a mano si puo' guardare https://docs.mongodb.com/manual/installation/ in particolare per Red-Hat https://docs.mongodb.com/manual/tutorial/install-mongodb-on-red-hat/ Su ambienti Arch si puo' scaricare dal AUR con ```shell yay -Syu mongo-bin mongo-tools-bin ``` I sorgenti del ambiente di laboratorio si possono trovare su https://gist.github.com/zommiommy/e2ef69f2464ead75e51576712e377281 ## Sito ambienti di lab L'ambiente e' hostato su una macchina EC2 di AWS: http://ec2-52-47-116-219.eu-west-3.compute.amazonaws.com:5000/ la password e' mongo2020, creare un container su una porta libera Poi collegatevi con ssh, putty o mobaXterm per mettere la tastiera in italiana eseguite ```shell setxkbmap it ``` ## Lanciamo il server Lanciamo il demone (come porta usate quella successiva a quella del ssh) ```shell mongod --port 8889 ``` Questo rimane in ascolto quindi ci si collega su una seconda instanza e proseguiamo: Per lanciarlo in detached e che non stampi a schermo: ```shell mongod --port 8889 > /dev/null & ``` Scarichiamo i dati: ```shell wget https://atlas-data-lake.s3.amazonaws.com/json/sample_airbnb/listingsAndReviews.json ``` carichiamo i dati ```shell mongoimport ./listingsAndReviews.json -c=airbnb --port 8889 ``` ## Lanciamo il client ```shell mongo --port 8889 ``` Ora dovrebbe esserci un nuovo db: ```javascript show dbs admin 0.000GB config 0.000GB local 0.000GB test 0.051GB ``` selezioniamo il nuovo db: ```javascript use test switched to db test ``` possiamo stampare le collections ```javascript show collections airbnb ``` Possiamo fare la prima query: ```javascript db.airbnb.findOne({}) ...DATI... ``` Questa e' equivalente a: ```SQL SELECT * FROM airbnb LIMIT 1 ``` # Prima parte, queries base Per dati di utility possiamo usare ```javascript db.serverStatus() db.stats() ``` Inseriamo un documento: ```javascript db.airbnb.insertOne({name:"pippo", test:1}) { "acknowledged" : true, "insertedId" : ObjectId("5ebd187bfda7af5b364f46b2") } ``` Cerchiamolo ```javascript db.airbnb.findOne({name:"pippo"}) { "_id" : ObjectId("5ebd187bfda7af5b364f46b2"), "name" : "pippo", "test" : 1 } ``` ```sql SELECT * FROM airbnb WHERE name = "pippo" LIMIT 1 ``` Per vedere esempi di traduzione da SQL a mongo possiamo usare https://docs.mongodb.com/manual/reference/sql-comparison/ Possiamo cercare piu' valori: ```javascript db.airbnb.find({beds:1}).limit(5).pretty() ``` ```sql SELECT * FROM airbnb WHERE beds = 1 LIMIT 5 ``` e possiamo inserire piu' valori ```javascript db.airbnb.insertMany([{"name":"gino"}, {"name":"giovanni", "test":"ciao"}]) ``` Possiamo filtrare le colonne che ci interessano ```javascript > db.airbnb.find({beds:1}, {beds:1, bedrooms:1}) { "_id" : "10021707", "bedrooms" : 1, "beds" : 1 } { "_id" : "10009999", "bedrooms" : 1, "beds" : 2 } { "_id" : "10006546", "bedrooms" : 3, "beds" : 5 } { "_id" : "10030955", "bedrooms" : 1, "beds" : 1 } { "_id" : "10047964", "bedrooms" : 2, "beds" : 6 } { "_id" : "10051164", "bedrooms" : 1, "beds" : 8 } { "_id" : "10057447", "bedrooms" : 1, "beds" : 2 } { "_id" : "10057826", "bedrooms" : 0, "beds" : 2 } { "_id" : "1003530", "bedrooms" : 1, "beds" : 1 } ``` ```sql SELECT beds, bedrooms FROM airbnb WHERE beds = 1 ``` per non metter l'id si fa; ```javascript > db.airbnb.find({}, {beds:1, bedrooms:1, _id:0}) { "bedrooms" : 1, "beds" : 1 } { "bedrooms" : 1, "beds" : 2 } { "bedrooms" : 3, "beds" : 5 } { "bedrooms" : 1, "beds" : 1 } { "bedrooms" : 2, "beds" : 6 } { "bedrooms" : 1, "beds" : 8 } { "bedrooms" : 1, "beds" : 2 } { "bedrooms" : 0, "beds" : 2 } { "bedrooms" : 1, "beds" : 1 } ``` ```sql SELECT beds FROM airbnb ``` possiamo aggiungere commenti ```javascript db.airbnb.find({beds:1, $comment:"Cerco tutte le stanze con un solo letto"}).limit(1).pretty() ``` Per contare quanti risultati abbiamo possiamo fare: ```javascript db.airbnb.find({beds:1}).count() ``` Oppure possiamo cercare che i valori siano in una lista di valori (equivalentemnte per $nin) ``` db.airbnb.findOne({beds:{$in:[1, 3]}}) ``` ```sql SELECT * FROM airbnb WHERE beds IN (1, 3) ``` per cancellare i valori ```javascript db.airbnb.remove({name:"pippo"}) WriteResult({ "nRemoved" : 1 }) ``` La creazione di collections e db e' implicita ```javascript > use db_di_test switched to db db_di_test > db.airbnb2.insertOne({name:"pippo"}) { "acknowledged" : true, "insertedId" : ObjectId("5ebd1fe5fd24da789c9a3f04") } > show collections airbnb2 > show dbs admin 0.000GB config 0.000GB db_di_test 0.000GB local 0.000GB test 0.052GB ``` Aggiungiamo il campo "age" con valore 22 ad un documento con beds = 1 ```javascript db.airbnb.updateOne({beds:1}, {$set:{age:22}}) ``` Possiamo rinominare i campi: ```javascript db.airbnb.updateMany({beds:1}, {$rename:{beds:"letti"}}) { "acknowledged" : true, "matchedCount" : 2699, "modifiedCount" : 2698 } ``` Possiamo cancellare campi a tutti i documenti ```javascript > db.airbnb.updateMany({}, {$unset:{age:null}}) { "acknowledged" : true, "matchedCount" : 5558, "modifiedCount" : 2700 } > db.airbnb.findOne({beds:1}, {beds:1, age:1}) { "_id" : "10030955", "beds" : 1 } ``` Null e' un placeholder che viene "ignorato" se usato "nella select" ```javascript > db.airbnb.find({CIAO:null}).count() 5558 > db.airbnb.find().count() 5558 ``` Possiamo settare un valore di default: ```javascript db.airbnb.updateMany({name:{$exists:false}}, {$set:{name:"NOME-NON-PRESENTE"}}) ``` Possiamo accedere ai valori di sotto oggetti (servono le viroglette nella chiave) ```javascript > db.airbnb.findOne({"address.country_code":"US"}) ``` Possiamo sottrarre 2 a tutti i valori dei beds ```javascript > db.airbnb.updateMany({}, {$inc: {beds: -2 }}) { "acknowledged" : true, "matchedCount" : 5558, "modifiedCount" : 5558 } ``` Troviamo tutti i documenti con beds negativi ```javascript > db.airbnb.find({beds:{$lt:0}}).count() ``` in generale abbiamo gli operatori: 1. $lt < 2. $lte <= 3. $gt > 4. $gte >= 5. $eq == 6. $ne != Chiarificazione sui null ```javascript /// nella insert i null si comportano normalmente > db.airbnb.insertOne({"name":"rita", test:null}) { "acknowledged" : true, "insertedId" : ObjectId("5ebd432a5e8da77ec546fedc") } /// infatti vengono assegnati > db.airbnb.findOne({"name":"rita"}) { "_id" : ObjectId("5ebd432a5e8da77ec546fedc"), "name" : "rita", "test" : null } /// qui invece i null viene ignorato > db.airbnb.find({test:null}).count() 5557 /// cosi cerchiamo i documenti dove test e' null > db.airbnb.find({test:{$type:10}}).count() 1 ``` ## Esercizi prima parte: 1. Una query che ritorna tutti i risultati 2. Una query che ritorna name, summary e property_type 3. Come sopra ma senza _id 4. Una query che ritorna tutti i documenti con property_type = "Apartment" 5. Come sopra ma solo 5 documenti 6. Conta quanti documenti hanno bed_type = "Real Bed" 7. Una query che conta quanti documenti ci sono 2 o 5 bedrooms 8. Fare una query che setta age = 18 come default se il campo age non e' presente 9. Contare quanti documenti hanno un number_of_reviews inferiore a 5 10. Trovare i documenti che non hanno il campo availability 11. Trovare 5 documenti che hanno come suburb "Brooklyn" dentro al address 12. Trovare tutti i documenti che hanno nel address, dentro a location, is_location_exact uguale a true 13. Troviare tutti i documenti dove minimum_nights sia "14" e maximum_nights sia "1125" 14. Trovare tutti i documenti dove i campi minimum_nights e maxnimum_nights sono di tipo stringa, i valori numerici dei tipi si possono trovare [a questo url](http://bsonspec.org/spec.html) o [qua (Piu' leggibile)](https://docs.mongodb.com/manual/reference/operator/query/type/) 15. Come sopra ma di tipo Double 16. Come sopra ma di tipo 64-bit integer 17. Cancellare il campo notes da tutti i documenti con numero di accommodates maggiore di 3 18. Trovare tutti i documenti con cancellation_policy uguale a "flexible", un numero di beds superiore a 3, property_type uguale ad "Apartment" e country_code sia "US" 19. Cosa fa `db.airbnb.find({notes:{$type:8}})` ? ## Soluzioni 1. ```javascript db.airbnb.find() ``` o equivalentemente ```javascript db.airbnb.find({}) ``` 2. ```javascript db.airbnb.find({}, {name:1, summary:1, property_type:1}) ``` 3. ```javascript db.airbnb.find({}, {_id:0, name:1, summary:1, property_type:1}) ``` 4. ```javascript db.airbnb.find({property_type:"Apartment"}) ``` 5. ```javascript db.airbnb.find({property_type:"Apartment"}).limit(5) ``` 6. ```javascript db.airbnb.find({bed_type:"Real Bed"}).count() ``` 7. ```javascript db.airbnb.find({bedrooms:{$in:[2, 5]}}).count() ``` 8. ```javascript db.airbnb.updateMany({age:{$exists:false}}, {$set:{age:18}}) ``` 9. ```javascript db.airbnb.find({number_of_reviews:{$lt:5}}) ``` 10. ```javascript db.airbnb.find({availability:{$exists:false}}) ``` 11. ```javascript db.airbnb.find({"address.suburb":"Brooklyn"}) ``` 12. ```javascript db.airbnb.find({"address.location.is_location_exact":true}) ``` 13. ```javascript db.airbnb.find({minimum_nights:"14", maximum_nights:"1125"}) ``` 14. ```javascript db.airbnb.find({minimum_nights:{$type:2}, maximum_nights:{$type:2}}) ``` 15. ```javascript db.airbnb.find({minimum_nights:{$type:1}, maximum_nights:{$type:1}}) ``` 16. ```javascript db.airbnb.find({minimum_nights:{$type:18}, maximum_nights:{$type:18}}) ``` o equivalentemente ```javascript db.airbnb.find({minimum_nights:{$type:0x12}, maximum_nights:{$type:0x12}}) ``` 17. ```javascript db.airbnb.updateMany({accomodates:{$gt:3}}, {$unset:{notes:null}}) ``` 18. ```javascript db.airbnb.find({ cancellation_policy:"flexible", beds:{$gt:3}, property_type:"Apartment", "address.country_code":"US" }) ``` 19. equivale a cercare se il valore e' boolean # Parte due, modifichiamo i dati Possiamo moltiplicare il valore dei documenti che hanno beds = 1, (modifica il campo originale) ```javascript db.airbnb.update({beds:1}, {$mul:{beds:2}}) ``` Se lo faccio su campi non esistenti: ```javascript > db.airbnb.update({beds:1}, {$mul:{qty:2, qty2:3}}) > db.airbnb.findOne({qty:{$exists:true}}) "qty":0, "qty2":0 ``` Quindi se non esiste usa il valore 0 di default Un altro esempio piu' semplice: ```javascript > db.airbnb.findOne({_id:1}) { "_id" : 1, "highScore" : 900, "lowScore" : 200 } > db.airbnb.update({_id:1}, {$mul:{lowScore:2}}) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) > db.airbnb.findOne({_id:1}) { "_id" : 1, "highScore" : 900, "lowScore" : 400 } ``` Operazione di max (equivalentemente per min) ```javascript > db.airbnb.insertOne({_id:1, highScore: 800, lowScore:200}) { "acknowledged" : true, "insertedId" : 1 } > db.airbnb.findOne({_id:1}) { "_id" : 1, "highScore" : 800, "lowScore" : 200 } > db.airbnb.update({_id:1}, {$max:{highScore:900}}) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })) > db.airbnb.findOne({_id:1}) { "_id" : 1, "highScore" : 900, "lowScore" : 200 } ``` altro esempio ```javascript > db.airbnb.findOne({_id:1}) { "_id" : 1, "highScore" : 900, "lowScore" : 400 } > db.airbnb.update({_id:1}, {$max:{lowScore:500}}) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) > db.airbnb.findOne({_id:1}) { "_id" : 1, "highScore" : 900, "lowScore" : 500 } ``` Possiamo **sostituire** un documento: ```javascript > db.airbnb.findOne({_id:1}) { "_id" : 1, "highScore" : 900, "lowScore" : 200 } > db.airbnb.replaceOne({_id:1}, {_id:1, highScore:800, lowScore:200}) { "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 } > db.airbnb.findOne({_id:1}) { "_id" : 1, "highScore" : 800, "lowScore" : 200 } ``` questo cancella tutto il vecchio documento ```javascript > db.airbnb.findOne({_id:1}) { "_id" : 1, "highScore" : 800, "lowScore" : 200 } > db.airbnb.update({_id:1}, {$set:{test:10}}) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) > db.airbnb.findOne({_id:1}) { "_id" : 1, "highScore" : 800, "lowScore" : 200, "test" : 10 } > db.airbnb.replaceOne({_id:1}, {_id:1, highScore:800, lowScore:200}) { "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 } > db.airbnb.findOne({_id:1}) { "_id" : 1, "highScore" : 800, "lowScore" : 200 } ``` Se settiamo upsert come settings il nuovo documento viene inserito anche se nessun documento matcha la query. ```javascript > db.airbnb.replaceOne({beds:"ciao"}, {name:"my_test", test:1}, {upsert:true}) { "acknowledged" : true, "matchedCount" : 0, "modifiedCount" : 0, "upsertedId" : ObjectId("5ebd4ec5c8b492c39c3336e4") } ``` Multiple condizioni su un campo: ```javascript > db.airbnb.find({beds:{$gt:2, $ne:3}}, {beds:1}).limit(1).pretty() { "_id" : "10047964", "beds" : 4 } ``` combiniamo condizioni con l'$and (equivalentemente con $or, $nand, $nor, $xor) ```javascript > db.airbnb.find({$and:[{beds:{$gt:2}}, {beds:{$ne:3}}]}, {beds:1}).limit(1).pretty() { "_id" : "10047964", "beds" : 4 } > db.airbnb.find({$and:[{beds:{$gt:2}}, {bedrooms:{$lt:2, $ne:0}}]}, {beds:1, bedrooms:1}).limit(1).pretty() { "_id" : "10051164", "bedrooms" : 1, "beds" : 6 } ``` O invertire una condizione: ```javascript > db.airbnb.find({$and:[{beds:{$not:{$gt:2}}}, {bedrooms:{$lt:2, $ne:0}}]}, {beds:1, bedrooms:1}).limit(1).pretty() { "_id" : "10021707", "bedrooms" : 1, "beds" : -4 } ``` Possiamo usare $where per usare funzioni di javascript come condizioni arbitrarie: ```javascript var test = function(){ return this.beds % 2 == 0; } db.airbnb.find({$where:test}).count() ``` Pero' e' molto lento perche' la funzione deve essere serializzata, mandata al server e poi poiche' e' una funzione "black-box" il motore non sa cosa fa quindi non vengono utilizzati eventuali indici. Questo comporta che le performance sono **molto** piu' lente rispetto ad una query nativa. Ad esempio, se i dati stanno in RAM, possiamo scaricare i dati e filtrarli localmente usando il javascript: ```javascript var x = db.airbnb.find({}); var total = 0; for(var i = 0; i < x.length(); i++){ if(x[i].beds % 2 == 0) total++; } print(total); ``` Oppure visto che $mod esiste possiamo fare: ```javascript db.airbnb.find({beds:{$mod:[2, 0]}}).count() 1976 ``` Ordiniamo i risultati: ```javascript > db.airbnb.find({test:{$exists:true}}).sort({name:1}) { "_id" : "ciao", "name" : "carlo", "test" : 1, "beds" : -2 } { "_id" : ObjectId("5ebd1a5104d726496b75f7e4"), "name" : "giovanni", "test" : "ciao", "beds" : -2 } { "_id" : ObjectId("5ebd4ec5c8b492c39c3336e4"), "name" : "my_test", "test" : 1 } { "_id" : ObjectId("5ebd432a5e8da77ec546fedc"), "name" : "rita", "test" : null } > db.airbnb.find({test:{$exists:true}}).sort({name:-1}) { "_id" : ObjectId("5ebd432a5e8da77ec546fedc"), "name" : "rita", "test" : null } { "_id" : ObjectId("5ebd4ec5c8b492c39c3336e4"), "name" : "my_test", "test" : 1 } { "_id" : ObjectId("5ebd1a5104d726496b75f7e4"), "name" : "giovanni", "test" : "ciao", "beds" : -2 } { "_id" : "ciao", "name" : "carlo", "test" : 1, "beds" : -2 } ``` 1 per crescente, -1 per decrescente, questo pero' se non c'e' un indice su quel campo puo' richiedere quantita' di RAM proibitive. ## Esercizi Parte Due 1. Trovare tutti i documenti che hanno (piu' di 2 beds ma non 3) oppure (hanno in address, in location, is_location_exact uguale a true e hanno property_type = "Apartment") 2. Contare quanti documenti hanno property_type = "Apartment" o numero di reviews maggiore di 5, ma non entrambe. 3. Moltiplicare per 3 i beds dei documenti che hanno property_type = "Apartment" e per 5 gli altri. 4. Sottrarre 2 al number_of_reviews se presente, se non e' presente, inserire 0 come valore di default. ## Soluzioni 1. ```javascript db.airbnb.find({$or:[{beds:{$gt:2, $ne:3}, {"address.location.is_location_exact":true, property_type:"Apartment"}}]}) ``` 2. ```javascript > db.airbnb.find({$and:[{$nor:[{$and:[{property_type:"Apartment"}, {number_of_reviews:{$gt:5}}]}]}, {$or:[{property_type:"Apartment"}, {number_of_reviews:{$gt:5}}]}]}).count() ``` oppure ```javascript var condition = function(){ return this.property_type === "Apartment" ^ this.number_of_reviews > 5; } db.airbnb.find($where:condition) ``` 3. ```javascript db.airbnb.updateMany({property_type:"Apartment"}, {$mul:{beds:3}}}) db.airbnb.updateMany({property_type:{$ne:"Apartment"}, {$mul:{beds:5}}}) ``` 4. ```javascript db.airbnb.updateMany({number_of_reviews:{$exists:true}}, {$inc:{number_of_reviews:-2}}}) db.airbnb.updateMany({number_of_reviews:{$exists:false}, {$set:{number_of_reviews:0}}}) ``` # Configurazione [Link Documentazione](https://docs.mongodb.com/manual/reference/configuration-options/) [Security Checklist](https://docs.mongodb.com/manual/administration/security-checklist/) [Strumeto di editing grafico](https://studio3t.com/features/) Di default il file di config e' in `/etc/mongod.conf`, `/etc/mongod.conf.orig` Altrimenti possiamo crearci un file dove vogliamo e facciam partire il server con `mongod --config /path/to/file` o shorthand `mongod -f /path/to/file` Un file di conf di solito e' fatto circa cosi ```yaml systemLog: destination: file path: "/var/log/mongodb/mongod.log" logAppend: true verbosity: 0 # default quiet: false traceAllExceptions: true storage: journal: enabled: true # processManagement: fork: true security: authorization: "disabled" javascriptEnabled: true # in produzione potrebbe aver senso disabilitarlo net: bindIp: 127.0.0.1 # Per ascoltare su tutto mettere 0.0.0.0, :: oppure * port: 27017 ipv6: true bindIpAll: true maxIncomingConnections: 65536 # default setParameter: enableLocalhostAuthBypass: false ``` ## Ruoli generici su tutti i database - `readAnyDatabase` - `writeAnyDatabase` - `userAdminAnyDatabase` e `clusterAdmin` e' l'unione di `clusterManager`, `clusterMonitor`, `hostManager` + il comando `dropDatabase` ci sono i ruoli specifici per fare backup e restore (separati) piu' il super user role: - `root` ## Creiamo un utente root Creiamo un utente di root (root ed altri ruoli che agiscono su tutti i db sono definiti solo dentro ad admin): ```javascript use admin db.createUser({user:"root", pwd:"toor", roles:["root"]}) ``` Spegniamo il server con ```javascript db.adminCommand({shutdown:1, force:true}) ``` oppure ```shell mongod shutdown ``` nel file di confs abilitiamo l'authenticaizone: ```yaml security: authorization: "enabled" ``` Rilanciamo il server: ```shell mongod -f /path/to/file ``` Ci colleghiamo authenticandoci ```shell mongo --port 8889 --authenticationDatabase "admin" -u "root" -p ``` o alternativamente possiamo fare: ```javascript use admin db.auth("root", "toor") ``` Modifichiamo root in modo che possa essere usato solo da localhost ```javascript db.updateUser("root", {authenticationRestrictions:[{clientSource:["127.0.0.1"]}]}) ``` Cambiamo password a root: ```javascript db.changeUserPassword("root", "password") ``` Prendiamo la lista degli utenti: ```javascript use admin db.system.users.find() ``` oppure info sul utente specifico e non mostrare dati semsibili ```javascript db.getUser("root", {showCredentials:false}) ``` Creiamo un secondo user per il backup: ```javascript > db.createUser({user:"backup", pwd:"password", roles:[{role:"read", db:"test"}]}) Successfully added user: { "user" : "backup", "roles" : [ { "role" : "read", "db" : "test" } ] } ``` Possiamo rimuoverer il ruolo dal user ```javascript > db.revokeRolesFromUser("backup", [{role:"read", db:"test"}]) ``` E infatti se prendiamo le info, il ruolo e' stato rimosso. ```javascript > db.getUser("backup") { "_id" : "admin.backup", "userId" : UUID("44a3baa1-28cc-46d6-8c02-bd833a245161"), "user" : "backup", "db" : "admin", "roles" : [ ], "mechanisms" : [ "SCRAM-SHA-1", "SCRAM-SHA-256" ] } ``` ## Creiamo un ruolo ```javascript db.createRole({role:"ruolo_su_collection", privileges:[{resource:{db:"test", collection:"airbnb"}, actions:["find"]}], roles:[]}) { "role" : "ruolo_su_collection", "privileges" : [ { "resource" : { "db" : "test", "collection" : "airbnb" }, "actions" : [ "find" ] } ], "roles" : [ ] } ``` ## Indici Creiamo un utente che possa creare indici sul db di test: ```javascript db.createUser({user:"test", pwd:"test", roles:[{role:"dbAdmin", db:"test"}]}) ``` Aggiungiamo i ruoli per vedere e scrivere i dati ```javascript db.grantRolesToUser("test", [{role:"readWrite", db:"test"}]) ``` Controlliamo di avere i permessi: ```javascript db.getUser("test") ``` Creiamo e controlliamo l'indice (crescente 1, decrescente -1) per beds ```javascript > db.airbnb.createIndex({beds:1}) { "createdCollectionAutomatically" : false, "numIndexesBefore" : 1, "numIndexesAfter" : 2, "ok" : 1 } > db.airbnb.getInd db.airbnb.getIndexKeys( db.airbnb.getIndexSpecs( db.airbnb.getIndexes( db.airbnb.getIndices( > db.airbnb.getIndices() [ { "v" : 2, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "test.airbnb" }, { "v" : 2, "key" : { "beds" : 1 }, "name" : "beds_1", "ns" : "test.airbnb" } ] ``` Quindi ora possiamo fare le operazioni di sorting ```javascript db.airbnb.find({}, {beds:1}).sort({beds:-1}).limit(1) { "_id" : "20701559", "beds" : 23 } ``` Creo uno user per compass: ```javascript db.createUser({user:"compass", pwd:"compass", roles:["root"]}) ``` creiamo un indice testuale: ```javascript > db.airbnb.createIndex({description:"text"}) { "createdCollectionAutomatically" : false, "numIndexesBefore" : 2, "numIndexesAfter" : 3, "ok" : 1 } ``` Creiamo una collection a cui aggiungiamo un validator ```javascript > db.createCollection("test_coll", {validator:{beds:1}, validationLevel:"strict"}) { "ok" : 1 } ``` Il validator prende una query e fa la stessa operazione della find e se il nuovo documento matcha allora e' valido. Inseriamo un documento valido ```javascript > db.test_coll.insertOne({beds:1}) { "acknowledged" : true, "insertedId" : ObjectId("5ebe8e2f5e3702b8782f3438") } ``` Inseriamo un documento non valido ```javascript > db.test_coll.insertOne({beds:2}) 2020-05-15T12:42:25.443+0000 E QUERY [js] WriteError({ "index" : 0, "code" : 121, "errmsg" : "Document failed validation", "op" : { "_id" : ObjectId("5ebe8e315e3702b8782f3439"), "beds" : 2 } }) : WriteError({ "index" : 0, "code" : 121, "errmsg" : "Document failed validation", "op" : { "_id" : ObjectId("5ebe8e315e3702b8782f3439"), "beds" : 2 } }) ``` Abbiamo due livelli di `validationLevel`, `strict` che applica la validazione a tutto e `moderate` che applica solo alle insert ed agli update di documenti gia' validi. Possiamo cambiare il livello di una colleciton con: ```javascript db.runCommand({collMod:"test_coll", {validationLevel:"moderate"}}) ``` Se vogliamo che venga sollevato solamente un warning invece che un errore modifichiamo `validationAction`. ```javascript db.runCommand({collMod:"test_coll", {validationAction:"warn"}}) ``` con `$jsonSchema` possiamo fare query sulla struttura dei documenti. Ad esempio: ```javascript var validator = { $jsonSchema: { required: [ "name", "major", "gpa", "address" ], properties: { name: { bsonType: "string", description: "must be a string and is required" }, address: { bsonType: "object", required: [ "zipcode" ], properties: { "street": { bsonType: "string" }, "zipcode": { bsonType: "string" } } } } } } ``` Regex siti [Regex101](https://regex101.com/) [debuggerx](https://www.debuggex.com/) Potremmo ad esempio validare il campo email cosi: ```javascript {email:{$regex:\w+@\w+\.\w{1,6}}} ``` cosi da imporre che il campo email oltre ad esistere sia anche una email ben formata. ## Schemas [Esempi](https://docs.mongodb.com/manual/tutorial/model-embedded-one-to-one-relationships-between-documents/) ## Statistiche del server ```shell mongostat --discover --port 8889 mongotop --port 8889 ``` Per visualizzare le operazioni in corso, ed il loro stato, si puo' usare: ```shell db.currentOp() ``` ## Profiling [exit codes table](https://docs.mongodb.com/manual/reference/exit-codes/) cerca profiling sul server basandosi sul timestamp: ```javascript db.system.profile.find({ts:{$gt: new ISODate("2020-05-15T16:00:00Z")}}) ``` # Backup Per beckuppare e' buona norma fare fsync [RIFERIMENTO](https://docs.mongodb.com/manual/reference/command/fsync/) che forza un flush e blocca tutte le modifiche. ```javascript db.fsyncLock() ``` poi per sbloccare fare ```javascript db.fsyncUnlock() ``` Tutti i dati sono di default in `/data/db` ma e' configurabile con `mongod --dbpath /data/mydb` o nei settings con ```yaml storage: dbPath: /data/testdb ``` ## Usando MongoDB Tools mongodump / mongorestore sono binary mongoexport / mongoimport sono human readable Creiamo il backup nella cartella specificata ```shell mongodump --collection=myCollection --db=test --out=/data/backup/ ``` Se non passiamo collection e db beckuppa tutto. ```shell mongorestore /data/backup/dump-2013-10-25/ ``` O la versione human readable ```shell mongoexport --db=test --collection=airbnb --out=backup.json ``` ```shell mongoimport --out=backup.json ``` Ma non sono troppo performani quindi il metodo consigliato e' il seguente ## Filesystem snapshots con LVM [Riferimenti](https://docs.mongodb.com/manual/tutorial/backup-with-filesystem-snapshots/) Creiamo un ramdisk e cloniamo la /data/db Creiamo lo snapshot ```s lvcreate --size 100M --snapshot --name mdb-snap01 /dev/vg0/mongodb ``` Salviamo e comprimiamo ```s umount /dev/vg0/mdb-snap01 dd if=/dev/vg0/mdb-snap01 | gzip > mdb-snap01.gz ``` Restoring ```s lvcreate --size 1G --name mdb-new vg0 gzip -d -c mdb-snap01.gz | dd of=/dev/vg0/mdb-new mount /dev/vg0/mdb-new /srv/mongodb ``` ## Rotate Logging ```js db.adminCommand( { logRotate : 1 } ) ``` # Replica sets Replica Set = Cluster ad alta affidabilita' almeno 3 max 50 nodi di cui al massimo 7 votanti. Nel caso di 3 nodi ci possono essere: o un principale e 2 secondari o un principale un secondario ed un arbitro arbitro server per le votazioni ma non ha dati. le votazioni vanno a maggioranza e quindi i votanti devono essere dispari, nel caso di server pari (di numero inferiore a 7) si aggiunge un arbitro. L'arbitro puo' essere solo uno e vota solo in caso di partia', altrimenti non fa nulla. L'arbitro non salva dati. Di per se l'arbitro non e' necessario. Perche' anche con un cluster da 2 o 4 possiamo rendere uno dei nodi non votanti e quindi avere un numero di nodi votanti dispari, e quindi possibilita' di fare elezioni. Pero' un nodo non votante non puo' essere eletto Principale, quindi in realta' cosi andiamo a perdere il possibile utilizzo di quel server, quindi aggiungendo l'arbitro allora anche questo nodo puo' diventare principale. Nel caso di cluster con piu' di 8 server il problema non si pone perche' tanto il massimo di votanti e' 7. Write solo al principale Read a tutti seconda readpreference, se si abilita setSlaveOk I server mantengono un heartbeat, un ping a cui va risposto. Se il principale crasha viene eletto uno nuovo, normalmente il timeout e' di circa 12 secondi. Puo' essere cambiato a `electionTimeoutMillis` ma sopratutto su cluster distribuiti geograficamente la latenza puo' limitare questo valore. Un sottocaso dei nodi a proprity 0 e' un nodo nascosto. Si puo' nascondere un nodo, in modo da tenerlo solo di backup ma che nessuno va a leggere o scrivere. Questi servono principalmente come puro backup poiche' sono totalmente "inutili" ma mantengono i dati aggiornati. Un sottocaso e' il delayed replica set In pratica mantiene i dati con ritardo fissato, questi hanno comunque l'operationlog aggiornato, ma eseguono le operazioni dopo un delay Questo aiuta nel caso di errori umani dove uno cancella un DB. ## Creiamo un piccolo replica set di 3 nodi Per tutti e 3 i nodi copiamo le conf di default scegliendo porte diverse perche' siamo sulla stessa macchina (1336, 1331, 1332) ``` cp /etc/mongod.conf.orig /etc/mongod.conf ``` E poi modifichiamo questi settings: ```yaml # Dove salva i dati del db storage: dbPath: /data # porta diversa perche' siamo sulla stessa macchina net: port: 1338 bindIp: 0.0.0.0 # Nome del replica set di cui il server fara' parte replication: replSetName: "rs0" # per avere il pad quando lo lanciamo (non necerssaria su server veri # dove mongod gira come servizio) processManagement: fork:true ``` Facciamo partire le instanze con ```shell mongod --config /etc/mongod.conf ``` Colleghiamo ci col client ad uno qualunque dei nodi: ``` mongo --port 1331 ``` Creiamo una variable con le configurazioni del replica sets: ```javascript rsconf = { _id: "rs0", members: [ { _id: 0, host: "127.0.0.1:1331" }, { _id: 1, host: "127.0.0.1:1332" }, { _id: 2, host: "127.0.0.1:1336" } ] } ``` Ora passiamo queste configurazioni al comando per inizzializzare il replica set ```javascript rs.initiate(rsconf) ``` e guardiamo le confs con ```javascript rs.conf() ``` Come test uccidiamo uno dei nodi e guardiamo lo stato del replica set ```javascript rs.status() ``` e cosi possiamo vedere quale membro e' in stato not reachable / healty. successivamente possiamo escluderlo dal replica set con: ```javascript rs.remove("127.0.0.1:1331") ``` Possiamo far diventare secondario un nodo primario ```javascript rs.stepDown() ``` dopo aver riavviato il nodo possiamo riaggiungerlo con: ```javascript rs.add("127.0.0.1:1331") ``` per abilitare la possibilita' di leggere da nodi secondari usiamo ```javascript db.getMongo().setSlaveOk() ``` ## Test read only Se nel cluster a 3 nodi ne killiamo due vediamo che il nodo rimanente diventa secondario e questo andra' in sola lettura perche' non non puo' piu' raggiungere la maggioranza e quindi non puo' eleggere il primario. ```javascript cfg = rs.conf() // rimuoviamo l'ultimo nodo cfg.members.pop() // alternativamente per rimuovere il nodo al indice index: // cfg.members.splice(index, 1) rs.reconfig(cfg, {force:true}) ``` Quindi per ritornare a poter scrivere rimuoviamo i due nodi morti (per usare la remove bisogna specificare force a true): ```javascript cfg = rs.conf() // rimuoviamo l'ultimo nodo cfg.members.splice(0, 1) cfg.members.splice(0, 1) rs.reconfig(cfg, {force:true}) ``` Ora abbiamo un replica set con un solo nodo, e siamo il primario. Quidni ora si puo' continuare a scrivere. Successivamente quando fixiamo gli altri nodi, possiamo riaggiungerli con ```javascript rs.add("127.0.0.1:1331") ``` Per connetterci al replica set "come client" usiamo: ```shell mongo "mongodb://127.0.0.1:1331,127.0.0.1:1332,127.0.0.1:1336/?replicaSet=rs0" ``` ## Oplog possiamo vederne la dimensione e l'intervallo di tempo recuperabile: ```javascript rs.printReplicationInfo( ) ``` Questa dimensione puo' essere cambiata con: (dimensione in Mb) ```javascript db.adminCommand({replSetResizeOplog:1, size: 163384}) ``` Alternativamente possiamo settare `flowControlTargetLagSeconds` per rallentare le write sul primario nel caso i secondari siano limitati dalla quantita' di banda. Nel caso di datacenter puo' aver senso far sincronizzare un solo secondario e gli altri nodi farli sicronizzare da questo con: ```javascript rs.syncFrom("127.0.0.1:1331") ``` Per trovare il primario possiamo fare ```javascript rs.status().members.filter(node => node.stateStr == "PRIMARY") ``` ## Aggiungiamo un altro nodo ed un arbitro Arbitro (8891), nuovo nodo (8889) con le stesse configuraizoni di prima. Il nodo normale lo aggiungiamo sempre con: ```javascript rs.add("127.0.0.1:8889") ``` Poi per aggiungere l'arbitro usiamo: ```javascript rs.addArb("127.0.0.1:8891") ```` ## Mettiamo majoirty come write concern di default ```javascript conf = rs.conf() conf.settings.getLastErrorDefaults.w = "majority" rs.reconfig(conf) ``` ovviamente possiamo utilizzare write concern diverso specificiandolo esplicitamente: ```javascript db.airbnb.insertOne({test:true, name:"test"}, {w:1}) ``` ## Una alternativa alla majority Possiamo fare una query che prima controlla quanti nodi sani ci sono e poi usa quel numero (escluso l'arbitro) come write concern, cosi che le write scrivano sempre su tutti i server vivi. ```javascript db.testcol.insertOne({test:"test"}, {w:rs.status().members.filter(member => member.health == 1).length - 1}) ``` Oppure possiamo fare in modo che ogni membro non attivo sia kickato dal replica set. ```javascript while true { rs.status().members.filter(member => member.health == 0).forEach(node => rs.remove(node.name)); sleep(1000); } ``` ## User per replica set ci colleghiamo al primario e creiamo lo user: ```shell db.createUser({user:"root", pwd:"toor", roles:["root"]}) ``` creiamo una chiave condivisa per tutti i server con: ```shell openssl rand -base64 741 > mongodb-keyfile chmod 400 mongodb-keyfile ``` cioe': ```shell cat mongodb-keyfile 3I+e635TySL8Pghn5F7XSWc6kPTJ4hwqogLhHOv2PbV83VqwvyJ0Gd2pSdF8aFKGqpYvLrenuylvdAXEM/quKD0ECdghpivWGm16CoQi7ePDD3MVE+2JgB5FvtthxoJRsQuxgb4Ci3nxBHcN9IYIomu+zrohNWKrPBGIIkXXueOL2bvgn2mkLrxqjWB37mIs49/kUbWYdh1mIZzJjsm8hjtaWbCnLqNL4a3rmZqwEP+gUSMm7KRf9v2SMd88eKYob108zXvcm3AmJsy27+J+T5HQLxJyihk+BdSVcqDazkCWfYwsHAP2vDz9/sB/S3V4TyQft9jJ2QwDcK6NrqoUIJX4Z5bRqFdVS+wcB+BCmNz6Lf7ESB3pvQDjR3pQOR29qJDnYQDffkbizKRcvJvEwi9mkT06gSPdi+dNgb6k2ct3+Bl1mRiDKHHBSwjNbTJBcVaw65Abrerqd6d32nlyugAMRrQ+UqMTG5cF6KeOf4XeIKWapL57x9CrTzvkmAx8g3cmnV3Hf7L/F2wNyE34CvQLyMOQ7aEzy3GzdRO3QhBbk+S5BAm+aTPke0ggVinUymrh3agIFLuYAE3P67qngZhWyipDFJhw4kl/boWvN9diM/b+KuxGJrmWTguXud6qxK8dmoBIKcBuLHRmsicGZHMj6exgW87+ZtcQX6ePb2wQT9+jzwVCEi0USwCfqtzVmxjqq3X2wdnEBZ+Xd0FW8sGpm79GyB7bo+L0MWaGhV1hNivrCRNfacx98DEIFUecww2BirdwD3wu7zM+HI0atK3AQkF79+6ntyWJy+L7Br8ZfnlRzwIYMLZ04xm1ByBToMP2/o7LVGT/B8PHqTizcLfsKFG858KDlY/kSmnqyVVxE17+tQAlCDZI7X4YsvMBGW+plTkZmaE5AgHIDR93PmBMUD0O8o/Oz9d2Fl2fm+0TuYqno0x0pjXpnV35DAgSJ20NxV3jT7/21+E3cvLSZjR+oiwM ``` poi abilitiamo l'authenticazione nei file di confs di tutti i server (compreso l'arbitro): ```yaml security: authorization: "enabled" keyFile: /root/mongodb-keyfile ``` Riavviamo uno ad uno i server. Poi connettiamoci authenticandoci con: ```shell mongo "mongodb://127.0.0.1:1331,127.0.0.1:1332,127.0.0.1:1336,127.0.0.1:8889,127.0.0.1:8891/?replicaSet=rs0" --authenticationDatabase "admin" -u "root" -p ``` Nel caso dia problemi possiamo rimuovere tutti i nodi dal replica set e poi riaggiungerli uno ad uno. # Sharding Il piano e': (ssh, port) - config_server_1 (8000, 8001) - config_server_2 (8002, 8003) - config_server_3 (8004, 8005) - shard_1_1 (8006, 8007) - shard_1_2 (8008, 8009) - shard_1_3 (8010, 8011) - shard_2_1 (8012, 8013) - shard_2_2 (8014, 8015) - shard_2_3 (8016, 8017) - router_1 (8018, 8019) - router_2 (8020, 8021) ### settings config_server: ```yaml # Dove salva i dati del db storage: dbPath: /data systemLog: destination: file logAppend: true path: /var/log/mongodb/mongod.log # porta da cambiare perche' siamo sulla stessa macchina net: port: 1338 bindIp: 0.0.0.0 # Nome del replica set di cui il server fara' parte replication: replSetName: "rscs0" sharding: clusterRole: "configsvr" # per avere il pad quando lo lanciamo (non necerssaria su server veri # dove mongod gira come servizio) processManagement: fork: true ``` ### settings shard1: ```yaml # Dove salva i dati del db storage: dbPath: /data systemLog: destination: file logAppend: true path: /var/log/mongodb/mongod.log # porta da cambiare perche' siamo sulla stessa macchina net: port: 1338 bindIp: 0.0.0.0 # Nome del replica set di cui il server fara' parte replication: replSetName: "rss1" sharding: clusterRole: "shardsvr" # per avere il pad quando lo lanciamo (non necerssaria su server veri # dove mongod gira come servizio) processManagement: fork: true ``` ### settings shard2: ```yaml # Dove salva i dati del db storage: dbPath: /data systemLog: destination: file logAppend: true path: /var/log/mongodb/mongod.log # porta da cambiare perche' siamo sulla stessa macchina net: port: 1338 bindIp: 0.0.0.0 # Nome del replica set di cui il server fara' parte replication: replSetName: "rss2" sharding: clusterRole: "shardsvr" # per avere il pad quando lo lanciamo (non necerssaria su server veri # dove mongod gira come servizio) processManagement: fork: true ``` ### settings router: ```yaml systemLog: destination: file logAppend: true path: /var/log/mongodb/mongod.log # porta da cambiare perche' siamo sulla stessa macchina net: port: 1338 bindIp: 0.0.0.0 sharding: configDB: "rscs0/127.0.0.1:8001,127.0.0.1:8003,127.0.0.1:8005" # per avere il pad quando lo lanciamo (non necerssaria su server veri # dove mongod gira come servizio) processManagement: fork: true ``` ### Inizzializziamo il config server facciamo partire i server con: ```shell mongod -f conf.yaml ``` poi collegnadoci ad uno qualsiasi dei server di questo replica set: ```shell mongo --port 8001 ``` creiamo le settings: ```javascript confs = { _id: "rscs0", members: [ {_id: 0, host: "127.0.0.1:8001"}, {_id: 1, host: "127.0.0.1:8003"}, {_id: 2, host: "127.0.0.1:8005"}, ] } ``` e facciam partire il replica set: ```javascript rs.initiate(confs) ``` ### Inizzializziamo i replica set 1 facciamo partire i server con: ```shell mongod -f conf.yaml ``` poi collegnadoci ad uno qualsiasi dei server di questo replica set: ```shell mongo --port 8007 ``` creiamo le settings: ```javascript confs = { _id: "rss1", members: [ {_id: 0, host: "127.0.0.1:8007"}, {_id: 1, host: "127.0.0.1:8009"}, {_id: 2, host: "127.0.0.1:8011"}, ] } ``` e facciam partire il replica set: ```javascript rs.initiate(confs) ``` ### Inizzializziamo i replica set 2 facciamo partire i server con: ```shell mongod -f conf.yaml ``` poi collegnadoci ad uno qualsiasi dei server di questo replica set: ```shell mongo --port 8013 ``` creiamo le settings: ```javascript confs = { _id: "rss2", members: [ {_id: 0, host: "127.0.0.1:8013"}, {_id: 1, host: "127.0.0.1:8015"}, {_id: 2, host: "127.0.0.1:8017"}, ] } ``` e facciam partire il replica set: ```javascript rs.initiate(confs) ``` ## Configurazione facciamo partire il router ```shell mongos -f confs.yaml ``` colleghiamoci al router: ```shell mongo --port 8019 ``` aggiungiamo gli shard: ```javascript sh.addShard("rss1/127.0.0.1:8007,127.0.0.1:8009,127.0.0.1:8011") sh.addShard("rss2/127.0.0.1:8013,127.0.0.1:8015,127.0.0.1:8017") ``` ## Importiamo i dati ora importiamo i dati sul nostro sharded cluster scarichiamo i dati: ```shell wget https://atlas-data-lake.s3.amazonaws.com/json/sample_airbnb/listingsAndReviews.json ``` ed importiamo i dati: ```shell mongoimport ./listingsAndReviews.json -d=airbnb -c=airbnb --port 8021 ``` ## Setuppiamo la collection come sharded Ricolleghiamoci al router per poter abilitare lo sharding: ```shell mongo --port 8021 ``` Abilitiamo la possibilita' di fare sharding sul nostro nuovo db: ```javascript sh.enableSharding("airbnb") ``` Possiamo controllare lo stato del db con: ```javascript use airbnb db.stats() ``` dove possiamo vedere che tutti i dati sono su un solo shard (guardando la storageSize) oppure possiamo vedere lo stato dello sharding con: ```javascript db.printShardingStatus() ``` Creiamo l'indice su cui vogliamo eseguire lo sharding: ```javascript db.airbnb.createIndex({beds:1}) ``` Poi shardiamo la collection: ```javascript sh.shardCollection("airbnb.airbnb", {beds:1}) ``` Pero' alcuni documenti non hanno beds! Quindi rimuoviamoli dai dati: ```javascript // per contarli possiamo fare // db.airbnb.find({beds:{$exists:false}}).count() db.airbnb.deleteMany({beds:{$exists:false}}) ``` Ora possiamo fare lo sharding perche' tutti i documenti hanno il campo beds: ```javascript sh.shardCollection("airbnb.airbnb", {beds:1}) ``` ora possiamo controllare che il balancer stia spostando i dati tra gli shards: ```javascript db.stats() ``` Notiamo che i dati non sono ben bilanciati, probabilmente perche' non abbiamo scelto una buona chiave visto che avremo tantissimi documenti con beds == 1 e molti meno con un numero diverso. Purtroppo fare l'unsharding di una collection non e' semplice https://jira.mongodb.org/browse/SERVER-9845 quindi per semplicita' la cancelliamo e ricreiamo: ```javascript db.airbnb.drop() ``` E reimportiamo i dati e ci colleghiamo: ```shell mongoimport ./listingsAndReviews.json -d=airbnb -c=airbnb --port 8021 mongo --port 8021 ``` ```javascript use airbnb db.airbnb.deleteMany({beds:{$exists:false}}) sh.enableSharding("airbnb") ``` creiamo un indice hashato (gli indici hashati non pososno essere multiindici) ```javascript db.airbnb.createIndex({beds:"hashed"}) sh.shardCollection("airbnb.airbnb", {beds:"hashed"}) ``` Ancora non abbiamo buoni risultati, quindi passiamo al indice come chiave: ricancelliamo e reimportiamo i dati come prima, ```javascript use airbnb sh.enableSharding("airbnb") db.airbnb.ensureIndex({_id:"hashed"}) sh.shardCollection("airbnb.airbnb", {_id:"hashed"}) ``` ed otteniamo un risultato migliore che possiamo controllare con: ```javascript db.airbnb.getShardDistribution() ``` Eventualmente possiamo rimuovere uno shard: ```javascript db.adminCommand({removeShard:"rs0"}) ``` Possiamo spostare i database tra shards con: ```javascript db.adminCommand({movePrimary:"testdb", to:"rss2"}) ``` Possiamo splittare il chunk in sotto chunks: ```javascript db.splitFind("airbnb.airbnb", { "_id" : "10009999" }) ``` # TLS Generiamo un certificato per mongodb: ```shell openssl genrsa -out rootCA.key 4096 ``` Lo firmiamo: ```shell openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 1024 -out rootCA.pem ``` L'importante e' che in common name si metta l'ip / host della macchina (quindi 127.0.0.1 nel nostro caso) Creiamo un CA per il server: ```shell openssl genrsa -out mongodb.key 4096 ``` Firmiamolo: ```shell openssl req -new -key mongodb.key -out mongodb.csr ``` L'importante e' che in common name si metta l'ip / host della macchina (quindi 127.0.0.1 nel nostro caso) Generiamo il pem per mongodb: ```shell cat mongodb.key mongodb.crt > mongodb.pem ``` Nelle settings aggiungiamo: ```yaml net: port: 27017 bindIp: 127.0.0.1 tls: mode: requireTLS certificateKeyFile: /root/mongodb.pem CAFile: /root/rootCA.pem ``` Facciamo partire il server: ```shell mongod -f confs.yaml ``` Poi colleghiamoci col client con: ```shell mongo --tls --host 127.0.0.1 --tlsCAFile ./rootCA.pem --tlsCertificateKeyFile ./mongodb.pem ``` # LDAP (Work in progress) Modifichiamo in `/etc/default/saslauthd` ``` MECHANISM="ldap" ``` Poi creiamo / modifichiamo `/etc/saslauthd.conf` ``` ldap_servers: ldap://127.0.0.1:389 ldap_search_base: dc=example,dc=org ldap_filter: (mail=%n) ``` Faccio partire il daemon di saslauthd: ```shell saslauthd -a "ldap" & ``` Poi facciamo partire il server ed il client: ```shell mongod -f confs.yaml ``` ```shell mongo --tls --host 127.0.0.1 --tlsCAFile ./rootCA.pem --tlsCertificateKeyFile ./mongodb.pem ``` e dentro eseguiamo: ```javascript sdb = db.getSiblingDB("$external") sdb.createUser({user:"test", roles:[{role:"read", db:"testdb"}]}) sdb.createUser({user:"admin", roles:[{role:"dbOwner", db:"testdb"}]}) ``` E poi spegniamo il server: ```javascript db.adminCommand({shutDown:1}) ``` aggiungiamo alle settings: ```yaml storage: dbPath: /var/lib/mongodb journal: enabled: true # engine: # mmapv1: # wiredTiger: # where to write logging data. systemLog: destination: file logAppend: true path: /var/log/mongodb/mongod.log # network interfaces net: port: 27017 bindIp: 127.0.0.1 tls: mode: requireTLS certificateKeyFile: /root/mongodb.pem CAFile: /root/rootCA.pem # how the process runs processManagement: timeZoneInfo: /usr/share/zoneinfo fork: true security: authorization: enabled setParameter: saslauthdPath: /var/run/saslauthd/mux authenticationMechanisms: PLAIN ``` Mi collego con: ```shell mongo --tls --host 127.0.0.1 --tlsCAFile ./rootCA.pem --tlsCertificateKeyFile ./mongodb.pem ``` poi mi authentico con: ```shell sdb = db.getSiblingDB("$external") sdb.auth({mechanism: "PLAIN", user:"admin@example.org", pwd:"admin"}) ```