From 0aca127062b2770009dc7ad3acb8146d6a7bac75 Mon Sep 17 00:00:00 2001 From: Brandur Date: Wed, 21 Mar 2018 15:28:18 -0700 Subject: [PATCH] Leaner Docker image through the use of multi-stage builds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In #50 I was able to successfully push an image to Docker Hub, but quite a large one -- weighing in at 91 MB, which is pretty heavy for this small program. Here we lean out the final image by switching `Dockerfile` to use a multi-phase build strategy. This creates one image initially based off of a Go image which is used to build the final release program, then creates a second image based on the lightweight Alpine Linux and copies over the release binary. This has the effect of reducing the final image's size from 91 MB to a lean 13.7 MB, which is faster/cheaper for users to pull. A smaller optimization is to add a `.dockerignore` file which prevents us from having to copy a number of extraneous files into the Docker daemon for the initial build. This will help nominally to speed things up. One slight loss is that I've removed testing within the Go image from `.travis.yml` because we no longer have a Go runtime with which to run `go test ./...`. I think this is okay because we're already running tests elsewhere in the Travis build, and it's unlikely that running them in a Docker container is going to surface anything extra. Here's me running the image locally: ``` $ docker run -d --name stripe-mock-container -p 12112:12111 stripemock/stripe-mock bc71abebe5f6d80a9af2d1d53c86777d8c50b2e9a8d4de4a8b25d66c32a5d44b $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES bc71abebe5f6 stripemock/stripe-mock "/bin/sh -c /stripe-…" 26 seconds ago Up 28 seconds 0.0.0.0:12112->12111/tcp stripe-mock-container ``` And a successful cURL: ``` $ curl http://localhost:12112 { "error": { "message": "Please authenticate by specifying an `Authorization` header with any valid looking testmode secret API key. For example, `Authorization: Bearer sk_test_123`. Authorization was ''.", "type": "invalid_request_error" } } ``` --- .dockerignore | 6 ++++++ .travis.yml | 1 - Dockerfile | 30 +++++++++++++++++++++++++----- 3 files changed, 31 insertions(+), 6 deletions(-) create mode 100644 .dockerignore diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..894a046a --- /dev/null +++ b/.dockerignore @@ -0,0 +1,6 @@ +README.md +LICENSE +Makefile +dist/ +goreleaser.yml +openapi/ diff --git a/.travis.yml b/.travis.yml index 5b4059cc..eb93ecef 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,7 +24,6 @@ script: - docker build -t "$DOCKER_REPO" . - docker run -d --name stripe-mock-container -p 12111:12111 "$DOCKER_REPO" - docker ps -a - - docker exec -it stripe-mock-container /bin/sh -c "cd /go/src/github.com/stripe/stripe-mock/; go test ./..." deploy: provider: script diff --git a/Dockerfile b/Dockerfile index c7eb34fd..01a066d6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,29 @@ -FROM golang:1.9-alpine +# -*- mode: dockerfile -*- +# +# A multi-stage Dockerfile that builds a Linux target then creates a small +# final image for deployment. -ADD . /go/src/github.com/stripe/stripe-mock +# +# STAGE 1 +# +# Uses a Go image to build a release binary. +# -RUN go install github.com/stripe/stripe-mock +FROM golang:1.9-alpine AS builder +WORKDIR /go/src/github.com/stripe/stripe-mock/ +ADD ./ ./ +RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o stripe-mock . -EXPOSE 12111 +# +# STAGE 2 +# +# Use a tiny base image (alpine) and copy in the release target. This produces +# a very small output image for deployment. +# -ENTRYPOINT /go/bin/stripe-mock +FROM alpine:latest +RUN apk --no-cache add ca-certificates +WORKDIR / +COPY --from=builder /go/src/github.com/stripe/stripe-mock/stripe-mock . +ENTRYPOINT /stripe-mock +EXPOSE 12111