Cloud Run サービス内の gcloud コマンドラインのチュートリアル

このチュートリアルでは、Cloud Run サービス内の Google Cloud CLI を使用して、Cloud Run サービスのインベントリを作成します。このチュートリアルで学んだことを既存の Cloud オペレーション スクリプトに適用することや、クライアント ライブラリを使用してより堅牢なサービスを構築する前に概念実証を作成することもできます。

Shell クイックスタートに示されているように、ウェブサービス内のシェル スクリプトと同様に、gcloud CLI を使用します。Cloud Run では、どちらのツールも Cloud Run サービス ID で自動的に認証することで Google Cloudサービスと連携します。サービス ID に付与されている権限はすべて gcloud CLI で使用できます。

gcloud CLI は、 Google Cloud 全体で情報の収集とリソース管理を幅広く行えるため、ウェブサービス内でこのツール使用することにより、呼び出し元がこれらの機能を誤って使用するリスクを最小限に抑えられます。セキュリティ制御を使用しない場合、偶発的または意図的な悪意のある活動を許可することにより、同じプロジェクト内で稼働している他のサービスまたはリソースに対してリスクが生じる場合があります。リスクの例として、以下が挙げられます。

  • プライベート仮想マシンの IP アドレスの検出が可能になる。
  • 同じプロジェクト内のデータベースからプライベート データへのアクセスが可能になる。
  • 他の稼働中のサービスの削除が可能になる。

たとえば、ユーザー入力として開いたままにする代わりに、コードで実行される gcloud コマンドを指定するように、このチュートリアルのいくつかの手順は、リスクを最小化するために制御を強制する方法を示しています。

Cloud Run サービス内でコマンドライン ツールを使用したスクリプト記述は、ローカルでコマンドラインを使用する場合と似ています。主な違いは、主要なスクリプト ロジックの周囲に追加する、追加の制限です。

gcloud のデフォルトを設定する

Cloud Run サービスを gcloud のデフォルトに構成するには:

  1. デフォルト プロジェクトを設定します。

    gcloud config set project PROJECT_ID

    PROJECT_ID は、このチュートリアルで作成したプロジェクトの名前に置き換えます。

  2. 選択したリージョン向けに gcloud を構成します。

    gcloud config set run/region REGION

    REGION は、任意のサポートされている Cloud Run のリージョンに置き換えます。

Cloud Run のロケーション

Cloud Run はリージョナルです。つまり、Cloud Run サービスを実行するインフラストラクチャは特定のリージョンに配置され、そのリージョン内のすべてのゾーンで冗長的に利用できるように Google によって管理されます。

レイテンシ、可用性、耐久性の要件を満たしていることが、Cloud Run サービスを実行するリージョンを選択する際の主な判断材料になります。一般的には、ユーザーに最も近いリージョンを選択できますが、Cloud Run サービスで使用されている他の Google Cloudプロダクトのロケーションも考慮する必要があります。 Google Cloud プロダクトを複数のロケーションで使用すると、サービスのレイテンシだけでなく、コストにも影響を及ぼす可能性があります。

Cloud Run は、次のリージョンで利用できます。

ティア 1 料金を適用

  • asia-east1(台湾)
  • asia-northeast1(東京)
  • asia-northeast2(大阪)
  • asia-south1(ムンバイ、インド)
  • europe-north1(フィンランド) リーフアイコン 低 CO2
  • europe-north2(ストックホルム) リーフアイコン 低 CO2
  • europe-southwest1(マドリッド) リーフアイコン 低 CO2
  • europe-west1(ベルギー) リーフアイコン 低 CO2
  • europe-west4(オランダ) リーフアイコン 低 CO2
  • europe-west8(ミラノ)
  • europe-west9(パリ) リーフアイコン 低 CO2
  • me-west1(テルアビブ)
  • northamerica-south1(メキシコ)
  • us-central1(アイオワ) リーフアイコン 低 CO2
  • us-east1(サウスカロライナ)
  • us-east4(北バージニア)
  • us-east5(コロンバス)
  • us-south1(ダラス) リーフアイコン 低 CO2
  • us-west1(オレゴン) リーフアイコン 低 CO2

ティア 2 料金を適用

  • africa-south1(ヨハネスブルグ)
  • asia-east2(香港)
  • asia-northeast3(ソウル、韓国)
  • asia-southeast1(シンガポール)
  • asia-southeast2 (ジャカルタ)
  • asia-south2(デリー、インド)
  • australia-southeast1(シドニー)
  • australia-southeast2(メルボルン)
  • europe-central2(ワルシャワ、ポーランド)
  • europe-west10(ベルリン)
  • europe-west12(トリノ)
  • europe-west2(ロンドン、イギリス) リーフアイコン 低 CO2
  • europe-west3(フランクフルト、ドイツ)
  • europe-west6(チューリッヒ、スイス) リーフアイコン 低 CO2
  • me-central1(ドーハ)
  • me-central2(ダンマーム)
  • northamerica-northeast1(モントリオール) リーフアイコン 低 CO2
  • northamerica-northeast2(トロント) リーフアイコン 低 CO2
  • southamerica-east1(サンパウロ、ブラジル) リーフアイコン 低 CO2
  • southamerica-west1(サンティアゴ、チリ) リーフアイコン 低 CO2
  • us-west2(ロサンゼルス)
  • us-west3(ソルトレイクシティ)
  • us-west4(ラスベガス)

Cloud Run サービスをすでに作成している場合は、Google Cloud コンソールの Cloud Run ダッシュボードにリージョンが表示されます。

サンプルコードを取得する

使用するコードサンプルを取得するには:

  1. ローカルマシンにサンプルアプリのリポジトリのクローンを作成します。

    git clone https://github.com/GoogleCloudPlatform/cloud-run-samples.git

    または、zip 形式のサンプルをダウンロードし、ファイルを抽出してもかまいません。

  2. Cloud Run のサンプルコードが含まれているディレクトリに移動します。

    cd cloud-run-samples/gcloud-report/

コードを確認する

このセクションには、取得したコードサンプルに関する情報が含まれています。

レポートを生成して Cloud Storage にアップロードする

このシェル スクリプトは、現在のプロジェクトとリージョンで Cloud Run サービスのレポートを生成し、結果を Cloud Storage にアップロードします。名前に指定した文字列 search 引数を持つサービスをリストします。

このスクリプトは、gcloud run services list コマンド、gcloud 高度なフォーマット オプションgcloud ストリーミング転送コピーモードを使用します。

set -eo pipefail

# Check for required environment variables.
requireEnv() {
  test "${!1}" || (echo "gcloud-report: '$1' not found" >&2 && exit 1)
}
requireEnv GCLOUD_REPORT_BUCKET

# Prepare formatting: Default search term to include all services.
search=${1:-'.'}
limits='spec.template.spec.containers.resources.limits.flatten("", "", " ")'
format='table[box, title="Cloud Run Services"](name,status.url,metadata.annotations.[serving.knative.dev/creator],'${limits}')'

# Create a specific object name that will not be overridden in the future.
obj="gs://${GCLOUD_REPORT_BUCKET}/report-${search}-$(date +%s).txt"

# Write a report containing the service name, service URL, service account or user that
# deployed it, and any explicitly configured service "limits" such as CPU or Memory.
gcloud run services list \
  --format "${format}" \
  --filter "metadata.name~${search}" | gsutil -q cp -J - "${obj}"

# /dev/stderr is sent to Cloud Logging.
echo "gcloud-report: wrote to ${obj}" >&2
echo "Wrote report to ${obj}"

このスクリプトを繰り返し呼び出すことでレポートが更新されるため、さらにコスト高となるチャーンが生じず、サービスとして実行しても安全です。新しい Cloud リソースの作成やコストのかかるタスクの実行など、gcloud CLI を使用する他のスクリプトは、何度も呼び出されると費用が高くなる場合があります。べき等のスクリプト(繰り返し呼び出しても同じ結果を生成するスクリプト)はサービスとして実行するほうが安全です。

HTTP リクエストでスクリプトを呼び出す

この Go のコードは、シェル スクリプトを実行してレポートを生成するウェブサービスを設定します。検索クエリはユーザー入力であるため、入力として悪意のあるコマンドを防ぐために、文字、数字、ハイフンのみが含まれように、コードによって入力が検証されます。この文字セットは、コマンド インジェクション攻撃を防ぐのに十分制限されています。

ウェブサービスは、引数として検索パラメータをシェル スクリプトに渡します。


// Service gcloud-report is a Cloud Run shell-script-as-a-service.
package main

import (
	"log"
	"net/http"
	"os"
	"os/exec"
	"regexp"
)

func main() {
	http.HandleFunc("/", scriptHandler)

	// Determine port for HTTP service.
	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
		log.Printf("defaulting to port %s", port)
	}

	// Start HTTP server.
	log.Printf("listening on port %s", port)
	if err := http.ListenAndServe(":"+port, nil); err != nil {
		log.Fatal(err)
	}
}

func scriptHandler(w http.ResponseWriter, r *http.Request) {
	search := r.URL.Query().Get("search")
	re := regexp.MustCompile(`^[a-z]+[a-z0-9\-]*$`)
	if !re.MatchString(search) {
		log.Printf("invalid search criteria %q, using default", search)
		search = "."
	}

	cmd := exec.CommandContext(r.Context(), "/bin/bash", "script.sh", search)
	cmd.Stderr = os.Stderr
	out, err := cmd.Output()
	if err != nil {
		log.Printf("Command.Output: %v", err)
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		return
	}
	w.Write(out)
}

go.mod ファイルは、go モジュールでアプリケーションの依存関係を宣言します。

module github.com/GoogleCloudPlatform/cloud-run-samples/gcloud-report

go 1.19

コンテナ環境を定義する

Dockerfile は、環境がどのようなものかをサービス向けに定義するものです。最終コンテナ イメージが gcloud Google Cloud CLI イメージに基づいている点を除いて、helloworld-shell クイックスタートの Dockerfile に似ています。これにより、Google Cloud CLI のカスタム インストールと構成の手順なしで、gcloud をサービスで使用できるようになります。


# Use the official golang image to create a binary.
# This is based on Debian and sets the GOPATH to /go.
# https://hub.docker.com/_/golang
FROM golang:1.20-buster as builder

# Create and change to the app directory.
WORKDIR /app

# Retrieve application dependencies.
# This allows the container build to reuse cached dependencies.
# Expecting to copy go.mod and if present go.sum.
COPY go.* ./
RUN go mod download

# Copy local code to the container image.
COPY invoke.go ./

# Build the binary.
RUN go build -mod=readonly -v -o server

# Use a gcloud image based on debian:buster-slim for a lean production container.
# https://docs.docker.com/develop/develop-images/multistage-build/#use-multi-stage-builds
FROM gcr.io/google.com/cloudsdktool/cloud-sdk:slim

WORKDIR /app

# Copy the binary to the production image from the builder stage.
COPY --from=builder /app/server /app/server
COPY *.sh /app/
RUN chmod +x /app/*.sh

# Run the web service on container startup.
CMD ["/app/server"]

Artifact Registry 標準リポジトリを作成する

コンテナ イメージを保存する Artifact Registry 標準リポジトリを作成します。

gcloud artifacts repositories create REPOSITORY \
    --repository-format=docker \
    --location=REGION

次のように置き換えます。

  • REPOSITORY は、リポジトリの一意の名前に置き換えます。
  • REGION は、Artifact Registry の Google Cloud リージョンに置き換えます。

Cloud Storage バケットを設定する

レポートをアップロードする Cloud Storage バケットを作成します。

gcloud storage buckets create gs://REPORT_ARCHIVE_BUCKET

REPORT_ARCHIVE_BUCKET は、グローバルに一意のバケット名に置き換えます。

サービス ID を設定する

サービスが持っている他のインフラストラクチャに対する権限を制限するには、サービス ID を作成し、作業に必要な特定の IAM 権限をカスタマイズします。

その場合、必要な権限は Cloud Run サービスを読み取る権限と、Cloud Storage バケットとの間で読み書きを行う権限です。

  1. サービス アカウントを作成します。

    gcloud iam service-accounts create gcloud-report-identity

  2. サービス アカウントに Cloud Run サービスの読み取り権限を付与します。

    gcloud projects add-iam-policy-binding PROJECT_ID \
      --member=serviceAccount:gcloud-report-identity@PROJECT_ID.iam.gserviceaccount.com \
      --role roles/run.viewer
  3. Cloud Storage バケットに対して読み書きを行う権限をサービス アカウントに付与します。

    gcloud storage buckets add-iam-policy-binding gs://REPORT_ARCHIVE_BUCKET \
      --member=serviceAccount:gcloud-report-identity@PROJECT_ID.iam.gserviceaccount.com \
      --role=roles/storage.objectUser

このカスタマイズされたサービス ID のアクセス制限により、サービスが他の Google Cloud リソースにアクセスすることを防止します。

サービスを公開する

配布コードは次の 3 つのステップで構成されます。

  • Cloud Build でコンテナ イメージを作成します。
  • コンテナ イメージを Artifact Registry にアップロードします。
  • コンテナ イメージを Cloud Run にデプロイします。

コードを配布するには:

  1. コンテナをビルドして、Artifact Registry に公開します。

    gcloud builds submit --tag REGION-docker.pkg.dev/PROJECT_ID/REPOSITORY/gcloud-report

    次のように置き換えます。

    • PROJECT_ID: 実際の Google Cloud プロジェクト ID。
    • REPOSITORY は、Artifact Registry リポジトリの名前に置き換えます。
    • REGION は、Artifact Registry の Google Cloud リージョンに置き換えます。

    gcloud-report は、サービスの名前です。

    成功すると、SUCCESS メッセージに ID、作成時間、イメージの名前が表示されます。イメージは Artifact Registry に保存され、必要に応じて再利用できます。

  2. 次のコマンドを実行して、サービスをデプロイします。

    gcloud run deploy gcloud-report \
       --image REGION-docker.pkg.dev/PROJECT_ID/REPOSITORY/gcloud-report \
       --update-env-vars GCLOUD_REPORT_BUCKET=REPORT_ARCHIVE_BUCKET \
       --service-account gcloud-report-identity \
       --no-allow-unauthenticated

    次のように置き換えます。

    • PROJECT_ID は、使用する Google Cloud プロジェクト ID に置き換えます。
    • REPOSITORY は、Artifact Registry リポジトリの名前に置き換えます。
    • REGION は、サービスの Google Cloud リージョンに置き換えます。

    gcloud-report は、コンテナ名とサービス名の一部です。コンテナ イメージは、gcloud を設定するで構成したサービスとリージョン(Cloud Run)にデプロイされます。

    --no-allow-unauthenticated フラグは、サービスへの公開アクセスを制限します。サービスを非公開にすることで、Cloud Run の組み込み認証を利用して不正なリクエストをブロックできます。Identity and Access Management(IAM)に基づく認証の詳細については、IAM を使用したアクセスの管理をご覧ください。

    デプロイが完了するまで待ちます。これには 30 秒ほどかかる場合があります。正常に完了すると、コマンドラインにサービス URL が表示されます。

  3. サービスにコードの更新をデプロイする場合は、上記のステップを繰り返します。サービスをデプロイするたびに、リビジョンが作成されます。準備ができると、トラフィックの送信が自動的に開始します。

Google Cloud ユーザーにこのサービスの呼び出し権限を付与する方法については、IAM を使用してアクセスを管理するをご覧ください。プロジェクト編集者とオーナーには、自動的にこの権限が付与されます。

レポートを生成する

Cloud Run サービスのレポートを生成するには:

  1. curl を使用して認証済みリクエストを送信します。

    curl -H "Authorization: Bearer $(gcloud auth print-identity-token)" SERVICE_URL

    SERVICE_URL は、デプロイの完了後に Cloud Run で提示された URL に置き換えます。

    新しいプロジェクトを作成して、このチュートリアルに沿って操作した場合、出力は次のようになります。

    Wrote report to gs://REPORT_ARCHIVE_BUCKET/report-.-DATE.txt

    ファイル名の . は、ソースコードに記載されているデフォルトの検索引数です。

    検索結果の機能を使用するには、リクエストに search 引数を追加します。

    curl -H "Authorization: Bearer $(gcloud auth print-identity-token)" SERVICE_URL?search=gcloud

    このクエリは次のような出力を返します。

    Wrote report to gs://REPORT_ARCHIVE_BUCKET/report-gcloud-DATE.txt
  2. gcloud CLI を使用してローカルでファイルを取得します。

    gcloud storage cp gs://REPORT_FILE_NAME .

    コマンド内の . は、現在の作業ディレクトリを意味します。

    REPORT_FILE_NAME は、前の手順で出力した Cloud Storage オブジェクト名に置き換えます。

ファイルを開いてレポートを確認します。次のようになります。

4 つのサービス属性の列を含む、プロジェクト内の Cloud Run サービスのリストのスクリーンショット。
4 つの列はサービスの説明から取得されます。これには、サービスの名前、最初のデプロイに割り当てられた URL、サービスの最初の作成者、サービスの最大の CPU とメモリの上限 を使用します。

将来に向けた堅牢性の改善

このサービの開発をさらに進める場合は、より堅牢なプログラミング言語で書き直し、Cloud Run Admin APICloud Storage クライアント ライブラリの使用を検討してください。

gcloud CLI コマンドに --log-http を追加すると、行われている API 呼び出しを調査できます(一部の認証の詳細も確認できます)。

このオペレーションを自動化する

これで、Cloud Run サービスのレポートを HTTP リクエストによってトリガーできるようになりました。レポートが必要なときは、レポートの生成に以下の自動化を使用します。