컨테이너 오케스트레이션과 실무
Kubernetes의 Pod·Deployment·Service·HPA가 컨테이너 배포·복구·확장을 자동화하는 구조를 이해합니다.
컨테이너 하나를 실행하는 것은 간단합니다.
그러나 수백, 수천 개의 컨테이너를 여러 서버에 걸쳐 배포하고, 장애 시 자동 복구하고, 트래픽에 따라 확장하려면 오케스트레이션(Orchestration) 도구가 필요합니다.
Kubernetes는 이 문제를 해결하는 사실상의 표준입니다.
Kubernetes 아키텍처
Kubernetes(K8s)는 구글의 Borg 운영 경험을 바탕으로 개발되어 오픈소스로 공개된 컨테이너 오케스트레이션 시스템입니다.
| 구성 요소 | 역할 |
|---|---|
| API Server | 클러스터 제어 API의 진입점, kubectl과 통신 |
| etcd | 클러스터 상태를 저장하는 분산 키-값 저장소 |
| Scheduler | 새 Pod를 어떤 Node에 배치할지 결정 |
| Controller Manager | 원하는 상태와 현재 상태를 맞추는 컨트롤 루프 |
| kubelet | 각 Node에서 Pod를 실제로 실행·관리 |
| kube-proxy | Service의 가상 IP 전달 규칙을 구현 |
Kubernetes 핵심 개념
Pod
Kubernetes의 최소 배포 단위입니다.
하나 이상의 컨테이너를 묶은 그룹입니다.
apiVersion: v1
kind: Pod
metadata:
name: web-pod
labels:
app: web
spec:
containers:
- name: nginx
image: nginx:1.25-alpine
ports:
- containerPort: 80
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"같은 Pod 안의 컨테이너는 네트워크(localhost)를 공유하며, 함께 마운트한 볼륨을 공유할 수 있습니다. 각 이미지의 루트 파일 시스템까지 자동으로 공유하지는 않습니다.
애플리케이션 컨테이너 옆에 로그 수집기나 프록시를 함께 배치하는 사이드카(Sidecar) 패턴이 대표적입니다.
Deployment
Pod의 선언적 관리를 담당합니다.
이 템플릿의 Pod 3개를 유지하라고 선언하면 컨트롤러가 현재 상태를 원하는 상태로 맞추도록 반복해서 동작합니다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-deployment
spec:
replicas: 3
selector:
matchLabels:
app: web
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # 업데이트 중 추가 Pod 수
maxUnavailable: 0 # 롤아웃으로 허용하는 unavailable Pod 수: 0
template:
metadata:
labels:
app: web
spec:
containers:
- name: nginx
image: nginx:1.25-alpine
resources:
requests:
cpu: "250m"
ports:
- containerPort: 80컨테이너 실패는 kubelet이 재시작 정책에 따라 같은 Pod 안에서 복구할 수 있습니다. Deployment가 관리하는 Pod가 사라지면 ReplicaSet 컨트롤러가 새 UID의 대체 Pod를 만듭니다. 기존 Pod 자체가 다른 노드로 이동하는 것은 아닙니다.
이미지를 변경하면 롤링 업데이트로 점진적으로 교체합니다. 이 예산이 외부 장애 중 가용 Pod 3개를 보장하지는 않으며, 종료 중인 Pod까지 세면 일시적으로 4개를 넘을 수도 있습니다.
이전 리비전과 이미지가 남아 있으면 롤백을 요청할 수 있지만 복구 완료는 별도로 확인해야 합니다.
# 배포
kubectl apply -f deployment.yaml
# 상태 확인
kubectl get pods -o wide
kubectl describe pod web-deployment-xxx
# 스케일링
kubectl scale deployment web-deployment --replicas=5
# 롤링 업데이트
kubectl set image deployment/web-deployment nginx=nginx:1.26-alpine
# 롤백
kubectl rollout undo deployment/web-deployment
kubectl rollout history deployment/web-deploymentPod 검사 실패가 바꾸는 상태
startupProbe를 정의한 정상 경로이다. readiness 실패 기준을 충족한다. 각 단계의 값과 전환 이유를 비교합니다.
Kubernetes · 검사와 상태
Readiness 실패는 트래픽 준비 상태를 바꾸며 컨테이너를 재시작하지 않습니다
probe를 정의한 경우에 적용됩니다. startup과 liveness는 실패 임계값에서 컨테이너 종료와 재시작 정책에 연결되고, readiness는 Service 트래픽 준비 상태를 바꿉니다.
| 검사 | 실패했을 때 | 다른 검사와의 관계 |
|---|---|---|
| 시작 검사 | 설정된 실패 임계값에 이르면 컨테이너 종료·재시작 정책 적용 | startupProbe가 성공하기 전에는 readiness와 liveness 검사를 시작하지 않음 |
| 준비 검사 | Pod의 Ready 상태가 false가 되어 일반 Service 트래픽 대상에서 제외 | 검사 실패 자체로 컨테이너를 재시작하지 않음 |
| 생존 검사 | 실패 임계값에 이르면 컨테이너 종료·재시작 정책 적용 | 일시적 부하를 실패로 오판하지 않도록 임계값 설계 |
- 시작 검사
- 실패했을 때: 설정된 실패 임계값에 이르면 컨테이너 종료·재시작 정책 적용다른 검사와의 관계: startupProbe가 성공하기 전에는 readiness와 liveness 검사를 시작하지 않음
- 준비 검사
- 실패했을 때: Pod의 Ready 상태가 false가 되어 일반 Service 트래픽 대상에서 제외다른 검사와의 관계: 검사 실패 자체로 컨테이너를 재시작하지 않음
- 생존 검사
- 실패했을 때: 실패 임계값에 이르면 컨테이너 종료·재시작 정책 적용다른 검사와의 관계: 일시적 부하를 실패로 오판하지 않도록 임계값 설계
앞의 YAML에는 probe가 없습니다. 애플리케이션 응답 상태를 검사하려면 대상에 맞는 probe를 별도로 정의해야 합니다.
이 예의 조건
- 본문과 전체 도표의 예를 따라 시작 검사 대기부터 준비 실패까지 진행합니다.
관계도의 보충 설명
| 구분 | 전체 도표의 내용 |
|---|---|
| 제목 | startup이 바꾸는 상태와 조건 |
| 표의 값 | 시작 검사. 실패했을 때: 설정된 실패 임계값에 이르면 컨테이너 종료·재시작 정책 적용. 다른 검사와의 관계: startupProbe가 성공하기 전에는 readiness와 liveness 검사를 시작하지 않음. 준비 검사. 실패했을 때: Pod의 Ready 상태가 false가 되어 일반 Service 트래픽 대상에서 제외. 다른 검사와의 관계: 검사 실패 자체로 컨테이너를 재시작하지 않음. 생존 검사. 실패했을 때: 실패 임계값에 이르면 컨테이너 종료·재시작 정책 적용. 다른 검사와의 관계: 일시적 부하를 실패로 오판하지 않도록 임계값 설계. |
| 관찰·상황 | 값·보충 정보 | 변화·이유 |
|---|---|---|
| 시작 검사 대기 startupProbe를 정의한 정상 경로이다. |
|
|
| startup 성공 다른 검사 시작 조건이 충족된다. |
|
|
| 준비 실패 readiness 실패 기준을 충족한다. |
|
|
Pod 검사 실패가 바꾸는 상태
startupProbe를 정의한 정상 경로이다. readiness 실패 기준을 충족한다. 각 단계의 값과 전환 이유를 비교합니다.
Kubernetes · 검사와 상태
Readiness 실패는 트래픽 준비 상태를 바꾸며 컨테이너를 재시작하지 않습니다
probe를 정의한 경우에 적용됩니다. startup과 liveness는 실패 임계값에서 컨테이너 종료와 재시작 정책에 연결되고, readiness는 Service 트래픽 준비 상태를 바꿉니다.
| 검사 | 실패했을 때 | 다른 검사와의 관계 |
|---|---|---|
| 시작 검사 | 설정된 실패 임계값에 이르면 컨테이너 종료·재시작 정책 적용 | startupProbe가 성공하기 전에는 readiness와 liveness 검사를 시작하지 않음 |
| 준비 검사 | Pod의 Ready 상태가 false가 되어 일반 Service 트래픽 대상에서 제외 | 검사 실패 자체로 컨테이너를 재시작하지 않음 |
| 생존 검사 | 실패 임계값에 이르면 컨테이너 종료·재시작 정책 적용 | 일시적 부하를 실패로 오판하지 않도록 임계값 설계 |
- 시작 검사
- 실패했을 때: 설정된 실패 임계값에 이르면 컨테이너 종료·재시작 정책 적용다른 검사와의 관계: startupProbe가 성공하기 전에는 readiness와 liveness 검사를 시작하지 않음
- 준비 검사
- 실패했을 때: Pod의 Ready 상태가 false가 되어 일반 Service 트래픽 대상에서 제외다른 검사와의 관계: 검사 실패 자체로 컨테이너를 재시작하지 않음
- 생존 검사
- 실패했을 때: 실패 임계값에 이르면 컨테이너 종료·재시작 정책 적용다른 검사와의 관계: 일시적 부하를 실패로 오판하지 않도록 임계값 설계
앞의 YAML에는 probe가 없습니다. 애플리케이션 응답 상태를 검사하려면 대상에 맞는 probe를 별도로 정의해야 합니다.
이 예의 조건
- 본문과 전체 도표의 예를 따라 시작 검사 대기부터 준비 실패까지 진행합니다.
관계도의 보충 설명
| 구분 | 전체 도표의 내용 |
|---|---|
| 제목 | startup이 바꾸는 상태와 조건 |
| 표의 값 | 시작 검사. 실패했을 때: 설정된 실패 임계값에 이르면 컨테이너 종료·재시작 정책 적용. 다른 검사와의 관계: startupProbe가 성공하기 전에는 readiness와 liveness 검사를 시작하지 않음. 준비 검사. 실패했을 때: Pod의 Ready 상태가 false가 되어 일반 Service 트래픽 대상에서 제외. 다른 검사와의 관계: 검사 실패 자체로 컨테이너를 재시작하지 않음. 생존 검사. 실패했을 때: 실패 임계값에 이르면 컨테이너 종료·재시작 정책 적용. 다른 검사와의 관계: 일시적 부하를 실패로 오판하지 않도록 임계값 설계. |
| 관찰·상황 | 값·보충 정보 | 변화·이유 |
|---|---|---|
| 시작 검사 대기 startupProbe를 정의한 정상 경로이다. |
|
|
| startup 성공 다른 검사 시작 조건이 충족된다. |
|
|
| 준비 실패 readiness 실패 기준을 충족한다. |
|
|
Service
Pod는 생성·소멸을 반복하므로 IP가 바뀝니다.
Service는 안정적인 네트워크 엔드포인트를 제공합니다.
apiVersion: v1
kind: Service
metadata:
name: web-service
spec:
type: ClusterIP
selector:
app: web
ports:
- port: 80
targetPort: 80- ClusterIP: 클러스터 내부에서 접근할 Service 주소를 제공합니다.
- NodePort: 노드 주소의 포트로 접근할 수 있게 합니다. 실제 접근은 방화벽·라우팅 정책에도 달려 있습니다.
- LoadBalancer: 구현 환경의 로드 밸런서를 요청합니다. 내부 또는 외부 노출 여부는 제공자 설정에 달려 있습니다.
- Ingress: HTTP/HTTPS 라우팅 규칙을 별도 리소스로 정의합니다.
Ingress Controller가 라우팅 규칙을 구현합니다. 또한 위 Service는 app: web 레이블을 선택하므로 같은 네임스페이스에 앞의 단독 web-pod도 만들었다면 그 Pod까지 대상이 될 수 있습니다.
HPA (Horizontal Pod Autoscaler)
트래픽에 따라 자동으로 Pod 수를 조절합니다.
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: web-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: web-deployment
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70여기서 70%는 Pod의 CPU request 대비 사용 비율을 평균한 목표입니다. Deployment에 CPU request를 설정했으며, 별도로 리소스 메트릭 API가 필요합니다. HPA는 누락 지표·허용 오차·안정화 정책도 반영하므로 임계값을 넘는 즉시 늘리거나 줄이는 단순 스위치는 아닙니다.
HPA가 관리하는 replicas와 수동 scale 또는 YAML 재적용으로 지정한 replicas는 충돌할 수 있으므로 확장 제어의 주체를 정해야 합니다.
HPA 목표의 분모와 간단한 계산
CPU request가 모두 250m인 세 Pod가 각각 350m·175m·175m를 사용한다고 가정하면 request 대비 비율은 140%·70%·70%, 평균은 약 93.33%입니다. 목표 70%에 대해 HPA의 기본 비율식을 먼저 적용하면 ceil(3 × 93.33 / 70)인 4를 얻습니다. 실제 컨트롤러는 준비되지 않은 Pod·누락 지표·허용 오차·안정화·변경 한도 등을 함께 처리하므로 이것을 실제 클러스터의 즉시 결과라고 읽지 않습니다.
직접 계산: 현재 네 Pod의 request는 각각 250m, 사용은 각각 350m이며 목표는 70%입니다. 누락·미준비·허용 오차·안정화·변경 정책을 제외한 기본식의 추천 개수를 구하세요. 원 Service와 같은 네임스페이스에 app:web 레이블의 별도 web-pod가 있으면 Deployment 소유가 아니라는 이유만으로 Service 선택에서 제외되나요?
직접 계산한 뒤 풀이 확인
평균은 350/250×100=140%라 ceil(4×140/70)=8입니다. 원 min 2·max 10 범위 안이지만 실제 축소·확장 완료는 선언한 단순 식과 별도입니다. 원 Service selector는 app:web 레이블을 보므로 같은 네임스페이스의 별도 Pod도 선택 대상이 될 수 있습니다. Pod의 controller 소유권과 selector 일치는 서로 다른 조건이며, 준비 상태 등 실제 트래픽 조건도 확인합니다.
컨테이너 네트워킹
Docker 네트워크
리눅스 Docker Engine의 기본 네트워크는 브리지 네트워크입니다.
각 컨테이너는 veth pair로 브리지에 연결됩니다.
기본 브리지의 외부 통신은 NAT 규칙을 이용합니다. 방화벽 백엔드는 구성에 따라 달라집니다.
Kubernetes 네트워크 모델
Kubernetes는 Pod 네트워크에 대해 세 가지 기본 원칙을 전제합니다.
- 모든 Pod는 NAT 없이 다른 Pod와 직접 통신할 수 있다
- 모든 Node는 NAT 없이 모든 Pod와 통신할 수 있다
- Pod가 보는 자기 IP = 다른 Pod가 보는 그 Pod의 IP
CNI(Container Network Interface) 플러그인 등이 Pod 네트워크를 구성합니다. 실제 통신은 NetworkPolicy 등으로 제한할 수 있으며, 일부 구현은 kube-proxy의 기능도 대체합니다.
오버레이(VXLAN 등), 노드 간 라우팅(BGP 등), eBPF 데이터 평면처럼 구현 방식이 다릅니다. Flannel·Calico·Cilium 등이 예이며, Weave도 메시 오버레이 사례로 사용되어 왔습니다. 제품 이름만으로 성능이나 적합한 클러스터 규모를 단정하지 않고 지원 상태와 구성 기능을 확인합니다.
서버리스와 마이크로 VM
서버리스(Serverless)는 개발자가 서버를 관리하지 않고 함수 단위로 코드를 배포하는 모델입니다.
AWS Lambda, Google Cloud Functions, Azure Functions가 대표적입니다.
서버리스라고 서버가 없는 것은 아닙니다.
내부적으로는 컨테이너나 마이크로 VM이 함수 실행 환경을 제공합니다.
AWS Lambda는 Firecracker라는 경량 VM을 사용합니다.
Firecracker는 KVM 기반 마이크로 VM으로 게스트 커널을 별도로 실행하면서 가상 장치 구성을 줄입니다. virtio-net·virtio-block 같은 제한된 장치를 제공하며, 시작 시간과 메모리 비용은 게스트 이미지·호스트·측정 구간에 따라 달라집니다.
서버리스의 가장 큰 도전 과제는 콜드 스타트(Cold Start)입니다.
요청이 없던 함수가 호출되면, 실행 환경을 새로 준비하는 데 수백 ms ~ 수 초가 걸립니다.
인프라 선택 기준
인프라 선택 · 운영 책임
필요한 제어와 직접 관리할 범위로 실행 환경을 비교합니다
장치·게스트 OS·이미지·함수 중 어디까지 제어해야 하는지 확인합니다. 아래 질문은 선택 기준이며 특정 환경의 성능이나 비용 우위를 보장하지 않습니다.
| 환경 | 직접 확인할 범위 | 선택 질문 |
|---|---|---|
| 베어메탈 | 물리 장비·OS·애플리케이션 | GPU·HPC의 장치 접근과 하드웨어 제어가 필요한가? |
| 가상 머신 | 게스트 OS·가상 자원·애플리케이션 | 게스트 커널 경계와 운영체제 제어가 필요한가? |
| 컨테이너 | 이미지·실행 정책·오케스트레이션 | 공유 커널 전제에서 배포 단위를 표준화할 수 있는가? |
| 서버리스 | 함수 코드·권한·서비스 제약 | 이벤트 모델과 실행·동시성 제한에 작업이 맞는가? |
- 베어메탈
- 직접 확인할 범위: 물리 장비·OS·애플리케이션선택 질문: GPU·HPC의 장치 접근과 하드웨어 제어가 필요한가?
- 가상 머신
- 직접 확인할 범위: 게스트 OS·가상 자원·애플리케이션선택 질문: 게스트 커널 경계와 운영체제 제어가 필요한가?
- 컨테이너
- 직접 확인할 범위: 이미지·실행 정책·오케스트레이션선택 질문: 공유 커널 전제에서 배포 단위를 표준화할 수 있는가?
- 서버리스
- 직접 확인할 범위: 함수 코드·권한·서비스 제약선택 질문: 이벤트 모델과 실행·동시성 제한에 작업이 맞는가?
관리형 서비스 사용 여부에 따라 운영 책임이 달라집니다. 한 시스템 안에서도 서로 다른 환경을 함께 사용할 수 있습니다.
데이터베이스는 VM이나 매니지드 서비스로, 애플리케이션 서버는 컨테이너(Kubernetes)로, 이벤트 처리는 서버리스로 구성하는 것이 흔한 패턴입니다.
def recommend_infra(workload):
"""워크로드 특성에 따른 인프라 추천"""
if workload["gpu_required"]:
return "베어메탈 또는 GPU VM"
if workload["multi_tenant"]:
return "VM (강한 격리 필요)"
if workload["traffic_pattern"] == "spiky":
if workload["avg_duration_ms"] < 300000: # 5분 미만
return "서버리스 (Lambda/Cloud Functions)"
else:
return "컨테이너 + HPA"
if workload["microservices"]:
return "Kubernetes 컨테이너"
return "VM (범용)"위 함수는 입력 키가 갖춰졌다고 가정한 의사결정 예제입니다. 조건 검사 순서가 추천을 결정하며, 5분은 여기서 정한 기준이지 특정 서버리스 서비스의 실행 시간 한도가 아닙니다.
CI/CD와 컨테이너
컨테이너는 CI/CD(지속적 통합/배포) 파이프라인과 자연스럽게 결합됩니다.
# 1. 푸시할 이름으로 이미지 빌드
docker build -t registry.example.com/myapp:${GIT_SHA} .
# 2. 보안 스캐닝
trivy image registry.example.com/myapp:${GIT_SHA}
# 3. 레지스트리에 푸시
docker push registry.example.com/myapp:${GIT_SHA}
# 4. Kubernetes에 배포
kubectl set image deployment/myapp \
myapp=registry.example.com/myapp:${GIT_SHA}
# 5. 배포 상태 확인
kubectl rollout status deployment/myapp이미지 태그로 Git 커밋 해시를 사용하면, 어떤 코드 버전이 배포되었는지 추적할 수 있습니다.
태그는 덮어쓸 수 있으므로 내용 고정이 필요하면 이미지 digest를 사용합니다. 파이프라인은 레지스트리 인증, 기존 myapp Deployment, 사용할 도구를 전제합니다. 롤백은 이전 이미지의 보존과 데이터 호환성까지 확인해야 합니다.
다음 장에서는 OS 지식이 실제로 쓰이는 순간들 — 리눅스 필수 명령어, 시스템 모니터링, 면접 주제를 정리하겠습니다.