Running Rails apps on GKE [part I]


Introduction

This part covers setting up your Rails app on GKE + your database with Google Cloud SQL.

In the next parts we’ll cover more topics like:

  • DNS
  • HTTPS
  • Redis
  • Sidekiq and background jobs
  • Google Cloud Storage

Google Kubernetes Engine

https://cloud.google.com/kubernetes-engine

Google Kubernetes Engine (GKE) provides a managed environment for deploying, managing, and scaling your containerized applications using Google infrastructure.

The GKE environment consists of multiple machines (specifically, Compute Engine instances) grouped together to form a cluster.

GKE main features list:

  • Run Kubernetes on a platform built by the largest engineering contributor to K8s

  • Start quickly with single-click clusters and scale up to 15000 nodes

  • Leverage a high-availability control plane including multi-zonal and regional clusters

  • Eliminate operational overhead with industry-first four-way auto scaling

  • Secure by default, including vulnerability scanning of container images and data encryption

Setup Google Cloud SQL

What is Google Cloud SQL?

https://cloud.google.com/sql

Fully managed relational database service for MySQL, PostgreSQL, and SQL Server.

  • Reduce maintenance cost with fully managed relational databases in the cloud

  • Ensure business continuity with reliable and secure services backed by 24/7 SRE team

  • Automate database provisioning, storage capacity management, and other time-consuming tasks

  • Database observability made easy for developers with Cloud SQL Insights

  • Easy integration with existing apps and Google Cloud services like GKE and BigQuery

Go to the Cloud SQL section in your GCP console.

Create database - mysql/postgresql:

~ ❯❯❯ gcloud beta sql instances create mydbinstance \
    --database-version POSTGRES_9_6 \
    --cpu=1 --memory=3840MiB --region=northamerica-northeast1-a

Create user:

~ ❯❯❯ gcloud sql users set-password postgres \
    --instance=mydbinstance \
    --password=my-password

!IMPORTANT!

You’ll need to run special proxy for database connections, check deployment file.

Set PROJECT_ID

Let’s setup variable with your GCP project ID.

We’ll use it later in our configuration setup.

~ ❯❯❯ PROJECT_ID=project_id
~ ❯❯❯ gcloud auth login your_account_name
~ ❯❯❯ gcloud config set project $PROJECT_ID

Cluster

What is Kubernetes cluster?

https://www.redhat.com/en/topics/containers/what-is-a-kubernetes-cluster

We can create a cluster via the command line with the following:

~ ❯❯❯ gcloud container clusters create rails \
    --enable-cloud-logging \
    --enable-cloud-monitoring \
    --machine-type n1-standard-1 \
    --num-nodes 1

When the cluster creation is complete, we’ll now need to give the cluster credentials to kubectl to begin deploying containers.

~ ❯❯❯ gcloud container clusters get-credentials rails

Using service account and set access

Let’s set access rights and create key file as described in manual:

https://cloud.google.com/sql/docs/mysql/connect-kubernetes-engine

Working with secrets

Let’s create secrets (you can use this template to create all secrets described in deployment.yml)

~ ❯❯❯ export DBPASSWORD="YOUR_SUPER_PASSWORD"
~ ❯❯❯ kubectl create secret generic cloudsql-db-credentials --from-literal=username=default --from-literal=password=${DBPASSWORD}

App deployment config

Let’s take a look to our Dockerfile (depends on your app):

FROM ruby:2.6.2

RUN apt-get update && apt-get install -y build-essential git
RUN apt-get install -y libpq-dev
RUN apt-get install -y nodejs

RUN mkdir -p /app
WORKDIR /app

ENV LANG C.UTF-8

COPY Gemfile Gemfile.lock ./
RUN gem install bundler && bundle install --jobs 20 --retry 5
COPY . $APP_ROOT

ENV RAILS_LOG_TO_STDOUT=true
ENV RAILS_ENV production

RUN chmod +x /app/entrypoint.sh
ENTRYPOINT ["/app/entrypoint.sh"]

Entrypoint.sh file example.

#!/bin/bash
cd /app
RAILS_ENV=production bundle exec rake db:create
RAILS_ENV=production bundle exec rake db:migrate
RAILS_ENV=production bundle exec rake db:seed
bundle exec rails s -e production

Deployments

Let’s create our first deployment.

Read more: https://kubernetes.io/docs/concepts/workloads/controllers/deployment/

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: myapp
  name: myapp
spec:
  replicas: 2
  minReadySeconds: 5
  selector:
    matchLabels:
      app: myapp
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
    type: RollingUpdate
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - env:
        - name: SECRET_KEY_BASE
          valueFrom:
            secretKeyRef:
              key: secret-key-base
              name: rails
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              key: password
              name: cloudsql-db-credentials
        - name: DB_USER
          valueFrom:
            secretKeyRef:
              key: username
              name: cloudsql-db-credentials
        - name: RAILS_ENV
          value: production
        - name: RACK_ENV
          value: production
        image: gcr.io/YOUR_APP/myapp:latest
        imagePullPolicy: Always
        name: rails
        ports:
        - containerPort: 3000
          protocol: TCP
        resources: {}
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
      - command:
        - /cloud_sql_proxy
        - -instances=YOUR_APP_DB:northamerica-northeast1:myappdb=tcp:5432
        - -credential_file=/secrets/cloudsql/credentials.json
        image: gcr.io/cloudsql-docker/gce-proxy:1.16
        imagePullPolicy: IfNotPresent
        name: cloudsql-proxy
        resources: {}
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
        volumeMounts:
        - mountPath: /secrets/cloudsql
          name: cloudsql-instance-credentials
          readOnly: true
      dnsPolicy: ClusterFirst
      restartPolicy: Always
      schedulerName: default-scheduler
      securityContext: {}
      terminationGracePeriodSeconds: 30
      volumes:
      - name: cloudsql-instance-credentials
        secret:
          defaultMode: 420
          secretName: cloudsql-instance-credentials

Service

Services description: https://kubernetes.io/docs/concepts/services-networking/service/

apiVersion: v1
kind: Service
metadata:
  name: rails
  labels:
    app: rails
spec:
  type: LoadBalancer
  ports:
    - port: 80
      targetPort: 3000
      protocol: TCP
  selector:
    app: myapp

Let’s deploy!

Our last step - let’s run our deployments for app and service:

Let’s commit and push changes, and run make script:

~ ❯❯❯ make build push

Let’s process our main deployment:

~ ❯❯❯ kubectl apply -f k8s/deployment.yml

Let’s deploy our service and provide public acccess to the service:

~ ❯❯❯ kubectl apply -f k8s/service.yml

That’s it! Run/deploy/test and stay tuned.