At VIBBIO we have adopted a devops mindset to everything we do. An essential part of such a mindset is to ๐Ÿšข the ๐Ÿ’ฉ to production. In order to do so safely and with even more control we have added a new tool to our toolbox: feature toggles.

The lovely people at Finn.no have developed an Open Source server for administering and using feature toggles. The project is called Unleash and is their company, but it is also gaining traction in a lot of other Norwegian companies as well. One thing we especially like about Unleash is that it’s a relatively simple solution which is fast to setup and get going. You do not need Zookeeper clusters or Big Data storage rigs to get this up and running ๐Ÿ‘๐Ÿพ.

VIBBIO runs it’s services on Google Cloud and it’s Kubernetes Engine. Naturally we wanted to run unleash on the same infrastructure. The project has a docker sample which shows how to spin up a server using docker compose.

YAML YAML YAML YAML YAML YAML YAML

โš ๏ธ Before you proceed, if you have allergies towards yaml or experience stress when exposed to excessive amounts of yaml you are best advised to stop reading right now โ›”

In order to get Unleash ready we need two things:

  • Postgres database
  • Unleash server

Luckily the nice folks who wrote Unleash has a docker sample which provides a good starting point for getting it setup on Kubernetes Engine.

๐Ÿ™‰ Volumes, ๐Ÿ™ˆ Claims & ๐Ÿ™Š Configuration

You do want your data to be persistent and not disappear when the app gets recreated. In order to get this working you must setup a persistent volume and a persistent volume claim (more information on this are in the Google Cloude doucumentation)

kind: PersistentVolume
apiVersion: v1
metadata:
  name: unleash-db-volume
  labels:
    type: local
    app: unleash
spec:
  storageClassName: manual
  capacity:
    storage: 5Gi
  accessModes:
    - ReadWriteMany
  hostPath:
    path: "/var/lib/postgresql/data"
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: unleash-db-claim
  labels:
    app: unleash
spec:
  storageClassName: manual
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 5Gi

Database configuration can be done by using a config map. You probably should use Kubernetes secrets for the credentials, but for this sample we just add it here.

apiVersion: v1
kind: ConfigMap
metadata:
  name: unleash-db-config
  labels:
    app: unleash-db
data:
  POSTGRES_DB: db
  POSTGRES_USER: postgres
  POSTGRES_PASSWORD: somepassword

Data exposure ๐Ÿ–ผ

In order for the application to use your database, you must set it up as a services

apiVersion: v1
kind: Service
metadata:
  name: unleash-db
  labels:
    app: unleash-db
spec:
  type: NodePort
  ports:
   - port: 5432
  selector:
   app: unleash-db

Finally, the deployment descriptor

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: unleash
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: unleash
    spec:
      volumes:
        - name: unleash-storage
          persistentVolumeClaim:
            claimName: unleash-claim
      containers:
        - name: unleash
          image: postgres:10.4
          imagePullPolicy: "IfNotPresent"
          ports:
            - containerPort: 5432
          envFrom:
            - configMapRef:
                name: unleash-config
          volumeMounts:
            - mountPath: /var/lib/postgresql/data
              name: unleash-storage

Once you’ve added all this config, you should query the cluster to see if it worked. You should expect something along the lines of this. However the numbers and IPs will be different for each system.

$ kubectl get \
pods,services,persistentvolumes,persistentvolumeclaims,deployments\
 --selector=app=unleash-db

NAME                             READY     STATUS    RESTARTS   AGE
po/unleash-db-56d4cdf6f6-h7wk8   1/1       Running   0          24d

NAM              TYPE       CLUSTER-IP    EXTERNAL-IP   PORT(S)          AGE
svc/unleash-db   NodePort   10.7.255.41   <none>        5432:31643/TCP   24d

NAME                CAPACITY ACCESS MODES RECLAIM POLICY  STATUS CLAIM
pv/unleash-db-volume   5Gi     RWX         Retain Bound   default/unleash-db-claim

NAME                   STATUS    VOLUME            CAPACITY   ACCESS MODES   STORAGECLASS   AGE
pvc/unleash-db-claim   Bound     unleash-db-volume   5Gi        RWX            manual       24d

NAME                DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
deploy/unleash-db   1         1         1            1           24d

๐ŸŽ‰ ๐ŸŽ† You made it! It just took about 2 meters of yaml-configuration to get the storage setup. Now, for the server!

๐Ÿ˜… Now, the server

First off, lets describe how to deploy the unleash-server Docker image.

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: unleash-server
  labels:
    app: unleash-server
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: unleash-server
    spec:
      containers:
      - name: unleash-server
        image: us.gcr.io/vibbioexpress/unleash-server:latest
        imagePullPolicy: Always
        ports:
        - containerPort: 4242
        env:
        - name: NODE_ENV
          value: 'production'
        - name: DATABASE_URL
          value: postgres://postgres:unleash@unleash-db.default/postgres
        - name: POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace

In order to expose it, we need to create a service.

apiVersion: v1
kind: Service
metadata:
  name: unleash-server-service
  annotations:
    service.alpha.kubernetes.io/app-protocols: '{"http-port":"HTTP"}'
  labels:
    app: unleash-server
spec:
  type: NodePort
  selector:
    app: unleash-server
  ports:
  - name: http-port
    port: 80
    targetPort: 4242

So far so good, but it would be beneficial for humans to be able to use the administration UI too. We will create an ingress for this and this one relies on a static IP in Google Cloud (see the documentation on how to do this)

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: unleash-server-ingress
  labels:
    app: unleash-server
  annotations:
    kubernetes.io/ingress.global-static-ip-name: unleash-server-ip
spec:
  tls:
    - secretName: ssl-secret
  backend:
    serviceName: unleash-server-service
    servicePort: 80

Once all this is fired up, you should be able to visit your configured IP and see the administration UI ๐ŸŽ‰ ๐ŸŽ‰

Summarizing

In retrospect getting Unleash on Kubernetes Engine was quite simple. It just took some configuring and that was it.

NOTE! This write up is not recommended settings for a production environment. You should use secrets for credentials. In addition you should secure Unleash according to the documentation.