🙌

Cloud Run 待望のマルチコンテナ構成を検証しました

2023/05/17に公開

Cloud Run でマルチコンテナ機能がプレビューで利用可能に GA になりました(2023年11月)!

長らく待ち望まれていたCloud Run のマルチコンテナが、パブリックプレビュー GAになりました!!
Cloud Run の第一世代、第二世代ともに利用が可能です。
Update: Google Cloud コンソールからマルチコンテナが設定できるようになりました。

ドキュメントはこちらです。

マルチコンテナのサポートにより、複数のコンテナを使いながら

  • ロギング、モニタリング、トレーシング
  • Nginx や Envoy、Apache2 などのリバースプロキシ
  • 認証、フィルタの追加
  • データベースのプロキシ

など、ユースケースが柔軟に広がることが期待できます。

より具体的な例として PHP を使ったアプリケーション は、Nginx と PHP-FPM の組み合わせが多いため、恩恵が大きいのではないかと思います。同様に Nginx と Ruby on Rails(Unicorn)などの例も多いですね。
今回は PHP を動作させるアプリケーションサーバとして、Nginx、PHP-FPM 及び Cloud Spanner PostgreSQL Interface のコネクタである PgAdapter の 3つをマルチコンテナとし、1つの Cloud Run のサービスとして試しつつ動作を確認しました。

今回試している新機能

マルチコンテナに関連する機能はもちろん、マルチコンテナならではのコントロールが必要になる部分もあります。

  • コンテナのヘルスチェック(startup probe)
  • コンテナ間の依存性
  • リソース制限
  • 共有ボリューム
    なども設定して一緒に試しています。

設定


今回の構成

試した内容はこちらのリポジトリにまとめていますので、必要に応じてご確認ください。
(※ デプロイした Cloud Run サービスは誰でもアクセス可能ですので注意。)

現時点でマルチコンテナは Google Cloud コンソールでは設定ができないため、YAML で定義し、下記のようなコマンドで Cloud Run にデプロイします。

gcloud run services replace YAMLファイル --region=デプロイするリージョン

構成ファイルの YAML を貼ってみます。
YAML のリファレンスはこちらを参照してください。

apiVersion: serving.knative.dev/v1
kind: Service
metadata: 
  annotations: 
    run.googleapis.com/launch-stage: BETA
  name: my-php-sample
spec: 
  template: 
    metadata: 
      annotations: 
        run.googleapis.com/execution-environment: gen1 #or gen2
        run.googleapis.com/container-dependencies: '{"proxy":["pgadapter", "php"]}'
    spec: 
      containers: 
      - image: asia-northeast1-docker.pkg.dev/${GOOGLE_CLOUD_PROJECT}/my-app/my-nginx
        name: proxy
        ports: 
        - containerPort: 80
        volumeMounts: 
        - mountPath: /var/share
          name: share
      - image: gcr.io/cloud-spanner-pg-adapter/pgadapter
        name: pgadapter
        args:
        - "-p ${GOOGLE_CLOUD_PROJECT}"
        - "-i test-instance"
        - "-d game"
        - "-x"
        startupProbe:
          tcpSocket:
            port: 5432
          initialDelaySeconds: 5
          timeoutSeconds: 1
          failureThreshold: 10
          periodSeconds: 3
        resources: 
          limits: 
            cpu: "1"
            memory: 256Mi
        volumeMounts: 
        - mountPath: /var/share
          name: share
      - image: asia-northeast1-docker.pkg.dev/${GOOGLE_CLOUD_PROJECT}/my-app/my-php
        name: php
        resources: 
          limits: 
            cpu: "1"
            memory: 512Mi
        volumeMounts: 
        - mountPath: /var/share
          name: share
      volumes:
      - name: share
        emptyDir: 
          medium: Memory
          sizeLimit: 8Mi

※ Cloud Run の構成ファイルでは変数が直接利用できないので、検証では Makefile で置き換えて適用しています

コンテナ3つ分にリソース制限やヘルスチェックの設定を追加していますので少し長くなってしまっていますね。
Kubernetes の Deployment に近い記述になっています。

ポイントをいくつか解説します。

公開するコンテナのポートは1コンテナのみ

図にもありますが、外部からリクエストを受け付けるコンテナは、Ingress コンテナ と呼ばれており Cloud Run のサービスと呼ばれるデプロイ単位に付きひとつです。
ここでは Nginx コンテナがそれにあたります。
その他のコンテナは、サイドカーコンテナと呼ばれています。

コンテナ間の通信とファイルの共有

それぞれのコンテナが内部で listen しているポートについては、コンテナ間でアクセスできます。
1つのコンテナインスタンスで localhost を共有しますので、localhost:5432 のような形で宛先を指定します。

検証した構成では、PHP は PgAdapter が listen している TCP の 5432 ポートにアクセスして、
バックエンドの Cloud Spanner に接続しています。

また、Kubernetes 同様、共有メモリボリュームでコンテナ間でファイル共有も可能です。
今回は共有メモリボリュームの検証のため PHP-FPM を unix ドメインソケットで listen するよう変更しており、共有メモリボリューム内の /var/share/php-fpm.sock に配置しています。
Ingress コンテナである Nginx からそのソケットファイルを利用することで、PHP のアプリケーションサーバとして動作させています。
(サイズを書かないと容量制限がかからないため、ここでは仮に8MiBとしています。)

依存関係とヘルスチェック

マルチコンテナならではの設定として、依存関係の制御があります。
例えばコンテナ Aが起動する前に、コンテナ Bが起動したら困る、という場合ですね。

今回の構成でも、PHP-FPM と PgAdapter が Ready にならないと、Nginx が起動してもリクエストを処理できません。
つまり Nginx(ここでは 'proxy')は、これら2つのコンテナに依存します。
そのため下記のような設定を入れています。

        run.googleapis.com/container-dependencies: '{"proxy":["pgadapter", "php"]}'

さらに PgAdapter は起動からリクエスト処理可能になるまで数秒〜10数秒の時間がかかりますので、その接続の確認をもって Ready としなくてはいけません。
startup probe という機能で、PgAdapter の listen ポートである TCP 5432の状態をチェックしています。(containerPort が設定されていると自動で設定されますが、最大試行回数や間隔を変更するため明示しています。)
閾値など条件を満たせないと、コンテナインスタンスとして起動失敗となります。
注意点としては、接続の確認はコンテナインスタンスの外部から行われるということです。
ここでは外部からのチェックを可能にするため、PgAdapter に "-x" オプションを引数として渡して起動しています。

なお今回は設定していませんが、HTTP や gRPC のエンドポイントがあれば liveness probe が設定可能で、コンテナに問題が発生したときに再起動することができます。
これらの機能については、こちらを御覧ください。

リソース制限

追加でマルチコンテナの際に特に考えるべきこととして、リソース制限があります。
Cloud Run ではコンテナインスタンスに、CPU や メモリなどを割り当てますが、マルチコンテナの場合は複数のコンテナがそれらのリソースを共有します。
ここでは特にメモリなどの使用率が高い PHP-FPM と PgAdapter に制限をいれています。(ここでの制限値は適当です。)

例えばメモリの制限を超過した場合、そのコンテナは終了し、コンテナインスタンス自体が再起動することになります。

Memory limit of 640 MiB exceeded with 652 MiB used. Consider increasing the memory limit, see https://cloud.google.com/run/docs/configuring/memory-limits

まとめ

誰でも利用可能となった Cloud Run のマルチコンテナと関連機能を紹介しました。

現在プレビューですので本番への投入はおすすめできませんが、魅力的な機能ですね。
検証していただきつつ、GA を楽しみにお待ち下さい。

追記: 同僚の Yoshi さんが OpenTelemetry Collector をサイドカーとして使うブログを書かれています。
併せて確認ください。
https://zenn.dev/google_cloud_jp/articles/20230516-cloud-run-otel

Google Cloud Japan

Discussion