IstioのAmbient Mesh について


## はじめに
この記事では[Istio](https://istio.io/)でサービスメッシュを実現する新たな仕組みである、Ambient Meshについて調べた内容を書いてみたいと思います。

### Istioとは

[Istio](https://istio.io/)はオープンソースのサービスメッシュです。
[Istio](https://istio.io/)を利用することで、カナリアリリースなどの高度なルーティングやリクエストの再試行、フェイルオーバー、フォルトインジェクションによりトラフィックを細かく制御することが可能となります。
[Istio](https://istio.io/)にはその他にも多くの機能がありますので、詳細については[Istioのドキュメント](https://istio.io/latest/about/service-mesh/#what-is-istio)などを参考としてください。

Kubernetes環境でIstioを利用する場合、各PodにサイドカーとしてEnvoy Proxyを実行し、トラフィックがサイドカーのEnvoy　Proxyを経由することでサービスメッシュを構成します。
下記は[Istioのブログ](https://istio.io/latest/blog/)から引用した図ですが、構成のイメージとしては下記です。
![スクリーンショット 2022-12-11 15.38.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80181/ba87b08f-83a7-d6a0-1902-9beb3ce51c2e.png)
参考:https://istio.io/latest/blog/2022/introducing-ambient-mesh/

サイドカーのEnvoy Proxyを利用することで、アプリケーションを変更せずともサービスメッシュを導入することが可能です。

### サイドカーパターンによる問題

サイドカーとしてEnvoy Proxyを利用することで、アプリケーションを変更せずともサービスメッシュを導入することが可能ではありますが、これにより[いくつかの制限](https://istio.io/latest/blog/2022/introducing-ambient-mesh/#istio-and-sidecars)が発生するとのことです。

 - サイドカーとしてEnvoy Proxyを注入する必要があるため、サイドカーのインストールやバージョンアップ時にアプリケーションのPodの再起動が必要となる。
 - サイドカーは関連するワークロード専用であるため、それぞれのPodが利用する可能性のある量のCPU とメモリ リソースをプロビジョニングする必要がある。それによりクラスター全体でリソースが効率的に利用されない可能性がある。
 - Istioのサイドカーで通常行われるトラフィックキャプチャとHTTP処理は、計算量が多く、HTTPの実装に準拠していない一部のアプリケーションを壊す可能性がある。

サイドカーには良い点があるものの、多くのサービスメッシュユーザーにとって、より負担の少ない、より簡単なオプションが必要であるとのことで、Ambient Meshという方法が考えられたようです。

### Ambient Mesh について
Ambient Meshは上記のサイドカーを利用したパターンとは異なるアプローチを取っています。
Istioの機能を下記のように２つのレイヤーに分割し、Secure overlay Layer に記載されている機能は、各ワーカーノードで起動する共有のエージェント(ztunnel エージェント)が担当し、L7 Processing Layerの機能はwaypoint proxyというコンポーネントが担当する構成となります。
![スクリーンショット 2022-12-11 19.50.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80181/678b3f81-9ca6-df5d-7ef8-df265b62f0f3.png)
参考:https://istio.io/latest/blog/2022/introducing-ambient-mesh/#slicing-the-layers

##### Secure Overlay Layer
上記の Secure Overlay Layer　に記載された Ambient Mesh の機能を Kubernetesで利用する場合、ztunnelを[DaemonSet](https://kubernetes.io/ja/docs/concepts/workloads/controllers/daemonset/)としてデプロイすることで、各ワーカーノードでPodを1つ起動する構成とします。

 構成のイメージとしては下記です。
![スクリーンショット 2022-12-16 10.10.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80181/da25d159-afeb-44b1-8341-2d9edef7bafe.png)
参考: https://istio.io/latest/blog/2022/introducing-ambient-mesh/#building-an-ambient-mesh

##### L7 Processing Layer
L7 の機能を利用する場合、Ambient Mesh は　Envoy ベースの waypoint proxy を経由する構成となります。
構成のイメージは下記です。

![スクリーンショット 2022-12-25 17.01.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80181/66751716-c68e-2bf6-648a-f832e389cd52.png)

参考: https://istio.io/latest/blog/2022/introducing-ambient-mesh/#building-an-ambient-mesh

L7 の処理を必要とする場合、ztunnel 間の通信は、上記のように waypoint proxy を経由する構成となります。
Kubernetes 環境で利用する場合、waypoint proxy　は通常のアプリケーションの Pod のように Deployment を利用して複数起動実行します。
トラフィックの量に応じて waypoint proxy の Pod をスケールアウトすることができますので、サイドカーとして Envoy を利用する場合よりも、リソースを効率良く利用できることが期待されます。



### 試してみる
実際に Ambient Mesh を試してみたいと思います。
下記のような設定ファイルをもとに [kind](https://kind.sigs.k8s.io/) を利用してローカルの環境で Kubernetes クラスターを作成します。
```md:cluster.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: ambient
nodes:
- role: control-plane
- role: worker
- role: worker
```

Kubernetes クラスターを作成します。
```
$ kind create cluster --config=cluster.yaml
```

こちらの[ページ](https://gcsweb.istio.io/gcs/istio-build/dev/0.0.0-ambient.191fe680b52c1754ee72a06b3e0d3f9d116f2e82) から ambient mesh をサポートしているプレビューバージョンの istioctl をダウンロードします。

ダウンロードした istioctl を利用して、作成した Kubernetes クラスターに istio をインストールします。
```
./istioctl install --set profile=ambient
```

インストールが完了すると、下記のコマンドで Istiod、ingress gateway、ztunnel、CNI plugin などが起動していることを確認できるかと思います。
```
$ kubectl get pod -n istio-system
```

istio をインストールしたクラスターに [bookinfo](https://istio.io/latest/docs/examples/bookinfo/) アプリケーションをデプロイします。
```
$ kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.16/samples/bookinfo/platform/kube/bookinfo.yaml
```

上記のアプリケーションにアクセスするための Client 側のアプリケーションをデプロイします。
```
$ kubectl apply -f https://raw.githubusercontent.com/linsun/sample-apps/main/sleep/sleep.yaml
```

istio の [Authorization Policy](https://istio.io/latest/docs/reference/config/security/authorization-policy/#Source) の機能を利用してアクセスの制限を試してみます。

下記のようなマニフェストファイルを作成します。
下記の例の場合、app: productpage ラベルが設定されたワークロードへのアクセスを制限します。
合わせて rules の設定により、sleep サービスアカウントからのアクセスを許可しています。
```md:authorization-policy.yaml
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
 name: productpage-viewer
 namespace: default
spec:
 selector:
   matchLabels:
     app: productpage
 action: ALLOW
 rules:
 - from:
   - source:
       principals: ["cluster.local/ns/default/sa/sleep"]
```

上記のマニフェストをファイルを apply します。
```
$ kubectl apply -f authorization-policy.yaml
```

先ほどデプロイした Client の Pod からアクセスを試してみます。
Client の Pod は sleep サービスアカウントが設定されているため、アクセスすることが可能です。
```
$ kubectl describe deployment sleep                                                                                                                                                                                  
Name:                   sleep
Namespace:              default
CreationTimestamp:      Sun, 25 Dec 2022 15:55:40 +0900
Labels:                 <none>
Annotations:            deployment.kubernetes.io/revision: 1
Selector:               app=sleep
Replicas:               1 desired | 1 updated | 1 total | 1 available | 0 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        0
RollingUpdateStrategy:  25% max unavailable, 25% max surge
Pod Template:
  Labels:           app=sleep
  Service Account:  sleep
  Containers:
   sleep:
    Image:      curlimages/curl
(略)
```

```
$ kubectl exec deploy/sleep -- curl -s http://productpage:9080/ | head -n1                                 
```

異なるサービスアカウントが設定された Pod からはアクセスができないことが確認できます。
```
$ kubectl apply -f https://raw.githubusercontent.com/linsun/sample-apps/main/sleep/notsleep.yaml
$ kubectl exec deploy/notsleep -- curl -s http://productpage:9080/ | head -n1
```

次に　L7 の Authorization Policy の機能を試してみます。
上記の機能を利用するには　waypoint proxy が必要となるため、下記のマニフェストをデプロイします。
```md:waypoint-proxy.yaml
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: Gateway
metadata:
 name: productpage
 annotations:
   istio.io/service-account: bookinfo-productpage
spec:
 gatewayClassName: istio-mesh
```
```
$ kubectl apply -f waypoint-proxy.yaml
```

私の環境の場合、下記のように `bookinfo-productpage-waypoint-proxy-6f88c55d59-h26qt` という Pod が起動します。
```
$ kubectl get pods                                                                                           
NAME                                                   READY   STATUS    RESTARTS   AGE
bookinfo-productpage-waypoint-proxy-6f88c55d59-h26qt   1/1     Running   0          142m
(略)
```

AuthorizationPolicy のマニフェストファイルを下記のように修正して apply します。
```md:authorization-policy.yaml
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
 name: productpage-viewer
 namespace: default
spec:
 selector:
   matchLabels:
     app: productpage
 action: ALLOW
 rules:
 - from:
   - source:
       principals: ["cluster.local/ns/default/sa/sleep"]
   to:
   - operation:
       methods: ["GET"]
```

GET メソッドのアクセスだけが許可されているため、sleep サービスアカウントが設定されている Pod からのアクセスであっても GET メソッド以外のアクセスは失敗することが確認できるかと思います。
```
$ kubectl exec deploy/sleep -- curl -s http://productpage:9080/ -X DELETE | head -n1
$ kubectl exec deploy/sleep -- curl -s http://productpage:9080/ | head -n1
```

### さいごに
確認が完了して、検証用の Kubernetes クラスタが不要になりましたら下記のコマンドでクラスタを削除してください。
```
$ kind delete cluster --name ambient
```

### まとめ
簡単に試してみただけの内容となってしまいましたが、Ambient Mesh について調べた内容を書いてみました。
今回は動作の詳細などについては踏み込んで調査できませんでしたが、今後、機会があればより詳細について調査してみたいと思います。

## 参考資料
 - [Introducing Ambient Mesh](https://istio.io/latest/blog/2022/introducing-ambient-mesh/)
 - [Get Started with Istio Ambient Mesh](https://istio.io/latest/blog/2022/get-started-ambient/)