APC 技術ブログ

株式会社エーピーコミュニケーションズの技術ブログです。

株式会社 エーピーコミュニケーションズの技術ブログです。

Application Gateway for ContainersでGateway APIを使う

はじめに

こんにちは!ACS事業部の谷合です。
先日Application Gateway for Containersプレビュー版の使用感の記事を、弊社吉川より(おそらく日本最速で)投稿しました。 techblog.ap-com.co.jp

実は、Application Gateway for ContainersではIngressの他にもGateway APIがサポートされています。
今回は(おそらく日本最速で)Application Gateway for ContainersでGateway APIを使う方法をご紹介します。
Application Gateway for Containers単体でIngressとGateway APIが使えるのはとっても便利ですよね。

Gateway APIって?

Application Gateway for Containersで個人的に素晴らしいなと思う点に、IngressとGateway APIのサポートあります。
Gateway APIはIngressの後継の位置付けて実装されており、以下の思想で実装されています。

gateway-api.sigs.k8s.io

  • Role-oriented - Gateway is composed of API resources which model organizational roles that use and configure Kubernetes service networking.
  • Portable - This isn't an improvement but rather something that should stay the same. Just as Ingress is a universal specification with numerous implementations, Gateway API is designed to be a portable specification supported by many implementations.
  • Expressive - Gateway API resources support core functionality for things like header-based matching, traffic weighting, and other capabilities that were only possible in Ingress through custom annotations.
  • Extensible - Gateway API allows for custom resources to be linked at various layers of the API. This makes granular customization possible at the appropriate places within the API structure.

今回はRole-orientedに着目してみます。
今までは、Ingressはnamespaceに閉じられており、異なるnamespaceにルーティングすることは正攻法では困難でした。
Gateway APIではこれを単体で解決することでマルチテナントを実現しています。

一先ずApplication Gateway for ContainersでGateway APIで、mTLS接続をば

以下のドキュメントを参考に検証を進めます。 learn.microsoft.com

早速実装してみましょう。
本手順は前回記事のBring Your Own (BYO)でのApplication Gateway for Containersデプロイを前提としています。
そのため、前回指定したResource GroupやResource Name、Frontend Nameを使用します。

まず、サンプルアプリをデプロイします。
この時、test-infra namespaceにアプリ、サービス、ConfigMap、Secret(証明書)がデプロイされます。

$ kubectl apply -f https://trafficcontrollerdocs.blob.core.windows.net/examples/https-scenario/end-to-end-ssl-with-backend-mtls/deployment.yaml
namespace/test-infra created
service/mtls-app created
deployment.apps/mtls-app created
configmap/mtls-app-nginx-cm created
secret/backend.com created
secret/frontend.com created
secret/gateway-client-cert created
secret/ca.bundle created

デプロイ確認してみましょう。

$ kubectl -n test-infra get all,secret,configmap
NAME                            READY   STATUS    RESTARTS   AGE
pod/mtls-app-5dd5c75d4f-bwp4w   1/1     Running   0          20s
pod/mtls-app-5dd5c75d4f-cpgd8   1/1     Running   0          20s

NAME               TYPE           CLUSTER-IP   EXTERNAL-IP     PORT(S)         AGE
service/mtls-app   LoadBalancer   10.0.210.6   4.156.162.222   443:30911/TCP   22s

NAME                       READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/mtls-app   2/2     2            2           21s

NAME                                  DESIRED   CURRENT   READY   AGE
replicaset.apps/mtls-app-5dd5c75d4f   2         2         2       21s

NAME                         TYPE                DATA   AGE
secret/backend.com           kubernetes.io/tls   2      21s
secret/ca.bundle             Opaque              1      19s
secret/frontend.com          kubernetes.io/tls   2      21s
secret/gateway-client-cert   kubernetes.io/tls   2      20s

NAME                          DATA   AGE
configmap/kube-root-ca.crt    1      25s
configmap/mtls-app-nginx-cm   1      23s

以降でGateway APIをデプロイしていきます。

まずは以下環境変数を設定してください。

$ RESOURCE_GROUP=rg-sample
$ RESOURCE_NAME='alb-sample'
$ RESOURCE_ID=$(az network alb show --resource-group $RESOURCE_GROUP --name $RESOURCE_NAME --query id -o tsv)
$ FRONTEND_NAME='frontend-sample'

次にGatewayリソースをデプロイします。
Gatewayリソースとは文字通り、Gatewayであり、外からの接続要求のFrontendとしての役割を持ちます。

$ kubectl apply -f - <<EOF
apiVersion: gateway.networking.k8s.io/v1beta1
kind: Gateway
metadata:
  name: gateway-01
  namespace: test-infra
  annotations:
    alb.networking.azure.io/alb-id: $RESOURCE_ID
spec:
  gatewayClassName: azure-alb-external
  listeners:
  - name: https-listener
    port: 443
    protocol: HTTPS
    allowedRoutes:
      namespaces:
        from: Same
    tls:
      mode: Terminate
      certificateRefs:
      - kind : Secret
        group: ""
        name: frontend.com
  addresses:
  - type: alb.networking.azure.io/alb-frontend
    value: $FRONTEND_NAME
EOF

kubectl get -oyamlコマンドを実行することで以下のような出力が確認できるはずです。

$ kubectl get gateway gateway-01 -n test-infra -o yaml
:
status:
  addresses:
  - type: IPAddress
    value: 4f264daf5a4666fb2142e1240a164e86.fz46.alb.azure.com
  conditions:
  - lastTransitionTime: "2023-07-27T08:05:21Z"
    message: Valid Gateway
    observedGeneration: 2
    reason: Accepted
    status: "True"
    type: Accepted
  - lastTransitionTime: "2023-07-27T08:05:21Z"
    message: Application Gateway for Containers resource has been successfully updated.
    observedGeneration: 2
    reason: Programmed
    status: "True"
    type: Programmed
  listeners:
  - attachedRoutes: 1
    conditions:
    - lastTransitionTime: "2023-07-27T08:05:21Z"
      message: ""
      observedGeneration: 2
      reason: ResolvedRefs
      status: "True"
      type: ResolvedRefs
    - lastTransitionTime: "2023-07-27T08:05:21Z"
      message: Listener is Accepted
      observedGeneration: 2
      reason: Accepted
      status: "True"
      type: Accepted
    - lastTransitionTime: "2023-07-27T08:05:21Z"
      message: Application Gateway for Containers resource has been successfully updated.
      observedGeneration: 2
      reason: Programmed
      status: "True"
      type: Programmed
    name: https-listener
    supportedKinds:
    - group: gateway.networking.k8s.io
      kind: HTTPRoute

次に、HTTPRouteリソースをデプロイします。
HTTPRouteリソースは、Gatewayリソースに紐づき、接続要求をルーティングします。
同じくGatewayリソースに紐づくListenerからリクエストをバックエンドに流します。

$ kubectl apply -f - <<EOF
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
  name: https-route
  namespace: test-infra
spec:
  parentRefs:
  - name: gateway-01
  rules:
  - backendRefs:
    - name: mtls-app
      port: 443
EOF

Gatewayリソースと同じくkubectl get -oyamlコマンドを実行することで以下のような出力があるはずです。
※数分かかる場合があります。

$ kubectl get httproute https-route -n test-infra -o yaml
:
status:
  parents:
  - conditions:
    - lastTransitionTime: "2023-07-27T08:05:21Z"
      message: ""
      observedGeneration: 1
      reason: ResolvedRefs
      status: "True"
      type: ResolvedRefs
    - lastTransitionTime: "2023-07-27T08:05:21Z"
      message: Route is Accepted
      observedGeneration: 1
      reason: Accepted
      status: "True"
      type: Accepted
    - lastTransitionTime: "2023-07-27T08:05:21Z"
      message: Application Gateway for Containers resource has been successfully updated.
      observedGeneration: 1
      reason: Programmed
      status: "True"
      type: Programmed
    controllerName: alb.networking.azure.io/alb-controller
    parentRef:
      group: gateway.networking.k8s.io
      kind: Gateway
      name: gateway-01

最後にBackendTLSPolicyリソースを作成します。
BackendTLSPolicyリソースは、HTTPRoute で参照されるバックエンド サービスのクライアント証明書と CA 証明書を持つバックエンド TLS ポリシーの作成を行います。

$ kubectl apply -f - <<EOF
apiVersion: alb.networking.azure.io/v1
kind: BackendTLSPolicy
metadata:
  name: mtls-app-tls-policy
  namespace: test-infra
spec:
  targetRef:
    group: ""
    kind: Service
    name: mtls-app
    namespace: test-infra
  default:
    sni: backend.com
    ports:
    - port: 443
    clientCertificateRef:
      name: gateway-client-cert
      group: ""
      kind: Secret
    verify:
      caCertificateRef:
        name: ca.bundle
        group: ""
        kind: Secret
      subjectAltName: backend.com
EOF

kubectl get -oyamlコマンドを実行してみましょう。

$ kubectl get backendtlspolicy -n test-infra mtls-app-tls-policy -o yaml
:
status:
  conditions:
  - lastTransitionTime: "2023-07-27T08:14:30Z"
    message: Valid BackendTLSPolicy
    observedGeneration: 1
    reason: Accepted
    status: "True"
    type: Accepted

これまでの作業で以下のような状況になっているはずです。

$ kubectl -n test-infra get pod,gateway,httproute,backendtlspolicy,secret
NAME                            READY   STATUS    RESTARTS   AGE
pod/mtls-app-5dd5c75d4f-bwp4w   1/1     Running   0          87m
pod/mtls-app-5dd5c75d4f-cpgd8   1/1     Running   0          87m

NAME                                           CLASS                ADDRESS                                               PROGRAMMED   AGE
gateway.gateway.networking.k8s.io/gateway-01   azure-alb-external   4f264daf5a4666fb2142e1240a164e86.fz46.alb.azure.com   True         73m

NAME                                              HOSTNAMES   AGE
httproute.gateway.networking.k8s.io/https-route               41m

NAME                                                           READY   AGE
backendtlspolicy.alb.networking.azure.io/mtls-app-tls-policy           38m

NAME                         TYPE                DATA   AGE
secret/backend.com           kubernetes.io/tls   2      21s
secret/ca.bundle             Opaque              1      19s
secret/frontend.com          kubernetes.io/tls   2      21s
secret/gateway-client-cert   kubernetes.io/tls   2      20s

これで準備が整いましたので、接続してみましょう。
問題なく接続できましたね!!

$ fqdn=$(kubectl get gateway gateway-01 -n test-infra -o jsonpath='{.status.addresses[0].value}')
$ curl --insecure https://$fqdn/
Hello World! 

namespace跨りのルーティングしてみる

namespace跨りのルーティングについては、以下の公式ドキュメントを参考に動かします。 gateway-api.f5se.io

ここからの手順は上記でデプロイしたリソースに変更を加えることで検証を行います。

まずは、GatewayリソースでmTLSを行う際に使用するfrontend用の証明書Secretのnamespaceを変更します。

$ k -n test-infra get secrets frontend.com -oyaml
# 別namespaceに変更
$ cat << EOT | k apply -f -
apiVersion: v1
data:
  tls.crt: LS0tLS1CRUdJTiBDR・・・ElGSUNBVEUtLS0tLQo=
  tls.key: LS0tLS1CRUdJTiBF・・・UFJJVkFURSBLRVktLS0tLQo=
kind: Secret
metadata:
  annotations:
    :
  creationTimestamp: "2023-07-27T07:25:58Z"
  name: frontend.com
  # test-infra → azure-alb-system
  namespace: azure-alb-system
  resourceVersion: "8807"
  uid: 2e8175e7-f92f-46cb-860a-6ec8061ddf55
type: kubernetes.io/tls
EOT

$ kubectl -n test-infra delete secrets frontend.com 

次に、Gatewayリソースをデプロイします。
この時以下の変更を行います。

  • .matadata.namespaceをSecretと同じnamespaceにする
  • .spec.listener[ ].allowedRoutes.namespacesをSelectorに変更し、HTTPRouteと同じnamespaceのラベルを指定する。
  • .spec.listener[ ].tls.certificateRefs[ ].namespaceをSecretと同じnamespaceにする
$ kubectl get ns test-infra --show-labels 
NAME         STATUS   AGE    LABELS
test-infra   Active   123m   kubernetes.io/metadata.name=test-infra

kubectl apply -f - <<EOF
apiVersion: gateway.networking.k8s.io/v1beta1
kind: Gateway
metadata:
  name: gateway-01
  namespace: azure-alb-system
  annotations:
    alb.networking.azure.io/alb-id: $RESOURCE_ID
spec:
  gatewayClassName: azure-alb-external
  listeners:
  - name: https-listener
    port: 443
    protocol: HTTPS
    allowedRoutes:
      namespaces:
        from: Selector
        selector:
          matchLabels:
            kubernetes.io/metadata.name: test-infra
    tls:
      mode: Terminate
      certificateRefs:
      - kind : Secret
        group: ""
        name: frontend.com
        namespace: azure-alb-system
  addresses:
  - type: alb.networking.azure.io/alb-frontend
    value: $FRONTEND_NAME
EOF

次にHTTPRouteリソースの構成変更を行います。
以下の箇所を変更して、applyします。
* .spec.parentRefs[ ].namespaceをGatewayリソースと同じにする

$ kubectl apply -f - <<EOF
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
  name: https-route
  namespace: test-infra
spec:
  parentRefs:
  - name: gateway-01
    namespace: azure-alb-system
  rules:
  - backendRefs:
    - name: mtls-app
      port: 443
EOF

これまでの作業で以下のような状況になっているはずです。

$ kubectl -n azure-alb-system get pod,gateway,secret
NAME                                            READY   STATUS    RESTARTS   AGE
pod/alb-controller-59455854-tkd8w               1/1     Running   0          143m
pod/alb-controller-bootstrap-5f8c757676-rs2vk   1/1     Running   0          143m

NAME                                           CLASS                ADDRESS                                               PROGRAMMED   AGE
gateway.gateway.networking.k8s.io/gateway-01   azure-alb-external   4f264daf5a4666fb2142e1240a164e86.fz46.alb.azure.com   True         10m

NAME                  TYPE                DATA   AGE
secret/frontend.com   kubernetes.io/tls   2      10m

$ kubectl -n test-infra get pod,httproute,backendtlspolicy,secret
NAME                            READY   STATUS    RESTARTS   AGE
pod/mtls-app-5dd5c75d4f-bwp4w   1/1     Running   0          133m
pod/mtls-app-5dd5c75d4f-cpgd8   1/1     Running   0          133m

NAME                                              HOSTNAMES   AGE
httproute.gateway.networking.k8s.io/https-route               87m

NAME                                                           READY   AGE
backendtlspolicy.alb.networking.azure.io/mtls-app-tls-policy           84m

NAME                         TYPE                DATA   AGE
secret/backend.com           kubernetes.io/tls   2      133m
secret/ca.bundle             Opaque              1      133m
secret/gateway-client-cert   kubernetes.io/tls   2      133m

これで準備完了です。
接続してみましょう。

$ curl --insecure https://$fqdn/
Hello World!

さいごに

Application Gateway for Containers、いいですよねえ。
Microsoftさん、なんて最高なものをリリースしてくれたんでしょう。
これはGAになれば、L7通信のデファクトスタンダードになる予感がします。
GAを楽しみに待ちましょう!!

ACS事業部のご紹介

私達ACS事業部はAzure・AKSなどのクラウドネイティブ技術を活用した内製化のご支援をしております。
www.ap-com.co.jp また、一緒に働いていただける仲間も募集中です!
今年もまだまだ組織規模拡大中なので、ご興味持っていただけましたらぜひお声がけください。 www.ap-com.co.jp

本記事の投稿者: 谷合純也
AKS/ACAをメインにインフラ系のご支援を担当しています。
junya0530さんの記事一覧 | Zenn