Ansible permet de créer, modifier ou supprimer des secrets
(community.docker.docker_secret
)
et des configs
(community.docker.docker_config
)
sur un cluster Docker Swarm de façon automatisée. Mais il y a quelques astuces à connaître avant de pouvoir
les utiliser correctement dans un playbook reproductible.
Les modules docker_secret et docker_config
Voici les exemples fournis par la documentation Ansible pour créer un secret ou ou une config.
- name: Create secret foo (from a file on the control machine)
community.docker.docker_secret:
name: foo
# If the file is JSON or binary, Ansible might modify it (because
# it is first decoded and later re-encoded). Base64-encoding the
# file directly after reading it prevents this to happen.
data: "{{ lookup('file', '/path/to/secret/file') | b64encode }}"
data_is_b64: true
state: present
- name: Create config foo (from a file on the control machine)
community.docker.docker_config:
name: foo
# If the file is JSON or binary, Ansible might modify it (because
# it is first decoded and later re-encoded). Base64-encoding the
# file directly after reading it prevents this to happen.
data: "{{ lookup('file', '/path/to/config/file') | b64encode }}"
data_is_b64: true
state: present
Première info importante : le fichier source local sera décodé puis réencodé,
il faut le transformer en base64 avant de l’envoyer, en utilisant le paramètre
data_is_b64
pour qu’il soit correctement décodé à l’écriture.
Reproductibilité du playbook
On peut appliquer l’exemple sur un cas concret : le déploiement de Traefik sur un cluster swarm. Dans le playbook, on applique la tâche :
- name: Push config
community.docker.docker_config:
name: traefik
# If the file is JSON or binary, Ansible might modify it (because
# it is first decoded and later re-encoded). Base64-encoding the
# file directly after reading it prevents this to happen.
data: "{{ lookup('file', 'config/traefik.static.yml') | b64encode }}"
data_is_b64: true
state: present
Le 1er déploiement va bien se dérouler, mais le 2e va échouer avec ce message d’erreur :
<hostname> failed | msg: Error removing config traefik: 400 Client Error
for http+docker://localhost/v1.41/configs/wqpa6u3jdcj5id6speqyf63oa:
Bad Request ("rpc error: code = InvalidArgument desc = config 'traefik'
is in use by the following service: traefik")
Le problème apparait à la fin du message : Docker ne permet pas de redéfinir une config ou un secret si un autre du même nom existe déjà. On pourrait avoir envie de rapidement supprimer puis recréer l’élement pendant le déploiement d’un nouveau service, mais il faudrait au préalable stopper l’existant, ce qui peut être gênant, voire absolument impossible selon les cas (mise en prod sans interruption de service).
Le playbook n’est plus reproductible.
Rotation de versions
Heureusement, Ansible propose une meilleure solution avec l’option rolling_versions
qui n’apparait pas dans
les exemples de la doc officielle.
Concrètement, à chaque execution la tâche va créer une nouvelle config en suffixant le nom de base avec
“_v1’, “_v2”, “_v3”, etc. Les nouveaux services peuvent démarrer directement avec la nouvelle version
pendant que les anciens continuent d’utiliser la précédente. Simple.
En plus de ça, le module offre une autre option bien pratique : versions_to_keep
. On peut indiquer
le nombre d’anciennes configs à garder. Le ménage est fait à chaque exécution.
- name: Push config
community.docker.docker_config:
name: traefik
data: "{{ lookup('file', 'config/traefik.static.yml') | b64encode }}"
data_is_b64: true
state: present
rolling_versions: true
versions_to_keep: 3
register: traefik_cfg
Attention : il ne faut pas oublier de sauvegarder le résultat dans une variable (avec register
) pour retrouver
la bonne config
au moment du déploiement.
Dans mon cas, je démarre directement depuis une stack.yml
que j’ai envoyé sur le host en utilisant
core.builtin.template
. J’ai donc accès au dict traefik_cfg
dans mon template, le nom final de la
config crée est donc accessible avec {{ traefik_cfg.config_name }}
.
version: "3.8"
configs:
{{ traefik_cfg.config_name }}:
external: true
services:
proxy:
image: traefik:2.9
# [...]
configs:
- source: "{{ traefik_cfg.config_name }}"
target: /etc/traefik/traefik.yml
mode: 0400
# [...]
Les options sont identiques pour le module community.docker.docker_secret
.
Crédit photo : iMattSmart