AKS と Service Mesh Add-Onの構成で Gateway APIを導入

## はじめに

[割と最近GAされたAKSのIstioベースのサービスメッシュアドオン
](https://azure.microsoft.com/en-us/updates/ga-istiobased-service-mesh-addon-for-azure-kubernetes-service/)を有効化した状態で[Gateway API](https://gateway-api.sigs.k8s.io/)を設置する方法についての記事です。

余談から入りますが、アドオンの略称がASM(Azure Service Mesh)なんですが、Anthos Service Meshも ASMの略称なので少し紛らわしいと感じました。どちらもマネージド版Istioベースのサービスメッシュという点では共通しているし。まあ中身は大きく異なるのですが。


AKSのサービスメッシュアドオンを有効化すると、オプションで簡単に Ingress Gatewayが構築できます。
[AKS external or internal ingresses for Istio service mesh add-on deployment](https://learn.microsoft.com/en-us/azure/aks/istio-deploy-ingress)

しかし、これはクラシックな Istio Ingress Gatewayで、現時点では最もStableな方式ですが、以下のIstio公式コメントより今後Kubernetes Gateway APIが標準になることが期待されます。

>Istio supports the Kubernetes Gateway API and intends to make it the default API for traffic management in the future. If you use the Gateway API, you will not need to install and manage a gateway Deployment as described in this document. By default, a gateway Deployment and Service will be automatically provisioned based on the Gateway configuration. Refer to the Gateway API task for details.

https://istio.io/latest/docs/setup/additional-setup/gateway/#deploying-a-gateway


## 構築手順

ゴールは Istio Ingress Gatewayの代わりに Gateway APIの導入によってIngress Trafficを通し、https でAKSにデプロイした`bff-service`へアクセスxできること。

### サービスメッシュ(Istio)アドオンの有効化

Azure Portal で有効化することもできます。Terraformでは以下のように記載します。

```tf

provider "azurerm" {
  features {}
}

resource "azurerm_kubernetes_cluster" "aks" {
  // 略
  
  service_mesh_profile  {
    mode                             = "Istio"
  }
```

### Gateway API をインストール

```bash
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.1.0/standard-install.yaml
```

上記のStandard Channelには、GatewayClass、Gateway、HTTPRoute、ReferenceGrantなどのGA/betaのCRDが含まれています。

次に、Gatewayのマニフェストを用意します。

```Yaml
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: mygw
  namespace: core
  labels:
    istio.io/rev: asm-1-21
  annotations:
    service.beta.kubernetes.io/azure-dns-label-name: "foo-instiny-aks-gw"
    service.beta.kubernetes.io/azure-load-balancer-health-probe-request-path: "/healthz/ready"
    service.beta.kubernetes.io/port_80_health-probe_protocol: http
    service.beta.kubernetes.io/port_80_health-probe_port: "15021"
    service.beta.kubernetes.io/port_443_health-probe_protocol: http
    service.beta.kubernetes.io/port_443_health-probe_port: "15021"
    cert-manager.io/issuer: letsencrypt
spec:
  gatewayClassName: istio
  listeners:
    - name: http
      hostname: foo-instiny-aks-gw.japaneast.cloudapp.azure.com
      protocol: HTTP
      port: 80
      allowedRoutes:
        namespaces:
          from: All

    - hostname: foo-instiny-aks-gw.japaneast.cloudapp.azure.com
      name: https
      port: 443
      protocol: HTTPS
      allowedRoutes:
        namespaces:
          from: All
      tls:
        mode: Terminate
        certificateRefs:
          - name: mygw-tls
            kind: Secret
```
* `istio.io/rev` には現在有効化している asmのバージョンを合わせる
* `service.beta.kubernetes.io/azure-dns-label-name` はDNSを自動設定する便利なアノテーションです。設定すると、`<label-name>.<location>.cloudapp.azure.com`でホストされます
* `cert-manager.io/issuer`はIssuerの名前です。まだ作成していないので、次のセクションで説明します

### TLSのセットアップ
TLS証明書を生成するために cert-managerをインストールします。[参考](https://cert-manager.io/docs/usage/gateway/)


```bash
helm upgrade cert-manager jetstack/cert-manager \
    --install \
    --create-namespace \
    --wait \
    --namespace cert-manager \
    --set installCRDs=true \
    --set "extraArgs={--feature-gates=ExperimentalGatewayAPISupport=true}"
```

次に、Issuerを定義します。この定義は、cert-managerがmygwを介してHTTP01チャレンジによってLet's Encryptを利用し、証明書を発行するものです。ACMEのprivate keyはissuer-account-keyという名前のk8s secretに保管されます。EmailはLet's Encryptからの通知先です。

```Yaml
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  name: letsencrypt
spec:
  acme:
    email: email@example.com
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: issuer-account-key
    solvers:
      - http01:
          gatewayHTTPRoute:
            parentRefs:
              - name: mygw
                kind: Gateway
```

### HTTPRouteの設定

`bff-service`にroutingされるように設定します。


```Yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: httproute
  namespace: core
spec:
  parentRefs:
    - name: mygw
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /
      backendRefs:
        - name: bff-service
          port: 80
```


### マニフェストのデプロイ

kubectl apply で上記のマニフェストを適用すると、'Gateway'リソースが `mygw-istio`を自動的にデプロイします。
```
kubectl get deploy -n core
kubectl get svc -n core
kubectl get po -n core
```

これらのコマンドで確認すると、mygw-istioがデプロイされていることを確認できます。デプロイが問題なく完了するとGatewayを通して`bff-service`へリクエストできるようになります。

```bash
curl https://foo-instiny-aks-gw.japaneast.cloudapp.azure.com
```

## おわりに
最新 Istio 1.22 では Gatyeway API v1.1のサポートがStableになりました。
ASMは現在、1.21ベースだと思うので待ち遠しいですね。asm-1.22がリリースされた時に興味深い変化があれば、また記事にしたいと思います。

>We are thrilled to announce that Service Mesh support for the Gateway API is now officially marked as “Stable”! With the release of Gateway API v1.1 and its support in Istio 1.22, you can make use of Kubernetes’ next-generation traffic management APIs for both ingress (“north-south”) and service mesh (“east-west”) use cases. Read more about the improvements in our Gateway API v1.1 blog.

https://istio.io/latest/news/releases/1.22.x/announcing-1.22/#gateway-api-now-stable-for-service-mesh