OKE上にAmbient Meshを構築してBookInfoアプリケーションを動かしてみよう

# はじめに

昨年IstioでAmbient Meshというサイドカープロキシなしのサービスメッシュデータプレーンのアーキテクチャが[アナウンス](https://istio.io/latest/blog/2022/get-started-ambient/)されました。

この前日に[Service Meshがっつり入門](https://speakerdeck.com/oracle4engineer/get-started-service-mesh)というセッションをやっていたので、~~翌日にコンテンツが陳腐化するという悪夢でした。~~

もちろん、従来のサイドカープロキシを廃止するわけではなく、2つのアーキテクチャは共存させていく(相互運用もサポートされているようです）ということをIstioが表明していますが、Ambient Meshを無視するわけにはいかないので、今回試してみました。

:::note alert 
**注意事項**
Ambient Meshはα版であり、今回利用する[OKEはIstioとして公式サポートしていません。](https://github.com/istio/istio/tree/experimental-ambient#supported-environments)
そのため、あくまでも参考情報として読んで頂ければと思います。
:::

# Ambient Meshについて

Istioは、2022年9月7日に"Ambient Mesh"と呼ばれる新しいデータプレーンのアーキテクチャを[発表](https://istio.io/latest/blog/2022/introducing-ambient-mesh/)しました。  
Ambient Meshは次期バージョンであるv1.18でメインブランチにマージされる予定です。  

前述したサイドカープロキシを利用したデータプレーンには更新時の再起動やリソースの利活用の観点でいくつか課題があります。
これらの課題に対応するために、従来は全ての通信をサイドカープロキシに任せていたアーキテクチャをAmbient Meshでは2つのレイヤーに分割しています。  

1つ目のレイヤーは"Secure Overlay Layer"と呼ばれ、mTLS、(TCPレベルの)Observabilityなどを管轄します。  
2つ目のレイヤーは"L7 Processing Layer"と呼ばれ、L7レイヤーでのルーティング、サーキットブレイカー、リトライ/タイムアウトの制御や(HTTPレベルの)Observabilityを管轄します。

![スクリーンショット 2023-05-19 15.56.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/40392/809482eb-3c22-edbb-336b-88157bf44899.png)



前者のレイヤーでは、Mesh内の各Nodeで動作するエージェントを利用します。  
Node間の通信はそれぞれのエージェント間でリダイレクトされます。  
Kubernetesでは、このエージェントはDaemonsetとなります。  

後者のレイヤーでは、KubernetesのNamespaceで動作する"waypoint proxies"というコンポーネントを利用します。  
このコンポーネントはEnvoyがベースとなっており、NamespaceでL7レイヤーでのトラフィック制御が必要な場合は、前述したエージェントから"waypoint proxies"にトラフィックが渡されます。  
Kubernetesでは、"waypoint proxies"はDeploymentとして動作しており、トラフィックの需要に合わせてオートスケールさせることができます。  

サイドカープロキシとAmbient Meshは、いずれもサポートされ続けていく予定になっており、ユースケースに応じてユーザがどちらを利用するかを選択できます。  
また、サイドカープロキシとAmbient Meshは相互運用をサポートしています。  

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

# 事前準備

OKEの構築は[こちら](https://oracle-japan.github.io/ocitutorials/cloud-native/oke-for-commons/)を参考に行なってください。

ただし、以下の環境を前提に実施します。

- AMDインスタンス×1
- Worker NodeのOSバージョンはOracle Linux7.9(Oracle Linux8.x系はサービスメッシュがうまく動作しない可能性があります)
- シェイプは2oCPU/16GB

この記事ではAMDインスタンスを前提に解説していきます。
また、シェイプは2oCPU/16GBくらいあれば十分動作します。

# Ambient Meshのインストール

Ambient Meshをインストールします。

この記事では[こちら](https://preliminary.istio.io/latest/docs/ops/ambient/getting-started/)の記事を参考にインストールしていきます。

まずは、資材をダウンロードします。
Ambiemt Meshは、まだIstioのメインブランチにマージされていないので、個別に資材のダウンロードが必要です。
ちなみにメインブランチには次期バージョンであるv1.18でマージされる予定です。

今回は、`Istio 1.18.0-beta.1`を利用します。
資材はクライアントによって[こちら](https://github.com/istio/istio/releases/tag/1.18.0-beta.1)から選択してください。
この記事では、AMD用の資材を利用します。
資材はwgetで取得します。

```sh
wget https://github.com/istio/istio/releases/download/1.18.0-beta.1/istio-1.18.0-beta.1-linux-amd64.tar.gz
```

解凍します。

```sh
tar zxvf istio-1.18.0-beta.1-linux-amd64.tar.gz
```

ディレクトリを移動します。

```sh
cd istio-1.18.0-beta.1
```

binディレクトリ配下に`istioctl`が含まれているので、こちらを利用してインストールします。
必要に応じてPATHを通しても問題ありません。
今回はPATHを設定せずに利用します。

Ambient Meshでは、Ingressの後継であるGateway APIがトラフィック管理のデフォルトなりますが、今回はIngress Gatewayを利用します。
そのための設定も追加します。

```sh
bin/istioctl install --set profile=ambient --set components.ingressGateways[0].enabled=true --set components.ingressGateways[0].name=istio-ingressgateway --skip-confirmation
```

以下のように出力されればOKです。

```sh
bin/istioctl install --set profile=ambient --set components.ingressGateways[0].enabled=true --set components.ingressGateways[0].name=istio-ingressgateway --skip-confirmation
✔ Istio core installed                                                                 
✔ Istiod installed                                                                         
✔ CNI installed                                                                        
✔ Ingress gateways installed                                                               
✔ Ztunnel installed                                                                     
✔ Installation complete
Making this installation the default for injection and validation.        
```

インストールされているコンポーネントを確認します。

```sh
$ kubectl get pods -n istio-system
NAME                                   READY   STATUS    RESTARTS   AGE
istio-cni-node-c6xpv                   1/1     Running   0          142m
istio-ingressgateway-5886cdc7f-2khgb   1/1     Running   0          142m
istiod-67d5f5c5f4-nsfl9                1/1     Running   0          142m
ztunnel-x7lb8                          1/1     Running   0          142m
```

`ztunnel`という見慣れないコンポーネントが確認できます。
これは、前述した"Secure Overlay Layer"に相当するものです。

このコンポーネントはDaemonsetとして動作していることが確認できます。

```sh
$ kubectl get daemonset -n istio-system
NAME             DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR            AGE
istio-cni-node   1         1         1       1            1           kubernetes.io/os=linux   149m
ztunnel          1         1         1       1            1           <none>                   149m
```

これでインストールは完了です。

# サンプルアプリケーションのデプロイと動作確認

サンプルアプリケーションをデプロイします。

サンプルアプリケーションはお馴染みのBookInfoを利用していきます。

資材の中にあるBookInfoアプリケーションのManifestをデプロイします。

```sh
kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml
```

IstioのGatewayリソースとVirtual Serviceも作成しておきます。

```sh
kubectl apply -f samples/bookinfo/networking/bookinfo-gateway.yaml
```

後ほど利用するクライアントをインストールします。

```sh
kubectl apply -f samples/sleep/sleep.yaml
kubectl apply -f samples/sleep/notsleep.yaml
```

最後に環境変数にGateway(LoadBalancer)のIPアドレスとGatewayのService Accountを環境変数に設定しておきます。

```sh
export GATEWAY_HOST=istio-ingressgateway.istio-system
export GATEWAY_SERVICE_ACCOUNT=ns/istio-system/sa/istio-ingressgateway-service-account
```

これでサンプルアプリケーションのデプロイは完了です。

ここで一旦アクセス確認をしておきます。

- 先ほどデプロイしたクライアント`sleep`からGateway(LoadBalancer)へのアクセス(ブラウザからでもアクセスできます)

```sh
kubectl exec deploy/sleep -- curl -s "http://$GATEWAY_HOST/productpage" | grep -o "<title>.*</title>"
```

- 先ほどデプロイしたクライアント`sleep`からproductpageアプリケーションへのアクセス

```sh
kubectl exec deploy/sleep -- curl -s http://productpage:9080/ | grep -o "<title>.*</title>"
```

- 先ほどデプロイしたクライアント`notsleep`からproductpageアプリケーションへのアクセス

```sh
kubectl exec deploy/notsleep -- curl -s http://productpage:9080/ | grep -o "<title>.*</title>"
```

どのパターンの場合も以下の出力になればOKです。

```sh
<title>Simple Bookstore App</title>
```

# Ambient Meshの構築

いよいよ、Ambint Meshを構築していきます。

と言っても、構築は非常に簡単で、Ambient Mesh用のラベルを追加するだけです。

```sh
kubectl label namespace default istio.io/dataplane-mode=ambient
```

ちなみにサイドカープロキシの場合は以下のようなラベルを追加するので、 ラベルが少し異なることが確認できます。

```sh
kubectl label namespace default istio-injection=enabled
```

これでサンプルアプリケーションがAmbient Mesh内に追加されました。
Podの状態を確認します。

```sh
$ kubectl get pods
details-v1-6997d94bb9-w7dnc                            1/1     Running   0          136m
notsleep-5fb85fb789-pppfz                              1/1     Running   0          100m
productpage-v1-58b4c9bff8-4zgrf                        1/1     Running   0          136m
ratings-v1-b8f8fcf49-lvr5d                             1/1     Running   0          136m
reviews-v1-5896f547f5-vkwnk                            1/1     Running   0          136m
reviews-v2-5d99885bc9-mznkz                            1/1     Running   0          136m
reviews-v3-589cb4d56c-7wks7                            1/1     Running   0          136m
sleep-bc9998558-65cxk                                  1/1     Running   0          102m
```

サイドカープロキシの場合はサイドカーがインジェクションされるようにPodを再起動する必要がありますが、Ambient Meshの場合はその必要はありません。
このように、再起動やアップデートなしにMesh環境を構築できるのがAmbient Meshのメリットの一つです。

これで、Ambient Meshの構築は完了です。

# Ambiemt Meshの動作確認

ここからは動作確認を行います。

今回は以下の２つの観点を確認します。

- 認可
- トラフィックルーティング

## 認可

### L4認可

まずはL4レイヤーでの認可ポリシーを試します。

この認可ポリシーはDaemonsetとして動作している`ztunnel`が担当します。

以下のManifestを適用します。

```sh
$ kubectl apply -f - <<EOF
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
       - cluster.local/$GATEWAY_SERVICE_ACCOUNT
EOF
```

ここで作成している`AuthorizationPolicy`は[こちら](https://speakerdeck.com/oracle4engineer/get-started-service-mesh?slide=41)をご確認ください。

これによって、productpageアプリケーションにアクセスできるのはクライアント`sleep`かGateway経由のトラフィックのみということになります。
つまり、クライアント`notsleep`からのアクセスはできません。
確認してみましょう。

```sh
$ kubectl exec deploy/sleep -- curl -s "http://$GATEWAY_HOST/productpage" | grep -o "<title>.*</title>"
<title>Simple Bookstore App</title>
```

```sh
$ kubectl exec deploy/sleep -- curl -s http://productpage:9080/ | grep -o "<title>.*</title>"
<title>Simple Bookstore App</title>
```

```sh
$ kubectl exec deploy/notsleep -- curl -s http://productpage:9080/ | grep -o "<title>.*</title>"
command terminated with exit code 56
```

クライアント`notsleep`からのアクセスは拒否されました。

### L７認可

L7レイヤーの場合は前述した"L7 Processing Layer"を担当するwaypoint proxyが必要になります。
waypoint proxyはサイドカープロキシでも利用されいているEnvoyになります。

まずはwaypoint proxyをインストールします。
waypoint　proxyはService Accountごとにデプロイします。
今回はproductpageアプリケーションのService Accountである`bookinfo-productpage`用のwaypoint proxyを追加します。

```sh
bin/istioctl x waypoint apply --service-account bookinfo-productpage
```

以下のようにデプロイされます。

```sh
$ kubectl get pods
NAME                                                   READY   STATUS    RESTARTS   AGE
bookinfo-productpage-istio-waypoint-5d6f6f54c9-b5f7q   1/1     Running   0          98m
details-v1-6997d94bb9-w7dnc                            1/1     Running   0          136m
notsleep-5fb85fb789-pppfz                              1/1     Running   0          100m
productpage-v1-58b4c9bff8-4zgrf                        1/1     Running   0          136m
ratings-v1-b8f8fcf49-lvr5d                             1/1     Running   0          136m
reviews-v1-5896f547f5-vkwnk                            1/1     Running   0          136m
reviews-v2-5d99885bc9-mznkz                            1/1     Running   0          136m
reviews-v3-589cb4d56c-7wks7                            1/1     Running   0          136m
sleep-bc9998558-65cxk                                  1/1     Running   0          102m
```

これでL7認可が実施できるので、試します。
今回はHTTPメソッドでGET以外のリクエストを拒否するように設定してみます。
L4認可の設定は先ほどと同じです。

```sh
$ kubectl apply -f - <<EOF
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
 name: productpage-viewer
 namespace: default
spec:
 selector:
   matchLabels:
     istio.io/gateway-name: bookinfo-productpage
 action: ALLOW
 rules:
 - from:
   - source:
       principals:
       - cluster.local/ns/default/sa/sleep
       - cluster.local/$GATEWAY_SERVICE_ACCOUNT
   to:
   - operation:
       methods: ["GET"]
EOF
```

これで再度動作確認してみます。

```sh
$ kubectl exec deploy/sleep -- curl -s "http://$GATEWAY_HOST/productpage" -X DELETE
RBAC: access denied
```

```sh
$ kubectl exec deploy/notsleep -- curl -s http://productpage:9080/
RBAC: access denied
```

```sh
kubectl exec deploy/sleep -- curl -s http://productpage:9080/ | grep -o "<title>.*</title>"
<title>Simple Bookstore App</title>
```

このようにL7レイヤーで認可ポリシーを適用できます。

## トラフィックルーティング

最後にトラフィックルーティングを試します。

BookInfoではreviewsアプリケーションに3つのバージョンが存在しているので、こちらを利用します。

まずは、reviewsアプリケーション用のwaypoint　proxyをデプロイします。
reviewsアプリケーションのService Accountは`bookinfo-reviews`になります。

```sh
bin/istioctl x waypoint apply --service-account bookinfo-reviews
```

以下のようにデプロイされます。

```sh
$ kubectl get pods
NAME                                                   READY   STATUS    RESTARTS   AGE
bookinfo-productpage-istio-waypoint-5d6f6f54c9-b5f7q   1/1     Running   0          98m
bookinfo-reviews-istio-waypoint-7ccc759f55-lp8xc       1/1     Running   0          95m
details-v1-6997d94bb9-w7dnc                            1/1     Running   0          136m
notsleep-5fb85fb789-pppfz                              1/1     Running   0          100m
productpage-v1-58b4c9bff8-4zgrf                        1/1     Running   0          136m
ratings-v1-b8f8fcf49-lvr5d                             1/1     Running   0          136m
reviews-v1-5896f547f5-vkwnk                            1/1     Running   0          136m
reviews-v2-5d99885bc9-mznkz                            1/1     Running   0          136m
reviews-v3-589cb4d56c-7wks7                            1/1     Running   0          136m
sleep-bc9998558-65cxk                                  1/1     Running   0          102m
```

今回は、v1(レビューの☆なし)とv2(レビューの☆が★)にそれぞれ９０％、１０％の割合でルーティングするポリシーを適用します。

```sh
$ kubectl apply -f samples/bookinfo/networking/virtual-service-reviews-90-10.yaml
$ kubectl apply -f samples/bookinfo/networking/destination-rule-reviews.yaml
```

ブラウザから確認するとほとんど、v1(レビューの☆なし)が表示され、稀にv2(レビューの☆が★)が表示されることが確認できます。

以上で、トラフィックルーティングの確認は完了です。

# まとめ

Ambient Meshを~~多少無理やり~~OKEで動作させてみました。
サイドカープロキシとは手順が異なりますが、できることはあまり変わらないようです。

これからはユースケースに応じて、サイドカープロキシとAmbient Meshを選択してIstioを利用することが求められそうですね。

# 参考資料

https://preliminary.istio.io/latest/docs/ops/ambient/getting-started/

https://istio.io/latest/blog/2022/introducing-ambient-mesh/

https://github.com/istio/istio/tree/experimental-ambient