1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
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
83
84
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
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
|
# Guidelines for minimizing the size of a container image
This tutorial introduces a set of guidelines for the minimization of container images.
You may choose to simply read through the tutorial or run the experiments, which is highly recommended.
Each section will guide you through an experiment that demonstrates, in a tangible manner, the benefits of following each guideline.
The expectation is that, with each experiment, you will gain insight into the value of the guideline.
## Clone the repo
This tutorial is self-contained, you should have access to all the artifacts you need to run the experiments.
To get started, get the code by cloning the repo with:
```bash
git clone https://gerrit.onap.org/r/oparent/cia
```
## Why are my numbers different?
Please note that image sizes may vary slightly from platform to platform. This tutorial was produced on a Linux server. If you use a different platform, images sizes are bound to be different. And that's OK.
## The canonical test app
We will use a web service to demonstrate the effect of the minimization guidelines on the size of a container image.
At any step of the process, you can run the container to verify the service functionality by executing:
```bash
$ docker run -p 5000:5000 <image-id or tag>
```
Point a browser to http://0.0.0.0:5000/. If everything is working fine, you should see a quote by Erwin Schrodinger.
# 1. Choose a small base image
Let's explore the effect the base image has on the size of a container image.
To demonstrate the effect of choosing the right base image, we will select three different base images to build container images that are functionally equivalent.
```bash
cd <project-root>/base-image/
```
## Using a python:2.7 base image
First, we will use a python:2.7 base image.
In this case, Dockerfile.python, contains:
```bash
FROM python:2.7
COPY . /app
WORKDIR /app
RUN pip install -r requirements.txt
ENTRYPOINT ["python"]
CMD ["app.py"]
```
Build the image with
```bash
$docker build --force-rm -t ado/app -f Dockerfile.python .
```
And check the size of the image with
```bash
$ docker images | grep ado/app
ado/app latest 2383bad1d517 14 seconds ago 908MB
```
Depending on your platform, the size of the container image should about 908MB.
Remove the image.
```bash
$docker rmi ado/app
```
Let's continue the exploration by selecting a different base image.
## Using an Ubuntu base image
The Dockerfile for this scenario (Dockerfile.ubuntu) contains
```bash
FROM ubuntu:latest
RUN apt-get update -y && \
apt-get install -y --no-install-recommends \
python-pip \
python-dev \
build-essential
COPY . /app
WORKDIR /app
RUN pip install --upgrade setuptools && pip install -r requirements.txt
ENTRYPOINT ["python"]
CMD ["app.py"]
```
Build the image with
```bash
docker build --force-rm -t ado/app -f Dockerfile.ubuntu .
```
And check the image size with
```bash
$ docker images | grep ado/app
ado/app latest dc99d28dec9d 40 seconds ago 399MB
```
The ubuntu base image has reduced the image size from 908MB to 399MB while retaining the exact same service function. Remember that your numbers may vary.
Remove the image.
```bash
$docker rmi ado/app
```
Can we do better? Let's try yet another base image.
## Using an alpine base image
In this case Dockerfile.alpine, contains
```bash
FROM python:2.7-alpine
COPY . /app
WORKDIR /app
RUN pip install -r requirements.txt
ENTRYPOINT ["python"]
CMD ["app.py"]
```
Build the image with
```bash
docker build --force-rm -t ado/app -f Dockerfile.alpine .
```
And check the image size with
```bash
$ docker images | grep ado/app
ado/app latest d11ab7ba27d2 39 seconds ago 85.7MB
```
By choosing the alpine base image we were able to produce a container image that's only 85.7MB.
That's a reduction of 91% in size compared to using the python base image. In other word we have an image that's only a tenth of the size of the python image.
Don't forget to remove the image.
```bash
$docker rmi ado/app
```
### Upshot
Before selecting a base image, determine if your application requires specific libraries or tools from a specific operating system and always use the smallest possible base images.
### Caveat emptor
We have used an alpine base image to demonstrate the dramatic effect the right base image can have on the size of the resulting container image. If your application can meet all of its requirements using a small base image, you will realize great space saving.
Having said that, we are not suggesting that ALL container images must use this same base image.
# 2. Be mindful of where your Dockerfile is located (i.e. understand the build context)
To illustrate this concept, we will use the last image we built as a reference, modify the context and see the effect on the image size.
Files for this section are under the "context" directory.
## Build with no extra files in build context
When we built the alpine version of the image in the previous section, we executed
```bash
$ docker build --force-rm -t ado/app -f Dockerfile.alpine .
```
The backend returned the following message at the beginning of the build.
```
Sending build context to Docker daemon 85.5kB
```
Notice the size of the build context, it is only 85.5kB. Recall that the image size was 85.7MB. Again your numbers may vary slightly.
## Adding extra files to context
To modify the context, we created a new directory called "downstream" and copied a PDF file. We have done that for you. You are welcome to copy other files for your own tests.
## Build with extra files in the build context
Now that we have added an extra, and unnecessary, file to the build context, let's look at the effect on the image size.
Make sure you are in the right directory
```bash
cd <project-root>/context/
```
And build the image with
```bash
docker build --force-rm -t ado/app -f Dockerfile.alpine .
```
The build backend should respond with
```bash
Sending build context to Docker daemon 1.517MB
```
Notice that the previous message indicated that the build context was only 85.5kB.
Let's check the size of the new image with (replace the image ID accordingly).
```bash
$ docker images | grep ado/app
ado/app latest 4fd041a960b7 21 seconds ago 87.2MB
```
Notice how the image went from 87.5MB to 87.2MB without any modifications to the code or the Dockerfile.
Don't forget to remove the image.
```bash
$docker rmi ado/app
```
## Upshot
Inadvertently including files that are not necessary for building an image results in a larger build context and larger image size.
# 3. Chain commands together to reduce the number of layers
In this section we explore the effect the chaining commands together on the size of a container image. Files for this section can be found under the "cmd-chain" directory.
To demonstrate the effect of chaining commands, we will work with the ubuntu Dockerfile we used before.
```bash
FROM ubuntu:latest
RUN apt-get update -y && \
apt-get install -y --no-install-recommends \
python-pip \
python-dev \
build-essential
COPY . /app
WORKDIR /app
RUN pip install --upgrade setuptools && pip install -r requirements.txt
ENTRYPOINT ["python"]
CMD ["app.py"]
```
Recall the size of the image was 399MB.
Now let's modify the commands and remove chaining with &&.
We now have the following Dockerfile (Dockerfile.ubuntu-no-chain)
```bash
FROM ubuntu:latest
RUN apt-get update -y # No command chaining
RUN apt-get install -y --no-install-recommends \ # No command chaining
python-pip \
python-dev \
build-essential
COPY . /app
WORKDIR /app
RUN pip install --upgrade setuptools # No command chaining
RUN pip install -r requirements.txt # No command chaining
ENTRYPOINT ["python"]
CMD ["app.py"]
```
Make sure you are in the right directory
```bash
cd <project-root>/cmd-chain/
```
Build the image with
```bash
docker build --force-rm -t ado/app -f Dockerfile.ubuntu-no-chain .
```
Check the new image size with
```bash
$ docker images | grep ado/app
ado/app latest ab2533223d80 About a minute ago 405MB
```
Notice how the image size went from 399MB to 405MB which is significant for a service as simple as our test app.
|