はじめに

こんにちは! フィフス・フロア技術チームのチームリーダーnotozekiです。

先日、ついにGoogle App Engine(以下App Engine)のスタンダード環境でRubyがサポートされました🎉

https://cloud.google.com/blog/products/application-development/ruby-support-comes-to-app-engine-standard-environment

Rubyのパパ、まつもとゆきひろさんも「感無量」とコメントしていますね☺️

スタンダード環境でのRubyのサポートはベータ版という扱いのようですが、すでに利用可能になっています。とういうことで、早速Rubyアプリをデプロイして試してみました🚀

App Engineのスタンダート環境とは

ここで、App Engineとそのスタンダート環境について簡単に説明します。

App Engineとは、Googleが提供するWebアプリケーションの実行環境のPaaSです。

いくつかの言語向けのWebアプリ実行環境がはじめから用意されていて、開発者は自分のアプリのコードをApp Engineにデプロイするだけで、簡単にWebアプリをホストすることができます。

App Engineには「スタンダート環境」と「フレキシブル環境」の2種類の環境が用意されています。これらの環境の比較は、以下の公式ドキュメントが参考になります。

https://cloud.google.com/appengine/docs/the-appengine-environments

スタンダート環境は軽量なランタイムが特徴で、デプロイや起動が高速です。また、「ゼロにスケーリング」することが可能で、運用コストを抑えることができます。一方で自由度は低く、あらかじめ用意されているランタイムをカスタマイズしたり、自分の用意したイメージを使うことはできません。

Rubyのランタイムは、以前はスタンダート環境ではサポートされていませんでした。そのためRubyアプリをApp Engineにデプロイするにはフレキシブル環境を使うしかなかったのですが、このたびスタンダート環境でもRubyランタイムがサポートされたため、Rubyアプリでもスタンダード環境の恩恵が受けられるようになりました🙌

ちなみに、用意されているランタイムのRubyのバージョンは2.5のみとなっています(2019年9月現在)。

0からデプロイまで

さて、ここから実際にRubyのスタンダート環境を触ってみます。

まずは公式のクイックスタートのドキュメントに従ってRubyアプリをデプロイしてみます。

https://cloud.google.com/appengine/docs/standard/ruby/quickstart

ちなみに、スタンダート環境には無料枠があるので、このクイックスタートのインスタンスを動かすだけなら料金は掛かりません。フレキシブル環境には無料枠が無いので、これはスタンダート環境を使えることの恩恵ですね✌️

始める前に

App Engineを使うには、まずCloud SDKのインストールと、GCPのプロジェクトをセットアップする必要があります。Cloud SDKは以下の手順に従ってインストールします。

https://cloud.google.com/sdk/docs/

Cloud SDKがセットアップできたら、GCPのプロジェクトを新しく作ります。

$ gcloud projects create <プロジェクトID>

<プロジェクトID>には、半角英数字とハイフン(-)で構成された適当な名前を指定します。

余談ですが、この「プロジェクトID」は意外と曲者で、筆者は有効なプロジェクトIDを作るまでに以下のような罠を踏みました。

  • 先頭が数字だとダメ
  • 文字数が30文字を超えるとダメ
  • appengine」という単語が含まれているとダメ(!?)

特に最後のは「project_id contains prohibited words」なんていうエラーが出て仰天しました。しかもどの単語が悪いのか教えてくれない😇(筆者はいろいろ試行錯誤してappengineが原因だと突き止めました)

みなさまもプロジェクトIDの命名にはお気をつけください。

App Engineの初期化

プロジェクトを作ったら、App Engineを初期化して使えるようにします。

ところで、App EngineはGCPプロジェクトにつき1つしかアプリを作れないという仕様になっています(*1)。したがって、ここではその「唯一のアプリ」を初期化することになります。なんとなく1つのGCPプロジェクトに複数のアプリを作れるようなイメージだったので、少し驚きました。

*1: 「サービス」という単位ではアプリを分割できます。詳しくはこちらなどを参照してください。

さて、App Engineの初期化は以下のコマンドで行います。

$ gcloud app create --project=<プロジェクトID>

アプリを作成するリージョンを聞かれるので、適当なリージョンを選びます。なお、このリージョンは後から変更することができないので注意してください。

最後に、支払いを有効化します。無料枠分しか使わないとしても、支払いの有効化は必要なようです。

https://console.cloud.google.com/projectselector/billing

これで準備は整いました。

サンプルアプリをデプロイ

公式で用意されているサンプルコードがあるので、それをデプロイしてみます。

https://github.com/GoogleCloudPlatform/ruby-docs-samples

まずはGitHubからリポジトリをcloneしてきましょう。リポジトリのappengine/standard-hello_worldディレクトリの中に、Rubyのスタンダード環境向けのサンプルアプリのコードが入っています。

$ git clone https://github.com/GoogleCloudPlatform/ruby-docs-samples
$ cd ruby-docs-samples/appengine/standard-hello_world

サンプルアプリは、/にアクセスするとHello world!と表示するだけのごく簡単なものです。Sinatraで作られていました。

このアプリをApp Engineにデプロイしましょう。デプロイには以下のコマンドを使います。

$ gcloud app deploy --project=<プロジェクトID>

…たったこれだけです! 今回のアプリでは、初回のデプロイには2分ほどかかりました。2回目以降は1分程度でデプロイされます。

さて、デプロイされたアプリをブラウザで確認してみましょう。以下のコマンドを使うと便利です👌

$ gcloud app browse --project=<プロジェクトID>
スクリーンショット 2019-08-29 20.49.39のコピー.png

やりました💪

疑問: bundle installはどうなってる?

ところで、このRubyアプリを動かすには、最初にbundle installしてSinatraなどの依存するGemをインストールする必要があります。しかし、自分で明示的にそれをする指定はとくにしていません。デプロイ時に自動的にbundle installされると仮定して良いのでしょうか?

ドキュメントを探してもとくに記述が見つからなかったので、Cloud Build(*2)のビルドフェーズのログを見てみました。

*2: App Engineスタンダード環境では、デプロイ時にCloud Buildを使ってイメージがビルドされます(参照)。

Step #1 - "builder": [2019-08-30 11:41:21 INFO] => Running ruby25 build
Step #1 - "builder": [2019-08-30 11:41:21 INFO] => Installing bundle
Step #1 - "builder": [2019-08-30 11:41:21 INFO] ["bundle", "install", "--deployment", "--without=\"development test\""]
Step #1 - "builder": Fetching gem metadata from https://rubygems.org/........
Step #1 - "builder": Using bundler 1.17.3
Step #1 - "builder": Fetching diff-lcs 1.3
Step #1 - "builder": Installing diff-lcs 1.3

...(中略)...

Step #1 - "builder": Fetching sinatra 2.0.5
Step #1 - "builder": Installing sinatra 2.0.5
Step #1 - "builder": Bundle complete! 3 Gemfile dependencies, 13 gems now installed.
Step #1 - "builder": Gems in the groups "development and test" were not installed.
Step #1 - "builder": Bundled gems are installed into `./vendor/bundle`
Step #1 - "builder": [2019-08-30 11:41:26 INFO] => Checking for Rails sprockets
Step #1 - "builder": [2019-08-30 11:41:26 INFO] => Archiving app
Step #1 - "builder": [2019-08-30 11:41:26 INFO] ["tar", "cf", "/app.tar", "--transform", "s|^|srv/|rSH", "."]
Step #1 - "builder": [2019-08-30 11:41:26 INFO] => Archiving /.googleconfig
Step #1 - "builder": [2019-08-30 11:41:26 INFO] ["tar", "rf", "/app.tar", ".googleconfig"]
Step #1 - "builder": [2019-08-30 11:41:26 INFO] => Compressing archive
Step #1 - "builder": [2019-08-30 11:41:26 INFO] gzip < /app.tar > /app.tar.gz
Step #1 - "builder": [2019-08-30 11:41:26 INFO] => Creating destination image

...(中略)...

Step #1 - "builder": [2019-08-30 11:41:35 INFO] => Completed ruby25 build

このように、イメージのビルド時にbundle installが実行されていました。したがって自動的にbundle installされると仮定して良いようです。「Checking for Rails sprockets」なんて記述があるのも気になります。Railsアプリの場合はrails assets:precompileなんかもしてくれるのでしょうか? Railsでも試してみたいですね。

おわりに

今回は、App Engineスタンダード環境にRubyアプリをデプロイして、その使用感を試してみました。

コマンド1つで手軽に、短時間でRubyのWebアプリをホストできる環境があるのはとても魅力的に感じました。実際、今回の調査ではプロジェクトIDに試行錯誤していた時間のほうが長かったくらいに、デプロイは一瞬でできました。無料枠があるので、「ちょっと試してみる」くらいのアプリを気軽に作れるのも嬉しいポイントです。

Rubyは言語自体の機動力はありますが、気軽にホストできる環境があまり無い印象でした。App Engineのスタンダード環境は、そこを補完してRubyにさらなる機動力を与える存在になり得ると感じます。

App Engine、ぜひ活用していきたいと思います!