今回は 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
次にマニフェストファイルを作成し、先ほどの値をそのまま記述します。
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
の部分で永続ストレージを参照するようにします。
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 コンテナからアクセスできるようにします。
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 を作成し、外部からアクセスできるようにします。
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
となっているのに注意してください。
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
で指定した環境変数を参照するように変更します。
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 して、イメージファイルを作成したいと思います
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 を試してみてください!!