Dockerizing - acbodine/Swift-Microservice GitHub Wiki

Jump to

Why use Docker?

Install Docker

Break down your Swift app

Why use Docker?

If you've never heard of Docker, no worries. Check here for more information: What is Docker?

Docker timeline

  • Images
    • Commit
    • Share
    • Write once
  • Run Anywhere
    • Linux, Darwin, Windows
    • Docker Swarm, Kubernetes
    • Bluemix Containers, AWS
    • OCI, runc

First things first

To install Docker pick your platform from here: Install Docker

Break down your Swift app

Build

Official Swift Docker Image This isn't working currently, but there is PR opened by one of our attendees. Strive to use this moving forward.

Start with least likely to change build instructions; put more likely to change instructions towards the end of Dockerfile. This helps speed up build iterations.

For the complete reference of Dockerfile build instructions refer here: Dockerfile Reference

  • System dependencies
  • Application source (fetch/unpack/build/compile)
  • Entrypoint and default params
  • Port declarations
  • Build arguments for Dockerfile re-use
  • Healthcheck

.dockerignore

In short, only upload parts of the build context that are necessary.

What is build context?

Build context in terms of Docker is everything in the directory that contains your application's Dockerfile. By default Docker will upload everything in that directory as part of the build context.

Dockerfile instructions like ADD or COPY can take local paths relative to the parent directory of your Dockerfile.

Important: you cannot reference paths that are outside of your build context, even with symlinks.

Fetch before build

This serves as a significant build speedup because you fetch your applications Swift dependencies prior to running $ swift build Otherwise, they will be fetched everytime you need to run a $ swift build and if you are making iterative changes to Swift source files, this can add up to significant time.

COPY Package.swift .
RUN swift package fetch

COPY Sources .
RUN swift build

Inheritance

Consider the scenario where you have many Swift services that all run on the same Swift version, and you want to Dockerize all of them.

Instead of copy/pasting the initial 3/4 of your Dockerfile to each service, extract that common 3/4 of the Dockerfile to a base service project.

FROM ubuntu:16.04

# Common Swift instructions like installing system packages, installing swift, etc
$ docker build -t base .

Then each of the Dockerfiles for your Swift services can inherit the base image layers and only be concerned with the service specific build instructions.

FROM base

# Service specific instructions

ENTRYPOINT []
CMD []

NOTE: This is useful for two reasons:

  • Obviously DRY
  • Smaller overall image storage, because the initial 3/4 of your Dockerfile is re-used for each service

There is this neat new Dockerfile instructions called ONBUILD which allows you to defer a build instruction until the image is built for a downstream service.

This allows you to delegate instructions that have to run in downstream builds, while allowing downstream overrides

Debugging build errors

Eventually you will write a Dockerfile that fails to build. Sometimes the output from the build is descriptive enough that you can figure out the problem. If you are wondering how to further inspect the failure, here is how:

Let's say your build output looks something like this:

Step 1/14 : FROM ubuntu:16.04
 ---> f753707788c5
Step 2/14 : ARG SWIFT_BUILD=swift-3.0.2-release
 ---> Using cache
 ---> f13b8ea741d7
Step 3/14 : ARG SWIFT_SNAPSHOT=swift-3.0.2-RELEASE
 ---> Using cache
 ---> 1e6d7821c6b5
Step 4/14 : ARG SWIFT_OS=ubuntu16.04
 ---> Using cache
 ---> 5e448dc9d0cc
Step 5/14 : ARG SWIFT_OS_DIR=ubuntu1604
 ---> Using cache
 ---> ed78b69f9b51
Step 6/14 : RUN apt-get update  && apt-get install -y     clang     gcc-4.8     git-core     libcurl3     libcurl4-gnutls-dev     libicu-dev     libssl-dev     libxml2     openssh-client     openssl     wget  && rm -rf /var/lib/apt/lists/*
 ---> Using cache
 ---> 898dc1fb8e16
Step 7/14 : This doesn't work :(

Look for the last successful image layer that was built before the Dockerfile step that failed, in this case 898dc1fb8e16

$ docker run --rm -it --entrypoint /bin/bash 898dc1fb8e16

Run

  • Hostname/IPs
  • Service Endpoints
  • Resource quotas
  • Storage

Memory

# NOTE: minimum is 4m
$ docker run .. -m 128m ..

Volumes

$ docker run .. -v /local/path:/container/path ..

Keys/Secrets

Don't bake them into your container image, layers can be inspected and secrets and passwords can be extracted from these layers. Instead put them in a volume or a volume container and mount them into your container at runtime.

Monitoring

  • Healthcheck
  • New Relic
  • Monit
  • Supervisord