Check EKS deployment and control plane information

안녕하십니까.
CloudNet@ 팀 'Gasida' 님이 진행하시는 스터디 내용 중,
EKS를 직접 배포한 후에 제어부, 시스템 파드 정보, add-on 등 정보를 확인하고
학습하는 시간을 갖도록 하겠습니다.

01. EKS 실습 코드 배포
이전 포스팅에서 배포 명령을 입력해둔 후 Terraform 배포 코드에 대해서 알아보았습니다.
이어서 시작해보자면, 아래와 같이 배포가 완료되었고 실제로 확인해보도록 하겠습니다.

위와 같이 문제없이 complete 된 상황이여서 간단하게 Console 에서도 확인 해보겠습니다.


네, Cluster 와 NodeGroup을 제외하고도 지정된 리소스들이 정상적으로 생성이 되었습니다.
만약 자격 증명 설정을 하지 않았다면 아래와 같이 AWS API를 호출하지 못하여 에러가 발생합니다.

📌 자격 증명 설정
aws eks update-kubeconfig --region ap-northeast-2 --name myeks

📌 rename context 설정
kube config 정보를 확인하였을 때 name이 ARN 형식으로 되어있는 것을 아래와 같이 확인 가능합니다.

- contexts: 부분 아래에 name: 부분이 보시다 싶이 ARN 형식으로 되어있습니다.
- 이렇게 되면 context를 변경할 때 ARN full name으로 변경을 해야합니다.
# rename 전
kubectl config use-context arn:aws:eks:ap-northeast-2:093359840099:cluster/myeks
# rename 후
kubectl config use-context myeks
이런식으로 쉽게 context를 변경하기 위해 아래와 같이 rename 작업 까지 해주시면 됩니다.
# k8s config 확인 및 rename context
cat ~/.kube/config | grep current-context | awk '{print $2}' kubectl config rename-context $(cat ~/.kube/config | grep current-context | awk '{print $2}') myeks
cat ~/.kube/config | grep current-context
02. EKS 제어부 정보 확인
✅ eks 클러스터 정보 확인
kubectl cluster-info
Kubernetes control plane is running at https://E2F77ED0429BBB82740C7C2A9EBCF47E.gr7.ap-northeast-2.eks.amazonaws.com
CoreDNS is running at https://E2F77ED0429BBB82740C7C2A9EBCF47E.gr7.ap-northeast-2.eks.amazonaws.com/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
- kubectl이 EKS API Server에 정상적으로 연결되어 있다는 의미.
- 이 URL이 ~/.kube/config의 server 값과 동일하며, E2F77ED0429... 부분이 클러스터 고유 ID를 의미.
- CoreDNS is running 상태를 보아 클러스터 내부 DNS 서비스인 CoreDNS가 정상 동작을 의미.

✅ Endpoint 확인
CLUSTER_NAME=myeks
aws eks describe-cluster --name $CLUSTER_NAME | jq
실제로 terraform에서 배포했던 리소스들에 대한 엔드포인트가 JSON 형태로 출력됩니다.
어떤 리소스들이 배포가 되었는지 AWS Console에서 같이 비교해도 좋을 것 같습니다.
✅ dig를 조회하였을 때 출력되는 IP 소유 확인
APIDNS=$(aws eks describe-cluster --name $CLUSTER_NAME | jq -r .cluster.endpoint | cut -d '/' -f 3)
dig +short $APIDNS
13.209.169.249
3.34.7.35
- 해당 IP들은 EKS Control Plane에 할당된 공인 IP입니다. 즉, AWS가 관리하는 인프라 IP
- 내가 관리할 수 있는 영역은 Data Plane의 EC2 인스턴스이며, EKS는 AWS가 관리하므로 AWS Managed VPC에 해당 출력되는 IP들이 생성되어 있으며 우리한테는 보이지 않는 영역입니다.
- terrform에서 옵션을 'endpointPublicAccess: true'로 설정하여 control plane이 public endpoint를 가짐.
그렇다면, 왜 IP가 2개일까? EKS는 기본적으로 API Server를 HA로 구성(Multi-AZ 분산)하기 때문.
✅ eks 노드 그룹 정보 확인
aws eks describe-nodegroup --cluster-name $CLUSTER_NAME --nodegroup-name $CLUSTER_NAME-node-group | jq
출력해보니, 아래와 같이 JSON 형태로 출력이 되었는데 각 옵션들을 확인해보자.

1) status / health 정보
- 노드 그룹의 현재 상태를 나타내며, 문제가 발생하면 health.issues 배열에 에러가 기록됨.
| status value | 의미 |
| ACTIVE | 정상 동작 중 |
| CREATING | 생성 중 |
| CREATE_FAILED | 생성 실패 |
| DEGRADED | 일부 노드 문제 |
2) scalingConfig - 노드 수 제어
- 노드 수를 결정하는 핵심 설정이며, Terraform 코드에서 정의한 대로 배포가 되었음.
- desired_size : 평시 유지할 노드 수
- minSize ~ maxSize : Cluster Autoscaler가 조절 가능한 범위
- desiredSize는 minSize와 maxSize 사이 값이어야 한다.
3) capacityType - 비용과 직결
| 값 | 의미 | 비용 | 적합 워크로드 |
| ON_DEMAND | 안정적, 중단 없음 | 정가 | 프로덕션 핵심 서비스 |
| SPOT | AWS가 회수 가능 | 최대 90% 절감 | 배치, 비핵심 서비스 |
4) updateConfig - 업그레이드 안정성
- 노드 그룹 버전 업그레이드 시 한 번에 교체하는 노드의 비율을 의미한다.
- 현재 설정은 33이며, 노드 2대 기준으로 33%이므로 1대씩 순차 교체를 의미함.
5) nodeRole - 노드에 부여된 IAM Role
- Terraform 코드에 IAM Role을 직접 작성하지 않았지만 EKS 모듈이 자동으로 생성함.

실제로 확인해보았을 때 위와 같은 Role을 자동으로 부여하였음을 알 수 있다. (EKS 내부 통신에 필요한 권한)
✅ 노드 정보 확인 (OS와 컨테이너 런타임)
kubectl get node --label-columns=node.kubernetes.io/instance-type,eks.amazonaws.com/capacityType,topology.kubernetes.io/zone
kubectl get node -owide

- 레이블 기반으로 노드 정보를 확인하였을 때 인스턴스 타입, 용량 유형, AZ를 확인할 수 있다.
- '-o wide' 명령으로 노드 상세 정보를 확인할 수 있으며, 내 외부 IP, OS 이미지, containerd 버전 등 확인 가능.
✅ 인증 정보 확인
kubectl get node -v=6

인증 정보는 어디에서 API를 호출하는지, 보안 설정이나 로컬 환경에 따라 달라지는 것 같다.
- Config loaded from file: /Users/sunghwan/.kube/config : 내 로컬 macOS의 /kube/config 참조
또한 round_trippers.go:632 (kubernetes/client-go 라이브러리 내부 파일) 확인
- kubectl을 로컬에서 https get 요청 → eks api server에서 200OK 반환, 응답 속도는 399ms
✅ 토큰 기반 통신 구조 확인
aws eks get-token --cluster-name $CLUSTER_NAME --region $AWS_DEFAULT_REGION
kubectl이 EKS API Server에 접근할 때 사용하는 임시 인증 토큰을 발급하는 명령어 입니다.

- kubectl이 kind, apiVersion 형식을 읽어서 인증 토큰을 추출하며 kubectl 명령 실행 시 자동으로 호출함.
- expirationTimestamp에 출력되는 시간이 토큰 만료 시간이며, 발급 후 15분 후에 만료된다.
- 만료 되면 kubectl이 자동으로 재발급하여 연결 유지.
token은 base64로 인코딩 되었는데, 반대로 디코딩을 해볼까?

파라미터 별 의미를 아래와 같이 나열해보겠습니다.
- Action=GetCallerIdentity : STS에게 "이 자격 증명이 누구냐?"라고 묻는 API
- EKS가 이걸 호출해서 "어떤 IAM User/Role이 요청했는지" 확인함.
- X-Amz-Credential : 사용자 IAM Access Key ID, 서명 날짜, 리전, 서비스, 서명 버전 등 정보 출력
- X-Amz-Date : 토큰이 발급된 시각
- X-Amz-Expires=60 : 60초 후 이 URL이 만료됨 (URL자체는 60초, EKS 토큰은 15분동안 캐싱)
- X-Amz-Algorithm=AWS4-HMAC-SHA256 : AWS Signature Version 4 방식으로 서명
- X-Amz-Signature : 내 Secret Access Key로 서명한 값을 의미 (위변조 방지용)
즉, 이해를 돕기 위해 EKS 인증 서명 방식을 풀어보자면 아래와 같습니다.
1) 로컬에 AWS IAM 자격증명을 기반으로 STS(Security Token Service)를 통해 내 신원 저장
- 확인 명령어 : "cat ~/.aws/credentials"
- aws_secret_access_key가 서명에 사용되는 키입니다.
- aws_access_key_id는 IAM User 식별자 입니다.
2) 로컬에서 사용자가 kubectl 명령 실행
- ~/.kube/config를 읽어서 인증 방식을 확인하며, exec 블록에 정의된 명령을 실행하여 토큰을 가져옴.

3) STS PreSigned URL 생성
- AWS CLI가 로컬의 Secret Key로 STS URL에 서명을 생성한다 (위 디코딩 된 token 참조)
- Secret Key 자체는 URL에 포함되지 않으며 서명값(Signature)만 전송된다.
4) token 생성
- 서명된 STS URL을 Base64 인코딩해서 토큰으로 변환한다.
5) EKS API Server로 토큰 전송
- kubectl이 토큰을 Authorization Header에 담아 EKS API Server로 전송한다.
- 'kubectl get node -v=6'으로 실제 HTTP 요청에서 확인 가능함.
GET /api/v1/nodes
Host: E2F77ED0429BBB82740C7C2A9EBCF47E.gr7.ap-northeast-2.eks.amazonaws.com
Authorization: Bearer k8s-aws-v1.aHR0cHM6Ly9zdHMu...
6) EKS가 STS로 토큰 검증
- EKS API Server가 토큰을 디코딩해서 STS URL을 직접 호출한다
7) 권한 확인 후 응답
- EKS가 access entry를 조회해서 해당 IAM의 권한을 확인한다
- terraform 배포 당시 "enable_cluster_creator_admin_permissions = true"로 인해 admin 권한 확인
03. 시스템 파드 정보 확인
3-1. Pods 정보 확인
kubectl get pod -n kube-system -o wide
kubectl get pod -A

현재 Pods 정보들을 확인하였을 때 coredns, kube-proxy, cni 등의 Pods들만 존재한다.
즉, Data plane 관련된 파드들만 떠있고 control plane 파드들은 보이지 않는다.
(kube-apiserver, etcd, kube-controller-manager, shceduler가 안보인다.)
이유는 EKS의 경우 Control Plane은 AWS가 완전히 관리하기 때문에 data plane 노드에서는 보이지 않습니다 !
3-2. kube-system 네임스페이스에 모든 리소스 확인
kubectl get deploy,ds,pod,cm,secret,svc,ep,endpointslice,pdb,sa,role,rolebinding -n kube-system
리소스들을 차례대로 아래와 같이 살펴보겠습니다.

- deployment - coredns
- 2개의 Pod가 2개 노드에 분산 배치 (고가용성)
- Replica 수를 조절해서 DNS 성능 조절이 가능하다.
- DaemonSet - node, kube-proxy
- DaemonSet은 모든 노드에 1개씩 자동 배포된다.
- 노드가 추가되면 자동으로 Pod 수도 추가됨 (노드 2대니까 각각 2개씩 떠 있는 것 확인)
- aws-node는 Pod IP 할당하는 역할을 하며, kube-proxy는 iptables를 관리한다.

- ConfigMap - 설정 저장소
- amazon-vpc-cni : vpc-cni 설정
- aws-auth : IAM 과 k8s간 권한을 맵핑해주는 역할 (NodeGroup의 IAM Role 확인)
- coredns : DNS 설정
- kube-proxy : iptables를 관리하는 kube-proxy 설정
- kube-proxy-config : proxy 상세 설정 정보
- kube-root-ca.crt : EKS 클러스터 CA 인증서

- Service / Endpoint
- kube-dns : Pod들이 DNS 조회할 때 '10.100.0.10'으로 요청을 보냄
- kube-proxy가 iptables 규칙을 참조하여 kube-dns의 pods들로 트래픽 분산시킴.
- eks-extension-metrics-api : AWS가 관리하는 Control Plane 영역의 IP
- EKS가 매트릭을 수집하는 앤드포인트임.
- kube-dns : Pod들이 DNS 조회할 때 '10.100.0.10'으로 요청을 보냄
- PodDisruptionBudget
- Node drain, upgrade 시 동시에 중단 가능한 core dns pod 수를 1개로 제한.
- core dns는 2개 있으니 최소 1개는 항상 살아있도록 보장하는 설정.

- ServiceAccount (총 40개 이상..)
- k8s 기본 Controller용 SA : deployment-controller, replicaset-controller etc..
- 각 컨트롤러가 k8s API를 호출할 때 사용하는 계정이며, EKS에서는 AWS 관리 영역에서 사용됨
- EKS 전용 SA : aws-node, tagging-controller, aws-cloud-provider etc..
- vpc-cni pod, aws 리소스 태깅 및 클라우드 연동하는 계정이며, data plane에서 사용됨.
- k8s 기본 Controller용 SA : deployment-controller, replicaset-controller etc..

- role.rbac (EKS 전용 Role)
- eks:addon-manager : add-on 설치/관리
- eks:authenticator : IAM 인증 처리
- eks:coredns-autoscaler : core dns 자동 스케일링
- eks:fargate-manager : Fargate 노드 관리
- eks:network-policy-controller : 네트워크 정책 관리
- eks:node-manager : 노드 라이프사이클 관리
- eks:az-pooler : AZ 가용성 체크
- 등 모든 Role 전부 AWS Control Plane이 k8s API를 호출하기 위해 필요한 권한을 정의함. (EKS 전용)
3-3. 모든 파드의 컨테이너 이미지 정보 확인
kubectl get pods --all-namespaces -o jsonpath="{.items[*].spec.containers[*].image}" | tr -s '[[:space:]]' '\n' | sort | uniq -c

현재 가용중인 모든 파드들에 대한 컨테이너 이미지 정보를 확인해본 결과,
전부 같은 ECR 계정에서 이미지를 가져옵니다.
602401143452.dkr.ecr.ap-northeast-2.amazonaws.com → AWS가 관리하는 공식 EKS ECR 계정 ID
즉, Docker Hub나 외부 레지스트리가 아닌 AWS 공식 ECR에서 직접 Pull 한 이미지임.
모두 AWS 공식 ECR에서 가져온 것이므로 AWS가 버전 관리 및 보안 패치를 담당한다.
3-4. kube-proxy 상세 정보 확인
kubectl describe pod -n kube-system -l k8s-app=kube-proxy
kubectl get cm -n kube-system kube-proxy -o yaml
kubectl get cm -n kube-system kube-proxy-config -o yaml

- Priority Class
- 메모리 부족 시 절대 evict되지 않는 최고 우선 순위임.
- kube-proxy가 죽으면 노드 전체 네트워크가 마비되기 때문에.
- IP = Node IP
- kube-proxy는 노드 네트워크 스택을 직접 다루기 때문에 별도 Pod IP없이 노드 IP를 그대로 사용함.

- HostPath 볼륨 마운트 : 노드의 실제 파일시스템을 직접 마운트 (iptalbes 규칙을 노드 커널에 직접 쓰기 때문)
- /run/xtables.lock (rw) : iptables 잠금 파일
- /lib/modules (ro) : 커널 모듈
- /var/log (rw) : 로그 파일

- Tolerations : op=Exists → 모든 taint 허용
- 노드 상태가 나빠도 절대 쫓겨나지 않습니다.
- 노드가 not-ready 상태여도 kube-proxy는 계속 실행돼야 네트워크 복구가 가능하기 때문에.

- kube-proxy의 configmap 설정 (kubeconfig)
server: https://e2f77ed0429bbb82740c7c2a9ebcf47e.gr7.ap-northeast-2.eks.amazonaws.com
tokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
kube-proxy가 EKS API Server에 접근하는 방식이며, ServiceAccount 토큰으로 인증합니다.
(아까 STS 방식과 다르게 내부 ServiceAccount 토큰을 사용함)
kubectl (외부) → STS 토큰으로 인증
kube-proxy (내부) → ServiceAccount 토큰으로 인증

- kube-proxy-config (핵심 설정)
bindAddress: 0.0.0.0 → 모든 인터페이스에서 수신한다는 의미 (특정IP로 제한X)
contrack → Linux 커널의 연결 상태 추적 테이블을 의미하며,
Service 트래픽의 NAT 상태를 추적하는데 사용한다. (값이 너무 낮으면 대규모 트래픽 시 연결이 끊김)
- maxperCore : CPU 코어당 최대 연결 추적 수
- min : 최소 연결 추적 수
- tcpEstablishedTimeout : TCP 연결 유지 시간
- tcpCloseWaitTimeout : CLOSE_WAIT 타임아웃
iptables: → 동기화 주기를 의미
- syncPeriod : 30초마다 iptables 규칙 동기화
- minSyncPeriod : 변경 감지 시 즉시 동기화
- masqueradeAll : 전체 SNAT 비활성화
- masqueradeBit : SNAT 마킹 비트
mode: "iptables" → Service에서 Pod 라우팅을 Linux iptables 규칙으로 구현한다는 의미.
3-5. Core DNS 상세 정보 확인
kubectl describe pod -n kube-system -l k8s-app=kube-dns
kubectl get cm -n kube-system coredns -o yaml
kubectl get pdb -n kube-system coredns -o jsonpath='{.spec}' | jq

- Priority Class
- 메모리 부족 시 일반 Pod보다 먼저 보호되는 높은 우선순위 (kube-proxy 보다 낮음)
- core dns가 죽으면 클러스터 전체 DNS 조회가 불가능해져서 모든 서비스 간 통신이 마비되기 떄문
- Pod IP
- 노드 IP와 다른 별도의 Pod IP가 할당됨
- vpc-cni가 VPC 서브넷 대역에서 직접 IP를 할당한다. (DNS 요청을 받아야 하므로 자체 IP 필요함)

- VolumMount : kube-proxy와 다르게 hostPath 등 마운트 없음.
- Toleration : op=Exists가 아닌 옵션을 제외하고 모든 taint 허용.
- control-plane:NoSchedule : Control Plane 노드에도 배포 가능 (on-prem 환경 고려)
- not-ready / unreachable for 300s : 노드에 문제가 생겨도 5분간 대기 후 다른 노드로 이동

- Core Dns의 configmap 설정
.:53 → 모든 도메인에 대해 53번 포트로 수신
errors → DNS쿼리 처리 중 발생하는 에러를 로그로 출력
health {
lameduck 5s → Pod 종료 시 5초간 헬스체크를 실패 상태로 반환해서 트래픽을 먼저 차단한 후 종료.
}
→ http://:8080/health 엔드포인트 활성화
→ describe Pod에서 봤던 Liveness Probe가 여기로 요청을 보냄
ready
→ http://:8181/ready 엔트포인트 활성화
→ describe Pod에서 봤던 Readiness Probe가 여기로 요청을 보냄
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
}
→ 클러스터 내부 DNS 도메인 처리의 핵심 플러그인
→ cluster.local : k8s 서비스 도메인 처리
forware . /etc/resolv/conf → cluster.local로 끝나지 않는 외부 도메인 쿼리를 상위 DNS로 전달

- Core DNS의 PDB 설정
- 자발적 중단(Voluntary Disruption)시 동시에 중단 가능한 Pod 수를 제한하는 정책.
- 자발적 중단 예시 : node drain (upgrade, 점검 등) , deployment 롤링 업데이트 등.
- 단, 하드웨어 장애같은 비자발적 중단은 PDB로 막을 수 없다!
04. add-on 정보 확인
클러스터에 설치된 Addon 목록을 확인해보자.
aws eks list-addons --cluster-name myeks | jq
{
"addons": [
"coredns",
"kube-proxy",
"vpc-cni"
]
}
현재 위와 같이 coredns, kube-proxy, vpc-cni의 추가 기능을 사용하는 것 확인해보았습니다.
아래와 같이 자세하게 볼 수도 있습니다.
# coredns
aws eks describe-addon --cluster-name myeks --addon-name coredns | jq
# kube-proxy
aws eks describe-addon --cluster-name myeks --addon-name kube-proxy | jq
# vpc-cni
aws eks describe-addon --cluster-name myeks --addon-name vpc-cni | jq
vpc-cni로 한번 살펴볼까요?

- 현재 ACTIVE 상태이고, 문제 발생 시 health.issues에 에러가 기록됩니다.
- 1.21.1 버전을 사용중이고, eksbuild.5는 AWS가 EKS용으로 빌드한 패치 번호를 의미합니다.
- eks.tf에서 'most_recent = true'로 설정했기 때문에 k8s 1.32와 호환되는 최신 버전이 자동 선택됨.
- kube-system의 네임스페이스에서 동작하는 것까지 확인이 가능합니다!
추가적으로 AWS Console → 클러스터 → 내 클러스터 선택 → 추가기능 에서도 확인이 가능합니다.
