| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 | 12 | 13 | 14 |
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
| 22 | 23 | 24 | 25 | 26 | 27 | 28 |
- go
- 컨테이너
- AutoScaling
- directpv
- kubernetes
- 업그레이드
- AKS
- 쿠버네티스
- WSL
- Karpenter
- cilium
- minIO
- calico
- Azure
- gateway api
- ansible
- windows
- ipam
- Object Storage
- 묘공단
- vscode
- curl
- KEDA
- upgrade
- HPA
- EKS
- aws
- Timeout
- VPA
- ubuntu
- Today
- Total
a story
AKS의 Azure CNI Powered by Cilium 본문
이번 포스트에서는 Azure Kubernetes Service(AKS)에서 제공하는 CNI 옵션인 Azure CNI Powered by Cilium에 대해서 살펴보겠습니다.
목차
- Azure CNI Powered by Cilium 개요
- 실습 환경 구성
- ACNS(Advanced Container Networking Services)
- Azure CNI Powered by Cilium 제약 사항
1. Azure CNI Powered by Cilium 개요
Azure CNI Powered by Cilium은 AKS에서 매니지드로 제공하는 CNI의 하나로 Azure CNI의 control plane과 Cilium의 data plane을 결합하여 높은 성능의 네트워킹과 보안을 제공할 수 있는 옵션입니다.
Azure CNI Powered by Cilium를 사용하는 것은 Cilium을 CNI Plugin으로 단독 사용하는 것은 아니며, 기본적으로 CNI를 Azure CNI나 혹은 Azure CNI Overlay을 지정해야 하며, data plane에 대한 처리만 cilium에 위임하는 방식으로 사용됩니다.
쉽게는 IPAM(IP Address Management)은 Azure CNI의 방식을 사용하고, Service Routing, Network Policy, Observability와 같은 영역에 Cilium을 사용하는 것으로 이해할 수 있습니다.

출처: https://isovalent.com/blog/post/tutorial-azure-cni-powered-by-cilium/
이때, Azure CNI는 파드의 IP를 Virtual Network에서 할당 받는 방식이며(노드와 파드가 동일한 IP를 할당 받음), Azure CNI Overlay는 파드의 IP를 Overlay Network에서 할당 받습니다.
Azure CNI Powered by Cilium를 설치하는 명령을 살펴보면 조금 더 명확해 집니다.
아래는 기본 예시이며, --network-plugin은 azure를 지정하고, --network-dataplane으로 cilium을 지정합니다.
az aks create \
--name <clusterName> \
--resource-group <resourceGroupName> \
--location <location> \
--network-plugin azure \
--network-dataplane cilium \
--generate-ssh-keys
실습을 통해서 상세한 내용을 살펴보겠습니다.
2. 실습 환경 구성
실습 환경을 구성하여 Azure CNI Powered by Cilium를 살펴보겠습니다.
# Set environment variables
export LOCATION="koreacentral"
export CLUSTER_NAME="aks-cilium"
export RESOURCE_GROUP=${CLUSTER_NAME}-rg
# Create a resource group
az group create --name $RESOURCE_GROUP --location $LOCATION
# Create an AKS cluster
az aks create \
--name $CLUSTER_NAME \
--resource-group $RESOURCE_GROUP \
--location $LOCATION \
--network-plugin azure \
--network-plugin-mode overlay \
--pod-cidr 192.168.0.0/16 \
--network-dataplane cilium \
--node-count 2 \
--generate-ssh-keys
# Get a kubeconfig
az aks get-credentials -g $RESOURCE_GROUP -n $CLUSTER_NAME
$ kubectl get no
NAME STATUS ROLES AGE VERSION
aks-nodepool1-90499020-vmss000000 Ready <none> 112s v1.32.6
aks-nodepool1-90499020-vmss000001 Ready <none> 111s v1.32.6
Azure CNI Powered by Cilium 환경 확인
설치 후 생성된 파드를 살펴보면 기존 Azure CNI에서 일부 차이점을 알 수 있습니다.
$ kubectl get po -A
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system azure-cns-drv9w 1/1 Running 0 115s
kube-system azure-cns-xpdm5 1/1 Running 0 114s
kube-system azure-ip-masq-agent-4x2rs 1/1 Running 0 114s
kube-system azure-ip-masq-agent-gbrs7 1/1 Running 0 115s
kube-system cilium-5tldb 1/1 Running 0 114s
kube-system cilium-bgk9b 1/1 Running 0 115s
kube-system cilium-operator-7f9474864f-g9mh2 1/1 Running 0 3m44s
kube-system cilium-operator-7f9474864f-gr8wp 1/1 Running 0 3m44s
kube-system cloud-node-manager-625qw 1/1 Running 0 114s
kube-system cloud-node-manager-8zzqj 1/1 Running 0 115s
kube-system coredns-6f776c8fb5-44lcl 1/1 Running 0 3m34s
kube-system coredns-6f776c8fb5-mmjkk 1/1 Running 0 91s
kube-system coredns-autoscaler-864c4496bf-5jnrc 1/1 Running 0 3m34s
kube-system csi-azuredisk-node-8vkj4 3/3 Running 0 115s
kube-system csi-azuredisk-node-hw45x 3/3 Running 0 114s
kube-system csi-azurefile-node-bwmxb 3/3 Running 0 114s
kube-system csi-azurefile-node-zns9n 3/3 Running 0 115s
kube-system konnectivity-agent-799cb8b8d-bqpbn 1/1 Running 0 91s
kube-system konnectivity-agent-799cb8b8d-fvhsc 1/1 Running 0 3m33s
kube-system konnectivity-agent-autoscaler-6ddd978bfc-2vz76 1/1 Running 0 3m33s
kube-system metrics-server-867c8845b7-7hmlv 2/2 Running 2 (89s ago) 3m33s
kube-system metrics-server-867c8845b7-mv79z 2/2 Running 2 (89s ago) 3m33s
먼저 azure-cns 라는 파드가 생성되고, 또한 cilium과 cilium-operator가 설치됩니다. 이때 azure-cns는 IPAM 역할을 하는 컴포넌트이고, cilium이 Cilium Agent에 해당합니다. azure-cns가 IPAM의 역할을 하기는 하지만, 아래의 그림의 설명은 다소 분명하지 않은 설명이기는 합니다. 이후 추가로 설명하겠습니다.

출처: https://isovalent.com/blog/post/azure-cni-cilium/
Cilium 명령어로 확인도 가능하며 cilium status를 확인해보겠습니다.
https://docs.cilium.io/en/stable/gettingstarted/k8s-install-default/#install-the-cilium-cli
$ cilium status
/¯¯\
/¯¯\__/¯¯\ Cilium: OK
\__/¯¯\__/ Operator: OK
/¯¯\__/¯¯\ Envoy DaemonSet: disabled (using embedded mode)
\__/¯¯\__/ Hubble Relay: disabled
\__/ ClusterMesh: disabled
DaemonSet cilium Desired: 2, Ready: 2/2, Available: 2/2
Deployment cilium-operator Desired: 2, Ready: 2/2, Available: 2/2
Containers: cilium Running: 2
cilium-operator Running: 2
clustermesh-apiserver
hubble-relay
Cluster Pods: 9/9 managed by Cilium
Helm chart version:
Image versions cilium mcr.microsoft.com/containernetworking/cilium/cilium:v1.17.4-250610: 2
cilium-operator mcr.microsoft.com/containernetworking/cilium/operator-generic:v1.17.4-250610: 2
cilium에서 IPAM을 확인해보면 delegated-plugin이라고 표시된 것으로 보입니다.
$ cilium config view |grep ipam
enable-lb-ipam false
ipam delegated-plugin
ipam-cilium-node-update-rate 15s
기본적으로 IPAM은 Azure CNI Overlay를 사용하기 때문에 nodes나 ciliumNode에서 podCIDR에 대한 정보가 확인되지 않습니다.
Azure CNI Overlay에서는 nodenetworkconfigs라는 CRD가 생성되며, 노드 이름으로 생성된 오브젝트를 통해서노드에 할당된 Pod 대역을 확인할 수 있습니다.
# 정보 없음
$ kubectl get nodes -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.podCIDR}{"\n"}{end}'
aks-nodepool1-90499020-vmss000000
aks-nodepool1-90499020-vmss000001
$ kubectl get ciliumnode -o json | grep podCIDRs -A2
# nodenetworkconfigs 확인
$ kubectl get nodenetworkconfigs -A
NAMESPACE NAME ALLOCATED IPS NC MODE NC VERSION
kube-system aks-nodepool1-90499020-vmss000000 256 static 0
kube-system aks-nodepool1-90499020-vmss000001 256 static 0
$ kubectl describe nodenetworkconfigs -n kube-system aks-nodepool1-90499020-vmss000000
Name: aks-nodepool1-90499020-vmss000000
Namespace: kube-system
Labels: kubernetes.azure.com/podnetwork-delegationguid=
kubernetes.azure.com/podnetwork-subnet=
kubernetes.azure.com/podnetwork-type=overlay
managed=true
owner=aks-nodepool1-90499020-vmss000000
Annotations: <none>
API Version: acn.azure.com/v1alpha
Kind: NodeNetworkConfig
Metadata:
Creation Timestamp: 2025-08-29T12:52:56Z
Finalizers:
finalizers.acn.azure.com/dnc-operations
Generation: 1
Owner References:
API Version: v1
Block Owner Deletion: true
Controller: true
Kind: Node
Name: aks-nodepool1-90499020-vmss000000
UID: 418f6a0c-0555-44af-8a4d-a62bceb75d72
Resource Version: 1237
UID: 0a716407-4f0d-4917-9b0f-528b9a37735c
Spec:
Requested IP Count: 0
Status:
Assigned IP Count: 256
Network Containers:
Assignment Mode: static
Id: 3b07b4a6-e277-40f5-8ea0-0ec79f1f96d8
Node IP: 10.224.0.4
Primary IP: 192.168.0.0/24
Subnet Address Space: 192.168.0.0/16
Subnet Name: routingdomain_d4933ee0-95b7-5249-b245-1fd5e2033272_overlaysubnet
Type: overlay
Version: 0
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal CreatingNC 43m dnc-rc/nnc-reconciler Creating new Overlay NC 3b07b4a6-e277-40f5-8ea0-0ec79f1f96d8 for node 68b1a1ba215d820001b7128d_aks-nodepool1-90499020-vmss000000
Normal UpdatedNC 43m dnc-rc/nnc-reconciler Published NC 3b07b4a6-e277-40f5-8ea0-0ec79f1f96d8
$ kubectl get po -A -owide |grep aks-nodepool1-90499020-vmss000000 |grep 192.168.0.
default aks-helloworld-66fd479f49-psdqn 1/1 Running 0 17m 192.168.0.174 aks-nodepool1-90499020-vmss000000 <none> <none>
kube-system coredns-6f776c8fb5-44lcl 1/1 Running 0 61m 192.168.0.235 aks-nodepool1-90499020-vmss000000 <none> <none>
kube-system coredns-autoscaler-864c4496bf-5jnrc 1/1 Running 0 61m 192.168.0.17 aks-nodepool1-90499020-vmss000000 <none> <none>
kube-system konnectivity-agent-autoscaler-6ddd978bfc-2vz76 1/1 Running 0 61m 192.168.0.90 aks-nodepool1-90499020-vmss000000 <none> <none>
kube-system konnectivity-agent-c9dc4888c-xjg4q 1/1 Running 0 24m 192.168.0.204 aks-nodepool1-90499020-vmss000000 <none> <none>
노드 이름으로 생성된 nodenetworkconfigs를 확인해보면 Node IP와 Primary IP를 확인할 수 있습니다. 여기서 Primary IP가 바로 해당 노드에 할당된 Pod CIDR입니다.
CNI plugin 설정 확인
노드에서 CNI 설정 파일을 확인해보면 Azure CNI Powered by Cilium에서 컨테이너 네트워크를 구성하는 동작 과정을 추가로 확인해볼 수 있습니다.
CNI 설정을 확인해보면 cilium을 사용하지만, ipam은 azure-ipam을 사용하고 있는 것을 확인할 수 있습니다.
root@aks-nodepool1-90499020-vmss000000:/etc/cni/net.d# cat 05-cilium.conflist
{
"cniVersion": "0.3.1",
"name": "cilium",
"plugins": [
{
"type": "cilium-cni",
"ipam": {
"type": "azure-ipam"
},
"enable-debug": true,
"log-file": "/var/log/cilium-cni.log"
}
]
}
아래 위치에서 CNI 관련 바이너리를 확인할 수 있습니다.
root@aks-nodepool1-90499020-vmss000000:/opt/cni/bin# ll
total 221264
drwxr-xr-x 2 root root 4096 Aug 29 12:53 ./
drwxr-xr-x 3 root root 4096 Aug 29 12:52 ../
-rw-r--r-- 1 root root 11357 Jan 6 2025 LICENSE
-rw-r--r-- 1 root root 2343 Jan 6 2025 README.md
-rwxr-xr-x 1 root root 48499839 Aug 29 12:53 azure-ipam* # azure-ipam
-rwxr-xr-x 1 root root 4655178 Jan 6 2025 bandwidth*
-rwxr-xr-x 1 root root 5287212 Jan 6 2025 bridge*
-rwxr-xr-x 1 root root 86128032 Aug 29 12:53 cilium-cni* # cilium-cni
-rwxr-xr-x 1 root root 12762814 Jan 6 2025 dhcp*
-rwxr-xr-x 1 root root 4847854 Jan 6 2025 dummy*
..
앞서 살펴본 azure-cns 파드는 컨트롤 플레인에서 노드별 할당될 PodCIDR을 할당받는 역할을 하며, 이 PodCIDR에서 IP를 받아오는 역할을 하는 CNI 바이너리가 azure-ipam입니다. 이후 실행된 컨테이너의 veth 인터페이스를 생성하고 라우팅과 같은 컨테이너 네트워크를 구성하는 역할을 cilium-cni에서 수행합니다.
예를 들어, 샘플 파드를 실행했을 때 192.168.0.174가 할당된 것을 볼 수 있습니다.
$ kubectl get po -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
aks-helloworld-66fd479f49-psdqn 1/1 Running 0 10s 192.168.0.174 aks-nodepool1-90499020-vmss000000 <none> <none>
$ kubectl get po aks-helloworld-66fd479f49-psdqn -oyaml |grep -i startTime
startTime: "2025-08-29T13:35:44Z"
노드에서 해당 시점 azure-ipam의 로그를 확인해보면 azure-ipam에서 IP를 요청해서 할당 받는 것을 알 수 있습니다.
# cat /var/log/azure-ipam.log
...
{"level":"info","ts":"2025-08-29T13:35:44.490Z","msg":"ADD called","args":{"ContainerID":"24fc7966fdce8940d4a577270a7d3d9b83b684db10c1af483ad7082c94a63675","Netns":"/var/run/netns/cni-df3f15ec-1031-9829-65ec-9d75df07c626","IfName":"eth0","Args":"K8S_POD_INFRA_CONTAINER_ID=24fc7966fdce8940d4a577270a7d3d9b83b684db10c1af483ad7082c94a63675;K8S_POD_UID=154e459c-18d1-4732-914c-bdf3465637b8;IgnoreUnknown=1;K8S_POD_NAMESPACE=default;K8S_POD_NAME=aks-helloworld-66fd479f49-psdqn","Path":"/opt/cni/bin","NetnsOverride":"","StdinData":"eyJjbmlWZXJzaW9uIjoiMC4zLjEiLCJlbmFibGUtZGVidWciOnRydWUsImlwYW0iOnsidHlwZSI6ImF6dXJlLWlwYW0ifSwibG9nLWZpbGUiOiIvdmFyL2xvZy9jaWxpdW0tY25pLmxvZyIsIm5hbWUiOiJjaWxpdW0iLCJ0eXBlIjoiY2lsaXVtLWNuaSJ9"}}
{"level":"debug","ts":"2025-08-29T13:35:44.490Z","msg":"Parsed network config","netconf":{"cniVersion":"0.3.1","name":"cilium","type":"cilium-cni","ipam":{"type":"azure-ipam"},"dns":{}}}
# Created CNS IP config request
{"level":"debug","ts":"2025-08-29T13:35:44.490Z","msg":"Created CNS IP config request","request":{"desiredIPAddresses":null,"podInterfaceID":"24fc7966fdce8940d4a577270a7d3d9b83b684db10c1af483ad7082c94a63675","infraContainerID":"24fc7966fdce8940d4a577270a7d3d9b83b684db10c1af483ad7082c94a63675","orchestratorContext":{"PodName":"aks-helloworld-66fd479f49-psdqn","PodNamespace":"default"},"ifname":"eth0","secondaryInterfacesExist":false}}
{"level":"debug","ts":"2025-08-29T13:35:44.490Z","msg":"Making request to CNS"}
# Received CNS IP config response
{"level":"debug","ts":"2025-08-29T13:35:44.492Z","msg":"Received CNS IP config response","response":{"podIPInfo":[{"PodIPConfig":{"IPAddress":"192.168.0.174","PrefixLength":16},"NetworkContainerPrimaryIPConfig":{"IPSubnet":{"IPAddress":"192.168.0.0","PrefixLength":16},"DNSServers":null,"GatewayIPAddress":""},"HostPrimaryIPInfo":{"Gateway":"10.224.0.1","PrimaryIP":"10.224.0.4","Subnet":"10.224.0.0/16"},"NICType":"InfraNIC","InterfaceName":"","MacAddress":"","SkipDefaultRoutes":false,"Routes":null}],"response":{"ReturnCode":0,"Message":""}}}
{"level":"debug","ts":"2025-08-29T13:35:44.492Z","msg":"Parsed pod IP","podIPNet":"192.168.0.174/16"}
{"level":"info","ts":"2025-08-29T13:35:44.492Z","msg":"ADD success","result":{"cniVersion":"0.3.1","ips":[{"version":"4","address":"192.168.0.174/16"}],"dns":{}}}
또한 cilium-cni 의 로그도 확인할 수 있으며, 로그를 보면 veth pair를 생성하고, Interface를 구성하고, routing을 추가하는 과정을 확인할 수 있습니다.
cat /var/log/cilium-cni.log
...
time="2025-08-29T13:35:44.479426508Z" level=debug msg="Processing CNI ADD request" args="K8S_POD_INFRA_CONTAINER_ID=24fc7966fdce8940d4a577270a7d3d9b83b684db10c1af483ad7082c94a63675;K8S_POD_UID=154e459c-18d1-4732-914c-bdf3465637b8;IgnoreUnknown=1;K8S_POD_NAMESPACE=default;K8S_POD_NAME=aks-helloworld-66fd479f49-psdqn" containerID=24fc7966fdce8940d4a577270a7d3d9b83b684db10c1af483ad7082c94a63675 eventID=722b455e-0c13-460c-8b3f-e83970a10723 file-path=/opt/cni/bin ifName=eth0 netconf="&{NetConf:{CNIVersion:0.3.1 Name:cilium Type:cilium-cni Capabilities:map[] IPAM:{Type:} DNS:{Nameservers:[] Domain: Search:[] Options:[]} RawPrevResult:map[] PrevResult:<nil> ValidAttachments:[]} MTU:0 Args:{} EnableRouteMTU:false ENI:{InstanceID: InstanceType: MinAllocate:0 PreAllocate:0 MaxAboveWatermark:0 FirstInterfaceIndex:<nil> SecurityGroups:[] SecurityGroupTags:map[] SubnetIDs:[] SubnetTags:map[] NodeSubnetID: VpcID: AvailabilityZone: ExcludeInterfaceTags:map[] DeleteOnTermination:<nil> UsePrimaryAddress:<nil> DisablePrefixDelegation:<nil>} Azure:{InterfaceName:} IPAM:{IPAM:{Type:azure-ipam} IPAMSpec:{Pool:map[] IPv6Pool:map[] Pools:{Requested:[] Allocated:[]} PodCIDRs:[] MinAllocate:0 MaxAllocate:0 PreAllocate:0 MaxAboveWatermark:0 StaticIPTags:map[]}} AlibabaCloud:{InstanceType: AvailabilityZone: VPCID: CIDRBlock: VSwitches:[] VSwitchTags:map[] SecurityGroups:[] SecurityGroupTags:map[]} EnableDebug:true LogFormat: LogFile:/var/log/cilium-cni.log ChainingMode:}" netns=/var/run/netns/cni-df3f15ec-1031-9829-65ec-9d75df07c626 subsys=cilium-cni
# Created veth pair
time="2025-08-29T13:35:44.494814463Z" level=debug msg="Created veth pair" subsys=endpoint-connector vethPair="[tmp24fc7 lxc1f5ebcfa8510]"
# Configuring link
time="2025-08-29T13:35:44.562160797Z" level=debug msg="Configuring link" interface=eth0 ipAddr=192.168.0.174 netLink="&{LinkAttrs:{Index:21 MTU:1500 TxQLen:1000 Name:eth0 HardwareAddr:f6:dc:bb:b3:f7:eb Flags:broadcast|multicast RawFlags:4098 ParentIndex:22 MasterIndex:0 Namespace:<nil> Alias: AltNames:[] Statistics:0xc000878180 Promisc:0 Allmulti:0 Multi:1 Xdp:0xc0007280a8 EncapType:ether Protinfo:<nil> OperState:down PhysSwitchID:0 NetNsID:0 NumTxQueues:8 NumRxQueues:8 TSOMaxSegs:0 TSOMaxSize:0 GSOMaxSegs:65535 GSOMaxSize:65536 GROMaxSize:0 GSOIPv4MaxSize:0 GROIPv4MaxSize:0 Vfs:[] Group:0 PermHWAddr: ParentDev: ParentDevBus: Slave:<nil>} PeerName: PeerHardwareAddr: PeerNamespace:<nil>}" subsys=cilium-cni
# Adding route
time="2025-08-29T13:35:44.562876511Z" level=debug msg="Adding route" route="{Prefix:{IP:169.254.23.0 Mask:ffffffff} Nexthop:<nil> Local:<nil> Device: MTU:0 Priority:0 Proto:0 Scope:universe Table:0 Type:0}" subsys=cilium-cni
time="2025-08-29T13:35:44.563401457Z" level=debug msg="Adding route" route="{Prefix:{IP:0.0.0.0 Mask:00000000} Nexthop:169.254.23.0 Local:<nil> Device: MTU:1500 Priority:0 Proto:0 Scope:universe Table:0 Type:0}" subsys=cilium-cni
time="2025-08-29T13:35:44.691502014Z" level=debug msg="Endpoint successfully created" args="K8S_POD_INFRA_CONTAINER_ID=24fc7966fdce8940d4a577270a7d3d9b83b684db10c1af483ad7082c94a63675;K8S_POD_UID=154e459c-18d1-4732-914c-bdf3465637b8;IgnoreUnknown=1;K8S_POD_NAMESPACE=default;K8S_POD_NAME=aks-helloworld-66fd479f49-psdqn" containerID=24fc7966fdce8940d4a577270a7d3d9b83b684db10c1af483ad7082c94a63675 error="<nil>" eventID=722b455e-0c13-460c-8b3f-e83970a10723 file-path=/opt/cni/bin ifName=eth0 k8sNamespace=default k8sPodName=aks-helloworld-66fd479f49-psdqn netns=/var/run/netns/cni-df3f15ec-1031-9829-65ec-9d75df07c626 subsys=cilium-cni
기타 Azure CNI Powered by Cilium의 특성은 아래와 같습니다.
kube-proxy Replacement
앞서 살펴본 파드 정보에서 kube-proxy가 존재하지 않습니다. AKS에서 Azure CNI Powered by Cilium를 사용하는 경우 자동으로 kube-proxy를 사용하지 않게 되는 것을 알 수 있습니다. 이 경우, kube-proxy를 사용하도록 전환은 불가합니다.
Network Policy
AKS에서는 Network Policy를 지정하지 않는 경우 Network Policy Engine이 없는 상태로 클러스터가 구성됩니다. 기존 CNI Plugin에서는 Azure NPM(Network Policy Manager)나 혹은 Calico를 지정할 수 있지만, Azure CNI Powered by Cilium에서는 기본적으로 Cilium의 Network Policy를 사용할 수 있습니다.
물론 Network Policy를 사용하지 않는다면 여전히 none으로 지정할 수 있습니다.
CNI Plugin 업그레이드
AKS에서 Azure CNI Powered by Cilium를 사용하게 되면 이는 매지니드로 제공되는 기능이기 때문에 Cilium에 대한 업그레이드를 고민하지 않아도 됩니다.
AKS에서 제공하는 매니지드 컴포넌트는 각 쿠버네티스 버전에 대해서 검증을 하여 제공합니다. 클러스터 업그레이드(쿠버네티스 버전 업그레이드)를 수행하면 매니지드 컴포넌트의 버전도 업그레이드 되는 방식을 취하게 됩니다.
아래 문서에서 각 쿠버네티스 버전에 따른 최소 Cilium 버전을 아래 문서에서 확인할 수 있습니다.
https://learn.microsoft.com/en-us/azure/aks/azure-cni-powered-by-cilium#versions

쿠버네티스의 버전에 따른 컴포넌트의 Breaking changes에 Cilium의 버전의 변경도 같이 확인하실 수 있습니다.
여기까지 Azure CNI Powered by Cilium의 CNI 동작 과정과 특성을 살펴볼 수 있었습니다.
3. ACNS(Advanced Container Networking Services)
AKS에서 Azure CNI Powered by Cilium를 쓰면서 아쉬운 점은 Hubble을 바로 사용하기 어렵다는 점입니다.

출처: https://github.com/Azure/AKS/issues/3978
대신 ACNS(Advanced Container Networking Services)라는 별도의 컨테이너 네트워킹을 위한 서비스를 대안으로 제공하고 있습니다.
이러한 방향에는 Product 관점의 고민이 있었던 것으로 보입니다. Azure CNI Powered by Cilium를 사용하는 고객도 있지만, 기존 CNI(Azure CNI, Azure CNI Overlay)를 사용하는 고객도 컨테이너 네트워크 Observability를 제공해야 하는 문제가 있습니다. 그런 방향에서 Hubble이 아닌 ACNS를 제공하는 방향으로 결정된 것이 아닌지 추정됩니다.
아래 그림을 보면 Cilium 노드에서는 Cilium Agent의 Hubble을 사용하고, Non-Cilium 노드에는 Retina를 통해서 Hubble을 enable하도록 제공하고 있습니다. 이 환경에서 추가로 Hubble CLI나 UI를 활성화 할 수 있습니다.

기본적으로 Azure CNI Powered by Cilium만 설치한 경우에는 hubble-relay가 설치되어 있지 않습니다.
$ kubectl get pods -o wide -n kube-system -l k8s-app=hubble-relay
No resources found in kube-system namespace.
아래 문서를 바탕으로 ACNS를 활성화하고 Hubble UI를 사용해보겠습니다.
먼저 기존 클러스터에 ACNS를 활성화 합니다. 해당 명령은 단순히 Hubble relay를 배포하는 것이 아닌, ACNS의 다른 컴포넌트들을 배포하는 역할을 함께 수행합니다.
az aks update \
--resource-group $RESOURCE_GROUP \
--name $CLUSTER_NAME \
--enable-acns
해당 명령을 수행하면 cilium-operator 및 cilium-agent 파드들이 자동으로 재시작하게 되며, 또한 acns-security-agent, 기타 hubble 관련된 파드들이 추가로 실행됩니다.
$ kubectl get po -A
NAMESPACE NAME READY STATUS RESTARTS AGE
default aks-helloworld-66fd479f49-psdqn 1/1 Running 0 38m
kube-system acns-security-agent-sf46s 1/1 Running 0 60s
kube-system acns-security-agent-wpgtd 1/1 Running 0 60s
kube-system azure-cns-drv9w 1/1 Running 0 81m
kube-system azure-cns-xpdm5 1/1 Running 0 81m
kube-system azure-ip-masq-agent-4x2rs 1/1 Running 0 81m
kube-system azure-ip-masq-agent-gbrs7 1/1 Running 0 81m
kube-system cilium-24tjm 0/1 Init:0/6 0 119s
kube-system cilium-5tldb 1/1 Running 0 81m
kube-system cilium-operator-7f5458cf6f-gvf88 1/1 Running 0 119s
kube-system cilium-operator-7f5458cf6f-jj2zq 1/1 Running 0 119s
kube-system cloud-node-manager-625qw 1/1 Running 0 81m
kube-system cloud-node-manager-8zzqj 1/1 Running 0 81m
kube-system coredns-6f776c8fb5-44lcl 1/1 Running 0 83m
kube-system coredns-6f776c8fb5-mmjkk 1/1 Running 0 81m
kube-system coredns-autoscaler-864c4496bf-5jnrc 1/1 Running 0 83m
kube-system csi-azuredisk-node-8vkj4 3/3 Running 0 81m
kube-system csi-azuredisk-node-hw45x 3/3 Running 0 81m
kube-system csi-azurefile-node-bwmxb 3/3 Running 0 81m
kube-system csi-azurefile-node-zns9n 3/3 Running 0 81m
kube-system hubble-generate-certs-clwpz 0/1 Completed 0 48s
kube-system hubble-relay-bfb769b86-dmjpt 0/1 ContainerCreating 0 48s
kube-system konnectivity-agent-autoscaler-6ddd978bfc-2vz76 1/1 Running 0 83m
kube-system konnectivity-agent-c9dc4888c-5dxdt 1/1 Running 0 46m
kube-system konnectivity-agent-c9dc4888c-xjg4q 1/1 Running 0 46m
kube-system metrics-server-6c4cb48ddc-sr9s5 2/2 Running 0 78m
kube-system metrics-server-6c4cb48ddc-t5vmh 2/2 Running 0 78m
참고로, acns-security-agent 는 FQDN-based filtering과 Layer 7 policy를 지원해주는 컴포넌트입니다.
잠시 후 확인해보면 hubble-relay가 정상적으로 실행 중인 것으로 확인됩니다.
$ kubectl get pods -o wide -n kube-system -l k8s-app=hubble-relay
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
hubble-relay-bfb769b86-dmjpt 1/1 Running 0 2m43s 192.168.0.177 aks-nodepool1-90499020-vmss000000 <none> <none>
이후 Hubble UI를 생성하겠습니다. Hubble UI 자체는 Addon으로 제공되지 않기 때문에 아래 문서에서 yaml을 확인하시고 배포하실 수 있습니다.
$ cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
name: hubble-ui
namespace: kube-system
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: hubble-ui
labels:
app.kubernetes.io/part-of: retina
rules:
- apiGroups:
- networking.k8s.io
resources:
- networkpolicies
verbs:
- get
- list
- watch
- apiGroups:
- ""
resources:
- componentstatuses
- endpoints
- namespaces
- nodes
- pods
- services
verbs:
- get
- list
- watch
- apiGroups:
- apiextensions.k8s.io
resources:
- customresourcedefinitions
verbs:
- get
- list
- watch
- apiGroups:
- cilium.io
resources:
- "*"
verbs:
- get
- list
- watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: hubble-ui
labels:
app.kubernetes.io/part-of: retina
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: hubble-ui
subjects:
- kind: ServiceAccount
name: hubble-ui
namespace: kube-system
---
apiVersion: v1
kind: ConfigMap
metadata:
name: hubble-ui-nginx
namespace: kube-system
data:
nginx.conf: |
server {
listen 8081;
server_name localhost;
root /app;
index index.html;
client_max_body_size 1G;
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
# CORS
add_header Access-Control-Allow-Methods "GET, POST, PUT, HEAD, DELETE, OPTIONS";
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Max-Age 1728000;
add_header Access-Control-Expose-Headers content-length,grpc-status,grpc-message;
add_header Access-Control-Allow-Headers range,keep-alive,user-agent,cache-control,content-type,content-transfer-encoding,x-accept-content-transfer-encoding,x-accept-response-streaming,x-user-agent,x-grpc-web,grpc-timeout;
if ($request_method = OPTIONS) {
return 204;
}
# /CORS
location /api {
proxy_http_version 1.1;
proxy_pass_request_headers on;
proxy_hide_header Access-Control-Allow-Origin;
proxy_pass http://127.0.0.1:8090;
}
location / {
try_files $uri $uri/ /index.html /index.html;
}
# Liveness probe
location /healthz {
access_log off;
add_header Content-Type text/plain;
return 200 'ok';
}
}
}
---
kind: Deployment
apiVersion: apps/v1
metadata:
name: hubble-ui
namespace: kube-system
labels:
k8s-app: hubble-ui
app.kubernetes.io/name: hubble-ui
app.kubernetes.io/part-of: retina
spec:
replicas: 1
selector:
matchLabels:
k8s-app: hubble-ui
template:
metadata:
labels:
k8s-app: hubble-ui
app.kubernetes.io/name: hubble-ui
app.kubernetes.io/part-of: retina
spec:
serviceAccountName: hubble-ui
automountServiceAccountToken: true
containers:
- name: frontend
image: mcr.microsoft.com/oss/cilium/hubble-ui:v0.12.2
imagePullPolicy: Always
ports:
- name: http
containerPort: 8081
livenessProbe:
httpGet:
path: /healthz
port: 8081
readinessProbe:
httpGet:
path: /
port: 8081
resources: {}
volumeMounts:
- name: hubble-ui-nginx-conf
mountPath: /etc/nginx/conf.d/default.conf
subPath: nginx.conf
- name: tmp-dir
mountPath: /tmp
terminationMessagePolicy: FallbackToLogsOnError
securityContext: {}
- name: backend
image: mcr.microsoft.com/oss/cilium/hubble-ui-backend:v0.12.2
imagePullPolicy: Always
env:
- name: EVENTS_SERVER_PORT
value: "8090"
- name: FLOWS_API_ADDR
value: "hubble-relay:443"
- name: TLS_TO_RELAY_ENABLED
value: "true"
- name: TLS_RELAY_SERVER_NAME
value: ui.hubble-relay.cilium.io
- name: TLS_RELAY_CA_CERT_FILES
value: /var/lib/hubble-ui/certs/hubble-relay-ca.crt
- name: TLS_RELAY_CLIENT_CERT_FILE
value: /var/lib/hubble-ui/certs/client.crt
- name: TLS_RELAY_CLIENT_KEY_FILE
value: /var/lib/hubble-ui/certs/client.key
livenessProbe:
httpGet:
path: /healthz
port: 8090
readinessProbe:
httpGet:
path: /healthz
port: 8090
ports:
- name: grpc
containerPort: 8090
resources: {}
volumeMounts:
- name: hubble-ui-client-certs
mountPath: /var/lib/hubble-ui/certs
readOnly: true
terminationMessagePolicy: FallbackToLogsOnError
securityContext: {}
nodeSelector:
kubernetes.io/os: linux
volumes:
- configMap:
defaultMode: 420
name: hubble-ui-nginx
name: hubble-ui-nginx-conf
- emptyDir: {}
name: tmp-dir
- name: hubble-ui-client-certs
projected:
defaultMode: 0400
sources:
- secret:
name: hubble-relay-client-certs
items:
- key: tls.crt
path: client.crt
- key: tls.key
path: client.key
- key: ca.crt
path: hubble-relay-ca.crt
---
kind: Service
apiVersion: v1
metadata:
name: hubble-ui
namespace: kube-system
labels:
k8s-app: hubble-ui
app.kubernetes.io/name: hubble-ui
app.kubernetes.io/part-of: retina
spec:
type: ClusterIP
selector:
k8s-app: hubble-ui
ports:
- name: http
port: 80
targetPort: 8081
EOF
$ kubectl patch svc hubble-ui -n kube-system \
-p '{"spec": {"type": "LoadBalancer"}}'
생성된 hubble-ui 서비스를 LoadBalancer로 변경해서 접속하면 Hubble UI가 확인됩니다.

테스트 파드로 호출에 대해서도 기록이 남는 것으로 확인됩니다.

4. Azure CNI Powered by Cilium 제약 사항
AKS에서 Azure CNI Powered by Cilium을 사용하더라도 완전히 Cilium의 기능을 사용할 수 있는 것은 아닙니다. 아래와 같은 차이가 있습니다.
아래 제약 사항은 이후 변경될 수 있으며 문서를 통해서 재 확인이 필요합니다.
참고: https://learn.microsoft.com/en-us/azure/aks/azure-cni-powered-by-cilium#limitations
- Linux 노드에만 지원되며 Windows 노드는 불가합니다.
- Cilium configuration 수정이 불가합니다. 보다 상세한 설정이 필요한 경우 Byo CNI로 Cilium을 사용할 수 있습니다.
CiliumNetworkPolicy사용이 불가하며, 기본적으로 쿠버네티스의 NetworkPolicy 리소스만 지원합니다.ClusterwideCiliumNetworkPolicy사용이 불가합니다.- Cilium의 L7 Network Policy나 FQDN Filtering, 혹은 Container Network Observability를 사용하려고 하는 경우에는 ACNS(Advanced Container Networking Services)라는 별도의 컴포넌트를 사용해야 합니다.
참고: https://learn.microsoft.com/en-us/azure/aks/advanced-container-networking-services-overview?tabs=cilium - Cilium 데몬 셋에 직접 Resource limit을 지정(변경)할 수 없습니다. 일반적으로 AKS가 관리하는 매니지드 컴포넌트에 대해서는 수정이 지원되지 않습니다.
- 기본 문서에서 설명하지 않고 있는 ClusterMesh, ServiceMesh와 같은 Cilium의 확장된 기능은 지원되지 않습니다.
마치며
살펴보기로 Azure CNI Powered by Cilium 은 data plane 영역을 담당하는 제한적인 역할과 Security, Observability 부분을 제공하고 있습니다. 이를 통해서 확장성과 성능 측면에서 효과가 있는 것은 맞습니다.
아래 문서를 확인해보면 많은 노드와 파드를 사용하는 환경에서 Azure CNI Powered by Cilium에서 큰 성능에 이점이 있는 것을 확인할 수 있습니다.
다만 제한적인 기능을 제공하고 있기 때문에 Cilium의 모든 기능을 사용할 수 있는 것은 아닌 점은 유의가 필요합니다.
참고 링크
https://learn.microsoft.com/en-us/azure/aks/azure-cni-powered-by-cilium
https://docs.cilium.io/en/latest/installation/k8s-install-aks/
https://isovalent.com/blog/post/azure-cni-cilium/
https://isovalent.com/blog/post/tutorial-azure-cni-powered-by-cilium/
'Cilium' 카테고리의 다른 글
| [10] Cilium - Security (0) | 2025.09.06 |
|---|---|
| [9] Cilium - ServiceMesh (0) | 2025.08.23 |
| [8] Cilium - Cluster Mesh (0) | 2025.08.14 |
| [7] Cilium - BGP Control Plane (0) | 2025.08.14 |
| [6] Cilium - LoadBalancer IPAM, L2 Announcement (0) | 2025.08.08 |