本記事の目的
- 細かいことは置いておき、Rails5.1+PostgresqlのアプリケーションをGKEで公開するところまでを目的とする
- 今回使用したサンプルアプリケーションは、以下に置いておきます
Rails5.1プロジェクトの作成
$ rails new rails-gke-sample --webpack=vue
開発環境の設定
Dockerize Rails application
- 開発環境は、
docker-compose
で開発できるようにしておく
ビルド
$ docker-compose build
DB作成
$ docker-compose run web rake db:create
$ docker-compose run web rake db:migrate
起動
$ docker-compose up
-
http://localhost:3000
にアクセスする
kubectlのインストール
- kubectlは、Kubernetesのクライアント
$ gcloud components update
$ gcloud components install kubectl
$ kubectl version --short
Client Version: v1.8.6
The connection to the server localhost:8080 was refused - did you specify the right host or port?
GCPプロジェクトの作成
- GCPコンソールより、プロジェクトを作成
- rails-gke-sample-project
APIの有効化
- GCPコンソールより、作成したプロジェクトで以下のAPIを有効化しておく
Google Container Engine API
Google Cloud Container Builder API
Google Cloud SQL API
Cloud SQL
- RailsをGCPで動かす場合、DBは、Cloud SQLでMySQL/PostgreSQLインスタンスを立てるのがよい
- GCPコンソールより、Cloud SQLで、インスタンスの作成からDBの作成までをする
- Instance: rails-gke-sample-production
- DBMS: PostgreSQL
- User: postgres
- DB: rails_gke_sample_production
アクティブなプロジェクトの設定
- gcloudコマンドでアクティブなプロジェクトを設定しておく
$ gcloud config set project rails-gke-sample-project
Updated property [core/project].
$ gcloud config set compute/zone us-central1-a
Updated property [compute/zone].
クラスタの作成
- クラスタは、複数のノードの集合体
- ノードは、AWSでいうインスタンスに相当
- ポッドは、1個以上のコンテナの集合体.デプロイの最小単位
- GKEでクラスタを起動すると、masterとnode(defaultだと3台)が立ち上がる
-
kubectl get nodes
でNodesが3台起動していることが確認できる - この時点では、Podsは作成されていない
-
$ gcloud container clusters create rails-gke-sample-cluster --machine-type=n1-standard-1 --zone=us-central1-a
クラスタ情報の取得
$ gcloud container clusters list
NAME LOCATION MASTER_VERSION MASTER_IP MACHINE_TYPE NODE_VERSION NUM_NODES STATUS
rails-gke-sample-cluster us-central1-a 1.8.8-gke.0 35.188.82.xxx n1-standard-1 1.8.8-gke.0 3 RUNNING
kubectlで確認
- kubectl cluster-infoでもGKEのクラスタ情報を確認できる
$ kubectl cluster-info
- ノードの確認
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
gke-rails-gke-sample-clu-default-pool-bc05e197-9dd8 Ready <none> 1m v1.8.8-gke.0
gke-rails-gke-sample-clu-default-pool-bc05e197-9hx8 Ready <none> 1m v1.8.8-gke.0
gke-rails-gke-sample-clu-default-pool-bc05e197-tvt2 Ready <none> 1m v1.8.8-gke.0
コンテナのビルド/Container Registryへの登録
- docker-compose upでローカルで起動できるようにしておく
- 以下のコマンドで、Container Builderを利用してコンテナのビルドを実施する
- ビルドに成功するとContainer Registryにイメージが登録される
-
rails-gke-sample
部分は、適当に名前をつければ良い
$ gcloud container builds submit --tag gcr.io/rails-gke-sample-project/rails-gke-sample .
環境変数の設定
- 以下のコマンドで環境変数を登録できる
- 環境変数は、クラスタに対して登録する (クラスタを削除したら登録し直す必要がある)
- 設定内容は、
- GCPコンソールのワークロード > 名前 > 設定
- ProxyのOverview > Config and Storage > シークレットで確認できる
$ kubectl create secret generic <name> --from-literal=<key>=<value>
Rails
SecretKeyの設定
$ kubectl create secret generic rails --from-literal=secret-key-base=3825d****
DB
サービスアカウントの作成
- IAMと管理 > サービス アカウントより、サービスアカウントを作成する
- rails-gke-sample-admin
- この時、[役割] で、[Cloud SQL] > [Cloud SQL Client] を選択する
- ダウンロードした秘密鍵を
~/.gcloud-secret/
に配置しておく
CloudSQLのクレデンシャルに指定
- ダウンロードした秘密鍵をCloudSQLのクレデンシャルに指定する
$ kubectl create secret generic cloudsql-oauth-credentials --from-file=credentials.json=/Users/hogehoge/.gcloud-secret/rails-gke-sample-project-d0c6a11a46eb.json
secret "cloudsql-oauth-credentials" created
- 削除する場合
$ kubectl delete secret cloudsql-oauth-credentials
DBアクセスのためのSecretを設定
$ kubectl create secret generic cloudsql-password --from-literal=username=postgres --from-literal=password=fugafuga
アプリケーションのデプロイ
Deploymentオブジェクト
- Deploymentオブジェクトは、新しいバージョンのリリースを管理するための仕組み
- マニフェストファイル(
deployment.yml
)を作成する
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: sample-app
spec:
replicas: 2
template:
metadata:
labels:
app: sample-app
spec:
containers:
- image: gcr.io/rails-gke-sample-project/rails-gke-sample
name: web
env:
- name: RAILS_DB_HOST
value: 127.0.0.1
- name: RAILS_DB_USER
valueFrom:
secretKeyRef:
name: cloudsql-password
key: username
- name: RAILS_DB_PASSWORD
valueFrom:
secretKeyRef:
name: cloudsql-password
key: password
- name: SECRET_KEY_BASE
valueFrom:
secretKeyRef:
name: rails
key: secret-key-base
- 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
command: ["/cloud_sql_proxy",
"-instances=rails-gke-sample-project:us-central1:rails-gke-sample-production=tcp:5432",
"-credential_file=/secrets/cloudsql/credentials.json"]
volumeMounts:
- name: cloudsql-oauth-credentials
mountPath: /secrets/cloudsql
readOnly: true
- name: ssl-certs
mountPath: /etc/ssl/certs
volumes:
- name: cloudsql-oauth-credentials
secret:
secretName: cloudsql-oauth-credentials
- name: ssl-certs
hostPath:
path: /etc/ssl/certs
コマンド実行
- 登録内容は、Proxyのデプロイメントから確認できる
$ kubectl create -f kube/deployment.yml
deployment "sample-app" created
コマンドによる確認
- Podsが起動するまでに、初回は数分時間がかかる
$ kubectl get deployments
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
sample-app 2 2 2 2 2m
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
sample-app-c6854f5d6-hvpfm 2/2 Running 0 2m
sample-app-c6854f5d6-th5xc 2/2 Running 0 2m
外部への公開
Serviceオブジェクト
- Serviceオブジェクトは、クラスタの内外に対してサービスを公開するための仕組み
- マニフェストファイル(
service.yml
)を作成- ここでは、80ポートを3000ポートと結びつける
apiVersion: v1
kind: Service
metadata:
name: sample-app
labels:
app: sample-app
spec:
type: LoadBalancer
ports:
- port: 80
targetPort: 3000
protocol: TCP
selector:
app: sample-app
コマンド実行
$ kubectl create -f kube/service.yml
コマンドによる確認
- しばらくすると、EXTERNAL-IPで、IPアドレスが付与されるのを確認できる
$ kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.63.240.1 <none> 443/TCP 46m
sample-app LoadBalancer 10.63.241.63 35.188.172.xxx 80:30516/TCP 1m
外部への公開
-
http://35.188.172.xxx
でアクセス
DBのマイグレーション
-
rake db:migrate
をいつやるか問題- a) コンテナにSSHした後、
rake db:migrate
コマンドを実行 - b) Jobを使用する
- a) コンテナにSSHした後、
- 今回は、簡単のため(a)でやる
アセットのプリコンパイル
-
rake assets:precompile
をいつやるか問題- a) Dockerfileに含めてしまう
- b) Jobを使用する
- 今回は、簡単のため(a)でやる
後始末
Deploymentの削除
$ kubectl delete deployment sample-app
deployment "sample-app" deleted
$ kubectl delete service sample-app
service "sample-app" deleted
クラスタの削除
$ gcloud container clusters delete rails-gke-sample-cluster
CloudSQLの停止
- GCPコンソールから停止
Tips
コンテナにSSHする方法
- rails c や rails dbが使える
$ kubectl exec -it <POD NAME> -c <CONTAINER NAME> bash
root@sample-app-6b9f746b6c-5ct48:/my_app#
ログを確認する方法
- sternを使用すると、複数のPodのログをまとめて参照できるので便利
$ stern sample-app
kubernetes使いは全員 stern を導入すべき – Daisuke Maki – Medium
Kubernetes関連の便利ツール: stern - そんな今日この頃の技術ネタ
wercker/stern: ⎈ Multi pod and container log tailing for Kubernetes
トラブルシューティングの方法
- Kubernetesにおけるデプロイの失敗は通常、特定のPodが立ち上がらないという状態で現れる。
- 存在しない、あるいは権限的な問題でアクセスできないコンテナイメージの指定
- アプリケーション実行時のConfigMapあるいはSecretが存在しない
- Specオブジェクトが無効
- リソース制限超過 : Podとコンテナにはそれぞれ、CPUおよびメモリ使用量の制限が設定されていて、これらの制限を超過するとPodの生成が行われない
- リソースクオータ超過 : リソースクオータは、ノード数の固定されたクラスタを複数チームが共有する場合に、名前空間毎のリソース消費を制限するためのメカニズム
- アプリのクラッシュ : ‘CrashLoopBackOff’メッセージを伴うローンチエラーで検出可能
- 起動状態チェックに対するURLからの応答に時間がかかると、タイムアウトが発生してデプロイに失敗
- 根本原因の特定には‘kubectrl describe’コマンドが有効
-
kubectl describe pod <pod名>
を実行すれば、エラーの原因を記述したイベントログが表示される kubectl logs <Pod名> <コンテナ名>
-
10 Most Common Reasons Kubernetes Deployments Fail (Part 1)
10 Most Common Reasons Kubernetes Deployments Fail (Part 2)
Refs.
RailsをGKEで動かす際に参考にしたアプリケーションのリンク集
- 別途以下にまとめました
RailsをGKEで動かす際に参考にしたアプリケーションのリンク集 - Qiita
その他記事
RailsアプリをGoogle Container Engineで動かす - Qiita
Kubernetesでのデプロイ中に'db:migrate'や'db:seed'などのRailsタスクを管理する(翻訳)
Deploying Rails on Kubernetes – Adwerx Engineering
Google Container Engine (GKE, Kubernetes) を使ってサービスを外部公開する | つかびーの技術日記