이번 포스트에서는 EKS의 Fargate에 대해서 살펴보겠습니다.
EKS Fargate는 EKS의 노드 그룹을 사용하지 않고 컨테이너를 서버리스 컴퓨팅 엔진에 실행하는 방식입니다.
먼저 EKS Fargate를 살펴보고, 이와 유사한 AKS의 Virtual Nodes를 통해 각 Managed Kubernetes Service에서 노드를 사용하지 않고 컨테이너를 실행하기 위한 구현 방식을 살펴보고, 실습을 통해 확인해보습니다.
목차
- EKS의 Fargate
- AKS의 Virtual Nodes
1. EKS Fargate
일반적으로 EKS에서는 노드 그룹을 생성하여 워커 노드를 사용할 수 있습니다. EKS의 컴퓨팅을 제공하는 옵션 중 노드인 EC2 인스턴스를 활용하지 않는 방식으로 EKS Fargate가 있습니다.
먼저 AWS Fargate를 이해하기 위해서 Amazon ECS를 먼저 살펴보겠습니다.
AWS에서 컨테이너를 실행하는 방식 중 하나로 Amazon ECS(Elastic Container Service)라는 완전 관리형 컨테이너 오케스트레이션 서비스를 제공하고 있습니다. 사용자는 Amazon ECS를 통해서 컨테이너화된 애플리케이션을 쉽게 배포하고 관리할 수 있습니다.
Amazon ECS는 아래와 같이 세 가지 계층을 가지고 있는데, 이 중 ECS가 실행되는 인프라를 의미하는 Capacity options에 AWS Fargate가 있다는 것을 알 수 있습니다.

출처: https://docs.aws.amazon.com/ko_kr/AmazonECS/latest/developerguide/Welcome.html
ECS의 용량 옵션에서 EC2를 선택하면 실제 EC2 인스턴스를 통해 컨테이너가 실행됩니다. 반면 Fargate는 서버리스 종량제 컴퓨팅 엔진을 의미합니다. 즉 가상 머신 자체를 배포하지 않는 형태이기 때문에 경량이라는 장점이 있습니다.
EKS의 Fargate도 동일합니다. EKS는 노드 그룹을 통해서 EC2를 통해 사용자 워커 노드를 제공하는데, 서버리스 컴퓨팅 엔진인 Fargate를 활용할 수 있습니다.
아래와 같이 EKS의 파드가 실행되는 Data Plane을 위한 개별 옵션입니다.

출처: https://www.eksworkshop.com/docs/fundamentals/fargate/
Fargate와 같은 컴퓨팅 옵션은 보통 지속적으로 실행하지 않아도 되는 유형이면서, stateless 한 애플리케이션에 적합합니다. 특정 Job을 수행하고 종료하는 워크로드 혹은 빠른 배포가 필요하고 필요없는 경우 종료가 가능한 유형의 워크로드라면 서버리스 컴퓨팅 엔진을 활용하는 Fargate를 고려할 수 있습니다.
실습을 통해서 EKS Fargate를 더 살펴보겠습니다.
해당 실습은 Amazone EKS Blueprints for Terraform의 예제를 통해서 진행하겠습니다.
참고: https://aws-ia.github.io/terraform-aws-eks-blueprints/
# 테라폼 코드 가져오기
git clone https://github.com/aws-ia/terraform-aws-eks-blueprints
cd terraform-aws-eks-blueprints/patterns/fargate-serverless
# 테라폼 초기화
terraform init
# 테라폼 Plan 확인
terraform plan
# 테라폼 배포
# 배포 : EKS, Add-ons, fargate profile - 13분 소요
terraform apply -auto-approve
# 배포 완료 후 확인
terraform state list
module.eks.data.aws_caller_identity.current
...
terraform output
...
생성된 리소스를 살펴보면 fargate 형태의 노드가 4대 확인되며, 또한 파드가 각 노드에 실행 중인 것을 알 수 있습니다.
이때 파드 IP와 노드 IP가 같은 것을 알 수 있는데, EKS fargate에서는 각 파드를 위해서 하나의 fargate노드가 실행되는 구조라는 것을 알 수 있습니다.
# kubeconfig 획득
aws eks --region us-west-2 update-kubeconfig --name fargate-serverless
# 노드, 파드 정보 확인
kubectl get no -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
fargate-ip-10-0-1-239.us-west-2.compute.internal Ready <none> 48m v1.30.8-eks-2d5f260 10.0.1.239 <none> Amazon Linux 2 5.10.234-225.910.amzn2.x86_64 containerd://1.7.25
fargate-ip-10-0-18-94.us-west-2.compute.internal Ready <none> 48m v1.30.8-eks-2d5f260 10.0.18.94 <none> Amazon Linux 2 5.10.234-225.910.amzn2.x86_64 containerd://1.7.25
fargate-ip-10-0-20-74.us-west-2.compute.internal Ready <none> 48m v1.30.8-eks-2d5f260 10.0.20.74 <none> Amazon Linux 2 5.10.234-225.910.amzn2.x86_64 containerd://1.7.25
fargate-ip-10-0-35-232.us-west-2.compute.internal Ready <none> 48m v1.30.8-eks-2d5f260 10.0.35.232 <none> Amazon Linux 2 5.10.234-225.910.amzn2.x86_64 containerd://1.7.25
kubectl get pod -A -o wide
NAMESPACE NAME READY STATUS RESTARTS AGE IP NODE
kube-system aws-load-balancer-controller-c946d85dd-2n65t 1/1 Running 0 48m 10.0.35.232 fargate-ip-10-0-35-232.us-west-2.compute.internal <none> <none>
kube-system aws-load-balancer-controller-c946d85dd-2t662 1/1 Running 0 48m 10.0.18.94 fargate-ip-10-0-18-94.us-west-2.compute.internal <none> <none>
kube-system coredns-69fd949db7-95njt 1/1 Running 0 49m 10.0.20.74 fargate-ip-10-0-20-74.us-west-2.compute.internal <none> <none>
kube-system coredns-69fd949db7-b5jpf 1/1 Running 0 49m 10.0.1.239 fargate-ip-10-0-1-239.us-west-2.compute.internal <none> <none>
노드 정보를 살펴보면 comput-type에 대해서 Label과 Taint가 적용된 것을 알 수 있습니다.
kubectl describe node | grep -A 3 Labels
Labels: beta.kubernetes.io/arch=amd64
beta.kubernetes.io/os=linux
eks.amazonaws.com/compute-type=fargate
...
kubectl describe node | grep Taints
Taints: eks.amazonaws.com/compute-type=fargate:NoSchedule
...
EKS에서 Fargate를 사용하기 위해서 Fargate Profile을 생성해야 합니다. 이 프로파일은 Fargate를 사용할 리소스의 네임스페이스와 Label을 사전에 지정(selectors)합니다. 또한 파드가 배포되는 서브넷과 IAM Role에 대한 정보도 Fargate Profile에 포함됩니다.

실습의 terraform 코드에서는 아래와 같이 fargate profile을 지정한 것을 알 수 있습니다.
fargate_profiles = {
app_wildcard = {
selectors = [
{ namespace = "app-*" }
]
}
kube_system = {
name = "kube-system"
selectors = [
{ namespace = "kube-system" }
]
}
}
웹 콘솔에서는 아래와 같이 확인할 수 있습니다.

kube-system 이라는 프로파일로 인해서 실제로 kube-system 또한 관리형 노드 그룹을 사용하지 않고 모두 fargate 형태로 실행되었습니다.
API 서버에 Fargate에 해당하는 파드가 요청되면 Admission controller에 의해 Mutating Webhook으로 Fargate로 스케줄링되도록 정보가 변경됩니다.
이 과정을 세부적으로 살펴보면 아래와 같이 파드가 요청되면 Mutating Webhook에 의해서 Fargate Profile에 대한 정보와 schedulerName이 Fargate-scheduler로 지정됩니다. 이 정보를 바탕으로 Fargate Scheduler는 Fagate 환경에 파드가 스케줄링하고 파드가 실행됩니다.

출처: https://aws.amazon.com/ko/blogs/containers/the-role-of-aws-fargate-in-the-container-world/
이를 coredns 파드를 통해서 살펴보면 아래와 같이 fargate-profile과 또한 schedulerName이 지정된 것을 확인할 수 있습니다.
kubectl get po -n kube-system coredns-69fd949db7-95njt -oyaml |grep fargate
eks.amazonaws.com/fargate-profile: kube-system
nodeName: fargate-ip-10-0-20-74.us-west-2.compute.internal
schedulerName: fargate-scheduler
이렇게 요청된 파드의 정보가 Fargate Profile의 Selector에서 지정한 정보와 일치하는 지를 바탕으로 스케줄링을 수행하기 때문에 Fargate로 스케줄링된 리소스는 일반 노드에는 배포되지 않습니다.
일반 노드에 실행되는 워크로드와 Fargate에 실행되는 워크로드는 스케줄링에 있어 배타적인 관계입니다. 예를 들어, 노드가 부족한 경우라도 파드가 Fargate로 Burst해서 실행할 수 있는 구조가 아닙니다.
Fargate 자체는 사용자가 생성한 노드 리소스가 아니기 때문에 EC2 인스턴스에서는 인스턴스가 확인되지 않습니다.
이때 Network Interface는 확인이 가능합니다. 다만 아래의 정보와 같이 Network Interface이 Owner와 Instance의 Owner가 다르다는 것을 알 수 있습니다.

EKS Fargate가 사용자 VPC와 연계되는 방식은 아래와 같은 형태로 구성됩니다.

출처: https://www.kiranjthomas.com/posts/fargate-under-the-hood/
1) Fargate를 위한 EC2 인스턴스가 별도의 Fargate VPC에서 실행됩니다.
2) 이 인스턴스의 Primary Network Interface는 Fargate VPC에 위치하여, Container Runtime, Fargate Agent, Guest Kernel& OS를 위한 네트워크 트래픽을 처리합니다.
3) 이 인스턴스의 Secondary Network Interface가 사용자 VPC에 연결되어 컨테이너간 통신과 Image Pulling과 같은 네트워크 트래픽을 처리합니다.
위의 그림과 설명에서는 Fargate가 EC2로 표현되어 있지만 이는 Lightweight VM으로 알려진 Firecracker를 사용하고 있습니다.
EKS의 Fargate는 EC2 인스턴스를 유지하지 않아도 되기 때문에 비용 효과적이라고 생각할 수 있지만, 일반적으로 Fargate는 동일한 용량의 EC2에 비해서는 비용이 더 비싸게 책정됩니다. 이는 실행되는 파드를 위해서 노드에서 실행되는 kube-proxy, containerd, kubelet 컴포넌트가 배포되어 일부 추가적인 리소스를 사용하기 때문입니다.
이를 아래 장표에서 보시면 256MB 정도가 추가되는 것을 확인할 수 있습니다.

출처: https://www.youtube.com/watch?v=N0uLK5syctU
추가로 이러한 리소스는 Fargate 리소스 타입에 맞춰 반올림되어 구성되기 때문에, 실제 파드 spec의 request 용량보다 큰 사이즈의 Fargate 리소스가 사용되는 점도 아실 필요가 있습니다.
그러하므로 EKS의 Fargate 옵션은 비용 측면보다는 서버리스 워크로드에 적합한지 여부를 바탕으로 판단할 필요가 있습니다.
또한 EKS Fargate에는 다수 고려사항이 있으므로, 제약사항을 문서를 통해 사전에 확인하시기 바랍니다.
https://docs.aws.amazon.com/ko_kr/eks/latest/userguide/fargate.html#fargate-considerations
아래와 같이 리소스를 정리하고 실습을 마무리 하겠습니다.
terraform destroy -auto-approve
2. AKS Virtual Nodes
AKS에서는 노드를 사용하지 않고 Virtual Nodes를 사용하여 파드를 실행할 수 있습니다.
Azure에서는 ACI(Azure Container Instance)라는 서버리스 컨테이너 서비스를 가지고 있습니다(이는 AWS의 ECS와 같은 서비스 입니다). AKS에서 Virtual Nodes를 통해 파드를 실행하면 파드는 ACI의 형태로 실행된다고 볼 수 있습니다.
참고: https://learn.microsoft.com/ko-kr/azure/container-instances/container-instances-overview
AKS에서 Virtual Nodes를 사용하면 실제로 노드를 확인 했을 때 Virtual Nodes가 추가되는 형태로 보이는데, 이는 Virtual Kubelet이라는 오픈 소스를 기반으로 동작합니다.
Virtual Kubelet은 kubelet과 같이 동작하면서 쿠버네티스가 다른 API와 연계되도록 동작합니다. 이 방식을 통해서 다른 ACI, AWS Fargate 등과 같은 서비스를 통해서 노드를 사용하는 것 처럼 할 수 있습니다.
아래 그림은 Virtual Kubelet의 동작 방식으로, Virtual Kublet은 kubelet과 같이 자신을 노드로 등록하여, 실제로 파드가 Virtual Node에 스케줄링될 수도록 API를 구현하고 있습니다.
출처: https://github.com/virtual-kubelet/virtual-kubelet?tab=readme-ov-file
AKS에서 Virtual Nodes를 사용하게 되면 Virtual Nodes에 스케줄링이 되고, virtual kubelet이 ACI와 연계하여 파드를 실행하는 방식으로 동작하게 됩니다.
AKS에서는 addon 형태로 Virtual Nodes를 지원합니다.
아래 실습 문서를 바탕으로 진행하면서 AKS Virtual Nodes에 대해서 살펴보겠습니다.
https://docs.azure.cn/en-us/aks/virtual-nodes-cli
# 변수 선언
PREFIX=aks-vn
RG=${PREFIX}-rg
AKSNAME=${PREFIX}
LOC=koreacentral
VNET=aks-vnet
AKSSUBNET=aks-subnet
VNSUBNET=vn-subnet
# 리소스 그룹 생성
az group create --name $RG --location $LOC -o none
az network vnet create --resource-group $RG --name $VNET --address-prefixes 10.0.0.0/8 --subnet-name $AKSSUBNET --subnet-prefix 10.240.0.0/16 -o none
az network vnet subnet create --resource-group $RG --vnet-name $VNET --name $VNSUBNET --address-prefixes 10.241.0.0/16 -o none
SUBNET_ID=$(az network vnet subnet show --resource-group $RG --vnet-name $VNET --name $AKSSUBNET --query id -o tsv)
# AKS 클러스터 설치
az aks create --resource-group $RG --name $AKSNAME --node-count 2 --network-plugin azure --vnet-subnet-id $SUBNET_ID --generate-ssh-keys
# 노드 정보 확인
az aks get-credentials --resource-group $RG --name $AKSNAME
kubectl get nodes
NAME STATUS ROLES AGE VERSION
aks-nodepool1-14565790-vmss000000 Ready <none> 100s v1.30.9
aks-nodepool1-14565790-vmss000001 Ready <none> 100s v1.30.9
AKS를 생성하면 기본 노드 2대가 확인됩니다. EKS는 addon 컴포넌트들도 Fargate로 실행될수 있는 반면, AKS는 기본적인 시스템 컴포넌트는 여전히 일반 노드에서 실행이 필요합니다.
이제 Virtual Nodes addon을 활성화하고 다시 노드를 살펴보면 virtual node에 해당하는 노드가 확인됩니다.
# Virtual Nodes addon 활성화
az aks enable-addons --resource-group $RG --name $AKSNAME --addons virtual-node --subnet-name $VNSUBNET
# 노드 정보 확인
kubectl get nodes
NAME STATUS ROLES AGE VERSION
aks-nodepool1-14565790-vmss000000 Ready <none> 14m v1.30.9
aks-nodepool1-14565790-vmss000001 Ready <none> 14m v1.30.9
virtual-node-aci-linux Ready agent 2m51s v1.25.0-vk-azure-aci-1.6.2
실행 중인 파드를 살펴보면 aci-connector-linux라는 파드가 실행되는 것을 알 수 있는데, virtual kubelet의 역할을 수행하며 AKS 클러스터와 ACI의 Management API 간의 가교 역할을 수행합니다.
아래 명령으로 살펴보면 aci-connector-linux 와 노드의 IP가 10.240.0.32으로 동일한 것을 알 수 있습니다.
kubectl get po -A -owide
NAMESPACE NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
kube-system aci-connector-linux-79d9bf8946-7hv8s 1/1 Running 0 17m 10.240.0.32 aks-nodepool1-14565790-vmss000001 <none> <none>
..
kubectl get no -A -owide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
..
virtual-node-aci-linux Ready agent 15m v1.25.0-vk-azure-aci-1.6.2 10.240.0.32 <none> <unknown> <unknown> <unknown>
또한 포탈에서 확인해보면 AKS 노드를 위한 서브넷과 다르게, Virtual Node를 위해 생성된 서브넷은 실제로 ACI에서 배포를 진행하게 되므로 Azure Container Instance에 위임된 상태임을 알 수 있습니다.

EKS에서는 Fargate Profile을 생성하고, 특정 파드가 이 프로파일에 적용 가능하면 Fargate Scheduler에 의해서 Fargate로 배포가 되는 형태였습니다.
반면 Virtual Nodes에는 기본적으로 아래와 같은 Taint가 적용되어 있고, 기본적인 Taint, Toleration 방식을 통해서 일반 노드나 혹은 Virtual Nodes로 배포되도록 할 수 있습니다. 이는 일반적인 스케줄링 기법과 다르지 않습니다.
$ kubectl describe no virtual-node-aci-linux |grep -A 1 -B 1 Taint
CreationTimestamp: Sat, 22 Mar 2025 15:57:33 +0000
Taints: virtual-kubelet.io/provider=azure:NoSchedule
Unschedulable: false
그러하므로 Virtual Nodes에 실행되는 워크로드는 Toleration이 필요합니다. 만약 파드의 스케줄링을 Virtual nodes로 강제하지 않으면 일반 노드에서도 실행될 수 있다는 것을 알 수 있습니다.
아래로 샘플 애플리케이션을 배포해서 실제로 어떻게 배포되는지 살펴보겠습니다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: aci-helloworld
spec:
replicas: 4
selector:
matchLabels:
app: aci-helloworld
template:
metadata:
labels:
app: aci-helloworld
spec:
containers:
- name: aci-helloworld
image: mcr.microsoft.com/azuredocs/aci-helloworld
ports:
- containerPort: 80
resources:
requests:
cpu: 200m
tolerations:
- key: virtual-kubelet.io/provider
operator: Exists
- key: azure.com/aci
effect: NoSchedule
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: type
operator: NotIn
values:
- virtual-kubelet
파드의 Toleration과 Afffinity를 살펴볼 필요가 있습니다. 먼저 Virtual Nodes의 Taint에 대한 toleration이 지정되어 있습니다.
tolerations:
- key: virtual-kubelet.io/provider
operator: Exists
- key: azure.com/aci
effect: NoSchedule
이 경우에는 파드가 바로 Virtual Nodes로 배포될 수 있으므로, 아래와 같이 nodeAffinity를 임의로 지정했습니다.
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: type
operator: NotIn
values:
- virtual-kubelet
이렇게 배포하면 nodeAffinity에 따라 virtual-kubelet이 아닌 노드에 먼저 스케줄링이 되고, 배포되지 못한 나머지 파드가 virtual node에 배포된 것을 확인할 수 있습니다.
kubectl get po -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
aci-helloworld-86c987d849-9pw8r 1/1 Running 0 52s 10.240.0.55 aks-nodepool1-14565790-vmss000000 <none> <none>
aci-helloworld-86c987d849-hp5nv 1/1 Running 0 53s 10.240.0.8 aks-nodepool1-14565790-vmss000001 <none> <none>
aci-helloworld-86c987d849-rh9tx 1/1 Running 0 52s 10.241.0.4 virtual-node-aci-linux <none> <none>
aci-helloworld-86c987d849-v8kdx 1/1 Running 0 52s 10.240.0.18 aks-nodepool1-14565790-vmss000001 <none> <none>
즉, 해당 파드는 toleration이 지정되어 있기 때문에 virtual node에도 배포가 가능하므로, unschedulable 파드가 virtual node로 배포가 됩니다. Cluster Autoscaler를 사용하지 않고도 Virtual Nodes를 통해 확장성을 가질 수 있습니다.
이때 aci-connector-linux 파드 로그를 살펴보면 실제로 ACI에서 container group을 생성하는 로그를 확인할 수 있습니다. 마지막에 컨테이너가 Started 된 로그를 ACI를 통해 전달받은 것을 확인할 수 있습니다.
time="2025-03-22T16:05:41Z" level=info msg="creating container group with name: default-aci-helloworld-6d49f9cfbc-h76bc" addedViaRedirty=false azure.region=koreacentral azure.resourceGroup=MC_aks-vn-rg_aks-vn_koreacentral delayedViaRateLimit=5ms key=default/aci-helloworld-6d49f9cfbc-h76bc method=CreateContainerGroup name=aci-helloworld-6d49f9cfbc-h76bc namespace=default originallyAdded="2025-03-22 16:05:41.362846244 +0000 UTC m=+488.605163852" phase=Pending plannedForWork="2025-03-22 16:05:41.367846244 +0000 UTC m=+488.610163852" pod=aci-helloworld-6d49f9cfbc-h76bc queue=syncPodsFromKubernetes reason= requeues=0 uid=d6a836b6-6b7d-4b57-90db-a5c109d17d6a workerId=49
...
time="2025-03-22T16:05:43Z" level=warning msg="cannot fetch aci events for pod aci-helloworld-6d49f9cfbc-h76bc in namespace default" error="cg is not found" method=PodsTracker.processPodUpdates
time="2025-03-22T16:05:43Z" level=info msg="Created pod in provider" addedViaRedirty=false delayedViaRateLimit=5ms key=default/aci-helloworld-6d49f9cfbc-h76bc method=createOrUpdatePod name=aci-helloworld-6d49f9cfbc-h76bc namespace=default originallyAdded="2025-03-22 16:05:41.362846244 +0000 UTC m=+488.605163852" phase=Pending plannedForWork="2025-03-22 16:05:41.367846244 +0000 UTC m=+488.610163852" pod=aci-helloworld-6d49f9cfbc-h76bc queue=syncPodsFromKubernetes reason= requeues=0 uid=d6a836b6-6b7d-4b57-90db-a5c109d17d6a workerId=49
time="2025-03-22T16:05:43Z" level=info msg="Event(v1.ObjectReference{Kind:\"Pod\", Namespace:\"default\", Name:\"aci-helloworld-6d49f9cfbc-h76bc\", UID:\"d6a836b6-6b7d-4b57-90db-a5c109d17d6a\", APIVersion:\"v1\", ResourceVersion:\"5821\", FieldPath:\"\"}): type: 'Normal' reason: 'ProviderCreateSuccess' Create pod in provider successfully"
E0322 16:05:43.818182 1 event.go:346] "Server rejected event (will not retry!)" err="events is forbidden: User \"system:serviceaccount:kube-system:aci-connector-linux\" cannot create resource \"events\" in API group \"\" in the namespace \"default\"" event="&Event{ObjectMeta:{aci-helloworld-6d49f9cfbc-h76bc.182f2b9f418838d9 default 0 0001-01-01 00:00:00 +0000 UTC <nil> <nil> map[] map[] [] [] []},InvolvedObject:ObjectReference{Kind:Pod,Namespace:default,Name:aci-helloworld-6d49f9cfbc-h76bc,UID:d6a836b6-6b7d-4b57-90db-a5c109d17d6a,APIVersion:v1,ResourceVersion:5821,FieldPath:,},Reason:ProviderCreateSuccess,Message:Create pod in provider successfully,Source:EventSource{Component:virtual-node-aci-linux/pod-controller,Host:,},FirstTimestamp:2025-03-22 16:05:43.814912217 +0000 UTC m=+491.057229925,LastTimestamp:2025-03-22 16:05:43.814912217 +0000 UTC m=+491.057229925,Count:1,Type:Normal,EventTime:0001-01-01 00:00:00 +0000 UTC,Series:nil,Action:,Related:nil,ReportingController:virtual-node-aci-linux/pod-controller,ReportingInstance:,}"
...
time="2025-03-22T16:06:50Z" level=error msg="failed to retrieve pod aci-helloworld-6d49f9cfbc-h76bc status from provider" error="container aci-helloworld properties CurrentState StartTime cannot be nil" method=PodsTracker.processPodUpdates
time="2025-03-22T16:06:55Z" level=info msg="Event(v1.ObjectReference{Kind:\"Pod\", Namespace:\"default\", Name:\"aci-helloworld-6d49f9cfbc-h76bc\", UID:\"d6a836b6-6b7d-4b57-90db-a5c109d17d6a\", APIVersion:\"v1\", ResourceVersion:\"5821\", FieldPath:\"spec.containers{aci-helloworld}\"}): type: 'Normal' reason: 'Pulling' pulling image \"mcr.microsoft.com/azuredocs/aci-helloworld@sha256:b9cec4d6b50c6bf25e3f7f93bdc1628e5dca972cf132d38ed8f5bc955bb179c3\""
time="2025-03-22T16:06:55Z" level=info msg="Event(v1.ObjectReference{Kind:\"Pod\", Namespace:\"default\", Name:\"aci-helloworld-6d49f9cfbc-h76bc\", UID:\"d6a836b6-6b7d-4b57-90db-a5c109d17d6a\", APIVersion:\"v1\", ResourceVersion:\"5821\", FieldPath:\"spec.containers{aci-helloworld}\"}): type: 'Normal' reason: 'Pulled' Successfully pulled image \"mcr.microsoft.com/azuredocs/aci-helloworld@sha256:b9cec4d6b50c6bf25e3f7f93bdc1628e5dca972cf132d38ed8f5bc955bb179c3\""
time="2025-03-22T16:06:55Z" level=info msg="Event(v1.ObjectReference{Kind:\"Pod\", Namespace:\"default\", Name:\"aci-helloworld-6d49f9cfbc-h76bc\", UID:\"d6a836b6-6b7d-4b57-90db-a5c109d17d6a\", APIVersion:\"v1\", ResourceVersion:\"5821\", FieldPath:\"spec.containers{aci-helloworld}\"}): type: 'Normal' reason: 'Started' Started container"
...
앞서 살펴본바와 같이 EKS의 Fargate에 실행되는 워크로드는 일반 노드에 배포되지 않는 배타적인 성격의 스케줄링이 된다면, AKS의 Virtual Nodes에 실행은 일반 노드에 대한 보완적인 관계가 됩니다. 즉 일반 노드에 배포되고, 그 이상의 리소스가 필요할 때 Cluster Autoscaler가 없어도 Virtual Nodes를 활용하는 시나리오를 사용할 수 있습니다.
물론 특별한 요구사항이 있는 경우에는 Virtual Nodes에만 배포되도록 아래와 같이 NodeSelector와 같은 스케줄링 기법을 사용하실 수 있습니다. 혹은 tolerance를 사용하시는 경우에도 Virtual Nodes에 먼저 스케줄링 되게 됩니다.
...
nodeSelector:
kubernetes.io/role: agent
beta.kubernetes.io/os: linux
type: virtual-kubelet
...
EKS는 Fargate 파드와 노드가 1:1로 맵핑되는 반면, AKS의 Virtual Nodes는 해당 노드에 실행되는 파드가 많아져도 대응하는 노드는 1대입니다.
아래와 같이 디플로이먼트를 6개로 스케일링하고 Virtual Nodes에 여러 개의 파드가 배포되도록 유도합니다. 노드 정보를 확인해보면 virtual node는 한대만 있는 것을 알 수 있습니다.
kubectl scale deployment aci-helloworld --replicas 6
deployment.apps/aci-helloworld scaled
kubectl get po -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
aci-helloworld-86c987d849-9dbdx 1/1 Running 0 79s 10.240.0.42 aks-nodepool1-14565790-vmss000000 <none> <none>
aci-helloworld-86c987d849-9pw8r 1/1 Running 0 7m49s 10.240.0.55 aks-nodepool1-14565790-vmss000000 <none> <none>
aci-helloworld-86c987d849-fchwx 1/1 Running 0 79s 10.241.0.5 virtual-node-aci-linux <none> <none>
aci-helloworld-86c987d849-rh9tx 1/1 Running 0 7m49s 10.241.0.4 virtual-node-aci-linux <none> <none>
..
kubectl get no
NAME STATUS ROLES AGE VERSION
aks-nodepool1-14565790-vmss000000 Ready <none> 41m v1.30.9
aks-nodepool1-14565790-vmss000001 Ready <none> 41m v1.30.9
virtual-node-aci-linux Ready agent 29m v1.25.0-vk-azure-aci-1.6.2
마지막으로 포탈에서 확인하면 AKS의 인프라스트럭처 리소스 그룹에 Virtual Nodes에 해당하는 파드들이 ACI의 형태로 실행되고 있는 것을 알 수 있습니다.

Virtual Node의 파드는 Azure 입장에서는 ACI의 형태로 실행되기 때문에 포탈에서 접근해서 ACI의 UI를 통해 로그 확인/콘솔 접속 등을 사용할 수 있습니다.
AKS의 Virtual Nodes 또한 몇 가지 제약사항을 가지고 있습니다. 이는 ACI의 제약사항을 상속받은 것일 수 있으며, daemonset이나 initContainer와 같은 사용이 불가한 점도 있습니다.
AKS Virtual Nodes에 대해서 문서의 제약사항을 살펴보시기 부탁드립니다.
https://learn.microsoft.com/en-us/azure/aks/virtual-nodes#limitations
리소스를 정리하고 실습을 마무리 하겠습니다.
az group delete --name $RG
마무리
EKS와 AKS에서 노드를 사용하지 않고 파드를 실행할 수 있는 방식을 살펴보았습니다.
EKS Fargate는 Admission controller를 통하여 Fargate scheduler를 통해 스케줄링을 하는 방식이었다면, AKS는 virtual kubelet을 통해서 Virtual Nodes를 등록하고 해당 노드의 Taint를 통해서 스케줄링을 유도하는 방식을 사용할 수 있었습니다.
그럼 이번 포스트를 마무리 하겠습니다.
'EKS' 카테고리의 다른 글
[8] EKS Upgrade (0) | 2025.04.02 |
---|---|
[6] EKS의 Security - EKS 인증/인가와 Pod IAM 권한 할당 (0) | 2025.03.16 |
[5-2] EKS의 오토스케일링 Part2 (0) | 2025.03.07 |
[5-1] EKS의 오토스케일링 Part1 (0) | 2025.03.07 |
[4] EKS의 모니터링과 로깅 (0) | 2025.03.01 |