Kubernetes에서 애플리케이션을 구동하면 가장 작은 단위인 파드(Pod)가 생성된다.
하지만 파드는 일시적인 존재로, 장애나 업데이트 시 자동으로 복구되거나 관리되지 않는다는 한계가 있다.


실제 서비스 운영 환경에서는 파드의 수 유지, 무중단 배포, 자동 롤백과 같은 기능이 필요하다.

이 문제를 해결하기 위해 Kubernetes는 Deployment라는 컨트롤러 리소스를 제공한다.


Deployment는 파드의 생애주기를 자동으로 관리하며, 선언적 방식으로 애플리케이션의 상태를 유지한다.

이번 실습에서는 다음의 질문을 중심으로 Deployment를 이해하고 적용해보았다:

  • Deployment가 필요한가?
  • 어떤 구조역할을 가지고 있는가?
  • RollingUpdate, Recreate, Blue/Green 전략은 어떻게 다른가?
  • 실시간으로 버전이 교체되는 과정은 어떻게 확인할 수 있는가?
  • 문제가 발생했을 때 롤백은 어떻게 처리되는가?

이를 통해 실무에서의 배포 전략과 파드 관리 방식을 직접 경험하고 정리할 수 있었다.

 

 

Deployment

파드를 지속적으로 원하는 상태로 유지하고, 자동으로 배포, 복구, 업데이트까지 담당하는 컨트롤러 리소스

왜 필요한가

k8s에서 가장 작은 실행 단위는 파드(Pod), 하지만 파드만 수동으로 만들면 문제가 생김

  • 파드가 죽음면 → 자동으로 다시 안 만들어짐
  • 여러 개 띄우려면 → 반복적으로 수동 복제
  • 롤링 업데이트 → 확인해가며 일일이 삭제하면서 다시 띄워야 함

→ 이걸 자동화 해주는 리소스

핵심 역할

  • 파드 수 유지: 설정된 개수를 항상 유지, 죽으면 다시 만들고 반복
  • 롤링 업데이트: 새 버전 이미지로 하나 씩 교체하면서 무중단 배포
  • 롤백: 문제 발생 시 이전 버전으로 즉시 복구(이전 버전 replicas 0으로 대기)
  • 복제: Pod를 원하는 수만큼 자동으로 생성(replicas 설정)
  • 셀프힐링: 노드가 죽거나 파드가 종료돼도 자동으로 만들어줌
### ▶ Deployment
apiVersion: apps/v1
kind: Deployment  # 파드를 여러 개 관리하고 자동으로 유지하는 리소스
metadata:
  namespace: anotherclass-123  # 이 Deployment는 해당 네임스페이스에 속함
  name: api-tester-1231  # Deployment 이름 (kubectl 명령에서 사용됨)
  labels:  # 라벨: 이 리소스를 식별하거나 그룹화할 때 사용
    part-of: k8s-anotherclass
    component: backend-server
    name: api-tester
    instance: api-tester-1231
    version: 1.0.0
    managed-by: dashboard
    
spec:  # spec: 이 리소스가 실제로 어떻게 동작해야 하는지 정의하는 부분
  selector:  # selector: 어떤 파드를 이 Deployment가 관리할지 지정함
    matchLabels:  # matchLabels: 라벨이 일치하는 파드를 대상으로 함
      part-of: k8s-anotherclass
      component: backend-server
      name: api-tester
      instance: api-tester-1231
  replicas: 2  # 파드를 2개 유지함 (장애가 나면 다시 생성)
  strategy:
    type: RollingUpdate  # 무중단 배포 전략. 순차적으로 새 파드를 띄우고 기존 파드를 종료함
    
  template:  # 이 템플릿 안에 파드 정의가 들어감
    metadata:
      labels:  # 파드에도 라벨을 붙여서 selector와 연결됨
        part-of: k8s-anotherclass
        component: backend-server
        name: api-tester
        instance: api-tester-1231
        version: 1.0.0
    spec:  # 이 파드 안의 컨테이너 구성 정의
      nodeSelector:
        kubernetes.io/hostname: k8s-master  # 이 파드는 k8s-master 노드에만 배치됨
      containers:
        - name: api-tester-1231  # 컨테이너 이름
          image: 1pro/api-tester:v1.0.0  # 사용할 컨테이너 이미지
          ports:
          - name: http
            containerPort: 8080  # 이 컨테이너가 리슨하는 포트
          envFrom:
            - configMapRef:
                name: api-tester-1231-properties  # 환경변수를 ConfigMap에서 불러옴
          
          startupProbe:  # 시작 시 정상 상태인지 확인하는 probe
            httpGet:
              path: "/startup" # 어떤 경로를 검사할지
              port: 8080 #어떤 포트로 요청할지
            periodSeconds: 5 # 몇 초마다 검사할지
            failureThreshold: 36 # 몇 번 실패하면 실패로 간주할지
          
          readinessProbe:  # 트래픽을 받아도 되는 상태인지 체크
            httpGet:
              path: "/readiness"
              port: 8080
            periodSeconds: 10
            failureThreshold: 3
          
          livenessProbe:  # 살아 있는지 체크. 실패 시 컨테이너 재시작
            httpGet:
              path: "/liveness"
              port: 8080
            periodSeconds: 10
            failureThreshold: 3
          # 이 컨테이너을 돌릴 때 자원을 얼마나 쓸지
          resources:  # 자원 요청/제한 설정
            requests:  # 최소 자원 (보장)
              memory: "100Mi"
              cpu: "100m"
            limits:  # 최대 자원 (초과 시 제한됨)
              memory: "200Mi"
              cpu: "200m"
          # 볼륨을 어디에 마운트할지
          volumeMounts:
            - name: files
              mountPath: /usr/src/myapp/files/dev  # 파드 내 파일이 마운트될 디렉토리
            - name: secret-datasource
              mountPath: /usr/src/myapp/datasource  # DB 정보가 담긴 Secret을 마운트
      # 어떤 볼륨을 사용할지
      volumes:
        - name: files
          persistentVolumeClaim:
            claimName: api-tester-1231-files  # PVC와 연결
        - name: secret-datasource
          secret:
            secretName: api-tester-1231-postgresql  # Secret과 연결

 

주요 필드 설명

1. 메타데이터 관련 → 이 리소스가 뭔지 정의

필드  설명
apiVersion: apps/v1 Deployment 리소스는 apps API 그룹에 속하며, 현재 버전은 v1
kind: Deployment 생성할 리소스의 종류가 Deployment
metadata.name 이 Deployment의 이름. kubectl get deployment 등 명령어에서 사용
metadata.namespace 이 Deployment가 속한 네임스페이스
metadata.labels 이 리소스를 다른 리소스와 연관시키거나 선택하기 위한 식별자들. 모니터링, 관리 도구에서도 자주 사용됨

2. 스펙(spec) → 전체 리소스 제어

2.1 파드 복제 및 유지 전략

필드  설명
replicas: 2 항상 2개의 파드를 유지함. 하나 죽으면 자동으로 다시 생성됨
strategy.type: RollingUpdate 무중단 배포 방식. 새로운 파드를 하나씩 띄우고, 기존 파드를 종료함

 

2.2 selector – 어떤 파드를 관리할지 지정

필드  설명
selector.matchLabels 이 라벨이 일치하는 파드만 이 Deployment가 관리함
template.metadata.labels 실제 생성될 파드에 부여할 라벨. 위 selector와 반드시 일치해야 함

3. 파드 템플릿 정의(template) → 자신이 관리할 파드의 설정

3.1 스케줄링 관련

필드  설명
nodeSelector 특정 노드(k8s-master)에만 이 파드를 배치하도록 지정함. 조건 기반 노드 배치 제어

3.2 컨테이너 정의 (containers:)

 

필드  설명
name 컨테이너 이름. Pod 내에서 유일해야 함
image 사용할 Docker 이미지 이름과 태그
ports.containerPort 컨테이너 내부에서 서비스가 사용하는 포트. 예: 8080

3.3 환경변수 설정

envFrom:
  - configMapRef:
      name: api-tester-1231-properties
  • Secret을 한꺼번에 파일의 형태로 환경변수로 주입
  • spring.datasource.url=${DB_URL} 이런 식으로 코드에서 사용할 수 있음

3.4 헬스체크(프로브)

필드  설명
startupProbe 컨테이너가 정상적으로 시작됐는지 확인. 시작 전에만 체크
readinessProbe 트래픽을 받아도 되는 상태인지 체크. 실패하면 서비스에서 제외됨
livenessProbe 컨테이너가 살아 있는지 주기적으로 검사. 실패하면 컨테이너 재시작

3.5 자원 제한

resources:
  requests:
    cpu: "100m"
    memory: "100Mi"
  limits:
    cpu: "200m"
    memory: "200Mi"

 

필드  설명
requests 최소 보장 자원. 이만큼은 꼭 주겠다
limits 최대 사용 가능 자원. 이 이상은 제한됨
예: CPU 100m → 0.1 core, Memory 100Mi → 약 100MB  

3.6 볼륨 마운트

volumeMounts:
  - name: files
    mountPath: /usr/src/myapp/files/dev
  • 앞에서 배운 PVC로 PV를 마운트함
  • 파일 기반 설정 또는 파일 저장에 사용

4. 📦 volumes: 정의

필드  설명
persistentVolumeClaim.claimName PVC를 참조하여 외부 저장소를 연결함
secret.secretName Secret을 컨테이너 내부 경로에 마운트할 수 있도록 지정함

여기서 사용할 PVC랑 secret 정의함

 

추가) Deployment 전략(strategy)

spec.starategy는 어떻게 파드를 업데이트할지를 정하는 것

새 버전의 이미지로 바꿀 때 기존 파드를 어떤 방식으로 교체할 것인지를 지정하는 전략

전략은 Recreate와 기본값인 RollingUpdate, k8s 기본 기능은 아닌 Blue/Green이 있음

  1. Recreate 전략→ 서비스 중단이 필연적으로 발생
  2. 기존 파드를 전부 종료하고 새 파드를 다시 생성하는 방식
  3. RollingUpdate 전략→ 서비스 중단 없이 즉 무중단 배포 가능
    • maxSurge: 새 파드를 몇 개 더 띄울 수 있는지
    • maxUnavailable: 동시에 꺼질 수 있는 기존 파드 수
  4. 최대 150%의 리소스 사용량 증가가 있음 → 기존의 것을 대체하기 위해 두 버전이 동시에 존재하는 타이밍
  5. 기존 파드를 하나씩 종료하면서 동시에 새 파드를 생성
  6. Blue/Green 전략2개의 버전을 완전히 띄우야 하니 리소스 사용량 200% 증가
  7. 완전히 새로운 버전을 새롭게 배포하고 → 트래픽을 구버전에서 신버전으로 차츰차츰 옮기는 방식

만약 template의 어떠한 값이라도 바뀌면 바로 새 ReplicaSet을 만듬 그 후 정해진 전략으로 배포를 함

→ 템플릿이 바뀌었다는건 Pod를 생성하는 방식이 달라졌다는 것이고 그거에 맞춰 다른 파드도 변경해야하니까.

 

실습

 

지금 하나는 V1 하나는 V2로 변경된 상황

V2 생성 중일 때 해당 파드는 V1으로 동작 중 → 이게 롤링업뎃

 

1. RollingUpdate 하기

// 1) HPA minReplica 2로 바꾸기 (이전 강의에서 minReplicas를 1로 바꿔놨었음)
kubectl patch -n anotherclass-123 hpa api-tester-1231-default -p '{"spec":{"minReplicas":2}}'

// 1) 그외 Deployment scale 명령
kubectl scale -n anotherclass-123 deployment api-tester-1231 --replicas=2
// 1) edit로 모드로 직접 수정
kubectl edit -n anotherclass-123 deployment api-tester-1231

// 2) 지속적으로 Version호출 하기 (업데이트 동안 리턴값 관찰)
while true; do curl http://192.168.56.30:31231/version; sleep 2; echo ''; done; 

// 3) 별도의 원격 콘솔창을 열어서 업데이트 실행 
kubectl set image -n anotherclass-123 deployment/api-tester-1231 api-tester-1231=1pro/api-tester:v2.0.0
kubectl set image -n anotherclass-123 deployment/api-tester-1231

// update 실행 포맷 
// kubectl set image -n <namespace> deployment/<deployment-name> <container-name>=<image-name>:<tag>
kubectl set image -n <namespace> deployment/<deployment-name> <container-name>=<image-name>:<tag>

두 버전이 공존
롤링 업데이트 중인 상황

2. RollingUpdate (maxUnavailable: 0%, maxSurge: 100%) 하기

apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: anotherclass-123
  name: api-tester-1231
spec:
  replicas: 2
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 25% -> 0%  # 수정
      maxSurge: 25% -> 100%      # 수정
kubectl set image -n anotherclass-123 deployment/api-tester-1231 api-tester-1231=1pro/api-tester:v1.0.0

 

이렇게 Blue/Green 처럼 만들 파드들 동시에 다 띄우고 모두가 활성화 됐을 때 과거의 것이 교체가 됨

이렇게 한 번에 교체되는 모습(v2 -> v1)

 

3. Recreate 하기

apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: anotherclass-123
  name: api-tester-1231
spec:
  replicas: 2
  strategy:
    type: RollingUpdate -> Recreate   # 수정
    rollingUpdate:        # 삭제
      maxUnavailable: 0%  # 삭제
      maxSurge: 100%      # 삭제
kubectl set image -n anotherclass-123 deployment/api-tester-1231 api-tester-1231=1pro/api-tester:v2.0.0

모든 파드들이 다 내려가고 서비스 중단됨

그 후 모든 파드가 만들어지고 서비스 재개

4. Rollback

// 이전 버전으로 롤백
kubectl rollout undo -n anotherclass-123 deployment/api-tester-1231

 

마무리

개요의 질문에 대한 답

  •  Deployment가 필요한가?
    • 파드의 수를 자동으로 유지하고, 무중단 배포 및 복구를 지원해 안정적인 애플리케이션 운영을 가능하게 한다.
  • 어떤 구조 역할을 가지고 있는가?
    • Deployment는 템플릿 기반으로 파드를 정의하고, ReplicaSet을 통해 원하는 수의 파드를 지속적으로 관리한다.
  • RollingUpdate, Recreate, Blue/Green 전략은 어떻게 다른가?
    • RollingUpdate는 점진적 교체, Recreate는 전체 종료 후 재생성, Blue/Green은 새 버전 전체를 띄운 뒤 트래픽 전환 방식이다.
  • 실시간으로 버전이 교체되는 과정은 어떻게 확인할 수 있는가?
    • 서비스 endpoint를 주기적으로 호출하면서 버전 응답 값의 변경을 통해 업데이트 상태를 확인할 수 있다.(실습으로 확인)
  • 문제가 발생했을 때 롤백은 어떻게 처리되는가?
    • kubectl rollout undo 명령어를 사용해 이전 버전으로 빠르게 롤백할 수 있다.
    • 빠르게 가능한 이유는 이전 버전의 ReplicaSet을 삭제하지 않고, "replicas:0"으로 보관하고 있기 때문임

 

파드를 자동으로 만들고, 배포하고, 파드 상태도 보는 k8s 운영에 가장 중요한 리소스라고 생각한다.

이번 포스팅을 적으면서 Deployment에 대해서 자세히 알게 됐고 실습을 통해 배포 전략별로 어떻게 동작하는 지 확인했다.

특히 롤링업데이트의 maxUnavailable, maxSurge의 값을 변경하므로써 다양한 배포 전략을 만들 수 있는게 흥미로웠다.

 

또한 롤링 업데이트는 무중단 배포라는 큰 장점이 있지만, 이전 버전의 파드와 새로운 버전의 파드가 공존해야 한다는 문제 때문에 고려할게 많을 것 같고, 그렇다고 blue/green을 하자니 리소스가 2배나 들고, recreate를 하자니 서비스가 중단되어야 하고..

이런 부분을 설계하고 조정하는게 서비스에 상당히 중요한 부분인 것 같다.

+ Recent posts