Autres
CI/CD d'une infra
Apprendre des technologies pour l'infra et savoir le faire avec qualité
🗓
Dernière maj le 14 octobre 2020
Le but de ce TP est de monter une pipeline de test d'infra et de code d'infra (Ansible) maison. Le tp permet de vous apprendre certaine technologie, au menu :

0. Setup environment

Poste de travail

Pendant le TP, je vous conseille d'utiliser une machine GNU/Linux comme "poste de travail". Si vous avez un GNU/Linux en dur c'est OK, sinon je vous recommande une VM "poste de travail" (un CentOS peut faire l'affaire). En soit aucun pb pour utiliser un autre OS, il faut simplement être à l'aise pour l'utilisation de git, docker, Python pip, et ansible, sur votre machine.

Machines virtuelles

L'OS conseillé pour les VMs en cours est CentOS7. Afin de faciliter et accélérer le déploiement, on va utiliser Vagrant.
Téléchargez Vagrant pour votre OS, puis initialisez une box centos/7 :
1
$ mkdir workdir
2
$ cd workdir
3
# Génération d'un Vagrantfile de base
4
$ vagrant init centos/7
5
6
# Allumer la VM
7
$ vagrant up
8
9
# Récupérer un shell dans la machine
10
$ vagrant ssh
11
12
# Détruire la VM
13
$ vagrant destroy -f
Copied!
Il peut être bon de repackager la box avec certains éléments préconfigurés, afin de gagner du temps à chaque destroy/up, par exemple :
1
# Allumage de la VM
2
$ vagrant up
3
# Récupération d'un shell dans la VM
4
$ vagrant ssh
5
6
# Mise à jour des dépôts et du système
7
$ sudo yum update -y
8
9
# Installation de paquets additionels
10
$ sudo yum install -y vim
11
12
# Désactivation de SELinux
13
$ sudo setenforce 0
14
$ sudo sed -i 's/SELINUX=enforcing/SELINUX=permissive/' /etc/selinux/config
15
16
# On quitte la VM
17
$ exit
18
19
# On package la VM sous forme d'un .box
20
$ vagrant package --output cicd.box
21
22
# On ajoute la box à Vagrant
23
$ vagrant box add cicd.box --name cicd
Copied!
Je vous conseille notamment d'installer Docker dans votre box repackagée, on en aura souvent besoin par la suite.
Une fois repackagée, il est possible d'utiliser la box comme base dans un Vagrantfile :
1
Vagrant.configure("2") do |config|
2
config.vm.box = "cicd"
3
end
Copied!

Images Docker

Pour ce qui est des images Docker, on va beaucoup réutiliser d'images déjà packagées par la communauté. Cela nous permettra d'aller un peu plus vite dans le TP, sans perdre du temps à écrire des Dockerfiles et build des images sur mesure.

I. Setup environnement Git

EDIT : vous pouvez utiliser le fichier docker-compose.yml :
1
version: "3.7"
2
3
services:
4
gitea:
5
image: gitea/gitea:1.10.3
6
environment:
7
- APP_NAME=Gitea
8
- USER_UID=1000
9
- USER_GID=1000
10
- ROOT_URL=http://gitea:3000
11
- SSH_DOMAIN=gitea
12
- SSH_PORT=2222
13
- HTTP_PORT=3000
14
- DB_TYPE=postgres
15
- DB_HOST=gitea-db:5432
16
- DB_NAME=gitea
17
- DB_USER=ci
18
- DB_PASSWD=ci
19
restart: always
20
volumes:
21
- gitea:/data
22
ports:
23
- "3000:3000"
24
- "2222:2222"
25
networks:
26
- ci
27
28
gitea-db:
29
image: postgres:alpine
30
container_name: gitea-db
31
restart: always
32
volumes:
33
- gitea-db:/var/lib/postgresql/data
34
environment:
35
- POSTGRES_DB=gitea
36
- POSTGRES_USER=ci
37
- POSTGRES_PASSWORD=ci
38
networks:
39
- ci
40
41
drone-server:
42
image: drone/drone:1.2.1
43
container_name: drone-server
44
ports:
45
- 80:80
46
- 9000
47
volumes:
48
- drone:/var/lib/drone/
49
restart: always
50
depends_on:
51
- gitea
52
environment:
53
- DRONE_RPC_SECRET=9c3921e3e748aff725d2e16ef31fbc42
54
- DRONE_OPEN=true
55
- DRONE_GITEA=true
56
- DRONE_NETWORK=ci
57
- DRONE_DEBUG=true
58
- DRONE_ADMIN=ci
59
- DRONE_USER_CREATE=username:ci,admin:true
60
- DRONE_SERVER_PORT=:80
61
- DRONE_DATABASE_DRIVER=postgres
62
- DRONE_DATABASE_DATASOURCE=postgres://ci:[email protected]:5432/postgres?sslmode=disable
63
- DRONE_GIT_ALWAYS_AUTH=false
64
- DRONE_GITEA_SERVER=http://gitea:3000
65
- DRONE_SERVER_HOST=drone-server:80
66
- DRONE_HOST=http://drone-server:80
67
- DRONE_SERVER_PROTO=http
68
- DRONE_TLS_AUTOCERT=false
69
- DRONE_AGENTS_ENABLED=true
70
networks:
71
- ci
72
73
drone-agent:
74
image: drone/agent:1.2.1
75
container_name: drone-agent
76
command: agent
77
restart: always
78
depends_on:
79
- drone-server
80
volumes:
81
- /var/run/docker.sock:/var/run/docker.sock
82
- drone-agent:/data
83
environment:
84
- DRONE_RPC_SERVER=http://drone-server:80
85
- DRONE_RPC_SECRET=9c3921e3e748aff725d2e16ef31fbc42
86
- DRONE_RUNNER_CAPACITY=1
87
- DRONE_RUNNER_NETWORKS=ci
88
networks:
89
- ci
90
91
volumes:
92
gitea: {}
93
gitea-db: {}
94
drone: {}
95
drone-agent: {}
96
97
networks:
98
ci:
99
name: ci
Copied!
Qui permet de monter facilement Gitea + Drone afin d'accélérer vos tests.
Pour ce qui est du dépôt git, on va utiliser Gitea. C'est une app minimaliste développée en go, qui permet d'héberger des dépôts Git.
Loin d'être fully-featured comme un Gitlab, Gitea opte plutôt pour un aspect modulaire : Gitea lui-même ne se charge que de gérer les dépôts git et leurs accès, il faudra lui greffer d'autres applications afin qu'ils profitent de fonctionnalités supplémentaires.
TO DO : écrire un Vagrantfile qui monte une machines virtuelle CentOS7
  • la VM va porter Gitea, Drone et run des jobs de build/test
  • SELinux doit être désactivé
  • 2048M RAM sont conseillés
  • pour lancer les services, libres à vous :
    • en dur
    • dans des conteneurs Docker (je vous le conseille, plus rapide et plus simple à faire évoluer pour faire joujou pendant le TP)
TO DO : mettre en place Gitea
  • installer Gitea en suivant la doc officielle
  • je vous conseille un conteneur Docker pour plus de flexibilité
N'oubliez pas de créer un utser admin lors de la première connexion à la WebUI
TO DO : Effectuer un push une fois que Gitea est fonctionnel
  • créer un premier dépôt de test
  • effectuer un premier push pour valider le bon fonctionnement de la solution

II. Mise en place de Drone

Drone est un outil léger permettant de mettre en place des pipelines de build et de test. Il se couple nativement très bien avec Gitea.
Comme beaucoup d'outils en son genre, il fonctionne sur un principe de master/runner :
  • le master expose la GUI, reçoit les ordres de build et de test, et demandent aux runners d'exécuter des tâches
  • le runner a pour charger d'exécuter les tâches demandées par le master : c'est lui qui effectue les builds/tests
    • on va utilise un runner de type Docker
    • c'est à dire que le serveur sera capable de lancer des conteneurs Docker afin d'y effectuer des tests
Dans le cadre du TP, ce sera la même VM qui portera le master et un runner Docker.
TO DO : configurer Drone pour le lier à Gitea
Les credentials sont les mêmes que l'utilisateur admin de Gitea.
TO DO : tester une première pipeline de test
  • créer un dépôt git dans la WebUI de Gitea
  • synchroniser le dépôt depuis l'interface de Drone
    • préciser que le dépôt est Trusted (paramètres du dépôt dans la WebUI de Drone)
  • cloner le projet
  • placer à la racine un fichier .drone.yml qui contient :
1
kind: pipeline
2
name: test-pipeline
3
type: docker
4
5
steps:
6
- name: say-hello
7
image: alpine
8
commands:
9
- echo 'HI THERE CICD'
Copied!
  • push le fichier
  • se rendre sur la WebUI de Drone pour voir le résultat

III. Ansible

Ansible est un outil de gestion et déploiement de configuration. Il permet de contrôler de façon centralisée et uniformisée un parc de machines. C'est désormais la techno open-source de référence pour ce qui est de la gestion de configuration.
Techno très demandée aujourd'hui, nous allons l'utiliser comme base afin de mettre en place des pipelines de CI/CD.
On ne va pas, dans ce cours, approfondir Ansible. Le but est simplement d'avoir des fichiers de code à tester, des applicatons à tester, build, et intégrer, mais aussi manipuler une technologie extrêmement demandée.
Quelques impératifs pour qu'Ansible fonctionne :
  • les fichiers Ansible sont au format yml
  • à l'intérieur de ces fichiers, on décrit ce qu'il faut installer et configurer sur les serveurs de destination
  • la machine qui possède les fichiers .yml doit pouvoir se connecter en SSH sur les serveurs de destination
  • l'utilisateur sur les serveurs de destination doit pavoir accès à des droits root (via sudo par exemple)
    • nécessaires pour beaucoup d'actions comme l'installation de paquets

0. Structure du dépôt Ansible

Vous devrez organiser votre dépôt Ansible selon un format standard :
Directory
Usage
inventory/
Contient l'inventaire des machines et les variables qui y sont liées
roles/
Contient l'ensemble des "roles" Ansible. Un "role" est un ensemble de tasks ayant un sens (par exemple un rôle "Apache" ou "MySQL")
playbooks/
La glu entre l'inventaire et les rôles : les playbooks décrivent quel rôle appliquer sur quelle machine

1. Création de playbooks

TO DO : setup Ansible
  • s'assurer que Python est installé sur les machines de destination
  • s'assurer qu'il existe un utilisateur pouvant accéder aux droits de root à l'aide de sudo sur les machines de destination
  • configurer un échange de clés SSH entre le poste de travail (qui héberge les fichiers yml Ansible) et les machines de destination
TO DO : un premier playbook
  • créer un inventaire contenant la machine à qualifier
  • créer un rôle qui installe et configure un serveur NGINX
  • créer un playbook qui permet de déployer le rôle NGINX sur la machine déclarée dans votre inventaire
Le dépôt Ansible est à déposer dans un endroit personnel (le homedir de votre utilisateur par exemple).
Exemple de structure de dépôt Ansible :
1
├── ansible.cfg
2
├── inventory
3
│ ├── cicd
4
│ │ └── hosts.yml
5
├── playbooks
6
│ └── test.yml
7
└─── roles
8
└── nginx
9
└── tasks
10
├── main.yml
11
└── install.yml
Copied!
Structure du fichier inventory/ci/hosts.yml :
1
all:
2
hosts:
3
node1:
4
ansible_host: <IP_MACHINE>
5
children:
6
ci:
7
hosts:
8
node1:
Copied!
Structure du fichier playbooks/test.yml :
1
- name: Test playbook
2
hosts: ci
3
roles:
4
- nginx
Copied!
Structure du fichier roles/nginx/tasks/main.yml :
1
- name: install epel-release
2
package:
3
name: epel-release
4
state: present
5
6
- name: install NGINX
7
package:
8
name: nginx
9
state: present
10
11
- name: setup default website
12
copy:
13
content: |
14
<h1>Hello CI</h1>
15
dest: /usr/share/nginx/html/index.html
16
17
- name: Copy NGINX startup script
18
copy:
19
src: start_nginx.sh
20
dest: /srv/start_nginx.sh
21
owner: root
22
group: root
23
mode: 700
24
25
- name: Starts nginx
26
command:
27
cmd: /srv/start_nginx.sh
28
creates: /var/run/nginx.pid
Copied!
Contenu de roles/nginx/files/start_nginx.sh
1
#!/bin/bash
2
3
if [[ -f /'var/run/nginx.pid' ]]; then
4
echo 'NGINX is already running'
5
exit 0
6
else
7
echo 'Starting NGINX'
8
nginx
9
fi
Copied!
TO DO : tester le bon déploiement du service NGINX et vérifier que le serveur Web est bien fonctionnel
  • NB : nous travaillerons essentiellement avec des conteneurs pour les tests. Or il n'existe pas de gestion de services (comme systemd) dans les conteneurs Vous ne pourrez donc pas utiliser quelque chose comme systemctl start nginx afin de démarrer NGINX. Le script start_nginx.sh est donc utilisé pour lancer le serveur.

2. Premiers tests

TO DO : tester le code ansible
TO DO : ajouter un rôle
  • ajouter un rôle qui déploie des utilisateurs, et le pousser sur le dépôt, afin de tester la pipeline

3. Molecule

Présentation

Molecule est un outil qui se couple à Ansible afin d'effectuer des tests d'infra et de conformité.
Le but de cette partie est d'effectuer des tests, à l'aide Molecule, sur le déploiement du rôle Ansible.
Le fonctionnement de Molecule est simple :
  • créer un environnement de test (conteneur, VM)
  • dérouler un rôle dans l'environnement
  • vérifier le bon déroulement du playbook
Par "vérifier le bon déroulement du playbook", on entend : vérifier que le playbook passe, que l'environnement est conforme à nos attentes après déroulement du playbook (est-ce que tel paquet a été bien installé ou tel port firewall a été correctement ouver) ou encore vérifier l'idempotence du playbook (en l'exécutant deux fois d'affilée).

Prise en main

Afin de prendre en main Molecule, il peut être bon de tester quelques commandes à la main.
TO DO : Installer Molecule (je vous recommande l'installation avec pip).
Molecule va nous permettre ici de tester le rôle nginx que nous venions d'écrire. Pour que Molecule accepte de tester notre rôle, il est nécessaire d'y ajouter quelques fichiers. Molecule permet de créer un rôle possédant une structure qui correspond aux bonnes pratiques Ansible, afin d'être testé correctement. Pour ce faire :
1
# Déplacement dans le répertoire qui contient les rôles:
2
$ cd roles
3
4
# On déplace notre ancien rôle
5
$ mv nginx nginx.old
6
7
# Création du nouveau rôle
8
$ molecule init role nginx
9
10
# Récupération des fichiers précédents
11
$ mv nginx.old/tasks/main.yml nginx/tasks/main.yml
12
$ mv nginx.old/files/start_nginx.sh nginx/files/start_nginx.sh
13
14
# Suppression de l'ancien module
15
$ rm nginx.old -r
Copied!
Le rôle est alors prêt à être tester :
1
$ cd roles/nginx
2
$ molecule test
Copied!

Setup dans la pipeline

TO DO : créer une pipeline qui utilise Molecule
  • éditer le drone.yml
  • la pipeline doit utiliser une image Docker qui contient Molecule
    • image officielle : quay.io/ansible/molecule:3.0.8
  • le test de la pipeline doit exécuter un molecule test
    • pour ce faire, vous allez devoir permettre au conteneur molecule de lui-même lancer des conteneurs. On peut le faire en montant le socket Docker /var/run/docker.sock dans le conteneur Molecule pour qu'il puisse lancer des conteneurs sur l'hôte
Auteur du TP et intervenant professionnel auprès des écoles du supérieur :
it4
GitLab
Note de coté pour Léolios :
1
version: "3.7"
2
3
services:
4
gitea:
5
image: gitea/gitea:1.10.3
6
environment:
7
- APP_NAME=Gitea
8
- USER_UID=1000
9
- USER_GID=1000
10
- ROOT_URL=http://gitea:3000
11
- SSH_DOMAIN=gitea
12
- SSH_PORT=2222
13
- HTTP_PORT=3000
14
- DB_TYPE=postgres
15
- DB_HOST=gitea-db:5432
16
- DB_NAME=gitea
17
- DB_USER=ci
18
- DB_PASSWD=ci
19
restart: always
20
volumes:
21
- gitea:/data
22
ports:
23
- "3000:3000"
24
- "2222:2222"
25
networks:
26
- ci
27
28
gitea-db:
29
image: postgres:alpine
30
container_name: gitea-db
31
restart: always
32
volumes:
33
- gitea-db:/var/lib/postgresql/data
34
environment:
35
- POSTGRES_DB=gitea
36
- POSTGRES_USER=ci
37
- POSTGRES_PASSWORD=ci
38
networks:
39
- ci
40
41
drone-server:
42
image: drone/drone:1.2.1
43
container_name: drone-server
44
ports:
45
- 80:80
46
- 9000
47
volumes:
48
- drone:/var/lib/drone/
49
restart: always
50
depends_on:
51
- gitea
52
environment:
53
- DRONE_RPC_SECRET=9c3921e3e748aff725d2e16ef31fbc42
54
- DRONE_OPEN=true
55
- DRONE_GITEA=true
56
- DRONE_NETWORK=ci
57
- DRONE_DEBUG=true
58
- DRONE_ADMIN=ci
59
- DRONE_USER_CREATE=username:ci,admin:true
60
- DRONE_SERVER_PORT=:80
61
- DRONE_DATABASE_DRIVER=postgres
62
- DRONE_DATABASE_DATASOURCE=postgres://ci:[email protected]:5432/postgres?sslmode=disable
63
- DRONE_GIT_ALWAYS_AUTH=false
64
- DRONE_GITEA_SERVER=http://gitea:3000
65
- DRONE_SERVER_HOST=drone-server:80
66
- DRONE_HOST=http://drone-server:80
67
- DRONE_SERVER_PROTO=http
68
- DRONE_TLS_AUTOCERT=false
69
- DRONE_AGENTS_ENABLED=true
70
networks:
71
- ci
72
73
drone-agent:
74
image: drone/agent:1.2.1
75
container_name: drone-agent
76
command: agent
77
restart: always
78
depends_on:
79
- drone-server
80
volumes:
81
- /var/run/docker.sock:/var/run/docker.sock
82
- drone-agent:/data
83
environment:
84
- DRONE_RPC_SERVER=http://drone-server:80
85
- DRONE_RPC_SECRET=9c3921e3e748aff725d2e16ef31fbc42
86
- DRONE_RUNNER_CAPACITY=1
87
- DRONE_RUNNER_NETWORKS=ci
88
networks:
89
- ci
90
91
volumes:
92
gitea: {}
93
gitea-db: {}
94
drone: {}
95
drone-agent: {}
96
97
networks:
98
ci:
99
name: ci
Copied!