This post is a walkthrough on my approach to develop and deploy Golang web service in Docker container. There are many reasons for integrating Docker into the daily routine as a developer, one being the simplification of setting up a separated runtime environment. Using containers in production has, even more, advantages from simplified release to better performance. Kubernetes and Docker Swarm is gaining more and more space, which I think is partly because of the simplified management on IBM Cloud and Google Cloud Platform.
The setup discussed in this article can be found at https://github.com/pete314/paste-api-golang. The repository contains a “toy” project, and the only purpose is to be used as an example. The implementation itself is a REST API with OAuth authentication and option to PUT and GET text, stored in Redis. The service is implemented on the top of fasthttp with fasthttprouter.
The project structure
The project is very simple as is the project. The api folder holds the source for the application with a Makefile, to simplify build process. Config holds the configuration for the application. Script folder contains a production build script, working with the docker file in Deploy, which builds on the scratch container. For the production build, Golang should be installed to avoid pulling the dependencies every time but can be run in a container as well. There circle.yml for configuring CI builds, which is still running on version one, without the nice feature of docker support (in 2.0).
├── api │ ├── Makefile │ └── src │ ├── modules │ │ └── v0 │ │ ├── auth │ │ │ ├── auth_controller.go │ │ │ └── auth_model.go │ │ ├── common │ │ │ ├── auth.go │ │ │ ├── cassandra.go │ │ │ ├── configuration.go │ │ │ ├── crypto.go │ │ │ ├── error_handler.go │ │ │ ├── redis_client.go │ │ │ ├── request.go │ │ │ ├── response.go │ │ │ └── server.go │ │ └── paste │ │ ├── paste_controller.go │ │ └── paste_model.go │ ├── runner.go │ └── tests │ └── modules │ └── v0 │ └── unit │ └── common_test.go ├── circle.yml ├── config │ ├── server-config.json │ └── server-config.local.json ├── deploy │ ├── api │ ├── Dockerfile.scratch │ └── server-config.local.json ├── docker-compose.yml ├── Dockerfile ├── README.md └── scripts └── deploy-api.sh
The development docker file
The container is based on the official golang base, golang:latest.The only additional packages are for the being able to run the “make”. The entry-point for the container points to the binary to simplify the execution. With the default configuration, the api is looking for an external Redis server, which can be run in another container or on a dedicated host. With this setup Redis can be shared between multiple applications, in production most likely it will have dedicated setup anyway.
FROM golang:latest RUN apt-get update &amp;&amp; apt-get install -y \ aufs-tools \ automake \ build-essential # Copy the API Source RUN mkdir /api ADD ./api /api/ WORKDIR /api # Copy config RUN mkdir /config ADD ./config /config # Compile the source RUN make # Expose the application on port 8080 EXPOSE 8080 # Create an entry point ENTRYPOINT ["/api/paste-api-golang"] # Create command entry point for app CMD ["/api/paste-api-golang", "-conf", "config/server-config.local.json"]
The above can be built and executed as
&lt;pre&gt;&lt;span class="pl-c"&gt;## Build the container&lt;/span&gt; sudo docker build -t paste-api-dev &lt;span class="pl-c1"&gt;.&lt;/span&gt; &lt;span class="pl-c"&gt;## Run source&lt;/span&gt; sudo docker run -p 6379 -p 8080:8080 -it paste-api-dev&lt;/pre&gt;
In the development stage, the above setup is enough to test manually or programmatically.
Deploy to production
The development build is light weight without much extra unnecessary OS packages, but it still contains a Debian base image with its default packages installed. Instead of using this the source can be run on the scratch base image. Building this image is only 6MB, plus the binary and dependencies required for running the API, which makes it perfect to move around on the network fast.
The deploy script “deploy-api.sh”, is compiling the source on the host for “Dockerfile.scratch” to build the final image. I found this strategy in this brilliant post about deploying on scratch image
</pre> ## Author: Peter Nagy <https://peternagy.ie> ## Since: June, 2017 ## Description: Deploy file to build production container # Create deploy space cd .. && rm -rf deploy/api # Compile the source CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o ./deploy/api api/src/runner.go # Move binary into container cp config/server-config.local.json deploy/ # Create production image cd deploy sudo docker build --no-cache=true -t paste-api-scratch -f Dockerfile.scratch .
And the Docketfile.scratch
FROM scratch ADD . /app WORKDIR /app CMD ["/app/api", "-conf", "/app/server-config.local.json"]