hobokai 님의 블로그

Kubernetes 완전 정복 가이드: 컨테이너 오케스트레이션부터 프로덕션까지 본문

DevOps

Kubernetes 완전 정복 가이드: 컨테이너 오케스트레이션부터 프로덕션까지

hobokai 2025. 7. 23. 08:43

목차

  1. Kubernetes란 무엇인가?
  2. Kubernetes 아키텍처
  3. 설치 및 환경 구성
  4. 핵심 개념과 오브젝트
  5. Pod와 컨테이너 관리
  6. Service와 네트워킹
  7. 볼륨과 스토리지
  8. ConfigMap과 Secret
  9. 배포 전략과 관리
  10. Ingress와 로드밸런싱
  11. 오토스케일링
  12. Helm 패키지 관리
  13. 모니터링과 로깅
  14. 보안
  15. 운영 베스트 프랙티스
  16. 결론

Kubernetes란 무엇인가?

Kubernetes(K8s)는 Google이 개발하고 2014년 오픈소스로 공개한 컨테이너 오케스트레이션 플랫폼입니다. 컨테이너화된 애플리케이션의 배포, 관리, 확장을 자동화하는 시스템입니다.

Kubernetes의 핵심 가치

  • 🚀 자동화 - 배포, 확장, 복구 자동화
  • 🔄 자가 치유 - 실패한 컨테이너 자동 재시작
  • 📈 확장성 - 수평/수직 스케일링 지원
  • 고가용성 - 다중 노드 클러스터로 무중단 서비스
  • 🌍 포터빌리티 - 클라우드 독립적 운영
  • 📦 선언적 구성 - 원하는 상태 정의로 관리

Kubernetes가 해결하는 문제들

문제 Docker만 사용 Kubernetes 해결
다중 호스트 관리 수동 관리 자동 스케줄링
로드 밸런싱 외부 도구 필요 내장 Service
서비스 디스커버리 수동 설정 자동 DNS
롤링 업데이트 스크립트 필요 내장 기능
자동 복구 모니터링 도구 자가 치유
구성 관리 환경별 설정 ConfigMap/Secret

Docker Swarm vs Kubernetes 비교

특성 Docker Swarm Kubernetes
설정 복잡도 낮음 높음
기능 풍부도 기본적 매우 풍부
커뮤니티 작음 매우 큼
학습 곡선 완만함 가파름
생태계 제한적 광범위함
기업 지원 Docker Inc. CNCF 전체

Kubernetes 아키텍처

클러스터 구성 요소

┌─────────────────────────────────────────────────────────────┐
│                        Master Node                          │
├─────────────────────────────────────────────────────────────┤
│  API Server  │  etcd    │  Scheduler  │  Controller Manager │
└─────────────────────────────────────────────────────────────┘
                              │
                              │ (API 통신)
                              │
┌─────────────────────────────────────────────────────────────┐
│                       Worker Nodes                          │
├─────────────────────────────────────────────────────────────┤
│  kubelet  │  kube-proxy  │  Container Runtime (Docker/CRI)  │
│                          │                                  │
│  ┌─────────────────────┐ │  ┌─────────────────────────────┐ │
│  │       Pod 1         │ │  │           Pod 2             │ │
│  │  ┌─────┐ ┌─────────┐│ │  │  ┌─────────┐ ┌─────────────┐│ │
│  │  │App A│ │ Sidecar ││ │  │  │  App B  │ │   App C     ││ │
│  │  └─────┘ └─────────┘│ │  │  └─────────┘ └─────────────┘│ │
│  └─────────────────────┘ │  └─────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘

Master Node (Control Plane) 구성 요소

1. API Server (kube-apiserver)

  • 클러스터의 게이트웨이
  • 모든 REST API 요청 처리
  • 인증, 인가, admission control
  • etcd와의 유일한 통신 창구

2. etcd

  • 분산 키-값 저장소
  • 클러스터의 모든 상태 정보 저장
  • 백업과 복원의 핵심 구성 요소

3. Scheduler (kube-scheduler)

  • Pod를 적절한 Node에 배치
  • 리소스 요구사항, 제약사항 고려
  • 커스텀 스케줄링 정책 지원

4. Controller Manager (kube-controller-manager)

  • 원하는 상태 유지 담당
  • ReplicaSet, Deployment, Service 등 관리
  • 지속적인 reconciliation loop 실행

Worker Node 구성 요소

1. kubelet

  • 각 노드의 Kubernetes 에이전트
  • Pod 생명주기 관리
  • Master와의 통신 담당

2. kube-proxy

  • 네트워크 프록시 역할
  • Service와 Endpoint 관리
  • iptables/IPVS 룰 관리

3. Container Runtime

  • 컨테이너 실행 환경
  • Docker, containerd, CRI-O 등

설치 및 환경 구성

로컬 개발 환경

1. minikube 설치

# macOS
brew install minikube

# Windows (Chocolatey)
choco install minikube

# Linux
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
sudo install minikube-linux-amd64 /usr/local/bin/minikube

# 클러스터 시작
minikube start --driver=docker
minikube start --cpus=4 --memory=8192  # 리소스 지정

# 상태 확인
minikube status
minikube dashboard  # 웹 UI 열기

2. Kind (Kubernetes in Docker)

# Kind 설치
curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.20.0/kind-linux-amd64
chmod +x ./kind
sudo mv ./kind /usr/local/bin/kind

# 클러스터 생성
kind create cluster --name my-cluster
kind create cluster --config=kind-config.yaml

# 다중 노드 클러스터 설정
cat << EOF > kind-config.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
- role: worker
EOF

kind create cluster --config=kind-config.yaml

3. Docker Desktop Kubernetes

# Docker Desktop 설정에서 Kubernetes 활성화
# Settings > Kubernetes > Enable Kubernetes

# 컨텍스트 확인
kubectl config get-contexts
kubectl config use-context docker-desktop

kubectl 설치 및 설정

# kubectl 설치
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl

# 자동 완성 설정
echo 'source <(kubectl completion bash)' >> ~/.bashrc
echo 'alias k=kubectl' >> ~/.bashrc
echo 'complete -o default -F __start_kubectl k' >> ~/.bashrc

# 클러스터 연결 확인
kubectl cluster-info
kubectl get nodes
kubectl version --client --output=yaml

프로덕션 클러스터 구축

kubeadm을 이용한 클러스터 구축

# 모든 노드에서 Docker와 kubelet 설치
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl

# Kubernetes 레포지토리 추가
sudo curl -fsSLo /usr/share/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg
echo "deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list

# kubelet, kubeadm, kubectl 설치
sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl

# Master 노드 초기화
sudo kubeadm init --pod-network-cidr=10.244.0.0/16

# kubectl 설정
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

# 네트워크 애드온 설치 (Flannel)
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml

# Worker 노드 조인
kubeadm join <master-ip>:6443 --token <token> --discovery-token-ca-cert-hash <hash>

핵심 개념과 오브젝트

Kubernetes 오브젝트 계층 구조

Namespace
├── Pod (기본 실행 단위)
│   └── Container(s)
├── ReplicaSet (Pod 복제 관리)
├── Deployment (애플리케이션 배포)
├── Service (네트워크 접근)
├── ConfigMap (설정 데이터)
├── Secret (민감한 데이터)
├── PersistentVolume (스토리지)
└── Ingress (외부 접근)

네임스페이스

# namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: production
  labels:
    environment: prod
---
apiVersion: v1
kind: Namespace
metadata:
  name: development
  labels:
    environment: dev
# 네임스페이스 관리
kubectl create namespace my-app
kubectl get namespaces
kubectl config set-context --current --namespace=my-app

# 네임스페이스별 리소스 확인
kubectl get pods -n production
kubectl get all -A  # 모든 네임스페이스

라벨과 셀렉터

# 라벨 사용 예시
apiVersion: v1
kind: Pod
metadata:
  name: web-app
  labels:
    app: web
    version: v1.0
    environment: production
    tier: frontend
spec:
  containers:
  - name: web
    image: nginx:1.20
# 라벨 기반 검색
kubectl get pods -l app=web
kubectl get pods -l environment=production,tier=frontend
kubectl get pods -l 'version in (v1.0, v1.1)'

# 라벨 수정
kubectl label pod web-app version=v1.1 --overwrite
kubectl label pod web-app tier-  # 라벨 삭제

Pod와 컨테이너 관리

Pod 기본 개념

Pod는 Kubernetes의 최소 배포 단위입니다. 하나 이상의 컨테이너를 포함하며, 같은 Pod 내 컨테이너들은 네트워크와 스토리지를 공유합니다.

단일 컨테이너 Pod

# simple-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
  labels:
    app: nginx
spec:
  containers:
  - name: nginx
    image: nginx:1.20
    ports:
    - containerPort: 80
    resources:
      requests:
        memory: "64Mi"
        cpu: "250m"
      limits:
        memory: "128Mi"
        cpu: "500m"
    env:
    - name: ENV
      value: "production"
    livenessProbe:
      httpGet:
        path: /
        port: 80
      initialDelaySeconds: 30
      periodSeconds: 10
    readinessProbe:
      httpGet:
        path: /
        port: 80
      initialDelaySeconds: 5
      periodSeconds: 5

다중 컨테이너 Pod (사이드카 패턴)

# multi-container-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: web-with-logging
spec:
  volumes:
  - name: shared-logs
    emptyDir: {}

  containers:
  # 메인 애플리케이션 컨테이너
  - name: web-app
    image: nginx:1.20
    ports:
    - containerPort: 80
    volumeMounts:
    - name: shared-logs
      mountPath: /var/log/nginx

  # 로그 수집 사이드카 컨테이너
  - name: log-shipper
    image: fluent/fluent-bit:1.8
    volumeMounts:
    - name: shared-logs
      mountPath: /var/log/nginx
    env:
    - name: FLUENTD_CONF
      value: "fluent-bit.conf"

Pod 생명주기와 상태

# Pod 생성 및 관리
kubectl create -f pod.yaml
kubectl apply -f pod.yaml

# Pod 상태 확인
kubectl get pods
kubectl get pods -o wide  # 더 많은 정보
kubectl describe pod nginx-pod

# Pod 로그 확인
kubectl logs nginx-pod
kubectl logs nginx-pod -c container-name  # 다중 컨테이너
kubectl logs -f nginx-pod  # 실시간 로그

# Pod 실행 명령
kubectl exec nginx-pod -- ls -la
kubectl exec -it nginx-pod -- bash

# Pod 삭제
kubectl delete pod nginx-pod
kubectl delete -f pod.yaml

Init 컨테이너

# init-container-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
spec:
  initContainers:
  - name: init-myservice
    image: busybox:1.28
    command: ['sh', '-c', 'until nslookup myservice; do echo waiting for myservice; sleep 2; done;']
  - name: init-mydb
    image: busybox:1.28
    command: ['sh', '-c', 'until nslookup mydb; do echo waiting for mydb; sleep 2; done;']

  containers:
  - name: myapp-container
    image: busybox:1.28
    command: ['sh', '-c', 'echo The app is running! && sleep 3600']

Service와 네트워킹

Service 타입별 특징

Service 타입 용도 접근 방법
ClusterIP 내부 통신 클러스터 내부에서만
NodePort 외부 노출 노드IP:포트
LoadBalancer 클라우드 LB 외부 로드밸런서
ExternalName 외부 서비스 DNS CNAME

ClusterIP Service (내부 통신)

# clusterip-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: my-app-service
spec:
  type: ClusterIP  # 기본값
  selector:
    app: my-app
  ports:
  - port: 80        # 서비스 포트
    targetPort: 8080 # 컨테이너 포트
    protocol: TCP
    name: http

NodePort Service (외부 노출)

# nodeport-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: my-app-nodeport
spec:
  type: NodePort
  selector:
    app: my-app
  ports:
  - port: 80
    targetPort: 8080
    nodePort: 30080  # 30000-32767 범위

LoadBalancer Service (클라우드)

# loadbalancer-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: my-app-lb
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-type: "nlb"  # AWS NLB
spec:
  type: LoadBalancer
  selector:
    app: my-app
  ports:
  - port: 80
    targetPort: 8080

Headless Service (StatefulSet용)

# headless-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: my-stateful-service
spec:
  clusterIP: None  # Headless 설정
  selector:
    app: my-stateful-app
  ports:
  - port: 80
    targetPort: 8080

서비스 디스커버리와 DNS

# 서비스 확인
kubectl get services
kubectl describe service my-app-service

# DNS 확인 (Pod 내부에서)
nslookup my-app-service
nslookup my-app-service.default.svc.cluster.local

# Endpoint 확인
kubectl get endpoints my-app-service

완전한 애플리케이션 예시

# web-app-complete.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
  labels:
    app: web-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web-app
  template:
    metadata:
      labels:
        app: web-app
    spec:
      containers:
      - name: web
        image: nginx:1.20
        ports:
        - containerPort: 80
        resources:
          requests:
            memory: "128Mi"
            cpu: "100m"
          limits:
            memory: "256Mi"
            cpu: "200m"

---
apiVersion: v1
kind: Service
metadata:
  name: web-app-service
spec:
  selector:
    app: web-app
  ports:
  - port: 80
    targetPort: 80
  type: LoadBalancer

볼륨과 스토리지

볼륨 타입별 특징

볼륨 타입 특징 사용 사례
emptyDir Pod 생명주기와 동일 임시 데이터
hostPath 노드 파일시스템 로깅, 모니터링
persistentVolumeClaim 영구 스토리지 데이터베이스
configMap/secret 설정 데이터 환경 설정
nfs/cephfs 네트워크 스토리지 공유 파일시스템

기본 볼륨 사용

# volume-examples.yaml
apiVersion: v1
kind: Pod
metadata:
  name: volume-pod
spec:
  containers:
  - name: app
    image: nginx:1.20
    volumeMounts:
    - name: html-volume
      mountPath: /usr/share/nginx/html
    - name: config-volume
      mountPath: /etc/config
    - name: cache-volume
      mountPath: /tmp/cache

  volumes:
  # 빈 디렉토리 볼륨
  - name: html-volume
    emptyDir: {}

  # ConfigMap 볼륨
  - name: config-volume
    configMap:
      name: app-config

  # 호스트 경로 볼륨
  - name: cache-volume
    hostPath:
      path: /tmp/pod-cache
      type: DirectoryOrCreate

PersistentVolume과 PersistentVolumeClaim

# persistent-volume.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-storage
spec:
  capacity:
    storage: 10Gi
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: manual
  hostPath:
    path: /mnt/data

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-storage
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi
  storageClassName: manual

---
apiVersion: v1
kind: Pod
metadata:
  name: pv-pod
spec:
  containers:
  - name: app
    image: nginx:1.20
    volumeMounts:
    - name: storage
      mountPath: /usr/share/nginx/html
  volumes:
  - name: storage
    persistentVolumeClaim:
      claimName: pvc-storage

StorageClass (동적 프로비저닝)

# storage-class.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast-ssd
provisioner: kubernetes.io/aws-ebs
parameters:
  type: gp3
  iops: "3000"
  throughput: "125"
allowVolumeExpansion: true
reclaimPolicy: Delete

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: dynamic-pvc
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 20Gi
  storageClassName: fast-ssd

스토리지 관리 명령어

# PV/PVC 확인
kubectl get pv
kubectl get pvc
kubectl describe pv pv-storage

# 스토리지 클래스 확인
kubectl get storageclass
kubectl describe storageclass fast-ssd

# 볼륨 사용량 확인
kubectl exec -it pod-name -- df -h

ConfigMap과 Secret

ConfigMap 생성 및 사용

# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  # 키-값 데이터
  database_host: "postgres.example.com"
  database_port: "5432"
  log_level: "info"

  # 파일 형태 데이터
  app.properties: |
    server.port=8080
    server.host=0.0.0.0
    logging.level=INFO

  nginx.conf: |
    server {
        listen 80;
        location / {
            proxy_pass http://backend;
        }
    }

ConfigMap 사용 방법들

# configmap-usage.yaml
apiVersion: v1
kind: Pod
metadata:
  name: configmap-pod
spec:
  containers:
  - name: app
    image: nginx:1.20

    # 환경 변수로 사용
    env:
    - name: DATABASE_HOST
      valueFrom:
        configMapKeyRef:
          name: app-config
          key: database_host

    # 모든 키를 환경 변수로 사용
    envFrom:
    - configMapRef:
        name: app-config

    # 볼륨으로 마운트
    volumeMounts:
    - name: config-volume
      mountPath: /etc/config
    - name: nginx-config
      mountPath: /etc/nginx/conf.d

  volumes:
  - name: config-volume
    configMap:
      name: app-config
  - name: nginx-config
    configMap:
      name: app-config
      items:
      - key: nginx.conf
        path: default.conf

Secret 생성 및 사용

# secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: app-secrets
type: Opaque
data:
  # base64 인코딩된 데이터
  username: YWRtaW4=  # admin
  password: MWYyZDFlMmU2N2Rm  # 1f2d1e2e67df
stringData:
  # 평문 데이터 (자동으로 base64 인코딩됨)
  api-key: "super-secret-key"
  database-url: "postgresql://user:pass@db:5432/mydb"

---
# TLS 시크릿
apiVersion: v1
kind: Secret
metadata:
  name: tls-secret
type: kubernetes.io/tls
data:
  tls.crt: LS0tLS1CRUdJTi... # base64 encoded cert
  tls.key: LS0tLS1CRUdJTi... # base64 encoded key

Secret 사용

# secret-usage.yaml
apiVersion: v1
kind: Pod
metadata:
  name: secret-pod
spec:
  containers:
  - name: app
    image: nginx:1.20

    # 환경 변수로 사용
    env:
    - name: DB_USERNAME
      valueFrom:
        secretKeyRef:
          name: app-secrets
          key: username
    - name: DB_PASSWORD
      valueFrom:
        secretKeyRef:
          name: app-secrets
          key: password

    # 볼륨으로 마운트
    volumeMounts:
    - name: secret-volume
      mountPath: /etc/secrets
      readOnly: true

  volumes:
  - name: secret-volume
    secret:
      secretName: app-secrets
      defaultMode: 0400  # 읽기 전용

ConfigMap/Secret 관리 명령어

# ConfigMap 생성
kubectl create configmap app-config --from-literal=key1=value1 --from-literal=key2=value2
kubectl create configmap app-config --from-file=config.properties
kubectl create configmap app-config --from-env-file=.env

# Secret 생성
kubectl create secret generic app-secrets --from-literal=username=admin --from-literal=password=secret
kubectl create secret tls tls-secret --cert=tls.crt --key=tls.key
kubectl create secret docker-registry regcred --docker-server=registry.com --docker-username=user --docker-password=pass

# 확인
kubectl get configmap
kubectl get secret
kubectl describe configmap app-config
kubectl get secret app-secrets -o yaml

배포 전략과 관리

Deployment

Deployment는 애플리케이션의 선언적 업데이트를 제공하는 가장 중요한 오브젝트입니다.

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-deployment
  labels:
    app: web
spec:
  replicas: 3
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1      # 동시에 삭제 가능한 Pod 수
      maxSurge: 1           # 동시에 생성 가능한 추가 Pod 수
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
        version: v1.0
    spec:
      containers:
      - name: web
        image: nginx:1.20
        ports:
        - containerPort: 80
        resources:
          requests:
            memory: "128Mi"
            cpu: "100m"
          limits:
            memory: "256Mi"
            cpu: "200m"
        livenessProbe:
          httpGet:
            path: /
            port: 80
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /
            port: 80
          initialDelaySeconds: 5
          periodSeconds: 5

ReplicaSet

# replicaset.yaml
apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: web-replicaset
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
      - name: web
        image: nginx:1.20
        ports:
        - containerPort: 80

StatefulSet

# statefulset.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: postgres-statefulset
spec:
  serviceName: postgres-service
  replicas: 3
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      containers:
      - name: postgres
        image: postgres:14
        env:
        - name: POSTGRES_PASSWORD
          valueFrom:
            secretKeyRef:
              name: postgres-secret
              key: password
        ports:
        - containerPort: 5432
        volumeMounts:
        - name: postgres-storage
          mountPath: /var/lib/postgresql/data

  # 볼륨 템플릿 (각 Pod마다 개별 볼륨)
  volumeClaimTemplates:
  - metadata:
      name: postgres-storage
    spec:
      accessModes: ["ReadWriteOnce"]
      resources:
        requests:
          storage: 10Gi

DaemonSet

# daemonset.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: node-exporter
  labels:
    app: node-exporter
spec:
  selector:
    matchLabels:
      app: node-exporter
  template:
    metadata:
      labels:
        app: node-exporter
    spec:
      hostNetwork: true  # 호스트 네트워크 사용
      containers:
      - name: node-exporter
        image: prom/node-exporter:latest
        ports:
        - containerPort: 9100
        volumeMounts:
        - name: proc
          mountPath: /host/proc
          readOnly: true
        - name: sys
          mountPath: /host/sys
          readOnly: true
      volumes:
      - name: proc
        hostPath:
          path: /proc
      - name: sys
        hostPath:
          path: /sys

Job과 CronJob

# job.yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: data-migration
spec:
  completions: 1      # 성공적으로 완료되어야 하는 Pod 수
  parallelism: 1      # 동시에 실행할 Pod 수
  backoffLimit: 3     # 실패 허용 횟수
  template:
    spec:
      containers:
      - name: migration
        image: my-app:latest
        command: ["python", "migrate.py"]
      restartPolicy: Never  # Job에서는 Never 또는 OnFailure

---
# cronjob.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
  name: backup-cronjob
spec:
  schedule: "0 2 * * *"  # 매일 새벽 2시
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: backup
            image: backup-tool:latest
            command:
            - /bin/sh
            - -c
            - |
              echo "Starting backup..."
              pg_dump -h postgres -U user mydb > /backup/backup-$(date +%Y%m%d).sql
              echo "Backup completed"
          restartPolicy: OnFailure
  successfulJobsHistoryLimit: 3
  failedJobsHistoryLimit: 1

배포 관리 명령어

# Deployment 관리
kubectl create deployment web --image=nginx:1.20
kubectl get deployments
kubectl describe deployment web

# 스케일링
kubectl scale deployment web --replicas=5
kubectl autoscale deployment web --min=2 --max=10 --cpu-percent=80

# 롤링 업데이트
kubectl set image deployment/web web=nginx:1.21
kubectl rollout status deployment/web
kubectl rollout history deployment/web
kubectl rollout undo deployment/web  # 이전 버전으로 롤백
kubectl rollout undo deployment/web --to-revision=2  # 특정 버전으로 롤백

# 배포 일시 중지/재개
kubectl rollout pause deployment/web
kubectl rollout resume deployment/web

Ingress와 로드밸런싱

Ingress Controller 설치

# NGINX Ingress Controller
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.2/deploy/static/provider/cloud/deploy.yaml

# Traefik Ingress Controller
helm repo add traefik https://helm.traefik.io/traefik
helm repo update
helm install traefik traefik/traefik

# 설치 확인
kubectl get pods -n ingress-nginx
kubectl get svc -n ingress-nginx

기본 Ingress 설정

# basic-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx
  rules:
  - host: myapp.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: web-service
            port:
              number: 80

고급 Ingress 설정

# advanced-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: advanced-ingress
  annotations:
    # NGINX 특정 설정
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
    nginx.ingress.kubernetes.io/cors-allow-origin: "*"
    nginx.ingress.kubernetes.io/rate-limit: "10"
    nginx.ingress.kubernetes.io/proxy-body-size: "50m"

    # 캐싱 설정
    nginx.ingress.kubernetes.io/server-snippet: |
      location ~* \.(js|css|png|jpg)$ {
        expires 1y;
        add_header Cache-Control "public";
      }
spec:
  ingressClassName: nginx

  # TLS 설정
  tls:
  - hosts:
    - myapp.example.com
    - api.example.com
    secretName: tls-secret

  rules:
  # 메인 앱
  - host: myapp.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: frontend-service
            port:
              number: 80

  # API 서버
  - host: api.example.com
    http:
      paths:
      - path: /api/v1
        pathType: Prefix
        backend:
          service:
            name: api-service
            port:
              number: 8080
      - path: /api/v2
        pathType: Prefix
        backend:
          service:
            name: api-v2-service
            port:
              number: 8080

  # 경로 기반 라우팅
  - host: services.example.com
    http:
      paths:
      - path: /auth
        pathType: Prefix
        backend:
          service:
            name: auth-service
            port:
              number: 3000
      - path: /payment
        pathType: Prefix
        backend:
          service:
            name: payment-service
            port:
              number: 4000

완전한 웹 애플리케이션 예시

# complete-web-app.yaml
# Frontend Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
spec:
  replicas: 2
  selector:
    matchLabels:
      app: frontend
  template:
    metadata:
      labels:
        app: frontend
    spec:
      containers:
      - name: frontend
        image: nginx:1.20
        ports:
        - containerPort: 80

---
# Frontend Service
apiVersion: v1
kind: Service
metadata:
  name: frontend-service
spec:
  selector:
    app: frontend
  ports:
  - port: 80
    targetPort: 80

---
# Backend Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend
spec:
  replicas: 3
  selector:
    matchLabels:
      app: backend
  template:
    metadata:
      labels:
        app: backend
    spec:
      containers:
      - name: backend
        image: node:18-alpine
        ports:
        - containerPort: 3000
        env:
        - name: NODE_ENV
          value: production

---
# Backend Service
apiVersion: v1
kind: Service
metadata:
  name: backend-service
spec:
  selector:
    app: backend
  ports:
  - port: 3000
    targetPort: 3000

---
# Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web-app-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
  ingressClassName: nginx
  rules:
  - host: myapp.local
    http:
      paths:
      - path: /()(.*)
        pathType: Prefix
        backend:
          service:
            name: frontend-service
            port:
              number: 80
      - path: /api(/|$)(.*)
        pathType: Prefix
        backend:
          service:
            name: backend-service
            port:
              number: 3000

오토스케일링

Horizontal Pod Autoscaler (HPA)

# hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: web-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: web-deployment
  minReplicas: 2
  maxReplicas: 20
  metrics:
  # CPU 사용률 기반
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70

  # 메모리 사용률 기반
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80

  # 커스텀 메트릭 기반
  - type: Pods
    pods:
      metric:
        name: packets-per-second
      target:
        type: AverageValue
        averageValue: "1k"

  # 외부 메트릭 기반
  - type: External
    external:
      metric:
        name: pubsub.googleapis.com|subscription|num_undelivered_messages
        selector:
          matchLabels:
            resource.labels.subscription_id: my-subscription
      target:
        type: AverageValue
        averageValue: "30"

  behavior:
    scaleDown:
      stabilizationWindowSeconds: 300
      policies:
      - type: Percent
        value: 10
        periodSeconds: 60
    scaleUp:
      stabilizationWindowSeconds: 0
      policies:
      - type: Percent
        value: 100
        periodSeconds: 15
      - type: Pods
        value: 4
        periodSeconds: 15
      selectPolicy: Max

Vertical Pod Autoscaler (VPA)

# vpa.yaml
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
  name: web-vpa
spec:
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: web-deployment
  updatePolicy:
    updateMode: "Auto"  # Off, Initial, Auto
  resourcePolicy:
    containerPolicies:
    - containerName: web
      minAllowed:
        cpu: 100m
        memory: 50Mi
      maxAllowed:
        cpu: 1
        memory: 500Mi
      controlledResources: ["cpu", "memory"]

Cluster Autoscaler 설정

# cluster-autoscaler.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: cluster-autoscaler
  namespace: kube-system
spec:
  replicas: 1
  selector:
    matchLabels:
      app: cluster-autoscaler
  template:
    metadata:
      labels:
        app: cluster-autoscaler
    spec:
      containers:
      - image: k8s.gcr.io/autoscaling/cluster-autoscaler:v1.21.0
        name: cluster-autoscaler
        resources:
          limits:
            cpu: 100m
            memory: 300Mi
          requests:
            cpu: 100m
            memory: 300Mi
        command:
        - ./cluster-autoscaler
        - --v=4
        - --stderrthreshold=info
        - --cloud-provider=aws
        - --skip-nodes-with-local-storage=false
        - --expander=least-waste
        - --node-group-auto-discovery=asg:tag=k8s.io/cluster-autoscaler/enabled,k8s.io/cluster-autoscaler/my-cluster
        - --balance-similar-node-groups
        - --skip-nodes-with-system-pods=false

스케일링 관리 명령어

# HPA 생성 및 관리
kubectl autoscale deployment web-deployment --cpu-percent=50 --min=1 --max=10
kubectl get hpa
kubectl describe hpa web-hpa

# 메트릭 서버 설치 (필수)
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

# 리소스 사용량 확인
kubectl top nodes
kubectl top pods

# VPA 설치
git clone https://github.com/kubernetes/autoscaler.git
cd autoscaler/vertical-pod-autoscaler/
./hack/vpa-install.sh

Helm 패키지 관리

Helm 설치

# macOS
brew install helm

# Windows (Chocolatey)
choco install kubernetes-helm

# Linux
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash

# 버전 확인
helm version

Helm 기본 사용법

# 레포지토리 관리
helm repo add stable https://charts.helm.sh/stable
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
helm repo list

# 차트 검색
helm search repo nginx
helm search hub wordpress

# 차트 정보 확인
helm show chart bitnami/nginx
helm show values bitnami/nginx > nginx-values.yaml

Helm 차트 생성

# 새 차트 생성
helm create my-app

# 생성된 구조
my-app/
├── Chart.yaml          # 차트 메타데이터
├── values.yaml         # 기본 값들
├── charts/             # 의존성 차트들
├── templates/          # 쿠버네티스 매니페스트 템플릿
│   ├── deployment.yaml
│   ├── service.yaml
│   ├── ingress.yaml
│   ├── _helpers.tpl    # 템플릿 헬퍼들
│   └── tests/
└── .helmignore

Chart.yaml 예시

# Chart.yaml
apiVersion: v2
name: my-web-app
description: A Helm chart for my web application
type: application
version: 0.1.0
appVersion: "1.0.0"
maintainers:
- name: Your Name
  email: your-email@example.com
dependencies:
- name: postgresql
  version: 11.9.13
  repository: https://charts.bitnami.com/bitnami
  condition: postgresql.enabled
- name: redis
  version: 17.3.7
  repository: https://charts.bitnami.com/bitnami
  condition: redis.enabled

values.yaml 예시

# values.yaml
replicaCount: 2

image:
  repository: nginx
  tag: "1.20"
  pullPolicy: IfNotPresent

service:
  type: ClusterIP
  port: 80
  targetPort: 8080

ingress:
  enabled: true
  className: "nginx"
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
  hosts:
  - host: myapp.local
    paths:
    - path: /
      pathType: Prefix
  tls:
  - secretName: myapp-tls
    hosts:
    - myapp.local

resources:
  limits:
    cpu: 500m
    memory: 512Mi
  requests:
    cpu: 250m
    memory: 256Mi

autoscaling:
  enabled: true
  minReplicas: 2
  maxReplicas: 10
  targetCPUUtilizationPercentage: 80

postgresql:
  enabled: true
  auth:
    username: myapp
    password: mypassword
    database: myapp_db

redis:
  enabled: true
  auth:
    password: redis-password

템플릿 예시

# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "my-app.fullname" . }}
  labels:
    {{- include "my-app.labels" . | nindent 4 }}
spec:
  {{- if not .Values.autoscaling.enabled }}
  replicas: {{ .Values.replicaCount }}
  {{- end }}
  selector:
    matchLabels:
      {{- include "my-app.selectorLabels" . | nindent 6 }}
  template:
    metadata:
      labels:
        {{- include "my-app.selectorLabels" . | nindent 8 }}
    spec:
      containers:
      - name: {{ .Chart.Name }}
        image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
        imagePullPolicy: {{ .Values.image.pullPolicy }}
        ports:
        - name: http
          containerPort: {{ .Values.service.targetPort }}
          protocol: TCP
        env:
        {{- if .Values.postgresql.enabled }}
        - name: DATABASE_URL
          value: "postgresql://{{ .Values.postgresql.auth.username }}:{{ .Values.postgresql.auth.password }}@{{ include "my-app.fullname" . }}-postgresql:5432/{{ .Values.postgresql.auth.database }}"
        {{- end }}
        {{- if .Values.redis.enabled }}
        - name: REDIS_URL
          value: "redis://:{{ .Values.redis.auth.password }}@{{ include "my-app.fullname" . }}-redis:6379"
        {{- end }}
        livenessProbe:
          httpGet:
            path: /health
            port: http
        readinessProbe:
          httpGet:
            path: /ready
            port: http
        resources:
          {{- toYaml .Values.resources | nindent 12 }}

Helm 배포 관리

# 차트 설치
helm install my-release ./my-app
helm install my-release ./my-app --values custom-values.yaml
helm install my-release ./my-app --set replicaCount=3 --set image.tag=v2.0

# 릴리스 관리
helm list
helm list --all-namespaces
helm status my-release
helm get values my-release

# 업그레이드
helm upgrade my-release ./my-app --values new-values.yaml
helm upgrade my-release ./my-app --reuse-values --set image.tag=v2.1

# 롤백
helm rollback my-release 1  # 버전 1로 롤백
helm history my-release

# 삭제
helm uninstall my-release
helm uninstall my-release --keep-history

# 차트 검증
helm lint ./my-app
helm template my-release ./my-app --debug
helm install --dry-run --debug my-release ./my-app

모니터링과 로깅

Prometheus + Grafana 스택

# Prometheus Operator 설치
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update

# kube-prometheus-stack 설치 (Prometheus + Grafana + Alertmanager)
helm install monitoring prometheus-community/kube-prometheus-stack \
  --namespace monitoring \
  --create-namespace \
  --set prometheus.prometheusSpec.serviceMonitorSelectorNilUsesHelmValues=false \
  --set prometheus.prometheusSpec.retention=15d \
  --set grafana.adminPassword=admin123

커스텀 ServiceMonitor

# service-monitor.yaml
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: my-app-monitor
  labels:
    app: my-app
spec:
  selector:
    matchLabels:
      app: my-app
  endpoints:
  - port: metrics
    path: /metrics
    interval: 30s

애플리케이션 메트릭 노출

# app-with-metrics.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - name: app
        image: my-app:latest
        ports:
        - name: http
          containerPort: 8080
        - name: metrics
          containerPort: 9090
        env:
        - name: METRICS_PORT
          value: "9090"

---
apiVersion: v1
kind: Service
metadata:
  name: my-app-service
  labels:
    app: my-app
spec:
  selector:
    app: my-app
  ports:
  - name: http
    port: 80
    targetPort: 8080
  - name: metrics
    port: 9090
    targetPort: 9090

ELK 스택 (Elasticsearch, Logstash, Kibana)

# elk-stack.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: elasticsearch
spec:
  serviceName: elasticsearch
  replicas: 1
  selector:
    matchLabels:
      app: elasticsearch
  template:
    metadata:
      labels:
        app: elasticsearch
    spec:
      containers:
      - name: elasticsearch
        image: elasticsearch:8.5.0
        env:
        - name: discovery.type
          value: single-node
        - name: ES_JAVA_OPTS
          value: "-Xms512m -Xmx512m"
        - name: xpack.security.enabled
          value: "false"
        ports:
        - containerPort: 9200
        volumeMounts:
        - name: data
          mountPath: /usr/share/elasticsearch/data
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: ["ReadWriteOnce"]
      resources:
        requests:
          storage: 10Gi

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: kibana
spec:
  replicas: 1
  selector:
    matchLabels:
      app: kibana
  template:
    metadata:
      labels:
        app: kibana
    spec:
      containers:
      - name: kibana
        image: kibana:8.5.0
        env:
        - name: ELASTICSEARCH_HOSTS
          value: http://elasticsearch:9200
        ports:
        - containerPort: 5601

---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluent-bit
spec:
  selector:
    matchLabels:
      app: fluent-bit
  template:
    metadata:
      labels:
        app: fluent-bit
    spec:
      containers:
      - name: fluent-bit
        image: fluent/fluent-bit:2.0
        env:
        - name: FLUENT_ELASTICSEARCH_HOST
          value: elasticsearch
        - name: FLUENT_ELASTICSEARCH_PORT
          value: "9200"
        volumeMounts:
        - name: varlog
          mountPath: /var/log
        - name: varlibdockercontainers
          mountPath: /var/lib/docker/containers
          readOnly: true
        - name: fluent-bit-config
          mountPath: /fluent-bit/etc/
      volumes:
      - name: varlog
        hostPath:
          path: /var/log
      - name: varlibdockercontainers
        hostPath:
          path: /var/lib/docker/containers
      - name: fluent-bit-config
        configMap:
          name: fluent-bit-config

로그 수집 설정

# fluent-bit-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: fluent-bit-config
data:
  fluent-bit.conf: |
    [SERVICE]
        Daemon Off
        Flush 1
        Log_Level info
        Parsers_File parsers.conf

    [INPUT]
        Name tail
        Path /var/log/containers/*.log
        Parser cri
        Tag kube.*
        Refresh_Interval 5
        Mem_Buf_Limit 10MB

    [FILTER]
        Name kubernetes
        Match kube.*
        Kube_URL https://kubernetes.default.svc:443
        Kube_CA_File /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
        Kube_Token_File /var/run/secrets/kubernetes.io/serviceaccount/token

    [OUTPUT]
        Name es
        Match *
        Host elasticsearch
        Port 9200
        Index k8s-logs
        Type _doc

  parsers.conf: |
    [PARSER]
        Name cri
        Format regex
        Regex ^(?<time>[^ ]+) (?<stream>stdout|stderr) (?<logtag>[^ ]*) (?<message>.*)$
        Time_Key time
        Time_Format %Y-%m-%dT%H:%M:%S.%L%z

보안

RBAC (Role-Based Access Control)

# rbac.yaml
# ServiceAccount 생성
apiVersion: v1
kind: ServiceAccount
metadata:
  name: my-service-account
  namespace: default

---
# Role 생성 (네임스페이스 범위)
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: pod-reader
  namespace: default
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "watch", "list"]
- apiGroups: [""]
  resources: ["secrets"]
  verbs: ["get"]

---
# ClusterRole 생성 (클러스터 전체 범위)
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: node-reader
rules:
- apiGroups: [""]
  resources: ["nodes"]
  verbs: ["get", "watch", "list"]

---
# RoleBinding 생성
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-pods
  namespace: default
subjects:
- kind: ServiceAccount
  name: my-service-account
  namespace: default
- kind: User
  name: jane
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

---
# ClusterRoleBinding 생성
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: read-nodes
subjects:
- kind: ServiceAccount
  name: my-service-account
  namespace: default
roleRef:
  kind: ClusterRole
  name: node-reader
  apiGroup: rbac.authorization.k8s.io

Pod Security Standards

# pod-security.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: secure-namespace
  labels:
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/audit: restricted
    pod-security.kubernetes.io/warn: restricted

---
# 보안이 강화된 Pod
apiVersion: v1
kind: Pod
metadata:
  name: secure-pod
  namespace: secure-namespace
spec:
  securityContext:
    runAsNonRoot: true
    runAsUser: 1000
    runAsGroup: 3000
    fsGroup: 2000
    seccompProfile:
      type: RuntimeDefault

  containers:
  - name: app
    image: nginx:1.20
    securityContext:
      allowPrivilegeEscalation: false
      readOnlyRootFilesystem: true
      runAsNonRoot: true
      runAsUser: 1000
      capabilities:
        drop:
        - ALL
    resources:
      limits:
        memory: "128Mi"
        cpu: "500m"
      requests:
        memory: "64Mi"
        cpu: "250m"
    volumeMounts:
    - name: tmp
      mountPath: /tmp
    - name: var-cache
      mountPath: /var/cache/nginx
    - name: var-run
      mountPath: /var/run

  volumes:
  - name: tmp
    emptyDir: {}
  - name: var-cache
    emptyDir: {}
  - name: var-run
    emptyDir: {}

NetworkPolicy

# network-policy.yaml
# 기본적으로 모든 트래픽 차단
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: production
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress

---
# 웹 앱에 대한 허용 정책
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: web-netpol
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: web
  policyTypes:
  - Ingress
  - Egress

  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          name: ingress-nginx
    ports:
    - protocol: TCP
      port: 80

  egress:
  # DNS 허용
  - to: []
    ports:
    - protocol: UDP
      port: 53

  # 데이터베이스 접근 허용
  - to:
    - podSelector:
        matchLabels:
          app: database
    ports:
    - protocol: TCP
      port: 5432

  # 외부 API 허용
  - to: []
    ports:
    - protocol: TCP
      port: 443

이미지 보안 스캔

# admission-controller.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: image-policy
data:
  policy.json: |
    {
      "imagePolicy": {
        "rules": [
          {
            "resource": "pods",
            "allowed": [
              "registry.company.com/*",
              "docker.io/library/*"
            ],
            "denied": [
              "*:latest"
            ]
          }
        ]
      }
    }

운영 베스트 프랙티스

리소스 관리

# resource-management.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
  name: compute-resources
  namespace: development
spec:
  hard:
    requests.cpu: "4"
    requests.memory: 8Gi
    limits.cpu: "8"
    limits.memory: 16Gi
    persistentvolumeclaims: "4"
    pods: "10"

---
apiVersion: v1
kind: LimitRange
metadata:
  name: mem-limit-range
  namespace: development
spec:
  limits:
  - default:
      memory: "512Mi"
      cpu: "500m"
    defaultRequest:
      memory: "256Mi"
      cpu: "250m"
    type: Container

헬스 체크

# health-checks.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: robust-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: robust-app
  template:
    metadata:
      labels:
        app: robust-app
    spec:
      containers:
      - name: app
        image: my-app:latest
        ports:
        - containerPort: 8080

        # 시작 프로브 - 컨테이너가 시작되었는지 확인
        startupProbe:
          httpGet:
            path: /startup
            port: 8080
          failureThreshold: 30
          periodSeconds: 10
          timeoutSeconds: 5

        # 라이브니스 프로브 - 컨테이너가 살아있는지 확인
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10
          timeoutSeconds: 5
          failureThreshold: 3

        # 레디니스 프로브 - 트래픽을 받을 준비가 되었는지 확인
        readinessProbe:
          httpGet:
            path: /ready
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 5
          timeoutSeconds: 3
          failureThreshold: 3

배포 전략

# deployment-strategies.yaml
# Blue-Green 배포용 Service
apiVersion: v1
kind: Service
metadata:
  name: blue-green-service
spec:
  selector:
    app: my-app
    version: blue  # blue 또는 green으로 전환
  ports:
  - port: 80
    targetPort: 8080

---
# Canary 배포용 설정
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: canary-rollout
spec:
  replicas: 10
  strategy:
    canary:
      steps:
      - setWeight: 10    # 10% 트래픽
      - pause:
          duration: 10m  # 10분 대기
      - setWeight: 50    # 50% 트래픽
      - pause:
          duration: 5m   # 5분 대기
      - setWeight: 100   # 100% 트래픽
      canaryService: canary-service
      stableService: stable-service
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - name: app
        image: my-app:latest

백업 및 재해 복구

# backup-cronjob.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
  name: etcd-backup
spec:
  schedule: "0 2 * * *"  # 매일 새벽 2시
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: etcd-backup
            image: quay.io/coreos/etcd:latest
            env:
            - name: ETCDCTL_API
              value: "3"
            command:
            - /bin/sh
            - -c
            - |
              etcdctl --endpoints=https://etcd:2379 \
                --cacert=/etc/ssl/etcd/ca.crt \
                --cert=/etc/ssl/etcd/etcd.crt \
                --key=/etc/ssl/etcd/etcd.key \
                snapshot save /backup/etcd-$(date +%Y%m%d-%H%M%S).db
            volumeMounts:
            - name: backup-storage
              mountPath: /backup
          volumes:
          - name: backup-storage
            persistentVolumeClaim:
              claimName: backup-pvc
          restartPolicy: OnFailure

클러스터 업그레이드 절차

# 1. 현재 버전 확인
kubectl version --short

# 2. 업그레이드 가능한 버전 확인
sudo apt update
sudo apt-cache madison kubeadm

# 3. kubeadm 업그레이드 (마스터 노드)
sudo apt-mark unhold kubeadm
sudo apt-get update
sudo apt-get install -y kubeadm=1.28.x-00
sudo apt-mark hold kubeadm

# 4. 업그레이드 계획 확인
sudo kubeadm upgrade plan

# 5. 마스터 노드 업그레이드
sudo kubeadm upgrade apply v1.28.x

# 6. kubelet과 kubectl 업그레이드
sudo apt-mark unhold kubelet kubectl
sudo apt-get update
sudo apt-get install -y kubelet=1.28.x-00 kubectl=1.28.x-00
sudo apt-mark hold kubelet kubectl
sudo systemctl daemon-reload
sudo systemctl restart kubelet

# 7. 워커 노드 업그레이드 (각 노드에서)
kubectl drain worker-node --ignore-daemonsets
sudo kubeadm upgrade node
# kubelet, kubectl 업그레이드 (위와 동일)
kubectl uncordon worker-node

결론

Kubernetes는 현대 클라우드 네이티브 애플리케이션의 핵심 인프라입니다. 복잡하지만 체계적으로 학습하고 실습하면 강력한 컨테이너 오케스트레이션 플랫폼을 운영할 수 있습니다.

Kubernetes 마스터 로드맵

  1. 기초: Docker 기본, K8s 개념, Pod/Service 이해
  2. 중급: Deployment, ConfigMap, Ingress, 스토리지 관리
  3. 고급: RBAC, 네트워크 정책, 모니터링, Helm
  4. 전문가: 멀티 클러스터, 서비스 메시, GitOps, 운영 자동화

실무 적용 단계

  1. 로컬 환경: minikube/kind로 학습 환경 구축
  2. 개발 환경: 단순한 애플리케이션 배포
  3. 스테이징: 프로덕션과 유사한 환경 구축
  4. 프로덕션: 고가용성, 보안, 모니터링 완비

지속적 학습 리소스

마지막 조언

  • 실습 중심: 이론보다 직접 클러스터 운영 경험이 중요
  • 점진적 적용: 한 번에 모든 기능을 도입하지 말고 단계적으로
  • 모니터링 필수: 클러스터와 애플리케이션 상태를 지속적으로 관찰
  • 보안 우선: 처음부터 보안을 고려한 설계와 운영
  • 문서화: 클러스터 구성과 운영 절차를 반드시 문서화

Kubernetes를 마스터하고 클라우드 네이티브 시대의 핵심 기술을 정복해보세요! ☸️