TT Lab
시작하기
배우기 러닝패스 코스

KCNA — 쿠버네티스·클라우드 네이티브 입문

파드 하나에서 워크로드 컨트롤러까지

TT Lab 에서 이어서 보기

한 줄 요약

파드는 배포 단위가 아니라 스케줄링 단위입니다. 사람이 직접 만들 일은 거의 없고, 실제로는 컨트롤러가 만들어 줍니다. 어떤 컨트롤러를 고르느냐는 "이 일이 언제 끝나는가"로 갈립니다.

왜 이게 필요했나

컨테이너 하나가 최소 단위였다면 로그 수집기나 프록시 같은 보조 프로세스를 어디에 둘지 애매해집니다. 같은 이미지에 욱여넣으면 12-factor 의 "프로세스 하나에 관심사 하나"가 깨지고, 다른 머신에 두면 localhost 로 통신할 수 없습니다.

파드는 그 사이의 답입니다. 네트워크 네임스페이스(같은 IP·포트 공간)와 볼륨을 공유하는 컨테이너 묶음. 그래서 사이드카는 localhost 로 앱에 붙을 수 있고, 앱과 사이드카는 항상 같은 노드에 함께 스케줄됩니다.

하지만 파드 자체는 자기를 되살리지 않습니다. 노드가 죽으면 그 위의 파드도 같이 사라집니다. 그래서 파드 위에 컨트롤러가 필요합니다.

재생성은 원래 파드의 부활이 아니다

ReplicaSet이 목표 3개를 유지하는 상황이다. 삭제된 파드의 로컬 상태가 저절로 복원되지는 않는다.

  1. 목표와 실제 비교원하는 수는 3, 현재 관리 대상 파드는 2개다.
  2. 컨트롤러 조정부족한 수를 채우도록 새 파드를 만든다.
  3. 새 파드 준비새 UID의 파드가 배치·기동되고 준비 상태에 도달한다.

여기서 구분할 것 컨테이너 재시작과 새 파드 생성은 다른 사건이다. 파드 IP·이름·로컬 파일이 그대로일 것이라 가정하지 않는다.

잠깐, 예측해 보세요 파드 3개가 다시 보인다. 삭제 전 데이터까지 복구됐다는 증거일까?

설명 확인 · 채점 없는 자가 점검

아니다. 개수 복원은 데이터 복원의 증거가 아니다. 필요한 상태는 영속 저장소와 복구 검증으로 따로 지켜야 한다.

근거 문서

어떻게 동작하나

소유 관계의 사슬

Deployment  --(소유)-->  ReplicaSet  --(소유)-->  Pod

Deployment 를 만들면 컨트롤러 매니저가 ReplicaSet 을 만들고, ReplicaSet 이 파드를 만듭니다. 각 오브젝트의 metadata.ownerReferences 에 부모가 적히고, 이 필드가 가비지 컬렉션의 근거가 됩니다. Deployment 를 지우면 ownerReferences 를 따라 ReplicaSet 과 파드가 연쇄적으로 정리됩니다.

Deployment 를 롤아웃하면 왜 ReplicaSet 이 여러 개가 될까요? 이전 버전의 ReplicaSet 을 남겨 두기 때문입니다. 그래서 kubectl rollout undo 가 가능합니다. 되돌린다는 건 옛 ReplicaSet 의 replicas 를 다시 올리는 것에 지나지 않습니다.

워크로드 컨트롤러 고르기

컨트롤러 언제 쓰나 끝나는가
Deployment 무상태 서비스. 웹, API 안 끝난다
StatefulSet 안정된 이름·순서·전용 스토리지가 필요한 것. DB, 큐 안 끝난다
DaemonSet 노드마다 하나씩. 로그 수집기, CNI, 노드 익스포터 안 끝난다
Job 한 번 돌고 끝나는 일. 마이그레이션, 배치 끝난다
CronJob 정해진 시각에 Job 을 만든다 Job 이 끝난다

핵심 판별식은 "이 프로세스는 스스로 끝나는가" 입니다. 끝나는 일에 Deployment 를 쓰면 컨테이너가 종료될 때마다 재시작되어 무한 루프가 됩니다. 그래서 Job 의 파드 템플릿은 restartPolicy 로 Always 를 쓸 수 없습니다.

DaemonSet 은 replicas 필드가 없습니다. 개수를 사람이 정하는 게 아니라 노드 수가 곧 개수이기 때문입니다. 노드를 추가하면 자동으로 하나 더 생깁니다.

셀프힐링은 마법이 아니다

파드를 지우면 다시 생기는 것은 ReplicaSet 컨트롤러가 이렇게 돌기 때문입니다.

  1. 내 셀렉터에 맞는 파드가 지금 몇 개인가?
  2. spec.replicas 는 몇 개인가?
  3. 모자라면 만들고, 남으면 지운다

이름이 같은 파드가 돌아오는 게 아니라 새 파드가 생깁니다. 이름은 매번 달라집니다. 그래서 파드 이름에 의존하는 설계는 언제나 깨집니다.

프로브

readiness 를 안 붙이면 부팅 중인 파드에 트래픽이 꽂히고, liveness 를 너무 공격적으로 잡으면 잠깐 느려진 앱이 계속 재시작되며 상황이 악화됩니다.

현장에서 만나는 모습

저자의 홈랩에서 Cilium 을 설치한 직후 hubble-relay 와 hubble-ui 파드가 Pending 에 머물렀습니다. 이벤트는 0/1 nodes are available: 1 node(s) had untolerated taint(s) 였습니다.

원인은 컨트롤러 종류의 차이였습니다. 컨트롤 플레인 노드에는 node-role.kubernetes.io/control-plane:NoSchedule taint 가 걸려 있는데, hubble 구성요소는 DaemonSet 이 아니라 Deployment 라 이 taint 를 톨러레이트하지 않습니다. 같은 시점에 CoreDNS 는 잘 떴는데, CoreDNS 는 기본적으로 control-plane 톨러레이션을 갖고 있기 때문이었습니다. 이건 오류가 아니라 정상 동작이었고, 워커 노드가 조인하자 바로 해소됐습니다.

또 하나. 같은 클러스터를 7 노드로 키우면서 GPU 워커가 4 대가 됐는데(RTX 3090 24GB, RTX 5090 32GB, RTX 4070 Laptop 8GB 두 대), 파드가 nvidia.com/gpu: 1 만 요청하면 32GB 5090 이 필요한 학습이 8GB 노트북 GPU 에 얹힐 수 있었습니다. 쿠버네티스 입장에서는 둘 다 "GPU 1개"니까요. 결국 gpu.homelab/tier=xlarge|large|small 같은 의미 기반 라벨을 직접 붙이고 nodeSelector 로 골라 쓰게 했습니다. 리소스 이름이 같다고 같은 자원이 아니라는 것, 그리고 그 간극을 메우는 게 라벨이라는 것을 보여주는 사례입니다.

트래픽을 멈출 때와 재시작할 때를 구분한다

프로브마다 답하는 질문이 다르다. 단순히 모두 /에 연결하면 판단 근거가 섞인다.

  • Readiness지금 요청을 받을 수 있는가? 실패하면 일반적인 Service 트래픽 대상에서 제외된다.
  • Liveness재시작해야 회복되는가? 설정된 실패 임계치를 넘으면 해당 컨테이너를 재시작한다.

여기서 구분할 것 Startup probe가 있다면 성공하기 전까지 liveness와 readiness 검사는 시작하지 않는다.

잠깐, 예측해 보세요 잠깐 느려진 외부 DB 때문에 모든 파드를 재시작하면 회복에 도움이 될까?

설명 확인 · 채점 없는 자가 점검

오히려 연결 폭주와 용량 감소를 만들 수 있다. 외부 의존성 장애를 재시작 사유로 삼을지 신중히 나누고 readiness와 복구 정책을 별도로 설계한다.

근거 문서

다음 실습에서 할 것

다음 실습에서 첫 파드를 띄우고, Deployment 를 만들어 스케일하고, ReplicaSet 의 ownerReferences 를 직접 확인합니다. Job 과 CronJob 과 DaemonSet 을 하나씩 만들고, 마지막에는 파드를 일부러 지워 셀프힐링이 실제로 도는 것을 삭제 전후 목록 비교로 증명합니다.