icon

안동민 개발노트

13장 : 가상화와 컨테이너

컨테이너 오케스트레이션과 실무


컨테이너 하나를 실행하는 것은 간단합니다. 그러나 수백, 수천 개의 컨테이너를 여러 서버에 걸쳐 배포하고, 장애 시 자동 복구하고, 트래픽에 따라 확장하려면 오케스트레이션(Orchestration) 도구가 필요합니다. Kubernetes는 이 문제를 해결하는 사실상의 표준입니다.


Kubernetes 아키텍처

Kubernetes(K8s)는 구글이 내부에서 사용하던 Borg 시스템을 기반으로 만들고 오픈소스로 공개한 컨테이너 오케스트레이션 시스템입니다.

┌─────────────────── Control Plane ───────────────────┐
│  ┌──────────┐  ┌──────────┐  ┌─────────────────┐    │
│  │ API      │  │ etcd     │  │ Scheduler       │    │
│  │ Server   │  │ (저장소) │  │                 │    │
│  └─────┬────┘  └──────────┘  └─────────────────┘    │
│        │       ┌──────────────────────┐             │
│        │       │ Controller Manager   │             │
│        │       └──────────────────────┘             │
└────────┼────────────────────────────────────────────┘

    ┌────┴────────────── Worker Nodes ─────────────────┐
    │  ┌──────────────────────────────────────────┐    │
    │  │ Node 1                                   │    │
    │  │  ┌────────┐  ┌────────┐  ┌────────────┐  │    │
    │  │  │kubelet │  │kube-   │  │Container   │  │    │
    │  │  │        │  │proxy   │  │Runtime     │  │    │
    │  │  └────────┘  └────────┘  └────────────┘  │    │
    │  │  ┌──────┐ ┌──────┐ ┌──────┐              │    │
    │  │  │Pod A │ │Pod B │ │Pod C │              │    │
    │  │  └──────┘ └──────┘ └──────┘              │    │
    │  └──────────────────────────────────────────┘    │
    └──────────────────────────────────────────────────┘
구성 요소역할
API Server모든 요청의 진입점, kubectl과 통신
etcd클러스터 상태를 저장하는 분산 키-값 저장소
Scheduler새 Pod를 어떤 Node에 배치할지 결정
Controller Manager원하는 상태와 현재 상태를 맞추는 컨트롤 루프
kubelet각 Node에서 Pod를 실제로 실행·관리
kube-proxy서비스의 네트워크 트래픽을 Pod로 라우팅

Kubernetes 핵심 개념

Pod

Kubernetes의 최소 배포 단위입니다. 하나 이상의 컨테이너를 묶은 그룹입니다.

pod.yaml
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의 선언적 관리를 담당합니다. 이 이미지의 컨테이너 3개를 항상 유지하라고 선언하면, Kubernetes가 자동으로 그 상태를 맞춥니다.

deployment.yaml
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   # 항상 3개 유지
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
      - name: nginx
        image: nginx:1.25-alpine
        ports:
        - containerPort: 80

Pod가 충돌하면 자동으로 새 Pod를 생성합니다. 이미지를 변경하면 롤링 업데이트로 하나씩 교체합니다. 문제가 발생하면 즉시 이전 버전으로 롤백할 수 있습니다.

kubectl_commands.sh
# 배포
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-deployment

Service

Pod는 생성·소멸을 반복하므로 IP가 바뀝니다. Service는 안정적인 네트워크 엔드포인트를 제공합니다.

service.yaml
apiVersion: v1
kind: Service
metadata:
  name: web-service
spec:
  type: ClusterIP
  selector:
    app: web
  ports:
  - port: 80
    targetPort: 80
Service 타입접근 범위용도
ClusterIP클러스터 내부내부 서비스 간 통신
NodePort외부 (Node IP:Port)개발·테스트
LoadBalancer외부 (클라우드 LB)프로덕션 외부 노출
Ingress외부 (L7 라우팅)도메인 기반 라우팅

HPA (Horizontal Pod Autoscaler)

트래픽에 따라 자동으로 Pod 수를 조절합니다.

hpa.yaml
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

CPU 사용률이 70%를 넘으면 Pod를 늘리고, 낮아지면 줄입니다. 개발자가 인프라를 직접 관리하는 대신, 원하는 상태를 선언하면 시스템이 유지합니다.


컨테이너 네트워킹

Docker 네트워크

Docker는 기본적으로 브리지 네트워크를 생성합니다.

┌──────── 호스트 ──────────────────────────────────┐
│                                                  │
│  ┌──────────┐     ┌──────────┐                   │
│  │Container │     │Container │                   │
│  │  eth0    │     │  eth0    │                   │
│  └────┬─────┘     └────┬─────┘                   │
│       │ veth           │ veth                    │
│  ┌────┴────────────────┴────┐                    │
│  │     docker0 (브리지)     │                    │
│  └───────────┬──────────────┘                    │
│              │ NAT (iptables)                    │
│         ┌────┴────┐                              │
│         │  eth0   │ ← 호스트 NIC                 │
│         └─────────┘                              │
└──────────────────────────────────────────────────┘

각 컨테이너는 veth pair로 브리지에 연결됩니다. 외부 통신은 NAT(iptables)를 거칩니다.

Kubernetes 네트워크 모델

Kubernetes는 세 가지 네트워크 원칙을 따릅니다.

  • 모든 Pod는 NAT 없이 다른 Pod와 직접 통신할 수 있다
  • 모든 Node는 NAT 없이 모든 Pod와 통신할 수 있다
  • Pod가 보는 자기 IP = 다른 Pod가 보는 그 Pod의 IP

CNI(Container Network Interface) 플러그인이 이를 구현합니다.

CNI 플러그인방식특징
FlannelVXLAN 오버레이간단, 소규모 적합
CalicoBGP / VXLAN네트워크 정책 지원, 대규모 적합
CiliumeBPF 기반고성능, 관찰 가능성
Weave메시 오버레이설정 간단

서버리스와 마이크로 VM

서버리스(Serverless)는 개발자가 서버를 관리하지 않고 함수 단위로 코드를 배포하는 모델입니다. AWS Lambda, Google Cloud Functions, Azure Functions가 대표적입니다.

서버리스라고 서버가 없는 것은 아닙니다. 내부적으로는 컨테이너나 마이크로 VM이 함수 실행 환경을 제공합니다. AWS Lambda는 Firecracker라는 경량 VM을 사용합니다.

특성일반 VMFirecracker
부팅 시간수십 초~125ms
메모리 오버헤드수백 MB~5MB
보안 격리하이퍼바이저KVM 기반
디바이스전체 에뮬레이션최소 (virtio-net, virtio-block)

서버리스의 가장 큰 도전 과제는 콜드 스타트(Cold Start)입니다. 요청이 없던 함수가 호출되면, 실행 환경을 새로 준비하는 데 수백 ms ~ 수 초가 걸립니다.


인프라 선택 기준

인프라성능격리배포 속도관리 부담적합한 경우
베어메탈최고물리적느림높음GPU 학습, HPC
VM높음강함분 단위중간멀티테넌트
컨테이너높음중간초 단위낮음마이크로서비스
서버리스가변강함즉시최소이벤트 처리

실무에서는 이들을 혼합합니다. 데이터베이스는 VM이나 매니지드 서비스로, 애플리케이션 서버는 컨테이너(Kubernetes)로, 이벤트 처리는 서버리스로 구성하는 것이 흔한 패턴입니다.

infra_decision.py
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 (범용)"

CI/CD와 컨테이너

컨테이너는 CI/CD(지속적 통합/배포) 파이프라인과 자연스럽게 결합됩니다.

코드 Push → 빌드 → 테스트 → 이미지 빌드 → 레지스트리 Push → K8s 배포
  (Git)     (CI)    (CI)     (Docker)       (ECR/GCR)         (CD)
cicd_pipeline.sh
# 1. 이미지 빌드 (멀티 스테이지로 크기 최소화)
docker build -t myapp:${GIT_SHA} .

# 2. 보안 스캐닝
trivy image 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 커밋 해시를 사용하면, 어떤 코드 버전이 배포되었는지 추적할 수 있습니다. 문제 발생 시 이전 커밋의 이미지로 즉시 롤백이 가능합니다.

다음 장에서는 OS 지식이 실제로 쓰이는 순간들 — 리눅스 필수 명령어, 시스템 모니터링, 면접 주제를 정리하겠습니다.

목차