以下の記事でGoogle App EngineにTwitter Botをデプロイしましたが、GAEの制約(HTTPリクエストが10分間来なかったらインスタンスがシャットダウンされる)を知らなかったため、24時間常に起動していてほしいBotのデプロイ先としては適していないことがわかりました。
GitHub Actions と Google App Engine でCI/CD

今回は、そのリベンジとしてGCEにデプロイして、問題なく動かすことができました。
CI/CDの自動化まで行ったので、手順を記録します。

ゴール

  • GCEにコンテナ化したアプリケーションをデプロイして動かすことができる
  • デプロイを自動化する


アプリケーションのDocker化

以下の記事に別途まとめました。
TypeScriptで作ったアプリケーションをコンテナで実行する

まずは手動でGCEにコンテナをデプロイしてみる

GCP Container Registory にイメージをpushする

ローカル環境でビルドしたDockerイメージをContainer Registoryにpushします。
次のステップで、GCEのインスタンスをたてるときにイメージを指定するためです。

ローカルのイメージにレジストリ名をタグ付けします。ホストはasia, イメージ名にはappを指定しました。

docker tag [IMAGE_NAME] asia.gcr.io/[PROJECT-ID]/app


タグ付けされたイメージをContainer Registoryにpushします。

docker push HOSTNAME/PROJECT-ID/IMAGE


GCEインスタンスにコンテナをデプロイする

Google Cloud Consoleから、Compute Engineの管理画面に行き、「インスタンスを作成」→「コンテナイメージをデプロイ」を選択します。
コンテナイメージには、先程pushしたイメージを指定します。
また、環境変数もここで設定可能です。
マシンタイプには「f1-micro」を指定しました。f1-microには無料枠があるので、1台までなら無料で使用できるようです。
ここまでできたら、「作成」をクリックでインスタンスが作成されます。
ちなみに、「コンテナイメージをデプロイ」を選択すると、Compute EngineのOSには、Docker コンテナの実行に最適化されたContainer-Optimized OSというOSが自動で選択されます。

以上で、手動で単一のコンテナをGCEにデプロイすることができました。

デプロイを自動化する

GitHub Actions を使って、masterブランチへのpush毎に、イメージのビルド・push、GCEのコンテナの更新を行うようにします。
buildのジョブでイメージのビルド・pushを行います。
deployのジョブでコンテナの更新を行います。
gcloud compute instances update-container コマンドで、インスタンスの停止→再起動と、起動時に新しいイメージをダウンロードしてコンテナの起動まで行ってくれます。

name: CI/CD

on:
  push:
    branches: [master]

env:
  IMAGE: asia.gcr.io/dev-article-twitter-bot/app:${{ github.sha }}

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        os: [ubuntu-latest]
        node-version: [14.x]
    steps:
      - name: checkout the repository
        uses: actions/checkout@v2

      - name: Use Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v1
        with:
          node-version: ${{ matrix.node-version }}

      - name: yarn install, and test
        run: |
          yarn
          yarn test
        env:
          CI: true

  build:
    runs-on: ubuntu-latest
    steps:
      - name: checkout the repository
        uses: actions/checkout@v2

      - name: set up Cloud SDK
        uses: google-github-actions/setup-gcloud@master
        with:
          project_id: ${{ secrets.GCP_PROJECT_ID }}
          service_account_key: ${{ secrets.GCP_SA_KEY }}
          export_default_credentials: true

      - name: configure docker to use the gcloud cli
        run: gcloud auth configure-docker --quiet

      - name: build a docker image
        run: docker build . -t $IMAGE

      - name: push the docker image
        run: docker push $IMAGE

  deploy:
    needs: [test, build]
    runs-on: ubuntu-latest
    steps:
      - name: set up Cloud SDK
        uses: google-github-actions/setup-gcloud@master
        with:
          project_id: ${{ secrets.GCP_PROJECT_ID }}
          service_account_key: ${{ secrets.GCP_SA_KEY }}
          export_default_credentials: true

      - name: set project
        run: gcloud config set project $GCP_PROJECT_ID
        env:
          GCP_PROJECT_ID: ${{ secrets.GCP_PROJECT_ID }}

      - name: deploy to GCE
        run: gcloud compute instances update-container $GCP_PROJECT_ID --container-image $IMAGE --zone=us-west1-b
        env:
          GCP_PROJECT_ID: ${{ secrets.GCP_PROJECT_ID }}


以上でデプロイの自動化までできました!

できあがったもの

dev.toの公開記事取得APIを使用して、リプライで受け取った単語を検索キーワードとし、ヒットした記事のタイトルとリンクをリアルタイムにリプライするTwitter Botです。
よかったら使ってみてください。
https://twitter.com/dev_reply_bot

リポジトリ

https://github.com/EringiV3/dev-article-reply-twitter-bot

感想

gcloudコマンドがとても便利で、デプロイの自動化も特に詰まることなくできました。
コマンド一つでGCEインスタンス上のコンテナ更新できるの凄いです。
ただ、GCEにデプロイできるコンテナは1つまでらしいので、複数のコンテナをデプロイする必要がある場合はGoogle Kubernetes Engineを使用してくださいと公式のドキュメントに書いてありました。
GCPのドキュメントは丁寧かつ知識のない人でも読みやすいようにできていて、これからもGCPを積極的に使いたいという気持ちになります。
GAE, GCEにデプロイするワークフローの作成を体験できたので、次なにかデプロイするときはCloud Runを使ってみたいです。
常時起動しておく必要があるアプリケーションの場合は、GAEは向かず、GCEのほうが適していることはわかりましたが、HTTPリクエストのたびに起動すればよいアプリケーションの場合、GAEとCloud Runどちらが適しているか、手を動かして理解したくなりました。

参考

VM と MIG へのコンテナのデプロイ
イメージの push と pull