K8S Scheduler

Scheduler가 노드를 선택하는 방식
필터링
- 필터 플러그인에 파드와 노드를 입력해 조건을 만족하는지 검사
- 예) NodeResourcesFit 플러그인에서 파드의 리소스 요청을 충족하는지 확인
- 모든 필터 플러그인을 통과한 노드 목록 생성
- 만약 배포에 적합한 노드 목록이 비어 있다면 스케줄링 불가
- 배포할 수 있는 노드 수가 1인 경우에는 해당 노드에 바로 할당
필터링 - 클러스터 크기가 큰 경우(1000대 이상)
- 모든 노드를 검색하지 않도록 검색할 노드의 최대 크기 제한
- 1000 x PercentageOfNodesToScores (50%)
- 500 노드를 배포 가능 노드로 찾으면 검색 중지
- 최소 100 노드 혹은 전체 노드의 최소 5% 비율은 무조건 사용
스코어링
- 필터링을 통한 모든 노드를 대상으로 점수 계산 및 순위 정렬
- 플러그인에 노드 정보와 파드 정보를 입력해 점수 계산
- 정규화 과정을 거친 점수와 플러그인 Weight 값을 고려해 노드의 최종 점수 계산
- 가장 높은 순위를 가진 노드를 파드에 할당
파드 스케줄링 제어 목적
- 파드를 특정한 조건을 가진 노드에서만 실행되도록 제한
- 파드를 여러 조건에 맞는 노드 중 하나에서 실행되도록 제한
- 파드를 특별한 장치(GPU 등)를 가진 노드에 배포
- 서로 다른 두 파드를 동일한 노드 혹은 동일한 가용 영역에 배포
- 명시적인 파드 스케줄링 대기
노드 레이블
- 노드 특징, 리소스, 성능 등 다양한 정보를 레이블로 표현
- 쿠버네티스 노드의 기본 레이블과 권장 레이블
파드 스케줄링 대기 - 1
- 파드 생성 후 스케줄링이 일어나는 시점을 제어할 때 사용 가능
- spec.schedulingGates 필드에 값을 추가 또는 삭제해 제어
- schedulingGates 필드는 파드를 생성할 때만 초기화 가능
- 생성 후에는 제거만 가능하고 추가는 허용되지 않음
- 쿠버네티스 v1.30 버전에서 Stable 단계
- schedulingGates 필드에 필요한 리소스 등을 추가해 표시
- 스케줄링 준비가 됐다면 schedulingGates 필드 삭제
- schedulingGates 필드를 삭제하면 스케줄러는 스케줄링 작업 시작
- schedulingGates 필드로 인해 schedulingGated 상태가 됨
- schedulingGates 필드를 삭제해 스케줄링 재개
- schedulingGated 상태일 때 스케줄링 조건은 강화만 가능
- .spec.nodeSelector 필드가 이미 있다면 조건 추가 가능
- .spec.affinity.nodeAffinity 필드가 nil이면 설정 변경 가능
- 이미 존재하는 required 조건은 추가만 허용
- preferred 조건에 대해서는 제한 없음
직접 지정
- 파드를 생성할 때 spec.nodeName 필드에 원하는 노드 지정
- 지정된 노드에 파드 배포 시도
- 주의할 점
- 없는 노드인 경우 배포 실패
- 리소스가 부족할 경우 배포 실패
- 특수한 상황이 아니면 권장하지 않음
노드 레이블 기반 배포
- 파드 spec.nodeSelector 필드에 노드 레이블 지정
- 지정한 노드 레이블이 모두 있는 노드만 선택
- 간단하게 노드 선택에 제약을 줄 수 있는 조건 설정
선호도 기반 배포: Affinity & Anti-Affinity
- NodeSelector 방식보다 다양한 표현 방식 지원
- In, NotIn, Exists, DoesNotExist, Gt, Lt 등의 연산자로 레이블 비교 가능
- NodeAffinity 기능으로 배포할 노드 조건을 정교하게 제어
- 모든 조건을 만족하는 노드가 없을 경우에도 스케줄링 가능하도록 조건 추가
- PodAffinity 기능으로 파드 레이블을 이용해 배포 조건 제어
- 서로 다른 파드를 동일한 노드 혹은 영역에 배포하도록 조건 설정
- 파드를 서로 다른 노드 혹은 영역에 나눠서 배포하도록 조건 설정
선호도 기반 배포: 오퍼레이터
- NodeAffinity & PodAffinity 모두에서 사용할 수 있는 연산자
오퍼레이터 | 작동 방식 |
In | 지정한 레이블 값이 Values 문자열 집합 안에 포함되는지 검사 |
NotIn | 지정한 레이블 값이 Values 문자열 집합 안에 없는지 검사 |
Exists | 지정한 레이블 키가 대상 노드 혹은 파드에 존재하는지 검사 |
DoesNotExist | 지정한 레이블 키가 대상 노드 혹은 파드에 존재하지 않는지 검사 |
- NodeAffinity 필드를 정의할 때만 사용할 수 있는 연산자
오퍼레이터 | 작동 방식 |
Gt | 정의한 필드 값을 정수로 간주해 대상 레이블 값보다 작은지 검사 |
Lt | 정의한 필드 값을 정수로 간주해 대상 레이블 값보다 큰지 검사 |
선호도 기반 배포: NodeAffinity
- NodeSelector 기능과 비슷하게 노드 레이블 조건 설정
- 두 가지 형식을 이용해 스케줄링 조건을 유연하게 제어
- requiredDuringSchedulingIgnoredDuringExecution
- 파드를 스케줄링할 때 반드시 필요한 조건 정의
- preferredDuringSchedulingIgnoredDuringExecution
- 조건과 가중치를 이요해 좀 더 선호하는 노드를 선택할 수 있도록 제어
선호도 기반 배포: Pod(Anti)Affinity
- 실행 중인 파드 레이블을 기반으로 배포할 노드 제한 가능
- 파드 역할/목적에 따라 분산ㅅ히키거나 같이 실행되도록 제어
- 파드 가용성 혹은 성능을 위해 서로 다른 노드에서 실행되도록 설정
- 상호 통신이 많은 파드를 같은 노드나 동일 영역에 배포되도록 설정
- 대규모 클러스터에서는 스케줄링 속도가 느려질 수 있어 주의
- 스케줄링 대상 노드에는 topologyKey 레이블 필수
- DB 파드와 동일한 존에 웹 파드를 배포하는 PodAffinity 설정
선호도 기반 배포: matchLabelKeys
- Pod(Anti)Affinity 조건에 일치할 파드 레이블 키 추가 지정
- 일반적으로 pod-template-hash 레이블 사용
- 롤링 업데이트 진행 중 이전 리비전 파드로 인한 혼란 방지
- 쿠버네티스 v1.31 버전에서 Beta 단계
파드 분산 제어: Topology Spread Constraints
- 클러스터에서 파드를 분산 배포하는 방식 제어
- topologyKey 레이블 값을 이용해 노드 그룹 생성
- 각 노드 그룹에 균등한 파드 배포 (maxSkew 설정)
- PodAffinity 기능보다 좀 더 간편하게 분산 배포할 때 사용
노드에서 파드 제외: Taints & Tolerations
- 노드에서 파드를 제외하는 형태로 제어하고 싶을 때 사용
- Taints 붙은 노드에 파드를 배포하기 위해 tolerations 필요
- 노드 실패인 경우 다른 노드로 파드를 옮기도록 제어
- Taint 추가 시 Noexecute 효과 적용
- 일부 노드를 독점적으로 사용하도록 Taint & Toleration 적용
- NoSchedule 효과 Taint 추가는 배포된 파드에 영향 없음
- .spec.nodeName 필드를 이용하는 경우 Taint 통과 가능
- NoSchedule 효과를 가진 Taint 조건은 통과해 파드 배포 가능
- NoExecute 효과를 설정한 Taint 조건이 있다면 바로 제외되어 파드가 삭제됨
리소스 단편화
- 클러스터 전체적인 리소스 여유 공간은 충분한 상태
- 하지만 파드에서 요청한 일부 리소스 붖고으로 인해 실행 불가
- 리소스 단편화로 인해 필요한 리소스를 한 노드에서 모두 할당할 수 없는 상황
- CPU, Memory 리소스는 상대적으로 발생할 확률이 낮음
- GPU 리소스는 상대적으로 자주 발생하는 문제
- 일반적으로 서버 한 대당 4, 8, 10 정도의 GPU 장치를 장착해 사용
- 스케줄러는 기본적으로 리소스가 좀 더 남아있는 노드 선택
- 파드에서 GPU 리소스를 요청한 경우 고르게 배포될 수 있도록 노드 선택
- 항상 1 GPU 리소스만 요청한다면 거의 발생하지 않는 문제
- LLM 등의 파드에서 멀티 GPU 리소스를 요청할 경우 발생 가능
- 노드 별로 한 두개 정도의 GPU 리소스만 남아있는 상황
- 단일 노드에서 요청한 GPU 리소스를 모두 확보하기 어려운 상황 발생
정리
- 스케줄러는 필터링과 스코어링 과정을 거쳐 노드 선택
- 실행할 노드를 제어하기 위해 다양한 파드 설정 사용
- NodeSelector
- Affinity
- NodeAffinity
- PodAffinity / PodAntiAffinity
- Topology Spread Constraints
- Taints & Tolerations
- GPU 리소스는 상대적으로 매우 적어 단편화 발생 가능성 높음
- 노드 리소스 사용량을 높이는 방향으로 스케줄러 설정 가능
- NodeResourcesFit 플러그인의 MostAllocated 전략
- 애플리케이션 성격에 따라 스케줄러를 선택해 배포 가능
- 스케줄링 설정을 변경할 때 검증하는 과정을 거치는 것이 필수
Fargate
소개
EKS(컨트롤 플레인) + Fargate(데이터 플레인)의 완전한 서버리스화(=AWS 관리형)
- Cluster Autoscaler 불필요, VM 수준의 격리 가능(VM isolation at Pod Level)

파게이트 프로파일(파드가 사용할 서브넷, 네임스페이스, 레이블 조건)을 생성하여 지정한 파드가 파게이트에서 동작하게 함

EKS 는 스케줄러가 특정 조건을 기준으로 어느 노드에 파드를 동작시킬지 결정, 혹은 특정 설정으로 특정 노드에 파드가 동작하게 가능함

Data Plane

Firecracker

- Firecracker를 가상화되지 않은 환경에서 1초도 되지 않는 시간 안에 경량 microVM(마이크로 가상 머신)을 시작할 수 있고, 컨테이너를 통해 제공하는 리소스 효율성과 기존 VM에서 제공하는 워크로드 격리 및 보안의 혜택을 그대로 활용할 수 있음.
- 보안 - 여러 수준의 격리와 보호를 사용하며 공격 노출 영역을 최소화
- 고성능 - 125밀리초 안에 microVM을 시작할 수 있고 단기간 또는 일시적인 워크로드를 비롯한 여러 유형의 워크로드에 적합
- 검증된 실적 - AWS Lambda 및 AWS Fargatge를 비롯한 사용량이 많은 여러 AWS 서비스가 Firecracker를 사용
- 낮은 오버헤드 - microVM당 약 5MiB의 메모리를 사용. 동일한 인스턴스에서 다양한 vCPU 및 메모리 구성 사양을 갖춘 안전한 수천개의 VM을 실행 할 수 있음.
- Firecracker는 미니멀리즘에 기반하여 제작. crosvm에서 시작하여 오버헤드를 줄이고 안전한 멀티 테넌시를 활용하도록 최소 디바이스 모델을 설정. Firecracker는 스레드 보안을 보장하고 보안 취약성을 야기할 수 있는 여러 유형의 버퍼 오버런 오류를 방지하는 최신 프로그래밍 언어인 Rust로 작성
- 단순한 게스트 모델 - Firecracker 게스트는 공격 영역을 최소화하기 위해 가상화된 단순한 디바이스 모델로 제시 됨(예: 네트워크 디바이스, 블록 I/O 디바이스, PIT(Programmable Interval Timer), KVM 클럭, 직렬 콘솔, 부분 키보드(VM을 재설정할 수 있을 정도의 기능)).
- 잠금(Jail) 처리 - Firecracker 프로세스는 cgroups 및 seccomp BPF를 사용하여 잠기며 매우 제한된 소량의 시스템 호출 목록에 액세스 함.
- 정적 연결 - Firecracker 프로세스는 정적으로 연결되며, 잠금자(jailer)에서 시작하여 가능한 안전하고 클린한 상태의 호스트 환경을 보장.
AWS EKS Fargate 아키텍처 (추정 포함)

- 사용자에게 보이지 않지만, Fargate Scheduler(Controller 추정)가 EKS Control Plane 에서 동작.
- fargate Scheduler(controller) 에 필요한 IAM Role(Policy)는 AWS에서 알아서 관리 하는 것으로 보임. 설치 시에 별도 설정 없음.
- Fargate 에 의해서 배포된 파드(노드 당 1개 파드)에 ENI는 사용자의 VPC 영역 내에 속하여, Fargate-Owned ENI 로 추정.
- 파드(노드)에 필요 IAM Role(Policy)는 Fargate 설치 시에 설정 필요함. 필요 시 파드에 IRSA 추가 설정 가능.
- 파드가 외부 통신 시에는 → NATGW(공인 IP로 SNAT) ⇒ IGW(외부 인터넷)
- 만약 파드가 퍼블릭 서브넷이 있을 경우 공인IP 비용 부과 및 외부 침입 시도가 빈번할듯..
- 외부에서 파드 내부로 인입 요청 시에는 → ALB/NLB ⇒ Fargate-Owned ENI 에 연결된 Fargate 파드(노드)로 전달

- firecracker-containerd 를 통하여 MicroVM(Application 컨테이너)를 배포.
- VMM을 통해서 MicroVM을 배포하고, FC Snapshotter 를 통해서 Application Container 의 이미지를 구현.
- MicroVM 마다 ‘Kubelet, Kube-proxy, Containerd’ 가 동작하여, 256 RAM 반드시 필요.
- 사용자에의 VPC에 보이는 ENI는 Application Containter 와 직접 매핑되어 있는 것으로 추정.
제약사항 및 고려사항
- 데몬셋은 Fargate에서 지원되지 않음. 애플리케이션에 데몬이 필요한 경우 해당 데몬을 파드에서 사이드카 컨테이너로 실행하도록 재구성.
- Fargate에서는 특권 컨테이너(Privileged containers)가 지원되지 않음.
- Fargate에서 실행되는 파드는 파드 매니페스트에서 HostPort 또는 HostNetwork를 지정할 수 없음.
- 현재 Fargate에서는 GPU를 사용할 수 없음.
- Can run workloads that require Arm processors 미지원.
- Can SSH into node 미지원.
- Fargate에서 실행되는 파드는 AWS 서비스에 대한 NAT 게이트웨이 액세스 권한이 있는 private 서브넷에서만 지원됨.
- 파드에는 Amazon EC2 인스턴스 메타데이터 서비스(IMDS)를 사용할 수 없음.
- 대체 CNI 플러그인을 사용할 수 없음.
- EFS 동적 영구 볼륨 프로비저닝을 사용할 수 없음.
- Fargate Spot을 지원하지 않음.
- EBS 볼륨을 Fargate 파드에 마운트할 수 없음
- Fargate does not currently support Kubernetes topologySpreadConstraints.
- Can run containers on Amazon EC2 dedicated hosts 미지원.
- Can run AWS Bottlerocket 미지원.
- Fargate Pods run with guaranteed priority, so the requested CPU and memory must be equal to the limit for all of the containers.
- Fargate는 필요한 Kubernetes 구성 요소(kubelet, kube-proxy, and containerd에 대해 각 파드의 메모리 예약에 256MB를 추가.
- 프로비저닝되면 Fargate에서 실행되는 각 파드는 기본적으로 20 GiB의 임시 저장소를 받게 됨. 임시 저장소의 총 양을 최대 175 GiB까지 늘릴 수 있음.
- Fargate의 Amazon EKS는 Fluent Bit 기반의 내장 로그 라우터를 제공. 즉, Fluent Bit 컨테이너를 사이드카로 명시적으로 실행하지 않고 Amazon에서 실행함..
실습 환경 배포
Terraform 설치
wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt install terraform
# 테라폼 버전 정보 확인
terraform version
Terraform으로 배포
#
git clone https://github.com/aws-ia/terraform-aws-eks-blueprints
tree terraform-aws-eks-blueprints/patterns
cd terraform-aws-eks-blueprints/patterns/fargate-serverless
테라폼 초기화
# init 초기화
terraform init
tree .terraform
cat .terraform/modules/modules.json | jq
tree .terraform/providers/registry.terraform.io/hashicorp -L 2
# plan
terraform plan
EKS 배포
# 배포 : EKS, Add-ons, fargate profile - 13분 소요
terraform apply -auto-approve
# 배포 완료 후 확인
terraform state list
module.eks.data.aws_caller_identity.current
...
terraform output
...
# EKS 자격증명
$(terraform output -raw configure_kubectl) # aws eks --region ap-northeast-2 update-kubeconfig --name fargate-serverless
cat ~/.kube/config
# kubectl context 변경
kubectl ctx
kubectl config rename-context "arn:aws:eks:ap-northeast-2:$(aws sts get-caller-identity --query 'Account' --output text):cluster/fargate-serverless" "fargate-lab"
# k8s 노드, 파드 정보 확인
kubectl ns default
kubectl cluster-info
kubectl get node
kubectl get pod -A
# 상세 정보 확인
terraform show
기본 정보 확인
# k8s api service 확인 : ENDPOINTS 의 IP는 EKS Owned-ENI 2개
kubectl get svc,ep

# node 확인 : 노드(Micro VM) 4대
kubectl get csr
kubectl get node -owide
kubectl describe node | grep eks.amazonaws.com/compute-type


# 파드 확인 : 파드의 IP와 노드의 IP가 같다!
kubectl get pdb -n kube-system
kubectl get pod -A -owide

# aws-load-balancer-webhook-service , eks-extension-metrics-api?
kubectl get svc,ep -n kube-system
# eks-extension-metrics-api?
kubectl get apiservices.apiregistration.k8s.io | grep eks
kubectl get --raw "/apis/metrics.eks.amazonaws.com" | jq
kubectl get --raw "/apis/metrics.eks.amazonaws.com/v1" | jq


# configmap 확인
kubectl get cm -n kube-system
# aws-auth 보다 우선해서 IAM access entry 가 있음을 참고.
# 기본 관리노드 보다 system:node-proxier 그룹이 추가되어 있음.
# fargate profile 이 2개인데, 그 profile 갯수만큼 있음.
kubectl get cm -n kube-system aws-auth -o yaml
#
kubectl rbac-tool lookup system:node-proxier
kubectl rolesum -k Group system:node-proxier
#
kubectl get cm -n kube-system amazon-vpc-cni -o yaml





# coredns 설정 내용
kubectl get cm -n kube-system coredns -o yaml
# 인증서 작성되어 있음 : client-ca-file , requestheader-client-ca-file
kubectl get cm -n kube-system extension-apiserver-authentication -o yaml
#
kubectl get cm -n kube-system kube-proxy -o yaml
kubectl get cm -n kube-system kube-proxy-config -o yaml



coredns 파드 상세 정보 확인
# coredns 파드 상세 정보 확인
kubectl get pod -n kube-system -l k8s-app=kube-dns -o yaml
apiVersion: v1
items:
- apiVersion: v1
kind: Pod
metadata:
annotations:
CapacityProvisioned: 0.25vCPU 0.5GB
Logging: LoggingEnabled
eks.amazonaws.com/compute-type: Fargate
creationTimestamp: "2025-03-22T09:22:52Z"
generateName: coredns-64696d8b7f-
labels:
eks.amazonaws.com/component: coredns
eks.amazonaws.com/fargate-profile: kube-system
k8s-app: kube-dns
pod-template-hash: 64696d8b7f
name: coredns-64696d8b7f-878qc
namespace: kube-system
ownerReferences:
- apiVersion: apps/v1
blockOwnerDeletion: true
controller: true
kind: ReplicaSet
name: coredns-64696d8b7f
uid: e250f48f-b9c6-4cbe-938b-1e25f72d96f3
resourceVersion: "1833"
uid: fb8d37e4-65f9-4e57-a34a-7940a88e5aac
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/os
operator: In
values:
- linux
- key: kubernetes.io/arch
operator: In
values:
- amd64
- arm64
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- podAffinityTerm:
labelSelector:
matchExpressions:
- key: k8s-app
operator: In
values:
- kube-dns
topologyKey: kubernetes.io/hostname
weight: 100
containers:
- args:
- -conf
- /etc/coredns/Corefile
image: 602401143452.dkr.ecr.ap-northeast-2.amazonaws.com/eks/coredns:v1.11.4-eksbuild.2
imagePullPolicy: IfNotPresent
livenessProbe:
failureThreshold: 5
httpGet:
path: /health
port: 8080
scheme: HTTP
initialDelaySeconds: 60
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 5
name: coredns
ports:
- containerPort: 53
name: dns
protocol: UDP
- containerPort: 53
name: dns-tcp
protocol: TCP
- containerPort: 9153
name: metrics
protocol: TCP
readinessProbe:
failureThreshold: 3
httpGet:
path: /ready
port: 8181
scheme: HTTP
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
resources:
limits:
cpu: 250m
memory: 256M
requests:
cpu: 250m
memory: 256M
securityContext:
allowPrivilegeEscalation: false
capabilities:
add:
- NET_BIND_SERVICE
drop:
- ALL
readOnlyRootFilesystem: true
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /etc/coredns
name: config-volume
readOnly: true
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
name: kube-api-access-ktjxl
readOnly: true
dnsPolicy: Default
enableServiceLinks: true
nodeName: fargate-ip-10-10-22-163.ap-northeast-2.compute.internal
preemptionPolicy: PreemptLowerPriority
priority: 2000001000
priorityClassName: system-node-critical
restartPolicy: Always
schedulerName: fargate-scheduler
securityContext: {}
serviceAccount: coredns
serviceAccountName: coredns
terminationGracePeriodSeconds: 30
tolerations:
- effect: NoSchedule
key: node-role.kubernetes.io/control-plane
- key: CriticalAddonsOnly
operator: Exists
- effect: NoExecute
key: node.kubernetes.io/not-ready
operator: Exists
tolerationSeconds: 300
- effect: NoExecute
key: node.kubernetes.io/unreachable
operator: Exists
tolerationSeconds: 300
topologySpreadConstraints:
- labelSelector:
matchLabels:
k8s-app: kube-dns
maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: ScheduleAnyway
volumes:
- configMap:
defaultMode: 420
items:
- key: Corefile
path: Corefile
name: coredns
name: config-volume
- name: kube-api-access-ktjxl
projected:
defaultMode: 420
sources:
- serviceAccountToken:
expirationSeconds: 3607
path: token
- configMap:
items:
- key: ca.crt
path: ca.crt
name: kube-root-ca.crt
- downwardAPI:
items:
- fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
path: namespace
status:
conditions:
- lastProbeTime: null
lastTransitionTime: "2025-03-22T09:23:33Z"
status: "True"
type: PodReadyToStartContainers
- lastProbeTime: null
lastTransitionTime: "2025-03-22T09:23:30Z"
status: "True"
type: Initialized
- lastProbeTime: null
lastTransitionTime: "2025-03-22T09:23:38Z"
status: "True"
type: Ready
- lastProbeTime: null
lastTransitionTime: "2025-03-22T09:23:38Z"
status: "True"
type: ContainersReady
- lastProbeTime: null
lastTransitionTime: "2025-03-22T09:23:30Z"
status: "True"
type: PodScheduled
containerStatuses:
- containerID: containerd://7a7178f07b9acdcb78a937db080ddad8bf46f9c8fc4d0d995166f6285433d1c9
image: 602401143452.dkr.ecr.ap-northeast-2.amazonaws.com/eks/coredns:v1.11.4-eksbuild.2
imageID: 602401143452.dkr.ecr.ap-northeast-2.amazonaws.com/eks/coredns@sha256:f184e31683ba315cb284bb6b429d416ecee71126ee1d9035af8d15462064e0b8
lastState: {}
name: coredns
ready: true
restartCount: 0
started: true
state:
running:
startedAt: "2025-03-22T09:23:33Z"
hostIP: 10.10.22.163
hostIPs:
- ip: 10.10.22.163
phase: Running
podIP: 10.10.22.163
podIPs:
- ip: 10.10.22.163
qosClass: Guaranteed
startTime: "2025-03-22T09:23:30Z"
- apiVersion: v1
kind: Pod
metadata:
annotations:
CapacityProvisioned: 0.25vCPU 0.5GB
Logging: LoggingEnabled
eks.amazonaws.com/compute-type: Fargate
creationTimestamp: "2025-03-22T09:22:52Z"
generateName: coredns-64696d8b7f-
labels:
eks.amazonaws.com/component: coredns
eks.amazonaws.com/fargate-profile: kube-system
k8s-app: kube-dns
pod-template-hash: 64696d8b7f
name: coredns-64696d8b7f-r7v4w
namespace: kube-system
ownerReferences:
- apiVersion: apps/v1
blockOwnerDeletion: true
controller: true
kind: ReplicaSet
name: coredns-64696d8b7f
uid: e250f48f-b9c6-4cbe-938b-1e25f72d96f3
resourceVersion: "1874"
uid: ede99777-9a0b-4ba8-9b71-eaafc476f196
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/os
operator: In
values:
- linux
- key: kubernetes.io/arch
operator: In
values:
- amd64
- arm64
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- podAffinityTerm:
labelSelector:
matchExpressions:
- key: k8s-app
operator: In
values:
- kube-dns
topologyKey: kubernetes.io/hostname
weight: 100
containers:
- args:
- -conf
- /etc/coredns/Corefile
image: 602401143452.dkr.ecr.ap-northeast-2.amazonaws.com/eks/coredns:v1.11.4-eksbuild.2
imagePullPolicy: IfNotPresent
livenessProbe:
failureThreshold: 5
httpGet:
path: /health
port: 8080
scheme: HTTP
initialDelaySeconds: 60
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 5
name: coredns
ports:
- containerPort: 53
name: dns
protocol: UDP
- containerPort: 53
name: dns-tcp
protocol: TCP
- containerPort: 9153
name: metrics
protocol: TCP
readinessProbe:
failureThreshold: 3
httpGet:
path: /ready
port: 8181
scheme: HTTP
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
resources:
limits:
cpu: 250m
memory: 256M
requests:
cpu: 250m
memory: 256M
securityContext:
allowPrivilegeEscalation: false
capabilities:
add:
- NET_BIND_SERVICE
drop:
- ALL
readOnlyRootFilesystem: true
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /etc/coredns
name: config-volume
readOnly: true
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
name: kube-api-access-c2vw7
readOnly: true
dnsPolicy: Default
enableServiceLinks: true
nodeName: fargate-ip-10-10-5-239.ap-northeast-2.compute.internal
preemptionPolicy: PreemptLowerPriority
priority: 2000001000
priorityClassName: system-node-critical
restartPolicy: Always
schedulerName: fargate-scheduler
securityContext: {}
serviceAccount: coredns
serviceAccountName: coredns
terminationGracePeriodSeconds: 30
tolerations:
- effect: NoSchedule
key: node-role.kubernetes.io/control-plane
- key: CriticalAddonsOnly
operator: Exists
- effect: NoExecute
key: node.kubernetes.io/not-ready
operator: Exists
tolerationSeconds: 300
- effect: NoExecute
key: node.kubernetes.io/unreachable
operator: Exists
tolerationSeconds: 300
topologySpreadConstraints:
- labelSelector:
matchLabels:
k8s-app: kube-dns
maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: ScheduleAnyway
volumes:
- configMap:
defaultMode: 420
items:
- key: Corefile
path: Corefile
name: coredns
name: config-volume
- name: kube-api-access-c2vw7
projected:
defaultMode: 420
sources:
- serviceAccountToken:
expirationSeconds: 3607
path: token
- configMap:
items:
- key: ca.crt
path: ca.crt
name: kube-root-ca.crt
- downwardAPI:
items:
- fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
path: namespace
status:
conditions:
- lastProbeTime: null
lastTransitionTime: "2025-03-22T09:23:41Z"
status: "True"
type: PodReadyToStartContainers
- lastProbeTime: null
lastTransitionTime: "2025-03-22T09:23:37Z"
status: "True"
type: Initialized
- lastProbeTime: null
lastTransitionTime: "2025-03-22T09:23:42Z"
status: "True"
type: Ready
- lastProbeTime: null
lastTransitionTime: "2025-03-22T09:23:42Z"
status: "True"
type: ContainersReady
- lastProbeTime: null
lastTransitionTime: "2025-03-22T09:23:37Z"
status: "True"
type: PodScheduled
containerStatuses:
- containerID: containerd://e68987580130c44cf91e48ad8e65ee187778ed8065eb09716b2d82a27eb4ee2d
image: 602401143452.dkr.ecr.ap-northeast-2.amazonaws.com/eks/coredns:v1.11.4-eksbuild.2
imageID: 602401143452.dkr.ecr.ap-northeast-2.amazonaws.com/eks/coredns@sha256:f184e31683ba315cb284bb6b429d416ecee71126ee1d9035af8d15462064e0b8
lastState: {}
name: coredns
ready: true
restartCount: 0
started: true
state:
running:
startedAt: "2025-03-22T09:23:40Z"
hostIP: 10.10.5.239
hostIPs:
- ip: 10.10.5.239
phase: Running
podIP: 10.10.5.239
podIPs:
- ip: 10.10.5.239
qosClass: Guaranteed
startTime: "2025-03-22T09:23:37Z"
kind: List
metadata:
resourceVersion: ""
AWS Console 확인
EC2 확인 불가

EBS 볼륨 확인 불가

ENI 확인 가능: 소유자는 내 계정이지만 인스턴스 소유자는 AWS일 것으로 추정

라우팅 테이블 확인 가능

서브넷 확인 가능

fargate에 kube-ops-view 설치
# helm 배포
helm repo add geek-cookbook https://geek-cookbook.github.io/charts/
helm install kube-ops-view geek-cookbook/kube-ops-view --version 1.2.2 --set env.TZ="Asia/Seoul" --namespace kube-system
# 포트 포워딩
kubectl port-forward deployment/kube-ops-view -n kube-system 8080:8080 &
# 접속 주소 확인 : 각각 1배, 1.5배, 3배 크기
echo -e "KUBE-OPS-VIEW URL = http://localhost:8080"
echo -e "KUBE-OPS-VIEW URL = http://localhost:8080/#scale=1.5"
echo -e "KUBE-OPS-VIEW URL = http://localhost:8080/#scale=3"
kube-ops-view 파드 정보 확인
# node 확인 : 노드(Micro VM)
kubectl get csr
kubectl get node -owide
kubectl describe node | grep eks.amazonaws.com/compute-type
# kube-ops-view 디플로이먼트/파드 상세 정보 확인
kubectl get pod -n kube-system
kubectl get pod -n kube-system -o jsonpath='{.items[0].metadata.annotations.CapacityProvisioned}'
kubectl get pod -n kube-system -l app.kubernetes.io/instance=kube-ops-view -o jsonpath='{.items[0].metadata.annotations.CapacityProvisioned}'
0.25vCPU 0.5GB
# 디플로이먼트 상세 정보
kubectl get deploy -n kube-system kube-ops-view -o yaml
...
template:
...
spec:
automountServiceAccountToken: true
containers:
- env:
- name: TZ
value: Asia/Seoul
image: hjacobs/kube-ops-view:20.4.0
imagePullPolicy: IfNotPresent
livenessProbe:
failureThreshold: 3
periodSeconds: 10
successThreshold: 1
tcpSocket:
port: 8080
timeoutSeconds: 1
name: kube-ops-view
ports:
- containerPort: 8080
name: http
protocol: TCP
readinessProbe:
failureThreshold: 3
periodSeconds: 10
successThreshold: 1
tcpSocket:
port: 8080
timeoutSeconds: 1
resources: {}
securityContext:
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1000
startupProbe:
failureThreshold: 30
periodSeconds: 5
successThreshold: 1
tcpSocket:
port: 8080
timeoutSeconds: 1
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
enableServiceLinks: true
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
serviceAccount: kube-ops-view
serviceAccountName: kube-ops-view
terminationGracePeriodSeconds: 30
...
# 파드 상세 정보 : admission control 이 동작했음을 알 수 있음
kubectl get pod -n kube-system -l app.kubernetes.io/instance=kube-ops-view -o yaml
...
metadata:
annotations:
CapacityProvisioned: 0.25vCPU 0.5GB
Logging: LoggingEnabled
...
resources: {}
...
dnsPolicy: ClusterFirst
enableServiceLinks: true
nodeName: fargate-ip-10-10-13-36.ap-northeast-2.compute.internal
preemptionPolicy: PreemptLowerPriority
priority: 2000001000
priorityClassName: system-node-critical
restartPolicy: Always
schedulerName: fargate-scheduler
securityContext: {}
serviceAccount: kube-ops-view
serviceAccountName: kube-ops-view
terminationGracePeriodSeconds: 30
tolerations:
- effect: NoExecute
key: node.kubernetes.io/not-ready
operator: Exists
tolerationSeconds: 300
- effect: NoExecute
key: node.kubernetes.io/unreachable
operator: Exists
tolerationSeconds: 300
...
qosClass: BestEffort
#
kubectl describe pod -n kube-system -l app.kubernetes.io/instance=kube-ops-view | grep Events: -A10
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal LoggingEnabled 22m fargate-scheduler Successfully enabled logging for pod
Normal Scheduled 21m fargate-scheduler Successfully assigned kube-system/kube-ops-view-796947d6dc-vrnjc to fargate-ip-10-10-13-36.ap-northeast-2.compute.internal
...


# 노드 상세 정보
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
deployment.kubernetes.io/revision: "1"
meta.helm.sh/release-name: kube-ops-view
meta.helm.sh/release-namespace: kube-system
creationTimestamp: "2025-03-22T09:32:15Z"
generation: 1
labels:
app.kubernetes.io/instance: kube-ops-view
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/name: kube-ops-view
app.kubernetes.io/version: 20.4.0
helm.sh/chart: kube-ops-view-1.2.2
name: kube-ops-view
namespace: kube-system
resourceVersion: "4486"
uid: 9680b8db-bf01-4ae3-92d4-3fedf9d281b6
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 3
selector:
matchLabels:
app.kubernetes.io/instance: kube-ops-view
app.kubernetes.io/name: kube-ops-view
strategy:
type: Recreate
template:
metadata:
creationTimestamp: null
labels:
app.kubernetes.io/instance: kube-ops-view
app.kubernetes.io/name: kube-ops-view
spec:
automountServiceAccountToken: true
containers:
- env:
- name: TZ
value: Asia/Seoul
image: hjacobs/kube-ops-view:20.4.0
imagePullPolicy: IfNotPresent
livenessProbe:
failureThreshold: 3
periodSeconds: 10
successThreshold: 1
tcpSocket:
port: 8080
timeoutSeconds: 1
name: kube-ops-view
ports:
- containerPort: 8080
name: http
protocol: TCP
readinessProbe:
failureThreshold: 3
periodSeconds: 10
successThreshold: 1
tcpSocket:
port: 8080
timeoutSeconds: 1
resources: {}
securityContext:
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1000
startupProbe:
failureThreshold: 30
periodSeconds: 5
successThreshold: 1
tcpSocket:
port: 8080
timeoutSeconds: 1
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
enableServiceLinks: true
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
serviceAccount: kube-ops-view
serviceAccountName: kube-ops-view
terminationGracePeriodSeconds: 30
status:
availableReplicas: 1
conditions:
- lastTransitionTime: "2025-03-22T09:33:10Z"
lastUpdateTime: "2025-03-22T09:33:10Z"
message: Deployment has minimum availability.
reason: MinimumReplicasAvailable
status: "True"
type: Available
- lastTransitionTime: "2025-03-22T09:32:15Z"
lastUpdateTime: "2025-03-22T09:33:10Z"
message: ReplicaSet "kube-ops-view-796947d6dc" has successfully progressed.
reason: NewReplicaSetAvailable
status: "True"
type: Progressing
observedGeneration: 1
readyReplicas: 1
replicas: 1
updatedReplicas: 1
파드 상세 정보 확인 시 admission control이 동작했음을 알 수 있다.
# 파드 상세 정보
apiVersion: v1
items:
- apiVersion: v1
kind: Pod
metadata:
annotations:
CapacityProvisioned: 0.25vCPU 0.5GB
Logging: LoggingEnabled
creationTimestamp: "2025-03-22T09:32:15Z"
generateName: kube-ops-view-796947d6dc-
labels:
app.kubernetes.io/instance: kube-ops-view
app.kubernetes.io/name: kube-ops-view
eks.amazonaws.com/fargate-profile: kube-system
pod-template-hash: 796947d6dc
name: kube-ops-view-796947d6dc-g7q88
namespace: kube-system
ownerReferences:
- apiVersion: apps/v1
blockOwnerDeletion: true
controller: true
kind: ReplicaSet
name: kube-ops-view-796947d6dc
uid: 62da6f68-5df7-4b08-851c-8f417265fe5f
resourceVersion: "4481"
uid: 221825be-704c-4586-b6d9-d14fb06dd8a3
spec:
automountServiceAccountToken: true
containers:
- env:
- name: TZ
value: Asia/Seoul
image: hjacobs/kube-ops-view:20.4.0
imagePullPolicy: IfNotPresent
livenessProbe:
failureThreshold: 3
periodSeconds: 10
successThreshold: 1
tcpSocket:
port: 8080
timeoutSeconds: 1
name: kube-ops-view
ports:
- containerPort: 8080
name: http
protocol: TCP
readinessProbe:
failureThreshold: 3
periodSeconds: 10
successThreshold: 1
tcpSocket:
port: 8080
timeoutSeconds: 1
resources: {}
securityContext:
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1000
startupProbe:
failureThreshold: 30
periodSeconds: 5
successThreshold: 1
tcpSocket:
port: 8080
timeoutSeconds: 1
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
name: kube-api-access-br5gk
readOnly: true
dnsPolicy: ClusterFirst
enableServiceLinks: true
nodeName: fargate-ip-10-10-23-60.ap-northeast-2.compute.internal
preemptionPolicy: PreemptLowerPriority
priority: 2000001000
priorityClassName: system-node-critical
restartPolicy: Always
schedulerName: fargate-scheduler
securityContext: {}
serviceAccount: kube-ops-view
serviceAccountName: kube-ops-view
terminationGracePeriodSeconds: 30
tolerations:
- effect: NoExecute
key: node.kubernetes.io/not-ready
operator: Exists
tolerationSeconds: 300
- effect: NoExecute
key: node.kubernetes.io/unreachable
operator: Exists
tolerationSeconds: 300
volumes:
- name: kube-api-access-br5gk
projected:
defaultMode: 420
sources:
- serviceAccountToken:
expirationSeconds: 3607
path: token
- configMap:
items:
- key: ca.crt
path: ca.crt
name: kube-root-ca.crt
- downwardAPI:
items:
- fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
path: namespace
status:
conditions:
- lastProbeTime: null
lastTransitionTime: "2025-03-22T09:33:06Z"
status: "True"
type: PodReadyToStartContainers
- lastProbeTime: null
lastTransitionTime: "2025-03-22T09:32:54Z"
status: "True"
type: Initialized
- lastProbeTime: null
lastTransitionTime: "2025-03-22T09:33:10Z"
status: "True"
type: Ready
- lastProbeTime: null
lastTransitionTime: "2025-03-22T09:33:10Z"
status: "True"
type: ContainersReady
- lastProbeTime: null
lastTransitionTime: "2025-03-22T09:32:54Z"
status: "True"
type: PodScheduled
containerStatuses:
- containerID: containerd://ebf888a56224d78a4bb7b85e913c9bbfb9e0ce1029a977d87a0649e750438dc9
image: docker.io/hjacobs/kube-ops-view:20.4.0
imageID: docker.io/hjacobs/kube-ops-view@sha256:58221b57d4d23efe7558355c58ad7c66c8458db20b1f55ddd9f89cc9275bbc90
lastState: {}
name: kube-ops-view
ready: true
restartCount: 0
started: true
state:
running:
startedAt: "2025-03-22T09:33:05Z"
hostIP: 10.10.23.60
hostIPs:
- ip: 10.10.23.60
phase: Running
podIP: 10.10.23.60
podIPs:
- ip: 10.10.23.60
qosClass: BestEffort
startTime: "2025-03-22T09:32:54Z"
kind: List
metadata:
resourceVersion: ""

fargate 에 netshoot 디플로이먼트(파드)
vCPU Value | Memory Value |
.25 vCPU | 0.5 GB, 1 GB, 2 GB |
.5 vCPU | 1 GB, 2 GB, 3 GB, 4 GB |
1 vCPU | 2 GB, 3 GB, 4 GB, 5 GB, 6 GB, 7 GB, 8 GB |
2 vCPU | Between 4 GB and 16 GB in 1-GB increments |
4 vCPU | Between 8 GB and 30 GB in 1-GB increments |
8 vCPU | Between 16 GB and 60 GB in 4-GB increments |
16 vCPU | Between 32 GB and 120 GB in 8-GB increments |
# 네임스페이스 생성
kubectl create ns study-aews
# 테스트용 파드 netshoot 디플로이먼트 생성 : 0.5vCPU 1GB 할당되어, 아래 Limit 값은 의미가 없음. 배포 시 대략 시간 측정해보자!
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: netshoot
namespace: study-aews
spec:
replicas: 1
selector:
matchLabels:
app: netshoot
template:
metadata:
labels:
app: netshoot
spec:
containers:
- name: netshoot
image: nicolaka/netshoot
command: ["tail"]
args: ["-f", "/dev/null"]
resources:
requests:
cpu: 500m
memory: 500Mi
limits:
cpu: 2
memory: 2Gi
terminationGracePeriodSeconds: 0
EOF
# 확인 : 메모리 할당 측정은 어떻게 되었는지?
kubectl get pod -n study-aews -o wide
kubectl get pod -n study-aews -o jsonpath='{.items[0].metadata.annotations.CapacityProvisioned}'

0.5 vCPU와 500MiB의 메모리의 리소스로 지정했지만 메모리가 1GB로 할당된 점을 확인할 수 있음.
이는 기본 kubelet, kube-proxy, containerd에서 최소 256MiB를 필요로 하기 때문에 500MiB보다 높은 1GB로 자동 할당 됨.
# 파드 내부에 zsh 접속 후 확인
kubectl exec -it deploy/netshoot -n study-aews -- zsh
-----------------------------------------------------
ip -c a
cat /etc/resolv.conf
curl ipinfo.io/ip # 출력되는 IP는 어떤것? , 어떤 경로를 통해서 인터넷이 되는 걸까?
ping -c 1 <다른 파드 IP ex. coredns pod ip>
lsblk
df -hT /
cat /etc/fstab
exit
-----------------------------------------------------

ip -c a : Pod IP 확인 시 노드의 IP와 동일함.
cat /etc/resolv.conf : 172.20.0.10을 DNS 서버로 사용
curl ipinfo.io/ip : Pod에서는 외부 통신 시 NAT GW를 타고 통신함.


ping -c 1 <다른 Pod IP> : 다른 파드와 통신 가능
lsblk : 파드 디스크 연결 정보 확인
df -hT : 루트 디스크 30GB 용량 할당 됨.
cat /etc/fstasb : 파드 디스크 연결 정보 확인
파드 권한과 호스트 네임스페이스 공유로 호스트 탈취 시도
kubectl apply -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
name: root-shell
namespace: study-aews
spec:
containers:
- command:
- /bin/cat
image: alpine:3
name: root-shell
securityContext:
privileged: true
tty: true
stdin: true
volumeMounts:
- mountPath: /host
name: hostroot
hostNetwork: true
hostPID: true
hostIPC: true
tolerations:
- effect: NoSchedule
operator: Exists
- effect: NoExecute
operator: Exists
volumes:
- hostPath:
path: /
name: hostroot
EOF
#
kubectl get pod -n study-aews root-shell
kubectl describe pod -n study-aews root-shell | grep Events: -A 10
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 48s fargate-scheduler Pod not supported on Fargate: fields not supported: HostNetwork, HostPID, HostIPC, volumes not supported: hostroot is of an unsupported volume Type, invalid SecurityContext fields: Privileged
# 출력 메시지
# Pod not supported on Fargate: fields not supported:
# HostNetwork, HostPID, HostIPC, volumes not supported:
# hostroot is of an unsupported volume Type, invalid SecurityContext fields: Privileged
# 삭제
kubectl delete pod -n study-aews root-shell
# (참고) fargate가 아닌 권한이 충분한 곳에서 실행 시 : 아래 처럼 호스트 네임스페이스로 진입 가능!
kubectl -n kube-system exec -it root-shell -- chroot /host /bin/bash
root@myk8s-control-plane:/# id
uid=0(root) gid=0(root) groups=0(root),1(daemon),2(bin),3(sys),4(adm),6(disk),10(uucp),11,20(dialout),26(tape),27(sudo)

Fargate에서 hostNetwork, hostPID, hostIPC 등의 권한을 지원하지 않기 때문에 파드를 생성할 수 없음
AWS ALB(Ingress)

# 게임 디플로이먼트와 Service, Ingress 배포
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: study-aews
name: deployment-2048
spec:
selector:
matchLabels:
app.kubernetes.io/name: app-2048
replicas: 2
template:
metadata:
labels:
app.kubernetes.io/name: app-2048
spec:
containers:
- image: public.ecr.aws/l6m2t8p7/docker-2048:latest
imagePullPolicy: Always
name: app-2048
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
namespace: study-aews
name: service-2048
spec:
ports:
- port: 80
targetPort: 80
protocol: TCP
type: ClusterIP
selector:
app.kubernetes.io/name: app-2048
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
namespace: study-aews
name: ingress-2048
annotations:
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: ip
spec:
ingressClassName: alb
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: service-2048
port:
number: 80
EOF
# 모니터링
watch -d kubectl get pod,ingress,svc,ep,endpointslices -n study-aews
# 생성 확인
kubectl get-all -n study-aews
kubectl get ingress,svc,ep,pod -n study-aews
kubectl get targetgroupbindings -n study-aews
# Ingress 확인
kubectl describe ingress -n study-aews ingress-2048
kubectl get ingress -n study-aews ingress-2048 -o jsonpath="{.status.loadBalancer.ingress[*].hostname}{'\n'}"
# 게임 접속 : ALB 주소로 웹 접속
kubectl get ingress -n study-aews ingress-2048 -o jsonpath='{.status.loadBalancer.ingress[0].hostname}' | awk '{ print "Game URL = http://"$1 }'
# 파드 IP 확인
kubectl get pod -n study-aews -owide
# 파드 증가
kubectl scale deployment -n study-aews deployment-2048 --replicas 4
# 게임 실습 리소스 삭제
kubectl delete ingress ingress-2048 -n study-aews
kubectl delete svc service-2048 -n study-aews && kubectl delete deploy deployment-2048 -n study-aews




Fargate Job
#
cat <<EOF | kubectl apply -f -
apiVersion: batch/v1
kind: Job
metadata:
name: busybox1
namespace: study-aews
spec:
template:
spec:
containers:
- name: busybox
image: busybox
command: ["/bin/sh", "-c", "sleep 10"]
restartPolicy: Never
ttlSecondsAfterFinished: 60 # <-- TTL controller
---
apiVersion: batch/v1
kind: Job
metadata:
name: busybox2
namespace: study-aews
spec:
template:
spec:
containers:
- name: busybox
image: busybox
command: ["/bin/sh", "-c", "sleep 10"]
restartPolicy: Never
EOF
#
kubectl get job,pod -n study-aews
kubectl get job -n study-aews -w
kubectl get pod -n study-aews -w
kubectl get job,pod -n study-aews
# 삭제
kubectl delete job -n study-aews --all


ttlSecondsAfterFinished에 의해 1분동안 Pending 상태를 유지하다가 실행된다.
Fargate Logging
- Fargate의 Amazon EKS는 Fluent Bit 기반의 내장 로그 라우터를 제공. 즉, Fluent Bit 컨테이너를 사이드카로 명시적으로 실행하지 않고 Amazon에서 실행함. 로그 라우터를 구성하기만 하면 됨.
- 구성은 다음 기준을 충족해야 하는 전용 ConfigMap을 통해 이루어짐.
- 이름 : aws-logging
- aws-observability라는 전용 네임스페이스에서 생성됨
- 5300자를 초과할 수 없음.
- ConfigMap을 생성하면 Fargate의 Amazon EKS가 자동으로 이를 감지하고 로그 라우터를 구성함. Fargate는 AWS에서 관리하는 Fluent Bit의 업스트림 호환 배포판인 Fluent Bit용 AWS 버전을 사용함.
- 로그 라우터를 사용하면 AWS의 다양한 서비스를 로그 분석 및 저장에 사용할 수 있음. Fargate에서 Amazon CloudWatch, Amazon OpenSearch 서비스로 로그를 직접 스트리밍할 수 있음. 또한 Amazon Data Firehose를 통해 Amazon S3, Amazon Kinesis 데이터 스트림 및 파트너 도구와 같은 대상으로 로그를 스트리밍할 수 있음.
- Fargate 파드를 배포할 기존 Kubernetes 네임스페이스를 지정하는 기존 Fargate 프로필임.
로그 발생 nginx 배포
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: sample-app
namespace: study-aews
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: nginx:latest
name: nginx
ports:
- containerPort: 80
name: http
resources:
requests:
cpu: 500m
memory: 500Mi
limits:
cpu: 2
memory: 2Gi
---
apiVersion: v1
kind: Service
metadata:
name: sample-app
namespace: study-aews
spec:
selector:
app: nginx
ports:
- port: 80
targetPort: 80
protocol: TCP
type: ClusterIP
EOF
# 확인
kubectl get pod -n study-aews -l app=nginx
kubectl describe pod -n study-aews -l app=nginx
# 반복 접속
kubectl exec -it deploy/netshoot -n study-aews -- curl sample-app | grep title
while true; do kubectl exec -it deploy/netshoot -n study-aews -- curl sample-app | grep title; sleep 1; echo ; date; done;
# 로그 확인
kubectl stern -n study-aews -l app=nginx

로그 설정 정보 확인
kube.*와 *에 매칭되면 /fargate/serverless/fargate-fluentbit-logs~ 로그 그룹에 로그를 수집하도록 설정됨
# main.tf
...
# Enable Fargate logging this may generate a large ammount of logs, disable it if not explicitly required
enable_fargate_fluentbit = true
fargate_fluentbit = {
flb_log_cw = true
}
...
# aws-observability라는 이름의 전용 네임스페이스 확인
kubectl get ns --show-labels
# Fluent Conf 데이터 값이 포함된 ConfigMap : 컨테이너 로그를 목적지로 배송 설정
## Amazon EKS Fargate 로깅은 ConfigMap의 동적 구성을 지원하지 않습니다.
## ConfigMap에 대한 모든 변경 사항은 새 포드에만 적용됩니다. 기존 포드에는 변경 사항이 적용되지 않습니다.
kubectl get cm -n aws-observability
kubectl get cm -n aws-observability aws-logging -o yaml
data:
filters.conf: |
[FILTER]
Name parser
Match *
Key_name log
Parser crio
[FILTER]
Name kubernetes
Match kube.*
Merge_Log On
Keep_Log Off
Buffer_Size 0
Kube_Meta_Cache_TTL 300s
flb_log_cw: "true" # Ships Fluent Bit process logs to CloudWatch.
output.conf: |+
[OUTPUT]
Name cloudwatch
Match kube.*
region ap-northeast-2
log_group_name /fargate-serverless/fargate-fluentbit-logs2025031600585521800000000c
log_stream_prefix fargate-logs-
auto_create_group true
[OUTPUT]
Name cloudwatch_logs
Match *
region ap-northeast-2
log_group_name /fargate-serverless/fargate-fluentbit-logs2025031600585521800000000c
log_stream_prefix fargate-logs-fluent-bit-
auto_create_group true
parsers.conf: |
[PARSER]
Name crio
Format Regex
Regex ^(?<time>[^ ]+) (?<stream>stdout|stderr) (?<logtag>P|F) (?<log>.*)$
Time_Key time
Time_Format %Y-%m-%dT%H:%M:%S.%L%z
Time_Keep On
수집된 파드 로그 확인 : CloudWatch Log streams 에서 nginx 로 검색 필터링(fluent-bot 포함 체크) → First event time 중 가장 최근것 클릭

Auto Mode
Auto Mode는 AWS가 EKS 리소스의 일부를 직접 책임지고 관리해 줌.
AWS EKS Auto Mode 아키텍처 (추정 포함)


AWS 측에서 관리가 가능한 부분은 안전하게 관리하고 의도적으로 사용자에게 노출하지 않는 것으로 추정됨.(사용자의 휴먼 에러 방지 목적)
실습 환경 배포
Terraform 배포
# Get the code : 배포 코드에 addon 내용이 읍다!
git clone https://github.com/aws-samples/sample-aws-eks-auto-mode.git
cd sample-aws-eks-auto-mode/terraform
# eks.tf : "system" 은 '전용인스턴스'로 추가하지 않는다
...
cluster_compute_config = {
enabled = true
node_pools = ["general-purpose"]
}
...
# Initialize and apply Terraform
terraform init
terraform plan
terraform apply -auto-approve
...
null_resource.create_nodepools_dir: Creating...
null_resource.create_nodepools_dir: Provisioning with 'local-exec'...
null_resource.create_nodepools_dir (local-exec): Executing: ["/bin/sh" "-c" "mkdir -p ./../nodepools"]
...
# Configure kubectl
cat setup.tf
ls -l ../nodepools
$(terraform output -raw configure_kubectl)
# kubectl context 변경
kubectl ctx
kubectl config rename-context "arn:aws:eks:ap-northeast-2:$(aws sts get-caller-identity --query 'Account' --output text):cluster/automode-cluster" "automode-lab"
kubectl ns default
# 아래 IP의 ENI 찾아보자
kubectl get svc,ep
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 172.20.0.1 <none> 443/TCP 27m
NAME ENDPOINTS AGE
endpoints/kubernetes 10.20.22.204:443,10.20.40.216:443 27m
#
terraform state list
terraform show
terraform state show 'module.eks.aws_eks_cluster.this[0]'
...
compute_config {
enabled = true
node_pools = [
"general-purpose",
]
node_role_arn = "arn:aws:iam::911283464785:role/automode-cluster-eks-auto-20250316042752605600000003"
}
...

AWS Console 확인
- VPC - ENI 확인 : EKS Owned ENI

- EKS : Cluster IAM Role, Node IAM Role, Auto Mode

- Compute : Built-in node pools

- Add-ons : 없음

- Access : IAM access entries

kubectl 확인
#
kubectl get crd
kubectl api-resources | grep -i node
# 노드에 Access가 불가능하니, 분석 지원(CRD)제공
kubectl explain nodediagnostics
#
kubectl get nodeclasses.eks.amazonaws.com
#
kubectl get nodepools
kubectl get nodepools -o yaml
#
kubectl get mutatingwebhookconfiguration
kubectl get validatingwebhookconfiguration








karpenter 동작 확인
# Step 1: Review existing compute resources (optional)
kubectl get nodepools
general-purpose
# Step 2: Deploy a sample application to the cluster
# eks.amazonaws.com/compute-type: auto selector requires the workload be deployed on an Amazon EKS Auto Mode node.
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: inflate
spec:
replicas: 1
selector:
matchLabels:
app: inflate
template:
metadata:
labels:
app: inflate
spec:
terminationGracePeriodSeconds: 0
nodeSelector:
eks.amazonaws.com/compute-type: auto
securityContext:
runAsUser: 1000
runAsGroup: 3000
fsGroup: 2000
containers:
- name: inflate
image: public.ecr.aws/eks-distro/kubernetes/pause:3.7
resources:
requests:
cpu: 1
securityContext:
allowPrivilegeEscalation: false
EOF
# Step 3: Watch Kubernetes Events
kubectl get events -w --sort-by '.lastTimestamp'
kubectl get nodes
스케일링 설정 후 확인
# 모니터링
eks-node-viewer --node-sort=eks-node-viewer/node-cpu-usage=dsc --extra-labels eks-node-viewer/node-age
watch -d kubectl get node,pod -A
#
kubectl scale deployment inflate --replicas 100 && kubectl get events -w --sort-by '.lastTimestamp'
#
kubectl scale deployment inflate --replicas 200 && kubectl get events -w --sort-by '.lastTimestamp'
#
kubectl scale deployment inflate --replicas 50 && kubectl get events -w --sort-by '.lastTimestamp'
# 실습 확인 후 삭제
kubectl delete deployment inflate && kubectl get events -w --sort-by '.lastTimestamp'

Graviton Workloads (2048 game) 배포 with ingress(ALB) : custom nodeclass/pool 사용
custom nodeclass/pool, ment 배포
# custom node pool 생성 : 고객 NodePool : Karpenter 와 키가 다르니 주의!
## 기존(karpenter.k8s.aws/instance-family) → 변경(eks.amazonaws.com/instance-family) - Link
ls ../nodepools
cat ../nodepools/graviton-nodepool.yaml
kubectl apply -f ../nodepools/graviton-nodepool.yaml
#
kubectl get NodeClass
kubectl get NodePool
#
ls ../examples/graviton
cat ../examples/graviton/game-2048.yaml
kubectl apply -f ../examples/graviton/game-2048.yaml
# c6g.xlarge : vCPU 4, 8 GiB RAM > 스팟 선택됨!
kubectl get nodeclaims
kubectl get cninodes.eks.amazonaws.com
kubectl get cninodes.eks.amazonaws.com -o yaml
eks-node-viewer --resources cpu,memory
kubectl get node -owide
kubectl describe node
#
kubectl get deploy,pod -n game-2048 -owide






- EKS - Compute : 내장 node pool 이 아닌 별도 node pool 생성 확인.

- EC2 - 1대 생성 확인, 접근 안됨. ⇒ Reboot 해보기 ⇒ Terminated 해보기



- 참고로, 생성되는 EC2의 OS는 Bottlerocket 이며, 루트 볼륨은 읽기 전용임.

- 해당 EC2에서 Monitoring → Instance audit 확인 ⇒ 맨 하단에 RunInstances(CloudTrail) 클릭 , 재부팅/삭제 실패도 클릭


- CloudTrail 확인 : 실행 주체 확인


- EC2 - 보안 그룹 확인

- EC2 - ENI 추가됨

ALB(Ingress) 설정
#
cat ../examples/graviton/2048-ingress.yaml
...
apiVersion: eks.amazonaws.com/v1
kind: IngressClassParams
metadata:
namespace: game-2048
name: params
spec:
scheme: internet-facing
---
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
namespace: game-2048
labels:
app.kubernetes.io/name: LoadBalancerController
name: alb
spec:
controller: eks.amazonaws.com/alb
parameters:
apiGroup: eks.amazonaws.com
kind: IngressClassParams
name: params
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
namespace: game-2048
name: ingress-2048
spec:
ingressClassName: alb
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: service-2048
port:
number: 80
kubectl apply -f ../examples/graviton/2048-ingress.yaml
#
kubectl get ingressclass,ingressclassparams,ingress,svc,ep -n game-2048
NAME CONTROLLER PARAMETERS AGE
ingressclass.networking.k8s.io/alb eks.amazonaws.com/alb IngressClassParams.eks.amazonaws.com/params 105s
NAME GROUP-NAME SCHEME IP-ADDRESS-TYPE AGE
ingressclassparams.eks.amazonaws.com/params internet-facing 105s
NAME CLASS HOSTS ADDRESS PORTS AGE
ingress.networking.k8s.io/ingress-2048 alb * k8s-game2048-ingress2-db993ba6ac-782663732.ap-northeast-2.elb.amazonaws.com 80 105s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/service-2048 NodePort 172.20.194.11 <none> 80:30280/TCP 105s
NAME ENDPOINTS AGE
endpoints/service-2048 10.20.30.64:80 105s
- Configure Security Groups : Configure security group rules to allow communication between the ALB and EKS cluster
- 보안 그룹 소스에 ALB SG ID가 이미 들어가 있는 상태라서 아래 규칙 추가 없이 접속이 되어야 하지만, 혹시 잘 안될 경우 아래 추가 할 것
# Get security group IDs
ALB_SG=$(aws elbv2 describe-load-balancers \
--query 'LoadBalancers[?contains(DNSName, `game2048`)].SecurityGroups[0]' \
--output text)
EKS_SG=$(aws eks describe-cluster \
--name automode-cluster \
--query 'cluster.resourcesVpcConfig.clusterSecurityGroupId' \
--output text)
echo $ALB_SG $EKS_SG # 해당 보안그룹을 관리콘솔에서 정책 설정 먼저 확인해보자
# Allow ALB to communicate with EKS cluster : 실습 환경 삭제 때, 미리 $EKS_SG에 추가된 규칙만 제거해둘것.
aws ec2 authorize-security-group-ingress \
--group-id $EKS_SG \
--source-group $ALB_SG \
--protocol tcp \
--port 80
# 아래 웹 주소로 http 접속!
kubectl get ingress ingress-2048 \
-o jsonpath='{.status.loadBalancer.ingress[0].hostname}' \
-n game-2048
k8s-game2048-ingress2-db993ba6ac-782663732.ap-northeast-2.elb.amazonaws.com


'k8s > AWS EKS' 카테고리의 다른 글
[AWS] EKS Upgrade (0) | 2025.04.02 |
---|---|
[AWS] EKS Security (0) | 2025.03.16 |
[AWS] EKS AutoScaling (0) | 2025.03.08 |
[AWS] EKS Observability (0) | 2025.03.01 |
[AWS] EKS Storage (0) | 2025.02.23 |