Fix link, add explicit solution to exercise
[rnaseq-cwl-training.git] / _episodes / 08-supplement-docker.md
1 ---
2 title: "Supplement: Creating Docker Images for Workflows"
3 teaching: 10
4 exercises: 1
5 questions:
6 - "How do I create Docker images from scratch?"
7 - "What some best practices for Docker images?"
8 objectives:
9 - "Understand how to get started writing Dockerfiles"
10 keypoints:
11 - "Docker images contain the initial state of the filesystem for a container"
12 - "Docker images are made up of layers"
13 - "Dockerfiles consist of a series of commands to install software into the container."
14 ---
15
16 Common Workflow Language supports running tasks inside software
17 containers.  Software container systems (such as Docker) create an
18 execution environment that is isolated from the host system, so that
19 software installed on the host system does not conflict with the
20 software installed inside the container.
21
22 Programs running inside a software container get a different (and
23 generally restricted) view of the system than processes running
24 outside the container.  One of the most important and useful features
25 is that the containerized program has a different view of the file
26 system.  A program running inside a container, searching for
27 libraries, modules, configuration files, data files, etc, only sees
28 the files defined inside the container.
29
30 This means that, usually, a given file _path_ refers to _different
31 actual files_ depending from the persective of being inside or outside
32 the container.  It is also possible to have a file from the host
33 system appear at some location inside the container, meaning that the
34 _same file_ appears at _different paths_ depending from the persective
35 of being inside or outside the container.
36
37 The complexity of translating between the container and its host
38 environment is handled by the Common Workflow Language runner.  As a
39 workflow author, you only need to worry about the environment _inside_
40 the container.
41
42 # What are Docker images?
43
44 The Docker image describes the starting conditions for the container.
45 Most importantly, this includes starting layout and contents of the
46 container's file system.  This file system is typically a lightweight
47 POSIX environment, providing a standard set of POSIX utilities like a
48 `sh`, `ls`, `cat`, etc and organized into standard POSIX directories
49 like `/bin` and `/lib`.
50
51 The image is is made up of multiple "layers".  Each layer modifies the
52 layer below it by adding, removing or modifying files to produce a new
53 layer.  This allows lower layers to be re-used.
54
55 # Writing a Dockerfile
56
57 In this example, we will build a Docker image containing the
58 Burrows-Wheeler Aligner (BWA) by Heng Li.  This is just for
59 demonstration, in practice you should prefer to use existing
60 containers from [BioContainers](https://biocontainers.pro/), which
61 includes `bwa`.
62
63 Each line of the Docker file consists of a COMMAND in all caps,
64 following by the parameters of that command.
65
66 The first line of the file will specify the base image that we are
67 going to build from.  As mentioned, images are divided up into
68 "layers", so this tells Docker what to use for the first layer.
69
70 ```
71 FROM debian:10-slim
72 ```
73
74 This starts from the lightweight ("slim") Debian 10 Docker image.
75
76 Docker images have a special naming scheme.
77
78 A bare name like "debian" or "ubuntu" means it is an official Docker
79 image.  It has an implied prefix of "library", so you may see the
80 image referred to as "library/debian".  Official images are published
81 on [Docker Hub](https://hub.docker.com/search?type=image&image_filter=official).
82
83 A name with two parts separated by a slash is published on Docker Hub
84 by someone else.  For example, `amazon/aws-cli` is published by
85 Amazon.  These can also be found on [Docker Hub](https://hub.docker.com/search?type=image).
86
87 A name with three parts separated by slashes means it is published on
88 a different container register.  For example,
89 `quay.io/biocontainers/subread` is published by `quay.io`.
90
91 Following image name, separated by a colon is the "tag".  This is
92 typically the version of the image.  If not provided, the default tag
93 is "latest".  In this example, the tag is "10-slim" indicating Debian
94 release 10.
95
96 The Docker file should also include a MAINTAINER (this is purely
97 metadata, it is stored in the image but not used for execution).
98
99 ```
100 MAINTAINER Peter Amstutz <peter.amstutz@curii.com>
101 ```
102
103 Next is the default user inside the image.  By making choosing root,
104 we can change anything inside the image (but not outside).
105
106 The body of the Dockerfile is a series of `RUN` commands.
107
108 Each command is run with `/bin/sh` inside the Docker container.
109
110 Each `RUN` command creates a new layer.
111
112 The `RUN` command can span multiple lines by using a trailing
113 backslash.
114
115 For the first command, we use `apt-get` to install some packages that
116 will be needed to compile `bwa`.  The `build-essential` package
117 installs `gcc`, `make`, etc.
118
119 ```
120 RUN apt-get update -qy && \
121         apt-get install -qy build-essential wget unzip
122 ```
123
124 Now we do everything else: download the source code of bwa, unzip it,
125 make it, copy the resulting binary to `/usr/bin`, and clean up.
126
127 ```
128 # Install BWA 07.7.17
129 RUN wget https://github.com/lh3/bwa/archive/v0.7.17.zip && \
130         unzip v0.7.17 && \
131         cd bwa-0.7.17 && \
132         make && \
133         cp bwa /usr/bin && \
134         cd .. && \
135         rm -rf bwa-0.7.17
136 ```
137
138 Because each `RUN` command creates a new layer, having the build and
139 clean up in separate `RUN` commands would mean creating a layer that
140 includes the intermediate object files from the build.  These would
141 then be carried around as part of the container image forever, despite
142 being useless.  By doing the entire build and clean up in one `RUN`
143 command, only the final state of the file system, with the binary
144 copied to `/usr/bin`, is committed to a layer.
145
146 To build a Docker image from a Dockerfile, use `docker build`.
147
148 Use the `-t` option to specify the name of the image.  Use `-f` if the
149 file isn't named exactly `Dockerfile`.  The last part is the directory
150 where it will find the `Dockerfile` and any files that are referenced
151 by `COPY` (described below).
152
153 ```
154 docker build -t training/bwa -f Dockerfile.single-stage .
155 ```
156
157 > ## Exercise
158 >
159 > Create a `Dockerfile` based on this lesson and build it for yourself.
160 >
161 > > ## Solution
162 > >
163 > > FROM debian:10-slim
164 > > MAINTAINER Peter Amstutz <peter.amstutz@curii.com>
165 > >
166 > > RUN apt-get update -qy
167 > > RUN apt-get install -qy build-essential wget unzip zlib1g-dev
168 > >
169 > > # Install BWA 07.7.17
170 > > RUN wget https://github.com/lh3/bwa/archive/v0.7.17.zip && \
171 > >     unzip v0.7.17 && \
172 > >     cd bwa-0.7.17 && \
173 > >     make && \
174 > >     cp bwa /usr/bin && \
175 > >     cd .. && \
176 > >     rm -rf bwa-0.7.17
177 > >
178 > {: .solution}
179 {: .challenge}
180
181 # Adding files to the image during the build
182
183 Using the `COPY` command, you can copy files from the source directory
184 (this is the directory your Dockerfile was located) into the image
185 during the build.  For example, you have a `requirements.txt` next to
186 Dockerfile:
187
188 ```
189 COPY requirements.txt /tmp/
190 RUN pip install --requirement /tmp/requirements.txt
191 ```
192
193 # Multi-stage builds
194
195 As noted, it is good practice to avoiding leaving files in the Docker
196 image that were required to build the program, but not to run it, as
197 those files are simply useless bloat.  Docker offers a more
198 sophisticated way to create clean builds by separating the build steps
199 from the creation of the final container.  These are called
200 "multi-stage" builds.
201
202 A multi stage build has multiple `FROM` lines.  Each `FROM` line is a
203 separate container build.  The last `FROM` in the file describes the
204 final container image that will be created.
205
206 The key benefit is that the different stages are independent, but you
207 can copy files from one stage to another.
208
209 Here is an example of the bwa build as a multi-stage build.  It is a
210 little bit more complicated, but the outcome is a smaller image,
211 because the "build-essential" tools are not included in the final
212 image.
213
214 ```
215 # Build the base image.  This is the starting point for both the build
216 # stage and the final stage.
217 # the "AS base" names the image within the Dockerfile
218 FROM debian:10-slim AS base
219 MAINTAINER Peter Amstutz <peter.amstutz@curii.com>
220
221 # Install libz, because the bwa binary will depend on it.
222 # As it happens, this already included in the base Debian distribution
223 # because lots of things use libz specifically, but it is good practice
224 # to explicitly declare that we need it.
225 RUN apt-get update -qy
226 RUN apt-get install -qy zlib1g
227
228
229 # This is the builder image.  It has the commands to install the
230 # prerequisites and then build the bwa binary.
231 FROM base as builder
232 RUN apt-get install -qy build-essential wget unzip zlib1g-dev
233
234 # Install BWA 07.7.17
235 RUN wget https://github.com/lh3/bwa/archive/v0.7.17.zip
236 RUN unzip v0.7.17
237 RUN cd bwa-0.7.17 && \
238     make && \
239     cp bwa /usr/bin
240
241
242 # Build the final image.  It starts from base (where we ensured that
243 # libz was installed) and then copies the bwa binary from the builder
244 # image.  The result is the final image only has the compiled bwa
245 # binary, but not the clutter from build-essentials or from compiling
246 # the program.
247 FROM base AS final
248
249 # This is the key command, we use the COPY command described earlier,
250 # but instead of copying from the host, the --from option copies from
251 # the builder image.
252 COPY --from=builder /usr/bin/bwa /usr/bin/bwa
253 ```
254
255 # Best practices for Docker images
256
257 [Docker has published guidelines on building efficient images.](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/)
258
259 Some additional considerations when building images for use with Workflows:
260
261 ## Store Dockerfiles in git, alongside workflow definitions
262
263 Dockerfiles are scripts and should be managed with version control
264 just like other kinds of code.
265
266 ## Be specific about software versions
267
268 Instead of blindly installing the latest version of a package, or
269 checking out the `master` branch of a git repository and building from
270 that, be specific in your Dockerfile about what version of the
271 software you are installing.  This will greatly aid the
272 reproducibility of your Docker image builds.
273
274 Similarly, be as specific as possible about the version of the base
275 image you want to use in your `FROM` command.  If you don't specify a
276 tag, the default tag is called "latest", which can change at any time.
277
278 ## Tag your builds
279
280 Use meaningful tags on your own Docker image so you can tell versions
281 of your Docker image apart as it is updated over time.  These can
282 reflect the version of the underlying software, or a version you
283 assign to the Dockerfile itself.  These can be manually assigned
284 version numbers (e.g. 1.0, 1.1, 1.2, 2.0), timestamps (e.g. YYYYMMDD
285 like 20220126) or the hash of a git commit.
286
287 ## Avoid putting reference data to Docker images
288
289 Bioinformatics tools often require large reference data sets to run.
290 These should be supplied externally (as workflow inputs) rather than
291 added to the container image.  This makes it easy to update reference
292 data instead of having to rebuild a new Docker image every time, which
293 is much more time consuming.
294
295 ## Small scripts can be inputs, too
296
297 If you have a small script, e.g. a self-contained single-file Python
298 script which imports Python modules installed inside the container,
299 you can supply the script as a workflow input.  This makes it easy to
300 update the script instead of having to rebuild a new Docker image
301 every time, which is much more time consuming.
302
303 ## Don't use ENTRYPOINT
304
305 The `ENTRYPOINT` Dockerfile command modifies the command line that is
306 executed inside the container.  This can produce confusion when the
307 command line that supplied to the container and the command that
308 actually runs are different.
309
310 ## Be careful about the build cache
311
312 Docker build has a useful feature where if it has a record of the
313 exact `RUN` command against the exact base layer, it can re-use the
314 layer from cache instead of re-running it every time.  This is a great
315 time-saver during development, but can also be a source of
316 frustration: build steps often download files from the Internet.  If
317 the file being downloaded changes without the command being used to
318 download it changing, it will reuse the cached step with the old copy
319 of the file, instead of re-downloading it.  If this happens, use
320 `--no-cache` to force it to re-run the steps.
321
322 > ## Episode solution
323 > * <a href="../assets/answers/ep8/Dockerfile.single-stage">Dockerfile.single-stage</a>
324 > * <a href="../assets/answers/ep8/Dockerfile.multi-stage">Dockerfile.multi-stage</a>
325 {: .solution}