본문으로 건너뛰기
김신건의 로그

[Kubernetes] CRDs & Operators

· 수정 · 📖 약 3분 · 937자/단어 #kubernetes #crd #operator #controller #extensibility
Kubernetes CRD, Custom Resource Definition, Kubernetes Operator, Operator Pattern, Operator SDK, Kubebuilder, controller-runtime, 쿠버네티스 오퍼레이터, 쿠버네티스 CRD

정의

Custom Resource Definition (CRD) 는 Kubernetes API 에 새 리소스 타입 을 추가하는 기능입니다. apiVersion + kind 를 정의하면 kubectl / apiserver 가 마치 내장 리소스처럼 취급합니다.

Operator 는 CRD + 그 리소스를 관리하는 컨트롤러 의 조합입니다. 사람 운영자가 하는 작업 (설치, 백업, 업그레이드, 스케일링) 을 코드로 자동화합니다. CoreOS 가 2016년 제안한 패턴.

왜 필요한가

CRD 없이의 한계

내장 리소스 (Pod, Deployment, Service) 만으로는 복잡한 앱 (DB, Kafka, ML pipeline) 관리에 부족.

  • DB primary 승격 절차: 여러 리소스 조합 + 순서 있는 조정
  • Backup / Restore: 시간 기반 이벤트 처리
  • Version upgrade: 데이터 마이그레이션 포함 단계별

이를 사람이 runbook 으로 관리하는 대신 선언적 리소스 + 자동 컨트롤러 로.

예: Postgres Operator

내장 리소스만으로 Postgres HA 클러스터 배포:

  • StatefulSet 3개 (primary + replica)
  • ConfigMap 여러 개
  • Service (primary, replica 각각)
  • PVC
  • Backup CronJob

Operator 는 이것을 하나의 CR 로 압축:

apiVersion: acid.zalan.do/v1
kind: postgresql
metadata:
  name: prod-db
spec:
  numberOfInstances: 3
  postgresql:
    version: "16"
  volume:
    size: 100Gi
  backup:
    schedule: "0 3 * * *"

Operator 가 모든 내부 리소스 자동 관리.

CRD 정의

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: myapps.example.com
spec:
  group: example.com
  versions:
    - name: v1alpha1
      served: true
      storage: true
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              properties:
                replicas:
                  type: integer
                  minimum: 1
                  maximum: 10
                image:
                  type: string
                config:
                  type: object
                  properties:
                    logLevel:
                      type: string
                      enum: [debug, info, warn, error]
              required: [replicas, image]
            status:
              type: object
              properties:
                readyReplicas:
                  type: integer
                phase:
                  type: string
      subresources:
        status: {}
        scale:
          specReplicasPath: .spec.replicas
          statusReplicasPath: .status.readyReplicas
      additionalPrinterColumns:
        - name: Image
          type: string
          jsonPath: .spec.image
        - name: Replicas
          type: integer
          jsonPath: .spec.replicas
        - name: Ready
          type: integer
          jsonPath: .status.readyReplicas
  scope: Namespaced
  names:
    plural: myapps
    singular: myapp
    kind: MyApp
    shortNames: [ma]

이후:

kubectl get myapp
kubectl apply -f myapp-instance.yaml
apiVersion: example.com/v1alpha1
kind: MyApp
metadata:
  name: prod-app
spec:
  replicas: 3
  image: myapp:1.0
  config:
    logLevel: info

CRD 만 만들면 저장은 되지만 아무 일도 안 일어남. Controller 가 필요.

Controller (Operator 의 핵심)

Controller 는 CR 을 watch 하며 spec 대로 실제 리소스를 조정. Reconciliation loop:

for {
  observed := watch(CR)               // 관측
  desired := computeDesiredState(observed)
  actual := getCurrentState()
  diff := compareStates(desired, actual)
  applyChanges(diff)                   // 조정
}

기본 원칙

  • 선언적 (declarative): spec 이 원하는 상태
  • Idempotent: 같은 spec 여러 번 reconcile 해도 동일 결과
  • Eventually consistent: 즉시 반영 아님, 최종 수렴

Kubebuilder / Operator SDK

Operator 를 처음부터 짜지 말고 프레임워크 활용:

  • Kubebuilder: kubernetes-sigs 프로젝트. Go 언어. controller-runtime 기반.
  • Operator SDK: Red Hat 주도. Kubebuilder + OperatorHub 통합. Go, Ansible, Helm.
  • KUDO: YAML DSL. 간단한 Operator.
  • Metacontroller: JavaScript/Python 등 임의 언어로 controller.
  • kopf: Python framework.

Kubebuilder 예시

# 초기화
mkdir myapp-operator && cd myapp-operator
kubebuilder init --domain example.com --repo example.com/myapp-operator

# API + Controller 생성
kubebuilder create api --group apps --version v1alpha1 --kind MyApp

# CRD schema 정의 (api/v1alpha1/myapp_types.go)
type MyAppSpec struct {
    Replicas int32  `json:"replicas"`
    Image    string `json:"image"`
}

type MyAppStatus struct {
    ReadyReplicas int32 `json:"readyReplicas"`
}

# Controller 로직 (controllers/myapp_controller.go)
func (r *MyAppReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
    var myapp examplev1.MyApp
    if err := r.Get(ctx, req.NamespacedName, &myapp); err != nil {
        return ctrl.Result{}, client.IgnoreNotFound(err)
    }

    // Deployment 존재 확인 & 조정
    deploy := &appsv1.Deployment{}
    err := r.Get(ctx, types.NamespacedName{Name: myapp.Name, Namespace: myapp.Namespace}, deploy)
    if apierrors.IsNotFound(err) {
        newDeploy := r.deploymentForMyApp(&myapp)
        r.Create(ctx, newDeploy)
        return ctrl.Result{RequeueAfter: time.Second * 30}, nil
    }

    // Replica 갱신
    if *deploy.Spec.Replicas != myapp.Spec.Replicas {
        deploy.Spec.Replicas = &myapp.Spec.Replicas
        r.Update(ctx, deploy)
    }

    // Status 갱신
    myapp.Status.ReadyReplicas = deploy.Status.ReadyReplicas
    r.Status().Update(ctx, &myapp)

    return ctrl.Result{}, nil
}

# 실행
make install run    # dev
make docker-build docker-push IMG=...
make deploy IMG=...  # prod

Capability Levels (Operator SDK)

Operator 성숙도 5단계:

  1. Basic Install: 앱 설치
  2. Seamless Upgrades: 버전 업그레이드
  3. Full Lifecycle: 백업/복구, 실패 복구
  4. Deep Insights: 메트릭, alert, 로그 통합
  5. Auto Pilot: 자동 스케일링, 자가 튜닝, 이상 감지

프로덕션 사용 시 3+ 요구.

OperatorHub

operatorhub.io 에 커뮤니티 operator 목록. 유명 operator:

  • Prometheus Operator (kube-prometheus-stack)
  • cert-manager
  • ArgoCD Operator
  • PostgreSQL Operator (Zalando / CloudNativePG / crunchy-data)
  • Kafka Operator (Strimzi)
  • Redis Operator (spotahome, ot-container-kit)
  • Elastic Cloud on Kubernetes (ECK)
  • Istio Operator
  • Percona Operators (MySQL, MongoDB)

실전 CRD 팁

1. Version strategy

v1alpha1 -> v1beta1 -> v1 순 관용. Alpha 는 실험, beta 는 안정 후 GA 준비, v1 은 안정.

Conversion webhook: 여러 버전 지원 시 서버측 변환:

spec:
  conversion:
    strategy: Webhook
    webhook:
      clientConfig:
        service:
          name: myapp-webhook
          path: /convert
      conversionReviewVersions: [v1]

2. Status subresource

specstatus 를 별도 subresource 로 관리하면:

  • Controller 만 status 갱신 (RBAC 분리)
  • kubectl edit 이 status 조작 방지
  • Conflict 감소

3. Printer columns

kubectl get myapp 표시 컬럼 커스터마이제이션.

4. Validating admission webhook

CR 생성/수정 시 추가 검증. Simple validation 은 OpenAPI schema, complex 는 webhook.

5. Finalizer

CR 삭제 시 정리 작업. metadata.finalizers 에 이름 추가하면 controller 가 정리 후 finalizer 제거해야 실제 삭제.

함정

WARNING

CRD 는 cluster-scoped. Namespace 마다 다른 CRD 불가. 조심스러운 도입.

CAUTION

CR schema 변경은 breaking. 필드 추가는 OK, 삭제/타입 변경은 미리 마이그레이션 계획.

WARNING

Controller 다중 replica 는 leader election 필요. 안 그러면 두 controller 가 동시 조정 -> race.

IMPORTANT

Reconcile 는 idempotent 여야. 여러 번 호출해도 같은 결과. Requeue + RequeueAfter 로 retry.

CAUTION

Finalizer 잘못 걸면 CR 삭제 불가. Controller 죽으면 stuck. 신중히.

WARNING

CRD 는 API 부하 증가. 큰 status field, 많은 CR 인스턴스 = etcd 부담. Compaction 정기.

관련 위키

이 글의 용어 (7개)
[GitOps] ArgoCD: Kubernetes GitOpsdevops
정의 ArgoCD = Kubernetes 의 GitOps 컨트롤러. Git 리포지토리의 manifest 가 source of truth → cluster 가 자동 동기화. Git…
[K8s] RBAC: Role, ClusterRole, ServiceAccount, RoleBindingkubernetes
정의 RBAC (Role-Based Access Control) = K8s 의 권한 부여 모델. 주체 + 권한 + 바인딩. 사용 시나리오 | 상황 | RBAC 역할 | |---|…
[Kubernetes] Admission Controllerskubernetes
정의 Admission Controller 는 kube-apiserver 가 인증/인가 후, etcd 저장 전에 요청을 가로채 검증 (validate) 하거나 수정 (mutate…
[Kubernetes] Architecture (Control Plane + Node)kubernetes
정의 Kubernetes Architecture 는 Control Plane (제어) 과 Worker Node (실행) 두 계층으로 구성됩니다. Control Plane 은 원하…
[Kubernetes] Namespacekubernetes
정의 Namespace 는 같은 물리 클러스터 안에서 리소스 (Pod, Service, ConfigMap 등) 를 논리적으로 격리 하는 단위입니다. 이름 충돌 회피, RBAC 스…
[LLM Eval] HELM: Holistic Evaluation of Language Modelsai
정의 HELM (Holistic Evaluation of Language Models) 는 Stanford CRFM (Center for Research on Foundation…
Kuberneteskubernetes
정의 Kubernetes (k8s) 는 컨테이너화된 애플리케이션의 배포, 스케일링, 관리 를 자동화하는 오픈소스 오케스트레이터입니다. Google 이 2014년 발표하고 2015…

💬 댓글

사이트 검색 / 명령어

검색

스크롤 = 확대/축소 · 드래그 = 이동 · 0 = 원래 크기 · ESC = 닫기