TechFeedTechFeed
Cloud & DevOps

Kubernetes 프로덕션 운영 가이드 — 리소스 관리, HPA, Probe, 장애 대응 실전 총정리

Kubernetes를 프로덕션에서 안정적으로 운영하기 위한 실전 가이드. requests/limits 설정 전략, HPA 자동 스케일링, Liveness·Readiness·Startup Probe 올바른 구성, ConfigMap·Secret 분리 패턴, RBAC 멀티팀 격리, Helm 배포 관리, 장애 대응 kubectl 명령어까지 코드 중심으로 정리.

Kubernetes는 로컬에서 돌릴 때와 프로덕션에서 운영할 때가 완전히 다른 도구다. kubectl apply가 성공했다고 끝이 아니다. 리소스 요청을 빠뜨리면 노드 한 대가 OOM으로 다운되고, Probe 설정이 잘못되면 멀쩡한 Pod가 무한 재시작된다. HPA를 켰는데 스케일아웃이 너무 늦으면 트래픽 피크에 서비스가 터진다.

이 글은 Kubernetes 기초를 이미 아는 개발자를 위한 프로덕션 운영 가이드다. 리소스 관리, HPA, Probe, ConfigMap/Secret, RBAC, Helm, 장애 대응을 코드 중심으로 정리했다. "왜 이렇게 설정하는가"에 집중했다. 이미 K8s 클러스터를 운영하거나 곧 운영할 팀에게 필요한 실전 판단 기준을 제공한다.

Kubernetes 프로덕션 환경이 로컬과 다른 이유

로컬 Minikube나 kind 클러스터에서는 노드가 1개뿐이고 리소스가 넉넉하다. 실수를 해도 Pod를 지우고 다시 올리면 그만이다. 프로덕션은 다르다. 수십 개의 노드가 있고, 노드마다 다른 워크로드가 경쟁한다. 한 Pod의 리소스 과소비가 같은 노드의 다른 서비스에 영향을 미친다. 배포 실수가 실서비스 다운으로 이어진다.

프로덕션 K8s 운영에서 반드시 다뤄야 할 영역은 다음 다섯 가지다.

  • 리소스 관리: requests/limits를 잘못 설정하면 노드가 OOM으로 다운되거나 Pod가 무한 Pending 상태가 된다.
  • HPA(Horizontal Pod Autoscaler): 트래픽 변화에 맞춰 자동으로 Pod 수를 늘리고 줄인다. 잘못 설정하면 스케일링 루프(flapping)가 발생한다.
  • Probe: Liveness/Readiness/Startup Probe는 K8s가 Pod 상태를 판단하는 유일한 수단이다. 설정 실수가 서비스 중단을 유발한다.
  • 설정 분리: ConfigMap과 Secret을 사용해 코드와 환경 설정을 분리해야 한다. 잘못된 Secret 관리는 보안 사고로 이어진다.
  • RBAC: 팀이 커질수록 권한 범위를 명확히 정의해야 한다. 과도한 권한은 실수로 인한 장애 범위를 키운다.

이 글은 각 영역의 실제 YAML 예시와 함께 "어떤 값이 맞는가"보다 "어떻게 생각하면 되는가"를 설명한다.

Kubernetes 프로덕션 아키텍처 구성 요소
Kubernetes 프로덕션 클러스터에서 운영자가 직접 관리해야 하는 영역들

리소스 requests와 limits — 가장 흔한 실수

requests는 스케줄러가 Pod를 노드에 배치할 때 기준으로 삼는 값이다. 노드의 allocatable 리소스 중 requests 합계가 초과되면 그 노드엔 배치하지 않는다. limits는 실제로 쓸 수 있는 최대값이다. CPU는 limits를 초과하면 throttling되고, 메모리는 limits를 초과하면 OOMKilled된다.

가장 흔한 실수 세 가지는 이렇다. 첫째, requests를 아예 안 쓰는 경우다. Scheduler가 Best Effort Pod로 분류하고 노드 리소스가 부족하면 제일 먼저 축출한다. 둘째, requests와 limits를 동일하게 설정하는 경우다(Guaranteed QoS). CPU 사용이 잠깐만 치솟아도 throttling이 발생해 레이턴시가 급등한다. 셋째, limits를 너무 낮게 설정하는 경우다. 정상 동작 중에도 OOMKilled가 반복된다.

권장 패턴은 requests를 평균 사용량 기준으로, limits를 피크 사용량의 150~200%로 설정하는 것이다. Vertical Pod Autoscaler(VPA)를 사용하면 실제 사용 패턴에서 자동으로 적정 값을 산출해준다.

deployment-resources.yaml — requests/limits 설정 패턴
apiVersion: apps/v1 kind: Deployment metadata: name: api-server spec: replicas: 3 selector: matchLabels: app: api-server template: metadata: labels: app: api-server spec: containers: - name: api image: myapp:1.0.0 resources: requests: # 스케줄러 배치 기준: 실제 평균 사용량 기준으로 설정 cpu: "250m" # 0.25 vCPU memory: "256Mi" limits: # 실제 허용 상한: 피크의 150~200% cpu: "500m" # 0.5 vCPU (throttling이지 OOM은 아님) memory: "512Mi" # 초과 시 OOMKilled --- # LimitRange: Namespace 기본값 설정 (requests 없는 Pod 방지) apiVersion: v1 kind: LimitRange metadata: name: default-limits namespace: production spec: limits: - type: Container default: # limits 미지정 시 이 값이 적용됨 cpu: "500m" memory: "512Mi" defaultRequest: # requests 미지정 시 이 값이 적용됨 cpu: "100m" memory: "128Mi" max: cpu: "4" memory: "4Gi" --- # ResourceQuota: Namespace 전체 상한 apiVersion: v1 kind: ResourceQuota metadata: name: namespace-quota namespace: production spec: hard: requests.cpu: "20" requests.memory: "40Gi" limits.cpu: "40" limits.memory: "80Gi" pods: "100"

CPU throttling 문제는 눈에 잘 안 띈다. Pod가 죽지 않고 그냥 느려지기 때문이다. Prometheus를 쓴다면 container_cpu_cfs_throttled_seconds_total 메트릭으로 throttling 비율을 확인할 수 있다. 이 값이 20% 이상이면 limits 값을 올리거나 더 많은 Replica를 운영하는 방향을 고려해야 한다.

메모리 OOMKilled는 kubectl get events --field-selector reason=OOMKilling으로 확인한다. Pod의 Status가 OOMKilled이고 Exit Code가 137이면 메모리 limits 초과가 원인이다.

Java/JVM 언어 주의사항: JVM은 기본적으로 컨테이너 메모리 limits를 인식하지 못하고 호스트 전체 RAM을 기준으로 힙 크기를 잡는다. -XX:+UseContainerSupport (JDK 11+는 기본값) 옵션 확인이 필수다. -Xmx를 명시적으로 지정하거나, limits 메모리의 75% 이하로 힙을 설정해야 컨테이너 OOM을 방지할 수 있다.

HPA로 자동 수평 확장 — 설정에서 주의사항까지

HPA(Horizontal Pod Autoscaler)는 메트릭 기반으로 Deployment의 replica 수를 자동으로 조절한다. CPU/메모리 사용률이 가장 기본이고, Prometheus Adapter를 통해 커스텀 메트릭(RPS, 큐 길이 등)도 사용할 수 있다. HPA v2(autoscaling/v2)는 K8s 1.23부터 stable이며, 복수의 메트릭을 함께 쓸 수 있다.

HPA가 제대로 동작하려면 반드시 requests가 설정되어 있어야 한다. HPA의 CPU 사용률은 current CPU usage / requests로 계산한다. requests가 없으면 HPA가 동작하지 않는다. 이것이 requests 설정이 선택이 아닌 이유다.

스케일링 속도도 중요하다. 기본적으로 HPA는 15초마다 메트릭을 평가하고, scale-up은 즉시 반응하지만 scale-down은 5분(300초) 지연이 있다. 이 지연은 스케일링 루프(scale-up → 잠깐 낮아짐 → scale-down → 다시 치솟음 → scale-up)를 방지하기 위한 것이다.

hpa.yaml — HPA v2 설정
apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: api-server-hpa spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: api-server minReplicas: 3 # 최소값: 단일 장애점 방지 maxReplicas: 20 # 최대값: 비용/인프라 상한 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 60 # CPU requests의 60% 초과 시 scale-up - type: Resource resource: name: memory target: type: Utilization averageUtilization: 70 # 메모리 requests의 70% 초과 시 scale-up behavior: scaleUp: stabilizationWindowSeconds: 0 # scale-up은 즉시 반응 policies: - type: Pods value: 4 # 한 번에 최대 4개 추가 periodSeconds: 60 - type: Percent value: 100 # 또는 현재 replica의 100%까지 periodSeconds: 60 selectPolicy: Max # 두 policy 중 더 많이 scale-up하는 쪽 선택 scaleDown: stabilizationWindowSeconds: 300 # scale-down 5분 대기 (flapping 방지) policies: - type: Percent value: 10 # 한 번에 현재 replica의 10%만 감소 periodSeconds: 60 --- # HPA 상태 확인 # kubectl get hpa api-server-hpa # kubectl describe hpa api-server-hpa # NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS # api-server-hpa Deployment/api-server 45%/60% 3 20 5

HPA와 Cluster Autoscaler의 관계를 이해해야 한다. HPA는 Pod 수를 늘리지만, 노드에 Pod를 배치할 공간이 없으면 Pending 상태가 된다. Cluster Autoscaler(CA)는 Pending Pod를 감지해 노드를 추가한다. 둘 다 필요하고, 동작 순서는 HPA → CA다. CA가 새 노드를 추가하는 데 보통 3~5분이 걸리므로, 트래픽 피크 전에 미리 스케일아웃되어 있어야 서비스 품질을 유지할 수 있다. 이를 위해 KEDA(Kubernetes Event-Driven Autoscaling)를 사용하면 메시지 큐 길이나 HTTP 요청 속도 같은 이벤트 기반 메트릭으로 더 빠르게 선제 스케일링이 가능하다.

Liveness · Readiness · Startup Probe 완전 정복

세 가지 Probe는 각자 다른 역할을 한다. Startup Probe는 컨테이너가 처음 뜨는 동안 다른 Probe를 비활성화한다. 시작이 느린 앱(JVM, ML 모델 로딩 등)에 필수다. 없으면 Liveness Probe가 시작 직후 실패해 Pod가 무한 재시작 루프에 빠진다. Readiness Probe는 Pod가 트래픽을 받을 준비가 됐는지 판단한다. 실패하면 Service의 Endpoints에서 제거돼 트래픽이 오지 않는다. 죽지 않았지만 아직 준비 안 된 상태를 나타낸다. Liveness Probe는 Pod가 살아있는지 판단한다. 실패하면 kubelet이 컨테이너를 재시작한다. 데드락 감지 용도로만 써야 한다.

가장 흔한 실수는 Liveness와 Readiness에 같은 엔드포인트를 쓰는 것이다. DB 연결 확인 로직을 Liveness에 넣으면 DB가 잠깐 느려질 때마다 멀쩡한 Pod가 재시작된다. Liveness는 앱 자체의 생사만, Readiness는 의존성 포함 준비 상태를 체크해야 한다.

deployment-probes.yaml — 세 가지 Probe 올바른 설정
apiVersion: apps/v1 kind: Deployment metadata: name: api-server spec: template: spec: containers: - name: api image: myapp:1.0.0 ports: - containerPort: 8080 startupProbe: # 앱이 완전히 뜰 때까지 다른 Probe 비활성화 httpGet: path: /health/startup port: 8080 failureThreshold: 30 # 최대 30번 재시도 periodSeconds: 10 # 10초 간격 → 최대 300초(5분) 기다림 readinessProbe: # 트래픽 수신 준비 여부 (DB 연결 포함 체크 가능) httpGet: path: /health/ready port: 8080 initialDelaySeconds: 5 periodSeconds: 5 failureThreshold: 3 # 3번 실패 시 Endpoints에서 제거 successThreshold: 1 # 1번 성공하면 다시 포함 livenessProbe: # 앱 자체 생사만 체크 (외부 의존성 포함 금지) httpGet: path: /health/live # 단순히 200 OK 반환하는 엔드포인트 port: 8080 initialDelaySeconds: 30 periodSeconds: 10 failureThreshold: 3 timeoutSeconds: 5 # 5초 내 응답 없으면 실패 카운트 # /health/live → 무조건 200 반환 (DB 체크 없이 앱 프로세스만 확인) # /health/ready → DB, 캐시 등 의존성 연결 확인 후 200/503 반환 # /health/startup → 초기화 완료 여부 확인 후 200/503 반환
preStop 훅 + terminationGracePeriodSeconds: Pod 종료 시 진행 중인 요청이 끊기지 않으려면 lifecycle.preStopsleep 5를 넣어야 한다. Service Endpoints에서 제거되는 데 약간의 시간이 걸리기 때문이다. terminationGracePeriodSeconds(기본 30초)는 SIGTERM 이후 강제 종료까지의 유예 시간이다. 앱이 graceful shutdown에 필요한 시간보다 길게 설정해야 한다.
Kubernetes Probe 동작 흐름과 Pod 상태 변화
Startup → Readiness → Liveness 순서로 활성화되며, 각 Probe 실패 시 다른 결과를 낳는다

ConfigMap vs Secret — 환경 설정 분리 전략

ConfigMap은 민감하지 않은 설정(포트, 타임아웃, 피처 플래그 등)을, Secret은 민감한 정보(DB 패스워드, API 키, TLS 인증서 등)를 저장한다. 둘 다 etcd에 저장되지만, Secret은 base64 인코딩만 할 뿐 기본적으로 암호화되지 않는다. 진짜 보안을 위해서는 etcd 암호화(at-rest encryption) 활성화 또는 외부 Secret 관리 도구(Vault, AWS Secrets Manager, External Secrets Operator)를 써야 한다.

ConfigMap과 Secret을 Pod에 주입하는 방법은 두 가지다. 환경 변수로 주입하면 간편하지만, 앱 재시작 없이 변경이 반영되지 않는다. 볼륨 마운트로 파일로 주입하면 변경 시 약 60초 이내에 파일이 자동 갱신된다. 설정 핫리로드가 필요한 앱이라면 볼륨 마운트를 선택해야 한다.

configmap-secret.yaml — 설정 분리 패턴
# ConfigMap: 비민감 설정 apiVersion: v1 kind: ConfigMap metadata: name: api-config namespace: production data: APP_PORT: "8080" LOG_LEVEL: "info" CACHE_TTL: "300" config.yaml: | server: timeout: 30s max_connections: 1000 --- # Secret: 민감 정보 (base64 인코딩 필수) # echo -n 'mypassword' | base64 → bXlwYXNzd29yZA== apiVersion: v1 kind: Secret metadata: name: api-secrets namespace: production type: Opaque data: DB_PASSWORD: bXlwYXNzd29yZA== API_KEY: c2VjcmV0LWtleS0xMjM= # 또는 stringData 사용 시 base64 불필요 (K8s가 자동 인코딩) stringData: REDIS_URL: "redis://redis-service:6379" --- # Deployment에서 사용 apiVersion: apps/v1 kind: Deployment spec: template: spec: containers: - name: api envFrom: - configMapRef: name: api-config # ConfigMap 전체를 env로 - secretRef: name: api-secrets # Secret 전체를 env로 env: - name: DB_HOST # 특정 값만 선택 valueFrom: configMapKeyRef: name: api-config key: DB_HOST volumeMounts: - name: config-volume mountPath: /etc/config # 파일로 마운트 (핫리로드 가능) volumes: - name: config-volume configMap: name: api-config

프로덕션에서는 External Secrets Operator를 적극 권장한다. AWS Secrets Manager, GCP Secret Manager, HashiCorp Vault에 저장된 시크릿을 K8s Secret으로 자동 동기화해준다. 시크릿 값을 변경하면 지정한 주기마다 K8s Secret도 갱신되고, 볼륨 마운트를 쓰는 앱은 파일도 자동 갱신된다. K8s YAML에 실제 시크릿 값이 노출되지 않는다는 것이 가장 큰 장점이다. 특히 GitOps 워크플로우(ArgoCD, Flux)를 쓰는 팀에서는 거의 필수다.

RBAC와 Namespace — 멀티팀 환경 격리 전략

팀이 커질수록 "kubectl apply -f" 권한을 모든 팀원에게 주는 것은 위험하다. 한 팀원의 실수가 전체 클러스터에 영향을 미칠 수 있다. Namespace로 격리하고, RBAC로 각 팀의 권한을 제한해야 한다.

RBAC의 핵심 개념: Role은 특정 Namespace 내의 리소스 접근 권한을 정의한다. ClusterRole은 클러스터 전체 또는 특정 클러스터 레벨 리소스(Node, PersistentVolume 등)에 대한 권한이다. RoleBinding은 User/Group/ServiceAccount에게 Role을 부여한다. ClusterRoleBinding은 ClusterRole을 부여한다.

가장 실용적인 패턴은 ClusterRole을 정의하고 각 Namespace에서 RoleBinding으로 부여하는 것이다. 개발팀은 자신의 Namespace에서만 Deployment를 수정할 수 있고, 플랫폼팀은 클러스터 전체 읽기 권한을 갖는 식으로 구성한다.

rbac.yaml — 팀별 권한 분리 패턴
# ClusterRole: 재사용 가능한 권한 세트 정의 apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: developer-role rules: - apiGroups: ["apps"] resources: ["deployments", "replicasets", "statefulsets"] verbs: ["get", "list", "watch", "update", "patch"] - apiGroups: [""] resources: ["pods", "pods/log", "pods/exec", "services", "configmaps"] verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] - apiGroups: [""] resources: ["secrets"] verbs: ["get", "list"] # Secret 내용 읽기만 허용, 수정 불가 --- # 읽기 전용 Role apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: readonly-role rules: - apiGroups: ["*"] resources: ["*"] verbs: ["get", "list", "watch"] --- # RoleBinding: 특정 Namespace에서 팀에게 ClusterRole 부여 apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: backend-team-binding namespace: production # 이 Namespace에서만 유효 subjects: - kind: Group name: backend-team # OIDC 그룹 또는 직접 정의한 그룹 apiGroup: rbac.authorization.k8s.io roleRef: kind: ClusterRole name: developer-role apiGroup: rbac.authorization.k8s.io --- # ServiceAccount: Pod 내부에서 K8s API 접근 권한 apiVersion: v1 kind: ServiceAccount metadata: name: api-service-account namespace: production annotations: # AWS IAM Role 연결 (IRSA: IAM Roles for Service Accounts) eks.amazonaws.com/role-arn: arn:aws:iam::123456789:role/api-server-role --- # 현재 사용자 권한 확인 # kubectl auth can-i create deployments --namespace=production # kubectl auth can-i "*" "*" --all-namespaces

장애 대응 실전 — kubectl 디버깅 흐름

프로덕션 장애 상황에서 K8s 디버깅은 순서가 있다. 증상에 따라 어느 레이어를 먼저 봐야 하는지 파악해야 시간을 아낄 수 있다. 가장 흔한 증상별 접근법은 이렇다.

Pod가 뜨지 않을 때(Pending/CrashLoopBackOff/ImagePullBackOff): kubectl describe pod의 Events 섹션이 항상 첫 번째 단서다. Pending이면 리소스 부족이나 nodeSelector/affinity 미충족, CrashLoopBackOff이면 앱 자체 문제나 Probe 설정 오류, ImagePullBackOff이면 이미지 이름/태그 오류나 Private Registry 인증 문제다.

서비스가 응답하지 않을 때: Service → Endpoints → Pod 순으로 확인한다. Endpoints가 비어있다면 selector 레이블 불일치나 Readiness Probe 실패가 원인이다. kubectl run debug --image=curlimages/curl -it --rm --restart=Never -- curl http://service-name:port로 클러스터 내부에서 직접 서비스를 호출해 네트워크 문제와 앱 문제를 분리할 수 있다.

kubectl-debug.sh — 장애 대응 명령어 모음
#!/bin/bash # K8s 장애 대응 필수 kubectl 명령어 NAMESPACE="production" APP="api-server" # --- Pod 상태 확인 --- kubectl get pods -n $NAMESPACE -l app=$APP -o wide kubectl describe pod -n $NAMESPACE -l app=$APP # Events 섹션 중요 # --- 로그 확인 --- kubectl logs -n $NAMESPACE -l app=$APP --tail=100 kubectl logs -n $NAMESPACE -l app=$APP --previous # 이전 컨테이너 로그 (재시작 후) kubectl logs -n $NAMESPACE pod/api-server-abc -c init-container # init container 로그 # --- 리소스 사용량 실시간 확인 (metrics-server 필요) --- kubectl top pods -n $NAMESPACE -l app=$APP kubectl top nodes # --- Service → Endpoints 확인 --- kubectl get svc -n $NAMESPACE $APP kubectl get endpoints -n $NAMESPACE $APP # Pod IP가 여기 없으면 readiness 실패 # --- 네트워크 디버깅용 임시 Pod --- kubectl run debug-pod --image=nicolaka/netshoot -it --rm --restart=Never -n $NAMESPACE -- bash # netshoot에서 사용 가능한 명령어: # nslookup api-server.production.svc.cluster.local # curl -v http://api-server:8080/health/ready # nc -zv api-server 8080 # --- 특정 Pod에 직접 접속 --- kubectl exec -it -n $NAMESPACE deployment/$APP -- /bin/sh # --- 이벤트 실시간 모니터링 --- kubectl get events -n $NAMESPACE --sort-by='.lastTimestamp' -w # --- Pod 재시작 카운트 및 상태 상세 --- kubectl get pods -n $NAMESPACE -o custom-columns="NAME:.metadata.name,READY:.status.containerStatuses[0].ready,RESTARTS:.status.containerStatuses[0].restartCount,STATUS:.status.phase" # --- OOMKilled 확인 --- kubectl get events -n $NAMESPACE --field-selector reason=OOMKilling # --- 특정 노드의 Pod 목록 --- kubectl get pods -n $NAMESPACE -o wide --field-selector spec.nodeName=<node-name>
Kubernetes 장애 대응 디버깅 플로우차트
Pod 상태별 원인과 확인 명령어 흐름 — describe → logs → exec → network 순서로 범위를 좁혀간다

Helm으로 배포 패키지 관리하기

Helm은 K8s의 패키지 매니저다. 여러 YAML 파일을 하나의 Chart로 묶고, values.yaml로 환경별 설정을 분리한다. 직접 kubectl apply를 쓰면 배포 이력이 남지 않고, 롤백도 어렵다. Helm은 helm rollback 한 줄로 이전 Release 상태로 돌아갈 수 있다.

Helm의 핵심 개념: Chart는 K8s 리소스 템플릿 묶음이다. Release는 클러스터에 배포된 Chart의 인스턴스다. 같은 Chart를 두 번 배포하면 두 개의 Release가 생긴다. Values는 Chart에 주입하는 변수다. 환경별 values 파일(values-prod.yaml, values-staging.yaml)을 별도로 관리하면 동일한 Chart로 여러 환경을 운영할 수 있다.

helm-commands.sh + values.yaml 구조
# Helm 기본 명령어 helm repo add bitnami https://charts.bitnami.com/bitnami helm repo update # Chart 설치 (첫 배포) helm install my-release ./my-chart -f values.yaml -f values-prod.yaml --namespace production --create-namespace # 업그레이드 (--install: 없으면 설치, 있으면 업그레이드) helm upgrade --install my-release ./my-chart -f values.yaml -f values-prod.yaml --namespace production --set image.tag=v1.2.3 # 특정 값만 override --atomic # 실패 시 자동 롤백 --timeout 5m # 배포 이력 확인 helm history my-release -n production # 롤백 (이전 revision으로) helm rollback my-release 2 -n production # revision 2로 롤백 # 현재 Release의 실제 YAML 확인 helm get manifest my-release -n production # values.yaml 구조 예시 # ---------------------------------------- # image: # repository: myregistry.io/api-server # tag: "latest" # pullPolicy: IfNotPresent # # replicaCount: 3 # # resources: # requests: # cpu: 250m # memory: 256Mi # limits: # cpu: 500m # memory: 512Mi # # autoscaling: # enabled: true # minReplicas: 3 # maxReplicas: 20 # targetCPUUtilizationPercentage: 60 # # ingress: # enabled: true # host: api.example.com # tls: true

GitOps 워크플로우에서는 Helm을 ArgoCD 또는 Flux와 함께 사용하는 것이 표준이다. ArgoCD는 Git 저장소의 Chart를 Watch하다가 변경이 감지되면 자동으로 클러스터에 동기화한다. 배포 이력이 Git 커밋으로 남고, 롤백도 Git revert로 처리할 수 있다. 인프라 변경 사항이 PR → 리뷰 → 머지 → 자동 배포 파이프라인을 타기 때문에 승인 없이 프로덕션 환경이 변경되는 일이 없다.

프로덕션 K8s 배포 전 필수 체크리스트

Cloud & DevOps 더 보기
KubernetesK8sHPAProbeRBACHelm컨테이너DevOps클라우드프로덕션

관련 포스트

Docker Compose vs Kubernetes — 실무에서 언제 무엇을 쓸까2026-03-20Docker Compose vs Kubernetes — 언제 무엇을 선택할까2026-03-22프로덕션 배포 체크리스트 — 서비스 출시 전 반드시 점검해야 할 42개 항목2026-03-25Docker 멀티스테이지 빌드 실전 가이드 — 이미지 크기 90% 줄이기2026-03-31