Rust のアプリケーションを Google App Engine で動かして Google Cloud Build を使って自動デプロイする

# はじめに

* Rust でアプリケーションを作ってみたいと思ったので、検証のために Google App Engine でうごかしてみた。
* ついでに Google Cloud Build を使った自動デプロイも試してみる

# プロジェクトの作成

```sh
cargo init rust-gae-gcb
```

# Rust のアプリケーション

* アプリケーションはなんでもいいので HTTP サーバーライブラリ Hyper の Hello World をつかう
* リッスンするアドレスだけ Google App Engine の仕様に合わせて `0.0.0.0:8080` に固定する

```rust:src/main.rs
extern crate hyper;

use hyper::{Body, Response, Server};
use hyper::rt::Future;
use hyper::service::service_fn_ok;

static TEXT: &str = "Hello, World!";

fn main() {
    let addr = ([0, 0, 0, 0], 8080).into();

    let new_svc = || {
        service_fn_ok(|_req|{
            Response::new(Body::from(TEXT))
        })
    };

    let server = Server::bind(&addr)
        .serve(new_svc)
        .map_err(|e| eprintln!("server error: {}", e));

    hyper::rt::run(server);
}
```

* Hyper だけ使えればいいので Cargo.toml の依存関係の記述はこう

```toml:Cargo.toml
[dependencies]
hyper = "0.12.25"
```

# Google App Engine の設定ファイル

* Google App Engine の実行環境には、手軽に扱えるが言語に制限があるスタンダード環境と、言語に制限がないフレキシブル環境があり、Rust はスタンダード環境でサポートされていないのでフレキシブル環境 `env: flex` を利用する
* フレキシブル環境でも Python, Java, Node.js, Go, Ruby, PHP, .NET など実行マシンが用意されているものもあるが Rust はそれもないので自前で Docker マシンを用意する `runtime: custom` を利用する

```yaml:app.yaml
runtime: custom
env: flex
```

# 実行マシンの Docker ファイル

* Google App Engine のカスタムランタイムとして利用するための Docker マシンをビルドする Dockerfile を用意する
* 基本的に CentOS に Rust をインストールしてアプリケーションをビルドして実行しているだけ

```dockerfile:Dockerfile
FROM centos:7
EXPOSE 8080
ENV SOURCES=/sources
RUN yum update -y
RUN yum install -y file gcc openssl-devel
RUN curl https://sh.rustup.rs -sSf > install.sh
RUN sh ./install.sh -y
RUN mkdir -p $SOURCES
ADD ./ $SOURCES
WORKDIR $SOURCES
RUN $HOME/.cargo/bin/cargo build --release
CMD ./target/release/rust-gae-gcb
```

# GCP のプロジェクトの用意

* [公式のクイックスタート](https://cloud.google.com/appengine/docs/standard/go/quickstart?hl=ja) の `始める前に` などを参考に GCP プロジェクトと App Engine アプリケーションの作成を行う。

# 試しにデプロイ

* ここまでのもので実際にうまく動くか手動でデプロイをして試してみる

```sh
gcloud app deploy
```

# Google Cloud Build の設定ファイル

* デフォルトのファイル名 `cloudbuild.yaml` にすると App Engine のデプロイ時に不都合が出るので `gae-cloudbuild.yaml` とする
  * App Engine デプロイ時に実行されるカスタムランタイムの実行マシン作成には Cloud Build が利用されていると思われる
  * Cloud Build ではデフォルトの設定ファイルとして `Dockerfile` か `cloudbuild.yaml` を利用するが、両ファイルがあるとエラー扱いとするようなので名前を変更する

```yaml:gae-cloudbuild.yaml
steps:
- name: "gcr.io/cloud-builders/gcloud"
  args: ["app", "deploy"]
timeout: "1600s"
```

# Google Cloud Build の設定

* Google Cloud Build の設定を行い、リポジトリにコミットがあれば自動で Google App Engine にデプロイしてくれるようにする
* 基本的に[公式ドキュメント](https://cloud.google.com/source-repositories/docs/quickstart-triggering-builds-with-source-repositories)のとおりだがかいつまんでまとめていく

## コードのアップロード

* Cloud Build では Github.com と Google Source Repository と Bitbucket へ連携しブランチへのプッシュやタグのプッシュなどに反応して任意の処理を実行することができる
* まずは上記のコードをいずれかのリポジトリに保存する

## Cloud Build のサービスアカウントから App Engine にアクセスできるようにする

* GCP の各サービスはそれを実行するための専用アカウントであるサービスアカウントを持つ
* 以下は Cloud Build のサービスアカウントへ App Engine を操作する権限を追加してデプロイを実行できるようにする手順

```
1. Google Cloud Console で [IAM] ページを開く

2. プロジェクトを選択し、[続行] をクリック

3. メンバーリストで、[PROJECT_NUMBER]@cloudbuild.gserviceaccount.com という名前の Cloud Build サービス アカウントを探す。ここでの [PROJECT_NUMBER] は GCP プロジェクトのプロジェクト番号

4. その行の鉛筆アイコンをクリック

5. [別の役割を追加] をクリック

6. [役割の選択] プルダウン メニューから [App Engine] を選択し、[App Engine 管理者] を選択

7. [保存] をクリック

8. [別の役割を追加] をクリック

9. [役割の選択] プルダウン メニューから [Project] を選択し、[編集者] を選択

10. [保存] をクリック
```

## Cloud Build で処理を実行するトリガーを設定する

* Cloud Build ではブランチへのプッシュなどをきっかけに処理を行うものをトリガーとよんでいる
* 以下はトリガーの作成手順

```
1. Google Cloud Console で Cloud Build を開く

2. プロジェクトを選択し、[開く] をクリック

3. [トリガーを作成] をクリック

4. 任意のリポジトリを選択

5. 使用可能なリポジトリのリストから利用するリポジトリを選択して、[続行] をクリック

6. [トリガーのタイプ] で [ブランチ] を選択

7. [ビルド構成] で、[cloudbuild.yaml] を選択

8. [cloudbuild.yaml の場所] ボックスに「/gae-cloudbuild.yaml」と入力

10. [トリガーを作成] をクリック
```

## App Engine Admin API を有効にする

* これでデプロイを試したところ App Engine Admin API が無効になっているということでエラーになったので有効にする

```
1. Cloud Console で [API とサービス] を選択

2. "App Engine Admin API" を検索してクリック

3. API を有効にする
```

# デプロイする

* 適当にコードを変更してコミットしリモートに push してトリガーが実行されることを確認する
* 今回はブランチを指定しなかったので任意のブランチへプッシュすれば動作を確認できる

# おわりに

* 試しにやってみただけではあるがまあまあ素直に GAE で Rust を使うことができた。
* App Engine のビルドでも内部的に Cloud Build が使われているということでデプロイ用のトリガーの設定ファイル名に気をつける必要がある、というのは組み合わせて使ってみて初めて出てくる問題なのでちょっとはまった。