a story

AKS의 Azure CNI Powered by Cilium 본문

Cilium

AKS의 Azure CNI Powered by Cilium

한명 2025. 8. 29. 23:53

이번 포스트에서는 Azure Kubernetes Service(AKS)에서 제공하는 CNI 옵션인 Azure CNI Powered by Cilium에 대해서 살펴보겠습니다.

 

목차

  1. Azure CNI Powered by Cilium 개요
  2. 실습 환경 구성
  3. ACNS(Advanced Container Networking Services)
  4. 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을 사용하는 것으로 이해할 수 있습니다.

Image

출처: 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-pluginazure를 지정하고, --network-dataplane으로 cilium을 지정합니다.

https://learn.microsoft.com/en-us/azure/aks/azure-cni-powered-by-cilium#option-3-assign-ip-addresses-from-the-node-subnet

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 라는 파드가 생성되고, 또한 ciliumcilium-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의 버전의 변경도 같이 확인하실 수 있습니다.

https://learn.microsoft.com/en-us/azure/aks/supported-kubernetes-versions?tabs=azure-cli#aks-components-breaking-changes-by-version

 

여기까지 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를 활성화 할 수 있습니다.

 

출처: https://learn.microsoft.com/en-us/azure/aks/advanced-container-networking-services-overview?tabs=cilium

 

 

기본적으로 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를 사용해보겠습니다.

참고: https://learn.microsoft.com/en-us/azure/aks/how-to-configure-container-network-logs?tabs=cilium#enable-advanced-container-networking-services-on-an-existing-cluster

 

먼저 기존 클러스터에 ACNS를 활성화 합니다. 해당 명령은 단순히 Hubble relay를 배포하는 것이 아닌, ACNS의 다른 컴포넌트들을 배포하는 역할을 함께 수행합니다.

az aks update \
    --resource-group $RESOURCE_GROUP \
    --name $CLUSTER_NAME \
    --enable-acns

 

해당 명령을 수행하면 cilium-operatorcilium-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를 지원해주는 컴포넌트입니다.

https://learn.microsoft.com/en-us/azure/aks/advanced-container-networking-services-overview?tabs=cilium#container-network-security

 

잠시 후 확인해보면 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을 확인하시고 배포하실 수 있습니다.

참고: https://learn.microsoft.com/en-us/azure/aks/how-to-configure-container-network-logs?tabs=cilium#visualize-by-using-the-hubble-ui

$ 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에서 큰 성능에 이점이 있는 것을 확인할 수 있습니다.

https://azure.microsoft.com/en-us/blog/azure-cni-with-cilium-most-scalable-and-performant-container-networking-in-the-cloud/

 

다만 제한적인 기능을 제공하고 있기 때문에 Cilium의 모든 기능을 사용할 수 있는 것은 아닌 점은 유의가 필요합니다.

 

 

참고 링크

https://learn.microsoft.com/en-us/azure/aks/azure-cni-powered-by-cilium

https://learn.microsoft.com/en-us/azure/aks/advanced-container-networking-services-overview?tabs=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/

https://isovalent.com/blog/post/upgrade-cilium-in-azure/#scenario-6-kubenet-to-azure-cni-powered-by-cilium-disabling-network-policy

'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