README.md 13.6 KB
Newer Older
Nane Kratzke's avatar
Nane Kratzke committed
1
2
3
4
5
6
# Lab 05: Containerization

to be done

## Inhalt

Nane Kratzke's avatar
Nane Kratzke committed
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- [Lab 05: Containerization](#lab-05-containerization)
  - [Inhalt](#inhalt)
  - [Übung 01: Installation von Docker](#übung-01-installation-von-docker)
  - [Übung 02: Erstellung von einem Image](#übung-02-erstellung-von-einem-image)
    - [Aufgabe 02.1 HTTP-Service mittels eines NGINX-Basisimages](#aufgabe-021-http-service-mittels-eines-nginx-basisimages)
    - [Aufgabe 03.2: HTTP-Service mittels eines generellen Basisimages](#aufgabe-032-http-service-mittels-eines-generellen-basisimages)
  - [Übung 03: Vergleich von Imagegrößen](#übung-03-vergleich-von-imagegrößen)
    - [Aufgabe 03.1: Unnötige Dateien löschen](#aufgabe-031-unnötige-dateien-löschen)
    - [Aufgabe 03.2: Image Layer einsparen](#aufgabe-032-image-layer-einsparen)
    - [Aufgabe 03.3: Kleinere Basis Images nutzen](#aufgabe-033-kleinere-basis-images-nutzen)
  - [Übung 04: Push von einem Image in eine Registry](#übung-04-push-von-einem-image-in-eine-registry)
  - [Übung 05: Pipeline zum Bau und Test eines Images](#übung-05-pipeline-zum-bau-und-test-eines-images)
  - [Quellen und Referenzen](#quellen-und-referenzen)
  - [Was sollten Sie mitnehmen ...](#was-sollten-sie-mitnehmen-)
Nane Kratzke's avatar
Nane Kratzke committed
21
22
23

## Übung 01: Installation von Docker

Nane Kratzke's avatar
Nane Kratzke committed
24
- [Installieren](https://docs.docker.com/engine/install/) Sie gem. den verlinkten Anweisungen Docker für Ihr System:
Nane Kratzke's avatar
Nane Kratzke committed
25
26
27
28
29
30
31
32
33
34
35
36
37
38
    - [Mac](https://www.docker.com/products/docker-desktop)
    - [Windows](https://www.docker.com/products/docker-desktop)
    - [Linux](https://docs.docker.com/engine/install/)
- Prüfen Sie in Ihrer Konsole, ob die Installation erfolgreich war:
  ```
  docker --version
  ```
  Sie sollten eine Ausgabe mit der Versionsnummer und build id bekommen, z.B.:
  ```
  Docker version 19.03.8, build afacb8b
  ```

## Übung 02: Erstellung von einem Image

Nane Kratzke's avatar
Nane Kratzke committed
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
Sie werden in diesem Teil sehen, wie man Images baut. Häufig benötigt man Images für spezfische Dienste, wie bspw. Datenbanken, Webserver, usw. Hierfür bieten die Hersteller meist vorkonfigurierte Images an, die man nur noch mit einer kleinen Konfiguration (z.B. Access Credentials, Dateipfade, etc.) auf die spezifischen Bedürfnisse anpassen muss. Man kann aber auch Images auf Basis einer Standard Linux-Distribution aufsetzen. Sie werden beides am Beispiel eines kleinen Webservers sehen.

Klonen Sie sich bitte hierzu als Vorbereitung dieses Repository mittels:

```
git clone https://git.mylab.th-luebeck.de/cloud-native/lab-containerization.git
cd lab-containerization
```

### Aufgabe 02.1 HTTP-Service mittels eines NGINX-Basisimages

1. Öffnen Sie nun bitte die Datei `Dockerfile.nginx` (diese ist recht übersichtlich, versuchen Sie diese zu verstehen und nachzuvollziehen)
2. Erzeugen Sie aus dieser nun das für Docker verarbeitete Dockerfile mittles `cp Dockerfile.nginx Dockerfile`.
3. Bauen Sie nun ein Container-Image mittels `docker build -t web:nginx .` (vergessen Sie nicht die Punkt am Ende, der gibt das Current Directory an und ist wichtig!).
4. Prüfen Sie mittels `docker image list web*`, ob Ihr Image gebaut wurde. Sie sollten eine Ausgabe wie folgt (o. ähnl.) erhalten.
   ```
   REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
   web                 nginx               ca0547d1d208        seconds ago         133MB
   ```
5. Starten Sie dieses Image nun bitte mittels `docker run -p 8080:80 web:nginx` (Sie binden dadurch Ihren lokalen Port 8080 an den Port 80 des Containers).
6. Prüfen Sie, ob Ihre Website ausgeliefert wird, in dem Sie [http://localhost:8080](http://localhost:8080) aufrufen. Sie sollten dann folgende Webpage sehen.
![screenshot](index.html.png)
7. Beenden Sie den HTTP-Service in dem Sie in Ihre Konsole CTRL-C drücken (Sie senden damit das SIGTERM Signal an den NGINX Server-Prozess und der Container wird terminiert).

Das war ja einfach.

### Aufgabe 03.2: HTTP-Service mittels eines generellen Basisimages

In Fällen von weit verbreiteten Diensten und Produkten wie NGINX, Apache, Redis, Memcached, CouchDB, MySQL, usw. finden sich häufig solche Basisimages der entsprechenden Projekte. Das geht meist sehr schnell und ist unkompliziert, sie verlieren aber auch Kontrolle und Konfigurationsmöglichkeiten. In Fällen spezifischerer Produkte oder selbst geschriebener Software sind Sie sogar ggf. gezwungen selber ein Image zu erstellen und die entsprechende Software darauf zu installieren. Man geht in diesen Fällen üblicherweise von einem Distributions Basis-Image aus (wie bspw. dem Ubuntu 18.04 LTS Image).

Wir wollen nun denselben Service mit einem anderen Image bauen, um diesen generelleren Ansatz zu demonstrieren.

1. `cp Dockerfile.ubuntu Dockerfile`
2. Öffen Sie das `Dockerfile` in einem Editor und versuchen Sie es zu verstehen. Sie sehen es ist etwas länger, als das vorherige Image, aber Sie sollten die Wirkungsweise mittels der Kommentare nachvollziehen können.
3. Bauen Sie nun das Image zu diesem Ubuntu-basierten `Dockerfile` mittels `docker build -t web:ubuntu .`.
4. Prüfen Sie wieder mittels `docker image list web*`, ob Ihr Image gebaut wurde. Sie sollten eine Ausgabe wie folgt (o. ähnl.) erhalten.
   ```
   REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
   web                 ubuntu              8032bb56c82d        seconds ago         154MB
   web                 nginx               ca0547d1d208        minutes ago         133MB
   ```
5. Starten Sie dieses Image nun bitte mittels `docker run -p 8080:80 web:ubuntu`.
6. Prüfen Sie, ob Ihre Website ausgeliefert wird, in dem Sie [http://localhost:8080](http://localhost:8080) aufrufen. Sie sollten wieder dieselbe Webpage sehen, wie im vorherigen Teil. 

Nane Kratzke's avatar
Nane Kratzke committed
83
84
## Übung 03: Vergleich von Imagegrößen

Nane Kratzke's avatar
Nane Kratzke committed
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
Sie haben in Übung 02 einen einfachen Webserver samt Inhalt als Docker Image erzeugt.

- Einmal haben Sie auf dem von NGINX selbst bereitgestellten Image nur Ihren Inhalt (`web`-Verzeichnis) hinzugefügt.
- Im zweiten Fall haben Sie auf Basis eines Ubuntu 18.04 LTS Basis Images auch den Webserver NGINX installiert, den Inhalt hinzugefügt und den Entrypoint für den Container konfiguriert. Das war aufwändiger, Sie hatten aber dadurch letztlich mehr Kontrolle über die Konfiguration.

Sie sehen also, dass bereitgestellte Community-Images häufig komfortabler sind. Doch wie sieht es mit dem Größenbedarf der resultierenden Images aus?

Geben Sie dazu bitte folgendes in Ihrer Shell ein.

```
docker image list web*
```

Dies sollte folgende (o. ähnl.) Ausgabe erzeugen:

```
REPOSITORY          TAG                 IMAGE ID            CREATED              SIZE
web                 ubuntu              8032bb56c82d        57 minutes ago       154MB
web                 nginx               ca0547d1d208        14 hours ago         133MB
```

Sie sehen, Ihr selbst gebautes Ubuntu Image ist etwas größer. Das ist nicht wirklich erstaunlich, denn die NGINX Macher wissen vermutlich besser als Sie und ich, wie man eine NGINX-Basisinstallation effizient konfiguriert. Doch Images lassen sich auch "shrinken". Sie sollten sich dazu ins Bewusstsein rufen, dass Container Overlay-Dateisysteme nutzen, und jede Anweisungszeile eines Dockerfiles ein Image-Layer (mit Platzbedarf) erzeugt. Man kann sich das zu nutze machen und Images auf mehrere Arten "kleiner" machen:

1. Indem man für den Betrieb unnötige Dateien nicht in den Layer mit aufnimmt.
2. Indem man unnötige Image-Layer einspart.
3. Und indem man kleinere Basis-Images nutzt.

Die folgenden Aufgaben dienen dazu, Ihnen zu zeigen, was für Effekte diese drei Möglichkeiten auf die resultierenden Imagegrößen haben.

### Aufgabe 03.1: Unnötige Dateien löschen

Wir gehen von unserem `web:ubuntu` Image aus.

1. `cp Dockerfile.ubuntu Dockerfile`
2. Löschen Sie nun unnötige Package Manager Dateien mittels `apt-get clean` und `rm -rf /var/lib/apt/lists/*` in dem Sie die im Dockerfile dafür vorgesehenen Zeilen einkommentieren.
3. Bauen Sie nun ein neues Package mittels `docker build -t web:ubuntu-cleaned .`
4. Lassen Sie sich nun die Größen aller drei Packages mittels `docker image list web*` anzeigen.

Sie sehen, dass dadurch Ihr Image bereits etwas kleiner geworden ist, aber noch größer als das NGINX-Image ist.

### Aufgabe 03.2: Image Layer einsparen

In Linux/UNIX Shells kann man, um zwei Kommandos nacheinander auszuführen, entweder dies

```
cat /file/exists
echo "success"
```

oder dies

```
cat /file/exists && echo "success"
```

schreiben. "Success" wird im zweiten Fall sogar nur dann ausgegeben, wenn das erste Kommando `cat /file/exists` mit einem Exit-Code von 0 (also erfolgreich) beendet werden konnte. Das entspricht exakt der Ausführungsweise sequentiell aufeinander folgender `RUN`-Anweisungen. Mittels des `&&` Operators lassen sich also
mehrere Shell-Kommandos in einer `RUN`-Anweisung eines Dockerfiles unterbringen. Anstatt mehrere `RUN`-Anweisungen (also mehrere Image-Layers) benötigt man so nur eine `RUN`-Anweisung (und erzeugt nur einen Image-Layer).

Dieses __RUN-Chaining__ genannte Prinzip werden wir anwenden und sehen welchen Effekt dies auf Image-Größen haben kann.

1. `cp Dockerfile.ubuntu Dockerfile`
2. Kommentieren Sie nun alle `RUN`-Anweisungen im Dockerfile aus.
3. Kommentieren Sie nun bitte die einzelne `RUN`-Anweisung unter dem `Image Shrinking Effect`-Kommentar ein.
4. Bauen Sie nun ein neues Package mittels `docker build -t web:ubuntu-shrinked .`
5. Lassen Sie sich nun die Größen aller vier Packages mittels `docker image list web*` anzeigen.

Sie sollten etwa folgende (o. ähnl.) Ausgabe erhalten:

```
REPOSITORY          TAG                 IMAGE ID            CREATED              SIZE
web                 ubuntu-shrinked     f5f33804dcf2        38 minutes ago       117MB
web                 ubuntu-cleaned      4f345b6cac34        48 minutes ago       147MB
web                 ubuntu              8032bb56c82d        57 minutes ago       154MB
web                 nginx               ca0547d1d208        14 hours ago         133MB
```

### Aufgabe 03.3: Kleinere Basis Images nutzen

Alle Images haben bislang Größen, die deutlich die 100 MB sprengen. Das ist zwar kleiner als viele VM-Images, aber immer noch recht groß, um ein paar HTML-Seiten von wenigen KBs auszuliefern.

Aus diesem Grund gibt es Linux-Distributionen (Basis-Images), die deutlich kleiner sind, als die Standard-Distributionen. Diese beinhalten nur das absolut Wesentliche (quasi die POSIX-Schnittstelle) eines Linux-Betriebssystems. Hier gibt es mehrere Distributionen, die häufig verwendet werden. Bspw.:

- [Alpine Linux](https://alpinelinux.org)
- [Atomic](https://www.projectatomic.io)
- [Busybox](https://busybox.net)
- [RancherOS](https://rancher.com/rancher-os)
- [Photon](https://vmware.github.io/photon)

Wir werden die Übung 02 nun mit dem Alpine Linux Basis-Image wiederholen und sehen, wie sich diese Distribution auf die Image-Größen auswirkt.

1. `cp Dockerfile.alpine Dockerfile`
2. `docker build -t web:alpline .`
3. Kommentieren Sie dann folgende Zeile im Dockerfile ein: `RUN rm -rf /var/cache/apk/*`
4. `docker build -t web:alpine-cleaned .`
5. Kommentieren Sie nun alle `RUN` Commands im Dockerfile aus.
6. Kommenteren Sie dann das `RUN`-Chaining im Dockerfile ein.
7. `docker build -t web:alpine-shrinked .`
8. Lassen Sie sich nun mittels `docker image list web*` alle Image-Größen im Überblick anzeigen.

Sie sollten nun folgende (o. ähnl.) Ausgabe erhalten:

```
REPOSITORY          TAG                 IMAGE ID            CREATED              SIZE
web                 alpine-shrinked     647de3450afe        2 seconds ago        ??.??MB
web                 alpine-cleaned      9a30127f52b1        About a minute ago   ??.??MB
web                 alpine              b9f1decf5a22        2 minutes ago        ??.??MB
web                 ubuntu-shrinked     f5f33804dcf2        38 minutes ago       117MB
web                 ubuntu-cleaned      4f345b6cac34        48 minutes ago       147MB
web                 ubuntu              8032bb56c82d        57 minutes ago       154MB
web                 nginx               ca0547d1d208        14 hours ago         133MB
```

__Beantworten Sie nun die Frage, um wieviel Prozent der Image-Wechsel das resultierende Container-Image reduziert hat?__

Sie können auch gerne prüfen, dass die Images alle laufen, indem Sie diese jeweils mit den folgenden Kommandos starten

```
docker run -p 8080:80 web:alpine
docker run -p 8080:80 web:alpine-cleaned
docker run -p 8080:80 web:alpine-shrinked
```

und danach [http://localhost:8080](http://localhost:8080) aufrufen.

Nane Kratzke's avatar
Nane Kratzke committed
209
210
## Übung 04: Push von einem Image in eine Registry

Nane Kratzke's avatar
Nane Kratzke committed
211
212


Nane Kratzke's avatar
Nane Kratzke committed
213
214
215
216
217
218
219
220
221
222
223
## Übung 05: Pipeline zum Bau und Test eines Images

## Quellen und Referenzen

- [Docker Playground](https://labs.play-with-docker.com/) (Online Lab)
- [Docker Get Started](https://docs.docker.com/get-started/)
- [Docker Tutorials and Community Trainings](https://www.docker.com/play-with-docker)
- [Best Practices for Writing Dockerfiles](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/)

## Was sollten Sie mitnehmen ...

Nane Kratzke's avatar
Nane Kratzke committed
224
225
226
227
228
229
1. Mittels der den meisten Distributionen beiliegenden Package Managern ist es über
`RUN`-Commands möglich in Basis-Images beliebige Software komfortabel und "non-interactive" zu installieren.
1. Eigener Code, Content oder Konfigurationen können mittels `ADD`-Commands dem Container-Image hinzugefügt werden (und danach mittels `RUN`-Commands auch kompiliert und installiert werden).
2. Sie können Server-Dienste mittels TCP-Ports nach außen `EXPOSE`n.
3. Über `ENTRYPOINT` können Sie hierzu den Prozess starten, der durch den Container bereitgestellt werden soll (vermeiden Sie dabei Prozesse als Daemons zu starten).
4. Image-Größen lassen sich mittels **RUN-Chaining** und der Wahl kleiner Basis-Images signifikant reduzieren.
Nane Kratzke's avatar
Nane Kratzke committed
230