EKS上にCiliumサービスメッシュを稼動させてみた!


# はじめに

最近Ciliumが気になっていて、ちょっと触ってみたいなと思っていたところ、
EKS上でCiliumを動かすサンプルをAWSがGitHubで公開しているのを見つけたので試してみました!

https://github.com/aws-samples/cilium-service-mesh-on-eks

# Ciliumとは

Ciliumはextended Berkeley Packet(eBPF)というカーネル技術を用いてワークロード間のネットワークの接続を提供、保護、監視するOSSです。
eBPFを用いることで従来のサービスメッシュの様なサイドカーコンテナが不要になる、Pod間通信の経路がシンプルになりオーバーヘッドが少なる事による通信レイテンシが大幅に改善される、などの多くのメリットがあります。

https://github.com/cilium/cilium

Ciliumはネットワークを管理するCilium CNI,サービスメッシュ機能を提供するCilium Service Mesh,オブザーバビリティ機能を提供するCilium Hubbleから構成されています。


![cilium-overview.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1905292/dde22824-ed0f-4420-b8ed-9fcb92737acb.png)
Ciliumの概要(公式GitHubより抜粋)


Ciliumは2023/10/11にCNCFのGraduationプロジェクトに認定されており、Ciliumが本番システムで稼動できる品質になっている事が証明されています。

# 前提条件

このサンプルを稼動させるためには以下を用意する必要があります。

* AWSおよびKubernetes API Serverに接続可能な端末
   → 私はAWSコンソールからCloudShellを起動して使いました
* 端末に以下のCLIツールがインストールされていること
    * AWS CLI
    * Terraform
     * kubectl
     * Cilium CLI
     * Helm


# Step 1 - GitHubリポジトリをクローン

まず公式サンプルのリポジトリをクローンします。

```
git clone https://github.com/aws-samples/cilium-mesh-on-eks/
cd cilium-mesh-on-eks
```
```
[cloudshell-user@ip-10-2-1-25 ~]$ git clone https://github.com/aws-samples/cilium-mesh-on-eks/
Cloning into 'cilium-mesh-on-eks'...
remote: Enumerating objects: 445, done.
remote: Counting objects: 100% (268/268), done.
remote: Compressing objects: 100% (168/168), done.
remote: Total 445 (delta 158), reused 169 (delta 100), pack-reused 177
Receiving objects: 100% (445/445), 412.77 KiB | 4.86 MiB/s, done.
Resolving deltas: 100% (248/248), done.
[cloudshell-user@ip-10-2-1-25 ~]$ cd cilium-mesh-on-eks
[cloudshell-user@ip-10-2-1-25 cilium-mesh-on-eks]$ ls
CODE_OF_CONDUCT.md  CONTRIBUTING.md  images  LICENSE  productapp  README.md  terraform  values_cilium.yaml
```

# Step 2 - TerraformでEKSクラスタをデプロイ

クローンしたリポジトリ内にあるTerraformテンプレートファイルを使って、EKSクラスタをデプロイします。

```
cd terraform
terraform init
terraform apply --auto-approve
```

```
d=terraform:initial-20240720080453271400000012]
module.eks.aws_eks_addon.this["vpc-cni"]: Creating...
module.eks.aws_eks_addon.this["coredns"]: Creating...
module.eks.aws_eks_addon.this["kube-proxy"]: Creating...
module.eks.aws_eks_addon.this["kube-proxy"]: Creation complete after 7s [id=terraform:kube-proxy]
module.eks.aws_eks_addon.this["vpc-cni"]: Still creating... [10s elapsed]
module.eks.aws_eks_addon.this["coredns"]: Still creating... [10s elapsed]
module.eks.aws_eks_addon.this["coredns"]: Creation complete after 14s [id=terraform:coredns]
module.eks.aws_eks_addon.this["vpc-cni"]: Still creating... [20s elapsed]
module.eks.aws_eks_addon.this["vpc-cni"]: Still creating... [30s elapsed]
module.eks.aws_eks_addon.this["vpc-cni"]: Still creating... [40s elapsed]
module.eks.aws_eks_addon.this["vpc-cni"]: Creation complete after 44s [id=terraform:vpc-cni]

Apply complete! Resources: 66 added, 0 changed, 0 destroyed.

Outputs:

configure_kubectl = "aws eks --region us-west-2 update-kubeconfig --name terraform"
```

オレゴンリージョンに```terraform```という名前のEKSクラスタが作成されました。

![1.オレゴンリージョンにEKSクラスタができた.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1905292/1bd4e065-dba8-f8fd-7873-168e65a88bd1.png)

Terraformコマンドの出力結果の最後に表示されるコマンドを実行して、作成したEKSクラスタに接続できる様にkubeconfigを書き換えます。

```
[cloudshell-user@ip-10-130-34-57 ~]$ aws eks --region us-west-2 update-kubeconfig --name terraform
Added new context arn:aws:eks:us-west-2:623649842711:cluster/terraform to /home/cloudshell-user/.kube/config
[cloudshell-user@ip-10-130-34-57 ~]$ kubectl get node
NAME                                        STATUS   ROLES    AGE     VERSION
ip-10-0-12-137.us-west-2.compute.internal   Ready    <none>   8m49s   v1.29.3-eks-ae9a62a
ip-10-0-39-27.us-west-2.compute.internal    Ready    <none>   8m49s   v1.29.3-eks-ae9a62a
```

ちゃんと```kubectl```コマンドで作成したEKSクラスタに接続できましたね!


# Step 3 - EKSクラスタにCiliumをデプロイ

いよいよ、ciliumをインストールします。helmでインストールします、簡単ですねー

```
helm repo add cilium https://helm.cilium.io/
helm upgrade --install cilium cilium/cilium --version 1.14.7 \
--namespace kube-system \
--reuse-values -f ../values_cilium.yaml \
--set hubble.enabled=true \
--set hubble.tls.auto.enabled=true \
--set hubble.metrics.enabled="{dns,drop,tcp,flow,icmp,http}" \
--set hubble.relay.enabled=true \
--set hubble.ui.enabled=true \
--set hubble.ui.service.type=NodePort \
--set hubble.relay.service.type=NodePort \
--set kubeProxyReplacement=strict \
--set encryption.enabled=false \
--set encryption.nodeEncryption=false \
--set routingMode=native \
--set ipv4NativeRoutingCIDR="0.0.0.0/0" \
--set bpf.masquerade=false \
--set nodePort.enabled=true \
--set autoDirectNodeRoutes=true \
--set hostLegacyRouting=false \
--set ingressController.enabled=true \
--set ingressController.loadbalancerMode=shared \
--set cni.chainingMode=aws-cni \
--set cni.install=true
```

```
[cloudshell-user@ip-10-130-34-57 terraform]$ pwd
/home/cloudshell-user/cilium-mesh-on-eks/terraform
[cloudshell-user@ip-10-130-34-57 ~]$ helm repo add cilium https://helm.cilium.io/
"cilium" has been added to your repositories
[cloudshell-user@ip-10-130-34-57 terraform]$ helm upgrade --install cilium cilium/cilium --version 1.14.7 \
> --namespace kube-system \
> --reuse-values -f ../values_cilium.yaml \
> --set hubble.enabled=true \
> --set hubble.tls.auto.enabled=true \
> --set hubble.metrics.enabled="{dns,drop,tcp,flow,icmp,http}" \
> --set hubble.relay.enabled=true \
> --set hubble.ui.enabled=true \
> --set hubble.ui.service.type=NodePort \
> --set hubble.relay.service.type=NodePort \
> --set kubeProxyReplacement=strict \
> --set encryption.enabled=false \
> --set encryption.nodeEncryption=false \
> --set routingMode=native \
> --set ipv4NativeRoutingCIDR="0.0.0.0/0" \
> --set bpf.masquerade=false \
> --set nodePort.enabled=true \
> --set autoDirectNodeRoutes=true \
> --set hostLegacyRouting=false \
> --set ingressController.enabled=true \
> --set ingressController.loadbalancerMode=shared \
> --set cni.chainingMode=aws-cni \
> --set cni.install=true
Release "cilium" does not exist. Installing it now.
NAME: cilium
LAST DEPLOYED: Sat Jul 20 08:30:59 2024
NAMESPACE: kube-system
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
You have successfully installed Cilium with Hubble Relay and Hubble UI.

Your release version is 1.14.7.

For any further help, visit https://docs.cilium.io/en/v1.14/gettinghelp
```

↑のコマンドのオプションの重要ポイントは以下です。

* ```kubeProxyReplacement=strict``` - kube-proxyをeBPFベース実装のものに置き換えます。
* ```ingressController.enabled=true``` - Cilium Ingressコントローラを有効にします。
* ```--reuse-values -f ../values_cilium.yaml``` - ```values_cilium.yaml```ファイルにIngressControllerのアノテーションが記載されています。これにより、インターネットからAWS Network Loadbalancer経由でCilium Ingressコントローラに接続できる様になります。

```values_cilium.yaml
ingressController:
  service:
   annotations:
     service.beta.kubernetes.io/aws-load-balancer-scheme: "internet-facing"
```

* ```hubble.enabled=true``` - ObservabilityツールであるHubbleを有効にします。



Cilium関連のPodが起動されているか確認してみましょう。


```
[cloudshell-user@ip-10-130-34-57 terraform]$ kubectl get pod -A
NAMESPACE     NAME                                           READY   STATUS    RESTARTS   AGE
kube-system   aws-load-balancer-controller-999bf8598-n6fmh   1/1     Running   0          39m
kube-system   aws-load-balancer-controller-999bf8598-wkllq   1/1     Running   0          39m
kube-system   aws-node-7bbjh                                 2/2     Running   0          37m
kube-system   aws-node-7r4sp                                 2/2     Running   0          37m
kube-system   cilium-5qm5f                                   1/1     Running   0          13m
kube-system   cilium-7mdgf                                   1/1     Running   0          13m
kube-system   cilium-operator-689df79c69-6dj27               1/1     Running   0          13m
kube-system   cilium-operator-689df79c69-8n7jg               1/1     Running   0          13m
kube-system   coredns-5b8cc885bc-96s2q                       1/1     Running   0          12m
kube-system   coredns-5b8cc885bc-fxxnp                       1/1     Running   0          13m
kube-system   hubble-relay-6f6f5476d9-5kh86                  1/1     Running   0          13m
kube-system   hubble-ui-644d9df97c-l72lc                     2/2     Running   0          13m
kube-system   kube-proxy-mffff                               1/1     Running   0          38m
kube-system   kube-proxy-xnkcn                               1/1     Running   0          38m
```

それらしいPodがちゃんと起動していますね!

Cilium Ingressの為のNLBもLoadBalancerタイプのServiceとして作成されています。

```
[cloudshell-user@ip-10-130-34-57 terraform]$ kubectl get svc -A | grep cilium
kube-system   cilium-ingress                      LoadBalancer   172.20.254.89    k8s-kubesyst-ciliumin-2ba9716b18-dfe80b478d25f342.elb.us-west-2.amazonaws.com   80:31121/TCP,443:31115/TCP   17m
```

```values_cilium.yaml```ファイルで指定したとおり、```internet-facing```で作成されていますねー

![2.NLBが作成された.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1905292/86a7816a-f204-b42b-628d-5dfca6beb43a.png)

## Step 3.1 - kube-proxyアドオンをアンインストール(オプション)

上述の通り```kube-proxy```はCiliumで置き換えられますので、EKSアドオンとして稼動している```kube-proxy```は削除が可能です。

以下のコマンドで現状インストールされているアドオンを確認してみましょう。

```
aws eks list-addons --cluster-name  terraform --region us-west-2
```

```json
[cloudshell-user@ip-10-132-51-169 ~]$ aws eks list-addons --cluster-name  terraform --region us-west-2
{
    "addons": [
        "coredns",
        "kube-proxy",   ← ★kube-proxyアドオンがインストールされている。
        "vpc-cni"
    ]
}
```
kube-proxyアドオンがインストールされている事が分りますね。
では削除してみましょう。以下のコマンドで削除できます。


```
aws eks delete-addon --cluster-name terraform --addon-name kube-proxy --region us-west-2
```

```json
[cloudshell-user@ip-10-132-51-169 terraform]$ aws eks delete-addon --cluster-name terraform --addon-name kube-proxy --region us-west-2
{
    "addon": {
        "addonName": "kube-proxy",
        "clusterName": "terraform",
        "status": "DELETING",
        "addonVersion": "v1.29.0-eksbuild.1",
        "health": {
            "issues": []
        },
        "addonArn": "arn:aws:eks:us-west-2:623649842711:addon/terraform/kube-proxy/bac86796-b972-ec67-b07d-ba05add90120",
        "createdAt": "2024-07-20T08:06:20.865000+00:00",
        "modifiedAt": "2024-07-20T14:10:11.400000+00:00",
        "tags": {
            "Blueprint": "terraform",
            "GithubRepo": "github.com/aws-ia/terraform-aws-eks-blueprints"
        }
    }
}
```

再度インストールされているアドオン一覧を確認しましょう。

```json
[cloudshell-user@ip-10-132-51-169 terraform]$ aws eks list-addons --cluster-name  terraform --region us-west-2
{
    "addons": [
        "coredns",
        "vpc-cni"
    ]
}
```

削除されている事が分りますね!

```kubectl get pod -A```を実行してみても先程まであった```kube-proxy```Podが無くなっていました。```cilium```Podが代りになって```kube-proxy```の仕事をするみたいです。面白いですね!!

```
[cloudshell-user@ip-10-132-51-169 terraform]$ kubectl get pod -A
NAMESPACE     NAME                                           READY   STATUS    RESTARTS   AGE
kube-system   aws-load-balancer-controller-999bf8598-n6fmh   1/1     Running   0          6h9m
kube-system   aws-load-balancer-controller-999bf8598-wkllq   1/1     Running   0          6h9m
kube-system   aws-node-7bbjh                                 2/2     Running   0          6h8m
kube-system   aws-node-7r4sp                                 2/2     Running   0          6h8m
kube-system   cilium-5qm5f                                   1/1     Running   0          5h43m
kube-system   cilium-7mdgf                                   1/1     Running   0          5h43m
kube-system   cilium-operator-689df79c69-6dj27               1/1     Running   0          5h43m
kube-system   cilium-operator-689df79c69-8n7jg               1/1     Running   0          5h43m
kube-system   coredns-5b8cc885bc-96s2q                       1/1     Running   0          5h43m
kube-system   coredns-5b8cc885bc-fxxnp                       1/1     Running   0          5h43m
kube-system   hubble-relay-6f6f5476d9-5kh86                  1/1     Running   0          5h43m
kube-system   hubble-ui-644d9df97c-l72lc                     2/2     Running   0          5h43m
```

最近発表されたアップデートで、```Amazon VPC CNI```, ```CoreDNS```, ```kube-proxy```アドオンをインストールしないでEKSクラスタを構築できる様になりました。EKSクラスタ構築時点からCiliumを使うのであればここでインストールしない様にしておけば手間を省けますね。

https://aws.amazon.com/jp/about-aws/whats-new/2024/06/amazon-eks-cluster-creation-flexibility-networking-add-ons/

# Step 4 - サンプルアプリケーションをデプロイ

リポジトリ内にあるサンプルアプリをhelmでインストールします。

```
kubectl create namespace workshop

cd .. 
cd productapp
helm install productapp . -n workshop
```

```
[cloudshell-user@ip-10-132-51-169 terraform]$ kubectl create namespace workshop
namespace/workshop created
[cloudshell-user@ip-10-132-51-169 terraform]$ cd .. 
[cloudshell-user@ip-10-132-51-169 cilium-mesh-on-eks]$ cd productapp
[cloudshell-user@ip-10-132-51-169 productapp]$ helm install productapp . -n workshopNAME: productapp
LAST DEPLOYED: Sat Jul 20 14:29:42 2024
NAMESPACE: workshop
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
Product Catalog Application is succesfully installed !
```
インストール成功したみたいですねー


# Step 5 - サンプルアプリの構成を確認

サンプルアプリは```Frontend```,```Product Catalog```,```Catalog Detail```という3種類のPodから構成されています。マイクロサービスアーキテクチャですね。

![application_architecture.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1905292/148f7cc4-3a25-87e8-fc97-3d59932f9745.png)
サンプルアプリの構成図(AWS公式サンプルのGitHubから引用)

サンプルアプリのk8sリソースは以下のコマンドで確認できます。

```
kubectl get deployment,pod,service  -n workshop
```

```
[cloudshell-user@ip-10-132-51-169 productapp]$ kubectl get deployment,pod,service  -n workshop
NAME                             READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/catalogdetail    1/1     1            1           9m58s
deployment.apps/catalogdetail2   1/1     1            1           9m58s
deployment.apps/frontend         1/1     1            1           9m58s
deployment.apps/productcatalog   1/1     1            1           9m58s

NAME                                  READY   STATUS    RESTARTS   AGE
pod/catalogdetail-5896fff6b8-4z48j    1/1     Running   0          9m58s
pod/catalogdetail2-7d7d5cd48b-zwdfq   1/1     Running   0          9m58s
pod/frontend-78f696695b-sgb79         1/1     Running   0          9m58s
pod/productcatalog-64848f7996-skk9d   1/1     Running   0          9m58s

NAME                     TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
service/catalogdetail    ClusterIP   172.20.189.226   <none>        3000/TCP   9m58s
service/frontend         ClusterIP   172.20.5.113     <none>        9000/TCP   9m58s
service/productcatalog   ClusterIP   172.20.134.52    <none>        5000/TCP   9m58s
```

```catalogdetail```はバージョンが違うのか、2種類のDeployment,Podが作成されています。このあたり、Ciliumのサービスメッシュの機能であるTraffic Shiftingで```productcatalog```からのリクエストを振り分けられそうです。


# Step 6 - サンプルアプリにアクセスするためにIngressを設定

エンドユーザからのアクセスを受け付けるためにはIngressを作成する必要があります。
以下のコマンドで作成します。

```yaml
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  namespace : workshop
  name: productappingress # name given to the ingress
spec:
  ingressClassName: cilium
  rules:
  - http:
      paths:
      - path: / # this rule applies to all requests that specifies this path
        pathType: Prefix
        backend:
          service:
            name: frontend # route all these requests to this service
            port:
              number: 9000 # route the requests to this port of the frontend service
EOF
```

```ingressClassName```を```cilium```で設定しているのが特徴的ですね。それ以外は通常のIngressと変らないシンプルな設定になっています。

```
[cloudshell-user@ip-10-132-51-169 productapp]$ cat <<EOF | kubectl apply -f -
> apiVersion: networking.k8s.io/v1
> kind: Ingress
> metadata:
>   namespace : workshop
>   name: productappingress # name given to the ingress
> spec:
>   ingressClassName: cilium
>   rules:
>   - http:
>       paths:
>       - path: / # this rule applies to all requests that specifies this path
>         pathType: Prefix
>         backend:
>           service:
>             name: frontend # route all these requests to this service
>             port:
>               number: 9000 # route the requests to this port of the frontend service
> EOF
ingress.networking.k8s.io/productappingress created
[cloudshell-user@ip-10-132-51-169 productapp]$ kubectl get ingress -A
NAMESPACE   NAME                CLASS    HOSTS   ADDRESS                                                                         PORTS   AGE
workshop    productappingress   cilium   *       k8s-kubesyst-ciliumin-2ba9716b18-dfe80b478d25f342.elb.us-west-2.amazonaws.com   80      11s
```

Ingressも正常に作成できました! 

# Step 7 - サンプルアプリにアクセス

ブラウザからサンプルアプリにアクセスしてみましょう。
以下のコマンドでURLを取得します。

```
CILIUM_INGRESS_URL=$(kubectl get svc cilium-ingress -n kube-system -o jsonpath='{.status.loadBalancer.ingress[*].hostname}')
echo "http://$CILIUM_INGRESS_URL"
```

```
[cloudshell-user@ip-10-132-51-169 productapp]$ CILIUM_INGRESS_URL=$(kubectl get svc cilium-ingress -n kube-system -o jsonpath='{.status.loadBalancer.ingress[*].hostname}')
[cloudshell-user@ip-10-132-51-169 productapp]$ echo "http://$CILIUM_INGRESS_URL"
http://k8s-kubesyst-ciliumin-2ba9716b18-dfe80b478d25f342.elb.us-west-2.amazonaws.com
```

URLが返ってきましたねー このURLをブラウザに入れるとサンプルアプリが表示されます。

![サンプルアプリ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1905292/a0624ec8-4327-32b3-53ac-75d8c6d096de.png)

:::note warn
httpプロトコルが原因だと思いますが、ブラウザをシークレットモードにしないと以下のメッセージが出てアプリが正常に表示されませんでした。

```
upstream connect error or disconnect/reset before headers. reset reason: connection failure
```

Githubのページにも書いてあったのですが、、気付かずアプリが悪いのかと思いログを見たりで時間を潰してしまいました。。。ご注意ください。
:::


# Step 8 - Cilium Hubbleにアクセスして可視化されたPod間の通信を確認する

上述の通りCiliumをhelmでインストールする際にHubbleを合せてインストールしています。
ちょっとHubbleに接続してみましょう。

Githubのページでは、以下のコマンドが紹介されています。
```
cilium hubble ui
```

このコマンドを実行するとポートフォワード設定が自動で行われてHubble画面を表示できる様になります。ただ、私はAWS Cloud Shellを使って検証を行っておりポートフォワーディングで自分のブラウザからアクセスができませんでした。。
なので、以下の様にHubble用のIngressを作ってインターネット経由でアクセスしました。

```hubbleui_ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: hubbleuiingress
  namespace: kube-system
  annotations:
    ingress.cilium.io/loadbalancer-mode: dedicated
    service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing
spec:
  ingressClassName: cilium
  rules:
  - http:
      paths:
      - backend:
          service:
            name: hubble-ui
            port:
              number: 80
        path: /
        pathType: Prefix
```

```ingress.cilium.io/loadbalancer-mode: dedicated```でこのIngressリソース専用のNLBを立てる様にしています。
このAnnotationを付与しないと、サンプルアプリのIngressリソースとNLBを共用しますが、Pathでの振り分けが上手くいかなかったのでNLBから分ける様にしました。

```
[cloudshell-user@ip-10-136-121-133 ~]$ kubectl apply -f hubbleui_ingress.yaml                                  
ingress.networking.k8s.io/hubbleuiingress created
[cloudshell-user@ip-10-136-121-133 ~]$ kubectl get ingress -A
NAMESPACE     NAME                CLASS    HOSTS   ADDRESS                                                                         PORTS   AGE
kube-system   hubbleuiingress     cilium   *       k8s-kubesyst-ciliumin-2a89af3008-e3d4b9f38bec70b3.elb.us-west-2.amazonaws.com   80      6s
workshop      productappingress   cilium   *       k8s-kubesyst-ciliumin-2ba9716b18-dfe80b478d25f342.elb.us-west-2.amazonaws.com   80      9h
```

作成されたNLBのDNS名をブラウザに入れてみましょう。Hubbleの画面が表示されました!各Pod間の繋りがちゃんと表示できています。


![hubble画面を表示できた.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1905292/77813d1a-4a83-0320-f8b1-d3081e935507.png)

```productcatalog```Podから```catalogdetail```Podと```catalogdetail2 ```の両方に線で伸びている事が分ります。
これはServiceが```catalogdetail```の1つだけ作成されており、このServiceから```catalogdetail```Podと```catalogdetail2 ```の両方に振り分けられる様に設定されているためです。

```
[cloudshell-user@ip-10-136-121-133 ~]$ kubectl get svc -n workshop
NAME             TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
catalogdetail    ClusterIP   172.20.189.226   <none>        3000/TCP   10h   ← ★Serviceがcatalogdetailの一つだけ
frontend         ClusterIP   172.20.5.113     <none>        9000/TCP   10h
productcatalog   ClusterIP   172.20.134.52    <none>        5000/TCP   10h
[cloudshell-user@ip-10-136-121-133 ~]$ kubectl get deployment -n workshop
NAME             READY   UP-TO-DATE   AVAILABLE   AGE
catalogdetail    1/1     1            1           10h
catalogdetail2   1/1     1            1           10h
frontend         1/1     1            1           10h
productcatalog   1/1     1            1           10h
```


# Step 9 - Ciliumサービスメッシュの機能でPodのTraffic Shifting を試す

```catalogdetail```Podと```catalogdetail2 ```Podを使ってCiliumのTraffic Shiftingを試してみましょう。
まずは現状1つだったServiceリソースを```catalogdetail```と```catalogdetail2 ```のそれぞれに振り分けるために2つ作成します。

```yaml
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
  labels:
    app: catalogdetail
  name: catalogdetailv1
  namespace: workshop
spec:
  ports:
  - name: http
    port: 3000
    protocol: TCP
    targetPort: 3000
  selector:
    app: catalogdetail
    version: v1
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: catalogdetail
  name: catalogdetailv2
  namespace: workshop
spec:
  ports:
  - name: http
    port: 3000
    protocol: TCP
    targetPort: 3000
  selector:
    app: catalogdetail
    version: v2
EOF
```

```selector```として```app: catalogdetail```タグに加えて```version```タグを使う事で```catalogdetail```と```catalogdetail2 ```を区別します。


```
[cloudshell-user@ip-10-136-121-133 ~]$ cat <<EOF | kubectl apply -f -
> apiVersion: v1
> kind: Service
> metadata:
>   labels:
>     app: catalogdetail
>   name: catalogdetailv1
>   namespace: workshop
> spec:
>   ports:
>   - name: http
>     port: 3000
>     protocol: TCP
>     targetPort: 3000
>   selector:
>     app: catalogdetail
>     version: v1
> ---
> apiVersion: v1
> kind: Service
> metadata:
>   labels:
>     app: catalogdetail
>   name: catalogdetailv2
>   namespace: workshop
> spec:
>   ports:
>   - name: http
>     port: 3000
>     protocol: TCP
>     targetPort: 3000
>   selector:
>     app: catalogdetail
>     version: v2
> EOF

service/catalogdetailv1 created
service/catalogdetailv2 created
[cloudshell-user@ip-10-136-121-133 ~]$ kubectl get svc -n workshop
NAME              TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
catalogdetail     ClusterIP   172.20.189.226   <none>        3000/TCP   10h
catalogdetailv1   ClusterIP   172.20.31.251    <none>        3000/TCP   44s  ← ★追加された
catalogdetailv2   ClusterIP   172.20.103.189   <none>        3000/TCP   44s  ← ★追加された
frontend          ClusterIP   172.20.5.113     <none>        9000/TCP   10h
productcatalog    ClusterIP   172.20.134.52    <none>        5000/TCP   10h
```


次にTraffic Shifting の設定を行います。公式ものですと50％/50％で振り分ける設定になっていますが、Traffic Shiftingが効いているかより分りやすくするため```catalogdetailv1```：70％、```catalogdetailv2```：30％で設定してみました。



```yaml
cat <<EOF | kubectl apply -f -
apiVersion: cilium.io/v2
kind: CiliumEnvoyConfig
metadata:
  name: traffic-shifting-test
  namespace: workshop
spec:
  services:
    - name: catalogdetail
      namespace: workshop
  backendServices:
    - name: catalogdetailv1
      namespace: workshop
    - name: catalogdetailv2
      namespace: workshop
  resources:
    - "@type": type.googleapis.com/envoy.config.listener.v3.Listener
      name: traffic-shifting-test
      filter_chains:
        - filters:
            - name: envoy.filters.network.http_connection_manager
              typed_config:
                "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
                stat_prefix: traffic-shifting-test
                rds:
                  route_config_name: lb_route
                http_filters:
                  - name: envoy.filters.http.router
                    typed_config:
                      "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
    - "@type": type.googleapis.com/envoy.config.route.v3.RouteConfiguration
      name: lb_route
      virtual_hosts:
        - name: "lb_route"
          domains: [ "*" ]
          routes:
            - match:
                prefix: "/"
              route:
                weighted_clusters:
                  clusters:
                    - name: "workshop/catalogdetailv1"
                      weight: 70  ← ★v1を70％に設定
                    - name: "workshop/catalogdetailv2"
                      weight: 30  ← ★v2を30％に設定
                retry_policy:
                  retry_on: 5xx
                  num_retries: 3
                  per_try_timeout: 1s
    - "@type": type.googleapis.com/envoy.config.cluster.v3.Cluster
      name: "workshop/catalogdetailv1"
      connect_timeout: 2s
      lb_policy: ROUND_ROBIN
      type: EDS
      outlier_detection:
        split_external_local_origin_errors: true
        consecutive_local_origin_failure: 2
    - "@type": type.googleapis.com/envoy.config.cluster.v3.Cluster
      name: "workshop/catalogdetailv2"
      connect_timeout: 2s
      lb_policy: ROUND_ROBIN
      type: EDS
      outlier_detection:
        split_external_local_origin_errors: true
        consecutive_local_origin_failure: 2
EOF
```

```
[cloudshell-user@ip-10-136-121-133 ~]$ cat <<EOF | kubectl apply -f -
> apiVersion: cilium.io/v2
> kind: CiliumEnvoyConfig
> metadata:
>   name: traffic-shifting-test
>   namespace: workshop
> spec:
>   services:
>     - name: catalogdetail
>       namespace: workshop
>   backendServices:
>     - name: catalogdetailv1
>       namespace: workshop
>     - name: catalogdetailv2
>       namespace: workshop
>   resources:
>     - "@type": type.googleapis.com/envoy.config.listener.v3.Listener
>       name: traffic-shifting-test
>       filter_chains:
>         - filters:
>             - name: envoy.filters.network.http_connection_manager
>               typed_config:
>                 "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
>                 stat_prefix: traffic-shifting-test
>                 rds:
>                   route_config_name: lb_route
>                 http_filters:
>                   - name: envoy.filters.http.router
>                     typed_config:
>                       "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
>     - "@type": type.googleapis.com/envoy.config.route.v3.RouteConfiguration
>       name: lb_route
>       virtual_hosts:
>         - name: "lb_route"
>           domains: [ "*" ]
>           routes:
>             - match:
>                 prefix: "/"
>               route:
>                 weighted_clusters:
>                   clusters:
>                     - name: "workshop/catalogdetailv1"
>                       weight: 70 
>                     - name: "workshop/catalogdetailv2"
>                       weight: 30  
>                 retry_policy:
>                   retry_on: 5xx
>                   num_retries: 3
>                   per_try_timeout: 1s
>     - "@type": type.googleapis.com/envoy.config.cluster.v3.Cluster
>       name: "workshop/catalogdetailv1"
>       connect_timeout: 2s
>       lb_policy: ROUND_ROBIN
>       type: EDS
>       outlier_detection:
>         split_external_local_origin_errors: true
>         consecutive_local_origin_failure: 2
>     - "@type": type.googleapis.com/envoy.config.cluster.v3.Cluster
>       name: "workshop/catalogdetailv2"
>       connect_timeout: 2s
>       lb_policy: ROUND_ROBIN
>       type: EDS
>       outlier_detection:
>         split_external_local_origin_errors: true
>         consecutive_local_origin_failure: 2
> EOF
ciliumenvoyconfig.cilium.io/traffic-shifting-test created
[cloudshell-user@ip-10-136-121-133 ~]$ kubectl get ciliumenvoyconfig -n workshop
NAME                    AGE
traffic-shifting-test   101s
```

設定できました!

Traffic Shiftingが効いているか試してましょう。```productcatalog```Podから↑で作成したServiceに対して10回連続でcurlを打ってみましょう。

```
productcatalogpod=$(kubectl get pods -n workshop | awk '{print $1}' | grep -e "productcatalog")
for i in {1..10}; do echo "Output $i:"; kubectl -n workshop exec -it $productcatalogpod -- curl catalogdetail:3000/catalogDetail; echo ""; done
```
```
[cloudshell-user@ip-10-136-121-133 ~]$ for i in {1..10}; do echo "Output $i:"; kubectl -n workshop exec -it $productcatalogpod -- curl catalogdetail:3000/catalogDetail; echo ""; done
Output 1:
{"version":"1","vendors":["ABC.com"]}
Output 2:
{"version":"2","vendors":["ABC.com, XYZ.com"]}
Output 3:
{"version":"1","vendors":["ABC.com"]}
Output 4:
{"version":"1","vendors":["ABC.com"]}
Output 5:
{"version":"1","vendors":["ABC.com"]}
Output 6:
{"version":"1","vendors":["ABC.com"]}
Output 7:
{"version":"2","vendors":["ABC.com, XYZ.com"]}
Output 8:
{"version":"2","vendors":["ABC.com, XYZ.com"]}
Output 9:
{"version":"1","vendors":["ABC.com"]}
Output 10:
{"version":"1","vendors":["ABC.com"]}
```

```"version":"1"```が7回、```"version":"2"```が3回返ってきました。Traffic Shiftingで設定した割合と同じですね。ちゃんと効いている様です!

# まとめ

今回はGitHubで公開されているEKS上でCiliumを稼動させるサンプルを動かしてみました。
Ciliumを使う事で従来のkube-proxyやIngress Controllerが置き換わる事が判りとても勉強になりました。
また、Ciliumにはオブザーバビリティ機能であるHubbleも用意されており、これまでのEKSクラスタのコンポーネントの大部分を大胆に置き換わるものなのだなぁと実感しました。
今度は時間があればCiliumの性能検証なんかも試してみたいと思います。ではまた!