背景
herokuでRailsアプリを動かしていましたが、いかんせん無料なので起動が遅かったり、デプロイ時にR10(60sでタイムアウトする)に引っかかったりと辛くなってきました。
herokuへの課金や移行先を考えるにあたり、折角なのでGoogle Container Engine (GKE)も試してみました。
その際ちょいちょい躓く所があったので、備忘録として手順を残しておきます。
なお、とりあえず動かすことにフォーカスしています。
参考にされる場合、productionとして必要なNginxやSSLなどの諸々は追加で設定してください。
前提
- dockerコマンドはインストールされているものとします
- 動くRailsアプリがあるとします
- 無ければRails newしたものでも良いですが、DBとの通信を確認できた方が良いです
- migrationファイルを適当に用意しましょう
- MySQLで考えます
- 無ければRails newしたものでも良いですが、DBとの通信を確認できた方が良いです
- 無料枠がある(今まで使ったことがない)と考え、細かい設定は行いません
- Webの画面で作れる所は頼ります
- リージョンは東京です
Railsの設定
database.ymlは以下のような設定にします。
production:
<<: *default
adapter: mysql2
encoding: utf8
database: <db-name>
host: <%= ENV["RAILS_DB_HOST"] %>
username: <%= ENV["RAILS_DB_USER"] %>
MySQLで、ホスト/ユーザー名は外部から取ってくる仕様です。
はアプリに合わせたDB名に変更してください。
後ほど使いますので覚えておくか、忘れたらGCPの画面で都度確認しましょう。。
手順
クレカを登録すると沢山課金されるんじゃ……と怯えてしまいますが、$300あれば基本大丈夫なので色々試せます。
ビバ無料枠。
GCPへの登録
では、Google Cloud Platform (GCP) の登録から始めましょう。
手順に従ってゆけば登録できます。
- 個人/法人を 指定
- 名前、住所、クレカの入力
- 完了
$300が付与されます。
なお、試用期間中に仮に超えても、権限を付与しないと課金は発生しないと言われます。
親切設計。
Projectを作る
大きな括りであるProjectを作ります。
create project
を選び、好きなProject nameを付けます。
そして、隠れているオプションを開き、リージョンを設定します。
自分の場合だとus-central
がデフォルトで選択されており、変更し忘れるところでした。
asia-northeast1
に変更して作成します。
作成完了するとDashboard上でステータスが確認できます。
ProjectのIDは後ほど使うので場所を覚えておきます。
Clusterを作る
ここでGKEの設定を少しだけはさみます。
左のメニューから "Container Engine" を選んで有効化します。
初回に数分ほど時間がかかるので、Twitterを眺めて待ちましょう。
初期起動が終わったら、クラスタを作成します。
nameはわかりやすい名前をつけ、Zoneはasia-northeast1-a
にします。
また、Cloud SQLのPermissionをEnableにします。
他はとりあえずそのままで進めます。
クラスタ名は後ほど使うので、覚えておきます。
Google Cloud SDKを入れる
手元から操作をするために、Google Cloud SDKというものが必要です。
後々使いますので、この段階でインストールします。
↑を見て、プラットフォームにあったインストールを行います。
基本的にYを入力していれば終わります。
インストールが終わったらアップデートを行います。
$ gcloud components update
$ gcloud components install kubectl
ついでにkubectlというものも入れていますが、後半に出てきます。
ここで認証、デフォルト値の設定を行います。
プロジェクトやクラスタを毎回指定するのは手間ですので、デフォルトとして与えます。
$ gcloud init
$ gcloud config set container/cluster <クラスタ名>
$ gcloud container clusters get-credentials <クラスタ名>
指示に従い進めます。
"Do you want to configure Google Compute Engine"でYを押し、"asia-northeast1-a"を探して選びます。
個別に設定をしたい場合は、↓の中程にある(オプショナル)の箇所を参照してください。
https://cloud.google.com/container-engine/docs/before-you-begin#optional_set_gcloud_defaults
$ gcloud config set project <プロジェクト名>
$ gcloud config set compute/zone asia-northeast1-a
$ gcloud config set container/cluster <クラスタ名>
$ gcloud container clusters get-credentials <クラスタ名>
さらに、↓を参考に、デフォルトの認証情報も設定しておきます。
https://developers.google.com/identity/protocols/application-default-credentials
左のメニューから "API Manager" → "Credentials" を選び、上部"Create Credentials" / "Service account key"を選びます。
"Compute Engine default service account" / "JSON"を選択し、作成します。
するとJSONファイルがダウンロードされるので、ローカルの安全な場所に置きます。
.ssh
の中などがいいかもしれません。
置いた場所を環境変数GOOGLE_APPLICATION_CREDENTIALS
に設定し、↓を実行しておきます。
$ gcloud auth application-default login
これでデフォルトの設定は完了です。
DBを作る
アプリに先立って、DBを先に立てておきます。
左のメニュー → "STORAGE" → "SQL" でCloud SQL (MySQL) インスタンスを作ることが出来ます。
Cloud SQLには世代が2つありますが、速くて安い第二世代を選びましょう。
Instance IDはサービスを表すわかりやすい名前を付けます。
↓の記事を見ると、第一世代との性能差が結構あるようです。
シビアなサービスを作って色々と比較してみたいものです。
http://qiita.com/pakotan/items/1db981611ead66ce2c8b
Regionはasia-northeast1
、Zoneはasia-northeast1-a
を選びましょう。
Zoneは3つありますが、とりあえず分かりやすさのためaにします。
他、ハードウェアはデフォルトのままにしておきます。
設定を変えた際の各種スループットがグラフで出てとてもわかりやすく、いじってみると楽しいです。
今回はすぐにプロダクション、というわけではないので諸々設定していませんが、設定を眺めて「こんなことができるのかー」と調べておくのが良さそうです。
createボタンを押すとIPが割り振られて出来上がりです。
出来上がったら、"Instance details"から "Access Control" / "Users" を選び、rootのパスワードを変更しておきましょう。
手元のクライアントからつなぐ
出来上がったDBに早速つないでみたいですが、IPよりProxyを通してつなぐ方式が推奨されています。
この方法ですと、特定IPを許可する必要もなく安全です。
↑の記事に従い、cloud_sql_proxyをダウンロードします。
サービスアカウントは先程作ったものが使われますので、ここでは作らなくてもよいです。
例では/cloudsql
というディレクトリを作っていましたが、ルートに作るのが嫌だったのでホーム以下の適当なフォルダに作りました
(なお、Dockerを使ってProxyを立てる方法もありました)。
指定したフォルダにソケットが出来上がるので、それを指定してアクセスします。
コマンドラインでなくても、MySQL Workbenchなどが使えます。
先ほど変更したrootパスワードを使ってrootとしてログインします。
アプリ用DB、DBユーザーの作成
Railsからアクセスするため、アプリ用のユーザーを作ります。
アプリ-DB間の通信もSQL Proxyを使うのが良いとされているので、それに特化したユーザーにします。
https://cloud.google.com/sql/docs/sql-proxy
の中程「プロキシ用の特別な MySQL ユーザー アカウントの作成について」に書かれています。
ではrootでアクセスをした状態で作業をします。
<hoge-xxx>
はアプリに合わせてください。
CREATE DATABASE <hoge-dbname>;
CREATE USER '<hoge-name>'@'cloudsqlproxy~%';
GRANT ALL PRIVILEGES ON <hoge-dbname>.* TO '<hoge-name>'@'cloudsqlproxy~%';
これで、Cloud SQL Proxy経由のみでアクセスできるユーザーが生まれました。
アプリからはこのユーザーを指定してつなげます。
APIの許可
GKE上のCloud SQL ProxyからOAuthを使ってCloud SQLに接続する際、APIを通じて通信を行います。
これは事前に許可してあげないといけません。
左のメニュー"API Manager" -> "Dashboard" -> "Enable API" -> "Google Cloud SQL API ENABLE"の"ENABLE"をクリックすると許可されます。
Docker Imageのpush
Container Registryにイメージを配置し、Container Engineが簡単に取ってこれるようにします。
RailsアプリのDocker化はされているものとします。
ここでは、サンプルとして以下のようなDockerfileを用意しました。
FROM ruby:2.3.1
ENV LANG C.UTF-8
RUN apt-get update -qq && apt-get install -y build-essential nodejs
RUN mkdir /myapp
WORKDIR /myapp
ADD Gemfile Gemfile.lock /myapp/
ENV RAILS_ENV production
ADD . /myapp
RUN bundle install
CMD \
bundle exec rails db:migrate && \
bundle exec rake assets:precompile && \
bundle exec rails s -p 3000 -b '0.0.0.0'
アプリに合わせて変更したものを用意してください。
push先は、以下のようなURLです。
asia.gcr.io/<project-id>/<folder-name>
project-idはdashboardから確認できます。
folder-nameは好きな名前を付けてください。
URLを決めたら、↓のような手順でbuild、pushできます
(build -qなので反応が無いですが)。
$ IMAGE_ID=$(docker build -q -t foo .)
$ reg_url="asia.gcr.io/<project-id>/<folder-name>"
$ docker tag ${IMAGE_ID} ${reg_url}
$ gcloud docker -- push ${reg_url}
pushが完了したら、GCPのメニューから"Container Engine" → "Container Registry"を見て、イメージが配置されていることを確認しましょう。
コンテナの管理
さて、ここからはKubernetesを使ってコンテナを扱います。
http://kubernetes.io/docs/hellonode/
podやdeploymentなどの用語をふんわり抑えておくと、理解が深まるかと思います。
認証情報の設定
↑のページにもありますが、DBのパスワードなどを安全に管理する仕組みがあります。
kubectl上から設定が可能です。
<認証のjson>
には、以前.sshなどに保管したService Accountのjsonを指定します。
<db-user>
には、アプリ用に作成したDBユーザー名を指定します。
$ kubectl create secret generic cloudsql-oauth-credentials --from-file=credentials.json=<認証のjson>
$ kubectl create secret generic cloudsql --from-literal=username=<db-user>
これらの値は、最終的にRailsのdatabase.ymlへと受け渡されます。
状態を見る
まず覚えておくと便利な「困った時に叩くコマンド」として、kubectl get
があります。
$ kubectl get pod
$ kubectl get service
$ kubectl get deployment
などなど、さまざまな状態がkubectlで取得できます。
また、「ブラウザで状態を見たい!」という要望にも対応しており、以下のコマンドを叩いてhttp://127.0.0.1:8001/api/v1/proxy/namespaces/kube-system/services/kubernetes-dashboard/
に接続すると、dashboardで確認できます。
$ kubectl create -f https://rawgit.com/kubernetes/dashboard/master/src/deploy/kubernetes-dashboard.yaml
$ kubectl proxy
deploymentを作成する
↑に公式のサンプルがありますので、流用して1コンテナの設定を記述します。
このファイルを少し改変したものが以下です。
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: testapp
spec:
replicas: 1
template:
metadata:
labels:
app: testapp
spec:
containers:
- image: asia.gcr.io/<project-id>/<folder-name>:latest
name: web
env:
- name: RAILS_DB_HOST
value: 127.0.0.1
- name: RAILS_DB_USER
valueFrom:
secretKeyRef:
name: cloudsql
key: username
- name: SECRET_KEY_BASE
value: 'hoge'
- name: RAILS_ENV
value: production
- name: RACK_ENV
value: production
ports:
- containerPort: 3000
name: testapp
- image: b.gcr.io/cloudsql-docker/gce-proxy:1.05
name: cloudsql-proxy
command: ["/cloud_sql_proxy", "--dir=/cloudsql",
"-instances=<db-instance-id>=tcp:3306",
"-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
長いですが、やっていることはシンプルです。
プロジェクトに合わせて、↓の箇所を書き換える必要があります。
- image: asia.gcr.io/<project-id>/example:latest
"-instances=<db-instance-id>=tcp:3306",
は、ブラウザからGCPを見に行きましょう。
-instances=$PROJECT:$REGION:INSTANCE=tcp:3306.
という形式です。
env:
- name: RAILS_DB_HOST
value: 127.0.0.1
- name: RAILS_DB_USER
valueFrom:
secretKeyRef:
name: cloudsql
key: username
この辺りで秘密の情報を環境変数に受け渡しています。
これにより、kubectlで自由に設定した値を各コンテナで使用することができます。
また、Cloud SQL Proxyを別コンテナとして立て、それを利用することでProxyを通したDBへの接続を実現しています。
さて、この設定ファイルの内容を反映させます。
↓のコマンドで作成したファイルを指定すると一発です。
$ kubectl create -f <file-path>
ファイルの構文が正しいと、↓のような表示が出て成功します。
deployment "testapp" created
Railsアプリがちゃんと動いているかは、↓のコマンドで確認します。
$ kubectl get pod
これのステータスが"Running"になればOKです。
CrashLoopBackOffやErrorなどになっていると失敗です。
↓のコマンドなどでログを見て原因を探ると早いです。
$ kubectl get pod
pod-idを控える
$ kubectl logs <pod-id> web
このページの下に書いていますが、より詳細なログは他の場所で見ることがきます。
修正して再度デプロイしたい時は、とりあえずは↓のコマンドでまるまる消えてくれるので再試行できます。
$ kubectl delete -f deployment.yaml
serviceの作成
ついにここまで来ました。
上記のdeploymentは、まだ外部へのアクセス手段がありません。
そこで、serviceというものを利用し、外部に公開するIPを貼ります。
↓に様々な設定が載っています。
http://kubernetes.io/docs/user-guide/services/
今回は3000番を公開する↓のような設定ファイルを作ります。
apiVersion: v1
kind: Service
metadata:
name: testapp
labels:
app: testapp
spec:
type: LoadBalancer
ports:
- port: 3000
selector:
app: testapp
deploymentと同様のコマンドで作成したファイルを指定します。
$ kubectl create -f <file-path>
これにより、L4ロードバランサーが紐付けられ、外部からアクセスすることができます。
完了したら、以下のコマンドで確認をします。
作成したサービスにexternal IPが貼られたら完了です。
これは多少時間がかかるので、watchコマンドで更新しつつ、Twitterを眺めて待ちましょう。
$ kubectl get service
http://<external IP>:3000
にアクセスをすると、きっとRailsアプリが出迎えてくれているかと思います
Yay! You’re on Rails!
上手くいってない……??
$ kubectl get pod
をして、状態を確認しましょう。
Running以外(立ち上がっている最中か、エラーで再起動を繰り返しているか)などを確認し調査を進めます。
ログを見ると原因解決に役立ちます。
service-nameはweb, sql-proxyなど、deploymentファイルで指定したものです。
$ kubectl logs <pod-id> <service-name>
また、実際にノードにアクセスをしてみるとデバッグに役立ちます。
$ kubectl get node
ノード名をメモる
$ gcloud compute ssh <ノード名>
アクセス先で docker ps / docker exec -it xxxx /bin/bash など
詳細なログを見たい場合は、Stackdriverが役に立ちます。
左のメニュー"stackdriver" -> "Logging"でコンテナのログを見たり、SQLのエラーを見たりできます。
例えば、MySQLへの初期通信が上手く行かない場合などは、"API許可してますか?"というメッセージ付きのエラーが出たりします。
感想
公式ドキュメントが整備されており(日本語化されているものも多かった)、比較的進めやすかったです。
が、GKEとKubernetesがそれぞれ独立しており、Kubernetesを使ったことがないと少々悩む所が多いように感じました。
UI的にはmaterial designが洗練された感じを醸し出しており、AWSより個人的には見やすいと感じました。
予算設定、アラートも細かく設定できて良い感じです。
stackdriverとの連携など、この記事では扱わなかった機能がたくさんありますので、それらも使ってAWSと比較してみたいと思いました。