LoginSignup
51
49

More than 5 years have passed since last update.

RailsアプリをGoogle Container Engineで動かす

Last updated at Posted at 2016-11-08

背景

herokuでRailsアプリを動かしていましたが、いかんせん無料なので起動が遅かったり、デプロイ時にR10(60sでタイムアウトする)に引っかかったりと辛くなってきました。

herokuへの課金や移行先を考えるにあたり、折角なのでGoogle Container Engine (GKE)も試してみました。
その際ちょいちょい躓く所があったので、備忘録として手順を残しておきます。

なお、とりあえず動かすことにフォーカスしています。
参考にされる場合、productionとして必要なNginxやSSLなどの諸々は追加で設定してください。

前提

  • dockerコマンドはインストールされているものとします
  • 動くRailsアプリがあるとします
    • 無ければRails newしたものでも良いですが、DBとの通信を確認できた方が良いです
      • migrationファイルを適当に用意しましょう
    • MySQLで考えます
  • 無料枠がある(今まで使ったことがない)と考え、細かい設定は行いません
  • 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) の登録から始めましょう。

手順に従ってゆけば登録できます。

  • 個人/法人を 指定
  • 名前、住所、クレカの入力
  • 完了 :tada:

$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アプリが出迎えてくれているかと思います :tada:
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と比較してみたいと思いました。

51
49
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
51
49