Using Plume custom theme on Kubernetes cluster

Use Init Container and emptyDir volume

This #Plume instance "Konkoni" is running on a #Kubernetes cluster. For running Plume on Kubernetes, see @asonix@blog.asonix.dog's great post Plume Deployment.

Konkoni includes my custom theme "konkoni-light" and "konkoni-dark" and you can choose them for your blog on this instance. How did I install those themes on Kubernetes cluster? By using Init Container and emptyDir volume.

Plume custom theme

Plume recognizes directories in static/css as themes. For example, this instance has two themes: static/css/konkoni-light and static/css/konkoni-dark. Each directory must include a CSS file theme.css(static/css/konkoni-light/theme.css). See Plume document Creating a theme for details.

You can see my custom themes at GitLab repo.

Kubernetes Volume

#Kubernetes runs based on Docker(or other container engines), which means I need put my custom themes to the directory in Pods. There are some ways to do so:

  • Building Docker image including custom themes myself
  • Deploying custom themes to shared filesystem such as NFS, GlusterFS or so on
  • Doing git pull custom themes on every container startup

I chose the last way because I prefer to use official docker image which is automatically updated on every commit to master branch and running NFS is an overkill.

Init Container and emptyDir volume

Syncing Git repo to Kubernetes volume sounds good and actually there's a kind of volume for that: gitRepo. But its documentation says "The gitRepo volume type is deprecated. To provision a container with a git repo, mount an EmptyDir into an InitContainer that clones the repo using git, then mount the EmptyDir into the Pod’s container." So, I investigated and tried Init Cotainer and emptyDir volume.

Finally, I succeeded to do so. Here is a how-to:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: plume
  labels:
    app: plume
spec:
  replicas: 1
  selector:
    matchLabels:
      app: plume
  template:
    metadata:
      labels:
        app: plume
    spec:
      containers:
      - name: plume
        image: plumeorg/plume:latest
        ports:
        - containerPort: 7878
        env:
        - name: BASE_URL
          value: blogs.kitaitimakoto.net
        - name: DATABASE_URL
          valueFrom:
            secretKeyRef:
              name: plume
              key: database_url
        - name: ROCKET_SECRET_KEY
          valueFrom:
            secretKeyRef:
              name: plume
              key: rocket_secret_key
        - name: MIGRATION_DIRECTORY
          value: migrations/postgres
        - name: USE_HTTPS
          value: "1"
        - name: ROCKET_ADDRESS
          value: "0.0.0.0"
        - name: ROCKET_PORT
          value: "7878"
          #    :
          # (snip)
          #    :
        - name: DEFAULT_THEME
          value: konkoni-light
        volumeMounts:
        - name: plume-data
          mountPath: /data
        - name: plume-themes
          mountPath: /app/static/css
      initContainers:
      - name: plume-themes
        image: kitaitimakoto/rsass-ruby
        command: ["sh", "-c"]
        args:
        - |
          git clone https://gitlab.com/KitaitiMakoto/plume-themes.git && \
          cd plume-themes && \
          rake all &&
          mv dist/* /app/static/css/
        env:
        - name: RSASS
          value: /usr/bin/rsass
        volumeMounts:
        - name: plume-themes
          mountPath: /app/static/css
      volumes:
      - name: plume-data
        persistentVolumeClaim:
          claimName: plume-data
      - name: plume-themes
        emptyDir: {}

There are something to point out:

  • spec.template.spec.volumes includes emptyDir volume named plume-themes
  • spec.template.spec has initContainers section
  • A container named plume mounts plume-themes volume
  • plume container has env var DEFAULT_THEME and it indicates one of my custom themes

Kubernetes' [epmptyDir][] volume is, as it is named, a empty directory mounted at specified path. It is always reset to empty on every container startups and shared between all containers and init containers which mount it. It might sound no point but Init Container adds value to it.

Init Container is a container which runs every Pod startups and is removed before containers(spec.template.spec.containers) start. This is suitable for you to initialize something like syncing git repo to empty dir! Actually, my init container above runs shell to pull git repo, build themes(rake all command does it) and copy them to mounted emptyDir(/app/static/css). By this process, /app/static/css/konkoni-{light,dark} directories exist at beginning of Plume container.

In manifest above, I'm running rake command to build themes but it's not essential. If your repo has complete CSS file as Plume theme, what you need is to pull the repo. And, hosting built themes at, for instance, GitLab Pages as ZIP archive(say, plume-themes.zip) and wgetting it in init container is also good.

DEFAULT_THEME env var is optional. It specifies the default theme name of Plume instance.

In conclusion, you can prepare static files needed for Kubernets Pods by Initi Container and emptyDir volume, and you can use it for Plume custom theme installing.