We have an application where Nginx and Go containers are used. Nginx container proxies requests to Go container. Nginx exposes port 8080 and we map it to port 80 so it is accessible via port 80. Nginx then talks to Go container via port 9090 which is a static port. We will be using Kubernetes volumes for the Nginx config instead of creating a custom image.


Structure


├── Makefile
├── deploy
│ └── k8s
│ ├── configmap.yaml
│ ├── deployment.yaml
│ └── service.yaml
├── docker
│ └── dev
│ ├── docker-compose.yaml
│ ├── go
│ │ └── Dockerfile
│ └── nginx
│ ├── Dockerfile
│ └── default.conf
└── main.go

Files


deployment.yaml


apiVersion: apps/v1
kind: Deployment

metadata:
name: address-finder-deployment
labels:
app: address-finder

spec:
replicas: 1
selector:
matchLabels:
app: address-finder
template:
metadata:
labels:
app: address-finder
spec:
containers:
- name: go
image: you/address-finder-go:latest
- name: nginx
image: nginx:1.19.4-alpine
ports:
- containerPort: 8080
volumeMounts:
- name: nginx-config
mountPath: /etc/nginx/conf.d/default.conf
subPath: nginx.conf
readOnly: true
volumes:
- name: nginx-config
configMap:
name: address-finder-config

configmap.yaml


What you need to know about configmap is that, the changes in here won't restart the pods so you will have to handle restarts yourself which is not the scope of this post.


apiVersion: v1
kind: ConfigMap

metadata:
name: address-finder-config

data:
nginx.conf: |
server {
listen 8080 default_server;

location / {
proxy_pass http://127.0.0.1:9090/;
}
}

service.yaml


apiVersion: v1
kind: Service

metadata:
name: address-finder-service

spec:
type: NodePort
selector:
app: address-finder
ports:
- protocol: TCP
port: 80
targetPort: 8080

Dockerfile (go)


#
# STAGE 1: build
#
FROM golang:1.15-alpine3.12 as build

WORKDIR /source
COPY . .

RUN CGO_ENABLED=0 go build -ldflags "-s -w" -o bin/main main.go

#
# STAGE 2: run
#
FROM alpine:3.12 as run

COPY --from=build /source/bin/main /main

ENTRYPOINT ["/main"]

Dockerfile (nginx)


FROM nginx:1.19.4-alpine

COPY docker/dev/nginx/default.conf /etc/nginx/conf.d/default.conf

EXPOSE 8080

default.conf


server {
listen 8080 default_server;

location / {
proxy_pass http://address-finder-go:9090/;
}
}

docker-compose.yaml


version: "3.4"

services:

address-finder-go:
container_name: "address-finder-go"
build:
context: "../.."
dockerfile: "docker/dev/go/Dockerfile"

address-finder-nginx:
container_name: "address-finder-nginx"
build:
context: "../.."
dockerfile: "docker/dev/nginx/Dockerfile"
ports:
- "80:8080"
depends_on:
- "address-finder-go"

main.go


package main

import (
"log"
"net/http"
)

func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
_, _ = w.Write([]byte(r.Header.Get("X-Request-Id") + "\n"))
})

log.Println("go server is running on :9090")
log.Fatal(http.ListenAndServe(":9090", nil))
}

Makefile


The docker-run and docker-test commands are for your local environment. You can use them to test your application in local environment.


## Build and run.
.PHONY: docker-run
docker-run:
docker-compose -f docker/dev/docker-compose.yaml build --no-cache
docker-compose -f docker/dev/docker-compose.yaml up

## Remove.
.PHONY: docker-down
docker-down:
docker-compose -f docker/dev/docker-compose.yaml down
docker system prune --volumes --force

## Build, tag and push application image to registry then clean up.
.PHONY: docker-push
docker-push:
docker build -t you/address-finder-go:latest -f ./docker/dev/go/Dockerfile .
docker push you/address-finder-go:latest
docker rmi you/address-finder-go:latest
docker system prune --volumes --force

## Build and run.
.PHONY: docker-test
docker-test:
curl --request GET 'http://localhost:80' --header 'X-Request-Id: my-request-id'

## -----------------------------------------------------------------------------------

## Apply secret and deploy application to kubernetes cluster.
.PHONY: k8s-deploy
k8s-deploy:
kubectl apply -f deploy/k8s/configmap.yaml
kubectl apply -f deploy/k8s/deployment.yaml
kubectl apply -f deploy/k8s/service.yaml

## Send a dummy request to the exposed pod on kubernetes.
.PHONY: k8s-test
k8s-test:
curl --request GET $(shell minikube service address-finder-service --url) --header 'X-Request-Id: my-request-id'

Test


Docker


Let's test our application in our local environment to see if it works.


$ make docker-run
Recreating address-finder-go ... done
Recreating address-finder-nginx ... done
address-finder-go | 2020/11/15 21:50:14 go server is running on :9090
address-finder-nginx | /docker-entrypoint.sh: Configuration complete; ready for start up

$ make docker-test
curl --request GET 'http://localhost:80' --header 'X-Request-Id: my-request-id'
my-request-id

Let's now build and push our Docker image to Docker Hub. This image will be pulled when we run our deployment so it is important.


$ make push
docker build -t you/address-finder-go:latest -f ./docker/dev/go/Dockerfile .
Sending build context to Docker daemon 7.762MB
.......

Deployment


$ make k8s-deploy
kubectl apply -f deploy/k8s/configmap.yaml
configmap/address-finder-config created
kubectl apply -f deploy/k8s/deployment.yaml
deployment.apps/address-finder-deployment created
kubectl apply -f deploy/k8s/service.yaml
service/address-finder-service created

Let's check ConfigMap and Pod details.


$ kubectl describe configmap address-finder-config
Name: address-finder-config
Namespace: default
Labels:
Annotations: ...

Data
====
nginx.conf:
----
server {
listen 8080 default_server;

location / {
proxy_pass http://127.0.0.1:9090/;
}
}

$ kubectl get pods
NAME READY STATUS RESTARTS AGE
address-finder-deployment-68dd5b79b8-m6sgd 2/2 Running 0 110s

$ kubectl logs address-finder-deployment-68dd5b79b8-m6sgd go
2020/11/15 22:15:17 go server is running on :9090

$ kubectl logs address-finder-deployment-68dd5b79b8-m6sgd nginx
/docker-entrypoint.sh: Configuration complete; ready for start up

Let's not test in Kubernetes and see the logs.


$ make k8s-test
curl --request GET http://192.168.99.100:31615 --header 'X-Request-Id: my-request-id'
my-request-id

172.17.0.1 - - [15/Nov/2020:22:20:57 +0000] "GET / HTTP/1.1" 200 14 "-" "curl/7.54.0" "-"