기본 리소스가 아닌데 kubectl get이 된다 — CRD와 컨트롤러의 짝

kubectl get globalnetworkpolicies를 쳤더니 결과가 나온다. Calico의 정책 객체다. kubectl get certificates도 된다 — cert-manager의 리소스다. 이들은 Kubernetes 기본 리소스 목록에 없다. DeploymentService는 코어 API에 정의돼 있지만, GlobalNetworkPolicyCertificate는 아니다. 그런데 kubectl은 이 둘을 기본 리소스와 똑같이 가져온다. 어디서 왔는가?

답은 CustomResourceDefinition(CRD)이다. CRD는 매니페스트 하나로 Kubernetes API에 새로운 리소스 종류를 추가하는 정식 통로다. 이 글이 푸는 질문: CRD는 어떻게 "스키마 하나"로 API를 늘리며, 그 늘어난 API에 실제 동작을 어떻게 붙이는가? 그리고 왜 이 패턴이 KubeEdge·cert-manager·Istio 같은 현실의 모든 Kubernetes 확장의 뼈대가 되는가?

CRD가 푸는 문제 — 코어를 고치지 않고 API를 늘린다

Kubernetes의 모든 기능은 "리소스 + 컨트롤러" 짝으로 구성된다고 01장에서 세웠다. Deployment 리소스 → Deployment 컨트롤러. 그렇다면 새 기능을 추가하고 싶으면? "새 리소스 + 새 컨트롤러"를 하나 더 얹으면 된다.

문제는 "새 리소스"를 어떻게 추가하느냐다. Kubernetes 코어는 kube-apiserver가 컴파일 타임에 아는 리소스(Pod, Deployment, Service...)만 받아들인다. 코어에 없는 리소스를 kubectl apply로 보내면 "unknown resource type" 에러가 난다.

과거엔 두 가지 길밖에 없었다:

  1. 코어 코드 수정 — apiserver 소스를 고쳐 새 타입을 등록하고 재컴파일한다. Kubernetes를 fork하는 셈이다.
  2. API aggregation(AA) — 별도 apiserver를 띄워 특정 API 그룹을 위임받게 한다. 무겁다.

CRD는 세 번째 길을 열었다. 매니페스트 하나로 새 리소스 타입을 선언하면, kube-apiserver가 그것을 받아들여 기본 리소스와 동일하게 취급한다. 커스텀 컨트롤러는 별도 프로세스로 짜되, 코어 코드는 손대지 않는다. (Kubernetes docs - Custom Resources)

CRD의 apiextensions.k8s.io/v1은 Kubernetes 1.16(2019)에 GA(Stable)가 됐다. 그 이전에는 v1beta1이 있었고, 스키마 없이도 CRD를 만들 수 있었다. v1에서는 openAPIV3Schema가 필수가 됐다 — 이 변화의 의미는 뒤에 나온다.

CRD 정의 — 스키마가 곧 API다

CRD 자체도 Kubernetes 객체다. kind: CustomResourceDefinition에 정의를 적는다.

# Kubernetes 1.36 — CronTab이라는 커스텀 리소스를 정의
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  # 이름은 반드시 <plural>.<group> 형식이어야 한다
  name: crontabs.stable.example.com
spec:
  group: stable.example.com          # API 그룹 → /apis/stable.example.com/v1/
  names:
    plural: crontabs                  # URL 경로: /apis/.../crontabs
    singular: crontab
    kind: CronTab                     # 매니페스트의 kind:
    shortNames: [ct]                  # kubectl get ct 가능
  scope: Namespaced                   # Namespaced 또는 Cluster
  versions:
    - name: v1
      served: true                    # 이 버전을 API로 노출
      storage: true                   # etcd에 저장할 버전 (정확히 하나)
      schema:
        openAPIV3Schema:              # OpenAPI v3 스키마 (v1에서 필수)
          type: object
          properties:
            spec:
              type: object
              properties:
                cronSpec:
                  type: string
                  pattern: '^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$'
                image:
                  type: string
                replicas:
                  type: integer
                  minimum: 1
                  maximum: 10

(Kubernetes docs - Extend the API with CRD)

이 매니페스트를 kubectl apply하면 일어나는 일: kube-apiserver가 이 CRD를 읽고, /apis/stable.example.com/v1/crontabs라는 새 API 경로를 동적으로 등록한다. 컴파일도, 재시작도 없다. 이제 이 클러스터의 누구나 CronTab 객체를 만들 수 있다.

주목할 점은 스키마가 곧 API 정의라는 것이다. cronSpec은 문자열이고 정규식 패턴을 따라야 하며, replicas는 1~10 사이 정수다 — 이 규칙이 OpenAPI v3 스키마로 적혀 있고, apiserver가 객체를 받을 때 이 스키마로 검증한다. 코어 리소스의 경우 이 검증이 apiserver 코드에 하드코딩돼 있지만, CRD는 스키마를 데이터로 제공해 같은 효과를 낸다.

flowchart LR
    CRD["CRD 매니페스트<br/>kubectl apply"] --> APIS["kube-apiserver"]
    APIS -->|동적 API 등록| API["/apis/stable.example.com/v1/crontabs"]
    API --> OBJ["CronTab 객체<br/>kubectl apply"]
    OBJ --> ETCD["etcd에 저장"]

CRD 객체 생성 — 기본 리소스와 동일하게 취급된다

CRD가 등록되면, 그 리소스는 기본 리소스와 구분 없이 쓸 수 있다.

# Kubernetes 1.36 — CronTab 객체 생성
apiVersion: stable.example.com/v1
kind: CronTab
metadata:
  name: my-cron
spec:
  cronSpec: "*/5 * * * *"
  image: my-image
  replicas: 3
# Kubernetes 1.36
kubectl apply -f crontab.yaml
kubectl get crontabs
kubectl get ct          # shortName으로도 조회 가능

확인할 것: 기본 리소스처럼 NAME, AGE 컬럼이 찍힌다. CRD 객체가 코어 리소스와 같은 경로로 조회됨.

NAME      AGE
my-cron   5s

출력값은 1차 출처(kubernetes.io docs) 예시 기반이며, 실측 환경에 따라 값은 다름.

단순히 kubectl get이 되는 게 아니다. CRD로 만든 리소스는 코어 리소스와 동일한 인프라를 그대로 탄다. (Kubernetes docs - Custom Resources)

기능 CRD 객체에 대한 지원
CRUD + watch (HTTP/kubectl) O
클라이언트 자동 discovery (kubectl, dashboard) O
RBAC (Role/ClusterRole로 권한 제어) O
labels / annotations O
finalizers (삭제 지연) O
admission webhook (검증/기본값) O
kubectl get / describe / edit O

즉 "커스텀 리소스"라고 해서 기본 리소스보다 못한 대우를 받는 게 아니다. etcd에 저장되고, apiserver를 거치고, RBAC로 통제되고, watch로 감시된다 — 코어 리소스가 누리는 것을 전부 누린다. CRD가 강력한 이유가 바로 이것이다: 새 리소스를 만들어도 Kubernetes의 모든 기계장치가 그대로 작동한다.

# Kubernetes 1.36 — 등록된 CRD 확인
kubectl get crd

확인할 것: 클러스터에 설치된 CRD 목록. Calico·cert-manager·Istio를 깔았다면 여기에 그것들의 CRD가 보인다.

NAME                            CREATED AT
crontabs.stable.example.com     2026-07-13T...

실제 목록은 설치된 컴포넌트에 따라 다름.

왜 structural schema가 강제되는가 — pruning과 타입 안전

CRD v1에서는 openAPIV3Schema필수다. v1beta1 시절에는 스키마 없이도 CRD를 만들 수 있었지만, v1부터는 거부된다. 왜 강제일까?

이유는 pruning(가지치기)에 있다. kube-apiserver는 CRD 객체를 받을 때, 스키마에 정의되지 않은 필드를 자동으로 잘라낸다. 스키마에 cronSpec, image, replicas만 있는데 사용자가 foo: bar를 넣으면, foo는 저장 시 제거된다.

# 사용자가 보낸 객체
spec:
  cronSpec: "*/5 * * * *"
  foo: bar              # 스키마에 없는 필드

# etcd에 실제 저장되는 객체 (foo가 잘림)
spec:
  cronSpec: "*/5 * * * *"

이 동작이 왜 중요한가. 스키마 없이 "아무 필드나 받는" CRD가 허용되면:

  • 타입 불일치 — 한 클라이언트는 replicas를 정수로, 다른 클라이언트는 문자열로 쓸 수 있다. etcd에는 둘 다 들어가고, 누가 읽느냐에 따라 동작이 다르다.
  • 스키마 진화 불가 — 필드가 스키마에 명시되지 않으면, 나중에 스키마에 추가할 때 기존 데이터와 충돌하는지 알 수 없다.
  • 검증 불가 — apiserver가 "이 필드가 맞는 값인가"를 검사할 근거가 없다.

structural schema는 이것을 해결한다: 모든 필드는 타입을 가져야 하고, 정의되지 않은 필드는 잘린다. 타입 안전과 진화 가능성을 동시에 잡는 대가로 "엄격함"을 택한 것이다.

예외로, 의도적으로 임의 필드를 허용하고 싶으면 x-kubernetes-preserve-unknown-fields: true를 스키마에 붙인다. (Kubernetes docs - Preserve unknown fields) 보통은 임베드할 JSON 같은 경우에만 쓴다 — 남발하면 structural schema의 이점이 사라진다.

버전 관리 — served, storage, 그리고 conversion

CRD는 여러 버전을 동시에 노출할 수 있다. spec.versions에 여러 버전을 나열하고, 각각 served(API 노출 여부)와 storage(etcd 저장 형식)를 지정한다.

spec:
  versions:
    - name: v1alpha1
      served: true          # API로는 노출
      storage: false        # etcd에 이 형식으로 저장 안 함
    - name: v1
      served: true
      storage: true         # 이 형식이 etcd에 저장되는 형식

핵심 규칙: storage: true정확히 하나여야 한다. 사용자가 어떤 버전으로 객체를 보내든, apiserver는 storage: true인 버전으로 변환해 etcd에 저장한다. 읽을 때는 요청한 버전으로 다시 변환해 돌려준다.

이 "변환"이 문제다. 단순한 필드 추가·제거면 apiserver가 자동으로 처리하지만, 구조가 바뀌면(필드 이름 변경, 타입 변경, 중첩 재구성) apiserver는 변환 규칙을 모른다. 이때 conversion webhook이 필요하다.

spec:
  conversion:
    strategy: Webhook
    webhook:
      clientConfig:
        url: "https://my-converter.example.com/crdconvert"
        caBundle: <CABundle>
      conversionReviewVersions: [v1]

conversion webhook은 apiserver가 "이 객체를 v1alpha1에서 v1로 바꿔줘"라고 HTTP 호출하는 서버다. (Kubernetes docs - Conversion webhook) 운영자가 변환 로직을 직접 구현해서 서빙해야 한다.

이것이 CRD 운영에서 가장 무거운 비용이다. 처음엔 v1alpha1로 시작했고, 안정화하며 v1을 추가했다. 이제 모든 기존 객체를 v1으로 옮기려면:

  1. 새 버전을 CRD에 추가
  2. conversion webhook 서버 작성·배포
  3. storage 버전을 v1으로 전환
  4. (선택) 기존 객체 재작성 — Storage Version Migration 도구 사용

스키마를 처음부터 신중하게 설계하는 이유가 여기에 있다. 한번 storage: true로 굳어진 버전을 바꾸는 비용이 크다. v1alpha1을 서둘러 storage: true로 잡았다가 나중에 후회하는 사례가 많다 — alpha 단계에서는 필드 구조가 자주 바뀌기 때문.

CRD는 스키마만 정의한다 — 동작은 컨트롤러가

여기가 가장 흔한 오해의 지점이다. CRD를 만들면 객체가 etcd에 저장되지만, 아무 일도 일어나지 않는다. CronTab 객체를 kubectl apply하면 etcd에 들어가고, kubectl get으로 볼 수 있다. 하지만 그 cronSpec을 해석해 무언가를 실행하는 존재가 없다.

CRD는 "API의 모양"만 정의한다. "그 API에 대한 동작"은 별도의 custom controller가 담당한다.

flowchart TD
    CRD["CRD<br/>(스키마 정의)"] --> APIS["kube-apiserver<br/>CronTab 객체 받음"]
    APIS --> ETCD["etcd<br/>객체 저장"]
    ETCD -. watch .-> CC["custom controller<br/>(CronTab을 감시)"]
    CC -->|"spec 읽고 행동"| ACT["Cron 해석, Pod 생성 등"]
    ACT --> APIS

이 custom controller는 03장에서 본 informer·workqueue·조정 루프를 그대로 쓴다. 차이는 "감시할 리소스"가 Deployment가 아니라 CronTab이라는 점뿐이다. client-go의 informer는 CRD 리소스도 동일하게 감시한다 — CRD 객체가 코어 리소스와 동일한 API 경로를 타기 때문에, 감시하는 쪽에서도 차이가 없다.

정리하면 CRD 확장은 두 부품의 짝이다:

  1. CRD — "이런 리소스가 있다"고 API에 선언. 스키마·검증·저장을 담당.
  2. custom controller — 그 리소스를 watch하고, spec과 status를 비교해 조정 루프를 돌림.

01장에서 "모든 기능은 리소스 + 컨트롤러 쌍"이라고 했다. CRD는 바로 이 짝의 사용자 버전이다. 코어의 Deployment + Deployment 컨트롤러와 구조가 동일하다 — 다만 리소스 정의가 코어 코드가 아니라 CRD 매니페스트로 옮겨왔을 뿐.

CRD만 만들고 컨트롤러를 안 만들면? 객체는 저장되지만 영원히 아무 일도 안 한다. status 필드도 영원히 비어 있다(컨트롤러가 채우는 필드이므로). "CRD 적용했는데 아무 반응이 없다"는 대부분 이 상태다.

Operator 패턴 — CRD와 컨트롤러로 운영자를 코드로 옮기다

CRD + custom controller의 짝을 Operator 패턴이라 부른다. (Kubernetes docs - Operator pattern) 이름의 유래는 "인간 운영자(operator)가 하던 일을 코드로 옮겼다"는 데 있다.

예를 들어 보자. PostgreSQL 클러스터를 운영하는 인간 DBA가 하는 일:

  • 백업을 주기적으로 돌린다
  • 복제본이 lagging되면 경고한다
  • 장애 시 primary를 failover한다
  • 버전 업그레이드를 순서대로 수행한다

이걸 Kubernetes에서 자동화하려면? PostgresCluster라는 CRD를 만들고, 그 spec에 "replicas: 3, backupSchedule: daily, version: 16"을 적는다. 그리고 그 spec을 watch하는 컨트롤러가 DBA의 지식을 코드로 구현한다 — 백업 잡을 돌리고, 복제 상태를 점검하고, failover를 수행한다.

# 가상 예시 — Postgres Operator의 CRD 객체
apiVersion: db.example.com/v1
kind: PostgresCluster
metadata:
  name: prod-db
spec:
  replicas: 3
  version: "16"
  backup:
    schedule: "0 2 * * *"
    retention: 7d

운영자가 이 매니페스트를 kubectl apply하면, Operator 컨트롤러가 이를 읽고 실제 PostgreSQL 클러스터를 구성·유지한다. 노드가 죽어도, 컨트롤러가 재시작돼도, spec이 etcd에 남아 있어 조정 루프가 다시 수렴한다 — 01장의 선언적·self-healing 모델이 데이터베이스 운영에 그대로 적용되는 것이다.

Operator라는 이름과 개념은 CoreOS(현 Red Hat)가 2016년에 제안했다. (CoreOS - Introducing Operators, 2016) 그 뒤로 etcd Operator, Prometheus Operator, cert-manager, ArgoCD, Strimzi(Kafka) 등 수백 개의 Operator가 생겼다. 현대 Kubernetes 생태계의 상당수가 이 패턴 위에 서 있다 — "확장"이라는 말의 실체가 결국 Operator다.

이 서브프로젝트의 뒷글도 같은 패턴의 변주다. KubeEdge의 Device/DeviceModel은 CRD로 정의된 리소스고, CloudCore가 그 컨트롤러 역할을 한다. Calico의 GlobalNetworkPolicy도 CRD이고, Calico의 컨트롤러(typha)가 실제 정책을 데이터플레인에 내린다. CRD를 이해하면 이 뒷글들의 뼈대가 보인다.

검증을 넘어서 — admission webhook과 ValidatingAdmissionPolicy

CRD의 OpenAPI v3 스키마는 단일 필드 검증엔 강하다 — "replicas는 1~10", "cronSpec은 이 정규식". 하지만 교차 필드 검증이나 클러스터 상태 기반 검증은 못 한다. 예를 들어 "replicas가 5 이상이면 highAvailability: true도 설정돼야 한다"는 규칙은 OpenAPI v3로 표현할 수 없다.

이럴 때 admission webhook을 쓴다. ValidatingWebhookConfiguration을 등록하면, apiserver가 CRD 객체를 받을 때마다 지정한 HTTP 서버로 "이 객체 괜찮은가?"를 묻는다. webhook 서버가 "아니오"라고 하면 객체 생성이 거부된다.

# Kubernetes 1.36 — CRD 객체를 검증하는 webhook
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
  name: crontab-validator
webhooks:
  - name: validate.crontab.example.com
    rules:
      - apiGroups: ["stable.example.com"]
        resources: ["crontabs"]
        operations: ["CREATE", "UPDATE"]

두 가지 선택지가 있다:

  • ValidatingWebhook / MutatingWebhook (전통) — 별도 HTTP 서버 필요. 유연하지만 운영 비용이 있다.
  • ValidatingAdmissionPolicy (1.30 GA) — CEL(Common Expression Language)로 검증 규칙을 인라인으로 적는다. 별도 서버 없이 apiserver가 직접 평가한다. (Kubernetes docs - Validating Admission Policies)

ValidatingAdmissionPolicy가 단순한 교차 필드 검증엔 훨씬 가볍다. webhook은 복잡한 외부 상태 조회(다른 리소스와의 정합성 등)가 필요할 때 여전히 쓰인다. 둘의 선택은 03-k8s-security의 어드미션 컨트롤 글에서 더 깊이 다룬다.

CRD 스키마 검증 → ValidatingAdmissionPolicy(CEL) → ValidatingWebhook 순으로, 갈수록 표현력은 강해지지만 비용도 커진다. 간단한 건 스키마로, 교차 필드는 CEL로, 외부 상태 조회가 필요하면 webhook으로 — 이렇게 층을 나눠 설계하는 것이 현업 관행이다.

한계 — CRD가 못 하는 것, 비용이 큰 것

CRD가 "API를 쉽게 늘린다"고 해서 공짜는 아니다. 분명한 한계와 비용이 있다.

etcd 부담. CRD로 만든 객체는 전부 etcd에 저장된다. 커스텀 리소스 하나당 수 KB~수십 KB. 클러스터에 CRD 객체가 수만 개 쌓이면 etcd 용량과 apiserver 응답 시간에 타격이 온다. 코어 리소스보다 더 많이 만들기 쉬운 게 CRD 객체다 — "설정값 하나마다 CRD 객체" 식으로 설계하면 etcd가 가득 찬다. 대량의 상태는 CRD가 아니라 애플리케이션 자체 DB에 두는 것이 맞다.

스키마 복잡도. 실제 운영되는 CRD 스키마는 수백 줄이 되기 쉽다. 중첩 객체, 배열, enum, conditional 검증이 겹치면 작성·검증·유지 비용이 코어 리소스 수준을 넘는다. controller-gen이나 kubebuilder 같은 도구로 Go struct에서 스키마를 생성하는 관행이 널리 퍼진 이유다.

버전 마이그레이션. 앞서 본 conversion webhook. 한번 storage: true로 굳어진 버전을 바꾸는 비용이 크고, 실수하면 데이터 손상으로 이어진다. 초기 설계에서 스키마를 신중하게 잡지 않으면 나중에 갚을 빚이 된다.

코어 API와 다른 점. CRD 객체는 몇 가지 면에서 코어 리소스보다 제약이다:

  • status 하위 구조를 자유롭게 설계할 수 있지만, Kubernetes가 해석하지 않는다 — 전부 컨트롤러가 채운다.
  • 스케줄러·kubelet 같은 코어 컴포넌트는 CRD 객체를 직접 다루지 않는다. CRD 객체가 Pod를 만들게 하려면 컨트롤러가 그 일을 해야 한다.
  • scale subresource, /status subresource 같은 기능은 CRD에 명시적으로 선언해야 쓸 수 있다.

언제 CRD가 아니라 API aggregation(AA)을 쓰나. CRD는 "etcd에 저장되는 객체"에 적합하다. 하지만 커스텀 리소스가 etcd에 저장되는 게 부적절한 경우 — 대용량 데이터, 자체 스토리지가 필요한 경우, etcd를 거치지 않고 실시간으로 응답해야 하는 경우 — 는 API aggregation layer로 별도 apiserver를 두는 것이 낫다. (Kubernetes docs - API Aggregation) metrics-server가 대표적이다 — 실시간 메트릭을 etcd가 아니라 직접 반환한다.

흔히 묻는 것, 흔히 틀리는 것

오해 정정
"CRD를 만들면 자동으로 동작한다" CRD는 스키마만 정의. 동작은 custom controller가. 컨트롤러 없으면 객체는 etcd에 저장만 되고 아무 일도 안 일어남
"CRD 객체는 기본 리소스보다 못하다" RBAC·discovery·watch·kubectl·finalizer·admission 모두 동일하게 작동. 코어 리소스와 대우가 같다
"스키마 없이도 CRD를 만들 수 있다" v1(beta 아님)에서는 openAPIV3Schema 필수. 스키마 없는 필드는 pruning됨
"버전은 나중에 쉽게 바꾼다" storage: true 버전 변경은 conversion webhook + 마이그레이션 비용. 초기 설계가 중요
"CRD 객체가 많아도 etcd는 괜찮다" 객체당 수 KB. 수만 개 쌓이면 etcd 용량·apiserver 응답 시간 타격. 대량 상태는 앱 DB로
"CRD와 API aggregation은 같다" CRD는 etcd 저장 객체. AA는 별도 apiserver 위임. etcd를 안 거치려면 AA
"검증은 스키마로 충분하다" 단일 필드는 OK. 교차 필드·외부 상태 검증은 ValidatingAdmissionPolicy(CEL) 또는 webhook 필요

요약 — 이 글의 결론

  • CRD는 매니페스트 하나로 Kubernetes API에 새 리소스를 추가하는 정식 통로다. 코어 코드 수정·fork 없이, apiextensions.k8s.io/v1 객체 하나로 API 경로·검증·저장을 선언한다. 1.16 GA 이후 안정적.
  • CRD 객체는 코어 리소스와 동일한 대우를 받는다. etcd 저장, RBAC, discovery, watch, kubectl, admission webhook — 전부 동일하게 작동한다. "커스텀"이라서 반쪽짜리가 아니다.
  • 스키마가 곧 API다. openAPIV3Schema가 필수이며, 정의되지 않은 필드는 pruning된다. 타입 안전·검증·진화 가능성을 이 엄격함으로 산다.
  • CRD는 스키마만 정의한다. 동작은 custom controller가. CRD만 만들면 객체는 저장되지만 아무 일도 안 일어난다 — 03장의 informer·workqueue·조정 루프를 CRD 리소스에 적용한 것이 custom controller다.
  • CRD + controller = Operator. 인간 운영자의 지식을 코드로 옮긴 것. etcd·Prometheus·cert-manager·Strimzi 등 현대 Kubernetes 생태계의 상당수가 이 패턴 위에 서 있다.
  • 비용은 etcd 부담·스키마 복잡도·버전 마이그레이션. 대량 상태는 앱 DB로, 복잡한 교차 검증은 CEL/webhook으로, etcd를 안 거치는 실시간 응답은 API aggregation으로 — CRD가 만능이 아닌 경계를 아는 것이 설계의 시작이다.

생각해 볼 문제

  1. CRD만 만들고 custom controller를 배포하지 않았다. kubectl get은 되는데 status가 비어 있다. 이 상태에서 객체를 수정하면 무슨 일이 일어나나? (etcd엔 어떻게 저장되나?)
  2. storage: true인 버전을 v1alpha1에서 v1으로 바꾸려 한다. 기존 객체는 어떤 형태로 etcd에 남아 있으며, 변환 없이 storage만 바꾸면 어떤 일이 벌어지나?
  3. CRD 스키마에 x-kubernetes-preserve-unknown-fields: true를 붙이면 pruning이 안 된다. 이게 "편리하다"고 남발하면, 나중에 스키마에 그 필드를 공식 추가할 때 어떤 충돌이 생길까?
  4. Operator가 "인간 운영자를 코드로 옮긴다"고 했다. 그렇다면 Operator가 못 하는 "인간 운영자만 할 수 있는 일"은 무엇일까? (힌트: 판단·컨텍스트·외부 시스템)
  5. CRD 객체 수가 etcd 용량 한계에 다다랐다. "CRD를 쓰지 말아야 한다"와 "CRD를 쓰되 저장 위치를 바꿔야 한다"의 두 방향을 각각 어떻게 설계하겠는가?
  6. ValidatingAdmissionPolicy(CEL)가 대부분의 교차 필드 검증을 커버한다면, ValidatingWebhook은 어떤 경우에만 살아남을까?
  7. 01장에서 "CRD로 자기 리소스를 만들면 Kubernetes의 일부가 된다"고 했다. 이 글을 읽고 나서, "Kubernetes의 경계"는 어디까지라고 답하겠는가? — apiserver? etcd? 컨트롤러? 세 가지 각각의 관점에서.

참고

'Tech Artifacts > Kubernetes' 카테고리의 다른 글

Kubernetes - 15. helm  (0) 2026.07.13
Kubernetes - 14. kubeadm  (0) 2026.07.13
Kubernetes - 13. node administration  (0) 2026.07.13
Kubernetes - 12. autoscaling  (0) 2026.07.13
Kubernetes - 11. resource qos  (0) 2026.07.13