summaryrefslogtreecommitdiffstats
path: root/demos/min/README.md
blob: f6cb89005a88135e121f9f509948a02ca5f6a54e (plain)
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.