這篇文章主要是透過 minikube 在 local 端練習部署 rails 到 kubernetes 。由於主要是在練習 kubernetes,就不在 Rails 上著墨太多,就簡單的先 rails new 一個名為 rails-kibe-demo 的 project。

$ rails new rails-kube-demo

其他相關的環境設定

Rails: 5.1.1
ruby: 2.4.1p111
Docker: 17.05.0-ce
VirtualBox: 5.1.22 r115126
minikube: v0.19.0

Dockerize Rails Application

在開始之前,請先確保 docker 以及 docker-compose 已經安裝。首先在 rails-kube-demo 目錄底下建立一個 Dockerfile 檔案,這個 Dockerfile 是將 rails 會用到的環境以及需要的相依套件先定義好。

FROM ruby:2.4.1

ENV APP_HOME /home/app
ENV BUNDLE_PATH /bundle

WORKDIR $APP_HOME

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

RUN gem install bundler

ADD Gemfile $APP_HOME/Gemfile
ADD Gemfile.lock $APP_HOME/Gemfile.lock

RUN bundle install
ADD . $APP_HOME

EXPOSE 3000

接著建立 docker-compose.yml 檔案

version: '3'
services:
  app:
    build: .
    command: bundle exec rails s -p 3000 -b '0.0.0.0'
    volumes:
      - .:/home/app
    ports:
      - "3000:3000"
    depends_on:
      - db
  db:
    image: postgres

這邊稍微解釋一下,Dockerfile 跟 docker-compose 的關係,Dockerfile 就像是先制定好你的機器環境,需要先裝好什麼套件,而 docker-compose 則是定義多個 service,讓彼此在各自獨立的環境內一起執行。docker-compose 中的services 可以透過 build 的方式指定 Dockerfile 來產生 image,也可以透過 image 的方式來 pull docker hub 中別人已經包好的 image。

在這邊app service 的 image 是基於在 project 根目錄下建立的 Dockerfile,而 db,則是直接去 pull docker hub 中的 postgres 來產生 image。

接著輸入以下指令後,打開你的瀏覽器輸入 localhost:3000 應該就能看到成功的 rails 5 預設頁面

$ docker-compose up

Setting up a Kubernetes Cluster

上一步已經將必要的 docker images 產生出來,可以透過以下指令檢查。

$ docker-compose images

     Container           Repository        Tag       Image Id      Size
------------------------------------------------------------------------
railskubedemo_app_1   railskubedemo_app   latest   d809f94e0b2e   777 MB
railskubedemo_db_1    postgres            latest   ca3a55649cfc   257 MB

在上一篇文章中,是直接透過指令建立 node.js application 的 deployment 跟 service。但如果需要較為複雜的設定kubernetes 也能夠kubectl create -f <directory> 的方式來另外透過 .yml、.yaml 或 .json 檔案來實現。

在這邊為了清楚明瞭,先把等等會新增的檔案寫在前頭,並且都將這 4 個檔案放置 kube 目錄下統一管理。

  • kube/app-deployment.yml
  • kube/app-service.yml
  • kube/db-deployment.yml
  • kube/db-service.yml

app-deployment.yml

這個 deployment 設定會掛上一個app label,使用的 image 為 railskubedemo_app,這邊最有趣的就是 replicas 這項設定,這項設定確保了隨時都會有正確數量的 pod 在執行。在部署 deployment 成功後,立即會產生 pods。所以當程式有變動時,只需要再包新的一份 images,並且重新部署 deployment 設定即可。

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: app
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: app
    spec:
      containers:
      - name: app
        image: railskubedemo_app:latest
        imagePullPolicy: IfNotPresent
        ports:
          - containerPort: 3000
        command: ["bundle", "exec", "rails", "s", "-b", "0.0.0.0", "-p", "3000"]

執行以下指令後,可以除了可以看到,

$ kubectl apply -f kube/app-deployment.yml

在執行完後,發現 pod 的 status 出現了ErrImagePull,可以先跑下面指令,看看在 minikube 中是否有正確的 images。

$ minikube ssh
$ docker images

若是沒有剛剛產生的兩個 images,離開 minikube VM,執行以下指令,讓 minikube 這個 VM 可以共用 Host 相同的 docker daemon。

$ eval $(minikube docker-env)

app-service.yml

目的是規定將外部流量導向內部的哪個 Pod,,上面已經有將 pod 加上一個 app 的 label,所以這邊的 service 就能夠透過檔案中的 selector 指定 app 這個 label,當收到使用者請求時,就會將這些請求送到正確的 pods 中。

apiVersion: v1
kind: Service
metadata:
  name: rails
  labels:
    name: app-service
spec:
  selector:
    app: app
  type: NodePort
  ports:
  - port: 8080
    targetPort: 3000
    protocol: TCP
    name: http

執行以下指令

$ kubectl apply -f kube/app-deployment.yml

db-deployment.yml

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: postgres
spec:
  replicas: 1
  template:
    metadata:
      labels:
        name: postgres
    spec:
      containers:
      - name: postgres
        image: postgres
        imagePullPolicy: IfNotPresent

db-service.yml

apiVersion: v1
kind: Service
metadata:
  name: postgres
  labels:
    name: postgres
spec:
  ports:
    - port: 5432
  selector:
    name: postgres

再次確認,deployments 跟 services 都已經設定好,並且 pods 是有正常在運行

$ kubectl get deployments
---
NAME       DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
app        1         1         1            1           5h
postgres   1         1         1            1           2h

$ kubectl get service
---
kubernetes   10.0.0.1     <none>        443/TCP          10h
postgres     10.0.0.222   <none>        5432/TCP         2m
rails        10.0.0.254   <nodes>       8080:30576/TCP   4h

$ kubectl get pods
---
NAME                        READY     STATUS    RESTARTS   AGE
app-850775042-yvqzp         1/1       Running   0          5h
postgres-1675796181-hv3pe   1/1       Running   0          2h

以上 deployment 跟 service 都設定好後,透過以下指令,瀏覽器就會新開一個分頁,應該就能夠看到 Rails 5 的歡迎頁面。

$ minikube service rails

以上並沒有設定 rails 的 database.yml,所以下一篇文章將會提及 kubernetes 中有個 type 為 secrete 的 object,如何使用該 object 來保護一些重要的設定,像是一些 password、token 或者是 ssh key 等。