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 && 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


<pre><span class="pl-c">## Build the container</span>
sudo docker build  -t paste-api-dev <span class="pl-c1">.</span>

<span class="pl-c">## Run source</span>
sudo docker run -p 6379 -p 8080:8080 -it paste-api-dev</pre>

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"]

Share This