LabHub

KCNA — 쿠버네티스·클라우드 네이티브 입문 · 컨테이너 오케스트레이션과 워크로드 · 이론

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

LabHub 에서 이어서 보기

한 줄 요약

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

왜 이게 필요했나

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

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

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

어떻게 동작하나

소유 관계의 사슬

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-relayhubble-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 로 골라 쓰게 했습니다.
리소스 이름이 같다고 같은 자원이 아니라는 것, 그리고 그 간극을 메우는 게 라벨이라는 것을
보여주는 사례입니다.

다음 실습에서 할 것

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