|
| 1 | +# Using Cilium with Argo Rollouts for header based traffic split |
| 2 | + |
| 3 | +## Prerequisites |
| 4 | + |
| 5 | +A Kubernetes cluster. If you do not have one, you can create one using [kind](https://kind.sigs.k8s.io/), [minikube](https://minikube.sigs.k8s.io/), or any other Kubernetes cluster. This guide will use Kind. |
| 6 | + |
| 7 | +## Step 1 - Create a Kind cluster by running the following command |
| 8 | + |
| 9 | +```shell |
| 10 | +kind create cluster --config ./kind-cluster.yaml |
| 11 | +``` |
| 12 | + |
| 13 | +## Step 2 - Install Cilium |
| 14 | + |
| 15 | +I will use helm to install Cilium in the cluster, but before that we'll need to install Gateway API CRDs. You can also install Cilium using [cilium CLI](https://docs.cilium.io/en/stable/gettingstarted/k8s-install-default/#install-the-cilium-cli). |
| 16 | + |
| 17 | +> [!NOTE] |
| 18 | +> Cilium `v1.18.2` supports Gateway API v1.2.0, per [docs](https://docs.cilium.io/en/stable/network/servicemesh/gateway-api/gateway-api/). |
| 19 | +
|
| 20 | +```shell |
| 21 | +kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/v1.2.0/config/crd/standard/gateway.networking.k8s.io_gatewayclasses.yaml |
| 22 | +kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/v1.2.0/config/crd/standard/gateway.networking.k8s.io_gateways.yaml |
| 23 | +kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/v1.2.0/config/crd/standard/gateway.networking.k8s.io_httproutes.yaml |
| 24 | +kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/v1.2.0/config/crd/standard/gateway.networking.k8s.io_referencegrants.yaml |
| 25 | +kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/v1.2.0/config/crd/standard/gateway.networking.k8s.io_grpcroutes.yaml |
| 26 | +``` |
| 27 | +```shell |
| 28 | +helm repo add cilium https://helm.cilium.io/ |
| 29 | +helm repo update |
| 30 | +helm install cilium cilium/cilium --version 1.18.2 \ |
| 31 | + --namespace kube-system \ |
| 32 | + --set image.pullPolicy=IfNotPresent \ |
| 33 | + --set ipam.mode=kubernetes \ |
| 34 | + --set cni.exclusive=false \ |
| 35 | + --set kubeProxyReplacement=true \ |
| 36 | + --set gatewayAPI.enabled=true \ |
| 37 | + --wait |
| 38 | +cilium status --wait |
| 39 | +``` |
| 40 | + |
| 41 | +## Step 3 - Install Argo Rollouts and Argo Rollouts plugin to instruct Cilium to manage the traffic |
| 42 | + |
| 43 | +```shell |
| 44 | +helm repo add argo https://argoproj.github.io/argo-helm |
| 45 | +helm repo update |
| 46 | +helm install argo-rollouts argo/argo-rollouts --version 2.40.4 \ |
| 47 | + --namespace argo-rollouts \ |
| 48 | + --create-namespace \ |
| 49 | + --set 'controller.trafficRouterPlugins[0].location=https://github.com/argoproj-labs/rollouts-plugin-trafficrouter-gatewayapi/releases/download/v0.8.0/gatewayapi-plugin-linux-amd64' \ |
| 50 | + --set 'controller.trafficRouterPlugins[0].name=argoproj-labs/gatewayAPI' |
| 51 | +``` |
| 52 | + |
| 53 | +## Step 4 - Create the services required for traffic split |
| 54 | + |
| 55 | +Create three Services required for canary based rollout strategy |
| 56 | + |
| 57 | +```shell |
| 58 | +kubectl apply -f service.yaml |
| 59 | +``` |
| 60 | + |
| 61 | +## Step 5 - Create HTTPRoute that defines a traffic split between two services |
| 62 | + |
| 63 | +> [!IMPORTANT] |
| 64 | +> For Cilium the K8s Services refs need to use `group: ""`. This is different than Linkerd, where `group: "core"` could be used. |
| 65 | + ```yaml |
| 66 | + apiVersion: gateway.networking.k8s.io/v1beta1 |
| 67 | + kind: HTTPRoute |
| 68 | + spec: |
| 69 | + parentRefs: |
| 70 | + - group: "" |
| 71 | + name: |
| 72 | + kind: Service |
| 73 | + port: |
| 74 | + rules: |
| 75 | + - backendRefs: |
| 76 | + - group: "" |
| 77 | + name: |
| 78 | + kind: Service |
| 79 | + port: |
| 80 | + ``` |
| 81 | +
|
| 82 | +Create a GAMMA [producer `HTTPRoute`](https://gateway-api.sigs.k8s.io/concepts/glossary/#producer-route) resource and connect it to a parent K8s service (using a canary and stable K8s services as backends) |
| 83 | + |
| 84 | +```shell |
| 85 | +kubectl apply -f httproute.yaml |
| 86 | +``` |
| 87 | + |
| 88 | +## Step 6 - Create an example Rollout |
| 89 | + |
| 90 | +Deploy a rollout to get the initial version |
| 91 | + |
| 92 | +```shell |
| 93 | +kubectl apply -f rollout.yaml |
| 94 | +``` |
| 95 | + |
| 96 | +## Step 7 - Patch the rollout to see the canary deployment |
| 97 | +```shell |
| 98 | +kubectl patch rollout rollouts-demo --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/env/0/value", "value": "1.1.0"}]' |
| 99 | +``` |
| 100 | + |
| 101 | +## Step 8 - Observe the rollout and HTTPRoute rule addition of [canary header matching rule](https://gateway-api.sigs.k8s.io/guides/traffic-splitting/#canary-traffic-rollout) |
| 102 | + |
| 103 | +```shell |
| 104 | +$ kubectl argo rollouts promote rollouts-demo # promote to Rollout step 1 |
| 105 | +$ kubectl argo rollouts get rollout rollouts-demo |
| 106 | +Name: rollouts-demo |
| 107 | +Namespace: default |
| 108 | +Status: ॥ Paused |
| 109 | +Message: CanaryPauseStep |
| 110 | +Strategy: Canary |
| 111 | + Step: 3/5 |
| 112 | + SetWeight: 0 |
| 113 | + ActualWeight: 0 |
| 114 | +Images: hashicorp/http-echo:1.0 (canary, stable) |
| 115 | +Replicas: |
| 116 | + Desired: 5 |
| 117 | + Current: 6 |
| 118 | + Updated: 1 |
| 119 | + Ready: 6 |
| 120 | + Available: 6 |
| 121 | +
|
| 122 | +NAME KIND STATUS AGE INFO |
| 123 | +⟳ rollouts-demo Rollout ॥ Paused 114s |
| 124 | +├──# revision:2 |
| 125 | +│ └──⧉ rollouts-demo-7bd564d79f ReplicaSet ✔ Healthy 23s canary |
| 126 | +│ └──□ rollouts-demo-7bd564d79f-tshpg Pod ✔ Running 6s ready:1/1 |
| 127 | +└──# revision:1 |
| 128 | + └──⧉ rollouts-demo-784858d6db ReplicaSet ✔ Healthy 114s stable |
| 129 | + ├──□ rollouts-demo-784858d6db-d799l Pod ✔ Running 114s ready:1/1 |
| 130 | + ├──□ rollouts-demo-784858d6db-hh44q Pod ✔ Running 114s ready:1/1 |
| 131 | + ├──□ rollouts-demo-784858d6db-nf2wh Pod ✔ Running 114s ready:1/1 |
| 132 | + ├──□ rollouts-demo-784858d6db-qn7dc Pod ✔ Running 114s ready:1/1 |
| 133 | + └──□ rollouts-demo-784858d6db-ww2q5 Pod ✔ Running 114s ready:1/1 |
| 134 | +$ |
| 135 | +$ kubectl get httproute argo-rollouts-http-route -o yaml | yq .spec.rules |
| 136 | +- backendRefs: |
| 137 | + - group: "" |
| 138 | + kind: Service |
| 139 | + name: argo-rollouts-stable-service |
| 140 | + port: 80 |
| 141 | + weight: 100 |
| 142 | + - group: "" |
| 143 | + kind: Service |
| 144 | + name: argo-rollouts-canary-service |
| 145 | + port: 80 |
| 146 | + weight: 0 |
| 147 | + matches: |
| 148 | + - path: |
| 149 | + type: PathPrefix |
| 150 | + value: / |
| 151 | +- backendRefs: |
| 152 | + - group: "" |
| 153 | + kind: Service |
| 154 | + name: argo-rollouts-canary-service |
| 155 | + port: 80 |
| 156 | + weight: 0 |
| 157 | + matches: |
| 158 | + - headers: |
| 159 | + - name: X-Test |
| 160 | + type: Exact |
| 161 | + value: test |
| 162 | + path: |
| 163 | + type: PathPrefix |
| 164 | + value: / |
| 165 | +``` |
| 166 | +```shell |
| 167 | +$ kubectl run -it --image nicolaka/netshoot:v0.13 network-test -- sh # run a pod to source curl tests |
| 168 | +~ # |
| 169 | +~ # # stable K8s service targets any of the 5 stable pods |
| 170 | +~ # seq 1 100 | xargs -P 10 -I {} bash -c 'curl -s http://argo-rollouts-stable-service' > pods.txt |
| 171 | +~ # sort pods.txt | uniq -c | sort -rn |
| 172 | + 26 Hello from rollouts-demo-784858d6db-qn7dc |
| 173 | + 20 Hello from rollouts-demo-784858d6db-hh44q |
| 174 | + 19 Hello from rollouts-demo-784858d6db-ww2q5 |
| 175 | + 18 Hello from rollouts-demo-784858d6db-d799l |
| 176 | + 17 Hello from rollouts-demo-784858d6db-nf2wh |
| 177 | +~ # |
| 178 | +~ # |
| 179 | +~ # # canary K8s service targets the one canary pod, created for the `setCanaryScale` step in the Rollout |
| 180 | +~ # seq 1 100 | xargs -P 10 -I {} bash -c 'curl -s http://argo-rollouts-canary-service' > pods.txt |
| 181 | +~ # sort pods.txt | uniq -c | sort -rn |
| 182 | + 100 Hello from rollouts-demo-7bd564d79f-tshpg |
| 183 | +~ # |
| 184 | +~ # |
| 185 | +~ # # GAMMA-type HTTPRoute's `.spec.parentRefs` K8s service only targets stable pods since no `setWeight` step is used in the Rollout |
| 186 | +~ # seq 1 100 | xargs -P 10 -I {} bash -c 'curl -s http://argo-rollouts-service' > pods.txt |
| 187 | +~ # sort pods.txt | uniq -c | sort -rn |
| 188 | + 22 Hello from rollouts-demo-784858d6db-d799l |
| 189 | + 21 Hello from rollouts-demo-784858d6db-hh44q |
| 190 | + 20 Hello from rollouts-demo-784858d6db-qn7dc |
| 191 | + 19 Hello from rollouts-demo-784858d6db-ww2q5 |
| 192 | + 18 Hello from rollouts-demo-784858d6db-nf2wh |
| 193 | +~ # |
| 194 | +``` |
| 195 | + |
| 196 | +## Step 12 - Test the header-based routing with curl |
| 197 | + |
| 198 | +You can test the header-based routing by sending requests with the specified header. |
| 199 | +With header it always goes to canary: |
| 200 | + |
| 201 | +```shell |
| 202 | +$ kubectl exec -it network-test -- sh |
| 203 | +~ # seq 1 100 | xargs -P 10 -I {} bash -c 'curl -s -H "X-Test: test" http://argo-rollouts-service' > pods.txt |
| 204 | +~ # sort pods.txt | uniq -c | sort -rn |
| 205 | + 100 Hello from rollouts-demo-7bd564d79f-tshpg |
| 206 | +~ # |
| 207 | +``` |
0 commit comments