Deploy a Golang application

Deploy an application written in Go(lang) #

Requirements:

  1. DNS is set up, e.g. go-example.planetary-quantum.org and points to our cluster/endpoint (we use quantum-staging in our example)
  2. Docker is installed on your laptop/machine
  3. You can run a Makefile
  4. Your cluster is Traefik-enabled
  5. bonus points: you have direnv

About this application #

This is an example application written in Go(lang). The application follows 12-factor principals and shows you how to use environment variables to adjust how the application responds to a request. On top of this, we explain how to structure your Dockerfile for optimal builds, how to manage credentials (locally) and how to continuously deploy changes to your application within seconds to an online environment.

The application has multiple routes - most notably /hello.

/hello will respond based on the HELLO_RESPONSE environment variable.

Creating the image #

The included Dockerfile follows best practices and uses a multi-stage build process — a Docker image with Go(lang) which acts as a build stage, and the final (Debian-based) image which only contains the example-go-app binary.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
FROM golang:1.16 as build
ENV APP_USER app
ENV APP_HOME /app
RUN groupadd $APP_USER && useradd -m -g $APP_USER -l $APP_USER
USER $APP_USER
WORKDIR $APP_HOME
ADD go.mod .
ADD go.sum .
ADD main.go .
RUN go mod download
RUN go mod verify
RUN go build -o example-go-app

FROM debian:buster
ENV APP_USER app
ENV APP_HOME /app
ENV HELLO_RESPONSE W-O-R-L-D
RUN groupadd $APP_USER && useradd -m -g $APP_USER -l $APP_USER
USER $APP_USER
WORKDIR $APP_HOME
COPY --chown=0:0 --from=build /app/example-go-app $APP_HOME
EXPOSE 8080

CMD ["./example-go-app"]

Advantages of this process include improved caching (and therefore faster builds), the ability to manage how and when credentials are introduced in the build process, and leaner (smaller) images which are deployed faster and also scale up faster in the future.

Our binary (example-go-app) runs on port 8080 (inside the container), it’ll be mapped behind a Traefik when we deploy.

To build and publish the image, we included a Makefile:

$ make build
...
$ make push
You can override the version for all targets in the Makefile with make build VERSION=1.2.3, make push VERSION=1.2.3 or make deploy VERSION=1.2.3. Otherwise, it’ll default to dev.

Configuring the stack #

Quantum works so well because you don’t need to learn a ton of new manifests. We use docker-compose (or Swarm stack files) to deploy your stack.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
version: "3.8"

networks:
  public:
    external:
      name: public

services:
  example:
    image: r.planetary-quantum.com/quantum-public/example-go-app:$VERSION
    environment:
      HELLO_RESPONSE: $HELLO_RESPONSE
    networks:
      - public
    deploy:
      replicas: 2
    labels:
      traefik.enable: "true"
      traefik.docker.network: "public"
      traefik.frontend.rule: "Host:go-example.planetary-quantum.org"
      traefik.port: "8080"
Adjust the hostname when you deploy this.

Credentials #

The repository contains an .envrc-dist file which you should first copy:

cp .envrc-dist .envrc

And then customize your credentials and the HELLO_RESPONSE:

$ cat .envrc
# APP
export HELLO_RESPONSE=Mundo

# Deployment
export QUANTUM_USER=your@email
export QUANTUM_PASSWORD=your-password
export QUANTUM_ENDPOINT=your-endpoint
export QUANTUM_STACK=example-go-app
Either use direnv allow or source .envrc to activate the file.

Deployment #

To deploy the stack:

❯ make deploy
docker build -t r.planetary-quantum.com/quantum-public/example-go-app:dev .
[+] Building 3.2s (19/19)
...
 => CACHED [stage-1 4/4] COPY --chown=0:0 --from=build /app/example-go-app /app                                                                                                                  0.0s
...
docker push r.planetary-quantum.com/quantum-public/example-go-app:dev
The push refers to repository [r.planetary-quantum.com/quantum-public/example-go-app]
...
0e41e5bdb921: Layer already exists 
dev: digest: sha256:0db6270a475932b1f262e7f194efd9799b81520671bbc605309c8d26d1da273b size: 1155
docker run \
		--rm \
		-v /Users/till/example-go-app:/work -w /work \
		-e QUANTUM_USER \
		-e QUANTUM_PASSWORD \
		-e QUANTUM_ENDPOINT \
		-e QUANTUM_STACK \
		-e HELLO_RESPONSE \
		-e VERSION=dev \
		r.planetary-quantum.com/quantum-public/cli:2 quantum-cli stacks update --create --wait --stack example-go-app
time="2021-03-26T12:35:33Z" level=info msg="Updating stack 'example-go-app' (on endpoint: 'quantum-staging')"
time="2021-03-26T12:35:37Z" level=info msg="missing or unreadable .quantum file - using defaults"
Pulling image quantum-public/example-go-app:dev    ... done! [428ms]
time="2021-03-26T12:35:38Z" level=info msg="Updating 'example-go-app'"
time="2021-03-26T12:35:43Z" level=info msg="initial deployment done - now waiting for the containers to come up"
Waiting for service example-go-app_example ... done! [99ms]
time="2021-03-26T12:35:43Z" level=info msg="all services updated successfully"
time="2021-03-26T12:35:43Z" level=info msg="Updated stack successfully"
As you can see, make deploy invokes the targets build and push as well. Adjust this behavior, as you see fit.

Last but not least - verify:

$ curl https://go-example.planetary-quantum.org/hello
Mundo

How change the response #

To change the response of the app, repeat the steps:

  1. Change HELLO_RESPONSE in your .envrc file (and source/activate it)
  2. Run make deploy

Done #

Building upon what we showed you in this example, you have the power to rapidly deploy services (e.g. easily demo progress and gather feedback while you develop your application), 12-factorize your application (imagine HELLO_RESPONSE is a database password) and have all the required bits and pieces to implement a CI/CD pipeline which automates delivery of your application to an online environment.

Code #

You can find the code for the application, Dockerfile and (docker-)compose on Github: https://github.com/hostwithquantum/example-go-app