[AWES-3기]1주차 - Amazon EKS 설치 및 기본 사용

유형욱·2025년 2월 8일
0

AEWS - 3기

목록 보기
1/1

약 3년만에 Velog에 글을 작성하게 되었다.
현 직장으로 이직 후에는 블로깅보다 외부 발표용 슬라이드, 교육용 콘텐츠를 만드는데 시간을 많이 쏟다보니 블로깅 할 시간이 없었다.😒(핑계아님)

오랜만에 작성하는 블로그 글은 가시다님이 운영하시는 AWES 3기스터디의 과제와 후기를 정리하여 연재할 계획이다.

가시다님과 CloudNet@ 팀에서 운영중인 노션 페이지 - 링크


1. Amaonz EKS 소개

Amazon EKS(Elastic Kubernetes Service)는 쿠버네티스 컨트롤 플레인 또는 노드 영역에 대해 AWS에서 유지 관리하는 관리형 쿠버네테스 서비스다. [Amazon EKS란 무엇입니까?]

주요 기능

  • AWS 관리형 서비스: 쿠버네티스 컨트롤 플레인 영역을 AWS 관리 VPC에 구성하고 관리
  • 고가용성 구성: 다수의 AWS 가용 영역에 배치되어 고가용성 보장
  • 다양한 AWS 서비스와 통합: AWS 서비스와 통합되어 포괄적인 플랫폼을 제공

처음 Amazon EKS을 입문하시는 분들에게 추천드리는 영상👇

Amazon EKS 소개영상 - AWS Sr.Solutions Architect 최용호님

EKS 아키텍처

  • EKS 컨트롤 플레인 : 분산 구성 요소, 복원성, 가동 시간(SLA), AWS Managed VPC(3개 AZ, API NLB, ETCD ELB - 링크)
  • EKS 데이터 플레인 : Customer VPC - EKS owned ENI?, 노드 유형(Managed node groups, Self-managed nodes, AWS Auto Mode, AWS Fargate, Karpenter, AWS Hybrid Nodes) - 링크
  • Managed node groups (관리형 노드 그룹) : 최신 EKS Optimized AMI를 사용 - 링크, AWS에서 AMI 관리, Capacity(On-Demand, Spot) - 링크

EKS Cluster Endpoint 분석

실제 EKS Cluster Endpoint의 구조와 네트워크 트래픽 흐름은 정확하게 알아야 할 필요가 있다.
필자는 실제 금융권 EKS 관련 프로젝트를 진행하면서 Cluster Endpoint의 구조를 정확하게 알지 못하여 어려움을 겪었던 적이 있었다.. 최근에는 규제가 완화되어 변했을지 모르겠지만 필자가 프로젝트를 진행할 때에는 폐쇄망 환경에서 Private으로만 운영되는 것이 원칙이었다.

EKS Cluster Endpoint - Public :

제어부 → (EKS owned ENI) 워커노드 kubelet, 워커노드 → (퍼블릭 도메인) 제어부, 사용자 kubectl → (퍼블릭 도메인) 제어부

  • Flow 예시

EKS Cluster Endpoint - Public / Private :

제어부 → (EKS owned ENI) 워커노드 kubelet, 워커노드 → (프라이빗 도메인, EKS owned ENI) 제어부, 사용자 kubectl → (퍼블릭 도메인) 제어부

  • Flow 예시

EKS Cluster Endpoint - Private :

제어부 → (EKS owned ENI) 워커노드 kubelet, 워커노드,사용자 kubectl → (프라이빗 도메인, EKS owned ENI) 제어부

  • Flow 예시

3가지 모드 비교 표

모드API 서버 접근 방식인터넷에서 접근 가능 여부보안 수준사용 사례
PublicPublic Endpoint✅ 가능낮음테스트/개발
Public & PrivatePublic + Private✅ (제한 가능)중간내부+외부 관리 필요
PrivatePrivate Endpoint❌ 불가능높음보안이 중요한 프로덕션 환경

최적의 선택 방법

  1. 보안이 가장 중요하다면? → Private 모드
  2. 내부 네트워크에서만 관리하고, 일부 외부 접근도 필요하다면? → Public and Private 모드
  3. 빠르게 셋업하고, 간편하게 접근하고 싶다면? → Public 모드

2. 직접 처음부터 EKS 배포해보기

EKS을 배포하는 방식은 다음 3가지 방식이 대표적이다. 물론 Terraform으로 배포하는 것이 국룰이다.
1. 웹 관리 콘솔 - 링크
2. eksctl - 링크, 공식 홈페이지
3. IaC - CDK, CloudFormation, Terraform 등 -

필자는 HashiCorp에 재직중이기 때문에 당연히 Terraform을 통해서 EKS Cluster을 배포하는 것이 익숙하고 실제로도 가장 좋은 방법이라고 생각한다. 🤭
하지만 Terraform 문법이 익숙하지 않거나 처음 접하는 사용자라면 AWS Console에서 수동으로 클러스터, 노드그룹 등을 생성해보고 충분히 익숙해진 뒤 자동설치 도구를 활용해보는 것도 좋은 접근이라고 생각된다.

👉 Terraform을 통해서 EKS 배포가 처음이라면?
링크을 참고하여 Terraform으로 배포하는 방법을 학습 해보는 것을 추천한다!

실습환경 구성

필자는 AWS 실습 계정에 제약이 있어 작업용 EC2을 생성하지 않고 로컬환경(MacBook)에서 직접 관리할 예정이다.

EKS 배포

# 변수 설정
export AWS_DEFAULT_REGION=ap-northeast-2
export CLUSTER_NAME=myeks
export VPCID=$(aws ec2 describe-vpcs --filters "Name=tag:Name,Values=$CLUSTER_NAME-VPC" | jq -r .Vpcs[].VpcId)
echo $PubSubnet1,$PubSubnet2

# 변수 확인
echo $AWS_DEFAULT_REGION
echo $CLUSTER_NAME
echo $VPCID
echo $PubSubnet1,$PubSubnet2

# 옵션 [터미널1] EC2 생성 모니터링
while true; do aws ec2 describe-instances --query "Reservations[*].Instances[*].{PublicIPAdd:PublicIpAddress,PrivateIPAdd:PrivateIpAddress,InstanceName:Tags[?Key=='Name']|[0].Value,Status:State.Name}" --filters Name=instance-state-name,Values=running --output text ; echo "------------------------------" ; sleep 1; done
aws ec2 describe-instances --query "Reservations[*].Instances[*].{PublicIPAdd:PublicIpAddress,PrivateIPAdd:PrivateIpAddress,InstanceName:Tags[?Key=='Name']|[0].Value,Status:State.Name}" --filters Name=instance-state-name,Values=running --output table

# eks 클러스터 & 관리형노드그룹 배포 전 정보 확인
eksctl create cluster --name $CLUSTER_NAME --region=$AWS_DEFAULT_REGION --nodegroup-name=$CLUSTER_NAME-nodegroup --node-type=t3.medium \
--node-volume-size=30 --vpc-public-subnets "$PubSubnet1,$PubSubnet2" --version 1.31 --ssh-access --external-dns-access --dry-run | yh
...
vpc:
  autoAllocateIPv6: false
  cidr: 192.168.0.0/16
  clusterEndpoints:
    privateAccess: false
    publicAccess: true
  id: vpc-0505d154771a3dfdf
  manageSharedNodeSecurityGroupRules: true
  nat:
    gateway: Disable
  subnets:
    public:
      ap-northeast-2a:
        az: ap-northeast-2a
        cidr: 192.168.1.0/24
        id: subnet-0d98bee5a7c0dfcc6
      ap-northeast-2c:
        az: ap-northeast-2c
        cidr: 192.168.2.0/24
        id: subnet-09dc49de8d899aeb7

다음 그림과 같이 --dry-run 옵션을 통해서 생성될 manifest의 결과값을 미리 확인하여 생성될 클러스터 정보를 확인할 수 있다.

# eks 클러스터 & 관리형노드그룹 배포: 총 15분 소요
eksctl create cluster --name $CLUSTER_NAME --region=$AWS_DEFAULT_REGION --nodegroup-name=$CLUSTER_NAME-nodegroup --node-type=t3.medium \
--node-volume-size=30 --vpc-public-subnets "$PubSubnet1,$PubSubnet2" --version 1.31 --ssh-access --external-dns-access --verbose 4
...
023-04-23 01:32:22 []  setting current-context to admin@myeks.ap-northeast-2.eksctl.io
2023-04-23 01:32:22 []  saved kubeconfig as "/root/.kube/config"
...

3. API server endpoint access 분석

클러스터 배포확인

Cluster 생성 및 모니터링 해놓은 터미널 결과를 보니 정상적으로 Node가 생성된 것을 확인할 수 있다.

실제 AWS Console에 접속해서도 myeks-nodegroupNodes에도 2개의 Node가 생성된 것을 확인할 수 있다.

이렇게 아주 간단히 eksctl 명령을 활용하여 VPC 구성부터 Bastion Node, EKS Cluster등을 설치하여 실습환경 구성을 완료하였다.

여기서 유의깊게 살펴볼 부분은 바로 API server endpoint access: Public이다. 앞서 EKS Cluster 아키텍처를 소개하면서 언급했던 접근 방식을 바로 여기서 확인할 수 있다. 자세한 내용은 뒤에서 이어서 살펴보자

클러스터 확인(상세): kubectl을 활용한 확인

# krew 플러그인 확인
kubectl krew list
kubectl ctx
kubectl ns
kubectl ns default
kubectl get-all    # 모든 네임스페이스에서 모든 리소스 확인

krew 플러그인 설치방법: 링크

> kubectl cluster-info

Kubernetes control plane is running at https://7242BA454C638ACE3CC13B2D11E773B8.sk1.ap-northeast-2.eks.amazonaws.com
CoreDNS is running at https://7242BA454C638ACE3CC13B2D11E773B8.sk1.ap-northeast-2.eks.amazonaws.com/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
> aws eks describe-cluster --name $CLUSTER_NAME | jq -r .cluster.endpoint
https://50E14FE698DE0E5CA2055F72AB086163.gr7.ap-northeast-2.eks.amazonaws.com

클러스터 동작확인

필자는 Public Access 테스트를 위해서 Istio을 활용할 예정이다. 자세한 내용은 뒤에어 이어서 설명하겠다.

istioctl을 활용한 istio 설치 - 링크

istioctl install --set profile=default 
kubectl label namespace default istio-injection=enabled
kubectl get crd gateways.gateway.networking.k8s.io &> /dev/null || \
{ kubectl kustomize "github.com/kubernetes-sigs/gateway-api/config/crd?ref=v1.2.0" | kubectl apply -f -; }

istiod가 정상적으로 설치된 것을 확인할 수 있다.

이제 bookinfo 샘플 애플리케이션을 배포하보자.

kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.24/samples/bookinfo/platform/kube/bookinfo.yaml

default 네임스페이스에 bookinfo 샘플이 정상적으로 배포된 것을 확인할 수 있다.

bookinfo 애플리케이션이 정상적으로 동작하는지 curl 명령을 통해 확인해본다.

kubectl exec "$(kubectl get pod -l app=ratings -o jsonpath='{.items[0].metadata.name}')" -c ratings -- curl -sS productpage:9080/productpage | grep -o "<title>.*</title>"

<title>Simple Bookstore App</title>

이제 Gateway API을 통해 외부에서 직접 접속할 수 있는지 확인해보자.

  • 외부에서 API Gateway을 통한 트래팩 접속
kubectl apply -f samples/bookinfo/gateway-api/bookinfo-gateway.yaml
  
gateway.gateway.networking.k8s.io/bookinfo-gateway created
httproute.gateway.networking.k8s.io/bookinfo created

  • 기본값이 LoadBalancer 타입이므로 ClusterIP로 변경하고 테스트를 진행한다.
kubectl annotate gateway bookinfo-gateway networking.istio.io/service-type=ClusterIP --namespace=default

ClusterIP 타입은 외부에서 접속할 수 없지만 필자는 로컬 환경에서 테스트 중이기 때문때 kubectl port-forward 명령을 활용하여 바로 접속해보겠다.

kubectl port-forward svc/bookinfo-gateway-istio 8080:80

브라우저에서 http://localhost:8080/productpage로 접속을 확인해보자. 잘. 동작하는 것을 확인할 수 있다.


EKS Cluster Endpoint 를 Public(IP제한)+Private 로 변경 및 확인

EKS Cluster Endpoint Access - 그림출처: Ongja님 블로그

# [모니터링1] 설정 후 변경 확인 >> 추가로 자신의 집 PC에서도 아래 dig 조회 모니터링 걸어보자!
APIDNS=$(aws eks describe-cluster --name $CLUSTER_NAME | jq -r .cluster.endpoint | cut -d '/' -f 3)
dig +short $APIDNS
while true; do dig +short $APIDNS ; echo "------------------------------" ; date; sleep 1; done

# [모니터링2] 노드에서 ControlPlane과 통신 연결 확인 : IPv4 kube-proxy, kubelet 확인
N1=$(kubectl get node --label-columns=topology.kubernetes.io/zone --selector=topology.kubernetes.io/zone=ap-northeast-2a -o jsonpath='{.items[0].status.addresses[3].address}')

N2=$(kubectl get node --label-columns=topology.kubernetes.io/zone --selector=topology.kubernetes.io/zone=ap-northeast-2c -o jsonpath='{.items[0].status.addresses[3].address}')

본래 스터디에서는 bastion에서 EKS Node로 iternal ip을 활용해 접속하지만 필자는 Local에서 접속해야 해서 node-shell을 활용하서 해결해보았다.

# node-shell을 활용한 디버깅
kubectl node-shell $N1 -- bash -c "while true; do ss -tnp | egrep 'kubelet|kube-proxy'; echo '----------------'; sleep 1; done"
kubectl node-shell $N2 -- bash -c "while true; do ss -tnp | egrep 'kubelet|kube-proxy'; echo '----------------'; sleep 1; done"

이제 Public(IP제한)+Private 로 변경 : 설정 후 6분 정도 후 반영 >> IP를 자신의 집 공인 IP를 직접 넣어보자!

aws eks update-cluster-config --region $AWS_DEFAULT_REGION --name $CLUSTER_NAME --resources-vpc-config endpointPublicAccess=true,publicAccessCidrs="$(curl -s ipinfo.io/ip)/32",endpointPrivateAccess=true

실행 결과는 다음과 같다.

{
    "update": {
        "id": "4da323a5-0bc3-3378-9720-0a1bd8fec89a",
        "status": "InProgress",
        "type": "EndpointAccessUpdate",
        "params": [
            {
                "type": "EndpointPublicAccess",
                "value": "true"
            },
            {
                "type": "EndpointPrivateAccess",
                "value": "true"
            },
            {
                "type": "PublicAccessCidrs",
                "value": "[\"211.234.203.153/32\"]"
            }
        ],
        "createdAt": "2025-02-08T22:28:46.735000+09:00",
        "errors": []
    }
}

콘솔을 들어가서 확인해보니 클러스터 설정이 변경되고 있음을 확인할 수 있다. 업데이트되는 동안에는 "Manage VPC resources", "Manage endpoint access"가 비활성화 되는 것으로 보인다.

Velog Blob 서버에 이상이 있는지 중간중간 이미지가 다 날아갔다..🤬

자 이제 정상적으로 반영된 것을 확인할 수 있다.

# kubectl 사용 확인
kubectl get node -v=6
kubectl cluster-info
ss -tnp | grep kubectl  # 신규 터미널에서 확인 해보자 >> EKS 동작 VPC 내부에서는 Cluster Endpoint 도메인 질의 시 Private IP 정보를 리턴해준다

# EKS ControlPlane 보안그룹 ID 확인
aws eks describe-cluster --name myeks --query "cluster.resourcesVpcConfig.securityGroupIds" --output text

CPSGID=$(aws eks describe-cluster --name myeks --query "cluster.resourcesVpcConfig.securityGroupIds" --output text)

echo $CPSGID

# 노드 보안그룹에 eksctl-host 에서 노드(파드)에 접속 가능하게 룰(Rule) 추가 설정
aws ec2 authorize-security-group-ingress --group-id $CPSGID --protocol '-1' --cidr 192.168.1.100/32

# 결과예시
{
    "Return": true,
    "SecurityGroupRules": [
        {
            "SecurityGroupRuleId": "sgr-06e56e845dd6cbf85",
            "GroupId": "sg-00c33fdba493c50a4",
            "GroupOwnerId": "856117747411",
            "IsEgress": false,
            "IpProtocol": "-1",
            "FromPort": -1,
            "ToPort": -1,
            "CidrIpv4": "192.168.1.100/32"
        }
    ]
}
  • 모니터링 : tcp peer 정보 변화 확인
# kube-proxy rollout : ss에 kube-proxy peer IP 변경 확인
kubectl rollout restart ds/kube-proxy -n kube-system

# kubelet 은 노드에서 systemctl restart kubelet으로 적용해보자 : ss에 kubelet peer IP 변경 확인
for i in $N1 $N2; do 
  echo ">> node $i <<"; 
  kubectl node-shell $i -- bash -c "systemctl restart kubelet"; 
  echo; 
done

kube-proxy 파드와 kubelet 서비스를 재기동한 뒤 확인해보니 192 대역으로 변경된 것을 확인할 수 있다!

  • 변경된 결과화면

이로서 확인할 수 있는 것은 본래의 API Server Endpoint가 외부에서 호출할 때와 내부에서 호출할 때 다른 결과값을 리턴받는 것을 알 수 있다.

  • Node 내부에 접속 후 Dig 결과
dig +short 7242BA454C638ACE3CC13B2D11E773B8.sk1.ap-northeast-2.eks.amazonaws.com
192.168.1.34
192.168.2.46
  • Node 외부(로컬)에서 Dig 결과
dig +short 7242BA454C638ACE3CC13B2D11E773B8.sk1.ap-northeast-2.eks.amazonaws.com
3.36.226.243
3.35.29.22

이러한 동작이 가능한 이유는 "네트워크 환경에 따라 public access접근 주소, private access접근 주소를 DNS응답으로 사용하기 때문" - 그림출처: 악분님 블로그


클러스터 동작확인 - 샘플 애플케이션 배포 테스트

istioctl을 활용한 istio 설치 - 링크

istioctl install --set profile=minimal 
kubectl label namespace default istio-injection=enabled

istiod가 정상적으로 설치된 것을 확인할 수 있다.

이제 bookinfo 샘플 애플리케이션을 배포하보자.

kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.24/samples/bookinfo/platform/kube/bookinfo.yaml

default 네임스페이스에 bookinfo 샘플이 정상적으로 배포된 것을 확인할 수 있다.

bookinfo 애플리케이션이 정상적으로 동작하는지 curl 명령을 통해 확인해본다.

kubectl exec "$(kubectl get pod -l app=ratings -o jsonpath='{.items[0].metadata.name}')" -c ratings -- curl -sS productpage:9080/productpage | grep -o "<title>.*</title>"

<title>Simple Bookstore App</title>

이제 Gateway API을 통해 외부에서 직접 접속할 수 있는지 확인해보자.

  • 기본값이 LoadBalancer 타입이므로 ClusterIP로 변경하고 테스트를 진행한다.
kubectl annotate gateway bookinfo-gateway networking.istio.io/service-type=ClusterIP --namespace=default
  • 외부에서 API Gateway을 통한 트래팩 접속
kubectl apply -f samples/bookinfo/gateway-api/bookinfo-gateway.yaml
  
gateway.gateway.networking.k8s.io/bookinfo-gateway created
httproute.gateway.networking.k8s.io/bookinfo created

ClusterIP 타입은 외부에서 접속할 수 없지만 필자는 로컬 환경에서 테스트 중이기 때문때 kubectl port-forward 명령을 활용하여 바로 접속해보겠다.

kubectl port-forward svc/bookinfo-gateway-istio 8080:80

브라우저에서 http://localhost:8080/productpage로 접속을 확인해보자. 잘. 동작하는 것을 확인할 수 있다.

실제 kubectl 명령을 로컬(MacBook)에서 실행하는데도 kubectl 명령과 port-forward 명령등도 잘 동작하는 것으로 보인다.

사실 Public / Private 모드로 했을 때 동작안하는 부분이 있을 것으로 예상하고 진행했으나 한번에 잘 동작해서 당황스러웠다😅

다 사용한 리소스는 깔끔하게 지워주자 🧹

samples/bookinfo/platform/kube/cleanup.sh
kubectl delete -f samples/addons
istioctl uninstall -y --purge
kubectl delete namespace istio-system
kubectl label namespace default istio-injection-
kubectl kustomize "github.com/kubernetes-sigs/gateway-api/config/crd/experimental?ref=v1.2.0" | kubectl delete -f -

이번 글에서는 이번 스터디를 위한 EKS 구조 및 클러스터 구축방안을 살펴보고 "API Server Endpoint Access" 방법 중에서 Public, Public and Private 모드로 구성하여 실제 어떻게 동작하는지 살펴보았다.

다음에는 Private 모드 테스트를 위한 환경을 구성해서 살펴보는 글도 준비해보면 좋을 것 같다.

profile
DevOps / Platform Engineer / Instructor / Pre Sales 등으로 활동하고 있습니다!

0개의 댓글