지난 글에서 쿠버네티스가 왜 필요한지, 그리고 "원하는 상태를 선언하면 알아서 맞춰준다"는 철학을 잡았다. 오늘은 그 선언을 실제로 이루는 가장 기본적인 3개 개념을 본다.
- Pod: 컨테이너를 담는 최소 단위
- Deployment: Pod를 몇 개, 어떻게 유지할지 관리
- Service: Pod에 접속할 안정적인 통로
이 셋의 관계만 이해하면 쿠버네티스의 절반은 잡은 거다. 하나씩 가보자.
Pod — 컨테이너를 담는 최소 단위
쿠버네티스에서는 컨테이너를 직접 다루지 않는다. 대신 Pod(파드)라는 걸 다룬다.
Pod는 컨테이너를 하나(또는 여러 개) 감싼 껍데기다. "왜 컨테이너를 그냥 안 쓰고 한 겹 더 감쌌지?" 싶을 텐데, 쿠버네티스가 관리하는 최소 단위를 Pod로 통일해뒀다고 생각하면 된다. 컨테이너를 직접이 아니라 Pod 단위로 배치하고, 죽으면 Pod 단위로 다시 살린다.
대부분의 경우 Pod 하나 = 컨테이너 하나다. (가끔 보조 컨테이너를 같이 넣기도 하지만, 입문 단계에선 1:1로 봐도 된다.)
여기서 중요한 특징 하나. Pod는 언제든 죽을 수 있고, 죽으면 새로 만들어진다. 그리고 새로 만들어진 Pod는 IP 주소가 바뀐다. 이 "Pod는 일회용이고 IP가 계속 바뀐다"는 성질이 뒤에 나올 Service의 존재 이유가 된다. 기억해두자.
간단한 Pod 정의는 이렇게 생겼다. (실제로는 이걸 직접 만들 일은 별로 없다. 곧 설명할 Deployment를 쓴다.)
apiVersion: v1
kind: Pod
metadata:
name: my-app-pod
spec:
containers:
- name: my-app
image: ghcr.io/user/my-app:latest
ports:
- containerPort: 3000
Deployment — Pod를 관리하는 관리자
Pod를 직접 만들면 문제가 있다. Pod가 죽으면 그걸로 끝이다. 아무도 다시 안 살려준다. 지난 글에서 말한 "자동 복구"가 안 되는 거다.
그래서 실무에서는 Pod를 직접 만들지 않고 Deployment(디플로이먼트)를 통해 만든다. Deployment는 "이 Pod를 몇 개 유지해줘"라고 선언하면, 그 개수를 계속 지켜주는 관리자다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 3 # ← 항상 3개를 유지해라!
selector:
matchLabels:
app: my-app
template: # ← 이 틀로 Pod를 찍어낸다
metadata:
labels:
app: my-app
spec:
containers:
- name: my-app
image: ghcr.io/user/my-app:latest
ports:
- containerPort: 3000
핵심은 replicas: 3이다. "이 앱 Pod를 항상 3개 유지하라"는 선언이다. 이제 이런 일이 자동으로 일어난다.
- Pod 하나가 죽으면 → Deployment가 알아채고 새로 하나 띄운다 (자동 복구)
replicas: 5로 바꾸면 → 2개를 더 띄운다 (스케일 업)replicas: 1로 바꾸면 → 2개를 줄인다 (스케일 다운)
지난 글의 "선언하면 알아서 맞춰준다"가 바로 이거다. 나는 "3개"라는 목표만 말하고, 그걸 지키는 건 Deployment가 한다.
롤링 업데이트도 Deployment가 한다. 이미지를 새 버전으로 바꾸면, Deployment가 새 Pod를 하나씩 띄우면서 옛 Pod를 하나씩 내린다. 그래서 무중단 배포가 된다. CI/CD 글에서 아쉬웠던 그 다운타임이 여기서 해결되는 거다.
Service — 안정적인 접속 통로
이제 Pod가 3개 떠 있다. 그런데 문제가 있다. 아까 말했듯 Pod는 죽으면 IP가 바뀐다. 그리고 3개나 되는데, 접속하는 쪽에서 그중 누구한테 연결해야 하나?
- Pod IP는 계속 바뀐다 → 특정 IP를 적어두면 곧 못 쓰게 된다
- Pod가 3개다 → 트래픽을 어떻게 나눠줄까?
이 두 문제를 해결하는 게 Service(서비스)다. Service는 여러 Pod 앞에 서 있는 안정적인 대문이라고 보면 된다.
apiVersion: v1
kind: Service
metadata:
name: my-app-service
spec:
selector:
app: my-app # ← 이 라벨을 가진 Pod들에게 연결
ports:
- port: 80 # Service가 받는 포트
targetPort: 3000 # Pod로 전달할 포트
Service는 이런 일을 한다.
1. 고정된 접속 지점을 제공한다. Pod IP가 아무리 바뀌어도, Service의 주소는 안 바뀐다. 접속하는 쪽은 Service 이름/주소만 알면 된다. (Compose에서 "서비스 이름으로 접근"했던 거랑 같은 개념이다.)
2. 트래픽을 여러 Pod에 나눠준다(로드 밸런싱). Service로 들어온 요청을 뒤에 있는 3개 Pod에 골고루 분배한다. 어느 Pod가 죽으면 살아있는 Pod로만 보낸다.
selector가 연결의 핵심이다. app: my-app 라벨을 가진 Pod들을 자동으로 찾아서 뒤에 붙인다. Deployment가 그 라벨을 단 Pod를 만들고, Service가 그 라벨로 Pod를 찾는 식으로 둘이 연결된다.
셋의 관계를 그림으로
이제 세 개가 어떻게 엮이는지 전체를 보자.
[ 외부 요청 ]
│
▼
┌───────────────┐
│ Service │ ← 고정 주소 + 로드밸런싱
└───────────────┘
│ │ │
▼ ▼ ▼
[Pod] [Pod] [Pod] ← 실제 앱이 도는 곳
└──────┬──────┘
│
┌───────────────┐
│ Deployment │ ← Pod 3개를 유지/관리
└───────────────┘정리하면 이런 흐름이다.
- Deployment가 Pod를 3개 만들고 그 개수를 유지한다.
- Pod 안에서 실제 앱 컨테이너가 돈다.
- Service가 그 Pod들 앞에서 고정 주소를 제공하고 트래픽을 나눠준다.
외부에서는 Service로 접속하고, Service가 알아서 살아있는 Pod에 연결해준다. Pod가 죽든 새로 생기든, 외부에서는 신경 쓸 필요가 없다.
정리
| 개념 | 한 줄 요약 | 역할 |
|---|---|---|
| Pod | 컨테이너를 담는 최소 단위 | 앱이 실제로 도는 곳 |
| Deployment | Pod 개수를 유지하는 관리자 | 자동 복구, 스케일, 롤링 업데이트 |
| Service | Pod 앞의 고정 대문 | 안정적 주소 + 로드밸런싱 |
- Pod는 일회용이라 죽으면 IP가 바뀐다. 그래서 직접 안 쓰고 Deployment로 관리한다.
- Deployment에
replicas로 개수만 선언하면, 복구·확장·무중단 배포를 알아서 해준다. - Service는 계속 바뀌는 Pod들 앞에서 변하지 않는 접속 지점을 제공한다.
이 세 개가 쿠버네티스의 가장 기본 뼈대다. 실무 설정이 아무리 복잡해 보여도, 결국 이 셋의 조합 위에 얹혀 있다.
다음 글에서는 이 YAML들을 실제로 클러스터에 적용하고 확인하는 도구, kubectl 명령어를 다뤄본다. 지금까지 눈으로만 본 Pod, Deployment, Service를 직접 띄우고 조회해볼 거다.
'프로그래밍 > DevOps' 카테고리의 다른 글
| [DevOps] 쿠버네티스가 왜 필요한데? Docker Compose로는 부족한 순간 (0) | 2026.07.27 |
|---|---|
| [DevOps] CI/CD 실무 다듬기 — 브랜치별 환경 분리와 이미지 태그 전략 (0) | 2026.07.24 |
| [DevOps] GitHub Actions로 진짜 배포까지 — Docker 이미지 빌드하고 서버에 자동 배포하기 (0) | 2026.07.17 |
| [DevOps] GitHub Actions 첫걸음 — 푸시하면 자동으로 테스트 돌리기 (0) | 2026.07.17 |
| [DevOps] CI/CD가 대체 뭔데? 자동화 배포의 첫걸음 (0) | 2026.07.17 |