From ec89b340804a3077a7c11f75d3e65df8b4ff756d Mon Sep 17 00:00:00 2001 From: Quentin Duchemin <quentinduchemin@tuta.io> Date: Wed, 14 Oct 2020 17:14:47 +0200 Subject: [PATCH] [Doc] Move to wiki --- README.md | 90 +------------ doc/guide_bonnes_pratiques.md | 229 ---------------------------------- doc/launch_service.md | 89 ------------- doc/new_service.md | 61 --------- doc/update_and_test.md | 180 -------------------------- 5 files changed, 2 insertions(+), 647 deletions(-) delete mode 100644 doc/guide_bonnes_pratiques.md delete mode 100644 doc/launch_service.md delete mode 100644 doc/new_service.md delete mode 100644 doc/update_and_test.md diff --git a/README.md b/README.md index 268910db..1cf0e1be 100644 --- a/README.md +++ b/README.md @@ -1,93 +1,7 @@ # Dockerfiles de Picasoft -**Lisez cette documentation avant toute chose : elle fait office de référence générale et contient des informations supplémentaires par rapport aux README des dossiers :D** - Ce dépôt centralise les Dockerfiles et autre ressources utilisées pour construire **et** déployer les images Docker tournant en production sur l'infrastructure de Picasoft. -## Philosophie - -Historiquement, Picasoft procède de la manière suivante : -* Construire les images Docker à partir de Dockerfiles sur une machine quelconque, puis les pousser manuellement sur un registre privé. -* Gérer un fichier Compose global par machine, qui contient la configuration et les secrets. - -Cette approche pose plusieurs problèmes. Comment savoir ce qu'il y a dans une image, si on perd le Dockerfile ? Quel Dockerfile correspond à quelle version de l'image ? Si on perd l'accès à la machine, comment récupérer la configuration, remonter rapidement le service ? Comment versionner les changements de configuration ? Revenir à la version d'il y a deux semaines ? - -L'objectif de ce dépôt est de rendre possible le déploiement de n'importe quel service Picasoft avec la procédure suivante : -* Cloner le dépôt -* Se rendre dans le répertoire du service -* Lancer un `docker-compose up -d` pour démarrer le service - -Son objectif secondaire est de pouvoir revenir à l'état antérieur d'un service. En versionnant toute la configuration nécessaire, revenir à une ancienne version du service revient à : -* Cloner le dépôt -* Checkout un commit quelconque -* Lancer le service normalement - -Son dernier objectif est de permettre de rendre publics nos fichiers Compose et la configuration, sans exposer les secrets (mots de passe, etc). - -Mises à part les données à proprement parler du service, il n'y a donc aucune différence entre un service lancé sur une de nos machines et un service lancé à partir de ce dépôt sur une machine quelconque, ce qui s'inscrit dans la philosophie Docker. - -## Contenu du dépôt - -Le dépôt est organisé en dossiers. Chaque dossier correspond à un *service* Picasoft au sens large, c'est-à -dire à un ensemble de conteneurs liés entre eux *techniquement* (*e.g.* Mattermost et sa base de données) et/ou *sémantiquement* (*e.g.* sites web statiques, relai et boîte à mail). - -Ainsi, chaque dossier de ce dépôt contiendra, selon les situations : - -* Zéro, un ou plusieurs `Dockerfile` permettant de construire des images personnalisées pour Picasoft, -* Un fichier `docker-compose.yml` permettant de lancer le service, -* Si possible, un ou des fichiers de configuration permettant de personnaliser le service selon nos besoins, -* Un fichier d'exemple de secrets (mots de passe...) nécessaires au lancement du service, -* Un `README.md` résumant les paramètres modifiables sur le dépôt, les mécanismes pour en rajouter, etc, -* Pour les images maison, un `CHANGELOG.md` résumant les modifications faites au fil des version. - -Chaque service a son propre fichier Compose, lui permettant d'être déployé indépendamment des autres. - -Des exemples concrets peuvent être trouvés au niveau de [pica-mattermost](./pica-mattermost) ou [pica-etherpad](./pica-etherpad). - -## Guides - -### Lancer ou redémarrer un service sur une machine de Picasoft - -Lire [la documentation de déploiement et de maintenance](./doc/launch_service.md). - -### Mettre à jour un service ou sa configuration - -Lire [la documentation de mise à jour des services](./doc/update_and_test.md). - -### Mettre en place un nouveau service - -Lire [la documentation de versionnage d'un nouveau service](./doc/new_service.md) et [les bonnes pratiques pour Docker](./doc/guide_bonnes_pratiques.md). - -### Cloner le dépôt - -Si vous clonez le dépôt sur une nouvelle machine virtuelle, il est important de faire quelques opérations. - -À moins qu'on ouvre une nouvelle machine, ça ne devrait pas arriver - le dépôt existe déjà dans `/DATA/docker/dockerfiles` sur toutes les machines actuelles. - -En effet, le dépôt sera partagé et les fichiers doivent être éditables par tous les membres de Picasoft. - -Or, avec Git, tout `git pull` va associer les nouveaux fichiers écrits au compte qui effectue la commande, avec des permissions d'écriture uniquement pour ce membre (pour plus de détails, se référer à la [documentation Git](https://git-scm.com/docs/git-init#Documentation/git-init.txt---sharedfalsetrueumaskgroupallworldeverybody0xxx) et comprendre la notion de `umask`). - -Une solution pour rendre le dépôt "partagé" (*i.e.* éditable par tous les membres du groupe `docker`), quelles que soient les permissions initiales, est la suivante. - -1. Éditer `/DATA/docker/dockerfiles/.git/config`, et ajouter, sous `[core]` : -``` -sharedrepository = group -``` - -Ainsi, le dépôt sera "partagé", c'est-à -dire que les fichiers créés seront en théorie partagé avec le groupe propriétaire du dépôt, qui devrait être `docker`. Voir [la documentation](https://git-scm.com/docs/git-init#Documentation/git-init.txt---sharedfalsetrueumaskgroupallworldeverybody0xxx). - -2. Pour être certain que les fichiers créés manuellement sur la machine par les utilisateurs soient accessibles par le groupe `docker`, on entrera la commande suivante : - -```bash -sudo setfacl -Rdm g:docker:rwx /DATA/docker/dockerfiles -``` - -Ainsi, les fichiers créés auront le groupe `docker` par défaut ainsi que les permissions d'écriture pour le groupe. +La documentation pour comprendre et utiliser ce dépôt se trouve sur le Wiki : https://wiki.picasoft.net/doku.php?id=technique:docker:start -3. On terminera, pour être sûr, par : -```bash -sudo chgrp -R docker /DATA/docker/dockerfiles -sudo chmod -R g+w /DATA/docker/dockerfiles -sudo chmod g-w /DATA/docker/dockerfiles/.git/objects/pack/* -find /DATA/docker/dockerfiles -type d -exec sudo chmod g+s {} \; -``` +Il est important de la mettre à jour en cas de modification majeure sur ce dépôt ou sur la façon de gérer les services. :D diff --git a/doc/guide_bonnes_pratiques.md b/doc/guide_bonnes_pratiques.md deleted file mode 100644 index 526337f5..00000000 --- a/doc/guide_bonnes_pratiques.md +++ /dev/null @@ -1,229 +0,0 @@ -## Introduction - -Ce mini-guide vous donne des pistes pour créer de nouveaux services, tout en restant harmonieux par rapport à l'infrastructure existante. - -Pour une plongée plus complète dans les différents concepts abordés ici, on pourra se référer aux documentations officielles. Il n'est pas nécessaire de les lire en entier pour travailler sur ce dépôt : elles seront surtout utiles pour comprendre l'existant. - -* [Une introduction à Docker : pourquoi et comment](https://docs.docker.com/engine/docker-overview/) -* [Référence pour l'écriture d'un Dockerfile](https://docs.docker.com/engine/reference/builder/) -* [Bonnes pratiques pour l'écriture des Dockerfile](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/) -* [Orchestrer le lancement de conteneurs avec Docker Compose](https://docs.docker.com/compose/) -* [Référence pour l'écriture de fichiers Docker Compose](https://docs.docker.com/compose/compose-file/) - -## Se baser sur un autre Dockerfile ? - -Tous les services tournant sur l'infrastructure de Picasoft sont voués à être pris en charge par ce dépôt. - -Il y a plusieurs cas. - -### Je veux faire tourner un service existant, tel quel - -Si un service existant a une image Docker officielle, référencée sur le Docker Hub, et qu'elle nous suffit, on s'en servira telle quel dans le `docker-compose.yml`. - -### Je veux customiser un service existant - -Supposons que l'image Docker officielle ne nous convienne pas, que l'on soit obligé de la modifier pour régler des problèmes de sécurité, ou tout simplement qu'on veuille l'étendre, il faut créer un Dockerfile plus complet. Il y a deux solutions : - -* Soit on part de l'image officielle, avec un `FROM`, et on travaille dessus en rajoutant des fichiers, en enlevant des paquets... Cette solution a l'inconvénient de multiplier les *layers* inutiles, et d'augmenter la taille de l'image. -* Soit on copie le Dockerfile de l'image officielle (c'est le cas pour [Mattermost](../pica-mattermost)), et on fait nos modifications. Cette solution a pour inconvénient de devoir se synchroniser avec les modifications du Dockerfile officiel à chaque mise à jour, s'il contient des améliorations ou corrections importantes. - -### Je veux écrire un Dockerfile de zéro. - -Allez-y ! :D - -## Dockerfile - -Une [série de recommendations](https://wiki.picasoft.net/doku.php?id=technique:docker:tips) est disponible ici pour l'écriture des Dockerfile. - -## Image de base - -Préférez vous baser sur une image Alpine (peu de paquets, donc peu de vulnérabilités) ou Debian. -Pour Debian, préférez la version stable la plus récente possible (Buster plutôt que Stretch) pour mitiger d'emblée un grand nombre de vulnérabilités. - -Préférez également les versions `slim`, qui contiennent moins de paquets et suffisent souvent. - -Pour les services basés sur un langage interprété comme Python, utilisez l'image officielle en appliquant les principes précédents. - -## Builds reproductibles - -L'idée derrière un build "reproductible", c'est que si je me rends sur un ancien commit de ce dépôt et que je lance un `docker build` dans un dossier, comme `pica-mattermost`, l'image finale doit être la même quel que soit le temps écoulé depuis ce commit. Pourquoi ? Pour pouvoir relancer le build d'une ancienne version et la remettre en production en cas de problème. - -Souvent, un Dockerfile va récupérer du code sur un dépôt Git, ou encore un binaire sur un site de téléchargement de releases. -Il est fortement déconseillé de faire un `git clone` ou un `wget` de la dernière version (`latest`, `master`...), ce qui rend le build non reproductible et dépendant de cette dernière version. - -Il est donc important de gérer la version du service en question, par exemple avec une variable `ARG SERVICE_VERSION=1.0.1` qui sera ré-utilisée dans l'URL de téléchargement. - -En outre il existe deux solutions pour récupérer du code existant, versionné sur un dépôt Git distant : -* Installer Git dans le Dockerfile, utiliser un `git clone` puis un `git checkout <tag>` sur la version souhaitée et copier le code dans l'image. -* Utiliser un [submodule](https://git-scm.com/book/fr/v2/Utilitaires-Git-Sous-modules) dans le dossier du service, en particulier si le dépôt où se trouve le code est de petite taille et qu'il n'utilise pas les tags. En effet, comme un submodule est lié à un numéro de commit, chaque commit de ce dépôt sera associé à un commit précis du dépôt distant. On peut donc retrouver l'état du code distant avec le numéro de commit du submodule associé au commit local. - -## Volumes - -Une fois que l'on a identifié les dossiers du conteneur où l'on a besoin de persistence, il y a plusieurs manières de procéder : -* Soit utiliser un dossier de l'hôte comme stockage, via un [bind mount](https://docs.docker.com/storage/bind-mounts/) -* Soit utiliser un [volume Docker](https://docs.docker.com/storage/volumes/), géré en interne par Docker. - -Chacun a ses avantages et inconvénients, voilà ce que l'on fait chez Picasoft : -* Pour **monter un ou des fichiers existants** dans un conteneur, on utilise un bind mount, -* Pour **assurer la persistence d'un dossier du conteneur**, on utilise un volume Docker. - -### Bind mounts - -Si le fichier à monter est versionné sur ce dépôt, on utilisera un chemin relatif. Sinon, on utilisera des chemins absolus, ce qui enlève du côté "indépendant des machines", mais parfois on ne peut pas faire autrement. Par exemple, quand des certificats sont stockés sur une machine de production dans un dossier spécifique, on est obligés d'y faire référence. - -Exemple : - -```yaml -[...] -services: - exemple: - [...] - volumes: - # Dossier contenant les certificats sur les machines de production : utilisation du chemin absolu - - /DATA/docker/certs/example.picasoft.net:/certs - # Fichier de configuration versionné dans le même dossier : utilisation du chemin relatif - - ./config.json:/etc/config.json -``` - -### Volumes Docker - -Si on veut indiquer qu'un des dossiers du conteneur doit persister au fil des re-créations, alors on utilise des volumes Docker. C'est typiquement le cas pour le dossier `/var/lib/postgresql/data` d'une base PostgreSQL, qui ne doit pas supprimer les données à chaque re-création du conteneur. - -Exemple : - -```yaml -[...] -volumes: - # Nom du volume Docker - db: - # On spécifie le nom exact - name: db - - services: - exemple: - volumes: - # On fait référence au "db" défini ci-dessus, et on le monte sur /mount_point - - db:/mount_point -``` - -Il est suggéré d'éviter les volumes déclarés `external` : -* Un volume créé en dehors de Compose peut être utilisé sans être déclaré `external` ; -* En revanche un volume non-créé et déclaré `external` fera échouer les commandes Compose. - -## Reverse-proxy - -Si le service est un service **HTTP(S)** (*i.e.* Web), on utilisera systématiquement le reverse-proxy Traefik et on bindera **pas** son port interne sur un port de l'hôte. - -En effet, Traefik permet de gérer tout pour nous : la redirection vers le bon conteneur et le bon port en fonction du nom de domaine, la création et le renouvellement des certificats, etc. - -Il suffit pour ce faire d'ajouter les bons labels, et d'ajouter le conteneur au réseau par défaut de Traefik, qui s'appelle `proxy`, et existe indépendamment de Docker Compose. - -Exemple à reprendre : - -```yaml -networks: - proxy: - external: true - -services: - exemple: - networks: - - proxy - labels: - # Traefik va prendre ce conteneur en compte - traefik.enable: true - # websecure correspond au port 443 de la machine (config Traefik) - # Remplacer <exemple> par le nom du conteneur - traefik.http.routers.<exemple>.entrypoints: websecure - # Il redirigera vers ce port, exposé par le conteneur - # Remplacer <exemple> par le nom du conteneur - # Remplacer app.picasoft.net par l'URL souhaitée - traefik.http.routers.<exemple>.rule: Host(`app.picasoft.net`) - # Lorsque l'utilisateur consulte cette URL - # Remplacer <exemple> par le nom du conteneur - # Remplacer 80 par le port du service - traefik.http.services.<exemple>.loadbalancer.server.port: 80 -``` - -## Healthcheck - -Les `HEALTHCHECK`, au sens Docker, permettent de vérifier qu'un conteneur est en "bonne santé", par exemple en lançant une commande de type `curl`. - -Ils sont en général définis dans le `Dockerfile`, ce qui force à reconstruire l'image si on veut les modifier. - -Il est donc intéressant de les [définir directement dans le fichier Compose](https://docs.docker.com/compose/compose-file/#healthcheck) pour plus de souplesse, voire d'écraser le `HEALTHCHECK` existant. - -Notez que lorsqu'un `HEALTHCHECK` est présent, Traefik ne prend pas en compte le conteneur tant que celui-ci n'est pas noté `healthy`. Si l'intervalle de vérification est d'une minute, il faudra donc au moins une minute pour que le conteneur soit accessible sur Internet. - -Si le service est "critique", il faudra donc choisir un intervalle court. - -## Système init - -Tous les systèmes Linux ont un système dit `init`, correspondant au processus avec le premier PID (1). Ce processus est le parent de tous les autres, et doit transmettre les signaux qu'il reçoit à ses enfants (par exemple, un signal de terminaison). - -Quand vous lancez un conteneur avec un script Shell ou Bash comme entrypoint, ce script a le PID 1. -S'il démarre ensuite l'application, il ne transmettra pas le signal de terminaison à ses enfants (fonctionnement normal des scripts shell). - -Le souci, c'est qu'un `docker stop` enverra un signal `SIGTERM` au script d'entrypoint, mais il ne sera pas transmis au service en lui-même, qui se terminera brutalement par un `SIGKILL` après expiration du timeout. - -Compose, depuis la version 3.7, adresse ce problème avec une directive très simple : -``` -services: - exemple: - init: true -``` - -[Plus d'informations sur ce lien](https://hynek.me/articles/docker-signals/) et [sur la documentation de Compose](https://docs.docker.com/compose/compose-file/#init). - -## Réseaux - -L'idée est de mettre dans des réseaux séparés les services n'ayant pas besoin de communiquer entre eux, pour améliorer la sécurité de l'infrastructure. - -Ainsi, la compromission d'un service dans un réseau particulier ne permet pas d'accéder aux autres services qui ne seraient pas exposés sur internet. - -Imaginons un service web et sa base de données. Le service web a besoin d'être exposé sur Internet, via Traefik, mais sa base de données n'a pas besoin. - -Ce qui nous donnerait quelque chose comme : - -```yaml -networks: - # C'est le réseau dans lequel se trouve - # Traefik sur toutes les machines. - proxy: - external: true - # Ce réseau est créé uniquement pour - # ce fichier Compose. - db: - -services: - exemple: - # On voit que le service est dans le réseau proxy, pour être - # accessible depuis Traefik, mais aussi dans le réseau db, pour - # pouvoir parler à la base de données. - networks: - - proxy - - db - # La base de donnée n'est que dans son réseau, et n'est donc - # pas accessible depuis Internet. - exemple_db: - networks: - - db -``` - -## Mise en place de TLS - -Si le service est un service **TLS**, mais n'est pas un service web, alors il a besoin de certificats. - -Par exemple, pour pouvoir se connecter au serveur LDAP de manière sécurisée, on utilise LDAPS, mais on ne passe pas par Traefik, qui ne gère que les services web. - -Ces certificats peuvent être générés par Traefik en lui "faisant croire" que c'est un service web, mais on ne peut pas passer par lui pour servir les requêtes. C'est le cas pour le LDAP, le serveur mail... on utilisera dans ce cas l'outil [TLS Certs Monitor](./pica-tls-certs-monitor). En pratique, cela revient à ajouter des labels dans le fichier Compose du dépôt. - -## Divers - -On utilisera une version récente dans les fichiers `docker-compose.yml`, et au minimum la 3.7 pour profiter des fonctionnalités présentes dans ce guide. - -```yaml -version: '3.7' -``` - -On préférera utiliser la politique `restart: unless-stopped` pour les services. Ceci évite qu'un service arrêté explicitement ne se relance tout seul au démarrage de la machine. diff --git a/doc/launch_service.md b/doc/launch_service.md deleted file mode 100644 index b27a6982..00000000 --- a/doc/launch_service.md +++ /dev/null @@ -1,89 +0,0 @@ -## Administration des services - -Chaque service versionné sur ce dépôt est lancé sur une des machines virtuelles de Picasoft. - -En particulier, les fichier `docker-compose.yml` présents dans chaque dossier, ainsi que les fichiers de configuration et de secrets, permettent de lancer n'importe quel service de façon autonome, sur n'importe quel machine. - -Pour chacune des opérations détaillées dans cette page, on suppose que : -* Vous savez sur quel machine tourne ou doit tourner le service (`pica01`, `monitoring`...) -* Vous avez une connexion SSH active sur cette machine -* Vous êtes dans le dossier `<clone_depot>/<service>`, sur la branche `master`. Le clone devrait se trouver dans `/DATA/docker/dockerfiles`. -* Vous êtes connecté au registre de production : on s'assure de bien avoir exécuté la commande `docker login registry.picasoft.net`. Les identifiants sont sur le [pass](https://gitlab.utc.fr/picasoft/interne/pass). - -La branche `master` devant refléter l'état des services en production, toutes les opérations sur les machines de production devront être réalisées depuis `master` : cela signifie que les branches sur lesquelles vous travaillez devront être fusionnées à la fin de vos tests, et donc avant la mise en production. - -### Opérations usuelles - -Consulter les logs d'un service à la recherche d'une erreur : - -```bash -# n est le nombre de lignes à afficher, optionnel -docker-compose logs -f [--tail <n>] -``` - -Redémarrer un service, par exemple si celui-ci a planté : - -```bash -# Le nom du service est optionnel : si on veut tout -# redémarrer, on ne le met pas. Sinon, on indique -# le nom du service présent dans le fichier -# docker-compose.yml. -docker-compose restart [service] -``` - -Re-créer le conteneur si nécessaire, par exemple suite à une mise à jour de l'image ou de la configuration : - -```bash -docker-compose pull -# Le nom du service est optionnel : si on veut tout -# redémarrer, on ne le met pas. Sinon, on indique -# le nom du service présent dans le fichier -# docker-compose.yml. -docker-compose up -d [service] -``` - -Le `docker-compose pull` est **essentiel** : il permet de récupérer les nouvelles versions des images et de s'assurer qu'on a bien la dernière version indiquée dans le Compose. - -Éteindre un service : -```bash -docker-compose stop -``` - -etc. - -Attention : un conteneur noté `Unhealthy` à cause d'un mauvais `HEALTHCHECK` sera **exclu** de Traefik, même s'il fonctionne bien! - -### Lancement d'un nouveau service - -Lorsque vous lancez un nouveau service, il y a parfois quelques opérations à faire avant de lancer les conteneurs. -En effet, bien qu'en théorie un `docker-compose up -d` suffise, il y a quelques cas particuliers. - -#### Secrets - -Les secrets, tels que les mots de passe, ne sont évidemment pas présents sur ce dépôt. Lorsque des secrets sont utilisés, ils sont **systématiquement** indiqués par la présence de fichiers dans un sous-répertoire `secrets`. - -Dans ce cas, on copiera tous les fichiers `.secrets.example` en `.secrets`, et on remplacera les valeurs. - -Cette opération n'est nécessaire que lors du premier lancement. -Il y a plusieurs situations. - -1. Le mot de passe est "inédit", et ne concerne pas les utilisateurs (comme le mot de passe d'un utilisateur de base de données). On pourra générer un mot de passe avec la commande suivante : - -```bash -< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c${1:-32};echo; -``` - -2. Le mot de passe est connu, par exemple le mot de passe d'un compte LDAP. On le récupérera sur le [pass](https://gitlab.utc.fr/picasoft/interne/pass). -3. Le mot de passe est "inédit", mais doit être accessibles aux administrateurs (par exemple le mot de passe d'administration d'Etherpad). On le créera directement dans le [pass](https://gitlab.utc.fr/picasoft/interne/pass) (`picapass insert...`) et on le copiera dans le fichier de secrets. - -#### Création des volumes - -Si des volumes sont déclarés `external` dans le fichier Compose (ce que nous ne [recommendons pas](./guide_bonnes_pratiques.md)), il faut les créer manuellement au prélable : - -```bash -docker volume create <volume> -``` - -#### Opérations manuelles - -Certains services (comme [le serveur mail](../pica-mail)) nécessitent des opérations manuelles : créer des dossiers, puis générer des clés ou des certificats... Il faudra être attentif à le faire avant tout lancement. diff --git a/doc/new_service.md b/doc/new_service.md deleted file mode 100644 index d66d5d47..00000000 --- a/doc/new_service.md +++ /dev/null @@ -1,61 +0,0 @@ -# Versionnage d'un nouveau service - -L'idée de ce dépôt est de rendre nos services **indépendants** des machines virtuelles sur lesquels ils sont lancés, c'est-à -dire qu'à partir de ce dépôt, on devrait pouvoir remonter sans aucun problème un service sur une machine quelconque (sauf les données, bien sûr). - -Cette page donne quelques pistes pour versionner un nouveau service sur ce dépôt. - -On pourra partir du dossier [template](../template) comme base. - -Pour le contenu des fichiers `Dockerfile` et `docker-compose.yml`, on se réferera au [guide des bonnes pratiques](./guide_bonnes_pratiques.md) en cas de doute. - -## Choix des fichiers à versionner - -Pour savoir si vous avez versionné tout les fichiers nécessaires, posez-vous la question suivante : - -> Si je fais un `git pull` puis un `docker-compose up -d`, est-ce-que mon service démarre correctement ? - -Si oui, vous avez versionné les fichiers nécessaires. Si non, il faut remédier au problème. La seule exception concerne les opérations nécessaires pour créer les secrets (voir plus bas). - -### Fichiers nécessaires - -Pour chaque service, on aura au moins : -* Un `README.md`, qui explique de quoi il s'agit, comment lancer le service, comment le mettre à jour, etc. On pourra s'inspirer des services existants et du template. -* Un `docker-compose.yml`, qui permet de lancer le service sur les machines de Picasoft. Les images utilisées doivent toujours avoir un tag de version précis (voir bonnes pratiques). - -### Image personnalisée - -Si on utilise une image construite par nos soins, il faut rajouter : -* Un ou plusieurs `Dockerfile`, qui permet(tent) de construire l'image. -* Un `CHANGELOG.md`, qui indique les modifications effectuées au fil des versions. - -Le Dockerfile peut contenir des directives `COPY` pour ajouter des fichiers à l'image. S'il s'agit d'un ou deux fichiers de configuration, ou d'un entrypoint, aucun souci pour le versionner sur ce dépôt. S'il s'agit de code à proprement parler, il est préférable de créer un dépôt spécifique qui contiendra le code du service, et de faire un `git pull` dans le `Dockerfile` (ou d'utiliser un sous-module Git). - -En effet, ce dépôt n'est pas voué à contenir le code des services, mais simplement les fichiers nécessaires pour construire l'image, configurer et lancer le service. - -### Fichiers de configuration - -Il est préférable de versionner la configuration du service sur ce dépôt, pour pouvoir relancer rapidement le service sur une machine quelconque sans devoir récupérer la configuration sur l'ancienne machine. - -Cependant, si la configuration est souvent modifiée via l'interface d'administration du service, il est préférable de ne pas la versionner. Par exemple, Mattermost utilise un fichier `config.json`, mais on le modifie essentiellement à travers la Console Administrateur (interface graphique). Versionner ce fichier obligerait à modifier la configuration "à la main", puis à redémarrer Mattermost à chaque changement de paramètre. - -Par contre, Etherpad utilise un fichier `config.json` qui n'est pas modifiable via une interface graphique, et qui est pris en compte uniquement au démarrage. C'est donc un bon fichier à versionner. - -La *rule of thumb* est donc la suivante : on versionnera **tous les fichiers qui ne sont pas modifiés dynamiquement quand le service est lancé**. - -### Cas particuliers - -Certaines images officielles demandent d'effectuer des opérations manuellement avant de les lancer (initialisation de base de données, etc). - -Dans ce cas, on pourra créer un `entrypoint` qui remplace l'officiel et qui effectue ces opérations si jamais le service est lancé pour la première fois, puis qui lance le service normalement. On pourra marquer le fait qu'un service a déjà été initialisé en créant un fichier "marqueur" dans un volume. Voir [Plume](../pica-plume) pour un exemple. - -De manière générale, on hésitera pas à faire un `entrypoint` personnalisé si celui de l'image officielle ne suffit pas à nos besoins et qu'on ne peut pas configurer le service comme on le veut grâce aux variables d'environnement. - -## Gestion des secrets - -Un service a souvent besoin de secrets pour démarrer. Par secrets, on entend souvent mot de passe, ou clé privée. - -En général, les secrets sont configurés via des variables d'environnement. Quand c'est le cas, on créera un dossier `secrets`, puis des fichiers en `.secrets.example` qui contiennent une variable d'environnement par ligne. Voir les services existants pour un exemple. - -Quand le service demande les secrets en clair dans un fichier de configuration, et qu'on ne peut pas faire référence aux variables d'environnement dans le fichier, on gardera le système des variables d'environnement et on remplacera l'`entrypoint` pour injecter les variables dans le fichier de configuration. - -On trouvera un exemple dans [pica-metrics-bot](../pica-metrics-bot), où l'on voit que le fichier de configuration contient des marqueurs (`INFLUXDB_USER` par exemple), qui sont remplacés au démarrage du service par les valeurs des variables d'environnement correspondantes. diff --git a/doc/update_and_test.md b/doc/update_and_test.md deleted file mode 100644 index 66cc18e3..00000000 --- a/doc/update_and_test.md +++ /dev/null @@ -1,180 +0,0 @@ -# Mise à jour et test d'un service - -Cette page indique les différentes manières de mettre à jour un service et de le tester, avant sa mise en production. - -On peut vouloir mettre à jour : -* Le service (*e.g.* changer un numéro de version dans le Dockerfile), -* La configuration (éditer un fichier quelconque), -* La configuration des volumes (*e.g.* changer le point de montage du Docker Compose), -* etc. - -Dans tous les cas, les modifications apportées à n'importe quel fichier de ce dépôt **doivent être poussées sur ce dépôt**. -Vous pouvez effectuer les modifications sur votre machine ou sur une machine de Picasoft, du moment qu'elles sont synchronisées avec le dépôt. - -## Comment mettre à jour un service ? - -Il est pertinent de créer une branche avant chaque mise à jour où vous n'êtes pas sûr des modifications, et ne de fusionner dans `master` que lorsque les tests ont été effectués. - -### Mise à jour de la configuration - -On appelle configuration tout ce qui ne nécessite pas de reconstruire une image : -* Modification du `docker-compose.yml` (environnement, point de montage, tag d'une image officielle...) -* Modification d'un fichier de configuration -* Modification d'une page web d'accueil -* etc. - -Il suffit de mettre à jour les fichiers souhaitées, éventuellement de signaler les changements à l'équipe ou en commentaire, puis de synchroniser avec le dépôt. - -Si vous mettez à jour le tag d'une image officielle (*e.g* `postgres:10` → `postgres:12`), lisez au préalable la documentation de mise à jour ! Certaines nécessitent des interventions manuelles, et le README du service le précise en général. - -Toute image indiquée dans le fichier Compose **doit avoir un tag** (*e.g* `postgres:12` et pas `postgres`). N'utilisez jamais un tag vide (équivalent à `latest`). - -### Mise à jour d'une image maison - -Apportez vos modifications au Dockerfile (ajout d'un paquet...). Vous pouvez travailler en local et tester l'image sur votre machine si vous voulez. - -Si le code du service à copier dans l'image est intégré au dépôt par un submodule, on lancera la commande suivante pour le mettre à jour à la dernière version. Voir la documentation des submodules pour utiliser un tag ou une branche spécifique. -``` -git submodule update --recursive --remote <dossier du service> -``` - -Synchronisez ensuite le tout avec le dépôt (`git commit`, `git push...`). - -## Comment tester le service mis à jour ? - -Avant de mettre en production un service, il faut s'assurer que la mise à jour n'a rien cassé. Même si vous avez testé la nouvelle image sur votre machine, il faut également la tester sur l'infrastructure de Picasoft, en particulier parce que les services ont souvent besoin de Traefik pour fonctionner. - -Pour ce faire, rendez-vous sur `pica01-test`, dans le clone du dépôt (usuellement `/DATA/docker/dockerfiles`). - -Attention, notez que parfois il y a des problèmes indépendants du service quand celui-ci est accessible via Traefik : -* Tant que le `HEALTHCHECK` n'est pas passé, Traefik ne route pas vers le service (on vérifiera que le conteneur est noté `healthy` dans la sortie de `docker ps`) -* Parfois, Traefik ne route pas vers le service et renvoie un `Gateway Timeout`. Un `docker restart traefik` résoud le problème s'il vient de là . - -### Automatiquement - -Le script [docker_test.sh](../docker_test.sh) permet d'automatiser toutes les étapes pour tester un service et s'assurer qu'il fonctionne bien indépendamment des données précédentes, qui auraient été conservées dans un volume, par exemple. Il permet aussi de remplacer toutes les URL de production par des URL de test. - -Il a le désavantage qu'on comprend moins ce que l'on fait. Pour s'en servir, lancez : - -```bash -$ ./docker_test.sh <nom du dossier, e.g. pica-mattermost> -``` - -Vérifiez que les logs ne produisent aucune erreur et que le service fonctionne bien sur l'infrastructure de test, en y accédant via son URL par exemple (attention : remplacer `picasoft.net` par `test.picasoft.net` dans votre navigateur). - -### Manuellement - -Si vous voulez tester le service "à la main", ou que le script ne fonctionne pas pour vous, ou pour toute autre raison, suivez ces étapes : - -#### Synchronisation - -Synchronisez vous **exactement** avec le dépôt : - -```bash -# master ou votre branche perso -git checkout <votre branche> -git pull -``` - -#### Remise à zéro - -Si le service était déjà lancé sur la machine de test, éteignez-le et remettez les volumes à 0 : - -```bash -docker-compose down -v -``` - -Si des volumes sont déclarés comme `external` dans le fichier Compose, supprimez-les manuellement : - -```bash -docker volume rm <volume> -``` - -#### Construction des images - -Si le service utilise des images "maison", il faut les construire. Si le fichier Compose est bien fait, il suffit de lancer : - -```bash -docker-compose build -``` - -Sinon, il faut construire les images à la main. À répéter pour chaque image maison du service concerné : - -```bash -docker build [-f <Dockerfile>] -t <image> <context> -``` - -* Si le `Dockerfile` a un nom différent, on utilise le switch `-f` -* `<image>` doit avoir rigoureusement la valeur de la directive `image` du fichier Compose -* `<context>` est le contexte que reçoit Docker pour construire l'image (base pour copier les fichiers). En général, c'est le dossier du service (donc `.`, puisqu'on est dedans). - -#### Remplacement des URLs - -Si nécessaire. Certains services ne sont pas accessibles depuis Internet. - -Remplacez les URL de production (`.picasoft.net`) par des URL de tests (`.test.picasoft.net`), sauf dans le nom de l'image : - -* Si le service utilise Traefik, voir du côté de `traefik.http.services.<service>.loadbalancer.server.port` dans le fichier Compose -* Si le service utilise des fichiers de configuration, remplacez les références aux URL... - -### Créer les fichiers d'example - -Copier tous les `.secrets.example` en `.secrets`. - -#### Récupérer les nouvelles images - -Si le service utilise des images officielles, on s'assure qu'elles sont bien à jour avec la commande : - -```bash -docker-compose pull -``` - -#### Lancer le service - -```bash -docker-compose up -d -``` - -#### Vérifier que tout fonctionne bien - -```bash -docker-compose logs -f -``` - -On constate l'absence d'erreurs dans les logs, et si le service est accessible via Internet, on regarde que tout fonctionne bien depuis l'URL de test. - -### En cas de problème - -À partir de là , c'est carte blanche : on peut modifier la configuration, le Dockerfile, reconstruire les images, etc. L'infrastructure de test est un bac à sable. - -Si jamais les modifications ont permis de résoudre le problème, on les commit et on les synchronise avec le dépôt. - -### Revenir à l'état initial - -On se resynchronise avec l'état du dépôt en enlevant toutes les modifications induites par les tests. -```bash -git reset --hard -``` - -## Que faire après les tests ? - -Une fois qu'on est convaincu que la nouvelle version du service fonctionne bien, on peut le mettre en production. - -Au préalable, si le service utilise des images maison, on les pousse sur le registre de production. Pour ce faire, vous devez connecté au registre de production : on s'assure de bien avoir exécuté la commande `docker login registry.picasoft.net`. Les identifiants sont sur le [pass](https://gitlab.utc.fr/picasoft/interne/pass). - -```bash -docker-compose push -``` - -Cette commande pratique indique à Docker Compose de pousser toutes les images du fichier. - -On peut aussi le faire manuellement, pour chaque image maison (*i.e.* dont le nom commence par `registry.picasoft.net`) : - -```bash -# <image> doit correspondre exactement -# à la directive image du fichier Compose -# et commencer par registry.picasoft.net -docker push <image> -``` - -Une fois les images poussées, on se rend sur la machine de production et on [lance le service](./launch_service.md). -- GitLab