App EngineからCompute Engineに移行する

App Engine のFlexible 環境が高すぎて(f1-smallと同じスペックで月4,000円超)こりゃ厳しいと思い、GCEに逃げることにした。

ロードバランサーとかも組み込まれているようなので、アクセス数がそれなりにある場合は App Engine が大活躍だと思うが、超小規模 Web サービスには厳しかった。

今後のことも考えて docker で運用していきたい。

やること

  1. Cloud Build で Rails アプリケーションのイメージ化
  2. インスタンス作成
  3. SSL証明書取得
  4. Webアプリケーション起動
  5. cloudsql proxy との接続

Cloud Build で Rails アプリケーションのイメージ化

この記事 で Code Build を使ってイメージ化済み

インスタンス作成

Container-Optimized OS を選択し、イメージのデプロイはこのタイミングでは行わない。(docker-compose.yml で一括管理したいので)

SSL証明書取得

起動したインスタンスにSSH接続をして以下のコマンドを叩いておく。

DOMAIN_NAMEとEMAILは使う環境に合わせて変更する。

$ docker run  \
  --rm \
  -p 80:80 -p 443:443 \
  -v letsencrypt:/etc/letsencrypt \
  certbot/certbot:latest certonly \
    --non-interactive --agree-tos \
    --domain DOMAIN_NAME --email EMAIL \
    --standalone

これでインスタンス内に証明書が保存される。

更新時は webroot に変えて cron を使ってやれば良い。

注意

volume の指定を -v /etc/letsencrypt:/etc/letsencrypt のようにホストの /etc/ に保存させてしまうと、インスタンス再起動時になくなってしまう。
これは Container-Optimized OS のセキュリティのため。詳しくはこちら

Webアプリケーション起動 & cloudsql proxy との接続

docker-compose を使えるようにする

Container-Optimized OS ではデフォルトで docker-compose は入っていない。

docker 経由で docker-compose を行えるようにする方法が公式に記載されている。

なお、ドキュメントの docker-compose イメージのバージョンが相当古いのでここで最新のバージョンを確認すると良い。

# エイリアス作成
$ echo alias docker-compose="'"'docker run --rm \
    -v /var/run/docker.sock:/var/run/docker.sock \
    -v "$PWD:/rootfs/$PWD" \
    -w="/rootfs/$PWD" \
    docker/compose:1.22.0'"'" >> ~/.bashrc

# 反映
$ source ~/.bashrc

docker-compose で他のコンテナを構築

Container Registry のプライベートイメージをpullするにはちょっとだけ手間が必要。

GCEインスタンス作成時に指定すると持ってきてくれるけど、構築しにくかったので手動で持ってくるようにする。

詳細はここ参照。

以下のコマンドを実行すると、Container Registry にアクセスするための JSON ファイルが生成される

$ docker-credential-gcr configure-docker

事前に docker pull でイメージを取得しておく。(ここでいう docker-compose は実質 docker 内での挙動なので先程の JSON を見てくれないっぽい?)

あとは docker-compose で起動

docker-compose.yml と nginx の設定ファイルは ここ に置いた。

docker-compose.ymlと同じディレクトリに .env ファイルを作成して、 NGINX_CONFIG_PATH と INSTANCE_CONNECTION_NAME を指定する。

※ NGINX_CONFIG_PATH を指定する理由は docker イメージ内の docker-compose で動くからかわからないが、パスがずれるので絶対パスで指定すると動いたため。

cloudsql proxy との接続

docker-compose.yml 内の

app:
  volumes:
      - /mnt/stateful_partition/cloudsql:/cloudsql
gce-proxy:
    container_name: gce-proxy
    image: gcr.io/cloudsql-docker/gce-proxy
    command: /cloud_sql_proxy -dir=/cloudsql -instances=${INSTANCE_CONNECTION_NAME}
    volumes:
      - /mnt/stateful_partition/cloudsql:/cloudsql

の部分で UNIX ソケット通信している。 volume で /mnt/stateful_partition/cloudsql としているのは、 /cloudsql とするとインスタンス再起動時に初期化されてしまうため。

ロギング

少しハマったので別記事にまとめる。

完成

これで GAE っぽい雰囲気を出しつつロードバランサーを抜いて GCE の金額のみで運用できそうな気がする。