Hinweis : Dies ist kein vollständiger Leitfaden, sondern eine Erinnerung / ein Hinweis für diejenigen, die ConfigMap bereits in Kubernetes verwenden oder gerade ihre Anwendung für die Arbeit darin vorbereiten.
Hintergrund: Von rsync zu ... Kubernetes
Was ist vorher passiert? Im Zeitalter der „klassischen Administration“ wurde in der einfachsten Version die Konfigurationsdatei direkt neben den Anwendungen (oder, wenn Sie möchten, im Repository) platziert. Es ist ganz einfach: Wir machen eine elementare Lieferung (CD) für unseren Code zusammen mit der Konfiguration. Sogar eine bedingte rsync-Implementierung kann als die Grundlagen einer CD bezeichnet werden.Als die Infrastruktur wuchs, waren unterschiedliche Konfigurationen für unterschiedliche Umgebungen erforderlich (Entwickler / Bühne / Produktion). Die Anwendung wurde geschult, um zu verstehen, welche Konfiguration verwendet werden soll, und sie als Argumente zum Starten oder als Umgebungsvariablen in der Umgebung übergeben. Noch mehr CDs werden durch das Aufkommen solch nützlicher Köche / Puppen / Ansible kompliziert. Rollen werden auf Servern angezeigt, und Umgebungen werden an verschiedenen Stellen nicht mehr beschrieben - wir kommen zu IaC (Infrastruktur als Code).Was folgte? Wenn es möglich war, Kubernetes entscheidende Vorteile für sich selbst zu erkennen und sich sogar mit der Notwendigkeit abzufinden, Anwendungen so zu ändern, dass sie in dieser Umgebung funktionieren, kam es zu einer Migration. Unterwegs erwartete ich viele Nuancen und Unterschiede in der Konstruktion der Architektur, aber als ich den Hauptteil bewältigen konnte, lief die lang erwartete Anwendung in K8s.Sobald wir hier sind, können wir weiterhin die im Repository neben der Anwendung vorbereiteten Konfigurationen verwenden oder ENV an den Container übergeben. Zusätzlich zu diesen Methoden sind jedoch auch ConfigMaps verfügbar . Mit diesem K8-Grundelement können Sie Go-Vorlagen in Konfigurationen verwenden, d. H. Rendern Sie sie wie HTML-Seiten und laden Sie die Anwendung neu, wenn Sie die Konfiguration ohne Neustart ändern. Mit ConfigMaps müssen nicht mehr 3+ Konfigurationen für verschiedene Umgebungen gespeichert und die Relevanz der einzelnen Umgebungen verfolgt werden.Eine allgemeine Einführung in ConfigMaps finden Sie beispielsweise hier . In diesem Artikel werde ich mich auf einige Funktionen der Arbeit mit ihnen konzentrieren.Einfache ConfigMaps
Wie sahen Konfigurationen in Kubernetes aus? Was haben sie von den Go-Vorlagen bekommen? Hier ist beispielsweise eine normale ConfigMap für eine Anwendung, die aus einem Helmdiagramm bereitgestellt wird:apiVersion: v1
kind: ConfigMap
metadata:
name: app
data:
config.json: |
{
"welcome": {{ pluck .Values.global.env .Values.welcome | quote }},
"name": {{ pluck .Values.global.env .Values.name | quote }}
}
Hier werden die Werte ersetzt .Values.welcome
und .Values.name
aus der Datei übernommen values.yaml
. Warum genau von values.yaml
? Wie funktioniert die Go Template Engine? Wir haben bereits über diese Details genauer gesprochen hier .Der Anruf pluck
hilft bei der Auswahl der erforderlichen Linie aus der Karte:$ cat .helm/values.yaml
welcome:
production: "Hello"
test: "Hey"
name:
production: "Bob"
test: "Mike"
Darüber hinaus können Sie sowohl bestimmte Zeilen als auch ganze Fragmente der Konfiguration verwenden.ConfigMap könnte beispielsweise folgendermaßen aussehen:data:
config.json: |
{{ pluck .Values.global.env .Values.data | first | toJson | indent 4 }}
... und in values.yaml
- den folgenden Inhalten:data:
production:
welcome: "Hello"
name: "Bob"
Hier global.env
geht es um den Namen der Umgebung. Wenn Sie diesen Wert während der Bereitstellung ersetzen, können Sie ConfigMaps mit unterschiedlichen Inhalten rendern. first
hier gebraucht, weil pluck
Gibt eine Liste zurück, deren erstes Element den gewünschten Wert enthält.Wenn es viele Konfigurationen gibt
Eine ConfigMap kann mehrere Konfigurationsdateien enthalten:data:
config.json: |
{
"welcome": {{ pluck .Values.global.env .Values.welcome | first | quote }},
"name": {{ pluck .Values.global.env .Values.name | first | quote }}
}
database.yml: |
host: 127.0.0.1
db: app
user: app
password: app
Sie können sogar jede Konfiguration separat bereitstellen: volumeMounts:
- name: app-conf
mountPath: /app/configfiles/config.json
subPath: config.json
- name: app-conf
mountPath: /app/configfiles/database.yml
subPath: database.yml
... oder alle Konfigurationen auf einmal mit dem Verzeichnis abholen: volumeMounts:
- name: app-conf
mountPath: /app/configfiles
Wenn Sie die Beschreibung der Bereitstellungsressource während der Bereitstellung ändern, erstellt Kubernetes ein neues ReplicaSet, verringert das alte auf 0 und erhöht das neue auf die angegebene Anzahl von Replikaten. (Dies gilt für die Bereitstellungsstrategie RollingUpdate
.)Solche Aktionen führen zur Neuerstellung des Pods mit einer neuen Beschreibung. Zum Beispiel: Es gab ein Bild image:my-registry.example.com:v1
, wurde aber - image:my-registry.example.com:v2
. Dabei spielt es keine Rolle, was genau wir in der Beschreibung unserer Bereitstellung geändert haben: Hauptsache, dies hat dazu geführt, dass das replicaSet (und damit der Pod) neu erstellt wurde. In diesem Fall wird die neue Version der Konfigurationsdatei in der neuen Version der Anwendung automatisch bereitgestellt, und es tritt kein Problem auf.ConfigMap-Änderungsantwort
Bei Änderungen in ConfigMap können vier Ereignisszenarien folgen. Betrachten Sie sie:- : ConfigMap, subPath.
: . - : ConfigMap, pod.
: pod . - : ConfigMap Deployment -.
: , ConfigMap’, Deployment, pod , — . - : ConfigMap, .
: pod’ / pod’.
Wir werden genauer analysieren.Szenario 1
Wir haben nur ConfigMap korrigiert? Die Anwendung wird nicht neu gestartet. Bei der Montage durch subPath
werden keine Änderungen vorgenommen, bis der Pod manuell neu gestartet wird.Alles ist einfach: Kubernetes stellt unsere ConfigMap in einem Pod einer bestimmten Version der Ressource bereit. Da es mit gemountet ist subPath
, wird kein zusätzlicher "Einfluss" auf die Konfiguration mehr bereitgestellt.Szenario 2
Sie können die Datei nicht aktualisieren, ohne den Pod neu zu erstellen? Okay, wir haben 6 Replikate in Deployment, sodass wir abwechselnd alles manuell erledigen können delete pod
. Wenn sie dann neue Pods erstellen, werden sie die neue Version von ConfigMap "abholen".Szenario 3
Sind Sie es leid, solche Vorgänge manuell auszuführen? Eine Lösung für dieses Problem finden Sie in den Tipps und Tricks zu Helm :kind: Deployment
spec:
template:
metadata:
annotations:
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
[...]
Daher wird der spec.template
Hash der gerenderten Annotationskonfiguration einfach in die pod ( ) - Vorlage geschrieben .Anmerkungen sind beliebige Schlüsselwertfelder, in denen Sie Ihre Werte speichern können. Wenn Sie sie in der Vorlage des spec.template
zukünftigen Pods registrieren , fallen diese Felder in das ReplicaSet und den Pod selbst. Kubernetes wird feststellen, dass sich die Pod-Vorlage geändert hat (seit sich die sha256-Konfiguration geändert hat) und startet RollingUpdate
, wobei sich außer dieser Anmerkung nichts ändert.Infolgedessen speichern wir dieselbe Version der Anwendung und Beschreibung der Bereitstellung und lösen im Wesentlichen nur die Neuerstellung des Pods durch den Computer aus - ähnlich wie bei der manuellen Ausführung kubectl delete
, jedoch bereits „korrekt“: automatisch und mit RollingUpdate
.Szenario 4
Vielleicht weiß die Anwendung bereits, wie Änderungen in der Konfiguration überwacht und automatisch neu geladen werden? Hier liegt eine wichtige Funktion von ConfigMaps ...Wenn die Konfiguration in Kubernetes bereitgestellt wird subPath
, wird sie erst aktualisiert, wenn der Pod neu gestartet wird (siehe die ersten drei oben beschriebenen Szenarien) . Wenn Sie ConfigMap jedoch als Verzeichnis ohne bereitstellen subPath
, befindet sich im Container ein Verzeichnis mit einer aktualisierten Konfiguration, ohne den Pod neu zu starten.Es gibt andere nützliche Funktionen:- Eine solche aktualisierte Konfigurationsdatei im Container wird mit einiger Verzögerung aktualisiert. Dies liegt daran, dass die Datei nicht genau gemountet wird, sondern das Kubernetes-Objekt.
- Die darin enthaltene Datei ist ein Symlink. Beispiel mit
subPath
:
$ kubectl -n production exec go-conf-example-6b4cb86569-22vqv -- ls -lha /app/configfiles
total 20K
drwxr-xr-x 1 root root 4.0K Mar 3 19:34 .
drwxr-xr-x 1 app app 4.0K Mar 3 19:34 ..
-rw-r--r-- 1 root root 42 Mar 3 19:34 config.json
-rw-r--r-- 1 root root 47 Mar 3 19:34 database.yml
Und was passiert ohne, subPath
wenn es vom Verzeichnis gemountet wird?
$ kubectl -n production exec go-conf-example-67c768c6fc-ccpwl -- ls -lha /app/configfiles
total 12K
drwxrwxrwx 3 root root 4.0K Mar 3 19:40 .
drwxr-xr-x 1 app app 4.0K Mar 3 19:34 ..
drwxr-xr-x 2 root root 4.0K Mar 3 19:40 ..2020_03_03_16_40_36.675612011
lrwxrwxrwx 1 root root 31 Mar 3 19:40 ..data -> ..2020_03_03_16_40_36.675612011
lrwxrwxrwx 1 root root 18 Mar 3 19:40 config.json -> ..data/config.json
lrwxrwxrwx 1 root root 19 Mar 3 19:40 database.yml -> ..data/database.yml
Aktualisieren Sie die Konfiguration (über kubectl edit
Deployment oder ), warten Sie 2 Minuten (Apiserver-Caching-Zeit) - und voila:
$ kubectl -n production exec go-conf-example-67c768c6fc-ccpwl -- ls -lha --color /app/configfiles
total 12K
drwxrwxrwx 3 root root 4.0K Mar 3 19:44 .
drwxr-xr-x 1 app app 4.0K Mar 3 19:34 ..
drwxr-xr-x 2 root root 4.0K Mar 3 19:44 ..2020_03_03_16_44_38.763148336
lrwxrwxrwx 1 root root 31 Mar 3 19:44 ..data -> ..2020_03_03_16_44_38.763148336
lrwxrwxrwx 1 root root 18 Mar 3 19:40 config.json -> ..data/config.json
lrwxrwxrwx 1 root root 19 Mar 3 19:40 database.yml -> ..data/database.yml
Beachten Sie den geänderten Zeitstempel in dem von Kubernetes erstellten Verzeichnis.
Tracking ändern
Und schließlich - ein einfaches Beispiel dafür, wie Sie Änderungen in der Konfiguration überwachen können.Wir werden eine solche Go-Anwendung verwendenpackage main
import (
"encoding/json"
"fmt"
"log"
"os"
"time"
"github.com/fsnotify/fsnotify"
)
type Config struct {
Welcome string `json:"welcome"`
Name string `json:"name"`
}
var (
globalConfig *Config
)
func LoadConfig(path string) (*Config, error) {
configFile, err := os.Open(path)
if err != nil {
return nil, fmt.Errorf("Unable to read configuration file %s", path)
}
config := new(Config)
decoder := json.NewDecoder(configFile)
err = decoder.Decode(&config)
if err != nil {
return nil, fmt.Errorf("Unable to parse configuration file %s", path)
}
return config, nil
}
func ConfigWatcher() {
watcher, err := fsnotify.NewWatcher()
if err != nil {
log.Fatal(err)
}
defer watcher.Close()
done := make(chan bool)
go func() {
for {
select {
case event, ok := <-watcher.Events:
if !ok {
return
}
log.Println("event:", event)
if event.Op&fsnotify.Write == fsnotify.Write {
log.Println("modified file:", event.Name)
}
globalConfig, _ = LoadConfig("./configfiles/config.json")
log.Println("config:", globalConfig)
case err, ok := <-watcher.Errors:
if !ok {
return
}
log.Println("error:", err)
}
}
}()
err = watcher.Add("./configfiles/config.json")
if err != nil {
log.Fatal(err)
}
<-done
}
func main() {
log.Println("Start")
globalConfig, _ = LoadConfig("./configfiles/config.json")
go ConfigWatcher()
for {
log.Println("config:", globalConfig)
time.Sleep(30 * time.Second)
}
}
... mit einer solchen Konfiguration hinzufügen:$ cat configfiles/config.json
{
"welcome": "Hello",
"name": "Alice"
}
Wenn ausgeführt, lautet das Protokoll:2020/03/03 22:18:22 config: &{Hello Alice}
2020/03/03 22:18:52 config: &{Hello Alice}
Und jetzt werden wir diese Anwendung in Kubernetes installieren, nachdem wir die ConfigMap-Konfiguration im Pod anstelle der Datei aus dem Image bereitgestellt haben. Auf GitHub wurde ein Beispiel für ein Helm-Diagramm erstellt :helm install -n habr-configmap --namespace habr-configmap ./habr-configmap --set 'name.production=Alice' --set 'global.env=production'
Und ändern Sie nur ConfigMap:- production: "Alice"
+ production: "Bob"
Aktualisieren Sie das Helm-Diagramm im Cluster beispielsweise wie folgt:helm upgrade habr-configmap ./habr-configmap --set 'name.production=Bob' --set 'global.env=production'
Was wird passieren?Sie können prüfen , wie eine ähnliche Situation - ConfigMap die Änderungen Tracking - in einem erwachsenen gelöst wird (und nur „real“) Projekt hier .Wichtig! Es ist auch nützlich daran zu erinnern, dass all das oben Genannte im Artikel auch für Secret'ov in Kubernetes ( kind: Secret
) gilt: Nicht umsonst sind sie ConfigMap so ähnlich ...Bonus! Lösungen von Drittanbietern
Wenn Sie sich für das Thema Nachverfolgung von Änderungen in Konfigurationen interessieren, gibt es dafür bereits vorgefertigte Dienstprogramme:- jimmidyson / configmap-reload - Sendet eine HTTP-Anfrage, wenn sich die Datei geändert hat. Der Entwickler plant auch, SIGHUP das Senden beizubringen, aber das Fehlen von Commits ab Oktober 2019 lässt diese Pläne in Frage.
- stakater / Reloader - überwacht ConfigMap / Secrets und führt ein fortlaufendes Upgrade (wie der Autor es nennt) für die ihnen zugeordneten Ressourcen durch.
Es ist zweckmäßig, solche Anwendungen mit einem Beiwagencontainer für vorhandene Anwendungen zu starten. Wenn Sie jedoch die Funktionen von Kubernetes / ConfigMap kennen und konfigurieren, um nicht "live" (durch edit
), sondern nur im Rahmen der Bereitstellung zu bearbeiten , erscheinen die Funktionen solcher Dienstprogramme möglicherweise überflüssig, d. H. Grundfunktionen duplizieren.Fazit
Mit dem Aufkommen von ConfigMap in Kubernetes gingen die Konfigurationen in die nächste Entwicklungsrunde über: Die Verwendung einer Vorlagen-Engine brachte ihnen Flexibilität, die mit dem Rendern von HTML-Seiten vergleichbar war. Glücklicherweise ersetzten solche Komplikationen nicht die bestehenden Lösungen, sondern wurden zu ihrer Ergänzung . Daher sind für Administratoren (oder besser gesagt sogar Entwickler), die neue Funktionen für überflüssig halten, weiterhin gute alte Dateien verfügbar.Für diejenigen, die ConfigMap'ami bereits verwenden oder sich nur ansehen, bietet der Artikel einen kurzen Überblick über ihre Essenz und Verwendungsnuancen. Wenn Sie eigene Tipps und Tricks zum Thema haben, freue ich mich über die Kommentare.PS
Lesen Sie auch in unserem Blog: