본문 바로가기

k8s/AWS EKS

[AWS] EKS Mode/Nodes

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
...

 

 

kube-ops-view 디플로이먼트/파드 상세 정보 확인

 

 

# 노드 상세 정보
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}'

 

netshoot pod 확인

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를 타고 통신함.

확인된 IP가 NAT GW IP와 동일

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)

 

pod Describe

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

 

생성 확인
Ingress 확인
Ingress 확인
ALB 주소로 웹 접속

 

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'

karpenter 동작 확인

 

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

graviton-nodepool.yaml 확인
NodeClass, NodePool 확인
game-2048.yaml 확인
4vCore 8GiB, Spot으로 선택됨
cninodes.eks.amazonaws.com yaml 확인
node 정보 확인

 

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

 

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

reboot 시 권한이 없어 안되는 것으로 보임
Terminiated 도 동일함

 

  • 참고로, 생성되는 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

보안 그룹 확인
Ingress 접속 확인

 

'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