Summary
This post covers a Hello-Go web application written in Go and hosted in a docker container. The solution will use Docker multi-stage builds to create the container image and display ‘Hello-Go’ on web requests.
Prerequisites
- Visual Studio Code
- Docker Desktop
- Windows 10 (version 2004 or higher) for WSL2 support
Description
Go is an open source project from Google that is based on the C programming language. It is a compiled language with multi-platform support across Linux, Windows, macOS and more. With the Go runtime performance and succinct syntax of its programming language, Golang, it is well suited for making a lightweight web application. The compiled application will be published as a Docker image so it can run on a container platform.
A Docker multi-staged build process will be used to separate the build and runtime images. Separating the build and runtime provides the benefit of removing non-essential runtime files and applications to reduce the image size. Removing the unrelated applications is also a security hardening component to reduce attack vectors for when the container is running.
The following process represents the subsystems involved in building the Hello-Go web application.
In this example we’ll build the image for the Linux kernel using Windows10 WSL2.
Steps
1. Using Windows Terminal, open a WSL Linux terminal (such as Ubuntu), create a source folder, hello-go, and open the folder in Visual Studio Code
2. Create a source file named ‘hello-go.go’
3. Enter the following code for the hello-go web application
package main
import (
"fmt"
"log"
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello-Go")
})
log.Fatal(http.ListenAndServe(":8080", nil))
}
4. Create a docker file for the multi-stage build named ‘Dockerfile’ (no extension)
5. First section of the Dockerfile, we define the base image that will be used for the runtime image and expose the web application port the hello-go application is listening on, port 8080.
FROM alpine:latest as base
EXPOSE 8080
6. Next we define the build image that contains the Go compiler we can use to compile the .go source. We’ll create a /build folder and compile using the ‘go build’ command.
FROM golang:1.15.2-alpine3.12 as build
RUN mkdir /build
ADD . /build
WORKDIR /build
RUN go build -o hello-go .
7. With the application compiled, we can add the final stage by copying the build output to the runtime image defined at the beginning of the Dockerfile. We create an /app folder in the runtime image, copy the build to it, and specify the command to execute the application when the container runs
FROM base as final
RUN mkdir /app
WORKDIR /app
COPY --from=build /build .
CMD ["/app/hello-go"]
8. In Visual Studio Code, go to the Terminal menu and select New Terminal
9. Enter and run the following docker command to build and tag, hello-go:latest, the docker image
docker build -t hello-go:latest .
10. Verify the docker build completed successfully
11. Run the container and publish port 8080 on the host so it is accessible
docker run -d -p 8080:8080 hello-go:latest
12. Call the web application using ‘curl’
curl http://localhost:8080
13. Verify that the curl response displays ‘Hello-Go’
14. List the build and runtime images to see the size differences
docker images | grep 'hello-go\|golang'
15. Notice the size difference of 300MB for the build image, golang, and 12MB for the runtime image, hello-go.