AEWS Study #1 – Amzaon EKS 설치 및 기본 사용

PKOS Study가 끝나고 프로젝트와 개인적인 일이 바빠 다음 스터디는 조금 뒤로 미루어야지 하고 생각했었다. 하지만 EKS라는 매력적인 스터디를 넘어갈 수 없어…이번에도 신청했고 감사하게도 참여할 수 있게 됐다.
AEWS 는 AWS EKS Workshop Study의 약자이다. 앞으로 EKS를 기초부터 고급 활용까지 다양하게 배우고 또 그 내용을 정리할 예정이다. 그리고 내 블로그를 티스토리에서 워드프레스로 옮기고 첫 스터디이니 더 잘해볼 생각이다.

0. AWS EKS?

AWS EKS는 Kubernetes를 쉽게 실행할 수 있는 관리형 서비스로 AWS 환경에서 k8s Control Plane, Node 등을 직접 설치할 필요 없이 사용하게끔 해주는 서비스입니다.
https://docs.aws.amazon.com/eks/latest/userguide/what-is-eks.html

위 그림을 보면 EKS Control Plane이 있고 각 Worker Nodes는 VPC 내에 배포되는 것을 알 수 있다. 그리고 Control Plane은 하나의 AZ에만 배포되는 것이 아닌 여러 AZ에 배포되어 가용성도 확보되어있다.
ECR을 포함해서 AWS의 여러 기존 서비스와 통합해서 사용할 수 있기 때문에 AWS 종속성이 강한 인프라에서 사용하기 좋은 서비스라고 생각 된다.

위 그림은 앞선 그림보다 조금 더 상세하게 표현이 되어있는 그림이다. Control Plane은 AWS Managed VPC에 배포가 되어있는데 여기에는 API Server와 etcd가 있다.
여기서 etcd는 k8s Control Plane 컴포넌트에서 필요한 정보를 저장하고 사용자가 정의한 구성과 리소스 정보를 저장하는 k8s의 중요 구성 요소이다. EKS는 완전관리형 서비스이기 때문에 EKS CP에 속하는 etcd 또한 직접적인 관리가 필요하지 않고 EKS에서 자동 프리비저닝해 사용할 수 있다.

Node Group은 완전 관리형 노드그룹과 셀프형 노드그룹 그리고 서버리스인 AWS Fargate로 나뉜다
완전 관리형 노드그룹 : AWS Managed AMI 사용
셀프형 노드그룹 : Custom AMI 사용 가능, ASG/OS 관리 등을 직접 해야 함
서버리스 Fargate 노드그룹 : EC2 관리 요소 없음. 제공되는 Micro VM 사용

1. AWS EKS 구축

EKS에 대해 간단하게 알아봤고 이제 본격적으로 구축을 해보려고 한다.
EKS를 구축하는 방법에는 여러가지가 있다. AWS 콘솔에서, eksctl에서도 가능하고 Terraform이나 Cloudformation 등으로도 물론 가능하다.
나는 이번 구축은 스터디에서 제공한 yaml 파일을 사용해 Host EC2를 Cloudformation을 이용해 배포하고 해당 Host EC2에서 eksctl을 이용해 EKS을 배포를 할 예정이다.
배포 이전에 사전 준비해야 하는 내용은 많지 않다. ec2 연결에 사용할 Keypair와 SG에 지정할 접속지(보통은 집, 사무실) 공인 IP 주소이다. 2가지를 사전에 준비해놨고 아래 방법으로 배포를 진행했다.

# yaml 파일 다운로드
curl -O https://s3.ap-northeast-2.amazonaws.com/cloudformation.cloudneta.net/K8S/myeks-1week.yaml

# 배포
aws cloudformation deploy --template-file ~/Downloads/myeks-1week.yaml --stack-name myeks --parameter-overrides KeyName=aewspair SgIngressSshCidr=$(curl -s ipinfo.io/ip)/32 --region ap-northeast-2

위에서 사용한 yaml 파일은 각각 2개의 Public Subnet, Private Subnet을 갖고 있으며 NAT GW는 없고 Internet GW가 생성된다.
Host EC2에는 user-data로 kubectl, helm 등이 설치 된다.

Host EC2가 배포가 완료됐다면 접속해서 제대로 user-data가 진행됐는지 확인해본다.

# (옵션) cloud-init 실행 과정 로그 확인
sudo tail -f /var/log/cloud-init-output.log

# 사용자 확인
sudo su -
whoami

# 기본 툴 및 SSH 키 설치 등 확인
kubectl version --client=true -o yaml | yh
  gitVersion: v1.25.7-eks-a59e1f0

eksctl version
0.138.0

aws --version
aws-cli/2.11.15 Python/3.11.3 Linux/4.14.311-233.529.amzn2.x86_64 exe/x86_64.amzn.2 prompt/off

ls /root/.ssh/id_rsa*

# 도커 엔진 설치 확인
docker info

Host EC2가 잘 준비됐다면 eks 배포를 진행한다.

#vpcid와 Subnetid을 변수 입력
export VPCID=$(aws ec2 describe-vpcs --filters "Name=tag:Name,Values=$CLUSTER_NAME-VPC" | jq -r .Vpcs[].VpcId)
echo "export VPCID=$VPCID" >> /etc/profile
export PubSubnet1=$(aws ec2 describe-subnets --filters Name=tag:Name,Values="$CLUSTER_NAME-PublicSubnet1" --query "Subnets[0].[SubnetId]" --output text)
export PubSubnet2=$(aws ec2 describe-subnets --filters Name=tag:Name,Values="$CLUSTER_NAME-PublicSubnet2" --query "Subnets[0].[SubnetId]" --output text)
echo "export PubSubnet1=$PubSubnet1" >> /etc/profile
echo "export PubSubnet2=$PubSubnet2" >> /etc/profile

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

# 옵션 [터미널1] EC2 생성 모니터링
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.24 --ssh-access --external-dns-access --dry-run | yh
apiVersion: eksctl.io/v1alpha5
cloudWatch:
  clusterLogging: {}
iam:
  vpcResourceControllerPolicy: true
  withOIDC: false
kind: ClusterConfig
kubernetesNetworkConfig:

# 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.24 --ssh-access --external-dns-access --verbose 4
2023-04-28 23:59:29 [ℹ]  will create 2 separate CloudFormation stacks for cluster itself and the initial managed nodegroup
2023-04-28 23:59:29 [ℹ]  if you encounter any issues, check CloudFormation console or try 'eksctl utils describe-stacks --region=ap-northeast-2 --cluster=myeks'
2023-04-28 23:59:29 [ℹ]  Kubernetes API endpoint access will use default of {publicAccess=true, privateAccess=false} for cluster "myeks" in "ap-northeast-2"

배포가 잘 되는지 확인해보기 위해 Cloudformation과 EKS 그리고 EC2, ASG 등을 확인해보았다.

배포가 잘 된 것을 확인할 수 있었다.
kubectl get node 을 통해 node을 확인하면 일전에 kOps 스터디 때 결과와는 다르게 Control Plane은 나타나지 않는 것을 확인할 수 있다. api Server와의 통신은 가능하지만 get node에 별도로 Control Plane 정보를 표시해주진 않는다. 아래는 kOps 때 kubectl get node 결과이다 비교해보자.

Control Plane이 포함 된 kOps와 AWS Managed 영역에 Control Plane이 배포되는 EKS와 get node 결과가 다른 것을 확인할 수 있었다.

설치가 제대로 됐으니 이제 Worker Node에 접속을 해보겠다.

# 노드 IP 확인 및 PrivateIP 변수 지정
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
N1=192.168.1.149
N2=192.168.2.226

# eksctl-host 에서 노드의IP나 coredns 파드IP로 ping 테스트
ping -c 2 $N1
ping -c 2 $N2
PING 192.168.1.149 (192.168.1.149) 56(84) bytes of data.
--- 192.168.1.149 ping statistics ---
2 packets transmitted, 0 received, 100% packet loss, time 1020ms
#현재는 ping이 가지 않는다.

# 노드 보안그룹 ID 확인
aws ec2 describe-security-groups --filters Name=group-name,Values=*nodegroup* --query "SecurityGroups[*].[GroupId]" --output text
NGSGID=$(aws ec2 describe-security-groups --filters Name=group-name,Values=*nodegroup* --query "SecurityGroups[*].[GroupId]" --output text)
echo $NGSGID

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

# eksctl-host 에서 노드의IP나 coredns 파드IP로 ping 테스트
ping -c 2 $N1
ping -c 2 $N2
(k8sadmin@myeks:default) [root@myeks-host ~]# ping -c 2 $N1
PING 192.168.1.149 (192.168.1.149) 56(84) bytes of data.
64 bytes from 192.168.1.149: icmp_seq=1 ttl=255 time=0.535 ms
64 bytes from 192.168.1.149: icmp_seq=2 ttl=255 time=0.347 ms
--- 192.168.1.149 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1031ms
rtt min/avg/max/mdev = 0.347/0.441/0.535/0.094 ms
#이후 PING 테스트를 다시 하면 정상적으로 PING이 가는 것을 알 수 있다.

# 워커 노드 SSH 접속
ssh -i ~/.ssh/id_rsa ec2-user@$N1 hostname
ip-192-168-1-149.ap-northeast-2.compute.internal
ssh -i ~/.ssh/id_rsa ec2-user@$N2 hostname
ip-192-168-2-226.ap-northeast-2.compute.internal
ssh -i ~/.ssh/id_rsa ec2-user@$N1 
exit
ssh -i ~/.ssh/id_rsa ec2-user@$N2
exit

보안그룹에 Rule 추가 후 Ping 뿐 아니라 SSH 접속까지 문제 없이 되는 것을 확인할 수 있었다.

조금 재밌지만 어쩌면 당연한 사실은 ENI을 보면 Amazon EKS myeks로 설명이 되어있는 Control Plane ENI의 경우 소유자와 요청자의 Account ID가 다른 것을 확인할 수 있다. ENI의 소유자는 사용자 Account고 요청자는 다른 Account이다. ENI에 연결 된 인스턴스(AWS 콘솔 상에서는 확인되지 않는 CP Node)의 소유자 또한 사용자 Account가 아닌 별도의 Account임을 확인할 수 있다.

2. AWS EKS 기본 사용

EKS 설치를 확인했으니 간단한 기본 사용법을 확인해본다.
우선 사용 이전에 명령어 간소화를 위해 alias 축약 설정을 진행한다.

# 자동 완성 및 alias 축약 설정
source <(kubectl completion bash)
alias k=kubectl

(k8sadmin@myeks:default) [root@myeks-host ~]# k get node
NAME                                               STATUS   ROLES    AGE   VERSION
ip-192-168-1-149.ap-northeast-2.compute.internal   Ready    <none>   34m   v1.24.11-eks-a59e1f0
ip-192-168-2-226.ap-northeast-2.compute.internal   Ready    <none>   34m   v1.24.11-eks-a59e1f0

Sample Replica3개를 설정해서 Pod 배포를 진행해본다.

# 터미널1 (모니터링)
watch -d 'k get pod'

# 터미널2
# Deployment 배포(Pod 3개)
kubectl create deployment my-webs --image=gcr.io/google-samples/kubernetes-bootcamp:v1 --replicas=3
kubectl get pod -w

# 파드 증가 및 감소
kubectl scale deployment my-webs --replicas=6 && kubectl get pod -w
kubectl scale deployment my-webs --replicas=3
kubectl get pod

# 강제로 파드 삭제
kubectl delete pod --all && kubectl get pod -w
kubectl get pod

deployment 이후 3개의 Pod가 올라온 것을 확인할 수 있다.

Replicas을 6으로 설정하면 3개 Pod가 더 추가돼서 총 6개의 Pod가 올라오게 된다.

Replicas을 다시 3으로 조정하면 3개의 Pod가 줄어들어 기존 3개 올라왔던 Pod가 남게 된다.

Delete Pod -all로 강제로 Pod 삭제를 진행하게 되면 기존 올라왔던 Pod 3개가 지워지고 신규 Pod 3개가 다시 올라오는 것을 알 수 있다. (Replicas=3이기 때문에)

간단하게 Pod 배포 테스트를 통해 이상 없이 Pod 배포 및 Replicas 조절에 이상 없음을 확인할 수 있었다.

3. AWS EKS with Fargate

위 1번에서는 EKS 노드그룹 배포를 ASG를 통한 EC2 배포로 진행했었다. 여기서 추가로 노드그룹을 배포하면서 이번엔 Fargate로 배포를 진행해보려고 한다.


진행은 아래와 같이 진행해보았다.
fargate을 사용하는 eks cluster을 생성해준다.

# fargate을 사용하는 eks cluster을 생성
eksctl create cluster --name eks-fargate --region $AWS_DEFAULT_REGION --fargate
(k8sadmin@myeks:default) [root@myeks-host ~]# eksctl create cluster --name eks-fargate --region $AWS_DEFAULT_REGION --fargate
2023-04-29 01:07:34 [ℹ]  eksctl version 0.139.0
2023-04-29 01:07:34 [ℹ]  using region ap-northeast-2
2023-04-29 01:07:34 [ℹ]  setting availability zones to [ap-northeast-2c ap-northeast-2a ap-northeast-2b]


위와 같이 fargate profile과 node가 잘 생성 된 것을 확인했으면 pod 배포를 진행해서 테스트해보면 된다. 이때 동일 Account에 2개 이상의 EKS Cluster가 생성되었기 때문에 kubectl 명령어 사용 시 충돌이 발생하지 않게 하기 위해 kubeconfig 설정을 먼저 진행한다.

#현재 선택 된 context 확인
kubectl config current-context
(k8sadmin@eks-fargate:N/A) [root@myeks-host .kube]# kubectl config current-context
k8sadmin@eks-fargate.ap-northeast-2.eksctl.io

#eks-fargate cluster가 선택된 것을 확인했으나 혹시 앞서 실습한 myeks cluster일 경우 변경 진행
kubectl config use-context eks-fargate

#배포 시 context 사용하여 배포도 가능
kubectl apply -f filepath --context clustercontext

Fargate Node을 사용하는 k8s deployment 파일을 작성하고 배포한다.

#demo-fargate-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - name: my-app
        image: nginx:latest
        ports:
        - containerPort: 80

#yaml 파일 통한 배포
kubectl apply -f demo-fargate-deployment.yaml
deployment.apps/my-app created

#배포 확인
k get pods --output wide

위와 같이 제대로 배포가 완료된 것을 확인할 수 있다.

4. 정리

kOps 스터디를 진행한지 얼마 안 돼서 그런가 k8s가 조금은 익숙한 상태로 시작한 것 같다.
그럼에도 kOps와 EKS는 다른 부분들이 있어 그 부분들을 하나하나 짚어보며 상세히 알아가는 스터디가 될 것 같아서 기대가 된다. 그리고 이번 실습 때는 간단하게나마 fargate을 사용해봤는데 AWS을 쓰면서 fargate는 처음 사용해봐서 오늘 실습은 조금 더 의미가 있었던 것 같다.
다음에는 Private Repo을 활용하는 부분도 실습해볼 예정이다.