[챌린지 #1] 게임 서버 K8s 배포 - Part 3: Deployment

3 minute read

🎯 핵심 개념

이제 실제로 서비스를 띄워볼 차례다. K8s에서 애플리케이션을 실행하려면 Deployment를 만들어야 한다.

Deployment는 뭘까? 레스토랑 주방으로 비유해보자. 주방장(Deployment)이 요리사(Pod) 몇 명을 고용할지, 어떤 레시피(컨테이너 이미지)를 쓸지, 재료(리소스)는 얼마나 줄지 결정한다. 요리사 한 명이 아프면 자동으로 새 요리사를 뽑아주기도 한다.

💡 왜 Deployment를 쓰나

Pod를 직접 만들 수도 있지만, 실무에서는 거의 쓰지 않는다.

# ❌ Pod 직접 생성 (실무에서 안 씀)
apiVersion: v1
kind: Pod
metadata:
  name: lobby-pod
spec:
  containers:
  - name: lobby
    image: nginx:alpine

이렇게 하면 Pod가 죽었을 때 자동으로 다시 안 뜬다. 수동으로 다시 만들어야 한다.

# ✅ Deployment 사용 (실무 방식)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: game-lobby
spec:
  replicas: 3  # Pod 3개 유지

Deployment는 Pod가 죽으면 자동으로 다시 띄워준다. 업데이트할 때도 하나씩 교체해서 무중단 배포가 가능하다.

📌 주요 특징

게임 로비 Deployment 작성

apiVersion: apps/v1
kind: Deployment
metadata:
  name: game-lobby
  namespace: game-prod
  labels:
    app: game-lobby
    tier: frontend
spec:
  replicas: 3  # Pod 3개 실행
  selector:
    matchLabels:
      app: game-lobby
  template:
    metadata:
      labels:
        app: game-lobby
        tier: frontend
    spec:
      containers:
      - name: lobby-server
        image: nginx:alpine
        ports:
        - containerPort: 80
          name: http
        envFrom:
        - configMapRef:
            name: game-common-config
        - configMapRef:
            name: lobby-config
        resources:
          requests:
            memory: "128Mi"
            cpu: "100m"
          limits:
            memory: "256Mi"
            cpu: "200m"
        livenessProbe:
          httpGet:
            path: /
            port: 80
          initialDelaySeconds: 10
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /
            port: 80
          initialDelaySeconds: 5
          periodSeconds: 5

하나씩 뜯어보자.

replicas: 몇 개 띄울까

replicas: 3

Pod를 3개 띄우겠다는 뜻이다. 게임 로비는 트래픽이 많으니 여러 개 띄워서 부하를 분산한다.

하나가 죽어도 나머지 2개가 서비스를 계속한다. 그 사이에 K8s가 새 Pod를 자동으로 띄워서 다시 3개를 맞춘다.

resources: 리소스 할당

resources:
  requests:
    memory: "128Mi"  # 최소 보장
    cpu: "100m"
  limits:
    memory: "256Mi"  # 최대 사용
    cpu: "200m"

requests: 이 Pod가 최소한 필요한 리소스다. K8s는 이만큼 여유가 있는 노드에 Pod를 배치한다.

limits: 이 Pod가 최대로 쓸 수 있는 리소스다. 이걸 넘으면 컨테이너가 강제로 재시작된다.

Deployment 상세 정보

식당으로 비유하면, requests는 “최소 테이블 2개는 필요해요”, limits는 “테이블 4개 넘게는 안 써요”다.

probes: 건강 체크

livenessProbe: Pod가 살아있나 확인

livenessProbe:
  httpGet:
    path: /
    port: 80
  initialDelaySeconds: 10  # 10초 후부터 체크
  periodSeconds: 10        # 10초마다 체크

이게 실패하면 Pod를 죽이고 새로 띄운다. 애플리케이션이 멈춰있을 때 자동으로 복구하는 장치다.

readinessProbe: Pod가 트래픽 받을 준비됐나 확인

readinessProbe:
  httpGet:
    path: /
    port: 80
  initialDelaySeconds: 5
  periodSeconds: 5

이게 실패하면 Service에서 이 Pod를 제외한다. 초기화 중이거나 과부하 상태일 때 트래픽을 안 보내는 거다.

배포 및 확인

# 배포
kubectl apply -f 03-lobby-deployment.yaml

# Pod 상태 확인
kubectl get pods -n game-prod

# 상세 정보
kubectl describe deployment game-lobby -n game-prod

# 로그 확인
kubectl logs -f deployment/game-lobby -n game-prod

정상이면 이렇게 뜬다.

Pod 3개 실행

NAME                          READY   STATUS    RESTARTS   AGE
game-lobby-7d9f8c4b5-abc12    1/1     Running   0          1m
game-lobby-7d9f8c4b5-def34    1/1     Running   0          1m
game-lobby-7d9f8c4b5-ghi56    1/1     Running   0          1m

⚠️ 주의사항

selector와 labels 일치

spec:
  selector:
    matchLabels:
      app: game-lobby  # 이거랑
  template:
    metadata:
      labels:
        app: game-lobby  # 이거 같아야 함

이 둘이 안 맞으면 Deployment가 Pod를 못 찾는다. 처음엔 헷갈리는데, selector는 “이 라벨 가진 Pod 관리해줘”, template의 labels는 “내가 만드는 Pod 라벨이야”라는 뜻이다.

requests vs limits 설정

# ❌ 이렇게 하지 말자
requests:
  cpu: "100m"
limits:
  cpu: "10000m"  # 터무니없이 큼

limits를 너무 크게 잡으면 다른 Pod가 리소스를 못 쓴다. requests를 너무 작게 잡으면 Pod가 제대로 안 돌아간다.

실무에서는 보통 limits를 requests의 2배 정도로 잡는다.

image 태그 명시

# ❌ 이렇게 하지 말자
image: nginx

# ✅ 이렇게 하자
image: nginx:alpine

태그 없이 쓰면 latest가 적용되는데, 이건 버전이 계속 바뀐다. 프로덕션에서는 정확한 버전을 명시해야 한다.

정리

Deployment로 게임 로비를 띄웠다. replicas로 개수를 정하고, resources로 리소스를 할당하고, probes로 건강을 체크했다.

다음 글에서는 이 Pod들을 외부에서 접근할 수 있게 Service를 만들어볼 예정이다.

💭 생각해볼 점

Q: replicas를 10개로 늘렸는데 노드가 2개밖에 없으면 어떻게 될까?

힌트: K8s는 가능한 한 Pod를 여러 노드에 골고루 분산시킨다. 하지만 노드 리소스가 부족하면 일부 Pod는 Pending 상태로 남는다. 이때는 노드를 추가하거나, Pod의 requests를 줄여야 한다.

🎯 추가 학습

  • Rolling Update와 Recreate 배포 전략 차이
  • Pod Disruption Budget으로 안전한 업데이트
  • startupProbe는 언제 쓰나

🔗 참고