はじめに
「Railsを動かすならHeroku」の方が圧倒的に多いかと思いますが、Google App Engine(以下:GAE)でもFlexible Environmentにはなりますが、動かせます。
Docker化しているアプリケーションであれば、後々はk8s等の恩恵も受けたいはずで、それを考慮に入れてくると、GCP上に載せておくことは良いことかもしれません。
また、BigQueryやStackdriverなど、魅力的なプロダクトに繋げやすいのもGCPの利点です。
あまり、RailsをGoogle App Engineに載せている記事を見かけなかったので、簡単にまとめてみました。
Herokuデプロイ時に比べ、いくつかハマるポイントがありました……。
ここでは、Rails v5.1以上を対象に記載します。
GAEのRuby Base ImageのDockerfileを覗いてみる
まずは、GAEが使用するRuby Base ImageのDockerfileを覗いてみましょう。
https://github.com/GoogleCloudPlatform/ruby-docker/blob/master/appengine/image_files/Dockerfile (リンク切れ)
(2017-10-06 修正)
https://github.com/GoogleCloudPlatform/ruby-docker/blob/master/ruby-base/Dockerfile
大事な部分だけ、切り出します。
FROM gcr.io/google-appengine/debian8
---(snip)---
# (1)
RUN mkdir /nodejs && curl -s https://nodejs.org/dist/v6.11.1/node-v6.11.1-linux-x64.tar.gz | tar xvzf - -C /nodejs --strip-components=1
---(snip)---
# (2)
ENV DEFAULT_RUBY_VERSION 2.3.4
ENV BUNDLER_VERSION 1.15.1
---(snip)---
# (3)
ENV RACK_ENV=production \
RAILS_ENV=production \
APP_ENV=production \
RAILS_SERVE_STATIC_FILES=true \
RAILS_LOG_TO_STDOUT=true
# (4)
WORKDIR /app
EXPOSE 8080
ENV PORT=8080
ENTRYPOINT []
CMD []
(1)(2) デフォルトのRubyバージョンは、2.3.3
、Nodeは6.11.1
です。(このあたりはよく更新されます。)
(3) Heroku同様、RAILS_ENV
や、RAILS_SERVE_STATIC_FILES
など必要な環境変数は、コンテナビルド時に既にセットされます。
(4) ポイントとしては、8080
番ポートをリッスンしているところでしょうか。
GAEは、言語に限らず8080
番しかエントリーポイントとして許可しないのが習慣です。Railsサーバの起動は8080
番で行いましょう。
gcloud beta app gen-config で作られるDockerfileを覗いてみる
先ほどのBase Imageを使って、デプロイ用のコンテナがビルドされます。
app.yaml
でruntime: ruby
を指定した場合のDockerfileは、以下のコマンドで確認することができます。
$ gcloud beta app gen-config --custom
This looks like a Ruby application. Please confirm the command to run
as the entrypoint: [bundle exec rackup -p $PORT]:
Writing [app.yaml] to [/Users/takamario/qiita-appengine-rails-5.1].
Writing [Dockerfile] to [/Users/takamario/qiita-appengine-rails-5.1].
Writing [.dockerignore] to [/Users/takamario/qiita-appengine-rails-5.1].
FROM gcr.io/google-appengine/ruby:latest
# (1)
ARG REQUESTED_RUBY_VERSION=""
RUN if test -n "$REQUESTED_RUBY_VERSION" -a \
! -x /rbenv/versions/$REQUESTED_RUBY_VERSION/bin/ruby; then \
(apt-get update -y \
&& apt-get install -y -q gcp-ruby-$REQUESTED_RUBY_VERSION) \
|| (cd /rbenv/plugins/ruby-build \
&& git pull \
&& rbenv install -s $REQUESTED_RUBY_VERSION) \
&& rbenv global $REQUESTED_RUBY_VERSION \
&& gem install -q --no-rdoc --no-ri bundler --version $BUNDLER_VERSION \
&& apt-get clean \
&& rm -f /var/lib/apt/lists/*_*; \
fi
ENV RBENV_VERSION=${REQUESTED_RUBY_VERSION:-$RBENV_VERSION}
COPY . /app/
RUN if test -f Gemfile.lock; then \
bundle install --deployment --without="development test" \
&& rbenv rehash; \
fi
# (2)
ENV RACK_ENV=production \
RAILS_ENV=production \
RAILS_SERVE_STATIC_FILES=true
RUN if test -d app/assets -a -f config/application.rb; then \
bundle exec rake assets:precompile || true; \
fi
# (3) BUG: Reset entrypoint to override base image.
ENTRYPOINT []
# (4)
CMD bundle exec rackup -p $PORT
(1) ここで、使用するRubyのバージョンを設定できます。
(2) 念のため、ここでも環境変数はセットされています。
(3) BUGと書いてますね……。Base Imageでセットされてはいないですが、明示的に書いて置く必要がありました。
(4) bundle exec rails s -p $PORT でも問題ないです。
Rails 5.1以上用のDockerfile例
上で見てきたDockerfileは、あくまでもRubyアプリ全般、Railsに関しては、v5.1未満のものでした。
v5.1から導入されたyarn
などを使用するとなると、もうひと工夫必要になります。
(もちろん、そのうち修正されるとは思います。)
(2017-10-06 追記)
2017-09-20のコミットで、yarnのインストールが加わっていました。
https://github.com/GoogleCloudPlatform/ruby-docker/commit/8032c26cd3028176d0760b4f435c79463e8e1e30#diff-91e4c715bfb701f20104580d2fcdc94eR29
以下は、yarn
のインストールを含めたDockerfile例です。
FROM gcr.io/google-appengine/ruby:latest
# (1)
ARG REQUESTED_RUBY_VERSION=""
RUN if test -n "$REQUESTED_RUBY_VERSION" -a \
! -x /rbenv/versions/$REQUESTED_RUBY_VERSION/bin/ruby; then \
(apt-get update -y \
&& apt-get install -y -q gcp-ruby-$REQUESTED_RUBY_VERSION) \
|| (cd /rbenv/plugins/ruby-build \
&& git pull \
&& rbenv install -s $REQUESTED_RUBY_VERSION) \
&& rbenv global $REQUESTED_RUBY_VERSION \
&& gem install -q --no-rdoc --no-ri bundler --version $BUNDLER_VERSION \
&& apt-get clean \
&& rm -f /var/lib/apt/lists/*_*; \
fi
ENV RBENV_VERSION=${REQUESTED_RUBY_VERSION:-$RBENV_VERSION}
### 追加ここから
RUN apt-get update -qq && apt-get install -y apt-transport-https && \
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \
echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \
apt-get update -qq && apt-get install -y yarn
COPY . /app/
### 追加ここまで
RUN if test -f Gemfile.lock; then \
bundle install --deployment --without="development test" \
&& rbenv rehash; \
fi
ENV RACK_ENV=production \
RAILS_ENV=production \
RAILS_SERVE_STATIC_FILES=true
RUN if test -d app/assets -a -f config/application.rb; then \
bundle exec rake assets:precompile || true; \
fi
ENTRYPOINT []
CMD bundle exec rackup -p $PORT
app.yaml
最後にapp.yaml
ですが、動かすだけであれば、それほど複雑な記載は必要ありません。
GAEと一緒にCloudSQLを使うことが多いかと思いますので、その形で記載します。
また、Heroku同様、production環境ではDATABASE_URL
の環境変数で、DBの接続情報を渡すものとします。
({}の中身は、適宜置き換えて下さい。)
runtime: custom
env: flex
env_variables:
DATABASE_URL: mysql2://{USERNAME}:{PASSWORD}@localhost/{DATABASE}?socket=/cloudsql/{INSTANCE_CONNECTION_NAME}
beta_settings:
cloud_sql_instances: {INSTANCE_CONNECTION_NAME}
ハマるポイント
deviseを使っている場合、assets:precompileで落ちる
deviseに限らず発生する可能性はある事象ですが、GAEの設定ファイルであるapp.yaml
で環境変数は設定できますが、それはコンテナ起動時にのみセットされ、コンテナビルド時には設定されません。
回避策としては、以下のように適当にセットしておくか、
config.secret_key = ENV.fetch('SECRET_KEY_BASE', 'hogehoge')
Rails v5.1以上であれば、Encrypted Secretsを使って、ソースコードに含めてしまうのが良いかと思います。
config.secret_key = Rails.application.secrets.secret_key_base
Stackdriverでのログが見づらい
Base ImageでRAILS_LOG_TO_STDOUT=true
の環境変数をセットしていますので、Heroku同様、エラー等は標準出力され、GCPではStackdriverで確認可能なのですが、Railsのデフォルトのログフォーマットだと、見づらくなってしまいます。
以下のgemを入れるだけで、フォーマットをStackdriver用に整えてくれます。
google-cloud-ruby/stackdriver
また、RailsのSQLログは、色付き(ANSI Color)で出力されますが、Stackdriverは対応していないので、以下を設定しておくと良いかもしれません。
config.colorize_logging = false
Healthcheckのログがうざい、service-inされない
上のgemをインストールすると、healthcheckのログファイルを分けてくれるようになります。
ただ、事前にhealthcheckを返すアクションを作成しておく必要があります。
class GaeHealthcheckController < ApplicationController
def ok
render plain: 'ok'
end
end
if Rails.env.production?
get '/_ah/start', to: 'gae_healthcheck#ok'
get '/_ah/stop', to: 'gae_healthcheck#ok'
get '/_ah/health', to: 'gae_healthcheck#ok'
end
デプロイに時間がかかる
ログを見てもらうとわかりますが、デプロイ用のコンテナを作るまではそこまで時間がかからないのですが、GAE内部でserviceやversionの設定をしている部分に時間がかかっております。。
これからに期待ですね。。
CloudSQLへのDBマイグレーションは?
ローカルから、cloud_sql_proxyを用いて、マイグレーションを行うことも可能ですが、以下のgemで可能です。(上記の stackdriver
も依存gemとして含まれます。)
GoogleCloudPlatform/appengine-ruby
$ bundle exec rake appengine:exec -- bundle exec rake db:migrate
特にコンテナの中身にこだわりがなければ
2017-10-06現在、デフォルトのRuby用のapp.yamlを使用しても、yarnが既にインストールされているイメージが使われるので問題ないです。
entrypoint: bundle exec rackup --port $PORT
env: flex
runtime: ruby
beta_settings:
cloud_sql_instances: [YOUR_INSTANCE_CONNECTION_NAME]
この際、注意すべきところは、cloud_sql_proxy用にプロジェクトのサービスアカウントにEditor
のロールを付与してあげることです。
$ gcloud projects add-iam-policy-binding [YOUR-PROJECT-ID] \
--member=serviceAccount:[PROJECT_NUMBER]@cloudbuild.gserviceaccount.com \
--role=roles/editor
さいごに
Herokuと違って、準備しておかなければいけない部分は多少ありますが、Google Cloud Platformの他プロダクトの恩恵を受けやすいGoogle App Engineはとても魅力的だと思います。
コンテナ管理が流行っていくことも考慮に入れると、GAEは良い選択肢ではないでしょうか。