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.
Ce README fait office de documentation pour le fonctionnement et le formalisme de ce dépôt, les concepts ainsi que l'historique ne sont pas expliqués en détail. Pour une plongée plus complète dans les différents concepts abordés ici, on pourra se référer aux documentations officielles le cas échéant. Il n'est pas nécessaire de les lire en entier pour travailler sur ce dépôt : ils seront surtout utiles pour comprendre l'existant.
Ce `README` fait office de documentation pour le fonctionnement et le formalisme de ce dépôt, les concepts ainsi que l'historique ne sont pas expliqués en détail. 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 : ils 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/)
...
...
@@ -39,20 +39,20 @@ Ce README fait office de documentation pour le fonctionnement et le formalisme d
*[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/)
Ce dépôt utilise la [chaîne d'intégration de Gitlab](https://docs.gitlab.com/ee/ci/), ou **CI**, pour automatiser certaines tâches dès lors que des changements ont lieu sur le dépôt. L'idée est d'automatiser certaines tâches, ce qui permet d'améliorer la sécurité de l'infrastructure, d'unifier les procédures et de garder un historique clair des modifications, comme nous le verrons ci-après.
Ce dépôt utilise la [chaîne d'intégration de Gitlab](https://docs.gitlab.com/ee/ci/), ou **CI**, pour automatiser certaines tâches dès lors que des changements ont lieu sur le dépôt. L'idée est d'automatiser certaines tâches, ce qui permet d'améliorer la sécurité de l'infrastructure, d'unifier les procédures et de garder un historique clair des modifications.
**Disclaimer : un travail est actuellement en cours pour que l'intégralité des images utilisées en production soit centralisé sur ce dépôt. Il sera rendu public dès que ce sera le cas.**
## Contenu du dépôt
Ce dépôt contient (autant que faire se peut, il existe des exceptions) toutes les ressources permettant de déployer un service sur n'importe quelle machine virtuelle de l'infrastructure de Picasoft.
Ce dépôt contient toutes les ressources permettant de déployer un service sur n'importe quelle machine virtuelle de l'infrastructure de Picasoft, sans prérequis.
Cela signifie que le bon fonctionnement d'un service n'est pas dépendant de la machine virtuelle sur laquelle on essaye de le lancer (en adéquation avec la philosophie Docker).
Ainsi, chaque service versionné sur ce dépôt présentera :
Ainsi, chaque service versionné sur ce dépôt contiendra :
* Un Dockerfile permettant de construire l'image,
* Un Docker Compose permettant de lancer le service,
* Un `Dockerfile` permettant de construire l'image,
* 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 (mot de passe...) nécessaire au lancement du service,
* Un `README` résumant les paramètres modifiables sur le dépôt, les mécanismes pour en rajouter, etc.
...
...
@@ -61,7 +61,7 @@ Il n'y a pas de Docker Compose global : chaque service a son propre Docker Compo
## Principes de la CI
La CI est déclenchée dès lors qu'une modification susceptible d'altérer un service (c'est-à-dire, une modification conforme au [Formalisme du dépôt](#formalisme-du-dpt) est détectée.
La CI est déclenchée dès lors qu'une modification susceptible d'altérer un service, c'est-à-dire dès qu'une modification conforme au [Formalisme du dépôt](#formalisme-du-dpt) est détectée.
Elle va effectuer les opérations suivantes, automatiquement ou manuellement suivant le contexte (voir [Étapes manuelles ou automatiques ?](#tapes-manuelles-ou-automatiques-)) :
* Initialisation des variables (nom et version de l'image modifiée),
...
...
@@ -69,7 +69,7 @@ Elle va effectuer les opérations suivantes, automatiquement ou manuellement sui
* Analyses de sécurité,
* Push vers le registre de production.
Chaque étape peut échouer et bloque les étapes ultérieures. Il faudra régler les problèmes et mettre à jour le dépôt afin de relancer la CI.
Chaque étape peut échouer et bloquer les étapes ultérieures. Il faudra régler les problèmes et mettre à jour le dépôt afin de relancer la CI.
### Des analyses de sécurité ?
...
...
@@ -89,13 +89,13 @@ La mise à jour d'un `Dockerfile` entraîne le lancement de l'ensemble de la CI.
La mise à jour d'une liste blanche de CVE (voir [Formalisme du dépôt](#formalisme-du-dpt)) déclenche uniquement l'analyse de sécurité statique sur la dernière image construite (pas de nécessité de reconstruire l'image).
La mise à jour d'un fichier Docker Compose déclenche toutes les analyses de sécurité sur la dernière image construite (pas de nécessité de reconstruire l'image).
La mise à jour d'un `docker-compose.yml` déclenche toutes les analyses de sécurité sur la dernière image construite (pas de nécessité de reconstruire l'image).
La mise à jour d'un fichier autre donne la possibilité de déclencher manuellement les étapes de la CI : on appréciera au cas par cas s'il est nécessaire de reconstruire l'image. Par exemple, mettre à jour le README ou un fichier de secrets d'exemple ne devrait pas déclencher la CI, tandis que mettre à jour un fichier de configuration devrait déclencher la CI.
La mise à jour d'un fichier autre donne la possibilité de déclencher manuellement les étapes de la CI : on appréciera au cas par cas s'il est nécessaire de reconstruire l'image. Par exemple, mettre à jour le `README` ou un fichier de secrets d'exemple ne devrait pas déclencher la CI, tandis que mettre à jour un fichier de configuration devrait déclencher la CI.
## Mettre à jour un service existant
Cette section suppose que l'on veut modifier un service qui passe déjà la CI (exemples : Mattermost, Etherpad, Dokuwiki, Backup des BDD...).
On veut modifier un service qui passe déjà la CI (exemples : Mattermost, Etherpad, Dokuwiki, Backup des BDD...).
Modifier peut vouloir dire :
* Mettre à jour le service (*e.g.* changer un numéro de version dans le Dockerfile),
...
...
@@ -105,7 +105,7 @@ Modifier peut vouloir dire :
### Procédure standard
Il suffit de récupérer ce dépôt, de faire les mises à jour, de commit les changements et de les pousser. Attention, la CI se déclenche en regardant les modifications apportées lors du **dernier commit**. Si vous modifiez le Dockerfile dans un commit, puis le README dans un autre, et que vous poussez le tout, la construction ne se déclenchera pas automatiquement.
Il suffit de récupérer ce dépôt, de faire les mises à jour, de commit les changements et de les pousser. *Attention, la CI se déclenche en regardant les modifications apportées lors du dernier commit. Si vous modifiez le `Dockerfile` dans un commit, puis le `README` dans un autre, et que vous poussez le tout, la construction ne se déclenchera pas automatiquement.*
Vous pouvez suivre les différentes étapes de la CI dans la section [Pipelines](https://gitlab.utc.fr/picasoft/projets/dockerfiles/pipelines), et il est **indispensable** de lire les logs des différentes étapes en cliquant sur chacune d'entre elles. Si la construction et les analyses de sécurité se passent bien, tous les voyants sont au vert (sauf la dernière étape, qui doit être déclenchée manuellement). Sinon, référez-vous à la section [En cas d'erreur](#en-cas-derreur).
...
...
@@ -123,13 +123,14 @@ L'erreur peut aussi venir d'un nom d'image mal formaté dans le Docker Compose (
#### Lors de l'analyse de sécurité statique
Cette analyse échoue lorsque des CVE avec une classification`High` ou plus sont détectées. Les CVE de niveau plus faible étant inévitables, la CI n'échoue pas et se contente de les afficher.
Cette analyse échoue lorsque des CVE avec une criticité`High` ou plus sont détectées. Les CVE de niveau plus faible étant inévitables, la CI n'échoue pas et se contente de les afficher.
Clair vous indique alors quels paquets sont vulnérables. Plusieurs choix s'offrent à vous : mettre la CVE en liste blanche ou mitiger la CVE.
Une CVE est mise en liste blanche en l'ajoutant au fichier `clair-whitelist.yml`, dont on trouvera un exemple [ici](./pica-mattermost/clair-whitelist.yml). Il faut spécifier le nom de la CVE, le paquet affecté et la raison de la mise en liste blanche. Une mise en liste blanche est acceptable si :
* Clair détecte des vulnérabilités présentes dans des paquets installés dans des conteneurs intermédiaires mais ne faisant pas partie de l'image finale (cas des [multi-stage builds](https://docs.docker.com/develop/develop-images/multistage-build/)). On utilisera le motif *Paquet non présent*,
* Clair détecte des vulnérabilités sur le paquet `linux`. En effet, le noyau utilisé par le conteneur est celui de l'hôte. On utilisera le motif *Vulnérabilité Linux*,
Une CVE est mise en liste blanche en l'ajoutant au fichier `clair-whitelist.yml`, dont on trouvera un exemple [ici](./pica-mattermost/clair-whitelist.yml). Il faut spécifier le nom de la CVE, le paquet affecté et la raison de la mise en liste blanche.
Une mise en liste blanche est **acceptable** si :
* Clair détecte des vulnérabilités sur le paquet `linux`. En effet, le noyau utilisé par le conteneur est celui de l'hôte. On utilisera le motif `Vulnérabilité Linux`,
* Le paquet est à jour et ne peut pas être installé dans une version différente à cause de dépendances d'autres paquets,
* Le paquet ne peut pas être supprimé,
* Il n'existe pas de contre-mesure à la vulnérabilité,
...
...
@@ -141,7 +142,7 @@ Ensuite, on pousse les modifications et la CI ré-effectuera les tests de vulné
#### Lors de l'analyse de sécurité dynamique
Il est fort probable que l'erreur provienne du Docker Compose, et que Docker Bench for Security n'arrive pas à lancer votre ou vos conteneur(s), par exemple à cause d'un fichier manquant ou d'un mauvais formattage.
Il est fort probable que l'erreur provienne d'un mauvais Docker Compose, et que Docker Bench for Security n'arrive pas à lancer votre ou vos conteneur(s), par exemple à cause d'un fichier manquant ou d'un mauvais formattage.
## Déployer un service
...
...
@@ -149,7 +150,7 @@ Une fois les premières étapes de la CI passées, il est temps de déployer le
À ce stade, l'image est construite, saine, et poussée sur le registre de test. Avant de la mettre en production, il faut vérifier que le service se lance bien comme prévu. Pour ce faire, on se rend sur une machine virtuelle de test (exemple : `pica01-test.picasoft.net`), on clone le dépôt ou on se rend dans `/DATA/docker/dockerfiles` s'il existe.
Pour s'assurer que le service est fonctionnel, il faut s'assurer qu'il démarre indépendamment de ce qui existe sur la machine. Le script [`docker_test.sh`](./docker_test.sh) s'occupe de tout cela pour vous. Il suffit de lancer la commande suivante `$ ./docker_test.sh <nom du dossier>` pour effectuer les opérations suivantes :
Pour s'assurer que le service est fonctionnel, il faut s'assurer qu'il démarre indépendamment de ce qui existe sur la machine. Le script [`docker_test.sh`](./docker_test.sh) s'occupe de tout cela pour vous. Il suffit de lancer la commande `$ ./docker_test.sh <nom du dossier, e.g. pica-mattermost>` pour effectuer les opérations suivantes :
* Mise à jour du dépôt dans sa dernière version,
* Remplacement des URL en `picasoft.net` par `test.picasoft.net`,
* Création d'un fichier de secrets avec les valeurs d'exemple du dépôt,
...
...
@@ -159,9 +160,7 @@ Pour s'assurer que le service est fonctionnel, il faut s'assurer qu'il démarre
* Lancement de Docker Compose,
* Affichage des logs.
Vous pouvez effectuer ces opérations à la main si vous le souhaitez.
Regardez que les logs ne produisent aucune erreur, et testez si le service fonctionne bien sur l'infrastructure de test. Si ce n'est pas le cas, il faut revoir le Dockerfile ou la configuration.
Vérifiez que les logs ne produisent aucune erreur et que le service fonctionne bien sur l'infrastructure de test.
Lorsque tout est bon, on retourne sur la page [Pipelines](https://gitlab.utc.fr/picasoft/projets/dockerfiles/pipelines), on se rend dans la dernière pipeline et on lance le push de l'image sur le registre de production.
...
...
@@ -174,11 +173,9 @@ On se rend ensuite sur la machine de production, dans le dossier `/DATA/docker/d
* Lancement de Docker Compose,
* Affichage des logs.
À ce stade, si quelque chose ne fonctionne pas, il y a un sérieux problème.
## Formalisme du dépôt
Pour que la CI et le déploiement des services fonctionne correctement, il faut respecter plusieurs contraintes sur la nomenclature, l'arborescence et le contenu des fichiers. Il ne s'agit pas ici de bonnes pratiques - voir le rapport Docker Bench for Security pour cela.
Pour que la CI et le déploiement des services fonctionnent correctement, il faut respecter plusieurs contraintes sur la nomenclature, l'arborescence et le contenu des fichiers. Il ne s'agit pas ici de bonnes pratiques - voir le rapport Docker Bench for Security pour cela.
* Chaque service géré par la chaîne d'intégration est dans un sous dossier `pica-*`,
* Chaque sous dossier contient au moins un `Dockerfile`, un `docker-compose.yml` et un `clair-whitelist.yml`,
...
...
@@ -194,7 +191,7 @@ Un exemple concret peut être trouvé au niveau de [pica-mattermost](./pica-matt
Tous les services répertoriés sur ce dépôt ne passent pas encore la chaîne d'intégration. Pour les migrer dessus, il suffit de modifier les sous-dossiers des services pour qu'ils respectent le [Formalisme du dépôt](#formalisme-du-dpt).
Ensuite, il faudra les supprimer du `docker-compose.yml` global présent dans `/DATA/docker` sur les machines, et les déployer en utilisant des Docker Compose individuels.
Ensuite, il faudra les supprimer du `docker-compose.yml` global présent dans `/DATA/docker` sur les machines, et les déployer en utilisant les Docker Compose individuels.
## Troubleshooting
...
...
@@ -212,7 +209,7 @@ Il est aussi possible de relancer une pipeline plus ancienne, pour reconstruire
## Exemple
On veut mettre à jour Mattermost de la version 5.18.0 à la version 5.19.0. On fait les modifications dans le Dockerfile et on pousse le commit. Les modifications dépendent évidemment du service, on peut les voir [ici pour l'exemple](https://gitlab.utc.fr/picasoft/projets/dockerfiles/commit/472e0d72534659e215270adade6746b65b6f65e3).
On veut mettre à jour Mattermost de la version 5.18.0 à la version 5.19.0. On fait les modifications dans le Dockerfile et on pousse le commit. Les modifications dépendent évidemment du service, on peut les voir le commit [472e0d72](https://gitlab.utc.fr/picasoft/projets/dockerfiles/commit/472e0d72534659e215270adade6746b65b6f65e3) pour l'exemple.
Le [pipeline](https://gitlab.utc.fr/picasoft/projets/dockerfiles/pipelines/54707) se lance automatiquement. Je suis les logs des jobs un par un pour vérifier que tout se passe bien.
...
...
@@ -281,7 +278,6 @@ Je me rends ensuite sur la machine de production (`pica02` à ce jour) et je lan