Containers Homework

At work, we have several “working groups” centred around specific technologies we use. The purpose of these is to have a way for us lowly engineers to have input into our “tech menu” that we work with. There are several software-engineering-focused groups (JVM, Nodejs, etc.) but so far only one that relates to operations in a broad sense: the container working group.

For the past 4 or 5 meetings of this group, it has felt very much like just an update on what we as a business are doing with containers, rather than anything interesting, so the chair of the group took it upon himself to set us some homework to get us all involved in working with containers. The homework task was as follows:

Apologies for the short notice but I need to cancel today’s working group. In lieu of actually having the meeting I’d like to set you all a small piece of homework…! I’d like you to use Kubernetes to display a local web page which displays your name and a picture of you. Extra credit if you pull / create the name as a secret! And we’ll discuss and demo your attempts in the next session.

Now it would be easy enough to just write an HTML page, store it as a secret in Kubernetes, and then mount as /var/www/html/index.html in an off-the-shelf nginx container. Where is the fun in that? I decided that I would create my own application that would display all the required information (plus more) pulling from environment variables, and put that in a container.

The application

I initially defaulted to writing a quick web page in Express, quickly changing my mind and opting for writing it in Go instead. Full disclosure before you read this. I am not a software engineer of any kind, nor am I trained in CS in any way, shape, or form. I’m most comfortable in Bash and not afraid to admit it.

package main

import (

func main() {
		http.HandleFunc("/", Serve)
		http.ListenAndServe(":1337", nil)

func Serve(w http.ResponseWriter, r *http.Request) {

	tmpl, err := template.ParseFiles("index.gohtml")
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)

	type Data struct {
		Name string
		ImgURL	string
		BannerCol	string
		BannerTextCol	string

	data := Data{Name: os.Getenv("NAME"), ImgURL: os.Getenv("IMGURL"), BannerCol: os.Getenv("BANNERCOLOUR"), BannerTextCol: os.Getenv("BANNERTEXTCOLOUR")}

	err = tmpl.Execute(w, data)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)

The two interesting bits of this code (at least as far as I’m concerned) is the use of html/template and also os.Getenv. The template file is a very simple HTML page designed to display name and picture in a box in the middle of the screen:

<!DOCTYPE html>
<html lang="en">
<style type="text/css">
	html,body{height:100%;width:100%;padding:0;margin:0;font-family:'Helvetica Neue',sans-serif;color:#{{.BannerTextCol}};}
#contents{width:50%;border-radius:20px;border:solid 1px #ddd;box-shadow: 0px 0px 15px #bbb;text-align:center;}
h1{background-color:#{{.BannerCol}};border-top-left-radius:20px;border-top-right-radius:20px;padding:10px 0px;margin:0;}
<title>{{.Name}}'s Container WG Homework</title>
<div id="container">
<div id="contents">
<h1>My name is {{.Name}}</h1>
<img src="{{.ImgURL}}" alt="{{.Name}}'s image">

Putting it in a container

Once we have a working application, we need to get it running in a immutable container, which was accomplished with the following Dockerfile:

FROM golang:latest
LABEL maintainer="Oliver Leaver-Smith <>"
COPY ./app .
RUN go build -o main .
CMD ["./main"]

Onwards to the Kube cluster

In the application, we make reference to four environment variables for name, image URL, banner colour, and banner text colour. These need setting in our Kubernetes manifest along with a whole host of other configurations. The finished YAML looks like this:

apiVersion: apps/v1
kind: Deployment
	name: ols-ccwg-hwk
		app: ols-ccwg-hwk
	replicas: 3
			app: ols-ccwg-hwk-app
				app: ols-ccwg-hwk-app
			- name: ols-ccwg-hwk
				image: docker.<super secret internal artifact store>.io/ols/ols-ccwg-hwk:latest
				imagePullPolicy: Always
					- containerPort: 1337
				- secretRef:
						name: ols-ccwg-hwk-secrets
apiVersion: v1
kind: Service
	name: ols-ccwg-hwk-service
		app: ols-ccwg-hwk-app
	- protocol: TCP
		port: 1337
		targetPort: 1337
	type: LoadBalancer
apiVersion: v1
kind: Secret
	name: ols-ccwg-hwk-secrets
	NAME: b2xz
	IMGURL: aHR0cHM6Ly9vbHMud3RmL29scy5wbmc=

The result

Et voila! The page looks like this

Finished product


Our next meeting is next Monday (2019-08-05), so we will all demo our attempts then. I’m aware I’ve skipped through quite a lot of ground, and assumed quite a lot of knowledge.

Do you have a comment to make on this post? Start a discussion in my public inbox by emailing ~ols/ You can see the inbox here.