Docker Multi-stage Build for Go CLI Tools¶
Docker multi-stage builds are a pattern used to optimize the size of the final container image by separating the compilation environment from the runtime environment.^[400-devops__09-Scripting-Language__golang__introduction__part-4.commandline__readme.md] This is particularly beneficial for Go command-line tools, where the final executable can run on a minimal OS image without requiring the full Go compiler toolchain.^[400-devops__09-Scripting-Language__golang__introduction__part-4.commandline__readme.md]
Workflow¶
The build process typically involves a chain of three stages to move from source code to a compact runtime image.^[400-devops__09-Scripting-Language__golang__introduction__part-4.commandline__readme.md]
1. Development Stage¶
The process often begins with a "dev" stage that defines the version of the Go language used and sets up a working directory.^[400-devops__09-Scripting-Language__golang__introduction__part-4.commandline__readme.md] While the source text references this stage, the example provided focuses heavily on the build and runtime stages, implying the dev stage may be used for interactive containerized development or setting the base image.^[400-devops__09-Scripting-Language__golang__introduction__part-4.commandline__readme.md]
2. Build Stage¶
The "build" stage is responsible for compiling the application.^[400-devops__09-Scripting-Language__golang__introduction__part-4.commandline__readme.md]
* Base Image: It starts from a Go-specific image, such as golang:1.15-alpine.^[400-devops__09-Scripting-Language__golang__introduction__part-4.commandline__readme.md]
* Compilation: Source code is copied into the container, and the go build command is executed to produce a static binary (e.g., named videos).^[400-devops__09-Scripting-Language__golang__introduction__part-4.commandline__readme.md]
3. Runtime Stage¶
The final "runtime" stage creates the lean production image.^[400-devops__09-Scripting-Language__golang__introduction__part-4.commandline__readme.md]
* Base Image: It uses a minimal base image, such as alpine.^[400-devops__09-Scripting-Language__golang__introduction__part-4.commandline__readme.md]
* Artifact Copying: Instead of copying source code or build tools, it uses the COPY --from=build directive to extract only the compiled binary from the previous stage.^[400-devops__09-Scripting-Language__golang__introduction__part-4.commandline__readme.md]
* Dependencies: Necessary configuration files (e.g., videos.json) or entry point scripts are also copied.^[400-devops__09-Scripting-Language__golang__introduction__part-4.commandline__readme.md]
Example Configuration¶
The following Dockerfile demonstrates this pattern for a Go CLI tool named videos.^[400-devops__09-Scripting-Language__golang__introduction__part-4.commandline__readme.md]
FROM golang:1.15-alpine as dev
WORKDIR /work
FROM golang:1.15-alpine as build
WORKDIR /videos
COPY ./videos/* /videos/
RUN go build -o videos
FROM alpine as runtime
COPY --from=build /videos/videos /usr/local/bin/videos
COPY ./videos/videos.json /
COPY run.sh /
RUN chmod +x /run.sh
ENTRYPOINT [ "./run.sh" ]
Execution Strategy¶
To properly handle command-line arguments passed to the container, the binary is often executed via a shell script wrapper defined as the entrypoint.^[400-devops__09-Scripting-Language__golang__introduction__part-4.commandline__readme.md]
A script like run.sh is used to forward arguments ($@) to the Go application:^[400-devops__09-Scripting-Language__golang__introduction__part-4.commandline__readme.md]
#!/bin/sh
videos $@
This setup allows interactions like docker run -it videos get --help to function correctly.^[400-devops__09-Scripting-Language__golang__introduction__part-4.commandline__readme.md]