HAProxy レイヤ 7 ロード バランシングのリファレンス実装

Google Distributed Cloud(GDC)エアギャップにはマネージド レイヤ 4(L4)ロードバランサが組み込まれていますが、多くのエンタープライズ アプリケーションでは、ホストベースのルーティング、一元化された TLS 管理、複雑なトラフィック分割などの高度なレイヤ 7(L7)機能が必要です。これまで、これは Ingress API を使用して実現されてきましたが、Kubernetes コミュニティでは現在、機能がフリーズされています。

このリファレンス アーキテクチャは、セルフマネージドのレイヤ 7 ロード バランシング ソリューションを提供します。GDC 標準クラスタに一般的な HAProxy オープンソース コントローラをデプロイすることで、お客様は L7 トラフィックをハイブリッド環境にシームレスに転送できます。このアーキテクチャでは、TLS 終端(HTTPRoute)を使用して、Server Name Indication(SNI)に基づいて、組み込みのコンテナ化された Pod と外部の仮想マシンでホストされているアプリケーションの両方にトラフィックを転送します。

アーキテクチャ

GDC エアギャップでの HAProxy レイヤ 7 ロード バランシングのアーキテクチャ図。

ソリューションの主なコンポーネントは次のとおりです。

  • クライアント: アプリケーションとやり取りするために HTTPS リクエストを開始するエンティティ。
  • GDC 標準クラスタ: GDC には、Kubernetes Vanilla クラスタを作成する組み込みの方法が用意されています。このソリューションでは、クラスタは L7 LB とそのコントローラ、ワークロード、外部 VM のヘッドレス サービスをホストします。
  • GDC L4 ロードバランサ: エントリ ポイントとして機能する組み込みの L4 ロードバランサ。コントローラを実行する Kubernetes Pod に TCP/443 トラフィックを直接分散します。
  • Ingress コントローラ: 標準クラスタで実行されている HAProxy オペレーター。Ingress リソースをモニタリングし、基盤となるプロキシを動的に更新します。次の実装では HAProxy Ingress Controller が使用されます。
  • Ingress: 物理リスニング ポート(443)と、TLS 終端による SNI ベースのホスト ルーティング ルールを定義する標準化された Kubernetes リソース。
  • コンテナ化されたワークロード(Pod): 通常の Kubernetes Service を使用して内部的に公開される標準の Kubernetes Deployment。
  • VM ベースのワークロード(外部): プロジェクト ネットワーク上の外部 VM でホストされ、ヘッドレス Kubernetes Service と VM の直接 IP を含むカスタム エンドポイントを使用してプロキシに公開されるワークロード。
  • Harbor Registry: エアーギャップ環境でプロキシとアプリケーションのイメージを保存して提供するために使用される非公開コンテナ レジストリ。

標準クラスタで、次の 3 つの Namespace を作成します。

  • load-balancer Namespace には、HAProxy Ingress コントローラと HAProxy ロードバランサ ワークロードがホストされます。

    ロードバランサの Namespace リソース。

  • hello-app Namespace には、デモ コンテナ ワークロード用の DeploymentServiceIngress があります。

    hello-app Namespace のリソース。

  • vm-app Namespace には、外部 VM IP を公開するヘッドレス サービス、外部 IP を指す EndpointSliceIngress があります。

    vm-app Namespace リソース。

始める前に

このソリューションをデプロイする前に、次の前提条件を満たしていることを確認してください。

  • 必要なソフトウェア: helm、docker、kubectl
  • CLI ログインとローカル設定: GDC コンソールから gdcloud CLI をダウンロードし、ローカルで環境を設定します。

    export USER_NAME="USER_NAME"
    export PROJECT_ID="PROJECT_ID"
    export ZONE="ZONE"
    export ORG_NAME="ORG_NAME"
    export GDC_URL="GDC_URL"
    
    gdcloud components install gdcloud-k8s-auth-plugin
    
    gdcloud config set core/organization_console_url \
      https://console.$ORG_NAME.$ZONE.$GDC_URL
    gdcloud config set core/zone $ZONE
    gdcloud config set core/project ${PROJECT_ID}
    
    gdcloud auth login # use --login-config-cert option in case of TLS error
    
  • プロジェクトのセットアップ: GDC エアギャップ環境にプロジェクトを作成して、リソースを保持します。

    gdcloud projects create $PROJECT_ID
    
  • IAM ロール: ユーザーに クラスタ管理者ロールと 標準クラスタ管理者ロールを付与して Kubernetes リソースを管理し、Harbor インスタンス管理者ロールを付与してイメージを push します。

    # Grant standard cluster and cluster admin roles
    gdcloud projects add-iam-policy-binding ${PROJECT_ID} \
      --member="user:${USER_NAME}" \
      --role=cluster-admin
    
    gdcloud projects add-iam-policy-binding ${PROJECT_ID} \
      --member="user:${USER_NAME}" \
      --role=standard-cluster-admin
    
    # Grant Harbor instance admin role
    gdcloud projects add-iam-policy-binding ${PROJECT_ID} \
      --member="user:${USER_NAME}" \
      --role=harbor-instance-admin
    

標準クラスタを作成する

このセクションでは、GDC エアギャップ環境内に標準の Kubernetes クラスタを設定するプロセスについて説明します。標準クラスタは、HAProxy Ingress Controller やカスタム アプリケーションなど、さまざまなワークロードをデプロイするための柔軟で堅牢な基盤を提供します。次の手順を行うと、クラスタが正しく構成され、以降のデプロイで使用できるようになります。

  1. 使用可能な仮想マシン イメージタイプを特定するには、次のコマンドを実行します。

    gdcloud compute machine-types list
    
  2. クラスタ ワーカーノードに適したマシンタイプを選択します。このチュートリアルでは、4 つ以上の vCPU を備えたマシンタイプをおすすめします。

    export MACHINE_TYPE="MACHINE_TYPE"
    
  3. 管理 API サーバーの kubeconfig を取得して、エイリアスを設定します。

    export CLUSTER_NAME="CLUSTER_NAME"
    
    KUBECONFIG=kubeconfig-admin.yaml gdcloud clusters \
      get-credentials ${ORG_NAME}-admin
    
    alias km="kubectl --kubeconfig kubeconfig-admin.yaml"
    
  4. 2 つのワーカーノードを含む Standard クラスタを作成します。

    km create -f - <<EOF
    apiVersion: cluster.gdc.goog/v1
    kind: Cluster
    metadata:
      name: ${CLUSTER_NAME}
      namespace: ${PROJECT_ID}
    spec:
      nodePools:
      - machineTypeName: ${MACHINE_TYPE}
        nodeCount: 2
        name: ${CLUSTER_NAME}-node-pool
    EOF
    

    使用可能なオプションの詳細については、ドキュメントをご覧ください。

    標準クラスタの作成が完了するまでに最大 60 分ほどかかることがあります。ステータスを確認するには、次のコマンドを使用します。

    km get clusters/${CLUSTER_NAME} \
      -n ${PROJECT_ID} \
      --watch
    

    クラスタの準備が整うと、出力に次のように Running 状態が表示されます。

    NAME         STATE     K8S VERSION
    my-cluster   Running   1.30.12-gke.300
    
  5. クラスタの準備ができたら、認証情報を取得します。

    KUBECONFIG=kubeconfig-${CLUSTER_NAME}.yaml gdcloud clusters \
      get-credentials ${CLUSTER_NAME} \
      --standard \
      --project ${PROJECT_ID} \
      --zone ${ZONE}
    
  6. このガイドの残りの部分で kubectl コマンドを簡潔にするために、エイリアスを作成します。このエイリアスは、標準クラスタとのやり取りに使用されます。

    alias kk="kubectl --kubeconfig kubeconfig-${CLUSTER_NAME}.yaml"
    
  7. コントローラ、「hello-app」デモ コンテナ化アプリ、VM ベースのデモアプリの Namespace を作成します。

    kk create namespace load-balancer
    kk create namespace hello-app
    kk create namespace vm-app
    

Harbor レジストリを作成して統合する

Harbor は、GDC エアギャップでサポートが組み込まれているコンテナ イメージ レジストリです。このセクションでは、Harbor レジストリを標準クラスタに統合する手順について説明します。これには、イメージの安全な pull と push を有効にするための認証情報とシークレットの構成が含まれます。

  1. プロジェクトに Harbor インスタンスを作成します。
  2. Harbor インスタンスに Harbor プロジェクトを作成します。
  3. 環境変数を設定します。

    export HARBOR_INSTANCE_NAME="HARBOR_INSTANCE_NAME"
    export HARBOR_INSTANCE_URL="HARBOR_INSTANCE_URL"
    export HARBOR_PROJECT="HARBOR_PROJECT"
    export IMAGE_PULL_SECRET_NAME="harbor-secret"
    
  4. ロボット アカウントを使用して Harbor インスタンスにログインします。

    docker --config=./docker login ${HARBOR_INSTANCE_URL}
    
  5. Standard クラスタにシークレットを作成します。

    kk create secret docker-registry ${IMAGE_PULL_SECRET_NAME} \
      --from-file=.dockerconfigjson=./docker/config.json \
      -n load-balancer
    
    kk create secret docker-registry ${IMAGE_PULL_SECRET_NAME} \
      --from-file=.dockerconfigjson=./docker/config.json \
      -n hello-app
    

デモ コンテナ化アプリをデプロイする

このセクションでは、GDC エアギャップ Kubernetes クラスタ内でのデモ コンテナ化アプリケーション(hello-app)のデプロイについて詳しく説明します。hello-app を実行し、クラスタ内で内部的に公開するために必要な Kubernetes Deployment と Service リソースを作成し、L7 ロードバランサを使用してアクセスできるように準備します。

  1. デモ用のコンテナ化されたアプリのサンプル画像を Harbor にアップロードします。

    docker pull gcr.io/google-samples/hello-app:1.0 \
      --platform linux/amd64
    docker tag gcr.io/google-samples/hello-app:1.0 \
      ${HARBOR_INSTANCE_URL}/${HARBOR_PROJECT}/hello-app:1.0
    docker --config=./docker push \
      ${HARBOR_INSTANCE_URL}/${HARBOR_PROJECT}/hello-app:1.0
    
  2. 次のマニフェストを Standard クラスタにデプロイします。

    cat << EOF > hello-app.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: hello-app
      namespace: hello-app
    spec:
      replicas: 2
      selector:
        matchLabels:
          app: hello-app
      template:
        metadata:
          labels:
            app: hello-app
        spec:
          containers:
          - name: hello-server
            image: ${HARBOR_INSTANCE_URL}/${HARBOR_PROJECT}/hello-app:1.0
            ports:
            - containerPort: 8080
          imagePullSecrets:
          - name: ${IMAGE_PULL_SECRET_NAME}
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: hello-app
      namespace: hello-app
    spec:
      type: ClusterIP
      selector:
        app: hello-app
      ports:
        - protocol: TCP
          port: 80
          targetPort: 8080
    EOF
    
    kk apply -f hello-app.yaml
    

次に、Deployment と Service が存在することを確認します。

kk get svc,deploy -n hello-app

VM にデモアプリをデプロイする

このセクションでは、Kubernetes クラスタ外の仮想マシン(VM)内でのデモ アプリケーションのデプロイについて詳しく説明します。VM に HTTP サーバーを設定することで、ロードバランサが公開できる外部アプリケーションをシミュレートし、クラスタの内外のリソースへのトラフィックを管理する機能を示します。

まず、デモアプリの VM を作成します。

  1. ウェブブラウザで GDC コンソールを開きます。
  2. 標準 Kubernetes クラスタを作成したプロジェクトと同じプロジェクトを選択します。
  3. メニューを開き、[仮想マシン] をクリックします。
  4. [インスタンスを作成] をクリックします。
  5. VM に vm-workload という名前を付けます。この例では、2 vCPU のイメージで十分です。
  6. ブートディスク イメージには、Python がプリインストールされている Ubuntu 22.04 ディストリビューションを選択します。
  7. [作成] をクリックします。
  8. VM の準備が整うまで数分待ちます。
  9. VM への SSH 接続を確立します。
    1. GDC コンソールで、VM をクリックします。
    2. [SSH で接続] をクリックします。

SSH コンソールに接続したら、次のコマンドを実行します。

mkdir ~/simple-server
cd ~/simple-server
echo 'Welcome to my VM!' > index.html
python3 -m http.server --bind 0.0.0.0 8080 &

トラフィックを VM に転送するには、ヘッドレス Service(セレクタなし)を作成します。これは、EndpointSlice リソースを使用して VM の内部 IP アドレスに手動でマッピングされます。

kk apply -f - <<EOF
apiVersion: v1
kind: Service
metadata:
  name: vm-app-svc
  namespace: vm-app
spec:
  ports:
  - protocol: TCP
    port: 443
    targetPort: 443
EOF

次のコマンドを実行して、vm-workload VM の IP アドレスを取得します。

gdcloud compute instances list --project ${PROJECT_ID} \
  | grep workload-vm | awk '{print $3}'

出力は、EndpointSlice リソースの設定に必要な VM の IP アドレスになります。

VM-app-selector-less Service に接続し、トラフィックのルーティング先となる VM の IP アドレスを指定するリソース EndpointSlice を作成します。

apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
  name: vm-app-endpoints
  namespace: vm-app
  labels:
    kubernetes.io/service-name: vm-app-svc
addressType: IPv4
ports:
  - port: 8080
endpoints:
  - addresses:
    - "VM_IP"
    conditions:
      ready: true

自己署名証明書を作成する

このセクションでは、コンテナベースと VM ベースのアプリケーションの通信を保護するために、TLS 証明書と Kubernetes Secret を作成するプロセスについて説明します。このガイドでは、便宜上自己署名証明書を使用していますが、本番環境では、省略可: プロダクション レディ(な)証明書を使用するで説明されているように、本番環境グレードの証明書を使用する必要があります。これらのアプリには任意のサンプル ドメイン名を選択します。安全な接続を確立することで、HAProxy Ingress Controller を介してアプリケーションにアクセスするクライアントのデータの完全性と機密性を確保できます。

コンテナ化されたアプリの場合、自己署名証明書を作成し、ロードバランサの Namespace に Secret として保存します。これは、k8s-app.example.com をリクエストするときに TLS に使用されます。

openssl req -x509 -newkey rsa:2048 -nodes \
  -keyout tls-containerized.key \
  -out tls-containerized.crt \
  -subj "/CN=k8s-app.example.com" \
  -days 365

kk create secret tls tls-containerized \
  --namespace load-balancer \
  --key tls-containerized.key \
  --cert tls-containerized.crt

kk create secret tls tls-containerized \
  --namespace hello-app \
  --key tls-containerized.key \
  --cert tls-containerized.crt

VM アプリの場合も、同様の自己署名証明書が発行され、保存されます。これは、vm-app.example.com をリクエストするときに TLS に使用されます。

openssl req -x509 -newkey rsa:2048 -nodes \
  -keyout tls-vm.key \
  -out tls-vm.crt \
  -subj "/CN=vm-app.example.com" \
  -days 365

kk create secret tls tls-vm \
  --namespace load-balancer \
  --key tls-vm.key \
  --cert tls-vm.crt

kk create secret tls tls-vm \
  --namespace vm-app \
  --key tls-vm.key \
  --cert tls-vm.crt

HAProxy をデプロイする

HAProxy Ingress コントローラと L4 LB をインストールする

export HAPROXY_VERSION=3.1.14

# pull the HAProxy Ingress Controller image and push it to Harbor
docker pull haproxytech/kubernetes-ingress:${HAPROXY_VERSION} \
  --platform linux/amd64
docker tag haproxytech/kubernetes-ingress:${HAPROXY_VERSION} \
  ${HARBOR_INSTANCE_URL}/${HARBOR_PROJECT}/haproxy-ingress:${HAPROXY_VERSION}
docker --config=./docker push \
  ${HARBOR_INSTANCE_URL}/${HARBOR_PROJECT}/haproxy-ingress:${HAPROXY_VERSION}

# Get Helm repo
helm repo add haproxytech https://haproxytech.github.io/helm-charts
helm repo update

# Install the Ingress Controller with helm
helm upgrade --install haproxy-kubernetes-ingress \
  haproxytech/kubernetes-ingress \
  --kubeconfig kubeconfig-${CLUSTER_NAME}.yaml \
  --namespace load-balancer \
  --set controller.image.repository=${HARBOR_INSTANCE_URL}/${HARBOR_PROJECT}/haproxy-ingress \
  --set controller.image.tag=${HAPROXY_VERSION} \
  --set controller.existingImagePullSecret=${IMAGE_PULL_SECRET_NAME} \
  --set controller.service.type=LoadBalancer \
  --set-json \
  controller.service.annotations='{"networking.gke.io/load-balancer-type": "internal"}'

HAProxy Ingress Controller は、LoadBalancer タイプの Service を使用して、クライアント アクセス用の一意の仮想 IP アドレスを取得します。このサービスは、フルマネージドのレイヤ 4 ロードバランサを設定します。このガイドでは、簡単にするために、load-balancer-type アノテーションを internal に設定して内部ロードバランサを作成します。このアノテーションを省略すると、外部ロードバランサが作成されます。Kubernetes Deployment は、Harbor ロボット アカウントの認証情報を含む提供されたシークレット(${IMAGE_PULL_SECRET_NAME})を使用して、Harbor からイメージを安全に pull します。

HAProxy Ingress コントローラのインストールを検証する

HAProxy Ingress Controller の Pod が実行中で、準備ができていることを確認します。

kk get pods -n load-balancer

出力は次のようになります。

NAME                                          READY   STATUS      RESTARTS   AGE
haproxy-kubernetes-ingress-78dc9c8676-f8fcb   1/1     Running     0          35s
haproxy-kubernetes-ingress-78dc9c8676-lfnr2   1/1     Running     0          65s
haproxy-kubernetes-ingress-crdjob-3-tgj2h     0/1     Completed   0          65s

HAProxy Ingress Controller の Service が作成され、構成されていることを確認します。

kk get services -n load-balancer

出力は次のようになります。

NAME                         TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)                                                                  AGE
haproxy-kubernetes-ingress   LoadBalancer   10.252.27.46   10.252.4.17   80:32023/TCP,443:31103/TCP,443:31103/UDP,1024:30146/TCP,6060:30718/TCP   10m

デモアプリの Ingress リソースを定義する

HAProxy をコンテナ化されたアプリの Service に接続する Ingress リソースを作成する

cat << EOF > hello-app-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: hello-app-ingress
  namespace: hello-app
  annotations:
    haproxy.org/ssl-redirect: "true"
    haproxy.org/ssl-redirect-port: "443"
    haproxy.org/ssl-redirect-code: "308"
spec:
  ingressClassName: haproxy
  tls:
  - hosts:
    - "k8s-app.example.com"
    secretName: tls-containerized
  rules:
  - host: "k8s-app.example.com"
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: hello-app
            port:
              number: 80
EOF

kk apply -f hello-app-ingress.yaml

VM-app セレクタなしの Service に接続し、トラフィックのルーティング先となる VM の IP アドレスを指定する Ingress リソースを作成します。

cat << EOF > vm-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: vm-app-ingress
  namespace: vm-app
  annotations:
    haproxy.org/ssl-redirect: "true"
    haproxy.org/ssl-redirect-port: "443"
    haproxy.org/ssl-redirect-code: "308"
spec:
  ingressClassName: haproxy
  tls:
  - hosts:
    - "vm-app.example.com"
    secretName: tls-vm
  rules:
  - host: "vm-app.example.com"
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: vm-app-svc
            port:
              number: 443
EOF

kk apply -f vm-ingress.yaml

ロードバランサの IP アドレスを取得する

コマンドを実行して、ロードバランサの IP アドレスを取得します。

kk get services/haproxy-kubernetes-ingress \
  -n load-balancer \
  -o jsonpath='{.status.loadBalancer.ingress[0].ip}'

これは、アプリへのアクセスを確認する際に必要になります。これは LOAD_BALANCER_IP とします。

クライアント VM を作成する

クライアント VM を作成する手順は次のとおりです。

  1. ウェブブラウザで GDC コンソールを開きます。
  2. メニューを開き、[仮想マシン] をクリックします。
  3. [インスタンスを作成] をクリックします。
  4. client という名前の VM を作成し、小規模なマシンタイプを選択して、curl がプリインストールされている Rocky Linux または Ubuntu のいずれかを選択します。
  5. [作成] をクリックします。
  6. VM の準備が整うまで数分待ちます。
  7. VM の準備ができたら、VM との SSH 接続を確立します。
    1. GDC コンソールで、VM をクリックします。
    2. [SSH で接続] をクリックします。

アクセスとルーティングを確認する

ルーティングをテストするには、クライアント VM から curl コマンドを実行します。ロードバランサの IP アドレスを使用して、定義されたホスト名で両方のアプリケーションに接続できます。

curl で --resolve フラグを渡すことで、ドメイン名を GDC エアギャップ L4 ロードバランサの IP に強制的に解決できます。自己署名証明書を信頼するために -k フラグを渡すことに注意してください。

Kubernetes コンテナ化アプリをテストします。

curl -k --resolve k8s-app.example.com:443:$LOAD_BALANCER_IP https://k8s-app.example.com -v

外部 VM アプリをテストします。

curl -k --resolve vm-app.example.com:443:$LOAD_BALANCER_IP https://vm-app.example.com -v

正しく構成されている場合、Ingress コントローラは TLS ターミネータとしてシームレスに機能し、トラフィックを宛先にパススルーします。

省略可: プロダクション レディ(な)証明書を使用する

このセクションでは、GDC エアギャップ CA Service を活用してプライベート ルート認証局(CA)を作成し、ワークロードの署名付き証明書を発行し、GDC エアギャップ標準クラスタとクライアント VM を安全に更新する方法について説明します。

このセクションでは、GDC エアギャップ CA Service を使用してプライベート ルート認証局(CA)を作成し、アプリケーションの有効な証明書を発行する方法について説明します。このルート CA をクライアント VM にインストールすると、SSL 警告(curl -k の使用など)を回避することなく、TLS 終端が信頼できる証明書でシームレスに機能することを確認できます。

必要な権限を付与して認証情報を取得する

CA Service を管理して証明書を発行するには、ユーザーにプロジェクト内の適切な IAM ロールが必要です。

  1. certificate-authority-service-admin ロールと certificate-requester ロールを付与します。

    gdcloud projects add-iam-policy-binding ${PROJECT_ID} \
      --member=user:${USER_NAME} \
      --role=certificate-authority-service-admin
    
    gdcloud projects add-iam-policy-binding ${PROJECT_ID} \
      --member=user:${USER_NAME} \
      --role=certificate-requester
    
  2. 管理 API サーバーの認証情報を取得します。

    gdcloud clusters get-credentials ${ORG_NAME}-admin
    

ルート CA を作成する

プロジェクトの Namespace 内の管理 API サーバーに認証局を作成します。

  1. CertificateAuthority リソースを適用します。

    km apply -f - <<EOF
    apiVersion: pki.security.gdc.goog/v1
    kind: CertificateAuthority
    metadata:
      name: my-root-ca
      namespace: ${PROJECT_ID}
    spec:
      caProfile:
        commonName: "My Root CA"
        duration: 87600h # 10 years
        keyAlgorithm: RSA_2048
        maxChainLength: 1
      caType: ROOT
      keyLocation: HSM
      rotationPolicy:
        cronTime: 0 0 1 1 *
    EOF
    
    km -n ${PROJECT_ID} get \
    certificateauthority.pki.security.gdc.goog/my-root-ca -ojson \
    | jq -r '
    .status.conditions[] | select( .type as $id | "Ready" | index($id)) .status'
    

証明書を発行してデプロイする

CA の準備ができたら、コンテナ化されたアプリと VM ベースのアプリの両方の証明書をリクエストします。これらのリクエストは管理 API サーバーで行われ、生成された鍵は標準クラスタに移動する必要があります。

両方のドメインの作成リクエスト:

km apply -f - <<EOF
apiVersion: pki.security.gdc.goog/v1
kind: CertificateRequest
metadata:
  name: tls-containerized-req
  namespace: ${PROJECT_ID}
spec:
  certificateAuthorityRef:
    name: my-root-ca
    namespace: ${PROJECT_ID}
  certificateConfig:
    subjectConfig:
      commonName: "k8s-app.example.com"
      dnsNames:
      - "k8s-app.example.com"
  signedCertificateSecret: tls-containerized-signed
---
apiVersion: pki.security.gdc.goog/v1
kind: CertificateRequest
metadata:
  name: tls-vm-req
  namespace: ${PROJECT_ID}
spec:
  certificateAuthorityRef:
    name: my-root-ca
    namespace: ${PROJECT_ID}
  certificateConfig:
    subjectConfig:
      commonName: "vm-app.example.com"
      dnsNames:
      - "vm-app.example.com"
  signedCertificateSecret: tls-vm-signed
EOF

証明書が発行されるまでしばらく待ちます。Ready 条件が True の場合、準備ができていることを確認できます。

km get certificaterequests -n ${PROJECT_ID}

標準クラスタを更新する

このガイドの前のセクションに沿って操作した場合は、標準クラスタに自己署名シークレットがあります。新しい署名付きバージョンを作成する前に、これらを削除する必要があります。

kk delete secret tls-containerized -n load-balancer
kk delete secret tls-vm -n load-balancer

kk delete secret tls-containerized -n hello-app
kk delete secret tls-vm -n vm-app

次に、署名付き証明書を管理 API サーバーから抽出し、標準クラスタに新しい Secret を作成します。

km get secret -n ${PROJECT_ID} tls-containerized-signed \
  -o jsonpath='{.data.tls\.crt}' \
  | base64 -d > tls-containerized.crt

km get secret -n ${PROJECT_ID} tls-containerized-signed \
  -o jsonpath='{.data.tls\.key}' \
  | base64 -d > tls-containerized.key

kk create secret tls tls-containerized \
  --namespace load-balancer \
  --key tls-containerized.key \
  --cert tls-containerized.crt

kk create secret tls tls-containerized \
  --namespace hello-app \
  --key tls-containerized.key \
  --cert tls-containerized.crt

km get secret -n ${PROJECT_ID} tls-vm-signed \
  -o jsonpath='{.data.tls\.crt}' \
  | base64 -d > tls-vm.crt

km get secret -n ${PROJECT_ID} tls-vm-signed \
  -o jsonpath='{.data.tls\.key}' \
  | base64 -d > tls-vm.key

kk create secret tls tls-vm \
  --namespace load-balancer \
  --key tls-vm.key \
  --cert tls-vm.crt

kk create secret tls tls-vm \
  --namespace vm-app \
  --key tls-vm.key \
  --cert tls-vm.crt

新しいシークレットは自動的に取得され、ロードバランサによって更新されます。

クライアントの信頼を構成する

設定を確認するには、新しいルート CA を信頼するようにクライアント VM に指示する必要があります。

ルート CA 証明書をファイルに抽出します。

km get secret -n ${PROJECT_ID} my-root-ca-secret \
  -o jsonpath='{.data.tls\.crt}' \
  | base64 -d > my-root-ca.crt

証明書をクライアント VM に転送します。(my-root-ca.crt の内容をコピーして、クライアント VM のファイルに貼り付けることができます)。

クライアント VM で、トラスト ストアを更新します。

client VM が Ubuntu の場合:

sudo cp my-root-ca.crt /usr/local/share/ca-certificates/
sudo chmod 644 /usr/local/share/ca-certificates/my-root-ca.crt
sudo update-ca-certificates

client VM が Rocky Linux の場合:

sudo cp my-root-ca.crt /etc/pki/ca-trust/source/anchors/
sudo update-ca-trust

アクセス権の確認

これで、-k フラグなしで curl を使用してアプリケーションにアクセスできるようになりました。接続は完全に信頼されます。

k8s コンテナ化アプリをテストします。

curl -v --resolve k8s-app.example.com:443:LOAD_BALANDER_IP https://k8s-app.example.com

VM アプリをテストします。

curl -v --resolve vm.example.com:443:LOAD_BALANDER_IP https://vm-app.example.com

成功すると、SSL 証明書の警告なしでアプリケーションの出力がすぐに表示されます。