怖くないCloud Run

# はじめに

この記事ではGoogle Cloud Runの概要と基本的な使い方を紹介します。

Cloud Runの細かな説明は[公式ドキュメント](https://cloud.google.com/run/docs/overview/what-is-cloud-run?hl=ja)を読んでいただくこととして、ここではなるべくイメージをつかむための説明を心がけて行こうと思います。

この記事を通じて「簡単そうだな！」とか「Cloud Runなんて怖くない！」みたいな感想を持ってもらえたら嬉しいです。


# Cloud Runの概要

Cloud Runはコンテナ化したアプリをサーバにアップロード(以下「デプロイ」)し、HTTPリクエストやコマンドを通じてアプリを実行できるようにするサービスです。

また、Cloud Runはアプリの実行環境をアプリを動かす時だけ提供します。このためCloud Runは実際にリソースを使った分だけが課金対象になり余計なコストがかかりにくくなります。


# デプロイの種類

Cloud Runにデプロイするアプリは次の2種類に分かれます。

- Cloud Run Services

    HTTPリクエストやイベントをトリガーに動くアプリ。

    (使用例: Webサービス、RESTfull API など)
    <br>

- Cloud Run Jobs

    コマンドやイベントをトリガーに動くアプリ。

    (使用例: データバッチ処理、レポート生成 など)

以下は Cloud Run Services と Cloud Run Jobs の簡単な仕様比較です。

|項目|Cloud Run Services|Cloud Run Jobs|
|:--|:--|:--|
|実行環境|コンテナ|コンテナ|
|トリガー|HTTPリクエスト/イベント|コマンド/イベント|
|スケーリング|トラフィックの変動に応じて自動スケール|必要に応じてインスタンス数を手動設定|
|デプロイ数|最大1000|最大1000|
|リビジョン数|最大1000|最大1000|
|メモリ|512MiB～32GiB|512MiB～32GiB|
|CPU|1vCPU～8vCPU|1vCPU～8vCPU|
|処理時間|最大1時間|最大24時間|


# 演習の準備

## プロジェクトの作成

1. [Google Cloudダッシュボード](https://console.cloud.google.com/projectselector2/home/dashboard?hl=ja)を開き「プロジェクトを作成」を選択してください。

1. プロジェクトの作成画面が開いたらcloud-run-drillなどの名称で新規プロジェクトを作成してください。  
    ※ プロジェクトIDは重複が許されません。
    ※ 以後"cloud-run-drill"というIDは各自のプロジェクトIDに読み替えてください


## Google Cloud APIの有効化

1. Artifact Registry APIの有効化

    [Artifact Registry APIサービスの詳細ページ](https://console.cloud.google.com/apis/api/artifactregistry.googleapis.com/metrics?hl=ja)にアクセスして「有効にする」を選択してください。
    
    このAPIはGoogle Cloud CLIでコンテナイメージをプッシュする際などに使用します。

1. Cloud Run Admin APIの有効化

    [Cloud Run Admin APIサービスの詳細ページ](https://console.cloud.google.com/apis/api/run.googleapis.com/metrics?hl=ja)にアクセスして「有効にする」を選択してください。

    このAPIはCloud Runにアプリをデプロイしたりやデプロイを更新するのに使用します。

## ロールの割り当て

1. [IAMロールの設定画面](https://console.cloud.google.com/iam-admin/iam?hl=ja)で自分のアカウントに次のロールを付与してください。

    - Artifact Registry 管理者: 
        リポジトリを作成、管理するための管理者アクセス権。  
        演習用のリポジトリを作成するために使用。
        <br>

    - BigQueryジョブユーザー:
        ジョブを実行するためのアクセス権。
        (クエリをジョブとして実行する場合に使用)
        <br>
        
    - BigQueryデータ編集者:
        データセットのすべてのコンテンツを編集するためのアクセス権。  
        (データセット内にテーブルを作成する場合に使用)
        <br>
        
    - Cloud Run デベロッパー:
        すべてのCloud Runリソースに対する読み取りと書き込みアクセス権。  
        ジョブやサービスを作成するために使用。
        <br>
        
    - Cloud Run 起動元: 
        Cloud Runサービスを起動する権限。  
        ジョブやサービスを利用するために使用。
        <br>
        
    - サービスアカウントユーザ: 
        オペレーションをサービスアカウントとして実行する権限。
        (サービスアカウントを利用する場合に使用)
        <br>

    - ストレージフォルダ管理者:
        フォルダとオブジェクトに対する完全な制御権限。  
        (フォルダやオブジェクト(ファイル)の作成に使用)
        <br>
        
    - ログ閲覧者: 
        Cloud Runなどのログを確認する権限。  
        ジョブやサービスの実行ログを確認するために使用。
        <br>



# Cloud Run Jobsのデプロイ

それではいよいよ Cloud Run でジョブを作って行きましょう。


## Cloud Shell Editorを開く

1. [Cloud Shell Editor](https://shell.cloud.google.com/?hl=ja&show=ide&environment_deployment=ide)を開き、「Open Folder」から任意のディレクトリをワークスペースとして追加してください。

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3939665/c57131fd-9eea-b05f-0c35-66f08708d6cb.png)


    <details><summary>📝Cloud Shell Editorとは</summary>

    Cloud Shell EditorとはCloud Shellというクラウド上のシェル環境をブラウザでグラフィカルに操作するためのエディタアプリです。

    Cloud Shellの詳細については[公式ドキュメント](https://cloud.google.com/shell/docs?hl=ja)に譲りますが、Cloud ShellおよびCloud Shell Editorには Google Cloud CLI や Docker などの演習に必要なアプリがプリインストールされていて無料で使えるのでお試しには丁度いいかなと思います。

    </details>


## 作業ディレクトリの作成

1. 任意のディレクトリに run-python というディレクトリを作成して移動してください。

    ```sh
    mkdir run-python
    cd run-python
    ```

    ※ Cloud Shell Editorを開いていれば操作はGUIからもできますが、簡単のため操作の詳細はコマンドで補足します（以下略）
    <br>

## Dockerfileの作成

1. run-python ディレクトリ直下に Dockerfile という名前のファイルを作成します。

    ```sh
    cloudshell open Dockerfile
    ```
    <br>

1. Dockerfileを以下のように編集してください。

    ```dockerfile
    FROM python:3.11-slim

    COPY . /job

    WORKDIR /job

    RUN pip install --upgrade pip

    ENV TZ=Asia/Tokyo

    CMD ["python", "main.py"]
    ```

    - FROM: コンテナのベースイメージを記述  
    - COPY: 資材のコピー元(.)とコピー先(/job)のディレクトリを指定  
    - WORKDIR: 作業ディレクトリを指定（cdコマンドのようなもの）  
    - RUN: コンテナ上で実行するコマンドを記述  
    - ENV: コンテナ上に登録する環境変数を記述  
    - CMD: コンテナ起動時に実行されるコマンドを記述  


## アプリの作成

1. run-python ディレクトリ直下に main.py という名前のファイルを作成します。

    ```sh
    cloudshell open main.py
    ```
    <br>

2. main.pyの内容を以下のように編集してください。

    ```python
    import sys

    def main():
        for arg in sys.argv:
            print(arg)

    if __name__ == "__main__":
        main()
    ```
    <br>

## Dockerイメージのビルド

1. Cloud Runにデプロイするジョブをコンテナイメージとしてビルドします。

    ```sh
    docker build -t run-python .
    ```
    <br>

## Dockerイメージのプッシュ

1. 次のコマンドを実行してcloud-run-drillプロジェクトへの認証を対話的に進めてください。

    ```sh
    gcloud init
    ```
    <br>

1. Artifact Registoryにリポジトリを作成していない場合は以下のコマンドでリポジトリを作成します。

    ```sh
    gcloud artifacts repositories create sample-repo \
    --repository-format=docker \
    --location=asia-northeast1 \
    --description="Cloud Run Sample Repository" \
    --project="cloud-run-drill"
    ```
    - create sample-repo: "sample-repo"という名前のリポジトリを作成
    - --location: コンテナイメージのアップロード先リージョン  
    - --description: リポジトリの説明文(任意)  
    - --project: コンテナイメージのアップロード先プロジェクト  
    <br><br>

1. コンテナイメージのアップロード先リージョンへの接続認証を行います。

    ```sh
    gcloud auth configure-docker asia-northeast1-docker.pkg.dev
    ```
    <br>

1. コンテナイメージにタグ付けます。

    ```sh
    docker tag run-python:latest \
    asia-northeast1-docker.pkg.dev/cloud-run-drill/sample-repo/run-python:latest
    ```
    ※ Artifact RegistryのURIは以下のようなフォーマットになっています。  
        「リージョン名-docker.pkg.dev/プロジェクト名/リポジトリ名/イメージ名:タグ名」
    <br><br>

1. Artifact Registryにコンテナイメージをプッシュします。

    ```sh
    docker push asia-northeast1-docker.pkg.dev/cloud-run-drill/sample-repo/run-python:latest
    ```


## ジョブの作成
1. Cloud Run ジョブ を作成します。

    ```sh
    gcloud run jobs create run-python \
    --image asia-northeast1-docker.pkg.dev/cloud-run-drill/sample-repo/run-python:latest \
    --region=asia-northeast1 \
    --tasks=1 \
    --cpu=1 \
    --memory=512Mi \
    --max-retries=0 \
    --parallelism=1 \
    --task-timeout=300
    ```

    - create run-python: "run-python"という名前のジョブを作成
    - --image: アプリのコンテナイメージを指定
    - --region: アプリのデプロイ先リージョンを指定
    - --tasks: 並行稼働させるVMインスタンスの数を指定
    - --cpu: 1つのVMインスタンスに割り当てるVCPU数を指定
    - --memory: 1つのVMインスタンスに割り当てるメモリ容量を指定
    - --max-retries: ランタイムエラーが発生した場合の再試行回数を指定
    - --parallelism: ある時点で同時に実行されるタスクの最大数を指定
    - --task-timeout: タスクのタイムアウト時間(秒)を指定
    <br>


## ジョブの実行

1. Cloud Run ジョブを実行します。

    ```sh
    gcloud run jobs execute run-python --region=asia-northeast1
    ```
    <br>

1. ログを確認しましょう。

    ジョブの呼び出しが完了するとターミナルに "Or visit https://..." と表示されるので、そこに記載されているURLをブラウザで開いてください。

<br>


## クリーンアップ

Cloud Run や Artifact Registry は無償ではないので、今後使用しないリソースは以下の手順でクリーンアップしておきましょう。

### ジョブの削除
1. ジョブの一覧を確認します。

    ```sh
    gcloud run jobs list
    ```
    <br>

1. ジョブを削除します。

    ```sh
    gcloud run jobs delete run-python --project cloud-run-drill --region asia-northeast1
    ```
    <br>

1. ジョブが削除されていることを確認してください。

    ```sh
    gcloud run jobs list
    ```
    <br>


### イメージの削除
1. イメージの一覧を確認します。

    ```sh
    gcloud artifacts docker images list \
    asia-northeast1-docker.pkg.dev/cloud-run-drill/sample-repo
    ```
    <br>

1. イメージを削除します。

    ```sh
    gcloud artifacts docker images delete \
    asia-northeast1-docker.pkg.dev/cloud-run-drill/sample-repo/run-python
    ```
    <br>

1. イメージが削除されていることを確認してください。

    ```sh
    gcloud artifacts docker images list \
    asia-northeast1-docker.pkg.dev/cloud-run-drill/sample-repo
    ```


# Cloud Run Servicesのデプロイ

続いてCloud Runでサービスもデプロイしてみましょう。


## 作業ディレクトリの作成

1. 任意のディレクトリに hello-app というディレクトリを作成して移動ください。

    ```sh
    mkdir hello-app
    cd hello-app
    ```


## Dockerfileの作成

1. hello-app ディレクトリ直下に Dockerfile という名前のファイルを作成します。

    ```sh
    cloudshell open Dockerfile
    ```
    <br>

1. Dockerfileの内容を以下のように編集してください。

    ```dockerfile
    FROM python:3.11-slim

    COPY . /app

    WORKDIR /app

    RUN pip install --upgrade pip
    RUN pip install --no-cache-dir -r requirements.txt

    ENV TZ=Asia/Tokyo

    CMD ["python", "main.py"]
    ```
    <br>


## アプリの作成

1. hello-app ディレクトリ直下に main.py という名前のファイルを作成します。

    ```sh
    cloudshell open main.py
    ```
    <br>

2. main.pyの内容を以下のように編集してください。

    ```python
    import os
    from flask import Flask

    app = Flask(__name__)

    @app.route("/")
    def hello_world():
        return f"Hello, Cloud Run!\n"

    if __name__ == "__main__":
        app.run(debug=True, host="0.0.0.0", port=int(os.environ.get("PORT", 8080)))
    ```
    <br>

## requirements.txtの作成

1. hello-app ディレクトリ直下に requirements.txt という名前のファイルを作成します。

    ```sh
    cloudshell open requirements.txt
    ```
    <br>

1. requirements.txtの内容を以下のように編集してください。

    ```txt
    Flask==3.0.3
    ```
    ※ コンテナ上のPythonにインストールするライブラリの一覧を記述します
    <br>


## Dockerイメージのビルド

1. Cloud Runにデプロイするサービスをコンテナイメージとしてビルドします。

    ```sh
    cd hello-app
    docker build -t hello-app .
    ```
    <br>

## Dockerイメージのプッシュ

1. コンテナイメージにタグ付けます。

    ```sh
    docker tag hello-app:latest \
    asia-northeast1-docker.pkg.dev/cloud-run-drill/sample-repo/hello-app:latest
    ```
    <br>

1. Artifact Registryにコンテナイメージをプッシュします。

    ```sh
    docker push asia-northeast1-docker.pkg.dev/cloud-run-drill/sample-repo/hello-app:latest
    ```
    <br>


## サービスの作成
1. Cloud Runのサービスを作成する

    ```sh
    gcloud run deploy hello-app \
    --image asia-northeast1-docker.pkg.dev/cloud-run-drill/sample-repo/hello-app:latest \
    --no-allow-unauthenticated \
    --region=asia-northeast1 \
    --cpu=1 --memory=512Mi \
    --min-instances=0 \
    --max-instances=1 \
    --timeout=10 \
    --concurrency=1
    ```

    - deploy hello-app: "hello-app"という名前のサービスをデプロイする
    - --no-allow-unauthenticated：承認済みのクライアントからのみ実行許可する
    - --min-instances: 最小インスタンス数
    - --max-instances: 最大インスタンス数
    - --timeout: タイムアウト時間(秒)
    - --concurrency: 1インスタンスあたりの最大同時リクエスト数
    <br>


## サービスの実行

1. サービスのエントリポイントを確認します。

    ```sh
    # サービスの一覧を表示
    gcloud run services list

    # サービスの詳細を表示
    gcloud run services describe hello-app --region=asia-northeast1
    ```

    **※「URL」欄に表示さるURLがエンドポイントです**
    <br>


1. アクセストークンを取得する

    今回は認証済みのユーザしかサービスを実行できないようにしているため、リクエストに認証情報(トークン)を載せる必要があります。

    以下のコマンドでトークンを取得しておきましょう。

    ```sh
    # TOKEN変数に現在認証しているアカウントのアクセストークンを代入
    TOKEN=$(gcloud auth print-identity-token)
    ```
    <br>

1. サービスを実行する

    事前に取得したエンドポイントとアクセストークンを使ってCurlコマンドでサービスを実行してみます。

    ```sh
    curl -H "Authorization: Bearer $TOKEN" https://hello-app-xxxxxxxx.a.run.app
    ```

    ※ "https..."から始まるURLはエンドポイントのURLと置き換えてください  
    ※ コマンドラインに「Hello, Cloud Run!」と表示されればテストは成功です！
    <br>


## クリーンアップ

### サービスの削除
1. サービスの一覧を確認します。

    ```sh
    gcloud run services list
    ```
    <br>

1. サービスを削除します。

    ```sh
    gcloud run services delete hello-app --project cloud-run-drill --region asia-northeast1
    ```
    <br>

1. サービスが削除されていることを確認します。

    ```sh
    gcloud run services list
    ```
    <br>

### イメージの削除
1. イメージの一覧を確認します。

    ```sh
    gcloud artifacts docker images list \
    asia-northeast1-docker.pkg.dev/cloud-run-drill/sample-repo
    ```
    <br>

1. イメージを削除します。

    ```sh
    gcloud artifacts docker images delete \
    asia-northeast1-docker.pkg.dev/cloud-run-drill/sample-repo/hello-app
    ```
    <br>

1. イメージが削除されていることを確認します。

    ```sh
    gcloud artifacts docker images list \
    asia-northeast1-docker.pkg.dev/cloud-run-drill/sample-repo
    ```