はじめに
Google App Engineスタンダード環境でRubyがサポートされました。
https://cloud.google.com/appengine/docs/standard/ruby/?hl=ja
サポートされたんだよね、本記事執筆時点(2019/9/9)で「ベータ版です」とか書いてあるけどドキュメントが追い付いてない?
まあそれはともかくGAEスタンダード環境とあれば私の出番だということで(?)、GAEスタンダード環境でのRubyの使い方について見ていきたいと思います。見ていくポイントは以下の3点です。
- 依存ライブラリはどうやって入れる?
- DatastoreとかのGCPサービス使うAPI(Rubyライブラリ)ある?
- Rails動かせる?
依存ライブラリの指定方法
まずは依存ライブラリをどうやって入れるのかです。
結論から先に言うとGemfileを書いておけば普通にBundlerを使ってインストールしてくれます。ここら辺はPythonやNode.jsと同じですね。
Quickstartで紹介されているHello World app(Sinatraで書かれています)を見るとGemfileで依存ライブラリが指定されていることが確認できます。
起動方法と待ち受けるポート
起動するプログラムはapp.yamlにentrypoint
で指定します。先のHello World appの場合は以下のようになっています。
runtime: ruby25
entrypoint: bundle exec ruby app.rb
このアプリの場合、Sinatraデフォルトのサーバ起動が行われ、Sinatraしか入れてないので標準ライブラリのWEBrickがサーバとして使われます。
production環境でWEBrickはあかんやろm9(^Д^)プギャー、という場合はすぐ横にHello WorldアプリをPuma(Rack)使って起動している例があります1。
こちらを見ると待ち受けるポートは環境変数PORT
で渡されてくることがわかります2。またこの例を見るとstaticファイルはGAEにserveさせる方法もわかります。
https://github.com/GoogleCloudPlatform/ruby-docs-samples/tree/master/appengine/standard-hello_configs
GCPサービスのAPI
Rubyがスタンダード環境で動かせると言ってもGCPの各種サービスにアクセスできなければ意味がない、まあ究極的にはWeb APIなので自力で書くことはできますがライブラリがあるなら使いたい。
はい、あります。これも公式サンプルに用意されています。フレキシブル環境使ってますがスタンダード環境を使うように直すのは容易です。
https://github.com/GoogleCloudPlatform/ruby-docs-samples/tree/master/appengine/datastore
app.yamlを以下のように直してgcloud app deploy
するだけです。
runtime: ruby25
#env: flex
entrypoint: bundle exec ruby app.rb
Railsの動かし方
SinatraはいいからRailsはどう動かすんだという声が聞こえてきそうですが、あらかじめ言っておくと結構大変です(笑)
Railsなんだから当然データベース使いますよね。
ここで「無課金で動かす」は諦めてください。
GCPにはCloud SQLというRDBMS(MySQL or PostgreSQL)動かすサービスが提供されていますがお金かかります。
Cloud SQLはGCEのインスタンス立ててDB専用にするみたいな感じ、値段はそこそこのお値段になります3。
現時点ではフレキシブル環境のサンプルしかありません。スタンダード環境とフレキシブル環境の違いがわかっていればこれを参考にして動かすことは可能です。ということで以下その方法です。
https://cloud.google.com/ruby/rails/using-cloudsql-postgres
Rails / GAEスタンダード環境デプロイ方法
基本的に上記のチュートリアルに沿って話を進めていきます。
1. Cloud SQLインスタンスを作成する
先に説明した、Railsで使うデータベースを作成します。
インスタンス作成→データベース作成の順番になります。インスタンス作成時のpostgresユーザパスワードはもちろん覚えておくように。
2. Railsアプリの用意
私は普段Windowsで生きてるのですがデプロイできるバージョン構成が作れなかったので結局Cloud ShellでRailsアプリを作成・デプロイを行いました。
2019/9/9時点でRailsは6.0.0が入っているのですが6.0.0で作ってデプロイしたところ動かせなかった4ので以下のようにGemfile書いて5.2.3入れて5.2.3でアプリを作りました。
source 'https://rubygems.org'
gem 'rails', '5.2.3'
$ bundle install
$ bundle exec rails new cat_sample_app
以降、scaffoldしてroute.rbを設定するところは普通のRailsアプリ通りです。
3. GAE用の設定を書く
チュートリアルにあるようにpgとappengineのgemを足します。
$ bundle add pg
$ bundle add appengine
次にconfig/database.ymlのproductionを設定します。「インスタンス接続名」はコマンドか管理画面で確認できます。
パスワード直書きとかm9(^Д^)プギャーという方は環境変数DATABASE_URL
で指定できると思うので(未検証)、そちらで指定してください。ちなみに「/cloudsql/インスタンス接続名」とあるように接続はUNIXドメインソケットで行われます(実際にApp EngineとDBが同じマシン内で動くわけではないと思うのですが)
production:
adapter: postgresql
encoding: unicode
pool: 5
timeout: 5000
username: "postgres"
password: "パスワード"
database: "cat_list_production"
host: "/cloudsql/インスタンス接続名"
チュートリアルにはありませんが(フレキシブル環境だと必要ない?)、これ以外にGemfileでRubyのバージョンを指定しているのをコメントアウトする必要がありました。
スタンダード環境は2.5.5なので、それ以外が書いてあるとデプロイ時にエラーになります。
source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
#ruby '2.6.3'
4. app.yamlを書く
チュートリアルに書かれているのはフレキシブル環境用のapp.yamlなので、これをスタンダード環境用に書き換えます。cloud_sql_instances
は必要ありません。
entrypoint: bundle exec rackup --port $PORT
runtime: ruby25
env_variables:
SECRET_KEY_BASE: 「bundle exec bin/rails secret」の結果
5. .gcloudignoreを用意する
.gcloudignoreはデプロイしないファイルを指定するものです。これがない場合ドット始まりのファイルが送信されないという仕様?なのかsprocketsが正常動作しませんでした。
.gcloudignore
#!include:.gitignore
.git
.gitignore
!/public/assets
6. デプロイ(まだ終わりじゃないよ)
App Engineアプリを作り、アセットをコンパイルし、デプロイします。チュートリアルでは--no-promoteが付いていますが初デプロイの場合は結局それがメインのバージョンになるので違いはありません。
$ gcloud app create
$ bundle exec bin/rails assets:precompile
$ gcloud app deploy
7. データベースマイグレーション
上で一度デプロイをしているのはCloud Buildのサービスアカウントを作るためです。Cloud Buildとは何ぞやということは末尾の参考記事をご参照ください。
プロジェクトIDだったりプロジェクト番号だったりでうっとうしいですが以下のようにして必要な権限を追加します。
$ gcloud projects add-iam-policy-binding プロジェクトID --member=serviceAccount:プロジェクト番号@cloudbuild.gserviceaccount.com --role=roles/editor
さてとデータベースマイグレーションです。ここで先ほど入れたappengine gemが出てきます。
何をしているのかというと、テンポラリのapp.yamlを作って、entrypointに指定されたコマンドを書いて実行して、デプロイされたテンポラリのバージョンを消すという超絶なことがされています(笑)
このappengine gemが2.5で追加されたメソッド使っているので5、デプロイする環境のRubyも2.5以上である必要があります。
$ bundle exec rake appengine:exec -- bundle exec rake db:migrate
以上でデプロイは終了です。
二回目以降のデプロイ
二回目からは以下のコマンドを打てばデプロイが行えます。
$ bundle exec bin/rails assets:precompile
$ gcloud app deploy
$ bundle exec rake appengine:exec -- bundle exec rake db:migrate
おわりに
以上、Google App Engineのスタンダード環境でRubyを使う場合を見てきました。
Node.jsと大体同じでした(広く使われている依存ライブラリの指定方法、起動プログラムは自分で指定、ポートは環境変数PORT)
触ってみた感想としては、Rails動かすのは特に無課金で使うつもりだったのなら諦めてHerokuを使え、GAE使うならDBはDatastore使ってフレームワークはSinatra使うのが楽だぞというところです。
参考というかこちらもどうぞの記事
Google App EngineのNode.js/Standard環境の仕組みについて調べてみた
Node.jsがスタンダード環境にデプロイされる仕組み(デプロイプロセスは何を行っているのか)を調べた記録です。Rubyでも大体同じ仕組みでデプロイが行われています。
GoogleAppEngineでDjangoを3年ほど動かしてわかったこと
思い出話ですがこの記事と関連する箇所としてはCloud SQLの話があります。
-
ここで起動しているアプリサーバが直接リクエストを受けるわけではなくロードバランサがいてリクエストがフォワードされるわけですが、まあWEBrickよりはPumaの方がいいですよね。 ↩
-
素のSinatraの場合は環境変数PORTを見ているのでentrypointで指定はされていませんでした。 ↩
-
MySQLはインスタンスのスペックを落とせますがPostgreSQLは最低スペックがメモリ3.75GBのようです。asia-northeastだと$64/月かかります。 ↩
-
tmp/pids/server.pidがない、ていうか、tmp/pidsがないというエラー。もう少し調べれば動かせるのかもしれませんが。 ↩
-
具体的にはSecureRandom.alphanumeric。他にもあるかもしれません。 ↩