この記事は、CyberAgent Developers Advent Calendar 2025 19 日目の記事です。

こんにちは。株式会社 WinTicket の@dora1998です。
Platform チームに所属し、現在は Cloudflare Zero Trust の導入に向けて検証を進めています。

今回は、Cloud Run Worker Pools で cloudflared を立て、内部のみに公開している Cloud Run へ簡単に接続できたという検証のまとめとなります。

背景

Cloudflare Access (以下 Access)は、Cloudflare の Zero Trust 製品群の 1 つで、社内アプリケーションを安全に公開するプロキシの役割を持っています。
Access で社内アプリケーションを接続する方法としては、主に以下の 2 種類が考えられます (参考)。

  1. Cloudflare Tunnel (以下 Tunnel)経由で接続する
  2. 外部からアクセスできる CNAME, A レコードを設定する

Cloudflare Accessで社内アプリケーションを接続する方法2種類

一般的に外部から到達可能なエンドポイントがある時点で、攻撃の対象となる可能性があります。そのため、できるだけ 1 の Tunnel 経由で接続するのが好ましいと考えました。

課題1. cloudflared をどこに置くか?

前述の Tunnel 経由でのアクセスを実現するには、アプリケーションサーバーと同じネットワーク上に cloudflared デーモンを配置する必要があります。
Cloudflare 公式では、KubernetesCompute Engineでセットアップする方法が案内されています。

しかし、社内アプリケーションは主に Cloud Run でホスティングされており、ある Google Cloud プロジェクトでは GKE の利用予定もありませんでした。
また、このために Compute Engine を新たに保守するのも運用コストが懸念になりました。

解決1. Cloud Run Worker Poolsに cloudflared を置く

そこで、Cloud Run Worker Pools (以下 Worker Pool)を活用します。Worker Pool は、Cloud Run の実行モデルの 1 つで、今年パブリックプレビューとなったばかりの新しい実行モデルです。既にある Service, Job とは以下のように用途が分かれています。

  • Service: HTTP アプリケーション
  • Job: Scheduler や Pub/Sub などをトリガーにしたジョブ実行
  • Worker Pool: Pub/Sub のサブスクライバーなどのデーモン処理

なお、Worker Pool の詳細は以下 2 つの資料が詳しく、本検証の際にも大変参考にさせていただきました。

HTTP エンドポイントが必要なく、常時起動に向いている Worker Pool なら cloudflared のデプロイにも向いていそうです。
Worker Pool は VPC に接続できるため、VPC 経由で内部公開の Cloud Run サービスにもアクセスできると考えました。

※2025/12/18 時点では、Worker Pool はプレビュー版であることに注意が必要です

課題2. 内部公開のCloud Runにどうやってアクセスするか?

さて、次はデプロイした cloudflared から VPC を経由して内部のみに公開している Cloud Run Service にアクセスする必要があります。
Cloud Run 同士が VPC 経由で通信する方法には、以下の 2 種類があります (参考)。

  1. プライベート Google アクセス経由で .run.app のドメインを指定してアクセスする
  2. 内部アプリケーション ロードバランサか Private Service Connect を設定し、内部 IP アドレスを指定してアクセスする

しかし、アプリケーションごとにロードバランサ等を立てるのは手間なので、ここでは 1 のプライベート Google アクセス経由でアクセスする方法を選択しました。

Worker Pool 構築のポイント

以上を踏まえて、Worker Pool からプライベート Google アクセス経由で .run.app のドメインを指定してアクセスするために設定を行いました。
その際に、2 つほど詰まったポイントがあったので紹介します。

HTTP/2を指定する

cloudflared はデフォルトで QUIC (UDP)を用いて Cloudflare のネットワークに接続します。
しかし、Cloud Run から接続しようとすると、以下のようなエラーが出てしまい接続に失敗しました。

Failed to dial a quic connection error="failed to dial to edge with quic: timeout: no recent network activity" connIndex=0 event=0 ip=***.***.***.***

根本原因までは調査できていませんが直前に以下のようなメッセージが出ており、Cloud Run の仮想環境に起因するものと推測しました。

failed to sufficiently increase receive buffer size (was: 208 kiB, wanted: 7168 kiB, got: 416 kiB). See https://github.com/quic-go/quic-go/wiki/UDP-Buffer-Sizes for details.

QUIC (UDP)の接続に失敗するとフォールバックとして HTTP/2 (TCP)が使われますが、毎回最初に失敗してしまうため、 --protocol http2 を明示的に指定して対処しました。

プライベート IP へのリクエストのみを VPC にルーティングする

プライベート Google アクセス経由で *.run.app にアクセスしたい場合、すべてのトラフィックを VPC にルーティングするのが最も手軽です。
しかし、すべてのトラフィックを VPC に流すと、以下のようなエラーで Cloudflare への接続に失敗してしまいました。

Unable to establish connection with Cloudflare edge error="DialContext error: dial tcp ***.***.***.***:7844: i/o timeout" connIndex=0 event=0 ip=***.***.***.***

調べたところ、Cloud NAT を構成することでも解決できそうでしたが、今回はプライベート IP へのリクエストのみを VPC にルーティングすることで対応しました。
なお、その場合はプライベート DNS を構成して、*.run.appprivate.googleapis.com に解決するレコードの作成が必要です。

最終的なYAML

以上を踏まえて、最終的な Worker Pool の YAML の例を以下に示します。

apiVersion: run.googleapis.com/v1
kind: WorkerPool
spec:
  template:
    metadata:
      annotations:
        run.googleapis.com/vpc-access-egress: private-ranges-only
        run.googleapis.com/execution-environment: gen2
        run.googleapis.com/network-interfaces: '[{"network":"cloudflared-test","subnetwork":"cloudflared-test"}]'
    spec:
      containers:
        - name: cloudflared-1
          image: cloudflare/cloudflared:latest
          command:
            - cloudflared
          args:
            - tunnel
            - --no-autoupdate
            - --loglevel
            - info
            - --metrics
            - 0.0.0.0:2000
            - --protocol
            - http2
            - run
          env:
            - name: TUNNEL_TOKEN
              valueFrom:
                secretKeyRef:
                  key: "1"
                  name: cloudflared-token
          livenessProbe:
            initialDelaySeconds: 180
            timeoutSeconds: 1
            periodSeconds: 10
            failureThreshold: 1
            httpGet:
              path: /ready
              port: 2000

実際にアクセスしてみる

ここまでで cloudflared がようやく準備できました。残るは Cloudflare のコンソール上で設定するのみです。
下図のように「Published application routes」より追加すればアクセス可能になります。

Published application routesの設定画面

同一の Google Cloud プロジェクト内であれば、さらに Cloud Run サービスを増やした場合でも既存の cloudflared があればこのコンソールから簡単に接続できます。
一点気をつけるポイントとしては、「Additional application settings > HTTP Settings > HTTP Host Header」も設定して Host ヘッダを上書きする必要があります。

なお、先に Tunnel 側の設定をするといきなり全公開されてしまう仕様があるので、実務では Access アプリケーションを先に作成することを強く推奨します。
この仕様については、以下の SRG の記事が参考になります。

まとめ

新たに登場した Worker Pool を使うことで、Cloud Run メインのプロジェクトでも Tunnel 経由で安全に社内アプリケーションの公開が行えることがわかりました。
Slack ボットや GitHub のセルフホストランナーなど、Worker Pool の使い道は思ったよりも広そうなのでこれからも様々な用途を追求していきたいと思います。

アバター画像
2021年度新卒入社のWebフロントエンドエンジニアです。株式会社WinTicketでWINTICKET Webの開発に取り組んでいます。