27 min to read
EKS Week 1 - Architecture & Compute Options
Cloudnet@EKS Week1
Introduction
Kubernetes는 강력한 컨테이너 오케스트레이션 플랫폼이지만, 클러스터를 직접 구축하고 운영하는 것은 상당한 운영 부담을 동반합니다. 운영자는 일반적으로 다음과 같은 작업을 직접 수행해야 합니다.
kube-apiserver,etcd,kube-scheduler,kube-controller-manager등 컨트롤 플레인 구성 요소를 직접 설치하고 고가용성(HA) 구성을 잡아야 합니다.- etcd 데이터를 주기적으로 백업하고, 장애 시 복구 절차를 마련해야 합니다.
- Kubernetes 버전이 출시될 때마다 업그레이드 절차를 직접 수행해야 합니다.
- CNI 플러그인, 인증서 관리, RBAC 정책 등을 처음부터 설계해야 합니다.
이러한 운영 부담을 AWS가 대신 관리해주는 서비스가 바로 Amazon EKS(Elastic Kubernetes Service)입니다. EKS는 Kubernetes 컨트롤 플레인을 AWS가 관리해 주지만, 워크로드가 실제로 실행되는 데이터 플레인(Data Plane)은 여러 방식으로 구성할 수 있습니다.
Architecture
EKS 클러스터를 생성하면 내부적으로 두 개의 VPC가 관여합니다.
- AWS 관리 VPC(EKS account)에는 컨트롤 플레인이 위치합니다. API Server 인스턴스 최소 2개와 etcd 인스턴스 3개가 리전 내 3개 가용 영역에 분산 배치됩니다. 이 VPC는 AWS 소유이며 운영자는 직접 접근할 수 없습니다. 클러스터 간, AWS 계정 간 인프라가 완전히 격리됩니다.
- 고객 VPC(Customer account)에는 워커 노드(데이터 플레인)가 위치합니다. 운영자가 소유하고 관리하는 영역입니다.
두 VPC는 AWS가 고객 VPC 안에 생성하는 관리형 ENI(Elastic Network Interface)를 통해 연결됩니다. kubectl로 명령을 실행하면 요청은 EKS API 엔드포인트(*.gr7.*.eks.amazonaws.com)를 통해 AWS 관리 VPC의 API Server에 도달하고, API Server는 이 ENI를 통해 고객 VPC의 워커 노드와 통신합니다.
EKS에서는 일반적인 Kubernetes와 다르게 컨트롤 플레인 전체가 AWS 관리 VPC 안에 있어 kubectl get pod -n kube-system으로 API Server나 etcd Pod가 조회되지 않습니다.
Data Plane Compute Options
Kubernetes에서 데이터 플레인은 워커 노드의 집합입니다. 각 노드에는 kubelet, 컨테이너 런타임(containerd), kube-proxy가 실행되며, 컨트롤 플레인의 지시를 받아 실제 워크로드를 처리합니다.
EKS에서 달라지는 것은 Pod를 어떤 컴퓨팅 환경 위에서 실행하느냐입니다. 공식 문서에 따르면 하나의 EKS 클러스터는 다음 다섯 가지 옵션을 동시에 혼합하여 사용할 수 있습니다.
- Amazon EKS managed node groups
- EKS Auto Mode managed nodes
- self-managed nodes
- AWS Fargate
- Amazon EKS Hybrid Nodes
각 옵션은 운영 책임, 실행 위치, 제어권이 서로 다르기 때문에 단계적으로 이해하는 것이 좋습니다.
Operation Models
데이터 플레인 옵션을 선택하기 전에, 클러스터 수준에서 먼저 결정해야 할 것이 있습니다. 데이터 플레인 운영을 AWS에 위임할 것인지, 자체적으로 관리할 것인지입니다.
Amazon EKS란 무엇입니까? - Amazon EKS
EKS Standard에서는 AWS 계정에 컨트롤 플레인만 존재합니다. Karpenter, VPC CNI, EBS CSI Driver, Load Balancer Controller 등이 고객 계정 안에서 Add-On으로 실행되므로, 설치·버전 관리·업그레이드를 운영자가 직접 수행해야 합니다.
EKS Auto Mode에서는 이 Add-On들이 AWS 계정 쪽의 Managed Capabilities(Storage, Compute, Load Balancing)로 이동하여 AWS가 직접 관리합니다. EC2 인스턴스는 여전히 고객 계정에 위치하지만, 그 인스턴스의 운영은 AWS가 처리합니다. 위 다섯 가지 옵션 중 EKS Auto Mode managed nodes가 여기에 해당합니다.
Node Execution Environments
EKS Standard 환경에서는 다음으로 노드를 어디에서 실행할 것인지를 결정합니다.
AWS 클라우드에서 실행되는 옵션이 세 가지입니다.
- Managed Node Groups
- Self-managed Nodes
- AWS Fargate
고객의 온프레미스 또는 엣지 환경에서 실행되는 옵션은 하나입니다.
- Amazon EKS Hybrid Nodes
Amazon EKS Hybrid Nodes 출시: EKS 클러스터 온프레미스 인프라 사용 가능 | AWS 기술 블로그
EKS Hybrid Nodes는 컨트롤 플레인은 AWS가 관리하는 EKS 클러스터에 두되, 워커 노드는 고객 데이터센터의 물리 서버나 VM을 nodeadm 도구로 등록해서 사용합니다. AWS와의 네트워크 연결(VPN 또는 Direct Connect)이 전제 조건입니다. 레이턴시, 데이터 레지던시 등의 이유로 일부 워크로드를 반드시 온프레미스에서 실행해야 하지만, 클라우드의 EKS 클러스터와 동일한 도구로 통합 관리하고 싶을 때 적합합니다.
Compute Types
AWS 클라우드에서 실행되는 세 가지 옵션은 노드 운영 책임에 따라 구분할 수 있습니다.
AWS Fargate는 EC2 인스턴스 개념이 없는 서버리스 컨테이너 실행 환경입니다. Pod를 선언하면 AWS가 해당 Pod를 위한 격리된 실행 환경을 자동으로 프로비저닝합니다. Kubernetes API 상에서는 노드가 존재하지만, 운영자는 노드 프로비저닝이나 관리 작업을 전혀 수행하지 않아도 됩니다. 다만 DaemonSet 사용 불가, GPU 미지원, 노드 수준 커스터마이징 불가 등의 제약이 있어 범용 컴퓨팅보다는 특정 워크로드에 더 적합합니다.
왜 Fargate는 DaemonSet을 지원하지 않을까?
DaemonSet은 클러스터의 모든 노드에 Pod를 하나씩 실행하는 리소스입니다. 이것이 의미를 갖기 위해서는 여러 Pod이 공유하는 노드가 존재해야 합니다. 모니터링 에이전트나 로그 수집기를 DaemonSet으로 띄우는 이유도, 하나의 노드 위에서 실행 중인 여러 Pod를 에이전트 하나가 공유 커널과 네트워크 인터페이스를 통해 감시할 수 있기 때문입니다.
Fargate에서는 이 전제가 성립하지 않습니다. Fargate의 각 Pod은 자체 커널, CPU, 메모리, 네트워크 인터페이스를 독립적으로 가진 전용 VM 위에서 실행됩니다. 즉 여러 Pod이 하나의 노드를 공유하는 구조 자체가 없습니다.
따라서 공식 문서에도 DaemonSet 대신 에이전트를 각 Pod 안에 사이드카 컨테이너로 배포할 것을 권장합니다.
나머지 두 가지(Managed Node Groups, Self-managed Nodes)는 EC2 인스턴스가 노드로 실행되는 방식입니다. 차이는 그 EC2 인스턴스의 프로비저닝·패치·업그레이드를 누가 어느 범위까지 처리하느냐입니다. 공식 문서는 Managed Node Groups를 기반으로, Self-managed nodes에 대해서는 다음과 같이 설명합니다.
“Self-managed nodes are another option which support all of the criteria listed, but they require a lot more manual maintenance.”
즉, Self-managed nodes는 Managed Node Groups가 할 수 있는 것을 전부 지원하면서 그 이상의 제어권도 갖지만, 노드 운영 전반을 운영자가 직접 책임져야 합니다.
Compare compute options
공식 문서의 비교 표를 주요 항목 중심으로 정리하면 다음과 같습니다.
| 항목 | EKS managed node groups | EKS Auto Mode | EKS Hybrid Nodes |
|---|---|---|---|
| 노드 실행 위치 | AWS EC2 | AWS EC2 | 고객 온프레미스·엣지 |
| 노드 프로비저닝·스케일링 | 운영자 | AWS 자동 | 운영자 |
| OS 보안 패치 | 운영자 | AWS 자동 | 운영자 |
| 노드 OS 이미지(AMI) 업데이트 | 콘솔 알림 후 운영자 트리거 | AWS 자동 (최대 21일 수명) | 운영자 |
| Kubernetes 버전 업그레이드 | 콘솔 알림 후 운영자 트리거 | AWS 자동 | 운영자 |
| 노드 SSH 접속 | 가능 | 불가 | 가능 |
| 커스텀 노드 OS 이미지 사용 | 가능 | 불가 | 가능 |
| 커스텀 CNI 사용 | 가능 | 불가 | 가능 |
| 노드 시작 시 kubelet 플래그 등 부트스트랩 인수 전달 | 가능 | 불가 | 가능 |
| Windows 컨테이너 | 가능 | 불가 | 불가 |
| GPU 지원 | Amazon Linux 한정 | 가능 | 가능 |
| EBS 스토리지 | 가능 | 가능 (기본 내장) | 불가 |
| 퍼블릭 서브넷에서 Pod 실행 | 가능 | 가능 | 불가 |
| 요금 | EC2 비용 | EC2 비용 + Auto Mode 추가 요금 | 연결된 노드 vCPU 시간당 요금 |
관리형 노드 그룹은 EC2 인스턴스를 Auto Scaling Group으로 묶어 일부 작업을 AWS가 처리합니다. AMI 업데이트와 Kubernetes 버전 업그레이드는 콘솔 알림을 확인한 후 운영자가 직접 실행해야 합니다.
EKS는 기본적으로 AWS가 Kubernetes에 맞게 구성한 EKS 최적화 AMI를 노드 OS 이미지로 사용합니다. 관리형 노드 그룹은 EC2 Launch Template을 통해 이 기본 이미지 대신 직접 만든 AMI를 지정할 수 있어, 특수한 커널 모듈이나 내부 보안 정책을 노드에 적용해야 하는 경우에 유용합니다.
마찬가지로 EKS는 기본적으로 VPC CNI를 사용하여 Pod에 VPC IP를 직접 할당합니다. 관리형 노드 그룹은 Launch Template의 User Data(부트스트랩 스크립트)를 통해 노드 시작 시점에 Calico, Cilium 등 다른 CNI 플러그인을 설치하여 교체할 수 있습니다. EKS Auto Mode는 VPC CNI를 AWS가 직접 관리하는 기본 구성 요소로 통합하기 때문에 교체가 불가합니다.
EKS Auto Mode는 EC2 인스턴스의 프로비저닝, 패치, 업그레이드를 AWS가 완전히 처리합니다. 노드는 AWS가 선택한 AMI로 실행되며, SSH/SSM 접속이 차단됩니다. 노드 수명은 최대 21일로 제한되어 자동으로 교체됩니다. Karpenter 기반 오토스케일링, EBS CSI 스토리지, ALB/NLB 로드밸런서 프로비저닝, VPC CNI 네트워킹, Pod Identity가 별도 Add-on 없이 기본 제공됩니다. 노드 운영 부담을 제거하는 대신 직접 제어권을 포기하는 트레이드오프입니다.
EKS Hybrid Nodes는 노드가 고객 온프레미스에 위치한다는 점에서 근본적으로 다릅니다. 노드 운영 책임은 관리형 노드 그룹과 동일하게 운영자가 집니다. SSH, 커스텀 AMI, 커스텀 CNI 등 제어권도 관리형 노드 그룹과 같은 수준입니다. 다만 AWS 클라우드 리소스인 EBS를 사용할 수 없고, 퍼블릭 서브넷에서 Pod를 실행할 수 없습니다.
Add-ons and Capabilities
Add-ons
Add-ons는 Amazon EKS 클러스터의 핵심 기능을 확장하고 관리를 간소화하기 위해 AWS가 제공하는 검증된 소프트웨어 구성 요소입니다. VPC CNI, kube-proxy, CoreDNS처럼 클러스터가 동작하기 위해 필요한 구성 요소들이 여기에 해당합니다.
Kubernetes에서는 이런 구성 요소들을 Helm이나 kubectl apply로 직접 설치하고, 버전 업데이트도 운영자가 직접 처리해야 합니다. EKS는 이를 EKS 관리형 Add-on으로 제공합니다. AWS가 검증한 버전으로 설치되며, 보안 패치와 버그 픽스가 포함된 업데이트를 AWS Console, CLI, API를 통해 관리할 수 있습니다.
# 클러스터에 설치된 Addon 목록 확인
aws eks list-addons --cluster-name <cluster_name> | jq
# 특정 Addon 상세 정보
aws eks describe-addon --cluster-name <cluster_name> --addon-name vpc-cni | jq
클러스터 생성 시 다음 세 가지 Add-on이 자동으로 설치됩니다.
- VPC CNI — Pod에 VPC IP를 직접 할당하는 네트워크 플러그인
- kube-proxy — 각 노드에서 Service의 네트워크 규칙을 관리
- CoreDNS — 클러스터 내부 DNS 서비스

이 외에도 EBS CSI Driver, Pod Identity Agent, Node Monitoring Agent 등을 필요에 따라 추가할 수 있습니다. Add-on 목록과 각 노드 타입별 호환 여부는 AWS add-ons 페이지에서 확인할 수 있습니다.
앞서 다뤘듯이, EKS Auto Mode에서는 VPC CNI, EBS CSI Driver, Load Balancer Controller 등이 Add-on이 아닌 Managed Capabilities로 통합됩니다. 별도로 설치하거나 버전을 관리할 필요가 없습니다.
Capabilities
EKS Capabilities는 Add-on과 다릅니다. Add-on이 클러스터 운영을 위한 기반 구성 요소라면, Capabilities는 클러스터 위에서 애플리케이션과 인프라를 어떻게 관리할 것인가에 관한 기능입니다.
Add-on은 워커 노드 위에서 클러스터 리소스를 소비하며 실행되지만, Capabilities는 고객의 워커 노드가 아닌 EKS(AWS) 쪽에서 실행됩니다. 클러스터에는 Kubernetes CRD(Custom Resource Definition)만 설치되고, 실제 컨트롤러는 AWS가 관리하는 인프라에서 동작합니다. 보안 패치·업데이트·운영을 AWS가 처리하므로 운영자는 클러스터 플랫폼 운영이 아닌 애플리케이션 개발에 집중할 수 있습니다.
현재 세 가지 Capability가 제공됩니다.
- ACK(AWS Controllers for Kubernetes)는 Kubernetes API로 AWS 리소스를 관리합니다. S3 버킷, RDS 데이터베이스, IAM 역할 등을 Kubernetes YAML로 선언하면 ACK가 AWS API를 호출하여 리소스를 생성하고 상태를 지속적으로 동기화합니다. S3, RDS, DynamoDB, Lambda 등 50개 이상의 AWS 서비스를 지원합니다.
- Argo CD는 Git 저장소를 신뢰할 수 있는 유일한 소스(source of truth)로 삼아 GitOps 방식의 지속적 배포를 구현합니다. Git에 변경 사항이 커밋되면 클러스터를 자동으로 동기화하고, 선언된 상태와 실제 상태가 달라지는 드리프트를 감지하여 지속적으로 조정합니다. 단일 Argo CD 인스턴스로 여러 클러스터를 관리할 수 있습니다.
- kro(Kube Resource Orchestrator)는 여러 Kubernetes·AWS 리소스를 하나의 고수준 추상화로 묶는 커스텀 API를 생성합니다. 플랫폼 팀이 표준 패턴을 정의하면 개발자는 복잡한 인프라 세부사항 없이 단순한 API 호출로 전체 스택을 프로비저닝할 수 있습니다.
세 Capability는 독립적으로 사용할 수 있지만 함께 사용할 때 더 강력합니다. 예를 들어 Argo CD로 애플리케이션 배포를 자동화하고, ACK로 해당 애플리케이션이 필요한 RDS·S3 같은 AWS 리소스를 함께 관리하며, kro로 이 조합을 재사용 가능한 패턴으로 만들 수 있습니다.
Hands-on
실제 클러스터를 배포하여 클러스터와 워커 노드 등이 실제로 어떻게 구성되어 있는지 확인해보고자 합니다.
이를 위한 실습 환경은 아래 저장소에 위치하고 있습니다.
eks-pratice/week1/public at main · pyy0715/eks-pratice
kubectl Authentication
EKS에서 kubectl이 어떻게 동작하는지 이해하려면 인증(Authentication)과 인가(Authorization)가 어떻게 연결되는지를 먼저 파악해야 합니다.
kubeconfig
클러스터를 배포한 후 가장 먼저 하는 작업입니다.
CLUSTER_NAME=myeks
aws eks update-kubeconfig --region ap-northeast-2 --name $CLUSTER_NAME
이 명령어는 ~/.kube/config에 클러스터 접근 정보를 기록합니다. 실제로 무엇이 기록되는지 확인해 보겠습니다.

일반적인 Kubernetes의 kubeconfig에는 users 섹션에 클라이언트 인증서 경로나 정적 토큰이 직접 들어갑니다. EKS에서는 exec 블록이 그 자리를 차지합니다. kubectl이 API 서버에 요청을 보내기 직전마다 이 exec 블록의 명령어(aws eks get-token)를 실행하여 토큰을 동적으로 발급받습니다.
aws eks get-token이 실제로 무엇을 만드는지 확인합니다.
aws eks get-token --cluster-name $CLUSTER_NAME --region ap-northeast-2

k8s-aws-v1. 뒤의 문자열을 base64 디코딩하면 다음과 같은 구조가 드러납니다.
aws eks get-token --cluster-name "$CLUSTER_NAME" --region ap-northeast-2 \
| jq -r '.status.token' \
| cut -d. -f2 \
| base64 -d 2>/dev/null

토큰의 실체는 AWS STS GetCallerIdentity API에 대한 presigned URL입니다. 인증 정보 자체를 담고 있는 것이 아니라, 이 URL을 호출하면 AWS가 내 신원을 증명해줄 수 있다는 서명된 요청입니다. 유효 시간이 최대 15분인 이유도 presigned URL의 만료 시간 때문입니다.
aws eks get-token은 aws sts get-caller-identity로 출력되는 현재 자격증명으로 presigned URL을 서명합니다. 따라서 어떤 IAM identity로 토큰을 발급받느냐가 kubectl의 권한을 결정합니다.
IAM Authenticator
이제 이 토큰이 API 서버에 도달한 이후의 과정을 살펴보겠습니다. kubectl은 토큰을 Authorization: Bearer k8s-aws-v1.XXX 헤더에 담아 API 서버에 전송합니다.
이 과정을 실제로 관찰하려면 verbosity를 높이면 됩니다.
kubectl get node -v=6
# "Response" verb="GET" url="https://2EA390562555C77790151FF850D99BEB.gr7.ap-northeast-2.eks.amazonaws.com/api/v1/nodes?limit=500" status="200 OK" milliseconds=600
API 서버는 토큰을 직접 검증하지 않고 컨트롤 플레인에서 실행 중인 AWS IAM Authenticator 웹훅 서버에 TokenReview를 위임합니다. IAM Authenticator는 토큰에서 presigned URL을 추출하여 AWS STS를 직접 호출합니다. STS가 IAM principal ARN을 반환하면 인증이 성공합니다.
IAM 자격증명이 비활성화되거나 만료되면 STS 호출 자체가 실패하므로 기존 토큰도 즉시 무효화됩니다. 토큰의 서명이 위조된 경우에도 STS가 오류를 반환하여 인증이 실패합니다.
Access Entries
인증이 완료되었다고 바로 Kubernetes 리소스에 접근할 수 있는 것은 아닙니다. IAM 인증과 Kubernetes 인가는 별개의 계층입니다.
간소화된 Amazon EKS 액세스 관리 제어 톺아보기 | AWS 기술 블로그
IAM principal과 Kubernetes 권한을 연결하는 현재 권장 방식은 Access Entries입니다. EKS Cluster Access Management API를 통해 Access Entry를 생성하고, Access Policy를 연결하면 EKS Authorizer를 통과하여 권한이 부여됩니다.
- Access Entry는 IAM principal(User 또는 Role)과 EKS 클러스터를 연결하는 등록 항목입니다.
- Access Policy는 AWS가 미리 정의해 둔 Kubernetes 권한 템플릿입니다. IAM 정책과 달리 사용자가 직접 만들거나 수정할 수 없습니다. Access Entry에 연결하여 실제 권한을 부여합니다.
이전에는 kube-system 네임스페이스의 aws-auth ConfigMap에 IAM ARN과 Kubernetes 그룹을 직접 등록하는 방식을 사용했습니다. 클러스터 내부 리소스를 직접 수정해야 하므로 잘못 수정하면 클러스터 접근이 불가능해지는 위험이 있어 현재는 Access Entries로 마이그레이션이 권장됩니다.
사용 가능한 Access Policy는 aws eks list-access-policies 또는 문서에서 확인할 수 있습니다. 주요 정책들을 정리하면 다음과 같습니다.
| Access Policy | Kubernetes 권한 범위 | 적합한 대상 |
|---|---|---|
AmazonEKSClusterAdminPolicy |
클러스터 전체 모든 권한 (*) |
클러스터 관리자 |
AmazonEKSAdminPolicy |
대부분의 리소스 권한 (네임스페이스 단위) | 네임스페이스 관리자 |
AmazonEKSEditPolicy |
리소스 생성·수정·삭제 가능 | 개발자 |
AmazonEKSViewPolicy |
읽기 전용 | 모니터링, 감사 |
현재 클러스터에 등록된 Access Entry 목록을 확인합니다.
# 현재 클러스터에 등록된 Access Entry 목록
aws eks list-access-entries --cluster-name $CLUSTER_NAME
# 현재 자신의 IAM identity에 연결된 Kubernetes 권한
aws eks list-associated-access-policies \
--cluster-name $CLUSTER_NAME \
--principal-arn $(aws sts get-caller-identity --query Arn --output text)

principalArn— IAM useradmin이 Access Entry로 등록되어 있습니다.policyArn—AmazonEKSClusterAdminPolicy가 연결되어 있으므로 클러스터 전체에 대한 모든 Kubernetes 권한을 갖습니다.accessScope.type: cluster— 특정 네임스페이스가 아니라 클러스터 전체에 적용됩니다.
따라서 앞서 kubectl get node -v=6에서 200 OK가 반환된 것은 이 권한 구조가 통과된 결과입니다. IAM user admin이 AmazonEKSClusterAdminPolicy를 통해 노드 목록 조회를 포함한 모든 Kubernetes 작업이 허용되어 있기 때문입니다.
Worker Node
앞서 kubectl이 API Server에 인증하는 과정을 확인했습니다. 이번에는 실제 워크로드가 실행되는 워커 노드 안으로 들어가서, EKS 노드가 어떻게 구성되어 있는지 직접 확인합니다.
관리 콘솔 또는 CLI로 노드 IP를 확인하고 SSH 접속합니다.
# 실행 중인 EC2 인스턴스 목록 확인
aws ec2 describe-instances \
--filters Name=instance-state-name,Values=running \
--query "Reservations[*].Instances[*].{Name:Tags[?Key=='Name']|[0].Value,PublicIP:PublicIpAddress,PrivateIP:PrivateIpAddress}" \
--output table
# 노드 IP 변수 설정
NODE1=<퍼블릭 IP>
NODE2=<퍼블릭 IP>
# SSH 접속
ssh ec2-user@$NODE1
sudo su -
노드에 접속했다면, Kubernetes가 정상적으로 동작하기 위해 필요한 기본 조건들이 어떻게 설정되어 있는지 확인할 수 있습니다. 대표적으로 아래와 같습니다.
- SELinux Permissive
- Swap 비활성화
- 컨테이너 네트워킹을 위한 커널 파라미터
kubeadm으로 직접 설치해봤다면 이 항목들이 왜 필요한지 알고 있을 것입니다.
-
SELinux Permissive
Enforcing모드에서는 컨테이너가 호스트 파일시스템에 접근하거나 네트워크 네임스페이스를 조작할 때 SELinux 정책이 차단합니다. -
Swap 비활성화
kubelet은 Pod의 메모리 requests/limits를 기반으로 스케줄링과 리소스 관리를 수행합니다. Swap이 활성화되면 메모리 부족 상황에서 컨테이너가 종료되지 않고 Swap으로 밀려나면서, 이러한 보장이 깨집니다. 이 때문에 kubelet은 기본적으로 Swap이 활성화된 상태에서 실행을 거부합니다.
-
커널 파라미터
net.bridge.bridge-nf-call-iptables— 브리지(veth)를 통과하는 트래픽이 iptables를 거치도록 합니다. 설정이 없으면 kube-proxy의 Service 라우팅 규칙이 적용되지 않습니다.net.ipv4.ip_forward— 컨테이너에서 외부로 나가는 트래픽을 허용합니다. 이 값이 비활성화되어 있으면 Pod 간 통신이나 외부 통신이 제한됩니다.
위와 같은 설정들은 Kubernetes 동작에 필수적인 요소들입니다. 하지만 EKS에서 제공하는 최적화 AMI에는 이 값들이 이미 반영되어 있어, 사용자가 직접 설정할 필요는 없습니다.
containerd
전제 조건이 갖춰진 노드 위에서 실제로 컨테이너를 실행하는 것은 containerd입니다. kubelet은 CRI(Container Runtime Interface)를 통해 containerd에 컨테이너 실행을 지시합니다.
cat /etc/containerd/config.toml
SystemdCgroup = true— cgroup 관리 드라이버로 systemd를 사용합니다. kubelet 설정의cgroupDriver: systemd와 반드시 일치해야 합니다. 불일치하면 노드가NotReady상태가 되거나 컨테이너 리소스 관리에 문제가 발생합니다.sandbox = "localhost/kubernetes/pause"— 파드 생성 시, 가장 먼저 만드는 pause 컨테이너의 이미지 위치입니다.conf_dir = "/etc/cni/net.d"— CNI 플러그인 설정 파일 위치입니다. Pod 네트워크 네임스페이스를 초기화할 때 containerd가 이 디렉터리에서 설정을 읽어 VPC CNI를 호출합니다.
현재 실행 중인 컨테이너 목록을 확인합니다.
nerdctl ps
출력을 파드 단위로 묶으면 세 개의 파드가 실행 중입니다.
# coredns 파드
fe60921c61fc eks/coredns:v1.13.2 "/coredns" k8s://kube-system/coredns-d487b6fcb-hhvbw/coredns
99a53b84ae58 eks/pause:3.10 "/pause" k8s://kube-system/coredns-d487b6fcb-hhvbw
# kube-proxy 파드
6c91a6c00fef eks/kube-proxy:v1.34.3 "kube-proxy" k8s://kube-system/kube-proxy-tznr2/kube-proxy
41f7aad791b1 eks/pause:3.10 "/pause" k8s://kube-system/kube-proxy-tznr2
# aws-node 파드
da9d4c5c9bf4 amazon-k8s-cni:v1.21.1 "/app/aws-vpc-cni" k8s://kube-system/aws-node-q8zbj/aws-node
997b8fe2588e aws-network-policy-agent:... "/controller" k8s://kube-system/aws-node-q8zbj/aws-eks-nodeagent
1ad517ae9b07 eks/pause:3.10 "/pause" k8s://kube-system/aws-node-q8zbj
모든 이미지가 602401143452.dkr.ecr.*.amazonaws.com에서 옵니다. 602401143452는 AWS가 EKS 컴포넌트 이미지를 관리하는 공식 ECR 계정 ID입니다.
pause 컨테이너는 Pod의 network namespace와 IPC namespace를 생성하고 유지하는 역할을 하며, Pod 내부의 다른 컨테이너들은 이 namespace를 공유하여 동일한 네트워크 환경(IP, 포트 등)을 사용하게 됩니다.
EKS에서만이 아니라 모든 Kubernetes 환경에서 파드가 생성될 때 가장 먼저 실행됩니다. EKS에서 특이한 점은 이 pause 컨테이너의 이미지가 registry.k8s.io 대신 AWS 공식 ECR(602401143452.dkr.ecr.us-west-2.amazonaws.com/eks/pause)에서 오고, containerd 설정의 sandbox = "localhost/kubernetes/pause"로 로컬 캐시에서 가져오도록 구성되어 있다는 것입니다.
따라서 Pod가 생성될 때 가장 먼저 실행되는 컨테이너가 바로 pause 컨테이너이며, 이후 실제 애플리케이션 컨테이너(coredns, kube-proxy, aws-node 등)가 이 환경에 연결되어 동작합니다.
aws-node는 모든 노드에서 실행되는 DaemonSet으로, VPC CNI를 구성하는 핵심 컴포넌트입니다. 공식 문서에 따르면 VPC CNI는 두 가지 컴포넌트로 구성됩니다.
- CNI Binary (
/opt/cni/bin/aws-cni) — 노드 파일시스템에 위치하는 바이너리입니다. kubelet이 파드를 추가하거나 삭제할 때 직접 호출합니다. pause 컨테이너의 네트워크 네임스페이스에 veth pair를 연결하고 IP를 할당하는 실제 작업을 수행합니다. - ipamd (
aws-node컨테이너) — 노드에서 상시 실행되는 데몬입니다. ENI를 관리하고 IP Warm Pool을 유지합니다. CNI Binary가 파드에 IP를 요청하면 ipamd가 Warm Pool에서 꺼내 줍니다.
한편, 실제 aws-node Pod를 확인해보면 컨테이너가 두 개입니다. 이는 VPC CNI 구성 요소 외에, EKS에서 추가로 제공하는 기능이 함께 포함되어 있기 때문입니다.
kubectl get pod -n kube-system -l k8s-app=aws-node -o jsonpath='{.items[0].spec.containers[*].name}'
# -> aws-node
# -> aws-eks-node-agent
aws-node(amazon-k8s-cni): ipamd(IP Address Management Daemon)입니다. 노드의 ENI를 관리하고 파드에 할당할 IP 주소의 Warm Pool을 유지합니다. 파드 요청이 들어오기 전에 미리 Secondary ENI를 생성하고 IP를 확보해두어 파드 생성 시 즉시 IP를 할당할 수 있습니다.aws-eks-nodeagent(aws-network-policy-agent): VPC CNI의 일부는 아니며, Kubernetes Network Policy를 eBPF로 구현하는 에이전트입니다. 파드 간 트래픽을 네트워크 정책에 따라 허용하거나 차단하는 역할을 합니다.
따라서 containerd 설정의 conf_dir = "/etc/cni/net.d"가 이 흐름의 출발점입니다. pause 컨테이너로 network namespace가 확보되면 containerd는 이 디렉터리의 10-aws.conflist를 읽어 plugins 배열에 정의된 플러그인을 순서대로 실행합니다.
cat /etc/cni/net.d/10-aws.conflist | jq
- aws-cni — ipamd에 IP를 요청하고, veth pair를 생성하여 pause 컨테이너의 network namespace와 노드를 연결합니다.
- egress-cni — IPv4 전용 노드에서 파드가 IPv6 트래픽을 외부로 보낼 때 처리합니다.
"enabled": "false"로 현재 비활성화되어 있습니다. - portmap — 파드 스펙에
hostPort가 선언된 경우, 노드 포트와 파드 포트를 iptables NAT 규칙으로 매핑합니다.
kubelet
일반적인 Kubernetes에서는 컨트롤 플레인 구성 요소(kube-apiserver, etcd, kube-controller-manager, kube-scheduler)를 Static Pod로 관리합니다. Static Pod는 API Server를 거치지 않고 kubelet이 manifests/ 디렉터리를 직접 감시하여 실행하는 파드입니다. API Server 자체가 Static Pod이기 때문에 API Server가 없는 상황에서도 kubelet이 직접 기동할 수 있어야 하므로 이 방식을 사용합니다.
하지만 EKS에서는 컨트롤 플레인이 AWS 관리 VPC에서 실행됩니다. 워커 노드 입장에서 API Server는 관리형 ENI를 통해 연결되는 외부 엔드포인트입니다. 따라서 워커 노드의 manifests/ 디렉터리는 아래와 같이 비어 있습니다.
tree /etc/kubernetes
kubelet 핵심 설정을 확인합니다.
cat /etc/kubernetes/kubelet/config.json | jq ‘{cgroupDriver, maxPods, providerID, serverTLSBootstrap, evictionHard}’
-
cgroupDriver: systemd
앞서 containerd 설정에서 확인한
SystemdCgroup = true와 일치합니다. 두 설정이 같은 드라이버를 사용해야 kubelet과 containerd가 cgroup을 일관되게 관리할 수 있습니다. -
maxPods: 17
VPC CNI가 파드에 VPC 네이티브 IP를 직접 할당하는 구조상 ENI 수에 의해 결정됩니다. 자세한 내용은 네트워킹 섹션에서 다룹니다.
-
providerID
Cloud Controller Manager(CCM)가 Kubernetes 노드 오브젝트와 EC2 인스턴스를 연결하기 위해 사용하는 식별자입니다. CCM은 이 값을 기반으로 EC2 API를 호출하여 인스턴스 정보(AZ, 인스턴스 유형 등)를 노드 레이블에 반영하거나, EC2에서 종료된 인스턴스를 클러스터에서 제거합니다. 일반적인 Kubernetes에서는 kubelet 실행 시
--provider-id플래그로 수동 지정해야 했지만, EKS에서는nodeadm이 EC2 인스턴스 메타데이터에서 자동으로 읽어 설정합니다.
kubelet이 API Server에 인증하는 방식을 확인합니다.
cat /var/lib/kubelet/kubeconfig | grep -A8 “exec:”
kubectl의 kubeconfig와 동일한 구조입니다. kubelet도 aws eks get-token으로 IAM 토큰을 발급받아 API Server에 인증합니다. 노드에 할당된 EC2 IAM 역할이 이 토큰 발급의 자격증명입니다.
반대 방향인 API Server와 kubelet 통신에는 TLS 서버 인증서가 사용됩니다. serverTLSBootstrap: true에 의해 자동 발급된 인증서를 확인합니다.
cat /var/lib/kubelet/pki/kubelet-server-current.pem | openssl x509 -text -noout | grep -A6 “Subject Alternative Name”
일반적인 Kubernetes에서는 이 인증서의 SAN에 노드 IP 하나만 들어갑니다. EKS에서는 프라이빗 IP와 퍼블릭 IP가 모두 포함됩니다. 엔드포인트 설정이 퍼블릭이든 프라이빗이든 API Server가 관리형 ENI를 통해 kubelet에 접근할 때, TLS 검증이 정상 동작하도록 하기 위해서입니다.
이 인증서를 발급하기 위해 kubelet이 제출한 CSR은 로컬에서 확인할 수 있습니다.

Cluster Endpoint Access
앞서 EKS 클러스터에 두 개의 VPC가 관여한다고 설명했습니다. 이 두 VPC 사이의 실제 통신이 어떻게 이루어지는지를 확인해보도록 하겠습니다.
공식 문서에 따르면 EKS는 클러스터 생성 시, 아래 두 가지를 자동으로 생성합니다.
-
NLB 기반 API Server 엔드포인트
Kubernetes API Server 앞에 NLB를 배치하여 트래픽을 분산합니다. ALB가 아닌 NLB를 사용하는 이유는 Kubernetes API Server가 HTTP/HTTPS가 아닌 gRPC와 WebSocket 기반의 장기 연결을 사용하기 때문입니다. NLB는 L4(TCP) 레벨에서 동작하므로 이런 연결을 그대로 통과시킬 수 있습니다.
-
EKS managed ENI
서로 다른 AZ에 2개의 ENI를 고객 VPC 안에 생성합니다. API Server가 워커 노드의 kubelet에 접근할 때(kubectl logs, exec 등) 이 ENI를 통해 고객 VPC로 진입합니다.
NLB
NLB는 AWS 관리 VPC에 위치하므로 고객 계정에서 직접 확인할 수 없습니다. 단, API Server 엔드포인트 도메인에 대한 DNS 조회 결과에서 간접적으로 확인할 수 있습니다.
현재 설정을 먼저 확인합니다.
kubectl cluster-info
API 서버 엔드포인트가 *.gr7.ap-northeast-2.eks.amazonaws.com 형식입니다. 이 도메인이 AWS 관리 VPC 안의 로드밸런서(NLB)를 가리킵니다. 운영자는 이 엔드포인트 뒤에 있는 API Server나 etcd에 직접 접근할 수 없습니다.
이 도메인이 실제로 AWS 소유의 IP로 해석되는지 확인할 수 있습니다.
APIDNS=$(aws eks describe-cluster --name $CLUSTER_NAME | jq -r .cluster.endpoint | cut -d '/' -f 3)
dig +short $APIDNS
curl -s ipinfo.io/<조회된 IP>

현재 이 클러스터는 아래와 같이 퍼블릭 엔드포인트가 활성화되어 있어 인터넷에서 DNS 조회가 가능합니다. 접근 모드에 대해서는 아래에서 자세히 다룹니다.

EKS managed ENI
API Server가 워커 노드의 kubelet에 접근하려면 AWS 관리 VPC에서 고객 VPC로 진입할 수 있어야 합니다. EKS는 이를 위해 클러스터 생성 시 고객 VPC 서브넷에 ENI를 2개 생성합니다. API Server 프로세스는 이 ENI를 네트워크 인터페이스로 직접 사용하여 고객 VPC의 IP 주소를 획득합니다. 즉 API Server 자체가 고객 VPC 네트워크에 직접 연결된 구조입니다.
managed ENI는 Description 필드가 "Amazon EKS <클러스터명>" 형식으로 설정되어 있습니다.
aws ec2 describe-network-interfaces \
--filters "Name=description,Values=Amazon EKS $CLUSTER_NAME" \
--query "NetworkInterfaces[*].NetworkInterfaceId"
출력에 대해 확인해보면, 아래와 같이 소유자와 요청자가 다른 것을 확인할 수 있습니다.

EKS 보안 그룹은 AWS 콘솔과 API 기준으로 두 가지로 구분됩니다.
- Cluster Security Group: EKS가 클러스터 생성 시 자동으로 생성합니다. 공식 문서에 따르면 managed ENI와 워커 노드 양쪽에 자동으로 연결되며,
Self → All인바운드 규칙으로 컨트롤 플레인과 노드 간 모든 트래픽을 허용합니다. - Additional security group: 클러스터 생성 시 사용자가 선택적으로 지정하는 보안 그룹입니다. 공식 문서에 따르면 EKS는 이 보안 그룹을 managed ENI에는 연결하지만 노드 그룹에는 연결하지 않습니다.
When you create a cluster, you can (optionally) specify your own security groups. If you do, then Amazon EKS also associates the security groups that you specify to the network interfaces that it creates for your cluster. However, it doesn’t associate them to any node groups that you create.
managed ENI에 보안 그룹이 어떻게 연결되어 있는지 확인합니다.
aws ec2 describe-network-interfaces \
--filters "Name=description,Values=Amazon EKS $CLUSTER_NAME" \
--query 'NetworkInterfaces[*].{ENI:NetworkInterfaceId,SG:Groups[*].{ID:GroupId,Name:GroupName}}'
[
{
"ENI": "eni-06d60bba7d112a2cc",
"SG": [
{
"ID": "sg-03b28a8144a493f81",
"Name": "eks-cluster-sg-myeks-104368993"
},
{
"ID": "sg-0dea6d3699a2fc2cd",
"Name": "myeks-cluster-20260318003029966500000003"
}
]
},
{
"ENI": "eni-0d98c6f6ec61bf0b1",
"SG": [
{
"ID": "sg-03b28a8144a493f81",
"Name": "eks-cluster-sg-myeks-104368993"
},
{
"ID": "sg-0dea6d3699a2fc2cd",
"Name": "myeks-cluster-20260318003029966500000003"
}
]
}
]
이 실습 환경에서는 클러스터 생성 시 Additional security group으로 myeks-cluster-*(sg-0dea6d3699a2fc2cd)를 지정했습니다. 필요한 포트만 명시적으로 허용하는 방식입니다.
워커 노드에 연결된 보안 그룹을 확인합니다.
aws ec2 describe-instances \
--filters "Name=tag:eks:cluster-name,Values=$CLUSTER_NAME" \
--query 'Reservations[*].Instances[*].SecurityGroups[*].{ID:GroupId,Name:GroupName}'
[
[
[
{
"ID": "sg-05b853cef9dc77ba5",
"Name": "myeks-node-group-sg"
},
{
"ID": "sg-0ae9875131ffcb88d",
"Name": "myeks-node-20260318003029966800000004"
}
]
],
[
[
{
"ID": "sg-05b853cef9dc77ba5",
"Name": "myeks-node-group-sg"
},
{
"ID": "sg-0ae9875131ffcb88d",
"Name": "myeks-node-20260318003029966800000004"
}
]
]
]
워커 노드에 Cluster security group(eks-cluster-sg-*)이 없습니다. 워커 노드 보안 그룹의 인바운드 규칙을 확인하면 실제 통신 허용 구조를 확인할 수 있습니다.

Additional security group(sg-0dea6d3699a2fc2cd)이 managed ENI에 연결되어 있고, 워커 노드 보안 그룹(myeks-node-*)은 이를 소스로 여러 포트를 허용합니다. 공식 문서에서 컨트롤 플레인과 노드 간 최소 필요 포트로 443, 10250, 53을 안내하고 있으며, 실제 환경에서는 웹훅, 스케줄러 헬스체크 등을 위해 추가 포트가 필요합니다.
정리하면 Cluster security group은 EKS가 클러스터 생성 시 자동으로 생성합니다. 인바운드 규칙은 자기 자신을 소스로 참조하여 모든 트래픽을 허용합니다. 같은 보안 그룹에 속한 리소스끼리는 모든 트래픽이 허용되므로, 컨트롤 플레인과 워커 노드 간 통신을 보장하기 위한 AWS의 기본 안전장치입니다.
managed ENI에 자동으로 연결되며, 워커 노드에는 다음 경우에만 자동 연결됩니다.
- Launch Template을 별도로 지정하지 않은 Managed Node Group
- Fargate
자체 Launch Template을 지정한 Managed Node Group이나 Self-managed nodes는 보안 그룹을 사용자가 직접 지정하므로 Cluster security group이 자동 연결되지 않습니다. 또한 Fargate 파드는 Cluster security group만 사용합니다.
Additional security group은 클러스터 생성 시 사용자가 선택적으로 지정하는 보안 그룹입니다. managed ENI에만 연결되며 워커 노드에는 연결되지 않습니다. Cluster security group처럼 자기 자신을 소스로 참조하는 방식 대신 필요한 포트만 명시적으로 제어하고 싶을 때 사용합니다. 이 경우 워커 노드 보안 그룹에서 Additional security group을 소스로 필요한 포트를 직접 허용해야 합니다.
Access Mode
EKS 클러스터에서 발생하는 트래픽은 방향에 따라 세 가지로 구분됩니다.
- 사용자 → API Server
- 워커 노드 → API Server
- API Server → 워커 노드
이 중 세 번째 방향은 어떤 모드에서든 항상 managed ENI를 통해 이루어집니다. 앞서 확인한 kubelet 서버 인증서의 SAN에 프라이빗 IP와 퍼블릭 IP가 모두 포함되어 있던 이유가 여기 있습니다.
endpointPublicAccess와 endpointPrivateAccess 두 플래그의 조합으로 나머지 두 방향의 경로를 제어합니다.
aws eks describe-cluster --name $CLUSTER_NAME \
--query 'cluster.resourcesVpcConfig.{public:endpointPublicAccess,private:endpointPrivateAccess,cidrs:publicAccessCidrs}'
Public Only
클러스터 생성 시 기본 설정입니다. 사용자의 kubectl과 워커 노드의 kubelet 모두 인터넷을 통해 NLB로 접근합니다. VPC 내부에서 출발한 트래픽도 VPC 밖으로 나가지만 Amazon 네트워크 내에서만 이동합니다. 워커 노드의 트래픽이 인터넷을 경유하므로 운영 환경에서는 권장하지 않는 구성입니다.
De-mystifying cluster networking for Amazon EKS worker nodes | Containers
Public + Private
가장 일반적인 운영 환경 구성입니다. Private access를 활성화하면 EKS가 Route 53 프라이빗 호스팅 존을 자동으로 생성하여 고객 VPC에 연결합니다. VPC 내부에서 엔드포인트 도메인을 조회하면 NLB의 퍼블릭 IP 대신 managed ENI의 프라이빗 IP가 반환됩니다. 워커 노드 트래픽은 VPC 내부로 유지되고, 외부에서의 kubectl 접근은 퍼블릭 엔드포인트를 통해 유지됩니다.

Private Only
모든 API Server 접근이 VPC 내부 또는 연결된 네트워크(VPN, Direct Connect)를 통해서만 가능합니다. 이 모드에서는 퍼블릭 DNS 서버도 엔드포인트 도메인을 프라이빗 IP로 해석합니다. 도메인 자체는 인터넷에서 조회할 수 있지만 반환되는 IP가 VPC 내부 주소이므로 VPC 외부에서는 실제로 접근이 불가능합니다.
따라서 VPC 외부에서 kubectl을 사용하려면 VPC 내부로 진입할 수 있는 수단이 필요합니다.
- Bastion Host
- VPN / Direct Connect
- AWS Systems Manager Session Manager

한편 Private Only 모드에서 워커 노드의 아웃바운드 인터넷 액세스까지 차단한 완전한 격리 환경을 구성하려면 추가 요건이 생깁니다. 워커 노드가 ECR에서 이미지를 받고, STS로 자격증명을 발급받고, CloudWatch Logs에 로그를 전송하는 모든 경로가 인터넷을 통해 이루어지기 때문입니다. 이 경우 공식 문서에 따라 각 서비스에 대한 VPC 인터페이스 엔드포인트(PrivateLink)를 별도로 생성해야 합니다.
Comments