Remarque : ce n'est pas un article de guide à part entière, mais plutôt un rappel / indice pour ceux qui utilisent déjà ConfigMap dans Kubernetes ou qui préparent simplement leur application pour y travailler.
Contexte: De rsync Ă ... Kubernetes
Qu'est-il arrivé avant? À l'ère de «l'administration classique», dans la version la plus simple, le fichier de configuration était placé juste à côté des applications (ou dans le référentiel, si vous voulez). C'est simple: nous faisons la livraison élémentaire (CD) de notre code avec la config. Même une implémentation conditionnelle de rsync peut être appelée les rudiments d'un CD.Lorsque l'infrastructure s'est développée, différentes configurations ont été nécessaires pour différents environnements (développement / stade / production). L'application a été formée pour comprendre quelle configuration utiliser, en les passant comme arguments pour démarrer ou comme variables d'environnement définies dans l'environnement. Encore plus de CD est compliqué avec l'avènement de ce Chef / Marionnette / Ansible si utile. Les rôles apparaissent sur les serveurs et les environnements ne sont plus décrits à différents endroits - nous arrivons à IaC (Infrastructure en tant que code).Qu'est-ce qui a suivi? S'il était possible de voir par lui-même les avantages critiques de Kubernetes et même de comprendre la nécessité de modifier les applications pour qu'elles fonctionnent dans cet environnement, la migration s'est produite. Sur le chemin, je m'attendais à beaucoup de nuances et de différences dans la construction de l'architecture, mais quand j'ai réussi à faire face à la partie principale, j'ai obtenu l'application tant attendue en cours d'exécution dans K8s.Une fois ici, nous pouvons toujours utiliser les configurations préparées dans le référentiel à côté de l'application ou passer ENV au conteneur. Cependant, en plus de ces méthodes, ConfigMaps sont également disponibles . Cette primitive K8s vous permet d'utiliser des modèles Go dans les configurations, c'est-à -dire les rendre comme des pages HTML et recharger l'application lors du changement de la configuration sans redémarrage. Avec ConfigMaps, il n'est plus nécessaire de conserver 3+ configurations pour différents environnements et de garder une trace de la pertinence de chacune.Une introduction générale à ConfigMaps peut être trouvée, par exemple, ici . Et dans cet article, je vais me concentrer sur certaines fonctionnalités de leur travail.ConfigMaps simples
À quoi ressemblaient les configurations dans Kubernetes? Qu'ont-ils obtenu des modèles go? Par exemple, voici un ConfigMap ordinaire pour une application déployée à partir d'un graphique Helm: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 }}
}
Ici, les valeurs substituées .Values.welcome
et .Values.name
seront extraites du fichier values.yaml
. Pourquoi exactement values.yaml
? Comment fonctionne le moteur de modèles Go? Nous avons déjà parlé de ces détails plus en détail ici .L'appel pluck
permet de sélectionner la ligne nécessaire sur la carte:$ cat .helm/values.yaml
welcome:
production: "Hello"
test: "Hey"
name:
production: "Bob"
test: "Mike"
De plus, vous pouvez prendre à la fois des lignes spécifiques et des fragments entiers de la configuration.Par exemple, ConfigMap pourrait ressembler à ceci:data:
config.json: |
{{ pluck .Values.global.env .Values.data | first | toJson | indent 4 }}
... et dans values.yaml
- le contenu suivant:data:
production:
welcome: "Hello"
name: "Bob"
Celui qui global.env
est impliqué ici est le nom de l'environnement. En remplaçant cette valeur pendant le déploiement, vous pouvez rendre des ConfigMaps avec un contenu différent. first
nécessaire ici parce que pluck
renvoie une liste dont le premier élément contient la valeur souhaitée.Quand il y a beaucoup de configurations
Un ConfigMap peut contenir plusieurs fichiers de configuration: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
Vous pouvez même monter chaque configuration séparément: volumeMounts:
- name: app-conf
mountPath: /app/configfiles/config.json
subPath: config.json
- name: app-conf
mountPath: /app/configfiles/database.yml
subPath: database.yml
... ou récupérez toutes les configurations à la fois avec le répertoire: volumeMounts:
- name: app-conf
mountPath: /app/configfiles
Si vous modifiez la description de la ressource de déploiement pendant le déploiement, Kubernetes créera un nouveau ReplicaSet, diminuant l'ancien à 0 et augmentant le nouveau au nombre spécifié de réplicas. (Cela est vrai pour la stratégie de déploiement RollingUpdate
.)De telles actions entraîneront la recréation du pod avec une nouvelle description. Par exemple: il y avait une image image:my-registry.example.com:v1
, mais est devenu - image:my-registry.example.com:v2
. Et peu importe ce que nous avons changé exactement dans la description de notre déploiement: l'essentiel est que cela a entraîné la recréation du replicaSet (et, par conséquent, du pod). Dans ce cas, la nouvelle version du fichier de configuration dans la nouvelle version de l'application est automatiquement montée et il n'y aura aucun problème.Réponse au changement de ConfigMap
En cas de changements dans ConfigMap, quatre scénarios d'événements peuvent suivre. Considérez-les:- : ConfigMap, subPath.
: . - : ConfigMap, pod.
: pod . - : ConfigMap Deployment -.
: , ConfigMap’, Deployment, pod , — . - : ConfigMap, .
: pod’ / pod’.
Nous analyserons plus en détail.Scénario 1
Nous avons seulement corrigé ConfigMap? L'application ne redémarrera pas. Dans le cas d'un montage par, subPath
il n'y aura aucun changement jusqu'à ce que le pod soit redémarré manuellement.Tout est simple: Kubernetes monte notre ConfigMap dans un pod d'une version spécifique de la ressource. Puisqu'il est monté avec subPath
, aucune "influence" supplémentaire sur la config n'est plus fournie.Scénario 2
Vous ne pouvez pas mettre à jour le fichier sans recréer le pod? D'accord, nous avons 6 répliques dans le déploiement, nous pouvons donc nous relayer manuellement pour tout faire delete pod
. Ensuite, lors de la création de nouveaux pods, ils «récupèrent» la nouvelle version de ConfigMap.Scénario 3
Fatigué d'effectuer de telles opérations manuellement? Une solution à ce problème est décrite dans les conseils et astuces de Helm :kind: Deployment
spec:
template:
metadata:
annotations:
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
[...]
Ainsi, le spec.template
hachage de la configuration d'annotation rendue est simplement écrit dans le modèle pod ( ).Les annotations sont des champs de valeurs-clés arbitraires dans lesquels vous pouvez stocker vos valeurs. Si vous les enregistrez dans le modèle du spec.template
futur pod, ces champs tomberont dans le ReplicaSet et le pod lui-même. Kubernetes remarquera que le modèle de pod a changé (parce que la configuration sha256 a changé) et démarrera RollingUpdate
, dans lequel rien ne change, sauf cette annotation.En conséquence, nous enregistrons la même version de l'application et de la description du déploiement et déclenchons essentiellement la recréation du pod par la machine - de la même manière que nous le ferions manuellement kubectl delete
, mais déjà «correctement»: automatiquement et avec RollingUpdate
.Scénario 4
Peut-être que l'application sait déjà comment surveiller les changements dans la configuration et recharger automatiquement? Voici une caractéristique importante de ConfigMaps ...Dans Kubernetes, si la config est montée avec subPath
, elle ne sera pas mise à jour tant que le pod n'aura pas été redémarré (voir les trois premiers scénarios discutés ci-dessus) . Mais si vous montez ConfigMap en tant que répertoire, sans subPath
, à l'intérieur du conteneur, il y aura un répertoire avec une configuration mise à jour sans redémarrer le pod.Il existe d'autres fonctionnalités utiles à retenir:- Un tel fichier de configuration mis à jour à l'intérieur du conteneur est mis à jour avec un certain retard. Cela est dû au fait que le fichier n'est pas monté exactement, mais l'objet Kubernetes.
- Le fichier à l'intérieur est un lien symbolique. Exemple avec
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
Et que se passera-t-il sans une subPath
fois monté par le répertoire?
$ 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
Mettez Ă jour la configuration (via deploy ou kubectl edit
), attendez 2 minutes (temps de mise en cache apiserver) - et le tour est joué:
$ 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
Notez l'horodatage modifié dans le répertoire créé par Kubernetes.
Suivi des modifications
Et enfin - un exemple simple de la façon dont vous pouvez surveiller les changements dans la configuration.Nous utiliserons une telle application Gopackage 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)
}
}
... en l'ajoutant avec une telle configuration:$ cat configfiles/config.json
{
"welcome": "Hello",
"name": "Alice"
}
S'il est exécuté, le journal sera:2020/03/03 22:18:22 config: &{Hello Alice}
2020/03/03 22:18:52 config: &{Hello Alice}
Et maintenant, nous allons installer cette application dans Kubernetes, après avoir monté la configuration ConfigMap dans le pod au lieu du fichier image. Un exemple de graphique Helm a été préparé sur GitHub :helm install -n habr-configmap --namespace habr-configmap ./habr-configmap --set 'name.production=Alice' --set 'global.env=production'
Et modifiez uniquement ConfigMap:- production: "Alice"
+ production: "Bob"
Mettez Ă jour le graphique Helm dans le cluster, par exemple, comme ceci:helm upgrade habr-configmap ./habr-configmap --set 'name.production=Bob' --set 'global.env=production'
Que va-t-il se passer?Vous pouvez voir comment une situation similaire - suivre les changements de ConfigMap - est résolue dans un projet plus adulte (et simplement «réel»), ici .Important! Il est également utile de rappeler que tout ce qui précède dans l'article est également vrai pour Secret'ov dans Kubernetes ( kind: Secret
): ce n'est pas pour rien qu'ils sont si similaires Ă ConfigMap ...Prime! Solutions tierces
Si vous êtes intéressé par le sujet du suivi des modifications dans les configurations, il existe déjà des utilitaires prêts à l'emploi pour cela:- jimmidyson / configmap-reload - Envoie une requête HTTP si le fichier a changé. Le développeur prévoit également d'enseigner à SIGHUP d'envoyer, mais le manque de validations à partir d'octobre 2019 laisse ces plans en question;
- stakater / Reloader - surveille ConfigMap / Secrets et effectue une mise à niveau progressive (comme son auteur l'appelle) sur les ressources qui leur sont associées.
Il sera commode de lancer de telles applications avec un sidecar-container aux applications existantes. Cependant, si vous connaissez les fonctionnalités de Kubernetes / ConfigMap et des configurations à éditer non pas "en direct" (à travers edit
), mais uniquement dans le cadre du déploiement ... alors les capacités de ces utilitaires peuvent sembler superflues, c'est-à -dire duplication des fonctions de base.Conclusion
Avec l'avènement de ConfigMap dans Kubernetes, les configurations sont passées à la prochaine phase de développement: l'utilisation d'un moteur de modèle leur a apporté une flexibilité comparable au rendu des pages HTML. Heureusement, de telles complications n'ont pas remplacé les solutions existantes, mais sont devenues leur complément . Par conséquent, pour les administrateurs (ou plutôt, même les développeurs) qui considèrent les nouvelles fonctionnalités comme redondantes, de bons vieux fichiers sont toujours disponibles.Pour ceux qui utilisent déjà ConfigMap'ami ou qui les regardent simplement, l'article donne un bref aperçu de leur essence et de leurs nuances d'utilisation. Si vous avez vos propres trucs et astuces sur le sujet - je serai heureux de voir dans les commentaires.PS
Lisez aussi dans notre blog: