Last active
April 9, 2024 05:20
-
-
Save xeoncross/862d659e48b05d684dddad0ceaf36029 to your computer and use it in GitHub Desktop.
Examples of using multi-stage builds with docker and Go to reduce the final image size / attack surface.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Sample from @citizen428 https://dev.to/citizen428/comment/6cmh | |
FROM golang:alpine as build | |
RUN apk add --no-cache ca-certificates | |
WORKDIR /build | |
ADD . . | |
RUN CGO_ENABLED=0 GOOS=linux \ | |
go build -ldflags '-extldflags "-static"' -o app | |
FROM scratch | |
COPY --from=build /etc/ssl/certs/ca-certificates.crt \ | |
/etc/ssl/certs/ca-certificates.crt | |
COPY --from=build /build/app /app | |
ENTRYPOINT ["/app"] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# https://www.reddit.com/r/golang/comments/9u7qnl/deploying_go_apps_on_docker_scratch_images/ | |
# This is the first stage, for building things that will be required by the | |
# final stage (notably the binary) | |
FROM golang | |
# Copy in just the go.mod and go.sum files, and download the dependencies. By | |
# doing this before copying in the other dependencies, the Docker build cache | |
# can skip these steps so long as neither of these two files change. | |
COPY go.mod go.sum ./ | |
RUN go mod download | |
# Assuming the source code is collocated to this Dockerfile | |
COPY . . | |
# Build the Go app with CGO_ENABLED=0 so we use the pure-Go implementations for | |
# things like DNS resolution (so we don't build a binary that depends on system | |
# libraries) | |
RUN CGO_ENABLED=0 go build -o /myapp | |
# Create a "nobody" non-root user for the next image by crafting an /etc/passwd | |
# file that the next image can copy in. This is necessary since the next image | |
# is based on scratch, which doesn't have adduser, cat, echo, or even sh. | |
RUN echo "nobody:x:65534:65534:Nobody:/:" > /etc_passwd | |
# The second and final stage | |
FROM scratch | |
# Copy the binary from the builder stage | |
COPY --from=0 /myapp /myapp | |
# Copy the certs from the builder stage | |
COPY --from=0 /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ | |
# Copy the /etc/passwd file we created in the builder stage. This creates a new | |
# non-root user as a security best practice. | |
COPY --from=0 /etc/passwd /etc/passwd | |
# Run as the new non-root by default | |
USER nobody |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# https://medium.com/@pierreprinetti/the-go-1-11-dockerfile-a3218319d191 | |
# Accept the Go version for the image to be set as a build argument. | |
# Default to Go 1.11 | |
ARG GO_VERSION=1.11 | |
# First stage: build the executable. | |
FROM golang:${GO_VERSION}-alpine AS builder | |
# Create the user and group files that will be used in the running container to | |
# run the process an unprivileged user. | |
RUN mkdir /user && \ | |
echo 'nobody:x:65534:65534:nobody:/:' > /user/passwd && \ | |
echo 'nobody:x:65534:' > /user/group | |
# Install the Certificate-Authority certificates for the app to be able to make | |
# calls to HTTPS endpoints. | |
RUN apk add --no-cache ca-certificates | |
# Set the environment variables for the go command: | |
# * CGO_ENABLED=0 to build a statically-linked executable | |
# * GOFLAGS=-mod=vendor to force `go build` to look into the `/vendor` forlder. | |
ENV CGO_ENABLED=0 GOFLAGS=-mod=vendor | |
# Set the working directory outside $GOPATH to enable the support for modules. | |
WORKDIR /src | |
# Import the code from the context. | |
COPY ./ ./ | |
# Build the executable to `/app`. Mark the build as statically linked. | |
RUN go build \ | |
-installsuffix 'static' \ | |
-o /app . | |
# Final stage: the running container. | |
FROM scratch AS final | |
# Import the user and group files from the first stage. | |
COPY --from=builder /user/group /user/passwd /etc/ | |
# Import the Certificate-Authority certificates for enabling HTTPS. | |
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ | |
# Import the compiled executable from the second stage. | |
COPY --from=builder /app /app | |
# Declare the port on which the webserver will be exposed. | |
# As we're going to run the executable as an unprivileged user, we can't bind | |
# to ports below 1024. | |
EXPOSE 8080 | |
# Perform any further action as an unprivileged user. | |
USER nobody:nobody | |
# Run the compiled binary. | |
ENTRYPOINT ["/app"] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# https://raw.githubusercontent.com/syntaqx/sandbox/ | |
# Use Alphine instead of Scratch as it's be hardened | |
# Accept image version tags to be set as a build arguments | |
ARG GO_VERSION=1.11 | |
ARG ALPINE_VERSION=3.8 | |
# Throw-away builder container | |
FROM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS builder | |
# Install build and runtime dependencies | |
# - Git is required for fetching Go dependencies | |
# - Certificate-Authority certificates are required to call HTTPs endpoints | |
RUN apk add --no-cache git ca-certificates | |
# Normalize the base environment | |
# - CGO_ENABLED: to build a statically linked executable | |
# - GO111MODULE: force go module behavior and ignore any vendor directories | |
ENV CGO_ENABLED=0 GO111MODULE=on | |
# Set the builder working directory | |
WORKDIR /go/src/github.com/syntaqx/sandbox | |
# Fetch modules first. Module dependencies are less likely to change per build, | |
# so we benefit from layer caching | |
ADD ./go.mod ./go.sum* ./ | |
RUN go mod download | |
# Import the remaining source from the context | |
COPY . /go/src/github.com/syntaqx/sandbox | |
# Build a statically linked executable | |
RUN go build -installsuffix cgo -ldflags '-s -w' -o ./bin/app ./cmd/app | |
# Runtime container | |
FROM alpine:${ALPINE_VERSION} | |
# Copy the binary and sources from the builder stage | |
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt | |
COPY --from=builder /go/src/github.com/syntaqx/sandbox/bin/app ./app | |
# Create a non-root runtime user | |
RUN addgroup -S sandbox && adduser -S -G sandbox sandbox && chown -R sandbox:sandbox ./app | |
USER sandbox | |
# Document the service listening port(s) | |
EXPOSE 8080 | |
# Define the containers executable entrypoint | |
ENTRYPOINT ["./app"] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# https://github.com/usefathom/fathom/blob/master/Dockerfile | |
# Building a Go backend + Javascript UI | |
FROM node:alpine AS assetbuilder | |
WORKDIR /app | |
COPY package*.json ./ | |
COPY gulpfile.js ./ | |
COPY assets/ ./assets/ | |
RUN npm install && NODE_ENV=production ./node_modules/gulp/bin/gulp.js | |
FROM golang:latest AS binarybuilder | |
WORKDIR /go/src/github.com/usefathom/fathom | |
COPY . /go/src/github.com/usefathom/fathom | |
COPY --from=assetbuilder /app/assets/build ./assets/build | |
RUN make docker | |
FROM alpine:latest | |
EXPOSE 8080 | |
HEALTHCHECK --retries=10 CMD ["wget", "-qO-", "http://localhost:8080/health"] | |
RUN apk add --update --no-cache bash ca-certificates | |
WORKDIR /app | |
COPY --from=binarybuilder /go/src/github.com/usefathom/fathom/fathom . | |
CMD ["./fathom", "server"] |
Author
xeoncross
commented
Mar 13, 2019
•
- https://docs.docker.com/develop/develop-images/multistage-build/#use-multi-stage-builds
- breakdown of the Dockerfile commands
- https://gist.github.com/Xeoncross/c46ee08e04bb4fc08438d4ba80a0ac3c
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment