Dockerizing - acbodine/Swift-Microservice GitHub Wiki
Jump to
Why use Docker?
If you've never heard of Docker, no worries. Check here for more information: What is Docker?
- 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