Verwendung von Confd und Etcd zum dynamischen Neukonfigurieren von Diensten in CoreOS

Einführung

Mit CoreOS können Sie auf einfache Weise Dienste in Docker-Containern auf mehreren Computern ausführen. Das Verfahren dazu umfasst normalerweise das Starten einer oder mehrerer Instanzen eines Dienstes und das anschließende Registrieren jeder Instanz beietcd, dem verteilten Schlüsselwertspeicher von CoreOS.

Durch die Nutzung dieses Musters können verwandte Dienste wertvolle Informationen über den Zustand der Infrastruktur erhalten und dieses Wissen nutzen, um über ihr eigenes Verhalten zu informieren. Dies ermöglicht es Diensten, sich dynamisch zu konfigurieren, wenn sich signifikanteetcd-Werte ändern.

In diesem Handbuch wird ein Tool namensconfd erläutert, das speziell entwickelt wurde, um verteilte Schlüsselwertspeicher auf Änderungen zu überwachen. Es wird in einem Docker-Container ausgeführt und dient zum Auslösen von Konfigurationsänderungen und zum erneuten Laden von Diensten.

Voraussetzungen und Ziele

Um dieses Handbuch durcharbeiten zu können, müssen Sie über grundlegende Kenntnisse zu CoreOS und seinen Komponenten verfügen. In früheren Handbüchern haben wir einen CoreOS-Cluster eingerichtet und uns mit einigen Tools vertraut gemacht, die zum Verwalten Ihrer Cluster verwendet werden.

Nachfolgend finden Sie die Anleitungen, die Sie lesen sollten, bevor Sie mit diesem Artikel beginnen. Wir werden das Verhalten einiger der in diesen Handbüchern beschriebenen Dienste ändern. Obwohl es wichtig ist, das Material zu verstehen, sollten Sie mit der Verwendung dieses Handbuchs neu beginnen:

Um sich mit einigen der von uns verwendeten Verwaltungstools vertraut zu machen, sollten Sie außerdem die folgenden Handbücher durchgehen:

Der Leitfaden „So erstellen Sie flexible Services“ ist für diesen Leitfaden besonders wichtig, da die vorgefertigten main + sidekick-Services als Grundlage für den Front-End-Service dienen, den wir in diesem Leitfaden einrichten werden. Wie bereits erwähnt, werden in den obigen Handbüchern zwar die Erstellung von Apache- und Sidekick-Diensten erläutert, es gibt jedoch einige Konfigurationsänderungen für dieses Handbuch, die den Neuanfang erleichtern. Wir werden in diesem Handbuch geänderte Versionen dieser Dienste erstellen.

In diesem Tutorial konzentrieren wir uns auf die Erstellung eines neuen Anwendungscontainers mit Nginx. Dies dient als Reverse-Proxy für die verschiedenen Apache-Instanzen, die wir aus unseren Vorlagendateien erzeugen können. Der Nginx-Container wird mitconfd konfiguriert, um die Dienstregistrierung zu überwachen, für die unsere Sidekick-Dienste verantwortlich sind.

Wir werden mit denselben drei Maschinenclustern beginnen, die wir in dieser Serie verwendet haben.

  • Coreos-1

  • Coreos-2

  • Coreos-3

Wenn Sie mit dem Lesen der vorhergehenden Anleitungen fertig sind und Ihre drei Computercluster verfügbar haben, fahren Sie fort.

Konfiguration der Backend-Apache-Dienste

Zunächst richten wir unsere Backend-Apache-Dienste ein. Dies wird hauptsächlich den letzten Teil des vorherigen Handbuchs widerspiegeln, aber wir werden den gesamten Vorgang hier aufgrund einiger subtiler Unterschiede durchgehen.

Melden Sie sich bei einem Ihrer CoreOS-Computer an, um zu beginnen:

ssh -A core@ip_address

Apache Container Setup

Wir beginnen mit der Erstellung des Apache-Basiscontainers. Dies ist identisch mit der letzten Anleitung, sodass Sie dies nicht erneut ausführen müssen, wenn Sie dieses Image bereits in Ihrem Docker Hub-Konto verfügbar haben. Wir stützen diesen Container auf das Ubuntu 14.04-Container-Image.

Wir können das Basis-Image herunterziehen und eine Container-Instanz starten, indem wir Folgendes eingeben:

docker run -i -t ubuntu:14.04 /bin/bash

Sobald der Container gestartet wird, werden Sie in einebash-Sitzung versetzt. Von hier aus aktualisieren wir den lokalen Paketindex vonaptund installierenapache2:

apt-get update
apt-get install apache2 -y

Wir werden auch die Standardseite festlegen:

echo "

Running from Docker on CoreOS

" > /var/www/html/index.html

Wir können den Container jetzt verlassen, da er sich in dem Zustand befindet, den wir benötigen:

exit

Melden Sie sich bei Docker Hub an oder erstellen Sie ein Konto, indem Sie Folgendes eingeben:

docker login

Sie müssen Ihren Benutzernamen, Ihr Passwort und Ihre E-Mail-Adresse für Ihr Docker Hub-Konto angeben.

Als nächstes erhalten Sie die Container-ID der Instanz, die Sie gerade verlassen haben:

docker ps -l
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                     PORTS               NAMES
1db0c9a40c0d        ubuntu:14.04        "/bin/bash"         2 minutes ago       Exited (0) 4 seconds ago                       jolly_pare

Das hervorgehobene Feld oben ist die Container-ID. Kopieren Sie die Ausgabe, die Sie auf Ihrem eigenen Computer sehen.

Übernehmen Sie nun die Container-ID, Ihren Docker Hub-Benutzernamen und einen Namen für das Image. Hier verwenden wir "Apache":

docker commit 1db0c9a40c0d user_name/apache

Schieben Sie Ihr neues Image auf Docker Hub:

docker push user_name/apache

Jetzt können Sie dieses Bild in Ihren Servicedateien verwenden.

Erstellen der Apache Service Template Unit-Datei

Nachdem Sie einen Container zur Verfügung haben, können Sie eine Vorlageneinheitendatei erstellen, damitfleet undsystemd den Dienst korrekt verwalten können.

Bevor wir beginnen, richten wir eine Verzeichnisstruktur ein, damit wir organisiert bleiben können:

cd ~
mkdir static templates instances

Jetzt können wir unsere Vorlagendatei im Verzeichnistemplateserstellen:

vim templates/[email protected]

Fügen Sie die folgenden Informationen in die Datei ein. Sie können Details zu jeder der von uns verwendeten Optionen abrufen, indem Sie der vorherigen Anleitung zucreating flexible fleet unit files folgen:

[Unit]
Description=Apache web server service on port %i

# Requirements
Requires=etcd.service
Requires=docker.service
Requires=apache-discovery@%i.service

# Dependency ordering
After=etcd.service
After=docker.service
Before=apache-discovery@%i.service

[Service]
# Let processes take awhile to start up (for first run Docker containers)
TimeoutStartSec=0

# Change killmode from "control-group" to "none" to let Docker remove
# work correctly.
KillMode=none

# Get CoreOS environmental variables
EnvironmentFile=/etc/environment

# Pre-start and Start
## Directives with "=-" are allowed to fail without consequence
ExecStartPre=-/usr/bin/docker kill apache.%i
ExecStartPre=-/usr/bin/docker rm apache.%i
ExecStartPre=/usr/bin/docker pull user_name/apache
ExecStart=/usr/bin/docker run --name apache.%i -p ${COREOS_PRIVATE_IPV4}:%i:80 \
user_name/apache /usr/sbin/apache2ctl -D FOREGROUND

# Stop
ExecStop=/usr/bin/docker stop apache.%i

[X-Fleet]
# Don't schedule on the same machine as other Apache instances
Conflicts=apache@*.service

Eine Änderung, die wir hier vorgenommen haben, besteht darin, die private Schnittstelle anstelle der öffentlichen Schnittstelle zu verwenden. Da allen unseren Apache-Instanzen Trafficthrough der Nginx-Reverse-Proxy übergeben wird, anstatt Verbindungen aus dem offenen Web zu verarbeiten, ist dies eine gute Idee. Denken Sie daran, wenn Sie die private Schnittstelle in DigitalOcean verwenden, muss auf dem Server, den Sie hochgefahren haben, das Flag "Private Networking" bei der Erstellung ausgewählt worden sein.

Denken Sie auch daran, dieuser_name so zu ändern, dass sie auf Ihren Docker Hub-Benutzernamen verweisen, um die Docker-Datei korrekt abzurufen.

Erstellen der Sidekick-Vorlagen-Unit-Datei

Nun werden wir dasselbe für den Sidekick-Service tun. Diese werden wir in Erwartung der Informationen, die wir später benötigen, leicht ändern.

Öffnen Sie die Vorlagendatei in Ihrem Editor:

vim templates/[email protected]

Wir werden die folgenden Informationen in dieser Datei verwenden:

[Unit]
Description=Apache web server on port %i etcd registration

# Requirements
Requires=etcd.service
Requires=apache@%i.service

# Dependency ordering and binding
After=etcd.service
After=apache@%i.service
BindsTo=apache@%i.service

[Service]

# Get CoreOS environmental variables
EnvironmentFile=/etc/environment

# Start
## Test whether service is accessible and then register useful information
ExecStart=/bin/bash -c '\
  while true; do \
    curl -f ${COREOS_PRIVATE_IPV4}:%i; \
    if [ $? -eq 0 ]; then \
      etcdctl set /services/apache/${COREOS_PRIVATE_IPV4} \'${COREOS_PRIVATE_IPV4}:%i\' --ttl 30; \
    else \
      etcdctl rm /services/apache/${COREOS_PRIVATE_IPV4}; \
    fi; \
    sleep 20; \
  done'

# Stop
ExecStop=/usr/bin/etcdctl rm /services/apache/${COREOS_PRIVATE_IPV4}

[X-Fleet]
# Schedule on the same machine as the associated Apache service
MachineOf=apache@%i.service

Die obige Konfiguration unterscheidet sich in einigen Punkten von der in der vorherigen Anleitung. Wir haben den mit dem Befehletcdctl set eingestellten Wert angepasst. Anstatt ein JSON-Objekt zu übergeben, wird eine einfache Kombination aus IP-Adresse und Port festgelegt. Auf diese Weise können wir diesen Wert direkt lesen, um die Verbindungsinformationen zu finden, die für den Zugriff auf diesen Dienst erforderlich sind.

Wir haben auch die Informationen angepasst, um die private Schnittstelle anzugeben, wie wir es in unserer anderen Datei getan haben. Lassen Sie dies öffentlich, wenn Ihnen diese Option nicht zur Verfügung steht.

Instanziieren Sie Ihre Services

Jetzt erstellen wir zwei Instanzen dieser Dienste.

Zuerst erstellen wir die symbolischen Links. Wechseln Sie in das von Ihnen erstellte Verzeichnis~/instancesund verknüpfen Sie es, um die Ports zu definieren, auf denen sie ausgeführt werden. Wir möchten einen Dienst auf Port 7777 und einen anderen auf Port 8888 ausführen:

cd ~/instances
ln -s ../templates/[email protected] [email protected]
ln -s ../templates/[email protected] [email protected]
ln -s ../templates/[email protected] [email protected]
ln -s ../templates/[email protected] [email protected]

Jetzt können wir diese Dienste starten, indem wir das Verzeichnis~/instancesanfleet übergeben:

fleetctl start ~/instances/*

Nach dem Start Ihrer Instanzen (dies kann einige Minuten dauern) sollten Sie dieetcd-Einträge sehen können, die Ihre Sidekicks vorgenommen haben:

etcdctl ls --recursive /
/coreos.com
/coreos.com/updateengine
/coreos.com/updateengine/rebootlock
/coreos.com/updateengine/rebootlock/semaphore
/services
/services/apache
/services/apache/10.132.249.206
/services/apache/10.132.249.212

Wenn Sie nach dem Wert eines dieser Einträge fragen, sehen Sie, dass Sie eine IP-Adresse und eine Portnummer erhalten:

etcdctl get /services/apache/10.132.249.206
10.132.249.206:8888

Sie könnencurl verwenden, um die Seite abzurufen und sicherzustellen, dass sie ordnungsgemäß funktioniert. Dies funktioniert nur von Ihrem Computer aus, wenn Sie den Dienst für die Verwendung des privaten Netzwerks konfiguriert haben:

curl 10.132.249.206:8888

Running from Docker on CoreOS

Wir haben jetzt unsere Backend-Infrastruktur eingerichtet. Unser nächster Schritt besteht darin, sich mitconfd vertraut zu machen, damit wir die Position von/services/apacheinetcd auf Änderungen überwachen und Nginx jedes Mal neu konfigurieren können.

Erstellen des Nginx-Containers

Wir werden den Nginx-Container von der gleichen Ubuntu 14.04-Basis starten, die wir für die Apache-Dienste verwendet haben.

Installation der Software

Starten Sie einen neuen Container, indem Sie Folgendes eingeben:

docker run -i -t ubuntu:14.04 /bin/bash

Aktualisieren Sie den Paketcache Ihres lokalenaptund installieren Sie Nginx. Wir müssen auchcurl installieren, da das Basis-Image dies nicht enthält und wir es benötigen, um das stabileconfd-Paket für einen Moment von GitHub zu erhalten:

apt-get update
apt-get install nginx curl -y

Jetzt können wir in unseren Browsern in GitHub zureleases page fürconfd wechseln. Wir müssen den Link zur neuesten stabilen Version finden. Zum Zeitpunkt dieses Schreibens ist diesv0.5.0, dies hat sich jedoch möglicherweise geändert. Klicken Sie mit der rechten Maustaste auf den Link für die Linux-Version des Tools und wählen Sie "Linkadresse kopieren" oder eine ähnliche Option aus.

Verwenden Sie jetzt wieder in Ihrem Docker-Container die kopierte URL, um die Anwendung herunterzuladen. Wir werden dies in das Verzeichnis/usr/local/binetzen. Wir müssenconfd als Ausgabedatei auswählen:

cd /usr/local/bin
curl -L https://github.com/kelseyhightower/confd/releases/download/v0.5.0/confd-0.5.0<^>-linux-amd64 -o confd

Machen Sie die Datei jetzt ausführbar, damit wir sie in unserem Container verwenden können:

chmod +x confd

Wir sollten diese Gelegenheit auch nutzen, um die Konfigurationsstruktur zu erstellen, dieconfderwartet. Dies befindet sich im Verzeichnis/etc:

mkdir -p /etc/confd/{conf.d,templates}

Erstellen Sie eine Confd-Konfigurationsdatei, um Etcd-Werte zu lesen

Nachdem wir unsere Anwendungen installiert haben, sollten wir mit der Konfiguration vonconfd beginnen. Zunächst erstellen wir eine Konfigurationsdatei oder eine Vorlagenressourcendatei.

Konfigurationsdateien inconfd werden verwendet, um den Dienst so einzurichten, dass bestimmteetcd-Werte überprüft und Aktionen eingeleitet werden, wenn Änderungen erkannt werden. Diese verwenden das DateiformatTOML, das einfach zu verwenden und ziemlich intuitiv ist.

Beginnen Sie mit der Erstellung einer Datei in unserem Konfigurationsverzeichnis mit dem Namennginx.toml:

vi /etc/confd/conf.d/nginx.toml

Wir werden unsere Konfigurationsdatei hier ausbauen. Fügen Sie die folgenden Informationen hinzu:

[template]

# The name of the template that will be used to render the application's configuration file
# Confd will look in `/etc/conf.d/templates` for these files by default
src = "nginx.tmpl"

# The location to place the rendered configuration file
dest = "/etc/nginx/sites-enabled/app.conf"

# The etcd keys or directory to watch.  This is where the information to fill in
# the template will come from.
keys = [ "/services/apache" ]

# File ownership and mode information
owner = "root"
mode = "0644"

# These are the commands that will be used to check whether the rendered config is
# valid and to reload the actual service once the new config is in place
check_cmd = "/usr/sbin/nginx -t"
reload_cmd = "/usr/sbin/service nginx reload"

Die obige Datei enthält Kommentare, in denen einige der Grundgedanken erläutert werden. Wir können jedoch die folgenden Optionen erläutern:

Richtlinie Erforderlich? Type Beschreibung

src

Yes

String

Der Name der Vorlage, die zum Rendern der Informationen verwendet wird. Befindet sich dieser außerhalb von/etc/confd/templates, sollte der gesamte Pfad verwendet werden.

dest

Yes

String

Der Dateispeicherort, an dem die gerenderte Konfigurationsdatei abgelegt werden soll.

keys

Yes

Array von Zeichenfolgen

Dieetcd-Schlüssel, die die Vorlage benötigt, müssen korrekt gerendert werden. Dies kann ein Verzeichnis sein, wenn die Vorlage für die Verarbeitung untergeordneter Schlüssel eingerichtet ist.

Inhaber

No

String

Der Benutzername, der Eigentümer der gerenderten Konfigurationsdatei wird.

Gruppe

No

String

Die Gruppe, die den Gruppenbesitz der gerenderten Konfigurationsdatei erhält.

mode

No

String

Der oktale Berechtigungsmodus, der für die gerenderte Datei festgelegt werden soll.

check_cmd

No

String

Der Befehl, mit dem die Syntax der gerenderten Konfigurationsdatei überprüft werden soll.

reload_cmd

No

String

Der Befehl, der zum erneuten Laden der Konfiguration der Anwendung verwendet werden soll.

Präfix

No

String

Ein Teil der Hierarchie vonetcd, der vor den Schlüsseln in der Direktive vonkeysteht. Dies kann verwendet werden, um die Datei.tomlflexibler zu gestalten.

Die von uns erstellte Datei enthält einige wichtige Informationen zur Funktionsweise der Instanz vonconfd. Unser Nginx-Container verwendet eine Vorlage, die bei/etc/confd/templates/nginx.conf.tmpl gespeichert ist, um eine Konfigurationsdatei zu rendern, die bei/etc/nginx/sites-enabled/app.conf abgelegt wird. Die Datei erhält einen Berechtigungssatz von0644, und der Root-Benutzer erhält das Eigentum.

Die Anwendungconfducht am Knoten/services/apachenach Änderungen. Wenn eine Änderung angezeigt wird, fragtconfd nach den neuen Informationen unter diesem Knoten. Es wird dann eine neue Konfiguration für Nginx gerendert. Es überprüft die Konfigurationsdatei auf Syntaxfehler und lädt den Nginx-Dienst neu, nachdem die Datei vorhanden ist.

Wir haben jetzt unsere Vorlagenressourcendatei erstellt. Wir sollten an der eigentlichen Vorlagendatei arbeiten, die zum Rendern unserer Nginx-Konfigurationsdatei verwendet wird.

Erstellen Sie eine Confd-Vorlagendatei

Für unsere Vorlagendatei verwenden wir ein Beispiel ausGitHub documentationdesconfd-Projekts, um uns den Einstieg zu erleichtern.

Erstellen Sie die Datei, auf die wir in unserer obigen Konfigurationsdatei verwiesen haben. Legen Sie diese Datei in unserem Verzeichnistemplatesab:

vi /etc/confd/templates/nginx.tmpl

In dieser Datei erstellen wir einfach eine Standard-Reverse-Proxy-Konfigurationsdatei von Nginx neu. Wir werden jedoch eine Go-Template-Syntax verwenden, um einige der Informationen zu ersetzen, dieconfd vonetcd abruft.

Zuerst konfigurieren wir den Block mit den "Upstream" -Servern. In diesem Abschnitt wird der Serverpool definiert, an den Nginx Anforderungen senden kann. Das Format ist im Allgemeinen so:

upstream pool_name {
    server server_1_IP:port_num;
    server server_2_IP:port_num;
    server server_3_IP:port_num;
}

Auf diese Weise können wir Anforderungen anpool_name übergeben, und Nginx wählt einen der definierten Server aus, an die die Anforderung übergeben werden soll.

Die Idee hinter unserer Vorlagendatei ist es,etcd nach den IP-Adressen und Portnummern unserer Apache-Webserver zu analysieren. Anstatt unsere Upstream-Server statisch zu definieren, sollten wir diese Informationen beim Rendern der Datei dynamisch eingeben. Wir können dies tun, indem wirGo templates für den dynamischen Inhalt verwenden.

Dazu verwenden wir stattdessen diesen Block:

upstream apache_pool {
{{ range getvs "/services/apache/*" }}
    server {{ . }};
{{ end }}
}

Lassen Sie uns für einen Moment erklären, was los ist. Wir haben einen Block geöffnet, um einen Upstream-Pool von Servern mit dem Namenapache_pool zu definieren. In diesem Abschnitt geben wir mithilfe der doppelten Klammern an, dass wir mit einem Go-Sprachcode beginnen.

In diesen Klammern geben wir den Endpunkt vonetcdan, an dem die Werte gespeichert sind, an denen wir interessiert sind. Wir verwenden einrange, um die Liste iterierbar zu machen.

Wir verwenden dies, um alle Einträge, die von unterhalb der Position von/services/apacheinetcd abgerufen wurden, an den Blockrange zu übergeben. Wir können dann den Wert des Schlüssels in der aktuellen Iteration abrufen, indem wir einen einzelnen Punkt in den Feldern "\ {\ {" und "}}" verwenden, der einen eingefügten Wert angibt. Wir verwenden dies innerhalb der Bereichsschleife, um den Serverpool zu füllen. Schließlich beenden wir die Schleife mit der Anweisung{{ end }}.

Note: Denken Sie daran, das Semikolon nach der Direktiveserver innerhalb der Schleife einzufügen. Wenn Sie dies vergessen, funktioniert die Konfiguration nicht mehr.

Nach dem Einrichten des Serverpools können wir einfach einen Proxy-Pass verwenden, um alle Verbindungen in diesen Pool zu leiten. Dies ist lediglich ein Standard-Serverblock als Reverse-Proxy. Das einzige, was zu beachten ist, istaccess_log, das ein benutzerdefiniertes Format verwendet, das wir momentan erstellen werden:

upstream apache_pool {
{{ range getvs "/services/apache/*" }}
    server {{ . }};
{{ end }}
}

server {
    listen 80 default_server;
    listen [::]:80 default_server ipv6only=on;

    access_log /var/log/nginx/access.log upstreamlog;

    location / {
        proxy_pass http://apache_pool;
        proxy_redirect off;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

Dies reagiert auf alle Verbindungen an Port 80 und leitet sie mitapache_pool an den Serverpool weiter, der durch Betrachten deretcd-Einträge generiert wird.

Während wir uns mit diesem Aspekt des Dienstes befassen, sollten wir die Standard-Nginx-Konfigurationsdatei entfernen, damit wir später nicht auf Konflikte stoßen. Wir werden nur den symbolischen Link entfernen, der die Standardkonfiguration aktiviert:

rm /etc/nginx/sites-enabled/default

Jetzt ist auch ein guter Zeitpunkt, um das Protokollformat zu konfigurieren, auf das wir in unserer Vorlagendatei verwiesen haben. Dies muss imhttp-Block der Konfiguration erfolgen, der in der Hauptkonfigurationsdatei verfügbar ist. Öffne das jetzt:

vi /etc/nginx/nginx.conf

Wir werden einelog_format-Direktive hinzufügen, um die Informationen zu definieren, die wir protokollieren möchten. Es protokolliert den Client, der besucht wird, sowie den Back-End-Server, an den die Anforderung weitergeleitet wird. Wir werden einige Daten über die Zeit protokollieren, die diese Vorgänge in Anspruch nehmen:

. . .
http {
    ##
    # Basic Settings
    ##
    log_format upstreamlog '[$time_local] $remote_addr passed to: $upstream_addr: $request Upstream Response Time: $upstream_response_time Request time: $request_time';

    sendfile on;
    . . .

Speichern und schließen Sie die Datei, wenn Sie fertig sind.

Erstellen eines Skripts zum Ausführen von Confd

Wir müssen eine Skriptdatei erstellen, dieconfd mit unserer Vorlagenressourcendatei und unserer Vorlagendatei zu den entsprechenden Zeiten aufruft.

Das Skript muss zwei Dinge tun, damit unser Service korrekt funktioniert:

  • Es muss ausgeführt werden, wenn der Container gestartet wird, um die anfänglichen Nginx-Einstellungen basierend auf dem aktuellen Status der Back-End-Infrastruktur einzurichten.

  • Es muss weiterhin nach Änderungen an deretcd-Registrierung für die Apache-Server suchen, damit Nginx basierend auf den verfügbaren Backend-Servern neu konfiguriert werden kann.

Wir werden unser Skript vonMarcel de Graaf’s GitHub page erhalten. Dies ist ein schönes, einfaches Skript, dasexactlymacht, was wir brauchen. Wir werden nur ein paar kleinere Änderungen an unserem Szenario vornehmen.

Platzieren wir dieses Skript neben der ausführbaren Datei vonconfd. Wir werden diesconfd-watch nennen:

vi /usr/local/bin/confd-watch

Wir beginnen mit dem herkömmlichenbash-Header, um den benötigten Interpreter zu identifizieren. Wir werden dann einigebash-Optionen festlegen, damit das Skript sofort fehlschlägt, wenn etwas schief geht. Es wird der Wert des letzten fehlgeschlagenen oder ausgeführten Befehls zurückgegeben.

#!/bin/bash

set -eo pipefail

Als nächstes wollen wir einige Variablen einrichten. Durch die Verwendung der Parametersubstitution vonbashwerden Standardwerte festgelegt, aber eine gewisse Flexibilität eingebaut, damit wir die fest codierten Werte beim Aufrufen des Skripts überschreiben können. Dies wird im Grunde nur jede Komponente der Verbindungsadresse unabhängig einrichten und sie dann zusammenfassen, um die vollständige benötigte Adresse zu erhalten.

Die Parametersubstitution wird mit folgender Syntax erstellt:${var_name:-default_value}. Dies hat die Eigenschaft, den Wertvar_name zu verwenden, wenn er angegeben ist und nicht null, andernfalls wird standardmäßigdefault_value verwendet.

Wir verwenden standardmäßig die Werte, dieetcd standardmäßig erwartet. Auf diese Weise funktioniert unser Skript ohne zusätzliche Informationen gut, wir können es jedoch beim Aufrufen des Skripts nach Bedarf anpassen:

#!/bin/bash

set -eo pipefail

export ETCD_PORT=${ETCD_PORT:-4001}
export HOST_IP=${HOST_IP:-172.17.42.1}
export ETCD=$HOST_IP:$ETCD_PORT

Wir werden jetztconfd verwenden, um eine erste Version der Nginx-Konfigurationsdatei zu rendern, indem wir die Werte vonetcd lesen, die verfügbar sind, wenn dieses Skript aufgerufen wird. Wir werden eineuntil-Schleife verwenden, um kontinuierlich zu versuchen, die ursprüngliche Konfiguration zu erstellen.

Das Schleifenkonstrukt kann erforderlich sein, wennetcd nicht sofort verfügbar ist oder wenn der Nginx-Container vor den Backend-Servern online geschaltet wird. Dadurch kannetcd wiederholt abgefragt werden, bis schließlich eine gültige Erstkonfiguration erstellt werden kann.

Der tatsächliche Befehlconfd, den wir aufrufen, wird einmal ausgeführt und dann beendet. Auf diese Weise können wir 5 Sekunden bis zur nächsten Ausführung warten, damit sich unsere Back-End-Server registrieren können. Wir stellen eine Verbindung zu der vollständigenETCD-Variable her, die wir mithilfe der Standardeinstellungen erstellt oder Parameter übergeben haben, und verwenden die Vorlagendatei, um das Verhalten unserer Aufgaben zu definieren:

#!/bin/bash

set -eo pipefail

export ETCD_PORT=${ETCD_PORT:-4001}
export HOST_IP=${HOST_IP:-172.17.42.1}
export ETCD=$HOST_IP:$ETCD_PORT

echo "[nginx] booting container. ETCD: $ETCD"

# Try to make initial configuration every 5 seconds until successful
until confd -onetime -node $ETCD -config-file /etc/confd/conf.d/nginx.toml; do
    echo "[nginx] waiting for confd to create initial nginx configuration"
    sleep 5
done

Nachdem die Erstkonfiguration festgelegt wurde, sollte die nächste Aufgabe unseres Skripts darin bestehen, einen Mechanismus für die fortlaufende Abfrage einzurichten. Wir möchten sicherstellen, dass zukünftige Änderungen erkannt werden, damit Nginx aktualisiert wird.

Dazu können wir noch einmalconfd aufrufen. Dieses Mal möchten wir ein kontinuierliches Abfrageintervall festlegen und den Prozess in den Hintergrund stellen, damit er auf unbestimmte Zeit ausgeführt wird. Wir werden dieselbenetcd-Verbindungsinformationen und dieselbe Vorlagenressourcendatei übergeben, da unser Ziel immer noch dasselbe ist.

Nachdem wir den Prozessconfd in den Hintergrund gestellt haben, können wir Nginx sicher mit der erstellten Konfigurationsdatei starten. Da dieses Skript als Docker-Befehl "run" aufgerufen wird, muss es im Vordergrund ausgeführt werden, damit der Container an dieser Stelle nicht beendet wird. Wir können dies tun, indem wir nur die Protokolle nachverfolgen und uns Zugang zu allen Informationen verschaffen, die wir protokolliert haben:

#!/bin/bash

set -eo pipefail

export ETCD_PORT=${ETCD_PORT:-4001}
export HOST_IP=${HOST_IP:-172.17.42.1}
export ETCD=$HOST_IP:$ETCD_PORT

echo "[nginx] booting container. ETCD: $ETCD."

# Try to make initial configuration every 5 seconds until successful
until confd -onetime -node $ETCD -config-file /etc/confd/conf.d/nginx.toml; do
    echo "[nginx] waiting for confd to create initial nginx configuration."
    sleep 5
done

# Put a continual polling `confd` process into the background to watch
# for changes every 10 seconds
confd -interval 10 -node $ETCD -config-file /etc/confd/conf.d/nginx.toml &
echo "[nginx] confd is now monitoring etcd for changes..."

# Start the Nginx service using the generated config
echo "[nginx] starting nginx service..."
service nginx start

# Follow the logs to allow the script to continue running
tail -f /var/log/nginx/*.log

Wenn Sie damit fertig sind, speichern und schließen Sie die Datei.

Als letztes müssen wir das Skript ausführbar machen:

chmod +x /usr/local/bin/confd-watch

Verlassen Sie jetzt den Container, um zum Host-System zurückzukehren:

exit

Übernehmen Sie und schieben Sie den Container

Jetzt können wir den Container festschreiben und zum Docker Hub hochschieben, damit er unseren Maschinen zum Herunterfahren zur Verfügung steht.

Finden Sie die Container-ID heraus:

docker ps -l
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                          PORTS               NAMES
de4f30617499        ubuntu:14.04        "/bin/bash"         22 hours ago        Exited (0) About a minute ago                       stupefied_albattani

Die hervorgehobene Zeichenfolge ist die Container-ID, die wir benötigen. Übernehmen Sie den Container mit dieser ID zusammen mit Ihrem Docker Hub-Benutzernamen und dem Namen, den Sie für dieses Image verwenden möchten. Wir werden in diesem Handbuch den Namen "nginx_lb" verwenden:

docker commit de4f30617499 user_name/nginx_lb

Melden Sie sich bei Bedarf bei Ihrem Docker Hub-Konto an:

docker login

Jetzt sollten Sie Ihr festgeschriebenes Image nach oben verschieben, damit Ihre anderen Hosts es bei Bedarf nach unten ziehen können:

docker push user_name/nginx_lb

Erstellen Sie die statische Nginx-Einheitendatei

Der nächste Schritt besteht darin, eine Unit-Datei zu erstellen, die den soeben erstellten Container startet. Dadurch können wirfleet verwenden, um den Prozess zu steuern.

Da dies keine Vorlage sein wird, legen wir sie in dem Verzeichnis~/staticab, das wir am Anfang dieses Verzeichnisses erstellt haben:

vim static/nginx_lb.service

Wir beginnen mit dem Standardabschnitt[Unit], um den Service zu beschreiben und die Abhängigkeiten und die Reihenfolge zu definieren:

[Unit]
Description=Nginx load balancer for web server backends

# Requirements
Requires=etcd.service
Requires=docker.service

# Dependency ordering
After=etcd.service
After=docker.service

Als nächstes müssen wir den[Service]-Teil der Datei definieren. Wir werden das Timeout auf null setzen und den Killmode wieder auf none setzen, genau wie wir es mit den Apache-Service-Dateien gemacht haben. Wir werden die Umgebungsdatei erneut abrufen, damit wir auf die öffentlichen und privaten IP-Adressen des Hosts zugreifen können, auf dem dieser Container ausgeführt wird.

Wir werden dann unsere Umgebung bereinigen, um sicherzustellen, dass frühere Versionen dieses Containers getötet und entfernt werden. Wir ziehen den soeben erstellten Container herunter, um sicherzustellen, dass wir immer die neueste Version haben.

Zum Schluss starten wir den Container. Dazu müssen Sie den Container starten, ihm den Namen geben, auf den wir in den Befehlen remove und kill verwiesen haben, und ihm die öffentliche IP-Adresse des Hosts übergeben, auf dem er ausgeführt wird, um Port 80 zuzuordnen. Wir rufen das Skriptconfd-watchauf, das wir als Ausführungsbefehl geschrieben haben.

[Unit]
Description=Nginx load balancer for web server backends

# Requirements
Requires=etcd.service
Requires=docker.service

# Dependency ordering
After=etcd.service
After=docker.service

[Service]
# Let the process take awhile to start up (for first run Docker containers)
TimeoutStartSec=0

# Change killmode from "control-group" to "none" to let Docker remove
# work correctly.
KillMode=none

# Get CoreOS environmental variables
EnvironmentFile=/etc/environment

# Pre-start and Start
## Directives with "=-" are allowed to fail without consequence
ExecStartPre=-/usr/bin/docker kill nginx_lb
ExecStartPre=-/usr/bin/docker rm nginx_lb
ExecStartPre=/usr/bin/docker pull user_name/nginx_lb
ExecStart=/usr/bin/docker run --name nginx_lb -p ${COREOS_PUBLIC_IPV4}:80:80 \
user_name/nginx_lb /usr/local/bin/confd-watch

Jetzt müssen wir nur noch den Stoppbefehl und die Planungsanweisungen vonfleetaussortieren. Wir möchten, dass dieser Container nur auf Hosts initiiert wird, auf denen keine anderen Lastenausgleichsinstanzen oder Back-End-Apache-Server ausgeführt werden. Dadurch kann unser Service die Last effektiv verteilen:

[Unit]
Description=Nginx load balancer for web server backends

# Requirements
Requires=etcd.service
Requires=docker.service

# Dependency ordering
After=etcd.service
After=docker.service

[Service]
# Let the process take awhile to start up (for first run Docker containers)
TimeoutStartSec=0

# Change killmode from "control-group" to "none" to let Docker remove
# work correctly.
KillMode=none

# Get CoreOS environmental variables
EnvironmentFile=/etc/environment

# Pre-start and Start
## Directives with "=-" are allowed to fail without consequence
ExecStartPre=-/usr/bin/docker kill nginx_lb
ExecStartPre=-/usr/bin/docker rm nginx_lb
ExecStartPre=/usr/bin/docker pull user_name/nginx_lb
ExecStart=/usr/bin/docker run --name nginx_lb -p ${COREOS_PUBLIC_IPV4}:80:80 \
user_name/nginx_lb /usr/local/bin/confd-watch

# Stop
ExecStop=/usr/bin/docker stop nginx_lb

[X-Fleet]
Conflicts=nginx.service
Conflicts=apache@*.service

Speichern und schließen Sie die Datei, wenn Sie fertig sind.

Ausführen des Nginx Load Balancer

In diesem Tutorial sollten bereits zwei Apache-Instanzen ausgeführt werden. Sie können überprüfen, indem Sie Folgendes eingeben:

fleetctl list-units
UNIT                MACHINE             ACTIVE  SUB
[email protected]   197a1662.../10.132.249.206  active  running
[email protected]   04856ec4.../10.132.249.212  active  running
[email protected]     197a1662.../10.132.249.206  active  running
[email protected]     04856ec4.../10.132.249.212  active  running

Sie können auch überprüfen, ob sie sich korrekt beietcd registrieren, indem Sie Folgendes eingeben:

etcdctl ls --recursive /services/apache
/services/apache/10.132.249.206
/services/apache/10.132.249.212

Wir können jetzt versuchen, unseren Nginx-Dienst zu starten:

fleetctl start ~/static/nginx_lb.service
Unit nginx_lb.service launched on 96ec72cf.../10.132.248.177

Es kann ungefähr eine Minute dauern, bis der Dienst gestartet wird, je nachdem, wie lange es dauert, bis das Bild heruntergezogen ist. Wenn Sie nach dem Start die Protokolle mit dem Befehlfleetctl journal überprüfen, sollten Sie in der Lage sein, einige Protokollinformationen vonconfd anzuzeigen. Es sollte ungefähr so ​​aussehen:

fleetctl journal nginx_lb.service
-- Logs begin at Mon 2014-09-15 14:54:05 UTC, end at Tue 2014-09-16 17:13:58 UTC. --
Sep 16 17:13:48 lala1 docker[15379]: 2014-09-16T17:13:48Z d7974a70e976 confd[14]: INFO Target config /etc/nginx/sites-enabled/app.conf out of sync
Sep 16 17:13:48 lala1 docker[15379]: 2014-09-16T17:13:48Z d7974a70e976 confd[14]: INFO Target config /etc/nginx/sites-enabled/app.conf has been updated
Sep 16 17:13:48 lala1 docker[15379]: [nginx] confd is monitoring etcd for changes...
Sep 16 17:13:48 lala1 docker[15379]: [nginx] starting nginx service...
Sep 16 17:13:48 lala1 docker[15379]: 2014-09-16T17:13:48Z d7974a70e976 confd[33]: INFO Target config /etc/nginx/sites-enabled/app.conf in sync
Sep 16 17:13:48 lala1 docker[15379]: ==> /var/log/nginx/access.log <==
Sep 16 17:13:48 lala1 docker[15379]: ==> /var/log/nginx/error.log <==
Sep 16 17:13:58 lala1 docker[15379]: 2014-09-16T17:13:58Z d7974a70e976 confd[33]: INFO /etc/nginx/sites-enabled/app.conf has md5sum a8517bfe0348e9215aa694f0b4b36c9b should be 33f42e3b7cc418f504237bea36c8a03e
Sep 16 17:13:58 lala1 docker[15379]: 2014-09-16T17:13:58Z d7974a70e976 confd[33]: INFO Target config /etc/nginx/sites-enabled/app.conf out of sync
Sep 16 17:13:58 lala1 docker[15379]: 2014-09-16T17:13:58Z d7974a70e976 confd[33]: INFO Target config /etc/nginx/sites-enabled/app.conf has been updated

Wie Sie sehen können, hatconfd nachetcd gesucht, um die Erstkonfiguration zu erhalten. Dann wurdenginx gestartet. Danach sehen wir Zeilen, in denen die Einträge vonetcdneu ausgewertet und eine neue Konfigurationsdatei erstellt wurden. Wenn die neu generierte Datei nicht mitmd5sum der vorhandenen Datei übereinstimmt, wird die Datei ausgeschaltet und der Dienst neu geladen.

Auf diese Weise kann unser Lastausgleichsdienst letztendlich unsere Apache-Backend-Server nachverfolgen. Wennconfd ständig aktualisiert zu werden scheint, liegt dies möglicherweise daran, dass Ihre Apache-Instanzen ihre TTL zu oft aktualisieren. Sie können die Sleep- und TTL-Werte in der Sidekick-Vorlage erhöhen, um dies zu vermeiden.

Um den Load Balancer in Aktion zu sehen, können Sie beim Host, auf dem der Nginx-Dienst ausgeführt wird, nach der Datei/etc/environmentsfragen. Diese enthält die öffentliche IP-Adresse des Hosts. Wenn Sie diese Konfiguration verbessern möchten, sollten Sie einen Sidekick-Dienst ausführen, der diese Informationen mitetcd registriert, genau wie bei den Apache-Instanzen:

fleetctl ssh nginx_lb cat /etc/environment
COREOS_PRIVATE_IPV4=10.132.248.177
COREOS_PUBLIC_IPV4=104.131.16.222

Wenn wir nun die öffentliche IPv4-Adresse in unserem Browser aufrufen, sollte die Seite angezeigt werden, die wir in unseren Apache-Instanzen konfiguriert haben:

Apache index page

Wenn Sie sich Ihre Protokolle noch einmal ansehen, sollten Sie in der Lage sein, Informationen darüber anzuzeigen, auf welchem ​​Back-End-Server die Anforderung tatsächlich übergeben wurde:

fleetctl journal nginx_lb
. . .
Sep 16 18:04:38 lala1 docker[18079]: 2014-09-16T18:04:38Z 51c74658196c confd[28]: INFO Target config /etc/nginx/sites-enabled/app.conf in sync
Sep 16 18:04:48 lala1 docker[18079]: 2014-09-16T18:04:48Z 51c74658196c confd[28]: INFO Target config /etc/nginx/sites-enabled/app.conf in sync
Sep 16 18:04:48 lala1 docker[18079]: [16/Sep/2014:18:04:48 +0000] 108.29.37.206 passed to: 10.132.249.212:8888: GET / HTTP/1.1 Upstream Response Time: 0.003 Request time: 0.003

Fazit

Wie Sie sehen können, können Sie Ihre Dienste so einrichten, dassetcd auf Konfigurationsdetails überprüft werden. Tools wieconfd können diesen Prozess relativ einfach machen, indem sie die kontinuierliche Abfrage wichtiger Einträge ermöglichen.

In dem Beispiel in diesem Handbuch haben wir unseren Nginx-Dienst so konfiguriert, dassetcd zum Generieren seiner Erstkonfiguration verwendet wird. Wir haben es auch im Hintergrund eingerichtet, um kontinuierlich nach Änderungen zu suchen. In Kombination mit der dynamischen Konfigurationsgenerierung auf der Basis von Vorlagen konnten wir ein stets aktuelles Bild unserer Back-End-Server erhalten.