kubernetes, devto

How to Run Dev.to on Kubernetes

Last update:

Recently I was checking Dev.to community. I must say, I really like how the application looks, clean and simple. And more important I like the community there. I also started to republish some posts because I want to show Kubernetes to the larger audience, preferably developers. But, any time I check something new I get some new ideas. This time I saw that Dev.to is open source and thought, it would be pretty interesting for people to see how to run it on Kubernetes. I will explain how I do it as an experienced Kubernetes user. So, let's start!

Check the Installation Docs

Kind of obvious, this is the first step. Look into repo docs, getting started guides, check for Dockerfiles and other goodies. If the repo has a Dockerfile available, great, you will save some time on building docker image yourself.

I also found one Dev.to image on DockerHub, but it is old and I will not use it. Dockerfile from the repo is last updated a month ago at the time of writing, which is good. I will just have to build the image and store it under my namespace on DockerHub. You can use any Docker registry, doesn't matter.

Also, I will need to run PostgreSQL. I know that there is an image available for sure, so I went right ahead and checked if there is also a helm chart available. I will use helm to package this app for easier installation on Kubernetes. I was writing about helm before so you might check it out - Package Kubernetes Applications with Helm. Seems like there is an upstream PostgreSQL chart, so less work for me 😎.

The helm is not a must have, but is the way how I like to run apps on Kubernetes.

Build Docker Image

I see that there is a getting started guide for Docker in the repo. This is good, if you can run it and test using docker compose, it will be easier to migrate it to Kubernetes later. Also, you can learn a lot about app architecture just by looking into Dockerfile, docker-compose.yaml, and other config files.

While checking the available Dockerfile I figure out that it will not work for Kubernetes. The reason for this is not because it needs to be special, but this Dockerfile is for development only which assume that you will mount the local repo data into the container. I will have to clone repo inside the Dockerfile to get the code. Here is updated Dockerfile:

FROM ruby:2.5.3

# Make nodejs and yarn as dependencies
RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
RUN curl -sL https://deb.nodesource.com/setup_8.x | bash
RUN echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list

# Install dependencies and perform clean-up
RUN apt-get update -qq && apt-get install -y \
   build-essential \
   nodejs \
   yarn \
 && apt-get -q clean \
 && rm -rf /var/lib/apt/lists

RUN git clone https://github.com/thepracticaldev/dev.to /usr/src/app
WORKDIR /usr/src/app
ENV RAILS_ENV development

# Installing Ruby dependencies
RUN gem install bundler
RUN bundle install --jobs 20 --retry 5

# Install JavaScript dependencies
ENV YARN_INTEGRITY_ENABLED "false"
RUN yarn install && yarn check --integrity

ENTRYPOINT ["bundle", "exec"]

CMD ["rails", "server", "-b", "0.0.0.0", "-p", "3000"]

NOTE: This Dockerfile is not good for production, I'm pretty much aware of its size and other things but this is not the point of this blog post.

And the diff when compared to original Dockerfile in the repo:

< RUN git clone https://github.com/thepracticaldev/dev.to /usr/src/app
> COPY Gemfile* ./
> COPY yarn.lock ./

As you can see, just a simple change. Let's build the Dev.to docker image:

$ docker build -t komljen/devto:latest .
$ docker push komljen/devto:latest

Building image will take some time. The image is a little bit on a large side, 1.76GB, but ok. Don't go into optimizations at this point.

I always skip some steps from getting started guides. Definitely not proud of that. Didn't even try to follow instructions, but that is how I learn, the hard way.

Time for Kubernetes

And here is the tricky part, but only because I need to learn more about how everything is glued together for the app to work correctly. After some research, I think I got it. So, this is how it will look:

  • Create the new helm chart for Dev.to frontend
  • Put PostgreSQL as a requirement, so when you install Dev.to chart, PG will be installed also. PG will be persistent of course
  • Set default values for both, PG and Dev.to in one values.yaml file
  • Run rails db:setup as init container on install, and on upgrade, it will become rails db:migrate. Would be nicer if I could run only migrate
  • For config, create a config map and secret. Both will be available as env variables in the container
  • For DB access you simply use config map/secret from PG chart
  • Worker jobs will be running in a separate container, but the same pod

And after some trial and error here is Dev.to helm chart. Most of the issues I had during the development of this chart was because I don't have much experience with Ruby apps and especially about running Dev.to. Also, I found it very difficult to run the app in production mode because of too many dependencies on external services. So I gave up on that after I saw 30+ API keys missing. Definitely, the app is not developed with Kubernetes or containers in mind and some changes might be required to make it a first-class citizen.

I will not go into the details of this helm chart, it would be too much for a single post, but here are some tips when developing a new chart.

When writing the new helm chart I often reuse some existing that I wrote before. Who would say? Then I just adjust what is needed for the new app. You could also run helm create <chart_name> and helm would prepare some templates, to begin with. During testing, I often use helm template command which populates templates without using Tiller (the server component of helm which is running in Kubernetes cluster). Also, you could use --dry-run --debug options to see populated files with helm install.

Installation

First, prepare your values file to match with your environment. You only need Algolia Search API access for the development environment and you can find instructions for setting up a new account here.

Now, you can install the chart:

$ cat > myvalues.yaml <<EOF
secret:
  ALGOLIASEARCH_API_KEY: <YOUR_API_KEY>
  ALGOLIASEARCH_APPLICATION_ID: <YOUR_APPLICATION_ID>
  ALGOLIASEARCH_SEARCH_ONLY_KEY: <YOUR_SEARCH_ONLY_KEY>

ingress:
  enabled: true
  annotations:
    kubernetes.io/ingress.class: nginx
    kubernetes.io/tls-acme: "true"
  hosts:
    - devto.test.akomljen.com
  tls:
   - secretName: devto
     hosts:
       - devto.test.akomljen.com
EOF

$ helm repo add akomljen-charts https://raw.githubusercontent.com/komljen/helm-charts/master/charts/      
      
$ helm install --name dev \
    -f myvalues.yaml \
    akomljen-charts/devto

$ kubectl get po
NAME                        READY     STATUS    RESTARTS   AGE
dev-devto-d7f847969-dbtkq   2/2       Running   0          3m
dev-postgresql-0            1/1       Running   0          3m      

My Kubernetes cluster has a Nginx ingress running with automatic SSL, so after installation, I was able to access the app immediately. It is slow when accessing the app for the first time because in development mode it runs webpack compile.

Also, I see that app fails to load profile pictures, but I didn't have time to dig further into it. Maybe you know the answer. Either way, you saw how I run the not so ready for containers app in Kubernetes.

Summary

The time that I spent to write this post and prepare everything was around 6h. I would say not too much considering that Dev.to is pretty new to me. And also most of the time was some polishing and proofreading this post. Hope it will help you to understand the process of running open source apps on Kubernetes better. Stay tuned for the next one!