Erlang cluster on Kubernetes: Initial Deployment

21 Dec 2022 19:45 erlang kubernetes

For simplicity’s sake, I created a new application with rebar3 new app name=erlclu. I very soon regretted this decision, because I actually needed a release, so I ran rebar3 new release name=whoops and manually merged the relevant pieces together.

Creating a new container image

Dockerfile

That needs a Dockerfile; I’ll start with a two-stage alpine-based image, as follows:

FROM docker.io/erlang:25.1.2.0-alpine AS build

COPY / /build
WORKDIR /build

RUN rebar3 as prod release

#
FROM docker.io/alpine

RUN apk add --no-cache openssl && \
    apk add --no-cache ncurses-libs && \
    apk add --no-cache libstdc++

COPY --from=build /build/_build/prod/rel/erlclu /erlclu

EXPOSE 8080

ENTRYPOINT ["/erlclu/bin/erlclu", "foreground"]

It uses an erlang:alpine image as the base, and runs rebar3 release to build the release. That’s then copied into a (smaller) alpine image to be deployed. Erlang requires a few dependencies to be installed (the apk add commands). ENTRYPOINT runs the release in the foreground.

Makefile

I’m doing this on my Windows laptop, using WSL2 and I’ve got Docker Desktop installed. Because I can’t figure out how to get Docker Desktop to trust the private CA on my cluster’s docker registry, I’m using podman instead.

That ends up looking like this:

RELEASE_VSN ?= 0.1.0
DOCKER_REGISTRY ?= docker.k3s.differentpla.net

all: build-image push-image

release:
	rebar3 as prod release

build-image:
	podman build -f Dockerfile -t erlclu

push-image:
	podman push erlclu $(DOCKER_REGISTRY)/erlclu:$(RELEASE_VERSION)

Nothing particularly surprising here. There’s an extra release target so that I can run it locally.

deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: erlclu
  namespace: erlclu
spec:
  replicas: 1
  selector:
    matchLabels:
      app: erlclu
  template:
    metadata:
      labels:
        app: erlclu
    spec:
      containers:
      - name: erlclu
        image: docker.k3s.differentpla.net/erlclu:0.1.0
        imagePullPolicy: Always
        resources:
          limits:
            memory: "128Mi"
            cpu: "500m"
        ports:
        - containerPort: 8080

The only things of note here are:

  • I’ve set imagePullPolicy: Always, because I’ve not particularly thought about versioning the container image, so it’s better for nodes to pull a fresh image every time.
  • I’ve set some basic resource limits.

Comments

If you look at the initial-deployment tag, you’ll see that I just stuck the k8s directory inside the erlclu directory.

This is probably a mistake: the K8s application will eventually have a bunch of other things in it (init containers, cronjobs, etc.), and it would make more sense to have it at the top-level.