Deploy phoenix (elixir) on a Kubernetes cluster using helm

Phoenix applications are often much easier to deploy than apps that are not based on Elixir. The reason is that there are not more requirements than having a server where to run the Phoenix application. On other application, like rails, you need Nginx (for serving javascript, images, etc), something to cache slow request (For example,  puma on rails projects), etc. The reason for this is that elixir is a functional programming language, that can execute a nearly infinite number of processes with a relatively small effort (in comparing to many of the object-oriented languages).

So, why should we use Kubernetes if phoenix applications are so easy to deploy? One of the reason is that Kubernetes use docker and docker allow you to have a reproducible and idempotent deployment. Idempotent (in this context) mean, that you can deploy it two times and you will get (most of the times) the same result. Reproducible means, that you can deploy is anywhere (e.g. on your local computer) without any problems. Another reason is, that not all the world is using elixir and your application will depend often on other services/applications. This application needs to be deployed somewhere. Without Kubernetes or deep knowledge of the framework that is using all of these services, it is often very difficult to deploy them. Kubernetes make the deployment of this much more easier.

So, let us begin…

Create a new phenix app with a Dockerfile

Let us create a new phoenix application:

mix phx.new phoenix_kubernetes

To test it, just run the following commands.

cd phoenix_kubernetes
# install inotify-tools (if necessary)
# you will see a postgress error, just ignore it
mix phx.server
--> open in the browser localhost:4000

Now let us add the Dockerfile.

# file name: Dockerfile

FROM elixir:1.8-alpine

ENV PORT 4000

WORKDIR /app

RUN apk add --update \
    postgresql-client \
    nodejs nodejs-npm \
    inotify-tools yarn git \
    bash

RUN mix do local.hex --force, local.rebar --force
COPY mix.exs mix.lock ./
RUN MIX_ENV=prod mix do deps.get, deps.compile

COPY assets ./assets
RUN cd assets/ && \
    yarn install && \
    npm install && \
    npm run deploy && \
    cd -

COPY . ./
RUN MIX_ENV=prod mix do compile, phx.digest

CMD mix phx.server

Now, you can build and run the Dockerfile by executing the following commands:

docker build -t phoenix_kubernetes .
docker run -p 4000:4000 phoenix_kubernetes

Chose a docker registry

There exist different docker registries. Some of them are free to use:

E.g. for Docker Hub, do the following.

browser: register on https://hub.docker.com/
browser: create a repository. You can create a private one. But ,for this
         tutorial, I recomend to use a public one.

docker login <username>
docker build -t <username>/<repository>
docker push <username>/<repository>

# Make sure that all works
docker pull <username>/<repository>
docker run -p 4000:4000 <username>/<repository>
--> open in the browser localhost:4000

Add helm to the Phoenix app

You need to have installed helm on your local machine. On Ubuntu, you can just install it by typing snap install helm and on mac brew install helm.

helm create chart

Change some values on  chart/values.yml.

image:
  repository: <username>/<repository>
  tag: latest
  pullPolicy: IfNotPresent

...

Change the port and some environment variables on chart/templates/deployment.ynl

...
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
  - name: http
    containerPort: 4000
env:
  - name: MIX_ENV
    value: prod 
...

Helm has a linter, that allows us to check, that the chart has no syntax errors.

helm lint chart

Deploy the application on a Kubernetes cluster

To deploy the application on a Kubernetes, we need a Kubernetes cluster. For test porpuses, we can create a single node cluster, locally, using Minikube or Microk8s

Let us check, first, that the cluster works correctly

kubectl get nodes

Init helm (tiller) on the cluster.

helm init # wait a lite bit after running this (20 seconds)

Install the chart:

helm install --name phoenix-kubernetes chart/

The command should return some notes, specifying how to access it with a browser.

export POD_NAME=$(kubectl get pods --namespace default \
  -l "app.kubernetes.io/name=chart,app.kubernetes.io/instance=bunking-mastiff" \
  -o jsonpath="{.items[0].metadata.name}")
kubectl port-forward $POD_NAME 8080:80

--> open in the browser localhost:8080

Todos

There are still things to do. You need to configure Nginx ingress (url management) and Lets Encrypt (https). Both can only be done, easily, on a real cluster (e.g. GKE). I will create another tutorial, about how to deploy an application on GKE. 

NOTE: you can install Nginx-Ingress and Cert-Manager (Lets Encrypt) via helm packages. Then you have only to edit the ingress section under chart/ingress.yml