2024-04-27T12:05:15.000Z
지난 포스트에서는 Github Actions를 사용해서, 커밋을 원격 레포지토리에 푸시하면 빌드된 이미지를 AWS ECR에 자동으로 업로드되도록 해 보았다.
이번에는 실제로 AWS EKS를 사용해서 클러스터를 생성해보자.
실제 devOps 개발자 분들 혹은 클라우드 서비스에 일가견이 있으신 분들은 제 글을 보고 의문을 품는 부분이 한두 곳이 아니실 수 있습니다.
여러분들의 의견이 전부 맞습니다. 저는 프론트엔드 개발자이며, 단순히 회사의 k8s 인프라가 어떤 방식으로 동작하는 지 너무나도 궁금하여, 회사에서 구축한 방식을 기준으로 설명을 하는 것입니다.
제 수준에서 설명을 할 수 있는 부분(왜 spot 인스턴스를 사용했는가?)은 설명하겠지만, 너무 deep해지는 부분들(ex. istio는 무엇이며, 왜 사용하는가?)은 제 수준을 벗어나는 주제가 되어서 부득이 생략하도록 하겠습니다. 🙇
AWS EKS 대시보드에서 직접 cluster를 생성할 수도 있겠지만, AWS 공식 EKS CLI인 eksctl을 사용하면 터미널에서 손쉽게 클러스터를 생성할 수 있다.
맥의 경우 homebrew를 사용하면 eksctl을 간단히 설치할 수 있다.
아래와 같이 yaml 포맷의 ClusterConfig을 작성하며, 기본적인 구조는 AWS 공식 문서를 참조하여 작성하였고, 일부 속성만 추가 및 수정하였다.
# cluster-config.yaml
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadata:
name: greenhead-cluster
region: ap-northeast-2 # 서울 리전
version: "1.28"
vpc:
cidr: "10.0.0.0/16" # 클러스터에서 사용할 VPC의 CIDR
nat:
gateway: HighlyAvailable
managedNodeGroups:
- name: node-group # 클러스터의 노드 그룹명 (이름 변경!)
instanceType: t3.medium # 클러스터 워커 노드의 인스턴스 타입
desiredCapacity: 3 # 클러스터 워커 노드의 갯수
privateNetworking: true
iam:
withAddonPolicies:
imageBuilder: true # Amazon ECR에 대한 권한 추가
albIngress: true # albIngress에 대한 권한 추가
autoScaler: true # auto scaling에 대한 권한 추가
externalDNS: true # externalDNS에 대한 권한 추가
spot: true # ✅ Spot Instance로 노드 그룹 생성 (기본값은 On-Demand)
iam:
withOIDC: true위와 같이 apiVersion, kind 필드를 포함한 yaml 파일을 매니페스트Manifest 파일이라고 부른다.
매니페스트 파일은 k8s 리소스를 어떻게 생성/관리할 지를 정의하는 파일로, kubectl 혹은 eksctl 등의 CLI 툴을 사용하여 이를 클러스터에 적용할 수 있다.
기본적인 구조는 앞서 설명했듯이 AWS 공식 문서의 예제를 참조하였으나, 일부 수정된 부분이 있다. 그 부분을 간단히 설명해보겠다.
AWS EKS에서 지원하는 k8s의 각 버젼별 지원정보를 제공하는 AWS EKS Kubernetes release calendar를 보면, k8s 1.27버젼의 공식 지원 종료일은 2024년 7월 24일로, 현재 얼마 남지 않은 상태이다.
표준 지원 종료일(End of standard support)이 지났더라도 연장 지원 종료일(End of extended support)이 지나지 않은 k8s 버젼은 계속해서 사용할 수는 있다.
But, 표준 지원의 대상이 되는 k8s를 사용할 경우 EKS 클러스터 시간당 사용 비용은 0.1$이지만, 연장 지원의 대상이 되는 k8s를 사용하는 경우는 시간당 비용이 0.6$로 무려 6배나 높아진다!
클러스터를 한달 사용한다고 했을 때, 24시간 * 30일 = 720시간을 사용한다고 치면, 0.1$로 계산한다면 72$이지만, 0.6$로 계산한다면 432$이다.
사실, 단순 실습이 목적이므로 1.27 버젼의 표준 지원 종료일인 7월 24일까지는 충분히 여유가 있다. 하지만 마지막으로 실습하였을 때 1.28 버젼을 사용했기에 버전을 바뀜으로써 생길 수 있는 변수를 원치 않는데다, 굳이 더 낮은 버전을 고집할 그럴 듯한 이유도 없으므로, 이번에도 1.28 버전을 사용하였다.
AWS에서 제공하는 서비스인 CloudWatch는 실시간 데이터 모니터링 및 관리 기능을 제공하는데, 로깅 서비스 치고는 상당히 높은 비용을 요구한다.
금년 2월에 청구된 AWS 비용 중, CloudWatch는 28$ 정도 청구되었다.
나의 경우는 간단한 실습이 목적이므로, CloudWatch는 사용하지 않는다.
해당 필드의 값을 true로 설정하면 노드 그룹은 On-Demand가 아닌 Spot 인스턴스를 생성하게 된다. 이렇게 변경한 이유는, 대부분의 경우 Spot 인스턴스가 저렴하기 때문이다.
좀 더 자세히 설명해 보겠다. managedNodeGroups 필드는 클러스터의 노드 그룹을 정의 및 생성할 때 사용되는 필드이다. 노드 그룹은 노드 인스턴스를 생성하는 데, 각 노드는 AWS EC2 인스턴스로 생성된다.
AWS EC2 인스턴스는 예약 인스턴스를 제외하면 크게 온디맨드 인스턴스, 스팟 인스턴스 2가지로 나뉜다.
나의 경우 m3.medium 스팟 인스턴스를 사용할 것이다. 글 작성 기준으로 On-Demand에 비해 60% 정도 저렴하며, 중단 빈도도 5% 미만이므로 실습용으로 사용하기에 적합하다.
아래 링크에서 Spot 인스턴스와 On-Demand 인스턴스의 요금을 비교해보고, 실제 Spot 인스턴스의 요금 절감률과 중단률을 확인할 수 있다.
이제, 다음 eksctl 명령어를 사용하여 EKS 클러스터를 생성해보자.
eksctl create cluster --config-file=cluster-config.yaml명령어가 실행되면 터미널에 클러스터 생성과 관련된 로그가 엄청나게 올라오는 것을 확인할 수 있다.
...
...
2024-04-27 11:36:24 [ℹ] node "ip-10-0-161-211.ap-northeast-2.compute.internal" is ready
2024-04-27 11:36:25 [ℹ] kubectl command should work with "/Users/greenhead/.kube/config", try 'kubectl get nodes'
2024-04-27 11:36:25 [✔] EKS cluster "greenhead-cluster" in "ap-northeast-2" region is ready약 10 ~ 15분 정도의 제법 긴 시간이 지나면, EKS cluster "greenhead-cluster" in "ap-northeast-2" region is ready라는 로그와 함께 클러스터 생성이 완료된다.
AWS EKS 대시보드에서도 클러스터가 생성된 것을 확인할 수 있다!
솔직히, 단순 실습용으로 EKS를 다루기에는 요금이 너무 비쌉니다! 표준 지원 버젼 기준으로 클러스터만 1달 사용하는데만 비용이 약 72$입니다.
여기서 추가로 EC2, 로드밸런서 등의 요금까지 더해지면, 최대한 낮게 잡아도 150$ 이상은 나오게 됩니다.

내게 청구된 AWS 2월 비용 80만원… (다행히도 지금은 환불되었음)
제 글을 보고 따라해보실 분은 솔직히 없으리라 생각하지만, 만약 k8s를 실습해보고 싶으신 비 devOps 개발자분은 EKS 보다는 docker-desktop의 k8s 기능 혹은 minikube 등을 사용할 것을 추천합니다.
이제는 k8s 클러스터에 리소스를 정의하고, 해당 리소스를 클러스터에 배포해보자.
.kubernetes
├── base
│ ├── deployment.yaml
│ └── service.yaml
└── overlays
├── gateway.yaml
└── virtual-service.yaml위와 같이 프로젝트 루트 경로에 .kubernetes 폴더를 생성하고 base, overlays로 나누어 매니페스트 파일을 작성할 것이다.
작성한 매니페스트 파일을 클러스터에 배포하기 위해서는 kubectl이라는 CLI 도구를 사용해야 하므로 지금 미리 설치해 두도록 하자. 맥북을 사용한다면 eksctl과 마찬가지로 Homebrew를 통해 간단히 설치할 수 있다.
디플로이먼트Deployment를 설명하기 전에 먼저 파드Pod에 대해 간단하게 이해할 필요가 있다.
파드는 k8s에서 가장 작은 배포 단위이며, 하나 이상의 컨테이너로 구성된다. 파드를 직접 배포해도 컨테이너를 관리할 수 있지만, 만약 문제가 생겨 파드가 종료되면 해당 파드는 그대로 유실되며, 재생성되지 않는다!
파드는 직접 사용하기에는 불편한 점이 많은데, 이런 불편함을 해결하기 위해 컨트롤러Controller 객체를 사용한다. 컨트롤러 객체는 파드 등의 다른 리소스를 관리하는 k8s 리소스를 뜻하며, 대표적으로 디플로이먼트가 있다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: green-blog-co # Deployment 이름
spec:
selector:
matchLabels:
app: green-blog-co # Deployment가 Pod를 선택할 때 사용할 label
template:
metadata:
labels:
app: green-blog-co # Deployment가 생성할 Pod에 부여할 label
spec:
containers:
- name: green-blog-co
image: 971490215356.dkr.ecr.ap-northeast-2.amazonaws.com/green-blog-co # ECR에 업로드된 이미지 주소
ports:
- containerPort: 9000위 매니페스트 파일에 정의된 Deployment는 다음과 같은 역할을 한다.
spec.selector.matchLabels
app: green-blog-co 레이블을 가진 파드를 디플로이먼트가 관리한다. 해당 레이블을 가진 파드가 디플로이먼트가 유지해야 할 파드의 개수보다 적어지거나 많아지는 경우, 디플로이먼트는 파드의 개수를 조정하여 일치시킨다.spec.template: 디플로이먼트가 파드를 생성할 때 사용할 템플릿을 정의한다.
spec.template.metadata.labels: 파드에 부여할 레이블을 정의한다. spec.selector.matchLabels 필드에 정의된 레이블과 일치해야 하므로, app: green-blog-co로 작성한다.spec.template.spec.containers: 파드에 포함될 컨테이너를 정의한다. 여기서는 green-blog-co라는 이름의 컨테이너를 생성하며, 컨테이너를 생성할 때 사용할 이미지는 ECR에 업로드된 것을 사용한다. 마지막으로, 컨테이너가 사용할 포트는 9000번 포트로 설정한다.파드에 접근하기 위해서는 기본적으로 해당 파드의 IP 주소를 알아야 한다. 하지만 앞서 설명했듯 파드는 k8s의 가장 작은 배포 단위로, 노드에서 에러 발생 혹은 디플로이먼트 리스타트 등으로 기존 파드가 새로운 파드로 대체되는 경우가 제법 흔하다.
이때 파드는 생성될 때마다 IP 주소가 변경되므로, “특정 파드에 접근하기 위해 해당 파드의 IP 주소를 직접 사용하는 것”은 좋은 방식이 아니다. 그리고 실제로 그 누구도 이런 방식을 사용하지 않을 것이다.
언제든지 다른 것으로 바뀔 수 있는 리소스Pod에 접근하기 위해 서비스Service라는 k8s 리소 스를 사용한다. 서비스는 자신만의 IP 주소를 가지며, 해당 IP 주소로 요청을 보내면 서비스가 관리하는 파드의 실제 IP 주소로 요청이 연결된다.
또한, k8s 클러스터에는 전용 DNS 서버가 있다. 이 DNS 서버는 서비스 이름을 IP 주소로 변환해주는 역할을 한다. 최종적으로, 서비스 이름을 통해 파드에 접근할 수 있다.
apiVersion: v1
kind: Service
metadata:
name: green-blog-co-service
spec:
ports:
- port: 3000
targetPort: 9000
selector:
app: green-blog-co위 매니페스트 파일에 정의된 Service는 다음과 같은 역할을 한다.
서비스 이름 green-blog-co-service의 3000번 포트로 요청이 들어오면, 해당 요청을 app: green-blog-co 레이블을 가진 파드의 9000번 포트로 전달한다.
이번에는 eksctl을 사용하여 클러스터를 생성하고, k8s 리소스 중 가장 기본적인 Deploymnet와 Service를 정의하는 매니페스트 파일을 작성해 보았다.
다음에는 overlays 폴더 내부에 위치한 gateway.yaml과 virtual-service.yaml에 대해 다뤄볼 건데, 이 두 파일은 Istio라는 도구를 다루기 위한 매니페스트 파일이다.
해당 작업을 위해서는 매니페스트 파일을 정의하는 것 외에도 별도로 해주어야 하는 것이 많으므로, 내용이 길어질 수 있어 여기서부터는 다음 포스트에서 다루도록 하겠다.. 🙇