[CloudNeta] EKS 워크샵 스터디 (9) - 부록: 코드 살펴보기 (GenAI on EKS)
이번 배포는 워크샵이 참조하는 awslabs/ai-on-eks 공개 리포지터리에서 살펴볼 예정입니다. 먼저 코드를 살펴볼까요.
➜ tree infra/workshops/genai-on-eks
infra/workshops/genai-on-eks
├── README.md
├── genai-workshop-architecture.png
├── install.sh
└── terraform
├── blueprint.tfvars
├── grafana-dashboards/
├── grafana.tf
├── pull.tf
└── s3-workshop.tf
워크샵 폴더는 자체 모듈을 가지지 않고 베이스 모듈(infra/base/terraform/)을 통째로 복사한 뒤 워크샵 전용 파일을 덮어쓰는 방식으로 동작합니다. infra/base/terraform/은 ai-on-eks 리포가 공통으로 쓰는 EKS·Karpenter·관측 스택 베이스로, EKS, VPC, ArgoCD, Karpenter, kube-prometheus-stack, AMP, Neuron, NVIDIA GPU Operator, EFA 등 50여 개 .tf 파일이 들어 있고, 활성화 여부는 모두 variables.tf의 토글 변수로 제어됩니다. 워크샵은 자기 입맛에 맞게 토글만 잡고 베이스를 호출하는 셈입니다.
코드를 살펴봅시다
1. install.sh: 베이스를 복사해 워크샵 입맛으로 덮기
워크샵의 진입점입니다. 코드는 다음 한 줄짜리 흐름입니다.
#!/bin/bash
# Copy the base into the folder
mkdir -p ./terraform/_LOCAL
cp -r ../../base/terraform/* ./terraform/_LOCAL
cp ./terraform/s3-workshop.tf ./terraform/_LOCAL/
cp ./terraform/pull.tf ./terraform/_LOCAL/
cp ./terraform/grafana.tf ./terraform/_LOCAL/
cp -r ./terraform/grafana-dashboards ./terraform/_LOCAL/
cp ./terraform/blueprint.tfvars ./terraform/_LOCAL/
cd terraform/_LOCAL
source ./install.sh
요약하면:
terraform/_LOCAL/을 만든다 (작업 디렉터리)- 베이스 모듈을 통째로 복사한다 (
../../base/terraform/*) - 워크샵 전용 4개 파일(
s3-workshop.tf·pull.tf·grafana.tf·grafana-dashboards/)과blueprint.tfvars를 덮어쓴다 _LOCAL안에 들어가 베이스의install.sh를 실행한다
베이스를 직접 수정하지 않고 덮어쓰기 패턴으로 가는 이유는, 같은 베이스 위에 여러 워크샵·청사진을 얹어 운영하기 위해서입니다. 워크샵 전용 파일 4개와 토글 변수 파일 1개만 보면 "이 워크샵이 베이스와 다른 점"이 한눈에 들어옵니다.
2. 베이스 install.sh: 실제 terraform apply의 실행자
_LOCAL/install.sh는 베이스 모듈이 제공하는 실행 스크립트로, module을 단계별로 target apply한 뒤 마지막에 전체 apply하는 방식입니다.
targets=(
"module.vpc"
"module.eks"
"module.karpenter"
"module.argocd"
)
terraform init -upgrade
TERRAFORM_COMMAND="terraform apply -auto-approve"
if [ -f "../blueprint.tfvars" ]; then
TERRAFORM_COMMAND="$TERRAFORM_COMMAND -var-file=../blueprint.tfvars"
fi
for target in "${targets[@]}"; do
$TERRAFORM_COMMAND -target="$target"
done
# Final apply to catch any remaining resources
$TERRAFORM_COMMAND
핵심 포인트는 두 가지입니다.
- 순차 target apply: VPC → EKS → Karpenter → ArgoCD 순으로 의존성을 안전하게 풀어 가며 적용합니다. 한 번에
terraform apply만 던지면 EKS-Karpenter-ArgoCD 사이의 IRSA·OIDC·CRD 의존성 때문에 첫 회 실패하기 쉽습니다. blueprint.tfvars자동 감지: 워크샵이 둔blueprint.tfvars가 있으면-var-file로 자동 결합합니다. 별도 옵션 없이 워크샵 토글이 그대로 반영됩니다.
3. blueprint.tfvars: 워크샵 토글의 전부
워크샵이 베이스 모듈을 자신만의 모양으로 만드는 부분은 이 파일 한 장에 다 들어 있습니다.
# Cluster
name = "genai-workshop"
region = "us-east-2"
# EKS
enable_eks_auto_mode = true
enable_cluster_addons = {
metrics-server = false
amazon-cloudwatch-observability = false
}
# Observability
enable_kube_prometheus_stack = true
enable_grafana_operator = true
enable_amazon_prometheus = true
enable_nvidia_dcgm_exporter = false
kube_prometheus_stack_namespace = "monitoring"
grafana_service_port = 3000
grafana_admin_password = "notforproductionuse"
# Model Storage (S3)
enable_s3_models_storage = true
s3_models_bucket_create = false
s3_models_additional_buckets = ["genai-models-*"]
s3_models_sync_sa = "model-storage-sa"
| 분류 | 설정 | 의미 |
|---|---|---|
| 클러스터 | name = "genai-workshop", region = "us-east-2" |
클러스터 이름·리전 고정 |
| EKS | enable_eks_auto_mode = true |
노드 정의를 EKS에 위임 (Karpenter NodePool 수동 선언 없음) |
| EKS | metrics-server, amazon-cloudwatch-observability 모두 false |
관측 스택은 Prometheus/Grafana 축으로 단일화 |
| 관측 | kube_prometheus_stack, grafana_operator, amazon_prometheus 모두 true |
3층(셀프호스트 수집 + AMP 원격 쓰기 + Grafana Operator) |
| 관측 | nvidia_dcgm_exporter = false |
워크샵 데모 단순화를 위해 비활성 (실 운영에서는 true가 권장) |
| 스토리지 | enable_s3_models_storage = true, s3_models_sync_sa = "model-storage-sa" |
모델 가중치를 S3로 두고 Pod에서 마운트 |
grafana_admin_password = "notforproductionuse"변수 값 자체가 "운영용 아님"을 명시합니다. 데모/실습 외에는 절대 그대로 쓰면 안 되고, AWS Secrets Manager 또는 SSM Parameter Store에서 주입하는 패턴으로 바꿔야 합니다.
Part 1의 EKSworkshop AI/ML 트랙이 neuron·aiml 같은 Karpenter NodePool을 직접 선언했던 것과 정반대로, 이 워크샵은 Auto Mode를 켜고 노드 정의를 EKS에 위임합니다. NodePool requirement처럼 사전 제약이 없으므로 비용 알람과 인스턴스 사용 리포트가 사후 가드레일 역할을 합니다.
4. s3-workshop.tf: 모델 가중치를 위한 S3 + CSI 드라이버
워크샵 전용으로 추가되는 14개 리소스로, "모델 가중치를 어디에 두고 Pod에서 어떻게 마운트할지"를 풀어둔 파일입니다.
S3 (6개)
| 리소스 | 역할 |
|---|---|
aws_s3_bucket.model_storage |
모델 저장 버킷 |
aws_s3_bucket_versioning.model_storage |
버저닝 활성 |
aws_s3_bucket_server_side_encryption_configuration.model_storage |
AES256 SSE |
aws_s3_bucket_public_access_block.model_storage |
모든 공개 액세스 차단 |
aws_s3_bucket_lifecycle_configuration.model_storage |
30일 후 IA 전환, 미완료 멀티파트 7일 정리 |
aws_s3_object.model_prefix |
Ministral 모델 폴더(prefix) 생성 |
IAM (3개)
| 리소스 | 역할 |
|---|---|
aws_iam_role.s3_csi_driver_role |
Mountpoint S3 CSI 드라이버용 Role |
aws_iam_policy.s3_csi_driver_policy |
ListBucket/GetObject/PutObject/DeleteObject |
aws_iam_role_policy_attachment.s3_csi_driver |
위 정책을 Role에 연결 |
EKS 통합 (3개)
| 리소스 | 역할 |
|---|---|
aws_eks_addon.s3_csi_driver |
AWS Mountpoint S3 CSI 드라이버 애드온 |
aws_eks_pod_identity_association (×2) |
S3 CSI 컨트롤러·드라이버에 Role 매핑 |
Kubernetes (3개)
| 리소스 | 역할 |
|---|---|
kubectl_manifest.s3_sync_service_account |
model-storage-sa ServiceAccount |
kubectl_manifest.mistral_model_pv |
S3를 백엔드로 한 PV |
kubectl_manifest.mistral_model_pvc |
PV에 바인딩되는 PVC |
Mountpoint S3 CSI 드라이버는 S3 버킷을 마치 POSIX 파일 시스템처럼 마운트해 줍니다. 워크샵의 vLLM Pod는 PVC를 마운트하는 평범한 Deployment지만, 그 뒤가 S3라는 점이 인프라/모델 라이프사이클을 분리하는 핵심입니다.
5. pull.tf: 모델 가중치를 S3로 끌어오는 Job
kubectl_manifest.mistral_model_download 하나의 리소스로 구성된 파일입니다. 하는 일은 명확합니다.
- 이미지: Hugging Face 모델 다운로드 + AWS CLI가 들어간 컨테이너
- 모델:
mistralai/Ministral-3-8B-Instruct-2512를/tmp/mistral로 받음 - 파일 필터링:
.json,.txt,.md,.model,consolidated.safetensors만 선택 (불필요한 캐시·예제 가중치 제외) - 업로드:
/tmp/mistral→s3://<model-storage-bucket>/...(.cache제외) - 리소스: requests
cpu: 2 / mem: 4Gi, limitscpu: 4 / mem: 8Gi - Job 정책: 최대 3회 재시도, 2시간 타임아웃, 완료 후 1일 뒤 자동 삭제
Terraform이 적용된 직후 한 번만 동작해야 하는 작업이라 Job으로 묶었습니다. 다만 본문에서 강조했듯 모델 라이프사이클은 Terraform 바깥에 두는 게 운영 환경에서는 안전합니다. 워크샵 수준에서 "한 번에 모든 게 떠 있는 상태"를 만드는 편의 장치로 보세요.
6. grafana.tf: vLLM·Ray·DCGM 대시보드 등록
이 파일은 AWS 리소스를 만들지 않습니다. Grafana Operator 기반으로 kubectl manifest만 생성합니다.
1) Grafana 인스턴스
| 리소스 | 역할 |
|---|---|
kubectl_manifest.external_grafana |
기존 kube-prometheus-stack의 Grafana를 가리키는 Grafana CRD |
2) 대시보드 ConfigMap (5개)
grafana-dashboards/ 폴더의 JSON 파일을 ConfigMap으로 묶습니다.
| ConfigMap | 대시보드 |
|---|---|
vllm-grafana-dashboard-config |
vLLM 추론 메트릭 |
ray-grafana-default-dashboard-config |
Ray 기본 |
ray-grafana-serve-dashboard-config |
Ray Serve 라우터 |
ray-grafana-serve-deployment-dashboard-config |
Ray Serve deployment |
dcgm-dashboard-config |
NVIDIA DCGM (GPU 메트릭) |
3) GrafanaDashboard CRD (5개)
Grafana Operator가 위 ConfigMap을 읽어 실제 Grafana에 대시보드로 띄워 줍니다.
enable_amazon_prometheus = true일 때, 대시보드 JSON 안의 Prometheus 데이터소스 참조가 Amazon Managed Prometheus를 가리키도록 변환됩니다. 셀프호스트 Prometheus를 쓰는 다른 환경에 그대로 옮겨도 데이터소스만 갈아 끼우면 동작합니다.
blueprint.tfvars에서 enable_nvidia_dcgm_exporter = false로 두었기 때문에, DCGM 대시보드는 떠 있어도 메트릭이 들어오지 않습니다. 실 운영 도입 시 가장 먼저 켜야 할 토글입니다.
구동해봅시다
사전 도구 버전
| 도구 | 최소 버전 |
|---|---|
| AWS CLI | ≥ 2.32.8 |
| Terraform | ≥ 1.3.2 |
| kubectl | 최신 |
| Docker | 최신 |
배포
리포 클론 후 infra/workshops/genai-on-eks/로 들어가 install.sh를 실행합니다. 약 20~25분이 걸립니다.
git clone https://github.com/awslabs/ai-on-eks.git
cd ai-on-eks/infra/workshops/genai-on-eks
./install.sh
배포가 끝났다면 kubeconfig를 갱신합니다.
aws eks update-kubeconfig --region us-east-2 --name genai-workshop
비용 가드와 정리
GPU 노드가 따라붙는 워크샵이라 켜둔 만큼 비용이 발생합니다. 실습이 끝나면 반드시 정리합니다.
cd terraform/_LOCAL
./cleanup.sh
베이스의 cleanup.sh는 단순 terraform destroy가 아니라 다음을 순서대로 처리합니다.
- RayJob/RayService 선삭제 (Ingress/Service가 남으면 destroy가 멈추므로)
- Auto Mode 클러스터라면
aws eks update-cluster-config로 내장 NodePool을 비활성 (노드 드레인 안전 확보) - 모든
nodepool리소스 삭제 kubectl_manifest.*리소스 일괄 destroy (LB Controller 제외)- 나머지 리소스 destroy
- PVC가 만든 EBS 볼륨을 태그 기반으로 추가 삭제
베이스 cleanup.sh는 EKS 클러스터 삭제와 VPC 라우트 삭제가 동시에 일어나면서 노드가 네트워크에서 고립되는 시나리오를 방지하기 위해 노드 드레인을 먼저 강제합니다. 직접 terraform destroy만 던지면 종종 막히는 지점이라 이 스크립트를 그대로 쓰는 편이 안전합니다.