Build Docker Images
-
Do not reinvent the wheel. Check if Docker images already exist for your purposes in
-
alpine
OS based Dockerfiles are preferable. -
Use Docker multi-stage builds.
-
Concatenate
RUN
layers as a single command with&&
operations. -
Clean and purge all dependencies and libraries not used in the final Production image.
-
Use
curl
orgit clone
commands to pull source code from a repository and build the artifacts in your Dockerfile as part of thebuilder
stage. -
Use Docker build arguments (
ARG
) to limit the number of Dockerfiles needed for variations of your Docker image.- e.g., use build argument
ARG version
to build Docker images with different branches/versions of the source code.
- e.g., use build argument
-
Similarly, pull configuration files from a source code outside the Dockerfile repository, otherwise keep configuration files with your Dockerfile.
-
We follow the Twelve-Factor App methodology Practices for configuration. Create default Environment Variable values (
ENV
) in your Dockerfile. -
Tag your image verbosely, including appending the Docker image's OS type.
-
Comment verbosely.
-
Development: To avoid rebuilding your Dockerfile, run commands inside the Docker container in
sh
, and update the final Production Dockerfile accordingly. -
Development: Do not concatenate
RUN
commands until final production image -
Tip: If you'd like to send file logs to
docker logs
, setup a symbolic link tostdout
, e.g.RUN ln -sf /dev/stdout app.log
. This approach is also helpful if you'd like to send logs to Splunk (see splunk.yml) -
Do not use volumes (
VOLUME
). Volumes, preferably, should be defined in docker-composeyml
files, if at all required.
1. During Development, follow Best Practices 12 and 13.
# start Docker container with base Docker image (Dockerfile FROM command)
$ docker run --rm -it --entrypoint=sh golang:1.10-rc-alpine
# -- inside container --
# now inside container deployed with image, in this case, golang:1.10-rc-alpine
# start developing
#
$$/go apk update
$$/go apk add git make
$$/go git clone https://github.com/hms-bmi/secret-getter.git -b master /repo
$$/go cd /repo
$$/go make all
# ...
# run commands and keep record of them
# ...
# ...
# exit container
$$/go exit
# -- back at your command line --
$
2. Update your Development Dockerfile with latest changes (Best Practice 13).
FROM golang:1.10-rc-alpine AS builder
RUN apk update
RUN apk add git make
RUN git clone https://github.com/hms-bmi/secret-getter.git -b master /repo
RUN cd /repo
RUN make all
ENTRYPOINT ["ls"]
CMD ["-a", "/repo"]
$ docker build -t dbmi/secret-getter:test ./
$ docker run --rm -it dbmi/secret-getter:test
Example (secret-getter Dockerfile):
# best practices: 7
ARG image=alpine
ARG tag=latest
# best practices: 11
###########################
# build secret-getter
###########################
# best practices: 1, 2, 3
FROM golang:1.10-rc-alpine AS builder
# best practices: 7
ARG version
ENV GIT_REPO https://github.com/hms-dbmi/secret-getter.git
ENV VERSION ${version:-master}
# best practices: 4, 5, 6, 11
RUN apk update && DEPENDENCIES="make git" \
&& apk --no-cache add $DEPENDENCIES \
&& git clone $GIT_REPO -b ${VERSION} secret-getter \
&& cd secret-getter \
# disable crosscompiling
# compile linux only
# rebuild all packages
&& CGO_ENABLED=0 GOOS=linux GOARCH=amd64 REBUILD=1 make all \
&& cd .. \
&& rm -rf secret-getter \
&& apk del $DEPENDENCIES && rm -rf /var/cache/apk/
# best practices: 11
###########################
# secret-getter standalone
###########################
FROM scratch AS executable
COPY --from=builder /go/bin/secret_getter /usr/bin/
ENTRYPOINT ["secret_getter"]
# example command line options
CMD ["help"]
# best practices: 11
###########################
# secret-getter runtime
###########################
# best practices: 3, 7
FROM ${image}:${tag} AS runtime
# best practices: 6
COPY --from=builder /go/bin/secret_getter /usr/bin/
# best practices: 9
ENV SG_COMMAND ""
ENV SG_OPTIONS ""
ENTRYPOINT ["secret_getter"]
# example command line options
CMD ["help"]
2. Build your Production Docker image using verbose tags (Best Practice 10)
- Builds master branch (default setting
${version:-master}
of secret-getter. - Creates Docker image
dbmi/secret-getter:master-alpine
with the executable/usr/bin/secret_getter
.
$ docker build -t dbmi/secret-getter:master-alpine --target=executable ./
- Builds 0.9-alpha branch of secret-getter.
- Creates Docker image
dbmi/secret-getter:0.9-alpha-alpine
with the executable/usr/bin/secret_getter
$ docker build -t dbmi/secret-getter:0.9-alpha-alpine --build-arg version=0.9-alpha --target=executable ./
$ docker push dbmi/secret-getter:0.9-alpha-alpine
Production & Secrets
For any Production Docker images required to retrieve secrets from a secure location, such as Vault, you will need append the secret_getter
executable to your Production Docker Image. Appending secret_getter
to the new image alters the ENTRYPOINT
.
- Builds 0.9-alpha branch of secret-getter github repo.
- Copies the
secret_getter
executable built inbuilder
stage tohello-world:latest
Docker image.- see secret-getter Dockerfile build stage labeled
runtime
.
- see secret-getter Dockerfile build stage labeled
- Tags the new image as
hello-world:latest-sg.0.9-alpha-alpine
.
$ cd docker-images/secret-getter
$ docker build -t hello-world:latest-sg.0.9-alpha-alpine \
--build-arg version=0.9-alpha \
--build-arg image=hello-world \
--build-arg tag=latest \
--target=runtime ./
- Builds master branch (default setting
${version:-master}
) of secret-getter github repo. - Copies the
secret_getter
executable built inbuilder
stage todbmi/i2b2transmart:release-18.1
Docker image.- see secret-getter Dockerfile build stage labeled
runtime
.
- see secret-getter Dockerfile build stage labeled
- Tags the new image as
dbmi/i2b2transmart:release-18.1-sg.master
.
$ cd docker-images/secret-getter
$ docker build -t dbmi/i2b2transmart:release-18.1-sg.master \
--build-arg image=dbmi/i2b2transmart \
--build-arg tag=release-18.1
--target=runtime ./
To learn more how to use secret_getter
in your deployments, review Use Secret-Getter.
Versioning follows a verbose format that is semi-consistent. Use your best judgement. The goal is clarity.
# nginx on OS alpine running configuration v. tmpl.1.0
dbmi/nginx: alpine-tmpl.1.0
# i2b2tranSMART application version: release-18.1-beta, minor version: 9
dbmi/i2b2transmart: release-18.1-beta-9
# i2b2tranSMART application version: release-18.1-beta, minor version: 9
# running secret_getter version: 0.9-alpha
dbmi/i2b2transmart: release-18.1-beta-9-sg.0.9-alpha
# oracle database version: 12.2.0.1-ee
# installed schemas: i2b2 v. 1.7.09, tranSMART v. release-18.1
# minor version: 1.3
# pre-populated data: nhanes
dbmi/i2b2transmart-db: oracle.12.2.0.1-ee-i2b2.1.7.09-tm.release-18.1-v.1.3-nhanes
# rserve v. 3.2.1 built for tranSMART v. release-18.1
dbmi/rserve: 3.2.1-tm.release-18.1
# solr v. 4.5.0 built for tranSMART v. release-18.1
dbmi/solr: 4.5.0-tm.release-18.1
# secret-getter version: 0.9-alpha
dbmi/secret_getter: 0.9-alpha