[EKS] EKS: Public-Public EKS 클러스터 - 4. 클러스터 구성 요소 확인
서종호(가시다)님의 AWS EKS Workshop Study(AEWS) 1주차 학습 내용을 기반으로 합니다.
TL;DR
이번 글에서는 Terraform으로 배포한 EKS 클러스터의 구성 요소를 확인하고, 온프레미스 Kubernetes와의 구조적 차이를 확인해 본다.
- 클러스터 설정:
aws eks describe-cluster로 IAM Role, OIDC, KMS 암호화, Service CIDR 등 주요 설정 확인 - 노드: 관리형 노드 그룹의 스케일링·AMI·롤링 업데이트 설정, EKS 고유 라벨 확인
- 시스템 파드: 온프레미스와 달리 컨트롤 플레인 컴포넌트(API Server, etcd 등)가 보이지 않고,
aws-node(VPC CNI)가 추가됨 - 이미지 레지스트리: 모든 시스템 이미지가
registry.k8s.io가 아닌 AWS ECR에서 제공됨 - EKS Add-on: CoreDNS, kube-proxy, VPC CNI를 AWS가 버전 관리
들어가며
이전 글에서 kubeconfig를 설정하고 EKS API 서버의 IAM 기반 인증 구조를 확인했다. kubectl get nodes로 워커 노드 2대가 Ready 상태인 것을 확인했으니, 이번 글에서는 클러스터의 구성 요소를 확인한다.
온프레미스에서는 배포 후 컨트롤 플레인 컴포넌트(Static Pod, systemd 서비스), 인증서, kubeconfig, CNI 설정 등을 직접 확인했다. EKS에서는 컨트롤 플레인이 AWS 관리 영역에 있으므로, 사용자에게 보이는 것과 보이지 않는 것이 명확히 나뉜다. 이 차이를 의식하면서 확인해 보자.
클러스터 정보 확인
kubectl cluster-info
kubectl cluster-info
Kubernetes control plane is running at https://461A1FA....gr7.ap-northeast-2.eks.amazonaws.com
CoreDNS is running at https://461A1FA....gr7.ap-northeast-2.eks.amazonaws.com/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
온프레미스에서 kubectl cluster-info를 실행하면 API 서버 주소가 https://192.168.10.100:6443처럼 마스터 노드의 IP와 포트로 나왔다. EKS에서는 461A1FA....gr7.ap-northeast-2.eks.amazonaws.com이라는 도메인이 나온다.
| 부분 | 의미 |
|---|---|
461A1FA... |
클러스터별로 생성되는 고유 해시 식별자. EKS가 멀티테넌트로 운영되므로 각 클러스터 API 엔드포인트를 구분하기 위해 사용. 클러스터 이름을 직접 노출하지 않는 보안 효과도 있음 |
gr7 |
EKS 내부 리전 식별자 |
ap-northeast-2 |
AWS 리전 (서울) |
eks.amazonaws.com |
EKS 서비스 도메인 |
aws eks describe-cluster
AWS CLI로 클러스터의 전체 설정을 조회한다.
CLUSTER_NAME=myeks
aws eks describe-cluster --name $CLUSTER_NAME | jq
전체 출력
{
"cluster": {
"name": "myeks",
"arn": "arn:aws:eks:ap-northeast-2:988608581192:cluster/myeks",
"createdAt": "2026-03-13T22:57:12.819000+09:00",
"version": "1.34",
"endpoint": "https://461A1FA....gr7.ap-northeast-2.eks.amazonaws.com",
"roleArn": "arn:aws:iam::988608581192:role/myeks-cluster-20260313135647575900000001",
"resourcesVpcConfig": {
"subnetIds": [
"subnet-09ff80d6277d22372",
"subnet-0e89ca3e59aabe395",
"subnet-04b03277d6d97e07c"
],
"securityGroupIds": [
"sg-07ca47ee2acbb5ccd"
],
"clusterSecurityGroupId": "sg-0776f9de59b07e26a",
"vpcId": "vpc-0bbe44f398f6fc948",
"endpointPublicAccess": true,
"endpointPrivateAccess": false,
"publicAccessCidrs": [
"0.0.0.0/0"
]
},
"kubernetesNetworkConfig": {
"serviceIpv4Cidr": "10.100.0.0/16",
"ipFamily": "ipv4"
},
"logging": {
"clusterLogging": [
{
"types": ["api", "audit", "authenticator", "controllerManager", "scheduler"],
"enabled": false
}
]
},
"identity": {
"oidc": {
"issuer": "https://oidc.eks.ap-northeast-2.amazonaws.com/id/461A1FA..."
}
},
"status": "ACTIVE",
"certificateAuthority": {
"data": "LS0tLS1CRUdJTi..."
},
"platformVersion": "eks.17",
"tags": {
"Terraform": "true",
"Environment": "cloudneta-lab"
},
"encryptionConfig": [
{
"resources": ["secrets"],
"provider": {
"keyArn": "arn:aws:kms:ap-northeast-2:988608581192:key/7d150232-a52d-4bb3-a6cd-734f55cf67ed"
}
}
]
}
}
이전 글(코드 분석)과 배포 결과에서 콘솔로 확인한 내용이 대부분이므로, 아직 다루지 않은 필드를 중심으로 살펴 본다.
roleArn
"roleArn": "arn:aws:iam::988608581192:role/myeks-cluster-20260313135647575900000001"
Terraform 코드에서 IAM Role을 직접 선언하지 않았지만, EKS 모듈이 자동으로 생성한 클러스터 IAM Role이다. EKS 클러스터가 AWS 리소스(ENI 생성, 로그 전송, NLB 관리 등)를 조작하려면 IAM Role이 필요한데, EKS 모듈이 AmazonEKSClusterPolicy 등을 붙인 Role을 생성하고 연결한 것이다.
KMS 키 자동 생성과 마찬가지로 모듈이 내부적으로 생성하는 리소스다.
kubernetesNetworkConfig
"kubernetesNetworkConfig": {
"serviceIpv4Cidr": "10.100.0.0/16",
"ipFamily": "ipv4"
}
serviceIpv4Cidr는 ClusterIP 타입 서비스에 할당되는 가상 IP 대역이다. 온프레미스에서 kube-apiserver --service-cluster-ip-range로 직접 지정하던 것과 같은 개념인데, EKS에서는 클러스터 생성 시 AWS가 자동으로 할당한다. 실제로 이 클러스터의 CoreDNS ClusterIP가 10.100.0.10인 것도 이 대역 안에 있다.
identity (OIDC)
"identity": {
"oidc": {
"issuer": "https://oidc.eks.ap-northeast-2.amazonaws.com/id/461A1FA..."
}
}
EKS는 IAM Roles for Service Accounts(IRSA)를 지원하기 위해 OIDC Provider를 자동 생성한다. Kubernetes ServiceAccount 토큰을 AWS IAM이 신뢰할 수 있도록 OIDC Provider를 연결하는 구조로, 이를 통해 파드별로 세분화된 AWS 권한(S3 접근, DynamoDB 접근 등)을 부여할 수 있다. 온프레미스 Kubernetes에는 이 개념이 없다.
참고: OIDC Provider와 IRSA
OIDC(OpenID Connect)는 OAuth 2.0 위에 구축된 신원 확인 프로토콜이다. OIDC Provider는 “이 토큰을 가진 주체가 누구인지”를 제3자가 신뢰할 수 있도록 증명하는 인증 서버 역할을 한다.
EKS에서의 동작 흐름은 다음과 같다.
- Pod가 ServiceAccount 토큰(JWT)을 들고 AWS 리소스에 접근을 요청한다
- AWS IAM이 EKS OIDC Provider에 토큰 서명 검증을 요청한다 (
.well-known/openid-configuration엔드포인트에서 공개 키 조회)- 검증에 성공하면, 해당 ServiceAccount에 매핑된 IAM Role의 임시 자격증명(STS)을 발급한다
OIDC Provider URL이 클러스터별로 고유하므로, 다른 클러스터의 SA 토큰이 이 클러스터의 IAM Role에 접근하는 것은 불가능하다. IRSA가 없으면 EC2 Instance Profile을 노드 전체가 공유하게 되어 최소 권한 원칙을 지키기 어렵다. IRSA를 통해 파드 단위의 세분화된 AWS 권한 부여가 가능해진다.
certificateAuthority
"certificateAuthority": {
"data": "LS0tLS1CRUdJTi..."
}
Base64로 인코딩된 CA 공개 인증서다. 이전 글에서 kubeconfig의 certificate-authority-data에 들어가는 값이 바로 이것이다. kubectl이 API 서버의 TLS 인증서를 검증할 때 사용하고, 워커 노드의 /etc/kubernetes/pki/ca.crt에도 이 값이 저장된다.
핵심은 CA의 프라이빗 키는 AWS만 보유한다는 점이다. 온프레미스에서는 CA 프라이빗 키가 컨트롤 플레인 노드의 /etc/kubernetes/pki/ca.key에 저장되어 관리자가 직접 관리했지만, EKS에서는 사용자에게 공개 인증서만 제공되고 프라이빗 키에는 접근할 수 없다.
encryptionConfig
"encryptionConfig": [
{
"resources": ["secrets"],
"provider": {
"keyArn": "arn:aws:kms:ap-northeast-2:988608581192:key/7d150232-..."
}
}
]
배포 단계에서 plan으로 확인한 KMS 키가 실제 적용된 결과다. etcd에 저장되는 Secret 리소스가 이 KMS 키로 암호화된다. Hard Way 6단계에서 /dev/urandom으로 키를 생성하고 EncryptionConfiguration을 직접 작성했던 것을 EKS 모듈이 KMS로 자동 처리해 준 것이다.
노드 확인
노드 그룹
aws eks describe-nodegroup --cluster-name $CLUSTER_NAME --nodegroup-name $CLUSTER_NAME-node-group | jq
{
"nodegroup": {
"nodegroupName": "myeks-node-group",
"nodegroupArn": "arn:aws:eks:ap-northeast-2:988608581192:nodegroup/myeks/myeks-node-group/7ece73c1-...",
"clusterName": "myeks",
"version": "1.34",
"releaseVersion": "1.34.4-20260311",
"status": "ACTIVE",
"capacityType": "ON_DEMAND",
"scalingConfig": {
"minSize": 1,
"maxSize": 4,
"desiredSize": 2
},
"instanceTypes": ["t3.medium"],
"subnets": [
"subnet-09ff80d6277d22372",
"subnet-0e89ca3e59aabe395",
"subnet-04b03277d6d97e07c"
],
"amiType": "AL2023_x86_64_STANDARD",
"nodeRole": "arn:aws:iam::988608581192:role/myeks-node-group-eks-node-group-20260313135702848600000005",
"resources": {
"autoScalingGroups": [
{ "name": "eks-myeks-node-group-7ece73c1-a768-056e-3510-652d1175ebea" }
]
},
"health": { "issues": [] },
"updateConfig": {
"maxUnavailablePercentage": 33
},
"launchTemplate": {
"name": "default-20260313140422498100000008",
"version": "1",
"id": "lt-05b80e624cb67171d"
},
"tags": {
"Terraform": "true",
"Environment": "cloudneta-lab",
"Name": "myeks-node-group"
}
}
}
배포 단계에서 콘솔로 확인한 내용에 더해, CLI에서 확인할 수 있는 주요 필드를 짚는다.
releaseVersion
"releaseVersion": "1.34.4-20260311"
Kubernetes 버전(1.34)과 별개로, EKS가 제공하는 AMI 릴리즈 버전이다. 1.34.4는 kubelet 패치 버전, 20260311은 AMI 빌드 날짜다. EKS가 보안 패치나 런타임 업데이트를 적용할 때 이 릴리즈 버전이 올라간다.
updateConfig
"updateConfig": {
"maxUnavailablePercentage": 33
}
노드 그룹을 롤링 업데이트할 때, 동시에 unavailable 상태가 될 수 있는 노드의 최대 비율이다. 33%면 노드 3대 중 1대만 동시에 업데이트된다. 서비스 가용성과 업데이트 속도 사이의 균형을 조절하는 설정이다.
온프레미스에서 kubespray의 serial이나 RKE2의 SUC concurrency로 업그레이드 병렬도를 조절했던 것과 같은 역할이다.
launchTemplate
"launchTemplate": {
"name": "default-20260313140422498100000008",
"version": "1",
"id": "lt-05b80e624cb67171d"
}
EC2 시작 템플릿(Launch Template)으로, 노드(EC2 인스턴스)를 생성할 때 사용할 AMI, 인스턴스 타입, 보안 그룹, userdata 스크립트 등을 정의한 템플릿이다. 관리형 노드 그룹은 이 시작 템플릿을 기반으로 Auto Scaling Group을 통해 EC2 인스턴스를 프로비저닝한다.
노드 목록
kubectl get node --label-columns=node.kubernetes.io/instance-type,eks.amazonaws.com/capacityType,topology.kubernetes.io/zone
NAME STATUS ROLES AGE VERSION INSTANCE-TYPE CAPACITYTYPE ZONE
ip-192-168-2-21.ap-northeast-2.compute.internal Ready <none> 27h v1.34.4-eks-f69f56f t3.medium ON_DEMAND ap-northeast-2b
ip-192-168-3-96.ap-northeast-2.compute.internal Ready <none> 27h v1.34.4-eks-f69f56f t3.medium ON_DEMAND ap-northeast-2c
몇 가지 눈에 띄는 점이 있다.
-
ROLES가
<none>이다.: 온프레미스에서 kubeadm은 컨트롤 플레인 노드에control-planerole을 붙이고, 워커 노드는<none>(또는 수동 label)이었다. EKS에서는 컨트롤 플레인이 노드 목록에 아예 나타나지 않으므로, 모든 노드가<none>이다. -
노드 이름이 EC2 프라이빗 DNS다.:
ip-192-168-2-21.ap-northeast-2.compute.internal은 AWS가 EC2 인스턴스에 자동 부여하는 프라이빗 DNS 이름이다. 온프레미스에서k8s-m,k8s-w1처럼 호스트 이름을 직접 지정했던 것과 다르다. -
EKS 고유 라벨이 붙어 있다.:
eks.amazonaws.com/capacityType=ON_DEMAND,topology.kubernetes.io/zone등 AWS 인프라 정보가 라벨로 자동 부여된다. 이 라벨을 활용해 특정 AZ나 인스턴스 유형에만 워크로드를 스케줄링할 수 있다.
시스템 파드 확인
kube-system 파드
kubectl get pod -n kube-system -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
aws-node-m8zzp 2/2 Running 0 27h 192.168.2.21 ip-192-168-2-21.ap-northeast-2.compute.internal <none> <none>
aws-node-q2hph 2/2 Running 0 27h 192.168.3.96 ip-192-168-3-96.ap-northeast-2.compute.internal <none> <none>
coredns-5759dc8cd-jp5md 1/1 Running 0 27h 192.168.2.132 ip-192-168-2-21.ap-northeast-2.compute.internal <none> <none>
coredns-5759dc8cd-sscgz 1/1 Running 0 27h 192.168.3.89 ip-192-168-3-96.ap-northeast-2.compute.internal <none> <none>
kube-proxy-9hbkd 1/1 Running 0 27h 192.168.2.21 ip-192-168-2-21.ap-northeast-2.compute.internal <none> <none>
kube-proxy-fpq85 1/1 Running 0 27h 192.168.3.96 ip-192-168-3-96.ap-northeast-2.compute.internal <none> <none>
온프레미스와 비교하면 두 가지 핵심 차이가 보인다.
컨트롤 플레인 컴포넌트가 없다
온프레미스(kubeadm)에서는 kube-system 네임스페이스에 kube-apiserver, kube-controller-manager, kube-scheduler, etcd가 Static Pod로 보였다. EKS에서는 컨트롤 플레인이 AWS 관리 VPC에서 동작하므로 사용자 클러스터의 파드 목록에 나타나지 않는다. 이것이 EKS Overview에서 말한 컨트롤 플레인 불가시성의 실체다.
파드 IP가 VPC 서브넷의 실제 IP다
aws-node-m8zzp 192.168.2.21 ← 노드 IP와 동일 (hostNetwork)
coredns-... 192.168.2.132 ← 같은 서브넷의 다른 IP
coredns-... 192.168.3.89 ← 다른 서브넷의 IP
온프레미스에서는 파드 IP가 별도의 오버레이 대역(예: Flannel의 10.244.x.x, Calico의 10.233.x.x)이었다. EKS VPC CNI는 ENI의 보조 IP를 파드에 직접 할당하므로 파드 IP가 VPC 서브넷(192.168.x.x)의 실제 IP다. 이 차이는 aws-node 상세에서 더 자세히 다룬다.
kube-system 전체 리소스
kubectl get deploy,ds,pod,cm,secret,svc,ep,endpointslice,pdb,sa,role,rolebinding -n kube-system
전체 출력
Warning: v1 Endpoints is deprecated in v1.33+; use discovery.k8s.io/v1 EndpointSlice
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/coredns 2/2 2 2 27h
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
daemonset.apps/aws-node 2 2 2 2 2 <none> 27h
daemonset.apps/kube-proxy 2 2 2 2 2 <none> 27h
NAME READY STATUS RESTARTS AGE
pod/aws-node-m8zzp 2/2 Running 0 27h
pod/aws-node-q2hph 2/2 Running 0 27h
pod/coredns-5759dc8cd-jp5md 1/1 Running 0 27h
pod/coredns-5759dc8cd-sscgz 1/1 Running 0 27h
pod/kube-proxy-9hbkd 1/1 Running 0 27h
pod/kube-proxy-fpq85 1/1 Running 0 27h
NAME DATA AGE
configmap/amazon-vpc-cni 7 27h
configmap/aws-auth 1 27h
configmap/coredns 1 27h
configmap/extension-apiserver-authentication 6 27h
configmap/kube-apiserver-legacy-service-account-token-tracking 1 27h
configmap/kube-proxy 1 27h
configmap/kube-proxy-config 1 27h
configmap/kube-root-ca.crt 1 27h
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/eks-extension-metrics-api ClusterIP 10.100.213.67 <none> 443/TCP 27h
service/kube-dns ClusterIP 10.100.0.10 <none> 53/UDP,53/TCP,9153/TCP 27h
NAME ADDRESSTYPE PORTS ENDPOINTS AGE
endpointslice.discovery.k8s.io/eks-extension-metrics-api-wx9kx IPv4 10443 172.0.32.0 27h
endpointslice.discovery.k8s.io/kube-dns-7tfb4 IPv4 9153,53,53 192.168.2.132,192.168.3.89 27h
NAME MIN AVAILABLE MAX UNAVAILABLE ALLOWED DISRUPTIONS AGE
poddisruptionbudget.policy/coredns N/A 1 1 27h
전체 리소스를 온프레미스와 대비하여 정리한다.
워크로드
| 리소스 유형 | 이름 | 온프레미스(kubeadm) 대응 | 비고 |
|---|---|---|---|
| Deployment | coredns |
coredns (Deployment) | 동일 |
| DaemonSet | kube-proxy |
kube-proxy (DaemonSet) | 동일 |
| DaemonSet | aws-node |
해당 없음 | EKS 고유. VPC CNI 플러그인 |
aws-node는 EKS에서 VPC CNI 플러그인을 실행하는 DaemonSet이다. 온프레미스에서 Flannel이나 Calico를 별도로 설치했던 것에 대응하지만, EKS Add-on으로 관리된다는 점이 다르다.
ConfigMap
| ConfigMap | 설명 | 온프레미스 대응 |
|---|---|---|
amazon-vpc-cni |
VPC CNI 플러그인 설정 | 해당 없음 (EKS 고유) |
aws-auth |
IAM ↔ K8s RBAC 매핑 | 해당 없음 (EKS 고유) |
coredns |
CoreDNS Corefile | coredns (동일) |
kube-proxy |
kube-proxy kubeconfig | kube-proxy (동일) |
kube-proxy-config |
kube-proxy 설정 | kube-proxy-config (동일) |
kube-root-ca.crt |
CA 인증서 | kube-root-ca.crt (동일) |
Service
| Service | ClusterIP | 설명 |
|---|---|---|
kube-dns |
10.100.0.10 |
CoreDNS. serviceIpv4Cidr: 10.100.0.0/16 대역 안 |
eks-extension-metrics-api |
10.100.213.67 |
EKS 내부 확장 API |
eks-extension-metrics-api는 kubectl top이 사용하는 Metrics Server와는 다른 것이다. EKS 컨트롤 플레인이 노드/파드 상태를 모니터링하기 위해 사용하는 내부 확장 API다. EndpointSlice의 주소가 172.0.32.0으로, 사용자 VPC 대역(192.168.x.x)이 아닌 별도의 대역인 것에서 알 수 있듯 AWS 관리 영역의 컴포넌트다.
참고:
kubectl top은 Metrics Server가 필요한데, 현재 클러스터에는 설치되어 있지 않다. kubeadm으로 설치한 클러스터에서도 Metrics Server는 기본 포함이 아니라 별도 설치가 필요하다. EKS에서도 마찬가지로 Add-on으로 별도 설치해야 한다.
ServiceAccount
대부분의 ServiceAccount는 온프레미스 Kubernetes에도 있는 컨트롤러용 SA다. EKS 고유의 SA는 aws-cloud-provider, aws-node, tagging-controller 정도다.
Role / RoleBinding
eks: 접두사가 붙은 Role들이 EKS 고유한 것이다.
| Role | 역할 |
|---|---|
eks:addon-manager |
EKS Add-on 라이프사이클 관리 |
eks:authenticator |
IAM ↔ K8s 인증 연동 |
eks:node-manager |
노드 등록/관리 |
eks:network-policy-controller |
Network Policy 관리 |
eks:fargate-manager |
Fargate 프로파일 관리 |
이 Role들은 EKS 컨트롤 플레인이 사용자 클러스터의 리소스를 관리하기 위해 자동 생성한 것이다.
컨테이너 이미지: ECR
kubectl get pods --all-namespaces -o jsonpath="{.items[*].spec.containers[*].image}" | tr -s '[[:space:]]' '\n' | sort | uniq -c
2 602401143452.dkr.ecr.ap-northeast-2.amazonaws.com/amazon-k8s-cni:v1.21.1-eksbuild.3
2 602401143452.dkr.ecr.ap-northeast-2.amazonaws.com/amazon/aws-network-policy-agent:v1.3.1-eksbuild.1
2 602401143452.dkr.ecr.ap-northeast-2.amazonaws.com/eks/coredns:v1.13.2-eksbuild.1
2 602401143452.dkr.ecr.ap-northeast-2.amazonaws.com/eks/kube-proxy:v1.34.3-eksbuild.5
모든 이미지가 602401143452.dkr.ecr.ap-northeast-2.amazonaws.com에서 가져온 것이다.
| 부분 | 의미 |
|---|---|
602401143452 |
AWS가 EKS용으로 운영하는 공용 ECR 계정 ID |
dkr.ecr.ap-northeast-2 |
서울 리전의 ECR |
온프레미스 Kubernetes에서는 registry.k8s.io(구 k8s.gcr.io)에서 이미지를 가져오지만, EKS는 이를 전부 ECR로 대체한다.
| 온프레미스 (registry.k8s.io) | EKS (ECR) | |
|---|---|---|
| 위치 | 인터넷 (해외 CDN) | 같은 AWS 리전 내 |
| 속도 | 상대적으로 느림 | 빠름 (리전 내부 통신) |
| 가용성 | AWS 통제 불가 | AWS가 직접 관리 |
| 인증 | 없음 (공개) | IAM 기반 인증 |
EKS 시스템 컴포넌트(CoreDNS, kube-proxy, VPC CNI)는 클러스터 부팅에 필수이므로, 외부 레지스트리에 의존하지 않고 AWS가 자체 ECR에 이미지를 미러링해서 제공한다.
시스템 파드 상세
kube-proxy
kubectl describe pod -n kube-system -l k8s-app=kube-proxy
주요 설정을 발췌한다.
Priority Class Name: system-node-critical
Controlled By: DaemonSet/kube-proxy
Containers:
kube-proxy:
Image: 602401143452.dkr.ecr.ap-northeast-2.amazonaws.com/eks/kube-proxy:v1.34.3-eksbuild.5
Command:
kube-proxy
--v=2
--config=/var/lib/kube-proxy-config/config
--hostname-override=$(NODE_NAME)
Requests:
cpu: 100m
kube-proxy의 실제 설정은 kube-proxy-config ConfigMap에 저장되어 있다.
kubectl get cm -n kube-system kube-proxy-config -o yaml
data:
config: |-
apiVersion: kubeproxy.config.k8s.io/v1alpha1
bindAddress: 0.0.0.0
clientConnection:
kubeconfig: /var/lib/kube-proxy/kubeconfig
qps: 5
clusterCIDR: ""
conntrack:
maxPerCore: 32768
min: 131072
tcpCloseWaitTimeout: 1h0m0s
tcpEstablishedTimeout: 24h0m0s
iptables:
masqueradeAll: false
masqueradeBit: 14
minSyncPeriod: 0s
syncPeriod: 30s
mode: "iptables"
metricsBindAddress: 0.0.0.0:10249
온프레미스(kubeadm)의 kube-proxy 설정과 큰 차이는 없다.
mode: "iptables": EKS 기본은 iptables 모드다.clusterCIDR: "": 비어 있다. 온프레미스에서는 오버레이 네트워크의 Pod CIDR을 명시해야 했다 — Hard Way에서는10.200.0.0/16, kubeadm에서는10.244.0.0/16, RKE2에서는10.42.0.0/16. EKS VPC CNI에서는 Pod IP가 VPC 서브넷의 실제 IP이므로 별도의 clusterCIDR이 불필요하다.conntrack.maxPerCore: 32768: CPU 코어당 최대 conntrack 엔트리 수. 비교해 보면, kubeadm에서는null(커널 기본값 사용)이고, RKE2에서는--conntrack-max-per-core=0(제한 없음)이었다. EKS는32768로 명시적 상한을 설정한다.
kube-proxy의 kubeconfig도 확인해 보자.
kubectl get cm -n kube-system kube-proxy -o yaml
data:
kubeconfig: |-
kind: Config
apiVersion: v1
clusters:
- cluster:
certificate-authority: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
server: https://461a1fa334847e0e1b597af07ff0cce0.gr7.ap-northeast-2.eks.amazonaws.com
name: default
users:
- name: default
user:
tokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
온프레미스에서는 kube-proxy가 X.509 클라이언트 인증서로 API 서버에 인증했지만, EKS에서는 ServiceAccount 토큰(/var/run/secrets/kubernetes.io/serviceaccount/token)으로 인증한다. API 서버 주소도 EKS 엔드포인트 도메인이다.
CoreDNS
kubectl describe pod -n kube-system -l k8s-app=kube-dns
주요 설정을 발췌한다.
Priority Class Name: system-cluster-critical
Controlled By: ReplicaSet/coredns-5759dc8cd
Containers:
coredns:
Image: 602401143452.dkr.ecr.ap-northeast-2.amazonaws.com/eks/coredns:v1.13.2-eksbuild.1
Ports: 53/UDP (dns), 53/TCP (dns-tcp), 9153/TCP (metrics)
Limits:
memory: 170Mi
Requests:
cpu: 100m
memory: 70Mi
Liveness: http-get http://:8080/health delay=60s
Readiness: http-get http://:8181/ready delay=0s
Tolerations: CriticalAddonsOnly op=Exists
node-role.kubernetes.io/control-plane:NoSchedule
Topology Spread Constraints:
topology.kubernetes.io/zone:ScheduleAnyway when max skew 1 is exceeded for selector k8s-app=kube-dns
온프레미스(kubeadm)의 CoreDNS와 거의 동일하지만, 몇 가지 차이점이 있다.
Topology Spread Constraints가 설정되어 있다. topology.kubernetes.io/zone:ScheduleAnyway로 AZ 간 균등 분산을 유도한다. 온프레미스에서는 AZ 개념이 없었으므로 이 설정이 없었다. 실제로 CoreDNS 2개 파드가 ap-northeast-2b와 ap-northeast-2c에 하나씩 분산되어 있다.
PodDisruptionBudget이 설정되어 있다.
kubectl get pdb -n kube-system coredns -o jsonpath='{.spec}' | jq
{
"maxUnavailable": 1,
"selector": {
"matchLabels": {
"eks.amazonaws.com/component": "coredns",
"k8s-app": "kube-dns"
}
}
}
CoreDNS 2개 중 최대 1개까지만 동시에 중단될 수 있다. 노드 drain이나 업그레이드 시에도 최소 1개의 CoreDNS가 항상 동작하도록 보장한다.
Corefile 설정은 다음과 같다.
kubectl get cm -n kube-system coredns -o yaml
data:
Corefile: |
.:53 {
errors
health {
lameduck 5s
}
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
}
prometheus :9153
forward . /etc/resolv.conf
cache 30
loop
reload
loadbalance
}
온프레미스(kubeadm)의 Corefile과 동일한 구조다. forward . /etc/resolv.conf로 클러스터 외부 DNS 쿼리를 VPC의 DNS 서버(AmazonProvidedDNS, 일반적으로 VPC CIDR + 2)로 전달한다.
aws-node (VPC CNI)
kubectl describe pod -n kube-system -l k8s-app=aws-node
EKS에서 가장 핵심적인 네트워킹 컴포넌트다. 온프레미스의 Flannel이나 Calico에 대응하지만, 동작 방식이 근본적으로 다르다.
컨테이너 구성
aws-node 파드는 init 컨테이너 1개 + 일반 컨테이너 2개로 구성된다.
| 컨테이너 | 이미지 | 역할 |
|---|---|---|
aws-vpc-cni-init (init) |
amazon-k8s-cni-init |
CNI 바이너리를 호스트의 /opt/cni/bin에 설치 |
aws-node |
amazon-k8s-cni |
VPC CNI 플러그인 — 파드에 VPC IP 할당 |
aws-eks-nodeagent |
aws-network-policy-agent |
Network Policy 에이전트 — K8s NetworkPolicy 적용 |
VPC CNI 개요
온프레미스에서는 Calico, Flannel 같은 CNI가 오버레이 네트워크(VXLAN, IP-in-IP 등)를 만들어 파드 IP를 할당한다. EKS VPC CNI는 완전히 다른 접근으로, ENI(Elastic Network Interface)의 보조 IP를 파드에 직접 할당하여 VPC 서브넷의 실제 IP를 파드가 사용한다. 오버레이 터널이 없으므로 캡슐화 오버헤드가 없지만, 인스턴스 유형별 ENI × IP 수로 파드 수 상한이 생기는 트레이드오프가 있다.
| 온프레미스 (Calico/Flannel) | EKS (VPC CNI) | |
|---|---|---|
| 파드 IP | 별도의 오버레이 대역 (예: 10.244.x.x) |
VPC 서브넷의 실제 IP (192.168.x.x) |
| 네트워크 | VXLAN/IP-in-IP 터널 | 네이티브 VPC 라우팅 (터널 없음) |
| 성능 | 캡슐화 오버헤드 있음 | 오버헤드 없음 |
| 파드 수 제한 | 거의 없음 | ENI × IP 수로 제한 (인스턴스 유형별 상한) |
ENI warm pool, prefix delegation, SNAT 모드 등 VPC CNI의 세부 설정과 동작 원리는 이후 네트워킹 시리즈에서 다룬다.
aws-node 환경 변수 및 호스트 마운트 상세
주요 환경 변수
ENABLE_PREFIX_DELEGATION: false
WARM_ENI_TARGET: 1
WARM_PREFIX_TARGET: 1
AWS_VPC_ENI_MTU: 9001
AWS_VPC_K8S_CNI_VETHPREFIX: eni
AWS_VPC_K8S_CNI_EXTERNALSNAT: false
VPC_ID: vpc-0bbe44f398f6fc948
CLUSTER_ENDPOINT: https://461A1FA...eks.amazonaws.com
| 환경 변수 | 값 | 의미 |
|---|---|---|
WARM_ENI_TARGET |
1 |
파드 할당에 대비해 미리 1개의 ENI를 warm pool에 확보 |
WARM_PREFIX_TARGET |
1 |
/28 prefix 1개를 미리 확보 (prefix delegation 사용 시) |
ENABLE_PREFIX_DELEGATION |
false |
비활성화. 활성화하면 ENI당 더 많은 IP를 할당 가능 |
AWS_VPC_ENI_MTU |
9001 |
Jumbo Frame 지원. VPC 내 통신에서 오버헤드 감소 |
AWS_VPC_K8S_CNI_VETHPREFIX |
eni |
veth pair 이름 접두사. 온프레미스의 cali (Calico), veth (Flannel)에 대응 |
AWS_VPC_K8S_CNI_EXTERNALSNAT |
false |
VPC CNI가 직접 SNAT 처리. true면 외부 SNAT(iptables) 사용 |
호스트 경로 마운트
CNI 플러그인은 호스트 네트워크 스택을 직접 조작해야 하므로, 온프레미스의 Calico/Flannel과 마찬가지로 호스트 경로를 마운트한다.
| 마운트 경로 | 용도 |
|---|---|
/opt/cni/bin |
CNI 바이너리 배포. init 컨테이너가 여기에 CNI 바이너리를 설치 |
/etc/cni/net.d |
CNI 설정 파일(10-aws.conflist) 배포. kubelet이 이 경로에서 CNI 설정을 읽음 |
/run/xtables.lock |
iptables 규칙 동시 수정 방지를 위한 락 파일 |
/sys/fs/bpf |
eBPF 맵 고정(pinning). Network Policy 에이전트가 사용 |
EKS Add-on 확인
aws eks list-addons --cluster-name myeks | jq
{
"addons": [
"coredns",
"kube-proxy",
"vpc-cni"
]
}
Terraform 코드에서 addons 블록으로 선언한 3개가 그대로 설치되어 있다.
aws eks list-addons --cluster-name myeks \
| jq -r '.addons[]' \
| xargs -I{} aws eks describe-addon \
--cluster-name myeks \
--addon-name {} \
| jq '.addon | {addonName, addonVersion, status}'
{ "addonName": "coredns", "addonVersion": "v1.13.2-eksbuild.1", "status": "ACTIVE" }
{ "addonName": "kube-proxy", "addonVersion": "v1.34.3-eksbuild.5", "status": "ACTIVE" }
{ "addonName": "vpc-cni", "addonVersion": "v1.21.1-eksbuild.3", "status": "ACTIVE" }
세 애드온 모두 ACTIVE 상태다. 각 애드온에 EKS 빌드 버전(-eksbuild.x)이 붙어 있는데, 이는 AWS가 업스트림 버전에 EKS 환경 최적화 패치를 적용한 빌드임을 나타낸다.
온프레미스에서는 이 컴포넌트들의 버전 관리를 사용자가 직접 해야 했다.
| 온프레미스 | EKS Add-on | |
|---|---|---|
| 설치 | kubectl apply, Helm, 또는 도구별 자동 설치 |
Terraform addons 블록 또는 AWS 콘솔 |
| 버전 관리 | 직접 호환성 확인 후 수동 업그레이드 | most_recent = true로 클러스터 버전 호환 최신 자동 적용 |
| 롤백 | 직접 이전 버전 매니페스트 적용 | AWS API로 이전 버전 지정 |
| 상태 관리 | 직접 모니터링 | health.issues로 상태 확인 가능 |
추가 애드온이 필요하면 Terraform 코드의 addons 블록에 선언하면 된다. Metrics Server, AWS Load Balancer Controller 등이 대표적이다.
정리
EKS 클러스터의 구성 요소를 CLI로 확인하면서 온프레미스 Kubernetes와의 구조적 차이를 정리한다.
| 항목 | 온프레미스 | EKS |
|---|---|---|
| 컨트롤 플레인 가시성 | Static Pod / systemd로 직접 확인 가능 | 불가시. AWS 관리 VPC에 숨어 있음 |
| 시스템 파드 | API Server, etcd, Scheduler, CM + CoreDNS, kube-proxy | CoreDNS, kube-proxy, aws-node(VPC CNI) |
| 파드 IP | 오버레이 대역 (10.244.x.x 등) |
VPC 서브넷 실제 IP (192.168.x.x) |
| 이미지 레지스트리 | registry.k8s.io |
AWS ECR (602401143452.dkr.ecr...) |
| 컴포넌트 인증 | X.509 클라이언트 인증서 | ServiceAccount 토큰 |
| 애드온 관리 | 직접 설치·업그레이드 | EKS Add-on (AWS가 버전 관리) |
가장 큰 차이는 보이는 것의 범위다. 온프레미스에서는 kubectl get pod -n kube-system만으로 클러스터의 거의 모든 구성 요소를 볼 수 있었지만, EKS에서는 컨트롤 플레인 전체가 보이지 않는다. 대신 aws eks describe-cluster라는 AWS API를 통해 간접적으로 확인해야 한다. EKS Overview에서 이야기한 관리형 서비스의 추상화 수준이 한 단계 더 높다는 것이 CLI 확인 과정에서도 체감된다.
이 글에서 확인한 구성 요소(시스템 파드, VPC CNI, ECR, Add-on 등)는 엔드포인트 모드와 무관하게 모든 EKS 클러스터에 공통이다. 다음 글에서는 현재 Public-Public 구성의 엔드포인트 접근 경로를 분석한다.
댓글남기기