README.md 8.11 KB
Newer Older
Nane Kratzke's avatar
Nane Kratzke committed
1
2
# Lab: Deployment Pipelines as Code

Nane Kratzke's avatar
Nane Kratzke committed
3
Deployment Pipelines sind ein wesentlicher Baustein im DevOps Ansatz, um Entwicklungszyklen schnell und agil zu halten.
Nane Kratzke's avatar
Nane Kratzke committed
4
5
6
Ziel ist es, Code der in ein Code Repository eingebracht wird, möglichst automatisiert zu integrieren, bauen, testen
sowie ggf. in eine Umgebung (häufig Test, Staging, Production) auszubringen.

Nane Kratzke's avatar
Nane Kratzke committed
7
Mit jedem Code Push wird also automatisiert geprüft, ob der Code in die bestehende Codebasis integriert werden kann, compilierbar ist, alle Tests passiert und deploybar ist. Auf diese Weise können nur funktionierende Softwarezustände in funktionierende Softwarezustände überführt werden. Entwickler sind so nicht einmal in der Lage Code zu erzeugen, der nicht automatisiert durch die Deployment Pipeline verarbeitbar ist.
Nane Kratzke's avatar
Nane Kratzke committed
8

Nane Kratzke's avatar
Nane Kratzke committed
9
Gemäß dem Everything as Code Ansatz versucht man auch Deployment Pipelines als versionierbaren Code ausdrücken zu können.
Nane Kratzke's avatar
Nane Kratzke committed
10
Es gibt diverse solcher Managed oder Self-hosted Services, die als kommerzielle oder auch als Open Source Software genutzt werden können. Z.B.:
Nane Kratzke's avatar
Nane Kratzke committed
11
12
13
14
15
16

- GitLab CI
- Circle CI
- Travis CI
- Jenkins
- Bitbucket Pipelines
Nane Kratzke's avatar
Nane Kratzke committed
17
- und viele mehr
Nane Kratzke's avatar
Nane Kratzke committed
18
19

Da Gitlab als Open Source Lösung einfach installiert werden kann, werden wir das Prinzip einer Deployment Pipeline
Nane Kratzke's avatar
Nane Kratzke committed
20
as Code am Typvertreter Gitlab CI demonstrieren. Die Ansätze anderer CI/CD Dienste funktionieren aber nach sehr
Nane Kratzke's avatar
Nane Kratzke committed
21
22
23
vergleichbaren Konzepten. Die Wahl auf Gitlab CI als Typvertreter erfolgt schlicht und ergreifend auf Basis der
guten Verfügbarkeit von Gitlab als Open Source Software und dessen häufigen Einsatz in Cloud-native Kontexten.

Nane Kratzke's avatar
Nane Kratzke committed
24
25
26
27
28
Wer mag, kann dieses Lab auch mittels des Managed Service Gitlab.com nachvollziehen. Hierzu müssen Sie sich allerdings
registrieren.

## Inhalt

Nane Kratzke's avatar
Nane Kratzke committed
29
- [Übung 1: Erzeugung von Deployment Pipelines](#übung-1-erzeugung-von-deployment-pipelines)
Nane Kratzke's avatar
Nane Kratzke committed
30
31
- Übung 2: Weiterreichen von Job Erzeugnissen (Artifacts)
- Übung 3: Informationen in die Pipeline mittels [Environment Variables](https://docs.gitlab.com/ee/ci/variables/predefined_variables.html) geben.
Nane Kratzke's avatar
Nane Kratzke committed
32
33
- Nutzung von Images
- Bereitstellung von Images
Nane Kratzke's avatar
Nane Kratzke committed
34
- Deployments to Kubernetes
Nane Kratzke's avatar
Nane Kratzke committed
35
- Deployments to Serverless Environments
Nane Kratzke's avatar
Nane Kratzke committed
36

37
### Übung 1: Erzeugung von Deployment Pipelines
Nane Kratzke's avatar
Nane Kratzke committed
38

Nane Kratzke's avatar
Nane Kratzke committed
39
40
41
42
Eine Deployment Pipeline besteht aus einer Sequenz von Stages. Jede Stage kann ein oder mehrere Jobs haben. Alle Jobs innerhalb einer
Stage werden parallel und isoliert voneinander ausgeführt. Eine Stage wird nur dann ausgeführt, wenn alle Jobs der vorherigen Stage
erfolgreich ausgeführt werden konnten.

Nane Kratzke's avatar
Nane Kratzke committed
43
Eine typische Pipeline umfasst häufig die folgenden Stages (grundsätzlich können Pipelines beliebig aussehen, es bietet sich jedoch an bewährten Pipeline Blueprints zu folgen):
Nane Kratzke's avatar
Nane Kratzke committed
44
45
46
47
48

- build (zum Erzeugen von Executables)
- test (zum Testen von Executables)
- deploy (zum Ausbringen von Executables)

Nane Kratzke's avatar
Nane Kratzke committed
49
Solch eine einfache Deployment Pipeline wollen wir nun bauen. Führen Sie hierzu bitte die folgenden Schritte aus:
Nane Kratzke's avatar
Nane Kratzke committed
50
51
52
53

__Aufgaben:__

1. Forken Sie hierzu bitte dieses Repository in Gitlab.
54
2. Sie finden in diesem geforkten Repository eine leere `.gitlab-ci.yml` Datei an. Diese Datei definiert Ihre Pipeline, die Gitlab mit jedem Push in das Repository automatisch anstößt.
Nane Kratzke's avatar
Nane Kratzke committed
55
3. Fügen Sie in diese Datei nun bitte folgende Inhalte ein und committen+pushen Sie `.gitlab-ci.yml` in das Repository:
Nane Kratzke's avatar
Nane Kratzke committed
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70

    ```yaml
    stages:
        - build
        - test
        - deploy

    job1:
        stage: build
        script:
            - echo "Hello I am job 1"

    job2:
        stage: build
        script:
71
            - echo "Hello I am job 2"
Nane Kratzke's avatar
Nane Kratzke committed
72
73
74
75

    job3:
        stage: test
        script:
76
            - echo "Hello I am job 3"
Nane Kratzke's avatar
Nane Kratzke committed
77
78

    job4:
79
        stage: deploy
Nane Kratzke's avatar
Nane Kratzke committed
80
        script:
81
            - echo "Hello I am job 4"
Nane Kratzke's avatar
Nane Kratzke committed
82
    ```
Nane Kratzke's avatar
Nane Kratzke committed
83
4. Gitlab führt dann automatisch, die so definierte [Pipeline](../../../pipelines) aus.
Nane Kratzke's avatar
Nane Kratzke committed
84
   
Nane Kratzke's avatar
Nane Kratzke committed
85
   ![Pipeline](pipeline.png)
Nane Kratzke's avatar
Nane Kratzke committed
86
87
5. Klicken Sie auf einen dieser Jobs, dann erhalten Sie den Konsolenoutput des Jobs.
   
88
   ![Job console output](job-console.png)
Nane Kratzke's avatar
Nane Kratzke committed
89
90

Eine Pipeline ist also sehr einfach mit einer YAML Datei definierbar. YAML Dateien wiederum sind gut durch Code Versionssysteme versionierbar.
Nane Kratzke's avatar
Nane Kratzke committed
91
Das ist eigentlich auch schon das wesentliche Prinzip von einer Deployment Pipeline as Code. Sie sehen an diesem Beispiel allerdings auch bereits weitere Aspekte die typisch für Cloud-native Deployment Ansätze sind.
Nane Kratzke's avatar
Nane Kratzke committed
92
93
94

- Jobs sind eigentlich nichts weiter als Shellskripte, die in einem isolierten Container ausgeführt werden.
- Können alle Jobs einer Stage erfolgreich ausgeführt werden, (exit code == 0) werden die Jobs der nächsten Stage gestartet.
Nane Kratzke's avatar
Nane Kratzke committed
95
- Schlägt ein Job fehl (exit code != 0), wird die nächste Stage nicht gestartet. Sie können das ganz einfach ausprobieren, indem Sie bspw. den Befehl `exit 1` in *job3* ergänzen.
Nane Kratzke's avatar
Nane Kratzke committed
96
97
98
99
100
101
102
    ```yaml
    job3:
        stage: test
        script:
            - echo "Hello I am job 3"
            - exit 1
    ```
103
104
105
    Die Pipeline schlägt dann in job3 in Stage `test` fehl.

    ![Pipeline job failed](pipeline-job-failed.png)
Nane Kratzke's avatar
Nane Kratzke committed
106

Nane Kratzke's avatar
Nane Kratzke committed
107
### Übung 2: Weiterreichen von Job Erzeugnissen (Artifacts)
Nane Kratzke's avatar
Nane Kratzke committed
108

109
110
111
112
113
114
Jobs laufen isoliert in einem Container ab, sind also zustandslos oder anders ausgedrückt: Jobs "vergessen" erzeugte Artifakte. Dies ist sicherlich in vielen Fällen nicht sinnvoll.
Z.B. sollten durch den Compiler erzeugte `.class` Dateien in einem Java Build Schritt an einen Test Job weitergereicht werden können (ansonsten müsste der Test Job erneut kompilieren).
Zum Ende der Pipeline soll vielleicht auch eine `jar`-Datei als Endergebnis der Pipeline bereitgestellt werden können.

Hierfür dienen in Pipelines sogenannte Artefakte. Artefakte sind Job Erzeugnisse, die zwischen Jobs entlang einer Stage Sequenz fließen.

115
__Aufgabe__:
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144

Ändern Sie bitte Ihre Pipeline wie folgt ab:

```yaml
stages:
    - generate
    - consume

job1:
    stage: generate
    script:
        - mkdir build
        - echo "Hello I am job 1" > build/job1-result.txt

job2:
    stage: generate
    script:
        - mkdir build
        - echo "Hello I am job 2" > build/job2-result.txt

job3:
    stage: consume
    script:
        - cat build/*-result.txt
```

Die Jobs job1 und job2 lenken ihre Resultate also in zwei Dateien um, die im `build` Verzeichnis gespeichert werden.
Wir würden an dieser Stelle erwarten, dass der job3 daher folgende Konsolenausgabe erzeugen sollte:

145
```
146
147
148
149
Hello I am job1
Hello I am job2
```

150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
Tatsächlich schlägt der job3 aber wie folgt fehl:

```
cat: can't open 'build/*-result.txt': No such file or directory
$ cat build/*-result.txt
ERROR: Job failed: exit code 1
```

Und dies obwohl die Dateien in job1 und job2 korrekt angelegt wurden. Allerdings in einem Container. Und alle Jobs laufen
in isolierten Containern voneinander ab, d.h. job1 kennt job2 und job3 nicht, und job2 kenn job1 sowie job3 nicht, usw.
Wenn man Erzeugnisse eines Jobs anderen Jobs bereitstellen muss innerhalb einer Pipeline, dann kann man dies mittels Artefakten
machen.

Um Artefakte zu kennzeichnen, können Sie folgenden Eintrag den Jobs job1 und job2 hinzufügen.

```yaml
artifacts:
    paths:
    - build/
```

Diese Artefakte stehen dann allen Jobs in der Pipeline zur Verfügung. Sie müssen darauf achten, dass unterschiedliche Jobs
Nane Kratzke's avatar
Nane Kratzke committed
172
unterschiedlich benannte Artefakte erzeugen, ansonsten überschreiben sich identisch benannte Artefakte gegenseitig.
173
174
175
176
177
178
179

Auch dies können Sie einmal ausprobieren, indem Sie anstelle von 

```
echo "Hello I am job 2" > build/job2-result.txt
```

Nane Kratzke's avatar
Nane Kratzke committed
180
folgendes schreiben (also die Job-Nummern in den Artefaktbezeichnern sowohl in Job1 als auch in Job2 entfernen).
181
182
183
184

```
echo "Hello I am job 2" > build/job-result.txt
```
Nane Kratzke's avatar
Nane Kratzke committed
185

Nane Kratzke's avatar
Nane Kratzke committed
186
187
188
Dann werden Sie nur eine Ausgabe von job1 oder job2 bekommen. Welche Ausgabe ist davon abhängig welche Job Artifakte von der Pipeline
aus job1 und job2 als letztes gesichert wurden. Gehen Sie davon aus, dass dies nicht deterministisch ist - insbesondere bei parallel ablaufenden Jobs.

Nane Kratzke's avatar
Nane Kratzke committed
189
## Quellen für weitergehende Informationen:
Nane Kratzke's avatar
Nane Kratzke committed
190
191

- Youtube: [Gitlab CI pipeline tutorial for beginners](https://youtu.be/Jav4vbUrqII)
Nane Kratzke's avatar
Nane Kratzke committed
192
- Youtube: [Gitlab CI Pipeline, Artifacts and Environments](https://youtu.be/PCKDICEe10s)
Nane Kratzke's avatar
Nane Kratzke committed
193
- Youtube: [Automating Kubernetes Deployments](https://youtu.be/wEDRfAz6_Uw)
Nane Kratzke's avatar
Nane Kratzke committed
194
- Gitlab: [Job Artifacts](https://docs.gitlab.com/ee/ci/pipelines/job_artifacts.html)
Nane Kratzke's avatar
Nane Kratzke committed
195

Nane Kratzke's avatar
Nane Kratzke committed
196