Posted at

KubernetesでRailsを動かしてみよう!

More than 1 year has passed since last update.

今回は GoogleCloudPlatform の GKE を利用し、Rails を動かしてみました。


Kubernetes Engine クラスタの作成

Kubernetes Engine クラスタは、マスターと、ノードと呼ばれる複数のワーカーマシンで構成されます。

アプリケーションをクラスタにデプロイすると、アプリケーションがノードに配置され、それらのノードで実行されます。

GKE ではクラスタを作成するためには次のコマンドを実行します。

$ gcloud container clusters create rails-cluster

※クラスタの作成は完了するのに数分かかります。


認証情報の登録

認証情報は base64 でハッシュ化した値を登録する必要がありますので、まずは登録したい文字列を base64 でハッシュ化します。

$ echo -n "database_root_password" | base64

ZGF0YWJhc2Vfcm9vdF9wYXNzd29yZA==

$ echo -n "secret_key_base" | base64
c2VjcmV0X2tleV9iYXNl

次にマニフェストファイルを作成し、先ほどの値をそのまま記述します。


secret.yml

apiVersion: v1

kind: Secret
metadata:
name: rails
type: Opaque
data:
database_root_password: ZGF0YWJhc2Vfcm9vdF9wYXNzd29yZA==
secret_key_base: c2VjcmV0X2tleV9iYXNl

次のコマンドを実行することにより、このマニフェストファイルをデプロイすることができます。

$ kubectl create -f secret.yml


永続ストレージの作成

Kubernetes で作成される Docker コンテナではデータの永続化は行われないので、MySQL 用に永続ストレージを作成します。

$ gcloud compute disks create --size 200GB mysql-data

ここでは 200GB に指定して永続ストレージを作成しています。


MySQL をデプロイする

mysql.yml というファイル名でマニフェストファイルを作成しデプロイします。

secretKeyRef で先ほど登録した認証情報を、volumes の部分で永続ストレージを参照するようにします。


mysql.yml

apiVersion: extensions/v1beta1

kind: Deployment
metadata:
name: mysql
labels:
app: mysql
spec:
replicas: 1
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- image: mysql:5.6
name: mysql
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: rails
key: database_root_password
ports:
- containerPort: 3306
name: mysql
volumeMounts:
- name: mysql-persistent-storage
mountPath: /var/lib/mysql
volumes:
- name: mysql-persistent-storage
gcePersistentDisk:
pdName: mysql-data
fsType: ext4

secret.yml と同じように、このマニフェストファイルをデプロイします。

$ kubectl create -f mysql.yml

ここでは Docker コンテナに永続ストレージをアタッチするので、ポッドのステータスが Running になるまでは少し時間がかかります。

kubectl get pod コマンドでポッドのステータスを確認することができます。

$ kubectl get pod

NAME READY STATUS RESTARTS AGE
mysql-xxxxxxxxxx-xxxxx 1/1 Running 0 1m

次に、mysql-service.yml で MySQL コンテナの 3306 ポートを公開する Service を作成し、あとで作成する Rails コンテナからアクセスできるようにします。


mysql-service.yml

apiVersion: v1

kind: Service
metadata:
name: mysql
labels:
app: mysql
spec:
type: ClusterIP
ports:
- port: 3306
selector:
app: mysql

デプロイします。

$ kubectl create -f mysql-service.yml

作成された Service の一覧は kubectl get service コマンドで確認できます。

$ kubectl get service

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.xx.xx.xx <none> 443/TCP 29m
mysql ClusterIP 10.xx.xx.xx <none> 3306/TCP 7m


Rails アプリケーションをデプロイする

まずは MySQL と同じようにマニフェストファイルを作成し、その後 Service を作成し、外部からアクセスできるようにします。


app.yml

apiVersion: extensions/v1beta1

kind: Deployment
metadata:
name: rails
labels:
app: rails
spec:
replicas: 1
selector:
matchLabels:
app: rails
template:
metadata:
labels:
app: rails
spec:
containers:
- image: busybox
name: rails
env:
- name: DATABASE_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: rails
key: database_root_password
- name: SECRET_KEY_BASE
valueFrom:
secretKeyRef:
name: rails
key: secret_key_base
ports:
- containerPort: 3000
name: rails

後ほど、rails アプリケーションの image ファイルをデプロイするので、ここでは適当な image ファイルを指定しています。

デプロイします。

$ kubectl create -f app.yml

外部から Rails アプリケーションに接続できるようにするため、Service を作成します。

MySQL の場合と異なり、ここでは外部に公開するので type: LoadBalancer となっているのに注意してください。


app-service.yml

apiVersion: v1

kind: Service
metadata:
name: rails
labels:
app: rails
spec:
type: LoadBalancer
ports:
- port: 3000
selector:
app: rails

デプロイします。

$ kubectl create -f app-service.yml

kubectl get service で rails の EXTERNAL-IP<pending> から IP が表示されるまで待ちます。

これで http://<EXTERNAL-IP>:3000 でアクセスできるようになりましたが、まだアプリケーションをデプロイしていないので、エラー画面が表示されると思います。


アプリケーションのデプロイ

今回はさくっとローカルに Rails アプリケーションを new したものを使いたいと思います。

$ rails new sampleapp -d mysql

...
* bin/rake: spring inserted
* bin/rails: spring inserted

$ cd sampleapp

マイグレーションと画面表示の確認用に適当な scaffold を実行しておきます。

$ bin/rails generate scaffold user name:string age:integer

database のパスワードは先ほど app.yml で指定した環境変数を参照するように変更します。


config/database.yml

production:

<<: *default
host: mysql
database: sampleapp_production
username: root
password: <%= ENV['DATABASE_ROOT_PASSWORD'] %>

Kubernetes では Docker イメージからポッドを作成するので、new した Rails アプリケーションを Docker Registry に登録する必要があります。

GCP には Google Container Registry というサービスがあるので、ここにイメージを push したいと思います。

まずは Dockerfile を作成し、それを build して、イメージファイルを作成したいと思います


Dockerfile

FROM ruby:2.3

ENV APP_ROOT="/app"

RUN \
mkdir -p $APP_ROOT

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

RUN \
bundle config --global build.nokogiri --use-system-libraries

WORKDIR $APP_ROOT

ADD . $APP_ROOT

ENV RAILS_ENV production
WORKDIR $APP_ROOT

ENTRYPOINT \
bundle install && \
bin/rails db:create && \
bin/rails db:migrate && \
bin/rake assets:precompile && \
bin/rails server -p 3000 -b 0.0.0.0


build して、Google Container Registry にイメージを push します。

$ export PROJECT_ID="$(gcloud config get-value project -q)"

$ docker build -t gcr.io/${PROJECT_ID}/sampleapp:v1 .
$ gcloud docker -- push gcr.io/${PROJECT_ID}/sampleapp:v1

Google Container Registry にイメージを push できたので、rails のポッドをこのイメージファイルを適応したものに変更します。

$ kubectl set image deployment/rails rails=gcr.io/${PROJECT_ID}/sampleapp:v1

実行したあとに pod を確認すると次のように以前の pod が Terminating になり、新しいものが Running になるのが確認できるかと思います。

$ kubectl get pod

NAME READY STATUS RESTARTS AGE
mysql-xxxxxxxxxx-xxxxx 1/1 Running 0 40m
rails-xxxxxxxxxx-xxxxx 1/1 Running 0 16s
rails-xxxxxxxxxx-xxxxx 1/1 Terminating 0 21m

新しい pod が Running になったらアプリケーションのデプロイが完了しているので、http://<EXTERNAL-IP>:3000/users にアクセスしてみましょう!

問題なく画面が表示されているのが確認できるはずです!


クリーンアップ

課金されないように今回作成したリソースを全て削除します。

削除しないとどんどん課金されていくので気をつけてください

$ kubectl delete service rails

$ gcloud container clusters delete rails-cluster
$ gcloud compute disks delete mysql-data


まとめ

今回は GCP の GKE を使い、Kubernetes を試してみました。

AWS の EKS の GA など盛り上がりを見せている Kubernetes ですが、弊社でも本番環境での Kubernetes の導入を見据えて絶賛検証中です。

実行環境を作成するだけであれば今回のようにそれほど難しくはないので是非みなさんも Kubernetes を試してみてください!!