Rails5.2で作成したアプリケーションをGKE(Google Kubernetes Engine)にデプロイする方法をまとめておきたいと思います。
GKEは今回初めて使用したため、至らない点があるとは思いますが、ご了承下さい。
バージョン
Rails 5.2.1
Ruby 2.5.1
MySQL 5.7
目標
- GKEにRailsアプリケーションをデプロイして公開する
- データベースはGoogle Cloud SQLを使用する(GKEから接続する)
前提
- Google Cloud SDK(gcloudコマンドラインツール)がインストールされていること
※インストールされていない場合はこちらを参照 - GCPのプロジェクトは作成済みであること(GCPコンソールより作成可能)
Google Cloud SDKの認証
SDKをインストールしただけでは使用できないため、以下コマンドを実行していない場合は実行する。
実行すると、ブラウザが開いてGoogleアカウントでの認証が求められます。
$ gcloud auth application-default login
Railsプロジェクトの内容
構成としては、ビュー、コントローラー、モデルが1つずつのシンプルなもの。
プロジェクト名は「rails_sample」としています。
モデル
class User < ApplicationRecord
# カラムはemailとnameのみ
validates :email, presence: true
validates :name, presence: true
end
コントローラー
class UsersController < ApplicationController
def index
@user = User.all
end
end
ビュー
<table border="1">
<tr>
<th>Email</th>
<th>Name</th>
</tr>
<% @user.each do |user| %>
<tr>
<td><%= user.email %></td>
<td><%= user.name %></td>
</tr>
<% end %>
</table>
DB設定ファイル
default: &default
adapter: mysql2
encoding: utf8
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
username: root
password: password
host: db
development:
<<: *default
database: rails_sample_development
test:
<<: *default
database: rails_sample_test
# 環境変数は後ほど設定します
production:
<<: *default
host: <%= ENV['DB_HOST'] %>
database: <%= ENV['DB_NAME'] %>
username: <%= ENV['DB_USER'] %>
password: <%= ENV['DB_PASS'] %>
ルーティング
Rails.application.routes.draw do
root to: "users#index"
end
Dockerの内容
Dockerについては、buildしてイメージを作成しておく。
イメージ名は「gcr.io/プロジェクトID/任意のアプリ名」形式にしておく必要がある。
ここでは「gcr.io/プロジェクトID/rails_sample」という名前にしている。
version: '3'
services:
db:
image: mysql:5.7.23
environment:
MYSQL_ROOT_PASSWORD: password
ports:
- '4306:3306'
volumes:
- mysql-data:/var/lib/mysql
web:
build: .
image: gcr.io/プロジェクトID/rails_sample
command: bundle exec rails s -p 3000 -b '0.0.0.0'
volumes:
- ./rails_sample:/var/www/rails_sample
ports:
- '3000:3000'
environment:
- EDITOR=vim
depends_on:
- db
volumes:
mysql-data:
FROM ruby:2.5.1
ENV LANG C.UTF-8
RUN apt-get update -qq && apt-get install -y build-essential mysql-client default-libmysqlclient-dev vim
# Install nvm with node and npm
ENV NVM_DIR /usr/local/nvm
ENV NODE_VERSION 6.9.4
RUN curl https://raw.githubusercontent.com/creationix/nvm/v0.30.1/install.sh | bash \
&& . $NVM_DIR/nvm.sh \
&& nvm install $NODE_VERSION \
&& nvm alias default $NODE_VERSION \
&& nvm use default
ENV NODE_PATH $NVM_DIR/v$NODE_VERSION/lib/node_modules
ENV PATH $NVM_DIR/versions/node/v$NODE_VERSION/bin:$PATH
RUN npm install -g yarn
ENV APP_HOME /var/www/rails_sample
RUN mkdir -p $APP_HOME
WORKDIR $APP_HOME
ADD ./rails_sample/Gemfile $APP_HOME/Gemfile
ADD ./rails_sample/Gemfile.lock $APP_HOME/Gemfile.lock
RUN bundle install
ADD ./rails_sample $APP_HOME
RUN bundle exec rake assets:precompile
EXPOSE 3000
CMD ["rails", "server", "-b", "0.0.0.0"]
kubectlのインストール
$ gcloud components install kubectl
gcloudコマンドラインツールのデフォルトを設定する
$ gcloud config set project [PROJECT_ID]
$ gcloud config set compute/zone asia-northeast1-a
※この例では、zoneはasia-northeast1-aに設定している。任意のzoneで構わない。
APIの有効化
GCPコンソールより、作業するプロジェクトにて以下のAPIを有効化する
Google Container Engine API
Google Cloud Container Builder API
Google Cloud SQL API
Kubernetesクラスタの作成
コンテナクラスタを作成します。
後でGCPのレジストリにコンテナイメージを格納するのですが、このコンテナイメージを実行するのがコンテナクラスタになります。
作成には数分かかります。
$ gcloud container clusters create my-cluster \
--num-nodes 2 \
--machine-type n1-standard-1 \
--zone asia-northeast1-a
★注意!
既存のクラスタを使用する場合、又はGCPコンソールからクラスタを作成した場合は、以下のコマンドを実行する。
$ gcloud container clusters get-credentials クラスタ名
CloudSQLの設定
MySQLのCloudSQLインスタンスを作成する
$ gcloud sql instances create --gce-zone asia-northeast1-a --database-version MYSQL_5_7 --memory 4 --cpu 2 インスタンス名
zoneやメモリ、CPU数は任意で構いません。
作成したCloudSQLインスタンスにDatabaseを作成する
$ gcloud sql databases create DB名 --instance=インスタンス名
DBユーザーを作成する
DBユーザー作成と同時にパスワードも設定します。
$ gcloud sql users set-password DBユーザー名 \
--instance=インスタンス名 --password=DBパスワード
プロキシのインストール
CloudSQLにプロキシ経由で接続するため、プロキシをインストールします。
プロキシをダウンロード
# Macの場合
$ curl -o cloud_sql_proxy https://dl.google.com/cloudsql/cloud_sql_proxy.darwin.amd64
# Linuxなどwgetが使用できる場合
$ wget https://dl.google.com/cloudsql/cloud_sql_proxy.linux.amd64 -O cloud_sql_proxy
実行権限を付与しておく。
$ chmod +x cloud_sql_proxy
サービスアカウント作成
ここでは、[test-user]という名前のアカウントを作成。
$ gcloud iam service-accounts create test-user --display-name "test-user"
プロキシアカウントのメールアドレスを確認
サービスアカウントが作成されると、以下コマンドでメールアドレスを確認できる。
$ gcloud iam service-accounts list
NAME EMAIL
test-user test-user@プロジェクトID.iam.gserviceaccount.com
プロキシアカウントにCloudSQLClientロールを付与する
# メールアドレスは上記で確認したアドレスにする
$ gcloud projects add-iam-policy-binding プロジェクトID --member serviceAccount:test-user@プロジェクトID.iam.gserviceaccount.com --role roles/cloudsql.client
認証ファイル作成
# 認証ファイル名は「key.json」としている。メールアドレスは上記で確認したプロキシアカウントのアドレスにする。
$ gcloud iam service-accounts keys create key.json --iam-account test-user@プロジェクトID.iam.gserviceaccount.com
DockerイメージをGCPのContainer RegistryにPushする
$ docker push gcr.io/プロジェクトID/rails_sample
DockerコンテナをGKEにデプロイ
クラスタの情報を確認
$ gcloud container clusters list
# ここではIPアドレスは伏せています
NAME LOCATION MASTER_VERSION MASTER_IP MACHINE_TYPE NODE_VERSION NUM_NODES STATUS
my-cluster asia-northeast1-a 1.10.9-gke.5 ***.***.***.*** g1-small 1.10.9-gke.5 1 RUNNING
Kubernetes Secretsの作成
コンテナからプロキシ経由でDBにアクセスするため、環境変数を設定する必要あります。
上記プロキシインストール時に作成した認証ファイルを読み込むことで簡単に作成できます。
# 認証ファイルへのパスは相対パスで指定
$ kubectl create secret generic cloudsql-instance-credentials --from-file=credentials.json=./key.json
以下のようにシークレットを作成することも可能。ここではDBユーザーとパスワードを設定。
$ kubectl create secret generic cloudsql-db-credentials \
--from-literal=username=DBユーザー名 \
--from-literal=password=DBパスワード
マニフェストファイルを作成
コンテナ情報をマニフェストファイルに記載します。
Railsアプリ内で使用している環境変数の値もこのマニフェストファイルにて定義します。
# ファイルを作成。ファイル名は任意の名前でOK
$ vi deployment.yml
以下、マニフェストファイルの例です。ご自身の環境に合わせて設定値を変更して下さい。
設定値の説明については、以下例のコメントを参照下さい。
全て入力したら保存します。
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
# 任意の名前
name: sample-app
spec:
replicas: 2
template:
metadata:
labels:
# 任意の名前
app: sample-app
spec:
containers:
# Pushしたイメージの名前
- image: gcr.io/プロジェクトID/rails_sample
name: web
# env配下は全て環境変数。必要な変数は全て定義しています。
env:
# 上記で作成したKubernetes Secretsから読み込み
- name: DB_USER
valueFrom:
secretKeyRef:
name: cloudsql-db-credentials
key: username
- name: DB_PASS
valueFrom:
secretKeyRef:
name: cloudsql-db-credentials
key: password
# その他必要な環境変数を定義
- name: DB_NAME
value: sample-db
- name: DB_HOST
value: 127.0.0.1
- name: RAILS_ENV
value: production
- name: RACK_ENV
value: production
- name: RAILS_SERVE_STATIC_FILES
value: 'true'
ports:
- containerPort: 3000
name: sample-app
command: ["bundle", "exec", "rails", "server", "-p", "3000", "-b", "0.0.0.0"]
- image: b.gcr.io/cloudsql-docker/gce-proxy:1.11
name: cloudsql-proxy
# instancesはコンソールにて確認可能。※1を参照。
command: ["/cloud_sql_proxy",
"-instances=プロジェクトID:asia-northeast1:CloudSQLのインスタンス名=tcp:3306",
"-credential_file=./key.json"]
volumeMounts:
- name: cloudsql-instance-credentials
mountPath: /secrets/cloudsql
readOnly: true
volumes:
- name: cloudsql-instance-credentials
secret:
secretName: cloudsql-instance-credentials
※1 GCPのコンソールからSQL → 作成したインスタンス → 「このインスタンスに接続」の「インスタンス接続名」に記載されている。(以下画像の赤で囲った部分)
デプロイ実行
$ kubectl create -f deployment.yml
デプロイ確認
正しくデプロイできている場合は、pod内の両方のコンテナが正しく動作していることを示す2/2が表示される。
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
sample-app-xxxxxxxxx-xxxxx 2/2 Running 0 1m
サービスの公開
外部へ公開するため、ロードバランサーを作成する。80番ポートにて公開するため、3000番ボートを80番ポートに結びつけます。
$ kubectl expose deployment sample-app --type "LoadBalancer" --port 80 --target-port 3000
パブリックIPの確認
公開されるまで数分かかる場合があるので、以下コマンドは数分後に実行する。
$ kubectl describe services sample-app
# 上記コマンドを実行すると情報が色々出てくる。その中の以下の部分を参照する。
LoadBalancer Ingress: xxx.xxx.xxx.xxx
DBのマイグレーション
やり方としては以下が考えられます。
- コンテナにSSH接続して、rake db:migration コマンドを実行する
- Jobを使用してデプロイ時に実行する
本来は2.でやるべきだと思いますが、今回は1.で実行しました。
コンテナ接続する方法
# POD名はkubectl get pods コマンドで確認可能。コンテナ名はdeployment.ymlのcontainers配下のnameに指定した値
$ kubectl exec -it POD名 -c コンテナ名 bash
上記でコンテナに接続すると、アプリケーションのルートディレクトリがカレントになっているので、そのままrake db:migration コマンドを実行すればテーブルが作成されます。
また、このままrails c コマンドも実行できるので、これでデータも作成できます。
公開したアプリケーションの確認
上記でパブリックIPを確認していると思いますので、ブラウザから以下URLにアクセスします。
http://パブリックIP
まとめ
手順が多く、最初は大変だなと思ってしまいますが、これを使いこなせるようになると、おそらくこれまでのデプロイ方法に戻れなくなると思います。あまりにも便利なので。
今はまだ、本番環境でもコンテナで稼働しているサービスは少ないと思いますが、これから増えてくると予想されますので、今の内に知識を身に着けて損はないはずです。
おそらくもっとシンプルに、且つ効率的なやり方があると思うので、そこは継続して調査して行きます。
参考
https://cloud.google.com/kubernetes-engine/docs/tutorials/hello-app?hl=ja#step_4_create_a_container_cluster
https://qiita.com/jwako/items/90c0cb33cca4456c1d88
https://qiita.com/tkusumi/items/01cd18c59b742eebdc6a
https://qiita.com/chimame/items/4ac6bdd948a995ca0adf