MongoDB ShardedCluster mit X.509-Authentifizierung konfigurieren

Guten Tag an alle! Vor kurzem hat das Leben dem Autor eine aufregende Aufgabe gegeben, einen MongoDB-Cluster mit Replikations- und Sharding-Einstellungen sowie die Authentifizierung mithilfe von x.509-Zertifikaten bereitzustellen. In diesem Artikel möchte ich zunächst meine Gedanken ausdrücken und meine Erfahrungen teilen. Da einige Dinge nicht trivial waren und nicht beim ersten Mal erledigt werden konnten, denke ich, dass meine Schritt-für-Schritt-Anweisungen für diejenigen nützlich sein können, die nur mit Daten-Sharding und der Arbeit mit MongoDB im Allgemeinen vertraut sind.
Ich freue mich auch sehr über Empfehlungen zum Hinzufügen / Ändern der Clusterkonfiguration und nur über Fragen oder Kritik zum Artikel selbst oder zum Thema.

Eintrag


Das Projekt, in dessen Rahmen der Cluster eingeführt wurde, ist ein Dienst zum Sammeln von Statistiken auf Kundengeräten und zum Aggregieren seiner Bereitstellung auf der Site oder über die Rest-API. Das Projekt hat lange Zeit unter geringer Last stabil gearbeitet, und infolgedessen hat der MongoDB-Server, der „out of the box“ (ohne Sharding und Datenreplikation) installiert wurde, seine Aufgabe perfekt erfüllt, und durch tägliche Sicherungen der Datenbank auf der Krone wurde ein „ruhiger Schlaf“ erzielt. Der Donner schlug wie üblich einen schönen Moment nach dem Eintreffen mehrerer großer Kunden mit einer großen Anzahl von Geräten, Daten und Anfragen ein. Die Folge war eine unannehmbar lange Ausführung von Abfragen an eine ältere Datenbank, und der Höhepunkt war eine Störung des Servers, als wir fast Daten verloren haben.

Daher bestand über Nacht die Notwendigkeit, Arbeiten zur Erhöhung der Fehlertoleranz, Datensicherheit und Produktivität mit der Möglichkeit einer zukünftigen Skalierbarkeit durchzuführen. Es wurde beschlossen, das vorhandene Potenzial von MongoDB zu nutzen, um die aufgetretenen Probleme zu beseitigen, nämlich einen Sharded-Cluster mit Replikation zu organisieren und vorhandene Daten dorthin zu migrieren.

Ein bisschen Theorie


Zunächst werfen wir einen Blick auf ShardedCluster MongoDB und seine Hauptkomponenten. Sharding als solches ist eine Methode zur horizontalen Skalierung von Computersystemen, die Daten speichern und Zugriff darauf gewähren. Im Gegensatz zur vertikalen Skalierung funktioniert das Sharding , wenn die Systemleistung gesteigert werden kann, indem die Leistung eines einzelnen Servers erhöht wird, z. B. durch Umschalten auf eine leistungsstärkere CPU, Hinzufügen des verfügbaren RAM oder Speicherplatzes, indem der Datensatz und die Last auf mehrere Server verteilt werden und Hinzufügen neuer Server nach Bedarf (dies ist nur unser Fall).

Der Vorteil einer solchen Skalierung ist das nahezu endlose Expansionspotential, während ein vertikal skalierbares System beispielsweise durch die vom Hosting-Anbieter verfügbare Hardware bewusst eingeschränkt wird.

Was wird vom Wechsel zu einem MongoDB-Shard-Cluster erwartet? Erstens ist es notwendig, die Lastverteilung der Lese- / Schreibvorgänge zwischen den Shards des Clusters zu erhalten und zweitens eine hohe Fehlertoleranz (konstante Datenverfügbarkeit) und Datensicherheit aufgrund übermäßigen Kopierens (Replikation) zu erreichen.

Im Fall von MongoDB erfolgt das Daten-Sharding auf Sammlungsebene. Dies bedeutet, dass wir explizit angeben können, welche Sammlungsdaten auf vorhandene Cluster-Shards verteilt werden sollen. Dies bedeutet auch, dass der gesamte Satz von Dokumenten der Shardable-Sammlung in gleich große Teile unterteilt wird - Chunks (Chunk), die anschließend vom Monga Balancer fast zu gleichen Teilen zwischen den Scharaden des Clusters migriert werden.

Das Sharding für alle Datenbanken und Sammlungen ist standardmäßig deaktiviert, und Cluster-Systemdatenbanken wie admin und config können nicht shardet werden . Wenn wir dies versuchen, werden wir von Monga eine eindeutige Ablehnung erhalten:

mongos> sh.enableSharding("admin")
{ "ok" : 0, "errmsg" : "can't shard admin database" }

Der gemischte MongoDB-Cluster bietet drei Voraussetzungen: Das Vorhandensein von Shards , die gesamte Kommunikation zwischen dem Cluster und seinen Clients sollte ausschließlich über Mongos- Router erfolgen, und der Server muss über einen Konfigurationsserver verfügen (basierend auf einer zusätzlichen Mongod-Instanz oder wie auf Replica empfohlen) Seta).

In der offiziellen Mongodb-Dokumentation heißt es: „In der Produktion sollten alle Shards Replikatsets sein.“ . Als Replik ( Replica Set ), wobei jede Scherbe aufgrund mehrerer Daten erhöht seine Elastizität (in Bezug auf die Verfügbarkeit der Daten auf jeder Instanz Replika) Kopieren gut selbst bietet ihre Sicherheit besser.

Replik(Replikatsatz) ist eine Kombination aus mehreren laufenden Mongod-Instanzen, in denen Kopien desselben Datensatzes gespeichert sind. Im Fall einer Shard-Replik ist dies eine Reihe von Stücken, die vom Monga-Balancer an diesen Shard übergeben werden.

Eine der Replikatinstanzen wird als Hauptinstanz ( PRIMARY ) zugewiesen und akzeptiert alle Datenschreibvorgänge (während sie beibehalten und gelesen werden). Die verbleibenden Mongods werden dann als SECONDARY deklariert und aktualisieren in asynchroner Kommunikation mit PRIMARY ihre Kopie des Datensatzes. Sie stehen auch zum Lesen von Daten zur Verfügung. Sobald PRIMARY aus irgendeinem Grund nicht mehr zugänglich ist und nicht mehr mit anderen Mitgliedern des Replikats interagiert, wird eine Abstimmung unter allen verfügbaren Mitgliedern des Replikats angekündigtfür die Rolle des neuen PRIMARY. Zusätzlich zu PRIMARY und SECONDARY kann es eine weitere dritte Art von Teilnehmern am Replikatset geben - dies ist der Arbiter ( ARBITER ).

Der Schiedsrichter in der Replik erfüllt nicht die Rolle des Kopierens des Datensatzes, sondern löst die wichtige Aufgabe der Abstimmung und soll die Replik vor dem Ende der Abstimmung schützen. Stellen Sie sich eine Situation vor, in der eine gerade Anzahl von Teilnehmern an der Replik teilnimmt und zwei Bewerber mit der gleichen Gesamtzahl an Stimmen in zwei Hälften stimmen, und so endlos ... Wenn er einer solchen „geraden“ Bemerkung einen Schiedsrichter hinzufügt, entscheidet er über das Abstimmungsergebnis, indem er seine Stimme einem oder einem anderen Kandidaten für „ position ”PRIMARY, ohne dass Ressourcen erforderlich sind, um eine weitere Kopie des Datensatzes zu warten.

Ich stelle fest, dass das Replikatset die Vereinigung genau mongod Instanzen ist, das heißt, nichts hindert Sie daran, ein Replikat auf einem Server zusammenzustellen, Ordner auf verschiedenen physischen Medien als Datenspeicher anzugeben und eine gewisse Datensicherheit zu erreichen, aber dennoch eine ideale Option - Dies ist eine Replikat-Organisation mit dem Start von Mongod auf verschiedenen Servern. In dieser Hinsicht ist das MongoDB-System im Allgemeinen sehr flexibel und ermöglicht es uns, die von uns benötigte Konfiguration basierend auf unseren Anforderungen und Fähigkeiten zusammenzustellen, ohne strenge Einschränkungen aufzuerlegen. Das Replikatset als solches außerhalb des Kontexts von Sharded Cluster ist eines der typischen MongoDB-Serverorganisationsschemata, das ein hohes Maß an Fehlertoleranz und Datenschutz bietet. In diesem Fall speichert jeder Teilnehmer im Replikat eine vollständige Kopie des gesamten Datenbankdatensatzes und nicht einen Teil davon.

Die Infrastruktur


Die folgende Clusterkonfiguration basiert auf drei virtuellen OpenVZ-Containern (VBOs). Jede der virtuellen Maschinen befindet sich auf einem separaten dedizierten Server.

Zwei virtuelle Maschinen (im Folgenden: server1.cluster.com und server2.cluster.com ) verfügen über mehr Ressourcen - sie sind für die Replikation, das Sharding und die Bereitstellung von Daten für Clients verantwortlich. Die dritte Maschine ( server3.cluster.com ) hat eine schwächere Konfiguration - ihr Zweck ist es, den Betrieb von Instanzen von Mongod-Schiedsrichtern sicherzustellen.

In dem konstruierten Cluster haben wir jetzt drei Scharaden. In unserem Schema folgten wir der Empfehlung, Shards basierend auf Replikatsätzen zu erstellen, jedoch mit einigen Annahmen. Jeder Shard-Replikatsatz unseres Clusters verfügt über einen eigenen PRIMARY, SECONDARY und ARBITER, die auf drei verschiedenen Servern ausgeführt werden. Es gibt auch einen Konfigurationsserver, der ebenfalls mithilfe der Datenreplikation erstellt wird.

Wir haben jedoch nur drei Server, von denen einer keine Datenreplikationsfunktionen ausführt (nur im Fall eines Konfigurationsreplikats), und daher befinden sich alle drei Shards tatsächlich auf zwei Servern.

In den Diagrammen aus der Mongi-Dokumentation sind Mongos auf Anwendungsservern dargestellt. Ich habe beschlossen, diese Regel zu brechen und die Mongos (wir werden zwei haben) auf den Datenservern zu platzieren: server1.cluster.com und server2.cluster.com, um die zusätzliche Konfiguration von mongodb auf Anwendungsservern und aufgrund bestimmter Einschränkungen im Zusammenhang mit Anwendungsservern zu beseitigen. Anwendungsserver können eine Verbindung zu einem der beiden Mongos herstellen. Bei Problemen mit einem dieser Server werden sie nach kurzer Zeit wieder mit dem anderen verbunden. Anwendungsserver befinden sich wiederum hinter dem DNS-ten, auf dem Round Robin konfiguriert ist. Es gibt abwechselnd eine von zwei Adressen aus, um ein primitives Gleichgewicht der Verbindungen (Client-Anforderungen) zu gewährleisten. Es gibt Pläne, es durch ein "intelligentes" DNS zu ersetzen (vielleicht sagt Ihnen jemand in den Kommentaren eine gute Lösung, ich bin Ihnen dankbar!

Zur Verdeutlichung gebe ich ein allgemeines Diagramm des gebildeten Clusters mit Servernamen und darauf ausgeführten Anwendungen. Doppelpunkte geben die angegebenen Anwendungsports an.



Ersteinrichtung


Wir gehen zu server1.cluster.com und installieren die neueste Version des MongoDB Community Edition-Pakets aus dem offiziellen Repository. Zum Zeitpunkt der Montage ist der Cluster Version 3.2.8. In meinem Fall ist das Debian 8-Betriebssystem auf allen Cluster-Computern installiert. Detaillierte Installationsanweisungen auf Ihrem Betriebssystem finden Sie in der offiziellen Dokumentation .
Wir importieren den öffentlichen Schlüssel in das System, aktualisieren die Liste der Pakete und installieren den Mongodb-Server mit einer Reihe von Dienstprogrammen:

server1.cluster.com:~# apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv EA312927
server1.cluster.com:~# echo "deb http://repo.mongodb.org/apt/debian wheezy/mongodb-org/3.2 main" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.2.list
server1.cluster.com:~# apt-get update
server1.cluster.com:~# apt-get install -y mongodb-org

Fertig! Als Ergebnis der durchgeführten Aktionen erhalten wir auf unserer Maschine MongoDB einen Server, der bereits in Betrieb ist. Deaktivieren Sie vorerst den Mongod-Dienst (wir werden darauf zurückkommen):

server1.cluster.com:~# service mongod stop

Als nächstes erstellen Sie ein Verzeichnis, in dem wir alle Daten unseres Clusters speichern. Ich habe es im Pfad "/ root / mongodb". Im Inneren bilden wir die folgende Verzeichnisstruktur:

.
├── cfg
├── data
│   ├── config
│   ├── rs0
│   ├── rs1
│   └── rs2
├── keys
└── logs

Im Unterverzeichnis data speichern wir die Daten unserer Replikate (einschließlich des Konfigurationsreplikats) direkt. In cfg erstellen wir Konfigurationsdateien, um die erforderlichen mongo {d / s} -Instanzen auszuführen. In Schlüssel kopieren wir die Schlüssel und Zertifikate für die x.509-Authentifizierung von Clustermitgliedern. Der Zweck des Protokollordners, denke ich, versteht jeder.

Ebenso muss der Vorgang mit Installation und Verzeichnissen auf den verbleibenden zwei Servern wiederholt werden.

Stellen Sie vor dem Konfigurieren und Verknüpfen der Komponenten unseres Clusters sicher, dass alles ordnungsgemäß funktioniert. Führen Sie die Mongod-Instanz auf Port 27000 aus und geben Sie das Verzeichnis für die Daten in „/ root / mongodb / data / rs0“ an:

mongod --port 27000 --dbpath /root/mongodb/data/rs0

Öffnen Sie auf demselben Server ein anderes Terminal und stellen Sie eine Verbindung zum laufenden Mongod her:

mongo --port 27000

Wenn alles gut gegangen ist, werden wir in Shell Mongodb landen und können ein paar Befehle ausführen. Standardmäßig wechselt mong zur Testdatenbank. Sie können dies überprüfen, indem Sie den folgenden Befehl eingeben:

> db.getName()
test

Löschen Sie die nicht benötigte Datenbank mit dem folgenden Befehl:

> db.dropDatabase()
{ "ok" : 1 }

Und wir werden eine neue Datenbank initialisieren, mit der wir experimentieren werden, indem wir zu ihr wechseln:

> use analytics
switched to db analytics  

Versuchen Sie nun, die Daten einzugeben. Um nicht unbegründet zu sein, schlage ich vor, alle weiteren Vorgänge in diesem Artikel am Beispiel eines Systems zum Sammeln bestimmter Statistiken zu betrachten, bei dem regelmäßig Daten von Remote-Geräten eintreffen, die auf Anwendungsservern verarbeitet und dann in einer Datenbank gespeichert werden.

Wir fügen einige Geräte hinzu:

> db.sensors.insert({'s':1001, 'n': 'Sensor1001', 'o': true, 'ip': '192.168.88.20', 'a': ISODate('2016-07-20T20:34:16.001Z'), 'e': 0})
WriteResult({ "nInserted" : 1 })
> db.sensors.insert({'s':1002, 'n': 'Sensor1002', 'o': false, 'ip': '192.168.88.30', 'a': ISODate('2016-07-19T13:40:22.483Z'), 'e': 0})
WriteResult({ "nInserted" : 1 })

Hier ist
s die Seriennummer des Sensors;
n ist seine Zeichenkettenkennung;
o - aktueller Status (online / offline);
IP - Sensor IP Adresse;
a ist die Zeit der letzten Aktivität;
e ist ein Zeichen eines Fehlers;

Und jetzt ein paar Statistikaufzeichnungen des Formulars:

> db.statistics.insert({'s':1001, ‘ts’: ISODate('2016-08-04T20:34:16.001Z'), ‘param1’: 123, ‘param2’: 23.45, ‘param3’: “OK”, ‘param4’: True, ‘param5’: ‘-1000’, ‘param6’: [1,2,3,4,5])
WriteResult({ "nInserted" : 1 })

s ist die Sensornummer;
ts - TimeStamp;
param1..param6
- einige Statistiken.

Clients des statistischen Analysedienstes führen häufig aggregierte Abfragen durch, um repräsentative Daten zu Statistiken zu erhalten, die von ihren Geräten erfasst wurden. Bei fast allen Anfragen handelt es sich um die „Sensor-Seriennummer“ (Felder). Sortierungen und Gruppierungen werden häufig darauf angewendet. Daher fügen wir der Statistiksammlung zur Optimierung (sowie zum Sharding) einen Index hinzu:

mongos> db.statistics.ensureIndex({"s":1})

Die Auswahl und Erstellung der notwendigen Indizes ist ein Thema für eine gesonderte Diskussion, aber ich werde mich vorerst darauf beschränken.

Authentifizierung mit x.509-Zertifikaten


Um das Problem zu verstehen, gehen wir etwas weiter und stellen uns die Mongod-Instanzen vor, die auf verschiedenen Servern ausgeführt werden, die zu einem Replikat kombiniert werden müssen, verbinden Mongos mit ihnen und bieten die Möglichkeit, Clients sicher mit dem gebildeten Cluster zu verbinden. Natürlich müssen Teilnehmer am Datenaustausch bei der Verbindung authentifiziert werden (vertrauenswürdig sein) und es ist wünschenswert, dass der Datenkanal ebenfalls geschützt ist. In diesem Fall bietet MongoDB TSL / SSL-Unterstützung sowie mehrere Authentifizierungsmechanismen. Eine der Möglichkeiten, um Vertrauen zwischen den Teilnehmern am Datenaustausch im Cluster herzustellen, ist die Verwendung von Schlüsseln und Zertifikaten. In Bezug auf die Auswahl eines Mechanismus, der diese Option verwendet, gibt es eine Empfehlung in der Mongi-Dokumentation:

„Schlüsseldateien sind ein Minimum an Sicherheit und eignen sich am besten für Test- oder Entwicklungsumgebungen. Für Produktionsumgebungen empfehlen wir die Verwendung von x.509-Zertifikaten . “

X.509 ist der ITU-T-Standard für die Infrastruktur öffentlicher Schlüssel und die Verwaltung von Berechtigungen. Dieser Standard definiert das Format und die Verteilung öffentlicher Schlüssel mithilfe signierter digitaler Zertifikate. Das Zertifikat ordnet den öffentlichen Schlüssel einem Betreff zu - dem Benutzer des Zertifikats. Die Zuverlässigkeit dieser Beziehung wird durch eine digitale Signatur erreicht, die von einer vertrauenswürdigen Zertifizierungsstelle durchgeführt wird.

(Zusätzlich zu x.509 verfügt MongoDB über äußerst zuverlässige Methoden auf Unternehmensebene - dies sind die Kerberos-Authentifizierung und die LDAP-Proxy-Berechtigungsauthentifizierung), aber dies ist nicht unser Fall und hier werden wir die Konfiguration der x.509-Authentifizierung betrachten.

Der Authentifizierungsmechanismus mit x.509-Zertifikaten erfordert eine sichere TSL / SSL-Verbindung zum Cluster, die durch das entsprechende Argument zum Starten von mongod --sslMode oder durch den Parameter net.ssl.mode in der Konfigurationsdatei aktiviert wird . Die Authentifizierung des Clients, der in diesem Fall eine Verbindung zum Server herstellt, hängt von der Authentifizierung des Zertifikats ab und nicht von der Anmeldung und dem Kennwort.

Im Rahmen dieses Mechanismus werden die generierten Zertifikate in zwei Typen unterteilt: Cluster-Mitgliedszertifikate - sind an einen bestimmten Server gebunden, der für die interne Authentifizierung von Mongod-Instanzen auf verschiedenen Computern vorgesehen ist, und Client-Zertifikate- sind an einen separaten Benutzer gebunden, mit dem externe Clients des Clusters authentifiziert werden sollen.

Um die Bedingungen von x.509 zu erfüllen, benötigen wir einen einzigen Schlüssel - die sogenannte Certificate Authority (CA) . Auf dieser Grundlage werden sowohl Client- als auch Clustermitgliedszertifikate ausgestellt. Daher erstellen wir zunächst einen geheimen Schlüssel für unsere Zertifizierungsstelle. Alle folgenden Aktionen werden korrekt ausgeführt und geheime Schlüssel werden auf einem separaten Computer gespeichert. In diesem Artikel werden jedoch alle Aktionen auf dem ersten Server (server1.cluster.com) ausgeführt:

server1.cluster.com:~/mongodb/keys# openssl genrsa -out mongodb-private.key -aes256
Generating RSA private key, 2048 bit long modulus
.....................+++
........................................................+++
e is 65537 (0x10001)
Enter pass phrase for mongodb-private.key:
Verifying - Enter pass phrase for mongodb-private.key:

Mit dem Vorschlag, eine geheime Phrase einzuführen, führen wir eine zuverlässige Kombination ein und bestätigen sie, zum Beispiel „temporis $ filia $ veritas“ (natürlich haben Sie etwas anderes und komplizierteres). Der Satz muss beachtet werden, wir benötigen ihn, um jedes neue Zertifikat zu unterschreiben.

Als Nächstes erstellen wir ein CA-Zertifikat (direkt nach dem Starten des Befehls werden wir aufgefordert, die geheime Phrase aus dem von uns angegebenen Schlüssel einzugeben (im Parameter „key“):

server1.cluster.com:~/mongodb/keys# openssl req -x509 -new -extensions v3_ca -key mongodb-private.key -days 36500 -out mongodb-CA-cert.crt

Ich mache Sie auf den Tag- Parameter aufmerksam - er ist für die Dauer des Zertifikats verantwortlich. Ich bin mir nicht sicher, wer und wie viel an dem Projekt beteiligt sein wird, an dem ich gerade arbeite. Um unangenehme Überraschungen zu vermeiden, geben wir das Zertifikat 36.500 Lebenstage an, was 100 Jahren entspricht (sehr optimistisch, nicht wahr?).
Nach Überprüfung des Ausdrucks werden wir aufgefordert, Informationen über die Organisation einzugeben, der das Zertifikat gehört. Stellen Sie sich vor, unsere große Organisation heißt „SomeSysyems“ und befindet sich in Moskau (die eingegebenen Informationen folgen nach den Doppelpunkten):

Country Name (2 letter code) [AU]: RU
State or Province Name (full name) [Some-State]: MoscowRegion
Locality Name (eg, city) []: Moscow
Organization Name (eg, company) [Internet Widgits Pty Ltd]: SomeSystems
Organizational Unit Name (eg, section) []: Statistics
Common Name (e.g. server FQDN or YOUR name) []: CaServer
Email Address []: info@SomeSystems.com

Großartig! CA ist bereit und wir können damit Client-Zertifikate und Zertifikate von Cluster-Mitgliedern signieren. Ich füge hinzu, dass die Zuverlässigkeit der eingegebenen Daten die Funktionalität des CA-Zertifikats selbst nicht beeinträchtigt. Die signierten Zertifikate hängen jedoch jetzt von den eingegebenen Werten ab, die später erläutert werden.

Das Verfahren zum Erstellen von Zertifikaten für Clustermitglieder (Zertifikate für externe Clients werden separat betrachtet) lautet wie folgt:

  1. Wir generieren einen privaten Schlüssel (* .key - Datei) und eine "Zertifikatanforderung" (CSR-Datei). CSR (Certificate Signing Request) ist eine Textdatei, die verschlüsselte Informationen über die Organisation enthält, die das Zertifikat und den öffentlichen Schlüssel ausgestellt hat.

  2. Mit dem privaten Schlüssel und dem öffentlichen Zertifikat unserer Zertifizierungsstelle signieren wir das Zertifikat für den aktuellen Server.

  3. Aus dem neuen Schlüssel und dem Zertifikat des Clustermitglieds bilden wir die PEM-Datei, mit der wir eine Verbindung zum Cluster herstellen.

Wir erstellen einen privaten Schlüssel und eine Zertifikatanforderung für unseren ersten Server (server1.cluster.com). Ich werde auf ein wichtiges Detail achten, wenn beim Ausfüllen alle Felder mit Ausnahme von CN (Common Name) dieselben wie für das Stammzertifikat bleiben. Es muss für jedes Zertifikat eindeutig gemacht werden. In unserem Fall wird der vollqualifizierte Domänenname (FQDN) eines bestimmten Servers als Wert angegeben :

server1.cluster.com:~/mongodb/keys# openssl req -new -nodes -newkey rsa:2048 -keyout server1.key -out server1.csr
Country Name (2 letter code) [AU]: RU
State or Province Name (full name) [Some-State]: MoscowRegion
Locality Name (eg, city) []: Moscow
Organization Name (eg, company) [Internet Widgits Pty Ltd]: SomeSystems
Organizational Unit Name (eg, section) []: Statistics
Common Name (e.g. server FQDN or YOUR name) []: server1.cluster.com
Email Address []: info@SomeSystems.com
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

Zusätzliche Felder habe ich leer gelassen. Wenn Sie sich entscheiden, ein zusätzliches Passwort anzugeben (Ein Challenge-Passwort [] :), müssen Sie in der Mongod-Konfiguration ein Passwort für dieses Zertifikat angeben, für das die Parameter net.ssl.PEMKeyPassword und net.ssl.clusterPassword verantwortlich sind . (Details zu diesen Parametern finden Sie in der Dokumentation hier ).

Als nächstes signieren wir die CSR-Datei mit unserem CA-Zertifikat und erhalten ein öffentliches Zertifikat (* .crt-Datei):

server1.cluster.com:~/mongodb/keys# openssl x509 -CA mongodb-CA-cert.crt -CAkey mongodb-private.key -CAcreateserial -req -days 36500 -in server1.csr -out server1.crt
Signature ok
subject=/C=RU/ST=MoscowRegion/L=Moscow/O=SomeSystems/OU=Statistics/CN=server1.cluster.com/emailAddress=info@SomeSystems.com
Getting CA Private Key
Enter pass phrase for mongodb-private.key:

Jetzt müssen wir eine .pem-Datei erstellen:

server1.cluster.com:~/mongodb/keys# cat server1.key server1.crt > server1.pem

Wir werden die PEM-Datei direkt beim Starten von Mongod-Instanzen verwenden und sie in der Konfiguration angeben.
Jetzt müssen Sie den Vorgang wiederholen, um ein Zertifikat für die verbleibenden Server zu erstellen. Zum vollständigen Verständnis zitiere ich alle Befehle:

server1.cluster.com:~/mongodb/keys# openssl req -new -nodes -newkey rsa:2048 -keyout server2.key -out server2.csr
Country Name (2 letter code) [AU]: RU
State or Province Name (full name) [Some-State]: MoscowRegion
Locality Name (eg, city) []: Moscow
Organization Name (eg, company) [Internet Widgits Pty Ltd]: SomeSystems
Organizational Unit Name (eg, section) []: Statistics
Common Name (e.g. server FQDN or YOUR name) []: server2.cluster.com
Email Address []: info@SomeSystems.com
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

(zusätzliche Felder wurden nicht ausgefüllt) Wir

signieren die CSR-Datei mit unserem CA-Zertifikat, um das öffentliche Zertifikat (* .crt-Datei) des zweiten Servers zu erhalten:

server1.cluster.com:~/mongodb/keys# openssl x509 -CA mongodb-CA-cert.crt -CAkey mongodb-private.key -CAcreateserial -req -days 36500 -in server2.csr -out server2.crt
Signature ok
subject=/C=RU/ST=MoscowRegion/L=Moscow/O=SomeSystems/OU=Statistics/CN=server2.cluster.com/emailAddress=info@SomeSystems.com
Getting CA Private Key
Enter pass phrase for mongodb-private.key:

Jetzt müssen wir eine .pem-Datei erstellen:

server1.cluster.com:~/mongodb/keys# cat server2.key server2.crt > server2.pem

Und ähnlich für das dritte Serverzertifikat:

server1.cluster.com:~/mongodb/keys# openssl req -new -nodes -newkey rsa:2048 -keyout server3.key -out server3.csr
Country Name (2 letter code) [AU]: RU
State or Province Name (full name) [Some-State]: MoscowRegion
Locality Name (eg, city) []: Moscow
Organization Name (eg, company) [Internet Widgits Pty Ltd]: SomeSystems
Organizational Unit Name (eg, section) []: Statistics
Common Name (e.g. server FQDN or YOUR name) []: server3.cluster.com
Email Address []: info@SomeSystems.com
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

(zusätzliche Felder wurden nicht ausgefüllt) Wir

signieren die CSR-Datei mit unserem CA-Zertifikat, um das öffentliche Zertifikat (* .crt-Datei) des dritten Servers zu erhalten:

server1.cluster.com:~/mongodb/keys# openssl x509 -CA mongodb-CA-cert.crt -CAkey mongodb-private.key -CAcreateserial -req -days 36500 -in server3.csr -out server3.crt
Signature ok
subject=/C=RU/ST=MoscowRegion/L=Moscow/O=SomeSystems/OU=Statistics/CN=server3.cluster.com/emailAddress=info@SomeSystems.com
Getting CA Private Key
Enter pass phrase for mongodb-private.key:

Erstellen Sie eine PEM-Datei:

server1.cluster.com:~/mongodb/keys# cat server3.key server3.crt > server3.pem

Ich wiederhole, dass alle Schlüssel und Zertifikate von mir auf dem ersten Server erstellt und dann, falls erforderlich, auf den entsprechenden Server verschoben wurden. Daher sollte jeder der drei Server über ein öffentliches CA-Zertifikat (mongodb-CA-cert.crt) und eine PEM-Datei für diesen Server (Server <$ N> .pem) verfügen.

Konfiguration der Mongod-Instanzen


Für den korrekten Start müssen wir eine Reihe von Parametern an die Mongod-Instanzen übergeben. Dazu können Sie die Konfigurationsdatei verwenden oder alle erforderlichen Werte als Argumente an den Terminalbefehl übergeben. Fast alle Konfigurationsoptionen spiegeln sich in den entsprechenden Befehlszeilenargumenten wider. Meiner Meinung nach ist die Option mit einer Konfigurationsdatei gerechtfertigter, da eine separate strukturierte Datei leichter zu lesen und zu ergänzen ist. In diesem Fall wird beim Starten einer Instanz eines Programms nur ein einziges Argument übergeben - der Speicherort der Konfigurationsdatei:

mongod --config 

Erstellen Sie also eine Konfigurationsdatei für die Mongod-Instanz des ersten Shard-Replikats (rs0) auf dem ersten Server:

#
# /root/mongodb/cfg/mongod-rs0.conf
#
replication:
 replSetName: "rs0" # название реплики
net:
 port:      27000
 ssl:
      mode:       requireSSL # требуем защищенного соединения
      PEMKeyFile:  /root/mongodb/keys/server1.pem
      clusterFile: /root/mongodb/keys/server1.pem
      CAFile:     /root/mongodb/keys/mongodb-CA-cert.crt
      weakCertificateValidation: false # запрещаем подключаться без сертификата
      allowInvalidCertificates:  false # запрещаем подключение с невалидными сертификатами
security:
 authorization:   enabled # требуем обязательную авторизацию
 clusterAuthMode: x509 # метод авторизации - MONGODB-X509
storage:
 dbPath :   /root/mongodb/data/rs0 # указываем каталог для данных
systemLog:
  destination: file # будем выводить лог в файл
  path:     /root/mongodb/logs/mongod-rs0.log # путь для лог-файла
  logAppend:   true # дописывать лог-файл при следующем запуске

Wir erstellen eine ähnliche Datei für das zweite Shard-Replikat (rs1), ändern jedoch den Port, den Replikatnamen, den Speicherort des Datenverzeichnisses und die Protokolldatei:

#
# /root/mongodb/cfg/mongod-rs1.conf
#
replication:
 replSetName: "rs1"
net:
 port:      27001
 ssl:
      mode:       requireSSL
      PEMKeyFile:  /root/mongodb/keys/server1.pem
      clusterFile: /root/mongodb/keys/server1.pem
      CAFile:     /root/mongodb/keys/mongodb-CA-cert.crt
      weakCertificateValidation: false
      allowInvalidCertificates:  false
security:
 authorization:   enabled
 clusterAuthMode: x509
storage:
 dbPath :   /root/mongodb/data/rs1
systemLog:
  destination: file
  path:     /root/mongodb/logs/mongod-rs1.log
  logAppend:   true

Und analog zur dritten Replik (rs2):

#
# /root/mongodb/cfg/mongod-rs2.conf
#
replication:
 replSetName: "rs2"
net:
 port:      27002
 ssl:
      mode:       requireSSL
      PEMKeyFile:  /root/mongodb/keys/server1.pem
      clusterFile: /root/mongodb/keys/server1.pem
      CAFile:     /root/mongodb/keys/mongodb-CA-cert.crt
      weakCertificateValidation: false
      allowInvalidCertificates:  false
security:
 authorization:   enabled
 clusterAuthMode: x509
storage:
 dbPath :   /root/mongodb/data/rs2
systemLog:
  destination: file
  path:     /root/mongodb/logs/mongod-rs2.log
  logAppend:   true

Zusätzlich zu den Instanzen, die drei Shard-Replikate organisieren, wird es in unserem Cluster Mongods geben, die den Betrieb des Konfigurationsservers bereitstellen, der auf der Grundlage des Replikats (rscfg) erstellt wird.

Es ist zu erklären, dass die Rolle des Konfigurationsservers von einem Mongod (sowie vom Shard) ausgeführt werden kann. Um jedoch Zuverlässigkeit und Fehlertoleranz zu gewährleisten, wird empfohlen, den Konfigurationsserver auch auf der Grundlage des Replikatsatzes zu erstellen.

Die Konfigurationsdatei für das Dienstreplikat unterscheidet sich von Datenreplikaten durch das Vorhandensein des Parameters "sharding.clusterRole", der der Mongod-Instanz ihren speziellen Zweck mitteilt:

#
# /root/mongodb/cfg/mongod-rscfg.conf
#
sharding:
  clusterRole: configsvr # указываем роль в кластере - сервер конфигурации
replication:
 replSetName: "rscfg" # название реплики
net:
 port:      27888
 ssl:
      mode:       requireSSL
      PEMKeyFile:  /root/mongodb/keys/server1.pem
      clusterFile: /root/mongodb/keys/server1.pem
      CAFile:     /root/mongodb/keys/mongodb-CA-cert.crt
      weakCertificateValidation: false
      allowInvalidCertificates:  false
security:
 authorization:   enabled
 clusterAuthMode: x509
storage:
 dbPath :   /root/mongodb/data/config
systemLog:
  destination: file
  path:     /root/mongodb/logs/mongod-rscfg.log
  logAppend:   true

Jetzt müssen wir alle erstellten Konfigurationsdateien auf die anderen Server kopieren. Vergessen Sie nach dem Kopieren nicht, die Werte in den Parametern net.ssl.PEMKeyFile und net.ssl.clusterFile zu ändern, in denen die Zertifikate des entsprechenden Servers angegeben werden sollen (server2.pem, server3.pem).

Einrichten des Replikatsets


Führen Sie mongod auf dem ersten Server auf Port 27000 aus, ohne eine "Kampf" -Konfigurationsdatei anzugeben - nur den Port und das Datenverzeichnis. Dies geschieht, damit sich die gestartete Mongod-Instanz noch nicht als Mitglied des Replikats betrachtet und auch keine strengen Anforderungen an die Verbindung und Authentifizierung stellt, die wir in den Konfigurationsdateien angegeben haben:

mongod --port 27000 --dbpath /root/mongodb/data/rs0

Als Nächstes müssen wir eine Verbindung zum laufenden Mongod herstellen und den Superuser des zukünftigen Replikats hinzufügen, damit wir in Zukunft nach Aktivierung der in unserer Konfigurationsdatei angegebenen Autorisierung das Recht haben, das Replikat einschließlich der Initialisierung zu ändern. Wie die Praxis gezeigt hat, hindert uns die Aufnahme der x.509-Autorisierung nicht daran, der Datenbank herkömmliche Benutzer hinzuzufügen (authentifiziert durch Login und Passwort). Trotzdem habe ich mich entschlossen, nicht auf diese Gelegenheit zurückzugreifen, sondern den x.509-Mechanismus überall sowohl auf Clusterebene als auch bei der Bildung von Replikaten einzusetzen. Um dies zu verdeutlichen, möchte ich sagen, dass der Benutzer, den wir jetzt erstellen, ein Benutzer auf der Ebene dieses Replikats ist. Von anderen Replikaten und auf Clusterebene ist es nicht verfügbar.

Für den neuen Benutzer müssen wir ein weiteres Zertifikat erstellen, wie wir es bereits im Abschnitt „x.509-Authentifizierung“ getan haben. Der Unterschied zwischen diesem Zertifikat besteht darin, dass es nicht an ein Clustermitglied (Mongod-Instanz oder Server) gebunden ist, sondern an das Konto. Mit anderen Worten, wir erstellen ein Client-Zertifikat. Dieses Zertifikat wird an die Superuser-Replik (Stammrolle) des Satzes des ersten Shards (rs0) gebunden. In MongoDB integrierte Rollen finden Sie in diesem Abschnitt der offiziellen Dokumentation.

Wir müssen zu unserem CA-Server gehen. Und generieren Sie eine weitere Anforderung zum Signieren von Schlüsseln und Zertifikaten:

server1.cluster.com:~/mongodb/keys# openssl req -new -nodes -newkey rsa:2048 -keyout rsroot.key -out rsroot.csr
Generating a 2048 bit RSA private key
........................................................................+++
.........................+++
writing new private key to 'rsroot.key'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]: RU
State or Province Name (full name) [Some-State]: MoscowRegion
Locality Name (eg, city) []: Moscow      
Organization Name (eg, company) [Internet Widgits Pty Ltd]: SomeSystems
Organizational Unit Name (eg, section) []: StatisticsClient
Common Name (e.g. server FQDN or YOUR name) []: rsroot
Email Address []:
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

Wir unterschreiben das Zertifikat (wieder benötigen wir die geheime Phrase aus dem CA-Schlüssel):

server1.cluster.com:~/mongodb/keys# openssl x509 -CA mongodb-CA-cert.crt -CAkey mongodb-private.key -CAcreateserial -req -days 36500 -in rsroot.csr -out rsroot.crt
Signature ok
subject=/C=RU/ST=MoscowRegion/L=Moscow/O=SomeSystems/OU=StatisticsClient/CN=rsroot
Getting CA Private Key
Enter pass phrase for mongodb-private.key:

Erstellen Sie eine PEM-Datei:

server1.cluster.com:~/mongodb/keys# cat rsroot.key rsroot.crt > rsroot.pem

Ich möchte Ihre Aufmerksamkeit auf den Parameter Organisationseinheitenname (OU) lenken, der sich beim Generieren von Clientzertifikaten von dem unterscheiden muss, den wir beim Generieren der Zertifikate der Clustermitglieder angegeben haben. Andernfalls kann die Monga Sie mit einem Fehler ablehnen, wenn Sie dem Cluster einen Benutzer hinzufügen, der einen Betreff (siehe unten) mit einer Organisationseinheit enthält, die dem entspricht, was die Clustermitglieder in ihren Zertifikaten haben:

{
   "ok" : 0,
   "errmsg" : "Cannot create an x.509 user with a subjectname that would be recognized as an internal cluster member.",
   "code" : 2
}

Der Benutzer für die Autorisierung mithilfe des x.509-Mechanismus wird auf etwas ungewöhnliche Weise hinzugefügt. Wir müssen nicht seinen Namen und sein Kennwort angeben, sondern die Kennung (den Betreff) des entsprechenden Zertifikats. Sie können den Betreff aus der PEM-Datei abrufen, indem Sie den folgenden Befehl ausführen:

server1.cluster.com:~/mongodb/keys# openssl x509 -in rsroot.pem -inform PEM -subject -nameopt RFC2253
subject= CN=rsroot,OU=StatisticsClient,O=SomeSystems,L=Moscow,ST=MoscowRegion,C=RU
-----BEGIN CERTIFICATE-----

In der Ausgabe interessieren wir uns für den Inhalt der Zeile, die mit „subject =“ beginnt (ohne das „subject =“ selbst und das Leerzeichen). Stellen Sie eine Verbindung zum Mongod her und fügen Sie den Benutzer hinzu:

mongo --port 27000

> db.getSiblingDB("$external").runCommand({createUser: "CN=rsroot,OU=StatisticsClient,O=SomeSystems,L=Moscow,ST=MoscowRegion,C=RU",
roles: [{role: "root", db: "admin"}]
})

$ external ist der Name der virtuellen Datenbank, mit der Benutzer erstellt werden, deren Anmeldeinformationen beispielsweise außerhalb von MongoDB gespeichert sind, wie in unserem Fall (eine Zertifikatdatei wird zur Authentifizierung verwendet).

Jetzt verlassen wir die Monga-Shell und starten Mongod neu, jetzt mit der entsprechenden Konfigurationsdatei. Das Gleiche muss auf dem zweiten und dritten Server durchgeführt werden. Daher sollten alle Mongoden des ersten Replikats (rs0) gestartet werden.
Wir stellen mit dem Zertifikat des erstellten Superusers des Replikats (rsroot) eine Verbindung zum Mongod her und übergeben die Authentifizierung, indem wir den Betreff als Benutzernamen angeben:

server1.cluster.com:~/mongodb/keys# mongo admin --ssl --sslCAFile /root/mongodb/keys/mongodb-CA-cert.crt --sslPEMKeyFile /root/mongodb/keys/rsroot.pem --host server1.cluster.com --port 27000

> db.getSiblingDB("$external").auth({
   mechanism:"MONGODB-X509",
   user: "CN=rsroot,OU=StatisticsClient,O=SomeSystems,L=Moscow,ST=MoscowRegion,C=RU"
})

Initialisieren Sie unser Replikat:

rs.initiate(
  {
      _id: "rs0",
      members: [
      { _id: 0, host : "server1.cluster.com:27000" },
      { _id: 1, host : "server2.cluster.com:27000" },
      { _id: 2, host : "server3.cluster.com:27000", arbiterOnly: true },
      ]
  }
)

Achten Sie auf den Parameter arbiterOnly für den dritten Server, den wir zu Beginn vereinbart haben, einen „ Arbratorserver“ zu erstellen .

Nachdem wir uns wieder mit dem Mongod verbunden haben, werden wir durch das Präfix "rs0" in der Shell sehen, dass es jetzt zu dem gleichnamigen Replikat gehört:
rs0: PRIMARY (Ihr aktueller Server kann SECONDARY ausgewählt werden).

In ähnlicher Weise müssen zwei weitere Datenreplikate verknüpft werden.

1. Führen Sie die Mongodes ohne die Konfiguration auf dem ersten Server aus (der Port und das Datenverzeichnis haben sich geändert):

mongod --port 27001 --dbpath /root/mongodb/data/rs1

2. Stellen Sie eine Verbindung zum laufenden Mongod her und fügen Sie den Replikatsuperuser (rs1) hinzu. Ich werde für alle Replikate dasselbe Zertifikat verwenden, daher wird der Betreff genauso verwendet wie für das erste Replikat:

mongo --port 27001

> db.getSiblingDB("$external").runCommand({createUser: "CN=rsroot,OU=StatisticsClient,O=SomeSystems,L=Moscow,ST=MoscowRegion,C=RU",
roles: [{role: "root", db: "admin"}]
})

3. Starten Sie mongod auf dem ersten Server neu und geben Sie die Konfigurationsdatei an. Auf dem zweiten und dritten Server erhöhen wir auch den Mongod mit der entsprechenden Konfiguration:

root@server1.cluster.com# mongod --config /root/mongodb/cfg/mongod-rs1.conf
root@server2.cluster.com# mongod --config /root/mongodb/cfg/mongod-rs1.conf
root@server3.cluster.com# mongod --config /root/mongodb/cfg/mongod-rs1.conf

4. Stellen Sie mit dem Zertifikat eine Verbindung zum Mongod her, übergeben Sie die Authentifizierung und initialisieren Sie das rs1-Replikat:

root@server1.cluster.com# mongo admin --ssl --sslCAFile /root/mongodb/keys/mongodb-CA-cert.crt --sslPEMKeyFile /root/mongodb/keys/rsroot.pem --host server1.cluster.com --port 27001

> db.getSiblingDB("$external").auth({
   mechanism:"MONGODB-X509",
   user: "CN=rsroot,OU=StatisticsClient,O=SomeSystems,L=Moscow,ST=MoscowRegion,C=RU"
})
> rs.initiate(
  {
      _id: "rs1",
      members: [
      { _id: 0, host : "server1.cluster.com:27001" },
      { _id: 1, host : "server2.cluster.com:27001" },
      { _id: 2, host : "server3.cluster.com:27001", arbiterOnly: true },
      ]
  }
)

Wiederholen Sie den Vorgang für das dritte Replikat (rs2).

1. Führen Sie die Mongodes ohne die Konfiguration auf dem ersten Server aus (vergessen Sie nicht, den Port und das Datenverzeichnis zu ändern):

mongod --port 27002 --dbpath /root/mongodb/data/rs2

2. Stellen Sie eine Verbindung zu Mongod her und fügen Sie den Replikatsuperuser (rs2) hinzu:

mongo --port 27002

> db.getSiblingDB("$external").runCommand({createUser: "CN=rsroot,OU=StatisticsClient,O=SomeSystems,L=Moscow,ST=MoscowRegion,C=RU",
roles: [{role: "root", db: "admin"}]
})

3. Starten Sie den Mongod auf dem ersten Server mit der Konfigurationsdatei neu. Auf dem zweiten und dritten Server erhöhen wir auch den Mongod mit den entsprechenden Konfigurationen:

root@server1.cluster.com# mongod --config /root/mongodb/cfg/mongod-rs2.conf
root@server2.cluster.com# mongod --config /root/mongodb/cfg/mongod-rs2.conf
root@server3.cluster.com# mongod --config /root/mongodb/cfg/mongod-rs2.conf

4. Stellen Sie mit dem Zertifikat eine Verbindung zum Mongod her, übergeben Sie die Authentifizierung und initialisieren Sie das rs2-Replikat:

root@server1.cluster.com# mongo admin --ssl --sslCAFile /root/mongodb/keys/mongodb-CA-cert.crt --sslPEMKeyFile /root/mongodb/keys/rsroot.pem --host server1.cluster.com --port 27002

> db.getSiblingDB("$external").auth({
   mechanism:"MONGODB-X509",
   user: "CN=rsroot,OU=StatisticsClient,O=SomeSystems,L=Moscow,ST=MoscowRegion,C=RU"
})
> rs.initiate(
  {
      _id: "rs2",
      members: [
      { _id: 0, host : "server1.cluster.com:27002" },
      { _id: 1, host : "server2.cluster.com:27002" },
      { _id: 2, host : "server3.cluster.com:27002", arbiterOnly: true },
      ]
  }
)

Server konfigurieren


Ich habe beschlossen, den Konfigurationsreplikatsatz des Konfigurationsservers hervorzuheben, da er einige Funktionen enthält, für die einige zusätzliche Schritte erforderlich sind. Erstens sind alle Benutzer, von denen wir der Konfiguration ein Replikat hinzufügen, auf Clusterebene über die Mongos verfügbar. Daher werde ich separate Benutzer erstellen, die an einzelne Zertifikate gebunden sind. Zweitens erlaubt Monga nicht das Erstellen von Schiedsrichtern als Teil eines Konfigurationsreplikats. Wenn Sie dies versuchen, erhalten Sie eine Fehlermeldung:

{
   "ok" : 0,
   "errmsg" : "Arbiters are not allowed in replica set configurations being used for config servers",
   "code" : 93
}

Aus diesem Grund haben wir zwei SECONDARY-Instanzen / Mongods im Konfigurationsreplikat. Erstellen wir ein weiteres Zertifikat für den Superuser des rscfg-Replikats, das, wie gesagt, auch ein Stammverzeichnis auf Clusterebene sein wird.

server1.cluster.com:~/mongodb/keys# openssl req -new -nodes -newkey rsa:2048 -keyout rootuser.key -out rootuser.csr
Generating a 2048 bit RSA private key
......................+++
.........................................+++
writing new private key to 'rootuser.key'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]: RU
State or Province Name (full name) [Some-State]: MoscowRegion
Locality Name (eg, city) []: Moscow
Organization Name (eg, company) [Internet Widgits Pty Ltd]: SomeSystems
Organizational Unit Name (eg, section) []: StatisticsClient
Common Name (e.g. server FQDN or YOUR name) []: root
Email Address []:
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

server1.cluster.com:~/mongodb/keys# openssl x509 -CA mongodb-CA-cert.crt -CAkey mongodb-private.key -CAcreateserial -req -days 36500 -in rootuser.csr -out rootuser.crt
Signature ok
subject=/C=RU/ST=MoscowRegion/L=Moscow/O=SomeSystems/OU=StatisticsClient/CN=root
Getting CA Private Key
Enter pass phrase for mongodb-private.key:

server1.cluster.com:~/mongodb/keys# cat rootuser.key rootuser.crt > rootuser.pem


server1.cluster.com:~/mongodb/keys# openssl x509 -in rootuser.pem -inform PEM -subject -nameopt RFC2253
subject= CN=root,OU=StatisticsClient,O=SomeSystems,L=Moscow,ST=MoscowRegion,C=RU
-----BEGIN CERTIFICATE-----

1. Starten Sie mongod ohne Konfiguration auf dem ersten Server:

server1.cluster.com:~/mongodb/keys# mongod --port 27888 --dbpath /root/mongodb/data/config

2. Stellen Sie eine Verbindung zu mongod her und fügen Sie den Replikatsuperuser (rscfg) hinzu .:

server1.cluster.com:~/mongodb/keys# mongo --port 27888

> db.getSiblingDB("$external").runCommand({createUser: "CN=root,OU=StatisticsClient,O=SomeSystems,L=Moscow,ST=MoscowRegion,C=RU",
roles: [{role: "root", db: "admin"}]
})

3. Starten Sie mongod auf dem ersten Server mit der Konfigurationsdatei neu. Auf dem zweiten und dritten Server erhöhen wir auch den Mongod mit der entsprechenden Konfigurationsdatei:

root@server1.cluster.com# mongod --config /root/mongodb/cfg/mongod-rscfg.conf
root@server2.cluster.com# mongod --config /root/mongodb/cfg/mongod-rscfg.conf
root@server3.cluster.com# mongod --config /root/mongodb/cfg/mongod-rscfg.conf

4. Wir verbinden uns mit dem Zertifikat mit dem Mongod, übergeben die Authentifizierung und initialisieren das Konfigurationsreplikat (rscfg):

root@server1.cluster.com# mongo admin --ssl --sslCAFile /root/mongodb/keys/mongodb-CA-cert.crt --sslPEMKeyFile /root/mongodb/keys/rootuser.pem --host server1.cluster.com --port 27888

> db.getSiblingDB("$external").auth({
   mechanism:"MONGODB-X509",
   user: "CN=root,OU=StatisticsClient,O=SomeSystems,L=Moscow,ST=MoscowRegion,C=RU"
})
> rs.initiate(
  {
      _id: "rscfg",
      members: [
      { _id: 0, host : "server1.cluster.com:27888" },
      { _id: 1, host : "server2.cluster.com:27888" },
      { _id: 2, host : "server3.cluster.com:27888" }
      ]
  }
)

Unser auf dem Konfigurationsserver basierendes Replikatset ist fertig. Jetzt können Sie Mongos starten und eine Verbindung zum Cluster herstellen.

Mongos konfigurieren und starten


Das Ziel von Mongos ist es, einen Zugangspunkt zu Clusterdaten bereitzustellen (außerdem können Clients nur über Mongos auf Clusterdaten zugreifen). In den Diagrammen aus der MongoDB-Dokumentation wird Mongos auf Anwendungsservern ausgeführt. In der von mir dargestellten Clusterstruktur werden zwei Mongos-Instanzen direkt auf server1.cluster.com und server2.cluster.com ausgeführt.

Zunächst erstellen wir ebenso wie für Mongod eine Konfigurationsdatei, die wir beim Start auf unsere Mongos übertragen.

Der Hauptunterschied zwischen den Mongos-Einstellungen und dem Mongod besteht darin, dass die Mongos kein Datenverzeichnis haben, da sie keine, sondern nur Proxy-Daten speichern. Die Mongos erhalten alle erforderlichen Informationen zur Konfiguration und zum Status des Clusters aus der Konfigurationssammlung des Konfigurationsservers. Mongos lernt, wie man über den Parameter sharding.configDB eine Verbindung zum Konfigurationsserver herstellt. Da unser Konfigurationsserver auf einem Replikat des Satzes basiert, geben wir es im Replikatformat an: den Namen des Replikats selbst, einen Schrägstrich und dann eine Liste von Hosts mit ihren durch Kommas getrennten Ports. Wir werden die Mongos am Standardhafen von Monga - 27017 starten.

#
# /root/mongodb/cfg/mongos.conf
#
sharding:
 configDB: "rscfg/server1.cluster.com:27888,server2.cluster.com:27888,server3.cluster.com:27888"
net:
 port:      27017
 ssl:
      mode:             requireSSL
      PEMKeyFile:       /root/mongodb/keys/server1.pem
      clusterFile:      /root/mongodb/keys/server1.pem
      CAFile:           /root/mongodb/keys/mongodb-CA-cert.crt
      weakCertificateValidation: false
      allowInvalidCertificates:  false
security:
 clusterAuthMode: x509
systemLog:
  destination: file
  path:     /root/mongodb/logs/mongos.log
  logAppend:   true

Kopieren Sie die Konfigurationsdatei auf beide Server (unter Angabe der entsprechenden PEM-Zertifikate) und führen Sie den folgenden Befehl aus:

mongos --config  /root/mongodb/cfg/mongos.conf

Überprüfen Sie die Richtigkeit unserer Aktionen. Stellen Sie eine Verbindung zu Mongos her und authentifizieren Sie sich mit dem Root-Benutzerzertifikat, das wir dem Konfigurationsreplikat hinzugefügt haben (denken Sie daran, dass der Benutzer des Replikatreplikats ein Clusterbenutzer ist).

mongo admin --ssl --sslCAFile /root/mongodb/keys/mongodb-CA-cert.crt --sslPEMKeyFile /root/mongodb/keys/rootuser.pem --host server1.cluster.com --port 27017

Durch die Inschrift „Mongos>“ sehen wir, mit wem wir verbunden sind, dann ist alles in Ordnung.

mongos> db.getSiblingDB("$external").auth({
   mechanism:"MONGODB-X509",
   user: "CN=root,OU=StatisticsClient,O=SomeSystems,L=Moscow,ST=MoscowRegion,C=RU"
})

(Wir erwarten die positive "1" in der Ausgabe.)

Im Allgemeinen "mag" die Monga nicht, wenn sie sich von der Wurzel aus mit ihr verbindet, und wird Sie in diesem Fall darüber informieren, dass dies aus Sicherheitsgründen nicht sinnvoll ist. Wenn Sie mit einem echten Cluster arbeiten, empfehle ich daher auch, einen Benutzer (natürlich mit einem separaten Zertifikat) hinzuzufügen, der mit der integrierten Rolle userAdminAnyDatabase ausgestattet ist . Diese Rolle verfügt über fast alle Rechte, die zur Ausführung von Verwaltungsaufgaben erforderlich sind.

Ich denke, hier lohnt es sich, ein Beispiel für die Erstellung eines Zertifikats eines anderen Benutzers zu geben. Dieser Benutzer hat nur Zugriff auf die Analysedatenbank , und alle Anwendungen unseres Dienstes werden in seinem Namen mit dem Cluster verbunden.

Wir gehen also in das Verzeichnis, in dem sich unsere Zertifizierungsstelle befindet, und erstellen einen Schlüssel und eine Zertifikatsignierungsanforderung für einen neuen Benutzer, die wir als Analyticsuser bezeichnen :

server1.cluster.com:~/mongodb/keys# openssl req -new -nodes -newkey rsa:2048 -keyout analyticsuser.key -out analyticsuser.csr
Generating a 2048 bit RSA private key
......................+++
.........................................+++
writing new private key to 'analyticsuser.key'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]: RU
State or Province Name (full name) [Some-State]: MoscowRegion
Locality Name (eg, city) []: Moscow
Organization Name (eg, company) [Internet Widgits Pty Ltd]: SomeSystems
Organizational Unit Name (eg, section) []: StatisticsClient
Common Name (e.g. server FQDN or YOUR name) []: analyticsuser
Email Address []:
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

Wir unterschreiben das Zertifikat:

server1.cluster.com:~/mongodb/keys# openssl x509 -CA mongodb-CA-cert.crt -CAkey mongodb-private.key -CAcreateserial -req -days 36500 -in analyticsuser.csr -out analyticsuser.crt
Signature ok
subject=/C=RU/ST=MoscowRegion/L=Moscow/O=SomeSystems/OU=StatisticsClient/CN=analyticsuser
Getting CA Private Key
Enter pass phrase for mongodb-private.key:

Erstellen Sie eine PEM-Datei:

server1.cluster.com:~/mongodb/keys# cat analyticsuser.key analyticsuser.crt > analyticsuser.pem

Mal sehen, welches Fach unser Zertifikat hat:

server1.cluster.com:~/mongodb/keys# openssl x509 -in rootuser.pem -inform PEM -subject -nameopt RFC2253
subject= CN=analyticsuser,OU=StatisticsClient,O=SomeSystems,L=Moscow,ST=MoscowRegion,C=RU
-----BEGIN CERTIFICATE-----

Stellen Sie als Benutzer mit Administratorrechten eine Verbindung zum Cluster (Mongos) her und fügen Sie einen neuen Benutzer hinzu:

mongos> db.getSiblingDB("$external").runCommand({createUser: "CN=analyticsuser,OU=StatisticsClient,O=SomeSystems,L=Moscow,ST=MoscowRegion,C=RU",
roles: [{role: "readWrite", db: "analytics"}]
})

Bitte beachten Sie, dass wir dem Benutzer analysebenutzer Lese- und Schreibberechtigungen nur für eine Analysedatenbank erteilt haben. Dadurch wird der Cluster vor möglichen (rücksichtslosen oder böswilligen) Aktionen externer Anwendungen auf die Einstellungen der Analysedatenbank selbst und des gesamten Clusters geschützt.

Scherben


In unserem Fall teilt Sharding die hochgeladene Statistiksammlung an einem bestimmten Index - dem Shard-Schlüssel zwischen mehreren Shards, den wir in Kürze hinzufügen werden. Wenn das Sharding für eine Sammlung aktiviert ist, wird der gesamte Satz seiner Dokumente in n Teile unterteilt, die als Chunks bezeichnet werden . Die Anzahl der Blöcke, in die die Sammlung aufgeteilt wird, wenn das Sharding aktiviert ist, und wie oft neue Blöcke gebildet werden, hängt von der Datenmenge in Ihrer Sammlung sowie vom Parameter für die Blockgröße ab , der sich auf die Größe des Blocks auswirkt und standardmäßig 64 MB beträgt. Wenn Sie in Ihrem Cluster eine andere Blockgröße angeben möchten, muss dies erfolgen, bevor Sie das Sharding für diese Sammlungen aktivieren, da Die neue Blockgröße wird nur auf neu gebildete Blöcke angewendet.

Um die Größe des Blocks zu ändern, stellen wir mit einem Superuser-Zertifikat eine Verbindung zum Mongos her und übergeben die Authentifizierung. Im Allgemeinen kann die Authentifizierung mit der Eingabe kombiniert werden, indem der Mechanismus (Argument authenticationMechanism ), eine Datenbank, die für die Zertifikatauthentifizierung ( authenticationDatabase ) verantwortlich ist, und direkt dem Benutzer, dem das Zertifikat gehört ( u ), angegeben wird. Für unseren Superuser (root) hat der Befehl "connect + authentication" die folgende Form:

mongo --ssl --sslCAFile /root/mongodb1/keys/mongodb-CA-cert.crt --sslPEMKeyFile /root/mongodb1/keys/rootuser.pem --host server1.cluster.com --port 27017 --authenticationMechanism "MONGODB-X509" --authenticationDatabase "$external" -u “CN=root,OU=StatisticsClient,O=SomeSystems,L=Moscow,ST=MoscowRegion,C=RU”

Wählen Sie nach erfolgreicher Anmeldung die Konfigurationssammlung aus und ändern Sie den gewünschten Parameter:

mongos> use config
mongos> db.settings.save({_id: "chunksize", value: NumberLong(32)})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

Wir haben gerade die Blockgröße auf ruckartige 32 MB eingestellt. Sie können den aktuellen Wert dieser Einstellung mit dem folgenden Befehl überprüfen:

mongos> db.settings.find({'_id':"chunksize" })
{ "_id" : "chunksize", "value" : NumberLong(32) }

Um Shards zu verwalten (Sie müssen sie zuerst hinzufügen), müssen Sie als Benutzer eine Verbindung mit der integrierten ClusterAdmin- Rolle herstellen . Erstellen Sie ein Zertifikat für den Clusteradministrator:

server1.cluster.com:~/mongodb/keys# openssl req -new -nodes -newkey rsa:2048 -keyout clusterAdmin.key -out aclusterAdmin.csr
Generating a 2048 bit RSA private key
................+++
.......................................+++
writing new private key to 'clusterAdmin.key'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]: RU
State or Province Name (full name) [Some-State]: MoscowRegion
Locality Name (eg, city) []: Moscow
Organization Name (eg, company) [Internet Widgits Pty Ltd]: SomeSystems
Organizational Unit Name (eg, section) []: Statistics
Common Name (e.g. server FQDN or YOUR name) []: clusteradmin
Email Address []:
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

server1.cluster.com:~/mongodb/keys# openssl x509 -CA mongodb-CA-cert.crt -CAkey mongodb-private.key -CAcreateserial -req -days 36500 -in clusterAdmin.csr -out clusterAdmin.crt
Signature ok
subject=/C=RU/ST=MoscowRegion/L=Moscow/O=SomeSystems/OU=Statistics/CN=clusteradmin
Getting CA Private Key
Enter pass phrase for mongodb-private.key:

server1.cluster.com:~/mongodb/keys# cat clusterAdmin.key clusterAdmin.crt > clusterAdmin.pem

server1.cluster.com:~/mongodb/keys# openssl x509 -in clusterAdmin.pem -inform PEM -subject -nameopt RFC2253
subject= CN=clusteradmin,OU=StatisticsClient,O=SomeSystems,L=Moscow,ST=MoscowRegion,C=RU
-----BEGIN CERTIFICATE-----

Nichts Ungewöhnliches für uns, aber vergessen Sie nicht , die angeben , OU andere als OU für die Cluster - Mitglieder angegeben.

Stellen Sie nun erneut eine Verbindung zu Mongos her, authentifizieren Sie sich als Root und fügen Sie einen neuen Benutzer hinzu - den Cluster-Administrator:

mongos> db.getSiblingDB("$external").runCommand({
createUser: "CN=clusteradmin,OU=StatisticsClient,O=SomeSystems,L=Moscow,ST=MoscowRegion,C=RU",
roles: [{role: "clusterAdmin", db: "admin"}]
})

Wir stellen unter dem Clusteradministrator erneut eine Verbindung zu Mongos her (die Authentifizierung ist im Verbindungsbefehl enthalten):

mongo --ssl --sslCAFile /root/mongodb1/keys/mongodb-CA-cert.crt --sslPEMKeyFile /root/mongodb1/keys/clusterAdmin.pem --host server1.cluster.com --port 27017 --authenticationMechanism "MONGODB-X509" --authenticationDatabase "$external" -u “CN=clusteradmin,OU=StatisticsClient,O=SomeSystems,L=Moscow,ST=MoscowRegion,C=RU”

Fügen Sie Shards hinzu, die im Replikat von Sets angegeben sind, mit Ausnahme von Arbiter-Instanzen:

mongos> sh.addShard("rs0/server1.cluster.com:27000,server2.cluster.com:27000")
mongos> sh.addShard("rs1/server1.cluster.com:27001,server2.cluster.com:27001")
mongos> sh.addShard("rs2/server1.cluster.com:27002,server2.cluster.com:27002")

Wenn mit dem Hinzufügen von Shards alles gut gelaufen ist, können wir den aktuellen Status mit dem Sharding-Befehl anzeigen:

mongos> sh.status()
--- Sharding Status ---
 sharding version: {
   "_id" : 1,
   "minCompatibleVersion" : 5,
   "currentVersion" : 6,
   "clusterId" : ObjectId("5795284cd589624d4e36b7d4")
}
 shards:
   {  "_id" : "rs0",  "host" : "rs0/server1.cluster.com:27100,server2.cluster.com:27200" }
   {  "_id" : "rs1",  "host" : "rs1/server1.cluster.com:27101,server2.cluster.com:27201" }
   {  "_id" : "rs2",  "host" : "rs2/server1.cluster.com:27102,server2.cluster.com:27202" }
 active mongoses:
   "3.2.8" : 1
 balancer:
   Currently enabled:  yes
   Currently running:  no
   Failed balancer rounds in last 5 attempts:  0
   Migration Results for the last 24 hours:
       No recent migrations
 databases:

Wir sehen unsere Shards, wir sehen den Status des Balancers - er ist aktiviert, aber er ist jetzt inaktiv, da er noch keine Daten für die Migration von Chunks enthält, die er zwischen den verfügbaren Shards verteilen würde. Dies sagt uns die leere Liste "Datenbanken". Daher haben wir einen Shard-Cluster erstellt, aber standardmäßig ist für alle Shards aller Datenbanken das Sharding deaktiviert. Es ist in zwei Stufen unterteilt:

Schritt 1. Schalten Sie die Scherbe für die gewünschte Basis ein. In unserem Fall handelt es sich um Analysen:

mongos> sh.enableSharding("statistics")

Überprüfen Sie das Ergebnis:

mongos> sh.status()
--- Sharding Status ---
 sharding version: {
   "_id" : 1,
   "minCompatibleVersion" : 5,
   "currentVersion" : 6,
   "clusterId" : ObjectId("5795284cd589624d4e36b7d4")
}
 shards:
   {  "_id" : "rs0",  "host" : "rs0/server1.cluster.com:27000,server2.cluster.com:27000" }
   {  "_id" : "rs1",  "host" : "rs1/server1.cluster.com:27001,server2.cluster.com:27001" }
   {  "_id" : "rs2",  "host" : "rs2/server1.cluster.com:27002,server2.cluster.com:27002" }
 active mongoses:
   "3.2.8" : 1
 balancer:
   Currently enabled:  yes
   Currently running:  no
   Failed balancer rounds in last 5 attempts:  0
   Migration Results for the last 24 hours:
       No recent migrations
 databases:
   {  "_id" : "analytics",  "primary" : "rs2",  "partitioned" : true }

Die Analysedatenbank sollte in der Liste der Datenbanken erscheinen. Wir sehen auch, dass der Shard "rs2" dieser Datenbank als primärer Shard zugewiesen wurde (nicht zu verwechseln mit dem PRIMARY Set-Replikat). Dies bedeutet, dass alle Sammlungsdokumente mit deaktiviertem Sharding vollständig auf diesem primären Shard (rs2) gespeichert werden.

Schritt 2. Aktivieren Sie das Sharding für die Sammlung.

Wie bereits erwähnt, benötigt eine Monga einen Schlüsselindex - einen Sharding-Schlüssel, um die gesamte Sammlung von Dokumenten einer Shardable-Sammlung in Blöcke aufzuteilen. Seine Wahl ist eine sehr verantwortungsvolle Aufgabe, die mit Bedacht angegangen werden muss und sich an den Anforderungen Ihrer Implementierung und Ihres gesunden Menschenverstandes orientiert. Der Index, nach dem die Sammlung in Blöcke unterteilt wird, wird aus vorhandenen Indizes ausgewählt oder absichtlich zur Sammlung hinzugefügt. Auf die eine oder andere Weise muss zum Zeitpunkt der Aktivierung des Shardings ein dem Schlüssel entsprechender Index in der Sammlung vorhanden sein. Der Sharding-Schlüssel unterwirft dem entsprechenden Index keine besonderen Einschränkungen. Falls erforderlich, kann es zusammengesetzt werden, zum Beispiel {"s": 1, "ts": -1} .

Nachdem wir uns für den benötigten Index entschieden haben, erstellen wir ihn und geben ihn als Sharding-Schlüssel für die Statistiksammlung in der Analysedatenbank an. Wie ich bereits sagte, ist das repräsentativste Feld unserer Statistiksammlung die Sensorkennung, das Feld s . Wenn Sie den entsprechenden Index in der Sammlung noch nicht erstellt haben, müssen Sie ihn erstellen:

mongos> use analytics
mongos> db.statistics.ensureIndex({"s":1})

Aktivieren Sie das Sammlungssharding mit dem Schlüssel-Sharding-Index:

mongos> sh.shardCollection("analytics.statistics", {"s":1})

Von nun an können wir wirklich über das Sharding von Daten in unserem Cluster sprechen. Nachdem das Sharding für die Sammlung aktiviert wurde, wird es in Blöcke unterteilt (die Menge hängt von der Größe der Daten und der Größe des Blocks selbst ab), die sich zunächst im PRIMARY-Shard befinden und dann während des Ausgleichsprozesses (Migrationsprozesses) zwischen anderen Shards aufgeteilt werden. Der Ausgleichsprozess ist meiner Meinung nach sehr gemächlich. In unserem Fall wurde eine Sammlung von 3 Millionen Datensätzen mehr als eine Woche lang auf die drei Shards verteilt.

Führen Sie nach einiger Zeit den Befehl sh.status () erneut aus und sehen Sie, was sich geändert hat:

mongos> sh.status()
--- Sharding Status ---
 sharding version: {
   "_id" : 1,
   "minCompatibleVersion" : 5,
   "currentVersion" : 6,
   "clusterId" : ObjectId("5773899ee3456024f8ef4895")
}
 shards:
   {  "_id" : "rs0",  "host" : "rs0/server1.cluster.com:27000,server2.cluster.com:27000" }
   {  "_id" : "rs1",  "host" : "rs1/server1.cluster.com:27001,server2.cluster.com:27001" }
   {  "_id" : "rs2",  "host" : "rs2/server1.cluster.com:27002,server2.cluster.com:27002" }
 active mongoses:
   "3.2.8" : 1
 balancer:
   Currently enabled:  yes
   Currently running:  yes
       Balancer lock taken at Sun Jul 29 2016 10:18:32 GMT+0000 (UTC) by MongoDB:27017:1468508127:-1574651753:Balancer
   Collections with active migrations:
       statistic.statistic started at Sun Jul 29 2016 10:18:32 GMT+0000 (UTC)
   Failed balancer rounds in last 5 attempts:  0
   Migration Results for the last 24 hours:
       3 : Success
       2 : Failed with error 'aborted', from rs2 to rs0
 databases:
   {  "_id" : "analytics",  "primary" : "rs2",  "partitioned" : true }
       analytics.statistics
             shard key: { "s" : 1 }
             unique: false
             balancing: true
             chunks:
                   rs0    1
                   rs1    2
                   rs2    21
             too many chunks to print, use verbose if you want to force print

In der Analysedatenbank, für die wir zuvor das Sharding aktiviert haben, wurde eine Statistiksammlung angezeigt, in der der aktuelle Shard-Schlüssel angezeigt wird. In der Ausgabe finden Sie auch die Verteilung der Chunks nach Shards. Wenn Sie eine kleine Anzahl von Chunks in der Sammlung haben, wird auch eine kurze Zusammenfassung der Chunks angezeigt. Außerdem finden Sie im Balancer-Bereich Informationen zur erfolgreichen Migration von Chunks oder zu Fehlern am letzten Tag.

Supervisor


Nach der Installation des Standard-MongoDB-Community-Pakets wird der Mongodb-Dienst auf unserem System angezeigt und repräsentiert die "Boxed" -Version des Servers. Dieser Dienst wird standardmäßig nach der Installation von MongoDB gestartet.

Beim Starten des Dienstes wird ein Dämonisierungsskript bereitgestellt, das sich entlang des Pfads befindet: /etc/init.d/mongod. Wie Sie vielleicht bemerkt haben, müssen wir mehrere Instanzen von mongod und einen mongos für die Datenserver server1.cluster.com und server2.cluster.com auf demselben Computer ausführen.

Auf den ersten Blick gibt es eine fertige Lösung am Beispiel des Skripts /etc/init.d/mongod, aber die Option mit dem Supervisor-Dienstprogramm erschien mir bequemer und transparenter.

Supervisor gibt uns auch ein kleines Plus in Form der Möglichkeit, alle Befehle unseres Mongo {d / s} gleichzeitig zu starten und zu stoppen:

supervisorctl start all
supervisorctl stop all

(vorausgesetzt, die Maschine verfügt nicht mehr über andere vom Supervisor gestartete Anwendungen - wie in unserem Fall).
Das Supervisor-Paket wird auf den meisten Betriebssystemen der Linux-Familie aus dem Standard-Repository installiert. In meinem Fall (Debian 8) ist der Befehl relevant:

# apt-get install supervisor

Um den Supervisor zu konfigurieren, müssen wir für jede gestartete Anwendung eine Konfiguration in einer separaten Konfigurationsdatei erstellen oder alle Konfigurationen in einer kombinieren.

Hier ist ein Beispiel für eine Mongod-Konfiguration für das RS0-Replikat:

#
# /etc/supervisor/conf.d/mongod-rs0.conf
#
[program:mongod-rs0]
command=mongod --config /root/mongodb/cfg/rs0.conf
user=root 
stdout_logfile=/root/mongodb/logs/supervisor/mongod-rs0-stdout.log
redirect_stderr=true
autostart=true
autorestart=true
stopwaitsecs=60

In eckigen Klammern definieren wir die Kennung der Anwendung, mit der wir starten oder stoppen. Der Befehlsparameter legt tatsächlich den Befehl fest, den der Supervisor ausführen muss - mongod empfängt die Konfigurationsdatei. Geben Sie als Nächstes den Benutzer an, in dessen Namen der Prozess gestartet wird. Parameter stdout_logfile - Legt den Pfad zur Ausgabedatei fest, in die der Supervisor schreibt. Dies ist nützlich, wenn etwas schief geht und Sie verstehen müssen, warum der Supervisor die Anwendung nicht startet.

redirect_stderr weist den Supervisor an, den Fehlerstrom in dieselbe Protokolldatei wie oben angegeben umzuleiten. Stellen Sie als Nächstes sicher, dass Sie die Optionen Autostart und Autorestart einschließenIm Falle eines nicht autorisierten Serverneustarts und des Absturzes des Prozesses.

Es ist auch nützlich, den Parameter stopwaitsecs zu ändern , wodurch der Supervisor beim Stoppen der Anwendung auf die angegebene Anzahl von Sekunden wartet. Wenn die Anwendung beendet wird, sendet der Supervisor standardmäßig ein TERM-Signal und wartet dann 10 Sekunden. Wenn die Anwendung nach Ablauf nicht abgeschlossen ist, sendet sie bereits ein KILL-Signal, das von der Anwendung nicht ignoriert werden kann und theoretisch zu Datenverlust führen kann. Daher wird empfohlen, das Standardintervall für das Warten auf den Abschluss der Anwendung zu verlängern.

Die generierte Konfigurationsdatei muss im entsprechenden Supervisor-Verzeichnis abgelegt werden. In der Regel lautet sie unter Linux /etc/supervisor/conf.d/ .

Wenn alles fertig ist, müssen Sie die Supervisor-Konfiguration mit dem folgenden Befehl aktualisieren:

# supervisorctl reload


Das Stoppen, Starten und Überprüfen des Status einer konfigurierten Anwendung wird entsprechend durch die folgenden Befehle ausgeführt:

# supervisorctl stop mongod-rs0
# supervisorctl start mongod-rs0
# supervisorctl status mongod-rs0

Nach dem Wechsel zur Verwendung von Supervisor ist es wichtig zu verhindern, dass der Standard-Mongodb-Dienst gestartet wird, der möglicherweise Port 27017 (z. B. nach dem Neustart des Servers) übernimmt, auf dem wir Mongos ausführen. Dazu können Sie einfach das Skript /etc/init.d/mongod entfernen.

Eine nützliche Information


Aktivieren von Sharding für große Sammlungen

Die zum Zeitpunkt der Migration am meisten geladene Sammlung unserer Datenbank umfasste etwas mehr als 3 Millionen Datensätze, und während der Tests hat die Aufnahme von Sharding für eine solche Sammlung (Befehl sh.shardCollection ()) einwandfrei funktioniert. Es wurden jedoch auch Tests an einer künstlich erzeugten Datenbank mit 100 Millionen ähnlichen Datensätzen durchgeführt. Bei einem solchen Volume endet der Befehl sh.shardCollection () nach einer Weile mit dem Fehler "timeout". Die Lösung für diese Situation ist das folgende Verfahren:

Schritt 1. Importieren Sie die gesamte Datenbank in den Cluster.
Schritt 2. Erstellen Sie auf dem Produktionsserver oder bereits im Cluster einen Speicherauszug einer separaten "großen" Sammlung, zum Beispiel:

mongoexport --db analytics --collection statistics --out statistics.json

Schritt 3. Löschen Sie die "große" Sammlung im Cluster:

> use analytics
> db.statistics.drop()

Schritt 4. Erstellen Sie eine leere "große" Sammlung und fügen Sie einen Index hinzu, anhand dessen wir Folgendes teilen:

> db.analytics.ensureIndex({"s":1})

Schritt 5. Schalten Sie das Sharding der Sammlung mit dem Sharding-Schlüssel ein:

> sh.shardCollection("analytics.statistics", {"s":1})

Schritt 6. Und jetzt importieren wir die Sammlungsdaten:

mongoimport --db analytics --collection statistics --file statistics.json

Diese Technik hat bei mir funktioniert, aber denken Sie daran, dass das Exportieren / Importieren einer großen Sammlung im JSON-Format kein schneller Prozess ist.

Datenbanksicherung

Das Erstellen einer Sicherungskopie aller Komponenten eines Sharded-Clusters ist ein sehr kompliziertes Verfahren, bei dem der Balancer ausgeschaltet sein muss (er kann während der Migration nicht zwangsweise gestoppt werden) und die SECONDARY-Knoten auf jedem Shard für die nachfolgende Sicherung blockiert werden. Weitere Informationen zum Durchführen einer vollständigen Sicherung finden Sie in der offiziellen Dokumentation .

Für uns selbst haben wir das Sicherungsproblem gelöst, indem wir regelmäßig den üblichen Datendump der erforderlichen Datenbanken erstellt haben. Ich werde die Implementierung dieses Verfahrens hier beschreiben. Wir werden

die Analysedatenbank mit dem Dienstprogramm mongodump sichern, das Teil des MongoDB Community-Pakets ist.

MongoDB verfügt über eine spezielle integrierte Sicherungsrolle, die nur über minimale Rechte zum Durchführen einer Datensicherung verfügt. Um dieses Verfahren durchzuführen, erstellen wir einen einzelnen Benutzer und generieren traditionell zunächst ein x.509-Zertifikat für ihn. Ich werde nicht das gesamte Verfahren zum Generieren eines Zertifikats angeben, es wurde wiederholt im Artikel gezeigt. Ich werde nur sagen, dass Sie das folgende Thema erhalten sollten:

CN=backuper,OU=StatisticsClient,O=SomeSystems,L=Moscow,ST=MoscowRegion,C=RU

Stellen Sie nun eine Verbindung zum Cluster her und erstellen Sie den Backuper-Benutzer mit der integrierten Sicherungsrolle:

mongos> db.getSiblingDB("$external").runCommand({
createUser: "CN=backuper,OU=StatisticsClient,O=SomeSystems,L=Moscow,ST=MoscowRegion,C=RU",
roles: [{role: "backup", db: "admin"}]
})

Nach dem Erstellen des Benutzers können Sie versuchen, unsere Analysedatenbank zu sichern. Die Befehlsargumente für das Dienstprogramm mongodump ähneln der Verbindung mit der Authentifizierung. Zusätzlich wird nur der Datenbankname ( --db ) angegeben , das Verzeichnis, in dem der Speicherauszug ( -o ) gespeichert wird , sowie das Argument --gzip , das angibt, dass alle Speicherauszugsdateien komprimiert werden sollen:

mongodump --ssl --sslCAFile “/root/mongodb/keys/mongodb-CA-cert.crt” --sslPEMKeyFile “/root/mongodb/keys/backuper.pem” -u "CN=backuper,OU=StatisticsClient,O=SomeSystems,L=Moscow,ST=MoscowRegion,C=RU" --host server1.cluster.com --port 27017 --authenticationMechanism "MONGODB-X509" --authenticationDatabase "$external" --db analytics --gzip -o "/path/to/backup/"

Ein bisschen Code ...


Am Ende des Artikels möchte ich Beispiele für Programmcode veröffentlichen, in denen ich die Verbindung zum erstellten Cluster demonstrieren werde. Da unser Service, der mit dem Cluster arbeitet, aus vielen Teilen besteht, die in C ++ und Python geschrieben sind, werden die Beispiele in diesen wunderbaren Programmiersprachen sein.

Beginnen wir also mit einem C ++ - Beispiel. Das folgende Verbindungsbeispiel ist für den offiziellen MongoDB-Treiber mongodb-cxx-driver-Legacy-1.1.1 relevant .

#include 
#include 
...
mongo::DBClientConnection client(true); // включаем автореконнект
try {
      // заполняем структуру опций SSL подключения
      mongo::client::Options options;
      options.setSSLMode(mongo::client::Options::SSLModes::kSSLRequired);
      options.setSSLCAFile("/path_to_certs/mongodb-CA-cert.crt");
      options.setSSLPEMKeyFile("/path_to_certs/analyticsuser.PEM");
      mongo::Status status = mongo::client::initialize(options);
      mongo::massertStatusOK(status); // проверим, все ли в порядке
      client.connect("www.server1.cluster.com:27017"); // адрес и порт хоста на котором запущен mongos
      // настройки аутентификации: бд, пользователь, механизм
      mongo::BSONObjBuilder auth_params;
      auth_params.append("db", "$external");
      auth_params.append("user", "CN=username,OU=StatisticsClient,O=SomeSystems,L=Moscow,ST=MoscowRegion,C=RU");
      auth_params.append("mechanism", "MONGODB-X509");
      client.auth(auth_params.obj()); // выполняем аутентификацию
} catch (const mongo::DBException &e) {
      std::cout << "DBException : " << e.toString() << std::endl;
}
...

Bevor wir eine Verbindung zum Datenbankhost herstellen, müssen wir den Client mithilfe der Struktur mongo :: client :: Options initialisieren und dabei die SSL-Anforderungsebene (kSSLRequired) , das öffentliche CA-Zertifikat (mongodb-CA-cert.crt) und die angehängte PEM-Datei angeben an den Clusterbenutzer (in diesem Fall ist dies der zuvor erstellte Analytics-Benutzer).

Als nächstes stellen wir eine Verbindung zur Datenbank her und wenn alles durchläuft, authentifizieren wir uns erfolgreich. Achten Sie auf den Namen der Datenbank, über die die Authentifizierung erfolgt - "$ external", da der Name, den wir dem Betreff aus dem Benutzerzertifikat übergeben, den Authentifizierungsmechanismus nicht vergessen darf. Wir sehen auch, dass wir das Passwort seitdem nicht mehr übermitteln Unsere Authentifizierung erfolgt extern - durch Zertifikatauthentifizierung.

Im Webpart des in Python geschriebenen Projekts ist der reine Pymongo-Treiber beteiligt, und das Objektmodell wird mithilfe des Mongoengine-Frameworks erstellt.

Ein Beispiel für Pymongo:

import ssl
db_hosts="server1.cluster.com:27017,server2.cluster.com:27017"
db_port=None
client = MongoClient(db_hosts,
             db_port,
             read_preference=ReadPreference.NEAREST,
             ssl=True,
             ssl_certfile="/path_to_certs/analyticsuser.PEM",
             ssl_cert_reqs=ssl.CERT_REQUIRED,
             ssl_ca_certs="/path_to_certs/mongodb-CA-cert.crt")
db = client[db_name]
db.authenticate(name=db_user, source="$external", mechanism="MONGODB-X509")

Nichts Besonderes - wir übertragen auch ein öffentliches CA-Zertifikat und eine Client-PEM-Datei. Die Variable db_hosts verdient hier Beachtung - dies ist eigentlich eine Verbindungszeichenfolge, in der Adressen und Ports, an denen Mongos verfügbar sind, durch Kommas getrennt sind. Den Port-Parameter (db_port) können Sie in unserem Fall nicht angeben, ich habe ihn der Übersichtlichkeit halber angegeben. Der Pymongo-Treiber, der auf diese Weise verbunden ist, wenn die erste Adresse nicht verfügbar ist, versucht automatisch, die Verbindung zur zweiten Adresse wiederherzustellen und umgekehrt. Die Praxis zeigt, dass, wenn beide Server bei der ersten Verbindung verfügbar sind, die Adressen der Reihe nach ausgewählt werden, d. H. Die erste ist eine Verbindung zu server1.cluster.com:27017 .

Beim Testen dieses Pymogo-Verhaltens wurde jedoch festgestellt, dass vor der automatischen Wiederverbindung die Ausnahme pytmogo.errors.AutoReconnect generiert wird. Um diese Situation zu bewältigen, wurde ein kleiner Dekorator geschrieben, mit dem Sie beispielsweise die Funktionen zum Anzeigen einer Statistikseite oder einer API-Anforderung zum Lesen von Daten

umbrechen können: Importieren von Wraps
aus pymongo.errors Importieren der AutoReconnect-
Importzeit

def pymongo_reconnect(attempts=5):
  def decorator(f):
      @wraps(f)
      def decorated_function(*args, **kwargs):
          tries_reconnect = attempts
          if tries_reconnect <= 0:
              tries_reconnect = 1
          while tries_reconnect:
              try:
                  return f(*args, **kwargs)
              except AutoReconnect as ar:
                  tries_reconnect -= 1
                  print("Caught AutoReconnect exception.")
                  if tries_reconnect <= 0:
                      raise ar
                  time.sleep(0.1)
                  print("Attempt to reconnect (%d more)...\n" % tries_reconnect)
                  continue
      return decorated_function
  return decorator

Der Dekorateur gibt eine Reihe von Versuchen aus, die Funktion auszuführen (in diesem Fall 5), und nachdem alle Versuche ausgeführt wurden, endet sie mit einer Ausnahme.

Sie sollten auch einige Wörter zum Parameter read_preference aus dem Verbindungsbeispiel sagen. read_preference teilt dem Treiber mit, welche Datenleseregel in dieser Verbindung verwendet werden soll (das Schreiben erfolgt immer in PRIMARY, was logisch ist). Die folgenden möglichen Werte sind verfügbar:

PRIMARY - Liest immer Daten vom primären Mitglied des Shard-Replikats. PRIMARY_PREFERRED - wird vom primären Mitglied des Shard-Replikats gelesen, aber wenn es unmöglich ist, vom sekundären Mitglied zu lesen;
SEKUNDÄR - nur von einem sekundären Mitglied der Scherbe gelesen;
SECONDARY_PREFERRED - Lesen Sie so viel wie möglich vom sekundären Shard, aber wenn dies vom primären nicht möglich ist.
NEAREST - Lesen Sie alles, was verfügbar ist (wie in der Pymongo-Dokumentation angegeben), und in der Dokumentation der Monga selbst wird ausführlich beschrieben, dass nicht nur der erste Teilnehmer des Replikats verwendet wird, sondern derjenige mit der geringsten Netzwerkverzögerung - nur Ping, unabhängig davon, wer primäre oder sekundäre Daten bereitstellt.

Somit gibt uns dieser Parameter einerseits die Möglichkeit, die PRIMARY-Instanzen von der Last der Leseanforderungen zu entlasten, andererseits kann er zu irrelevanten / inkonsistenten Daten führen, weil SECONDARY-Instanzen haben irgendwie eine Verzögerung bei der Synchronisierung mit PRIMARY (dies hängt von der Konfiguration Ihres Replikats und der Verzögerung ab). Daher sollten Sie diese Option mit Vorsicht und basierend auf den Annahmen und Einschränkungen Ihres Systems auswählen.

Es sollte auch beachtet werden, dass pymongo eine OperationFailure-Ausnahme auslöst, wenn es nicht möglich ist, die PRIMARY- oder SECONDARY-Einstellungen zu erfüllen. Daher muss dieses Verhalten bei Verwendung dieser Optionen berücksichtigt werden.

Mit dem Mongoengine-Paket wurde alles trauriger. Das erste, was ich im Projekt sah, war der Verbindungspunkt zur Datenbank über das Mongoengine-Paket:

connect('default', host, port)

OK, ich dachte: "Jetzt werde ich die verbleibenden Verbindungsparameter auf mongoengine.connect übertragen, wie es bei pymongo war, und dies ist entschieden." Aber meine Bestrebungen waren vergebens, da ich in mongoengine.connect nicht die Parameter gefunden habe, die ich brauchte - es ist nur ein allgemeiner Wrapper für eine Funktion mit einer breiteren Liste von Argumenten: mongoengine.register_connection. Unter den Parametern dieser Funktion war auch keine Notwendigkeit vorhanden, über die der MONGODB-X509-Autorisierungsmechanismus auf die Verbindung übertragen werden konnte. Ich habe ein paar vergebliche Versuche unternommen, in der Hoffnung, dass das Framework „versteht“, was von ihm verlangt wird, aber als ich mich mit dem Quellcode befasste, war ich überzeugt von dem Mangel an gleichmäßiger Unterstützung und der Unfähigkeit, den notwendigen Mechanismus für die Mogoengine „weiterzuleiten“, wo Pymongo ihn versteht (auf dem er tatsächlich basiert) Mongoengine).

Es stellte sich heraus, dass auf Github für diesen Nachteil bereits ein ähnliches Ticket angeboten wurde , das nicht zu Ende gebracht wurde. Deshalb habe ich beschlossen, meine eigene Gabel zu bauen und alles hinzuzufügen, was ich brauchte.

Daher hat die Verbindung mit der x.509-Authentifizierung die folgende Form angenommen:

import ssl
from mongoengine import DEFAULT_CONNECTION_NAME, register_connection
db_hosts="server1.cluster.com:27017,server2.cluster.com:27017"
db_port=None
ssl_config = {
    'ssl': True,
    'ssl_certfile': "/path_to_certs/analyticsuser.PEM",
    'ssl_cert_reqs': ssl.CERT_REQUIRED,
    'ssl_ca_certs': "/path_to_certs/mongodb-CA-cert.crt",
  }
register_connection(alias=DEFAULT_CONNECTION_NAME,
                  name="statistic",
                  host=db_hosts,
                  port=db_port,
                  username="CN=username,OU=StatisticsClient,O=SomeSystems,L=Moscow,ST=MoscowRegion,C=RU",
                  password=None,
                  read_preference=ReadPreference.NEAREST,
                  authentication_source="$external",
                  authentication_mechanism="MONGODB-X509",
                  **ssl_config)

Leider konnte ich bisher nicht mit dem Haupt-Repository von MongoEngine zusammenführen, da nicht passieren Tests auf allen Kombinationen python / pymongo. Bei den neuesten Pool-Anfragen vieler Entwickler habe ich ähnliche Probleme mit denselben Tests festgestellt, sodass sich der Gedanke an eine mögliche Fehlfunktion im „stabilen“ Zweig des Frameworks einschleicht.

Ich hoffe, dass sich die Situation in naher Zukunft verbessern wird, das Problem besser verstanden werden kann und die Authentifizierungsunterstützung für x.509 im Haupt-Repository von MongoEngine angezeigt wird.

Update
Die offizielle Version von mongoengine wurde um Unterstützung für eine Authentifizierungsmechanismusoption erweitert.

Jetzt auch beliebt: