Kubernetes 지속성 시리즈 파트 3: 컨트롤러와 복원력 — 왜 Kubernetes는 자체 치유되는가

발행: (2026년 1월 11일 오전 09:42 GMT+9)
10 min read
원문: Dev.to

I’m happy to translate the article for you, but I need the text you’d like translated. Could you please paste the content of the article (or the portion you want translated) here? I’ll keep the source line and all formatting exactly as you requested.

배울 내용

  • 애플리케이션 컨트롤러(NGINX Ingress, cert‑manager)가 퇴거(eviction) 상황에서도 어떻게 지속되는지
  • 컨트롤러가 무상태(stateless)이며 어디서든 재시작될 수 있는 이유
  • 하드웨어부터 애플리케이션까지의 전체 지속성 체인
  • 파드 퇴거 시 살아남는 것과 그렇지 않은 것

이전

  • Part 1 – GKE 노드 업그레이드 후 사라진 인그리스를 디버깅했습니다.
  • Part 2systemdkubelet을 감독하는 방식과 kubelet이 정적 팟을 통해 컨트롤 플레인을 부트스트랩하는 방법을 탐구했습니다.

이제 최종 단계인 애플리케이션 컨트롤러에 도달했습니다—그리고 Kubernetes를 진정으로 탄력 있게 만드는 우아한 통찰력입니다.

Source:

Layer 4: Application Controllers

Application Controllers가 상태를 유지하는 방법

NGINX Ingress, cert‑manager, Prometheus Operator와 같은 컨트롤러는 일반적으로 Deployment 또는 StatefulSet으로 배포됩니다:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: ingress-nginx-controller
  namespace: ingress-nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: ingress-nginx
  template:
    spec:
      containers:
        - name: controller
          image: registry.k8s.io/ingress-nginx/controller:v1.9.0

이 파드가 퇴거될 때:

  1. kubelet이 파드 보고를 중단 → 컨트롤 플레인이 파드를 Terminated 상태로 표시합니다.
  2. ReplicaSet controllercurrentReplicas (0)을 확인합니다 …
    핵심 포인트: 컨트롤러 자체는 상태를 저장하지 않으며, API 서버(etc​d에 백업됨)에서 모든 정보를 읽어옵니다.

Helm Release 영속성

Helm은 릴리스 정보를 Kubernetes Secret에 저장합니다:

kubectl get secret -n monitoring -l owner=helm -o yaml
apiVersion: v1
kind: Secret
metadata:
  name: sh.helm.release.v1.prometheus.v3
  labels:
    owner: helm
    name: prometheus
    version: "3"
type: helm.sh/release.v1
data:
  release: H4sIAAAAAAAAA...   # Base64‑encoded release manifest

Secret에 포함되는 내용:

  • 설치된 차트
  • 사용된 값들
  • 모든 리소스의 계산된 매니페스트

이 Secret이 API 서버를 통해 etcd에 저장되기 때문에, Helm 릴리스는 파드 퇴거가 발생해도 유지됩니다.

전체 지속성 체인

┌─────────────────────────────────────────────────────────────────────┐
│                     Linux Host (Physical/VM)                        │
├─────────────────────────────────────────────────────────────────────┤
│  systemd (PID 1)                                                    │
│  ├── Supervises all system services                                 │
│  ├── Restarts failed services automatically                         │
│  └── Config: /etc/systemd/system/                                   │
│      │                                                              │
│      └── kubelet.service                                            │
│          ├── Started and supervised by systemd                      │
│          ├── Watches /etc/kubernetes/manifests/ for static pods     │
│          ├── Watches API server for scheduled pods                  │
│          └── Ensures containers match pod specs                     │
│              │                                                      │
│              ├── Static Pods (/etc/kubernetes/manifests/)           │
│              │   ├── etcd ──────────────────┐                       │
│              │   ├── kube-apiserver ◄───────┤ Persistent            │
│              │   ├── kube-controller-manager│ State Store           │
│              │   └── kube-scheduler         │                       │
│              │                              │                       │
│              └── Regular Pods ◄─────────────┘                       │
│                  │                 (scheduled via API server)       │
│                  │                                                  │
│                  ├── kube-system namespace                          │
│                  │   ├── CoreDNS                                    │
│                  │   ├── kube-proxy                                 │
│                  │   └── CNI plugins                                │
│                  │                                                  │
│                  ├── ingress-nginx namespace                        │
│                  │   └── NGINX Ingress Controller                   │
│                  │       └── Watches Ingress resources              │
│                  │                                                  │
│                  └── Application namespaces                         │
│                      ├── cert-manager                               │
│                      ├── Prometheus Operator                        │
│                      └── Your applications                          │
└─────────────────────────────────────────────────────────────────────┘

Source:

핵심 통찰: 컨트롤러는 무상태다

이것이 설계의 우아한 핵심이다: 컨트롤러는 상태를 저장하지 않는다.

컨트롤러가 수행하는 작업작동 방식
원하는 상태를 읽음API 서버(etc​d가 백엔드)에서
변경 사항을 감시함API 서버의 워치 메커니즘을 통해
변경을 수행함API 서버에 업데이트를 제출함으로써
어디서든 재시작 가능정보 손실이 없으며; API 서버가 단일 진실 소스 역할을 유지

API 서버 + etcd단일 진실 소스이며, 컨트롤러 자체가 아니다.

Stateless Controllers Architecture

감독 계층 (Mermaid 다이어그램)

flowchart LR
    subgraph Source["Source of Truth"]
        etcd[(etcd)]
        api[API Server]
    end

    subgraph StatelessControllers["Stateless Controllers"]
        deploy[Deployment Controller]
        rs[ReplicaSet Controller]
        nginx[NGINX Ingress]
        cert[cert-manager]
    end

    etcd -->> api
    api -->> deploy
    api -->> rs
    api -->> nginx
    api -->> cert

    style etcd fill:#e67700,color:#fff
    style api fill:#e67700,color:#fff

(다이어그램이 렌더링되지 않으면 위 코드를 사용하여 Mermaid Live Editor에서 직접 확인하세요.)

왜 이것이 중요한가

  • 컨트롤러 포드 삭제 → 재시작하고 상태를 따라잡습니다.
  • 컨트롤러를 노드 간 이동 → API 서버에 단순히 재연결합니다.
  • 컨트롤러를 여러 복제본으로 확장 → API 서버를 통해 조정합니다.
  • 컨트롤러 업그레이드 → 새 버전이 etcd에서 동일한 상태를 읽습니다.

What Survives vs. What Doesn’t

Survives Any Pod Eviction

ResourceWhy It Survives
etcd에 저장된 Kubernetes 객체파드와 독립적으로 저장됨
Helm 릴리스etcd에 시크릿으로 저장됨
Operator‑managed CRDOperator가 지속적으로 조정함
PersistentVolumes스토리지는 클러스터 외부에 존재
ConfigMaps / Secretsetcd에 저장됨

Doesn’t Survive Without Help

ResourceWhy It Doesn’t Survive
파드 로컬 EmptyDir 볼륨파드와 함께 삭제됨
의존성이 누락된 수동 적용 리소스재생성 시 검증 웹훅이 거부
인메모리 캐시프로세스 재시작 시 메모리 손실
노드 로컬 상태명시적으로 영구화하지 않으면 손실

디자인의 우아함

Kubernetes의 아키텍처는 여러 핵심 설계 원칙을 따릅니다:

  • 선언형이 명령형보다 우선 – 원하는 상태를 설명하고, 그 상태에 도달하기 위한 단계는 기술하지 않습니다.
  • 조정이 트랜잭션보다 우선 – 지속적으로 원하는 상태로 수렴합니다.
  • 무상태 컨트롤러 – 상태는 컨트롤러 프로세스가 아니라 etcd에 저장됩니다.
  • 계층적 감독 – 각 계층은 위의 계층을 감시합니다.
  • 실패는 정상 – 실패를 방지하기보다 복구를 설계합니다.

이러한 원칙 덕분에 Kubernetes 클러스터는 다음과 같은 상황을 견딜 수 있습니다:

  • 노드가 예기치 않게 사라짐.
  • 리소스 압박으로 인해 파드가 퇴출됨.
  • 네트워크 파티션 발생.
  • 롤링 업그레이드 수행.

…그리고 여전히 애플리케이션 가용성을 유지합니다.

결론

Ingress가 누락된 것을 디버깅하는 과정에서 전체 감독 계층을 이해하게 되면, Kubernetes가 회복력을 갖추게 하는 정교한 메커니즘을 알 수 있습니다.

systemd → kubelet → static pods → control plane → controllers → your apps

각 레이어는 다음 레이어를 감독하며, etcd는 어떤 구성 요소가 실패하더라도 살아남는 영구 메모리 역할을 합니다.

핵심 인사이트: Kubernetes는 실패를 방지하지는 않지만 레이어드 감독, etcd에 저장된 영구 상태, 그리고 지속적인 조정 루프를 통해 자동으로 복구합니다.

이것이 Kubernetes의 진정한 힘입니다: 문제가 절대 발생하지 않는 것이 아니라, 문제가 발생했을 때 시스템이 원하는 상태로 스스로 복원할 수 있다는 점입니다.

Series Recap

추가 읽을거리

이 시리즈가 유용했나요? 쿠버네티스 내부 내용 더 보려면 팔로우하세요!

Back to Blog

관련 글

더 보기 »

StatefulSet 프로젝트

전제 조건 StatefulSet은 다음 구성 요소가 필요합니다: - Headless Service – 각 pod에 대해 안정적인 DNS를 제공합니다. - StatefulSet manifest – pod들을 정의합니다.