README.md 13.3 KB
Newer Older
Rémi Cailletaud's avatar
exo2    
Rémi Cailletaud committed
1
# TP intégration et déploiement continus #
Rémi Cailletaud's avatar
Rémi Cailletaud committed
2

Rémi Cailletaud's avatar
Rémi Cailletaud committed
3
*Prérequis : des bases de Git, Python, Docker.*
Rémi Cailletaud's avatar
exo2    
Rémi Cailletaud committed
4

Rémi Cailletaud's avatar
Rémi Cailletaud committed
5
6
Le TP consiste en une suite de petits exercices consistant à écrire à chaque fois un fichier .gitlab-ci.yml.  
Le code source est fourni. Après l'échauffement, nous travaillerons sur deux (tout) petits codes :
Rémi Cailletaud's avatar
exo2    
Rémi Cailletaud committed
7
8

* python-extension : une extension python en C, pour voir un peu de vraie compilation, quand même ;
Rémi Cailletaud's avatar
Rémi Cailletaud committed
9
* python-dice : un module pur python d'une ligne, qui tire les dés. Avec lui nous irons des tests au déploiement en production en passant par la mise en ligne de la documentation et la publication sur pypi.
Rémi Cailletaud's avatar
exo2    
Rémi Cailletaud committed
10

Rémi Cailletaud's avatar
Rémi Cailletaud committed
11
À chaque étape, nous fournissons les mots-clés à chercher dans la [documentation très complète de gitlab](https://docs.gitlab.com/ee/ci/yaml/README.html). Comme nous sommes gentils, nous fournissons aussi les solutions dans les fichiers .gitlab-ci-\*.yml. Vous pouvez tricher !
Rémi Cailletaud's avatar
exo2    
Rémi Cailletaud committed
12

Rémi Cailletaud's avatar
Rémi Cailletaud committed
13
*Note* : Nous conseillons fortement l'utilisation de virtualenv pour les tests locaux.
Rémi Cailletaud's avatar
Rémi Cailletaud committed
14

15
16
## Échauffement ##

Rémi Cailletaud's avatar
Rémi Cailletaud committed
17
### 1. Mon premier fichier .gitlab-ci.yml ###
Rémi Cailletaud's avatar
Rémi Cailletaud committed
18

Rémi Cailletaud's avatar
Rémi Cailletaud committed
19
Il s'agit d'un fichier au format YAML décrivant un ensemble d'étapes ([stages](https://docs.gitlab.com/ee/ci/yaml/#stages)) qui peuvent comporter plusieurs [jobs](https://docs.gitlab.com/ee/ci/yaml/#jobs).  
Rémi Cailletaud's avatar
exo2    
Rémi Cailletaud committed
20
21
Chaque job contient un [script](https://docs.gitlab.com/ee/ci/yaml/#script), exécuté par le Runner.

Rémi Cailletaud's avatar
Rémi Cailletaud committed
22
**But** : Afficher "Hello world", /etc/issue et l'heure. Aller dans l'interface de Gitlab (*CI/CD -> Pipelines*) et constater le résultat. On peut voir le nom du Runner qui a exécuté le job.
Rémi Cailletaud's avatar
exo2    
Rémi Cailletaud committed
23

Rémi Cailletaud's avatar
Rémi Cailletaud committed
24
25
*Note 1* : Chaque job est indépendant (chaque job peut-être exécuté sur un Runner différent...).  
*Note 2* : L'image par défaut est choisie lors de l'installation du Runner.
Rémi Cailletaud's avatar
exo2    
Rémi Cailletaud committed
26
27


28
### 2. Le choix de l'image docker ###
Rémi Cailletaud's avatar
exo2    
Rémi Cailletaud committed
29

Rémi Cailletaud's avatar
Rémi Cailletaud committed
30
Les jobs sont exécutés dans des conteneurs Docker. Vous pouvez choisir le conteneur à utiliser avec le mot-clés [image](https://docs.gitlab.com/ee/ci/docker/using_docker_images.html#define-image-and-services-from-gitlab-ci-yml).
Rémi Cailletaud's avatar
exo2    
Rémi Cailletaud committed
31

Rémi Cailletaud's avatar
Rémi Cailletaud committed
32
**But** : Utiliser l'image Ubuntu à la place de celle par défaut. Constater la différence.
Rémi Cailletaud's avatar
exo2    
Rémi Cailletaud committed
33

Rémi Cailletaud's avatar
Rémi Cailletaud committed
34
*Note 1* : Par défaut on utilise DockerHub, le repository Docker n'étant pas (encore) activé sur notre instance Gitlab.  
Rémi Cailletaud's avatar
exo3    
Rémi Cailletaud committed
35
*Note 2* : On peut choisir une image différente par job.
Rémi Cailletaud's avatar
exo2    
Rémi Cailletaud committed
36

37
### 3. Le pipeline ###
Rémi Cailletaud's avatar
exo3    
Rémi Cailletaud committed
38

Rémi Cailletaud's avatar
Rémi Cailletaud committed
39
On définit les Pipelines en déclarant plusieurs [stages](https://docs.gitlab.com/ee/ci/yaml/#stages). Chaque job définit à quelle étape il appartient grâce au mot-clé [stage](https://docs.gitlab.com/ee/ci/yaml/#stage). 
Rémi Cailletaud's avatar
exo3    
Rémi Cailletaud committed
40

Rémi Cailletaud's avatar
Rémi Cailletaud committed
41
Plusieurs jobs de la même étape pourront être exécuté en même temps si les ressources le permettent. En revanche, l'exécution d'une étape dépend du bon déroulement de la précédente, à moins d'en autoriser explicitement l'échec avec [allow_failure](https://docs.gitlab.com/ee/ci/yaml/#allow_failure).
Rémi Cailletaud's avatar
exo3    
Rémi Cailletaud committed
42

Rémi Cailletaud's avatar
Rémi Cailletaud committed
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
```mermaid
graph LR;
subgraph stage1
job1
end
subgraph stage2
job2_1
job2_2
end
subgraph stage3
job3
end
job1-->job2_1
job1-->job2_2
job2_1-->job3
job2_2-->job3
```

Rémi Cailletaud's avatar
Rémi Cailletaud committed
61
**But** : Définir 3 étapes test1, test2 et test3. Les étapes 1 et 3 comporteront chacune un job, et l'étape 2 en comportera deux, dont un qui échoue.  
Rémi Cailletaud's avatar
exo3    
Rémi Cailletaud committed
62
63
Observer le comportement dans l'interface, puis ajouter autoriser l'échec pour le job qui échoue.

Rémi Cailletaud's avatar
Rémi Cailletaud committed
64
*Note* : Avec [allow_failure](https://docs.gitlab.com/ee/ci/yaml/#allow_failure), le pipeline passe, mais vous voyez quand même un warning...
Rémi Cailletaud's avatar
exo3    
Rémi Cailletaud committed
65

66
### 4. Une étape manuelle ###
Rémi Cailletaud's avatar
exo4    
Rémi Cailletaud committed
67

Rémi Cailletaud's avatar
Rémi Cailletaud committed
68
Le mot-clé [when](https://docs.gitlab.com/ee/ci/yaml/#when) permet de définir quand le job est exécuté : en cas de succès (comportement par défaut), d'échec, quoi qu'il arrive, ou manuellement.
Rémi Cailletaud's avatar
exo4    
Rémi Cailletaud committed
69

Rémi Cailletaud's avatar
Rémi Cailletaud committed
70
**But** : Ajouter un job manuel au pipeline. Lancer son exécution depuis l'interface.
Rémi Cailletaud's avatar
exo4    
Rémi Cailletaud committed
71

Rémi Cailletaud's avatar
Rémi Cailletaud committed
72
*Note* : Contrairement aux autres jobs, les jobs manuels sont autorisés à échouer par défaut (pour ne pas bloquer le pipeline). On peut changer ce comportement, mais il faudra alors une intervention humaine pour débloquer le pipeline...
Rémi Cailletaud's avatar
exo4    
Rémi Cailletaud committed
73
74


75
### 5. Pousser des modifications sans lancer le pipeline ###
Rémi Cailletaud's avatar
Rémi Cailletaud committed
76

Rémi Cailletaud's avatar
Rémi Cailletaud committed
77
Dans certains cas, on veut pouvoir pousser des changements sans lancer le pipeline. Pour cela, il suffit de spécifier [skip-ci] ou [ci-skip] dans le message de commit.
Rémi Cailletaud's avatar
Rémi Cailletaud committed
78
79
80

**But** : Après une modification, pousser le changement en précisant [skip-ci] dans le message de commit. Observer le comportement dans l'interface.

81
82
### 6. Programmer des pipelines : les schedules ###

Rémi Cailletaud's avatar
Rémi Cailletaud committed
83
On peut programmer des pipelines pour qu'ils soient lancés à intervalles réguliers (par exemple pour les *Nightly Builds*).
84

Rémi Cailletaud's avatar
Rémi Cailletaud committed
85
**But** : Programmer un pipeline dans *CI/CD -> Schedule*. Puis, ajouter un job qui ne sera exécuté que lors des pipelines programmés à l'aide du mot-clé [only](https://docs.gitlab.com/ee/ci/yaml/#only-and-except-simplified)
86
87


88
## Compilation python ##
Rémi Cailletaud's avatar
Rémi Cailletaud committed
89

Rémi Cailletaud's avatar
Rémi Cailletaud committed
90
91
Le premier "vrai" projet est une très simple extension C pour python, histoire de voir un peu de vraie compilation, quand même. Les sources sont dans le dossier [python-extension](python-extension) et comporte trois fichiers :
* [addmodule.c](python-extension/addmodule.c), le fichier source ;
92
93
* [setup.py](python-extension/setup.py), le fichier Distutils ;
* [test_addone.py](python-extension/test_addone.py), le test unitaire correspondant.
Rémi Cailletaud's avatar
Rémi Cailletaud committed
94

95
96
### 7. Utilisation de l'image python et compilation ###

Rémi Cailletaud's avatar
Rémi Cailletaud committed
97
**But** : Utiliser l'image python pour compiler et installer le module à l'aide de Distutils.
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115

*Note* : Si vous voulez tester l'installation en local sur votre poste, utilisez un virtualenv...

### 8. Exécution ###

**But** : Ajouter une étape qui exécute le module.

*Note* : `python -c`

### 9. Test ###

**But** : Ajouter une étape qui teste le module avec le test unitaire.

### 10. Factoriser les scripts ###

On peut factoriser une partie des trois étapes précédemment créées grâce au mot-clés [before_script](https://docs.gitlab.com/ee/ci/yaml/#before_script-and-after_script).

**But** : Simplifier le fichier .gitlab-ci.yml...
Rémi Cailletaud's avatar
readme    
Rémi Cailletaud committed
116

117
## Dice, un projet complet ! ##
Rémi Cailletaud's avatar
Rémi Cailletaud committed
118

Rémi Cailletaud's avatar
Rémi Cailletaud committed
119
120
121
122
123
124
125
126
Nous allons maintenant travailler avec un petit module en pur python créé pour l'occasion. Les sources sont dans le dossier [python-dice](python-dice) :
* [dice.py](python-dice/dice.py) : le code du module, avé les commentaire dedans ;
* [setup.py](python-dice/setup.py) : le fichier Distutils ;
* [test_dice.py](python-dice/test_dice.py) : le test unitaire correspondant ;
* [doc/source](python-dice/doc/source) : la doc Sphinx ;
* [dice-http-server](python-dice/dice-http-server) : notre application web totalement disruptive, qui utilise notre module ;
* [Dockerfile](python-dice/Dockerfile) : à votre avis ?

Rémi Cailletaud's avatar
Rémi Cailletaud committed
127
128
### 11. Tests et publication de la documentation à l'aide de Gitlab Pages ###

Rémi Cailletaud's avatar
Rémi Cailletaud committed
129
Nous allons écrire un pipeline complet, qui teste la fonctionnalité et le style du code (avec *pycodestyle*, disponible dans *pip*) et qui génère et déploie la documentation (avec *sphinx*, disponible lui aussi dans *pip*). Pour la partie documentation, on utilisera [only](https://docs.gitlab.com/ee/ci/yaml/#only-and-except-complex) pour ne la générer que si le message de commit commence par `[doc]`. Cette étape spéciale est appelée [pages](https://docs.gitlab.com/ee/ci/yaml/#pages) et s'appuie sur une fonctionnalité appelée [Gitlab Pages](https://docs.gitlab.com/ee/user/project/pages/index.html) (original !).
Rémi Cailletaud's avatar
Rémi Cailletaud committed
130

Rémi Cailletaud's avatar
Rémi Cailletaud committed
131
**But** : Écrire un fichier .gitlab-ci.yml qui comporte deux étapes.
Rémi Cailletaud's avatar
Rémi Cailletaud committed
132
* La première étape comporte deux jobs, l'un qui teste le code à l'aide du test unitaire, et l'autre qui teste son style à l'aide de pycodestyle. Comme nous ne sommes pas des orthodoxes, admettons que dans certains cas le *coding style* peut ne pas être respecté...
Rémi Cailletaud's avatar
Rémi Cailletaud committed
133
* La seconde étape génère et déploie la documentation dans Gitlab Pages.
Rémi Cailletaud's avatar
Rémi Cailletaud committed
134
135
136
137
138
139

Vérifier qu'en ne respectant pas la [PEP8](https://www.python.org/dev/peps/pep-0008/), le pipeline émet une alerte.  
Vérifier que la documentation est bien déployée (le lien est disponible dans *Settings -> Pages*).

*Note* : Pour générer la documentation dans `build/sphinx/html`, utilisez la commande distutils `build_sphinx`.

Rémi Cailletaud's avatar
Rémi Cailletaud committed
140
141
### 12. Les (presque) inutiles et donc indispensables badges ###

Rémi Cailletaud's avatar
Rémi Cailletaud committed
142
L'utilitaire *coverage* (disponible dans *pip*) permet d'obtenir le taux de couverture de vos tests, indicateur intéressant de la qualité du code. Cette information (ainsi que le status du pipeline) peut apparaître par exemple dans votre doc grâce aux désormais très populaires [Badges](https://docs.gitlab.com/ee/user/project/pipelines/settings.html#test-coverage-report-badge).
Rémi Cailletaud's avatar
Rémi Cailletaud committed
143

Rémi Cailletaud's avatar
Rémi Cailletaud committed
144
**But** : Ajouter les tests de couverture au job de test. Dans *Settings -> CI/CD -> General pipelines*, spécifier la regexp permettant de récupérer le taux de couverture, et récupérer le code HTML et/ou Markdown à ajouter à la doc et/ou au fichier README.md.
Rémi Cailletaud's avatar
Rémi Cailletaud committed
145

Rémi Cailletaud's avatar
Rémi Cailletaud committed
146
*Note 1* : Pour lancer les tests de couverture et obtenir le rapport :
Rémi Cailletaud's avatar
Rémi Cailletaud committed
147
148
149
150
151
152
```bash
coverage run -m unittest discover
coverage report
```

*Note 2* : On vous aide pour la regexp... 
Rémi Cailletaud's avatar
Rémi Cailletaud committed
153

Rémi Cailletaud's avatar
Rémi Cailletaud committed
154
155
156
```ruby
TOTAL\s+\d+\s+\d+\s+(\d+\%)$
```
Rémi Cailletaud's avatar
Rémi Cailletaud committed
157

Rémi Cailletaud's avatar
Rémi Cailletaud committed
158
159
### 13. Le déploiement sur Pypi ###

Rémi Cailletaud's avatar
Rémi Cailletaud committed
160
Nous allons maintenant  déployer notre module sur l'instance de test de Pypi à l'aide de *twine*, disponible dans *pip*. Le déploiement ne se fait que pour les branches ou tags nommés "version-\*", grâce à l'utilisation du mot-clé [only](https://docs.gitlab.com/ee/ci/yaml/#only-and-except-simplified). Pour éviter de spécifier les identifiants dans le dépôt, nous utilisons les [variables Gitlab](https://docs.gitlab.com/ee/ci/variables/#variables) (*Settings -> CI/CD -> Variables*).
Rémi Cailletaud's avatar
Rémi Cailletaud committed
161

Rémi Cailletaud's avatar
Rémi Cailletaud committed
162
**But** : Ajouter une étape qui va construire et déployer le paquet sur Pypi si le nom de la référence (branche ou tag) commence par "*version-*". Utiliser les variables Gitlab pour les identifiants. Pousser un nouveau tag ou une nouvelle branche, vérifier que le paquet est bien déployé, et que l'installation est possible via pip.
Rémi Cailletaud's avatar
Rémi Cailletaud committed
163

Rémi Cailletaud's avatar
Rémi Cailletaud committed
164
*Note 1* : Construire une wheel dans le répertoire wheelhouse :
Rémi Cailletaud's avatar
Rémi Cailletaud committed
165
166
167
```bash
pip wheel . -w wheelhouse
```
Rémi Cailletaud's avatar
Rémi Cailletaud committed
168
169
170
*Note 2* : Utiliser l'instance de test de Pypi :  
`--repository-url https://test.pypi.org/legacy/` pour twine  
`--index-url https://test.pypi.org/simple/` pour pip.
Rémi Cailletaud's avatar
Rémi Cailletaud committed
171

Rémi Cailletaud's avatar
Rémi Cailletaud committed
172
*Note 3* : On ne peut déployer plusieurs fois la même version d'un paquet sur Pypi. Utilisez un numéro de version majeure correspondant au numéro de votre dépôt (ex : version 1.x pour le groupe formation-ci-01).
Rémi Cailletaud's avatar
Rémi Cailletaud committed
173

Rémi Cailletaud's avatar
Rémi Cailletaud committed
174
175
176
177
178
179
*Note 4* : Installer une version particulière d'un paquet Pypi :
```bash
pip install paquet==version
 ```

*Note 5* : Pour les gros projets multi-dépôts, il est conseillé d'utiliser des outils dédiés pour la gestion de secrets (comme [Hashicorp Vault](https://www.vaultproject.io/)) plutôt que les [variables Gitlab](https://docs.gitlab.com/ee/ci/variables/#variables).
Rémi Cailletaud's avatar
Rémi Cailletaud committed
180
181


Rémi Cailletaud's avatar
Rémi Cailletaud committed
182
183
### 14. Dockerception ###

Rémi Cailletaud's avatar
Rémi Cailletaud committed
184
[dice-http-server](python-dice/dice-http-server) est un petit serveur web qui permet d'utiliser dice. Par exemple, l'appel à la page http://server/4-6 tirera 4 dés à 6 faces. Nous allons maintenant construire un conteneur Docker et le déployer sur notre infrastructure. Comme nous sommes prudents, le déploiement de l'image sera manuel.
Rémi Cailletaud's avatar
Rémi Cailletaud committed
185

Rémi Cailletaud's avatar
Rémi Cailletaud committed
186
Pour cela, nous allons avoir besoin du [Service](https://docs.gitlab.com/ee/ci/services/) *docker:dind*, qui permet de fournir le démon docker au conteneur exécutant notre job. Notre instance de Gitlab ne fournissant pour l'instant pas de registry, nous utiliserons Docker Hub. Nous utiliserons à nouveau les [variables Gitlab](https://docs.gitlab.com/ee/ci/variables/#variables) pour spécifier les identifiants.
Rémi Cailletaud's avatar
Rémi Cailletaud committed
187

Rémi Cailletaud's avatar
Rémi Cailletaud committed
188
**But** : Construire une image docker grâce au Dockerfile fourni, la pousser sur DockerHub, et permettre son déploiement en production manuel. Lors de la mise à jour, utiliser `docker stop` afin de stopper l'ancien conteneur avant de lancer le nouveau.
Rémi Cailletaud's avatar
Rémi Cailletaud committed
189
190
191

*Note 1* : Demandez le nom du serveur Docker...

Rémi Cailletaud's avatar
Rémi Cailletaud committed
192
*Note 2* : Afin d'éviter les conflits, exposez votre conteneur sur le port 80x0 avec x le numéro du dépôt (ex : port 8010 pour formation-ci-01).
Rémi Cailletaud's avatar
Rémi Cailletaud committed
193

Rémi Cailletaud's avatar
Rémi Cailletaud committed
194
*Note 3* : Évidemment, dans la réalité nous ne déploierons pas en production depuis le master... Jetez donc un oeil aux [Environments](https://docs.gitlab.com/ee/ci/environments.html).
Rémi Cailletaud's avatar
Rémi Cailletaud committed
195

Rémi Cailletaud's avatar
Rémi Cailletaud committed
196
*Note 4* : Idéalement, notre conteneur doit lui aussi recevoir une série de tests avant d'être déployé !
Rémi Cailletaud's avatar
Rémi Cailletaud committed
197
198
199
200
201

### 15. Canary Release ###

Un des patterns du déploiement continu est le Blue/Green Deployment[^1] (et sa variante Canary Release). Il consiste à avoir plusieurs conteneurs en production, et les mettre à jour de manière progressive afin de détecter d'éventuelles anomalies.

Rémi Cailletaud's avatar
Rémi Cailletaud committed
202
203
[^1]: [Martin Fowler, BlueGreenDeployment](https://martinfowler.com/bliki/BlueGreenDeployment.html)

Rémi Cailletaud's avatar
Rémi Cailletaud committed
204
![canary](img/canary-release.png)
Rémi Cailletaud's avatar
Rémi Cailletaud committed
205

Rémi Cailletaud's avatar
Rémi Cailletaud committed
206
Nous allons maintenant déployer deux conteneurs (dice-green et dice-blue), en utilisant une fonctionnalité YAML appelée [Anchors](https://docs.gitlab.com/ee/ci/yaml/#anchors) qui permet de factoriser le code.
Rémi Cailletaud's avatar
Rémi Cailletaud committed
207

Rémi Cailletaud's avatar
Rémi Cailletaud committed
208
**But** : Déployer deux conteneurs dice-blue et dice-green. Les jobs sont distincts et manuels.
Rémi Cailletaud's avatar
Rémi Cailletaud committed
209

Rémi Cailletaud's avatar
Rémi Cailletaud committed
210
*Note 1* : Parmi les [variables Gitlab prédéfinies](https://docs.gitlab.com/ee/ci/variables/README.html#predefined-variables-environment-variables) `$CI_JOB_NAME` pourrait vous intéresser...
Rémi Cailletaud's avatar
Rémi Cailletaud committed
211

Rémi Cailletaud's avatar
Rémi Cailletaud committed
212
*Note 2*: Utilisez les ports 80x0 et 80x1...
Rémi Cailletaud's avatar
Rémi Cailletaud committed
213

Rémi Cailletaud's avatar
Rémi Cailletaud committed
214

Rémi Cailletaud's avatar
Rémi Cailletaud committed
215
### 16. Bonus : HAProxy, docker-compose.yml ###
Rémi Cailletaud's avatar
Rémi Cailletaud committed
216
217
218
219

Maintenant, montez l'infrastructure complète grâce à docker-compose, avec un HAProxy en frontend et deux (ou plus) conteneurs dice en backend....

*Note* : Si vous êtes arrivés jusque là, on ne vous aide plus !