이코에코(Eco²)/Event Streams & Scaling

이코에코(Eco²) Streams & Scaling for SSE #1: 3-Node Redis Cluster 리소스 프로비저닝

mango_fr 2025. 12. 26. 13:23

 

Kubernetes 클러스터에 Redis 3노드를 추가하는 인프라 프로비저닝 과정을 다룹니다.

목표

항목 Before After
노드 수 1 (k8s-redis) 3 (auth, streams, cache)
인스턴스 t3.medium ×1 t3.medium ×1 + t3.small ×2
vCPU 2 6
메모리 4GB 8GB

Phase 1: Terraform 변경

기존 모듈

# terraform/main.tf (Before)
module "redis" {
  source        = "./modules/ec2"
  name          = "k8s-redis"
  instance_type = "t3.medium"
  # ...
}

3개 모듈로 분리

# terraform/main.tf (After)
module "redis_auth" {
  source        = "./modules/ec2"
  name          = "k8s-redis-auth"
  instance_type = "t3.medium"  # JWT Blacklist 중요도 높음
  node_labels   = { "redis-cluster" = "auth" }
  # ...
}

module "redis_streams" {
  source        = "./modules/ec2"
  name          = "k8s-redis-streams"
  instance_type = "t3.small"
  node_labels   = { "redis-cluster" = "streams" }
  # ...
}

module "redis_cache" {
  source        = "./modules/ec2"
  name          = "k8s-redis-cache"
  instance_type = "t3.small"
  node_labels   = { "redis-cluster" = "cache" }
  # ...
}

Ansible Inventory 템플릿

# terraform/templates/hosts.tpl
[redis]
k8s-redis-auth ansible_host=${redis_auth_public_ip} private_ip=${redis_auth_private_ip} redis_cluster=auth
k8s-redis-streams ansible_host=${redis_streams_public_ip} private_ip=${redis_streams_private_ip} redis_cluster=streams
k8s-redis-cache ansible_host=${redis_cache_public_ip} private_ip=${redis_cache_private_ip} redis_cluster=cache

Phase 2: vCPU 한계 이슈

문제 발생

terraform apply redis-only.plan

Error: creating EC2 Instance: operation error EC2: RunInstances, 
api error VcpuLimitExceeded: You have requested more vCPU capacity 
than your current vCPU limit of 43 allows

vCPU 현황 분석

aws ec2 describe-instances \
  --filters "Name=instance-state-name,Values=running" \
  --query 'Reservations[*].Instances[*].InstanceType' \
  --output text | tr '\t' '\n' | sort | uniq -c
타입 개수 vCPU 소계
t3.xlarge 1 4 4
t3.large 3 2 6
t3.medium 7 2 14
t3.small 9 2 18
합계 20 - 42 vCPU

계산: 42 + 6(새 Redis) = 48 > 43 (한계 초과)

고아 인스턴스 발견

이전 Terraform apply 실패로 생성된 중복 인스턴스:

aws ec2 describe-instances \
  --filters "Name=tag:Name,Values=k8s-redis-*" \
  --query 'Reservations[*].Instances[*].[InstanceId,Tags[?Key==`Name`].Value|[0]]' \
  --output table
Instance ID Name State Terraform State
i-0e9a1d55b934e6990 k8s-redis-cache running 없음 (고아)
i-0d2620f00548d6e87 k8s-redis-streams running 없음 (고아)

해결: 고아 인스턴스 정리

# 1. 고아 인스턴스 종료
aws ec2 terminate-instances --instance-ids \
  i-0e9a1d55b934e6990 \
  i-0d2620f00548d6e87

# 2. 종료 완료 대기
aws ec2 wait instance-terminated --instance-ids \
  i-0e9a1d55b934e6990 \
  i-0d2620f00548d6e87

# 3. Terraform State 정리 (필요시)
terraform state rm module.redis_cache.aws_instance.this
terraform state rm module.redis_streams.aws_instance.this

정리 후 vCPU

타입 개수 vCPU 소계
t3.xlarge 1 4 4
t3.large 3 2 6
t3.medium 6 2 12
t3.small 7 2 14
합계 17 - 36 vCPU

여유: 43 - 36 = 7 vCPU > 6 vCPU (필요) ✅


Phase 3: Terraform Apply

Target 옵션으로 범위 제한

전체 plan 시 AMI 버전 변경으로 모든 노드가 replace 대상이 됨.
-target 옵션으로 Redis 모듈만 적용:

# Plan
terraform plan \
  -var="dockerhub_password=xxx" \
  -target=module.redis_auth \
  -target=module.redis_streams \
  -target=module.redis_cache \
  -out=redis-fresh.plan

# Apply
terraform apply redis-fresh.plan

결과

module.redis_auth.aws_instance.this: Creating...
module.redis_streams.aws_instance.this: Creating...
module.redis_cache.aws_instance.this: Creating...

module.redis_auth.aws_instance.this: Creation complete after 35s [id=i-xxx]
module.redis_streams.aws_instance.this: Creation complete after 36s [id=i-xxx]
module.redis_cache.aws_instance.this: Creation complete after 37s [id=i-xxx]

Apply complete! Resources: 3 added, 0 changed, 0 destroyed.

Inventory 생성

# Terraform Output → Ansible Inventory
terraform output -raw ansible_inventory > ../ansible/inventory/hosts.ini

# 확인
grep -A5 "[redis]" ../ansible/inventory/hosts.ini
[redis]
k8s-redis-auth ansible_host=3.34.128.200 private_ip=10.0.2.43 redis_cluster=auth
k8s-redis-streams ansible_host=3.38.200.174 private_ip=10.0.2.215 redis_cluster=streams
k8s-redis-cache ansible_host=15.165.200.60 private_ip=10.0.2.202 redis_cluster=cache

Phase 4: Ansible 설정

SSH 연결 확인

cd ../ansible
ansible -i inventory/hosts.ini redis -m ping
k8s-redis-auth | SUCCESS => { "ping": "pong" }
k8s-redis-streams | SUCCESS => { "ping": "pong" }
k8s-redis-cache | SUCCESS => { "ping": "pong" }

새 노드 기본 설정

새로 생성된 EC2 인스턴스에는 Docker, containerd, kubeadm이 설치되어 있지 않음.
전용 playbook으로 초기 설정:

ansible-playbook -i inventory/hosts.ini playbooks/setup-new-nodes.yml -l redis
# ansible/playbooks/setup-new-nodes.yml
- name: Prerequisites - OS 설정
  hosts: all
  roles:
    - common  # 커널 모듈, sysctl, swap 비활성화

- name: Docker/Containerd 설치
  hosts: all
  roles:
    - docker  # containerd CRI 설정

- name: Kubernetes 패키지 설치
  hosts: all
  roles:
    - kubernetes  # kubeadm, kubelet, kubectl

결과

TASK [설치 상태 출력] ****
ok: [k8s-redis-auth] => 
  msg:
  - 'containerd: active'
  - 'kubeadm: v1.28.4'
  - 'kubelet: Kubernetes v1.28.4'

Phase 5: 클러스터 조인

Join Token 생성

ssh -i ~/.ssh/sesacthon.pem ubuntu@<master-ip> \
  "kubeadm token create --print-join-command" > /tmp/kubeadm_join_command.sh

Worker Join

ansible-playbook -i inventory/hosts.ini playbooks/03-worker-join.yml \
  -l redis \
  -e kubectl_user=ubuntu

노드 라벨 적용

ansible-playbook -i inventory/hosts.ini playbooks/fix-node-labels.yml \
  -l redis \
  -e kubectl_user=ubuntu

최종 확인

kubectl get nodes -l 'redis-cluster' -o wide
NAME                STATUS   ROLES    AGE   VERSION   INTERNAL-IP
k8s-redis-auth      Ready    <none>   45m   v1.28.4   10.0.2.43
k8s-redis-streams   Ready    <none>   45m   v1.28.4   10.0.2.215
k8s-redis-cache     Ready    <none>   45m   v1.28.4   10.0.2.202

교훈 및 Best Practices

1. AMI Lifecycle 관리

resource "aws_instance" "this" {
  lifecycle {
    ignore_changes = [ami]  # AMI 변경 무시
  }
}

2. vCPU 한계 사전 확인

# 프로비저닝 전 확인 스크립트
CURRENT=$(aws ec2 describe-instances ... | wc -l)
LIMIT=$(aws service-quotas get-service-quota ... | jq '.Quota.Value')
NEEDED=6

if [ $((CURRENT + NEEDED)) -gt $LIMIT ]; then
  echo "⚠️ vCPU 한계 초과 예상"
fi

3. Target 옵션 활용

# 영향 범위 제한
terraform plan -target=module.specific_module

4. State 불일치 복구

# 고아 리소스 정리
terraform state rm <resource>

# 또는 import
terraform import module.xxx.aws_instance.this <instance-id>

참고 자료