Skip to content
This repository has been archived by the owner on Jan 21, 2024. It is now read-only.

Build Docker Images

Andre Rosa edited this page Aug 10, 2018 · 5 revisions

Best Practices


  1. Do not reinvent the wheel. Check if Docker images already exist for your purposes in

  2. alpine OS based Dockerfiles are preferable.

  3. Use Docker multi-stage builds.

  4. Concatenate RUN layers as a single command with && operations.

  5. Clean and purge all dependencies and libraries not used in the final Production image.

  6. Use curl or git clone commands to pull source code from a repository and build the artifacts in your Dockerfile as part of the builder stage.

  7. 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.
  8. Similarly, pull configuration files from a source code outside the Dockerfile repository, otherwise keep configuration files with your Dockerfile.

  9. We follow the Twelve-Factor App methodology Practices for configuration. Create default Environment Variable values (ENV) in your Dockerfile.

  10. Tag your image verbosely, including appending the Docker image's OS type.

  11. Comment verbosely.

  12. Development: To avoid rebuilding your Dockerfile, run commands inside the Docker container in sh, and update the final Production Dockerfile accordingly.

  13. Development: Do not concatenate RUN commands until final production image

  14. Tip: If you'd like to send file logs to docker logs, setup a symbolic link to stdout, 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)

  15. Do not use volumes (VOLUME). Volumes, preferably, should be defined in docker-compose yml files, if at all required.


Docker Image Build Workflow

Development


1. During Development, follow Best Practices 12 and 13.

Example
# 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).

Example
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"]

3. Test your Development Dockerfile.

Example
$ docker build -t dbmi/secret-getter:test ./
$ docker run --rm -it dbmi/secret-getter:test

Production


1. Create Production 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)

Example 1
  1. Builds master branch (default setting ${version:-master} of secret-getter.
  2. 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 ./
Example 2
  1. Builds 0.9-alpha branch of secret-getter.
  2. 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 ./

3. Push Production Docker image to hub.docker.com

Example
$ docker push dbmi/secret-getter:0.9-alpha-alpine

Production & Secrets


1. Build your Production Docker Image with secret_getter

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.

Example 1
  1. Builds 0.9-alpha branch of secret-getter github repo.
  2. Copies the secret_getter executable built in builder stage to hello-world:latest Docker image.
    • see secret-getter Dockerfile build stage labeled runtime.
  3. 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 ./
Example 2
  1. Builds master branch (default setting ${version:-master}) of secret-getter github repo.
  2. Copies the secret_getter executable built in builder stage to dbmi/i2b2transmart:release-18.1 Docker image.
    • see secret-getter Dockerfile build stage labeled runtime.
  3. 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.


Docker Image Versioning Examples


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