Cloudflare Tunnelを使うと、パブリックIPやポート開放なしにKubernetesクラスタ上のサービスをインターネットに公開できます。 今回はCloudflare Tunnelの作成から、cloudflare-tunnel-ingress-controllerを使ってKubernetesのIngressリソースで自動的にトンネル経由の公開を行う設定までをまとめました。

Cloudflare Tunnelの仕組み

通常、クラスタ内のサービスを外部に公開するにはパブリックIPの取得やルーターのポート開放が必要です。Cloudflare Tunnelを使うとクラスタ側からCloudflareへアウトバウンド接続を張るだけで、Cloudflare経由で外部からのアクセスを受けられるようになります。

cloudflare-tunnel-ingress-controllerを使うと、KubernetesのIngressリソースを作成するだけで自動的にCloudflare Tunnelのルーティングが設定されます。通常のIngress Controllerと同じ使い勝手でトンネル経由の公開が可能です。

Cloudflare Tunnelの作成

Cloudflareダッシュボードからトンネルを作成します。

ネットワーク > Tunnels に移動し、「トンネルを作成」をクリックします。

トンネル名を入力します。ここでは kkato.app としました。

トンネルが作成されると、環境のセットアップ画面が表示されます。ここに表示されるトンネルトークンを後ほど使います。

作成が完了するとトンネルの概要が表示されます。

Cloudflare APIトークンの作成

cloudflare-tunnel-ingress-controllerがCloudflare APIを操作するためのトークンを作成します。

CloudflareダッシュボードのAPIトークンページにアクセスし、「Create Token」から「Create Custom Token」を選択します。

以下の権限を設定します。

  • Account / Cloudflare Tunnel / Edit
  • Zone / DNS / Edit

シークレットをGCP Secret Managerに登録

External Secrets Operatorを使ってGCP Secret ManagerからKubernetesのSecretを自動生成する構成にしています。以下の3つのシークレットを登録します。

echo -n "<your-api-token>" | gcloud secrets create cloudflare-api-token --data-file=-
echo -n "<your-account-id>" | gcloud secrets create cloudflare-account-id --data-file=-
echo -n "<your-tunnel-name>" | gcloud secrets create cloudflare-tunnel-name --data-file=-

アカウントIDはCloudflareダッシュボードのURLやトンネルトークンのデコード結果から確認できます。

ExternalSecretの作成

GCP Secret Managerから取得したシークレットをKubernetes Secretとして同期するExternalSecretを定義します。

apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
  name: cloudflare-credentials
  namespace: cloudflare-tunnel-ingress-controller
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: cluster-secret-store
    kind: ClusterSecretStore
  target:
    name: cloudflare-credentials
  data:
    - secretKey: api_token
      remoteRef:
        key: cloudflare-api-token
    - secretKey: account_id
      remoteRef:
        key: cloudflare-account-id
    - secretKey: tunnel_name
      remoteRef:
        key: cloudflare-tunnel-name

cloudflare-tunnel-ingress-controllerのインストール

Argo CD Applicationとして定義します。Helm Chartとvalues.yaml、そしてExternalSecretを含むGitリポジトリを組み合わせたマルチソース構成です。

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: cloudflare-tunnel-ingress-controller
  namespace: argocd
spec:
  project: default
  sources:
    - repoURL: https://helm.strrl.dev
      chart: cloudflare-tunnel-ingress-controller
      targetRevision: 0.0.22
      helm:
        valueFiles:
          - $values/addons/cloudflare-tunnel-ingress-controller/values.yaml
    - repoURL: git@github.com:kkato/k8s
      targetRevision: main
      ref: values
    - repoURL: git@github.com:kkato/k8s
      targetRevision: main
      path: addons/cloudflare-tunnel-ingress-controller
  destination:
    server: https://kubernetes.default.svc
    namespace: cloudflare-tunnel-ingress-controller
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
      - CreateNamespace=true
      - ServerSideApply=true

ServerSideApply=true を指定しています。これはCRDなど大きなリソースで metadata.annotations のサイズ上限(262144バイト)を超える問題を回避するためです。

values.yamlでは、トンネル名とシークレットの参照を設定します。

cloudflare:
  tunnelName: kkato.app
  secretRef:
    name: cloudflare-credentials
    accountIDKey: account_id
    tunnelNameKey: tunnel_name
    apiTokenKey: api_token

Ingressリソースでサービスを公開する

cloudflare-tunnel-ingress-controllerが動作していれば、ingressClassName: cloudflare-tunnel を指定したIngressリソースを作成するだけでサービスが公開されます。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-app
spec:
  ingressClassName: cloudflare-tunnel
  rules:
    - host: my-app.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: my-app
                port:
                  number: 80

Ingressを適用するとcloudflare-tunnel-ingress-controllerが自動的にCloudflare Tunnel側のルーティングを設定してくれます。DNSレコードもCloudflareに自動登録されるため、追加の設定は不要です。