본문 바로가기
Kubernetes/K8s

CRD (Custom Resource Definition) 만들어보자

by lumination 2025. 10. 14.

이 그림을 이해하는 것이 본 글의 학습 목표이다.

 

1. CRD 정의하기

todo-crd.yaml 파일을 만들어서 다음 내용을 저장:

 
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: todos.example.com

spec:
  group: example.com
	# group: example.com
	# 이 리소스가 속한 API 그룹 이름입니다.
	# 기본 쿠버네티스 리소스는 보통 apps, batch, core 등의 그룹을 가지는데
	# 여기서는 사용자가 정의한 example.com 그룹입니다.
	# 실제로 사용할 때는 이런 식으로 씁니다:
	# 최상위 선언 예) apiVersion: example.com/v1

kind: Todo
  versions:
    - name: v1
      served: true
      storage: true
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              properties:
                task:
                  type: string
                done:
                  type: boolean
  scope: Namespaced
  names:
    plural: todos
    singular: todo
    kind: Todo
    shortNames:
      - td

 

$ kubectl apply -f todo-crd.yaml
 
 

✅ 이제 쿠버네티스는 Todo라는 새로운 리소스 타입을 인식하게 된다.

$ kubectl get crd


2. CR(Custom Resource) 생성하기

이제 우리가 만든 타입으로 “객체”를 만들어 보자.
todo-sample.yaml 파일:

apiVersion: example.com/v1
kind: Todo
metadata:
  name: buy-milk
spec:
  task: "Buy some milk"
  done: false
$ kubectl apply -f todo-sample.yaml

3. 생성 CR 내용 보기

$ kubectl get todo buy-milk -o yaml

결과 예시:

apiVersion: example.com/v1
kind: Todo
metadata:
  name: buy-milk
spec:
  task: "Buy some milk"
  done: false

4. 파라미터 업데이트 테스트

이제 “할 일 완료”로 변경:

kubectl patch todo buy-milk --type merge -p '{"spec":{"done":true}}'

 

$ kubectl get todo buy-milk -o yaml
 

✅ done: true 로 바뀐 걸 확인.


5. CR만 있을 때

  • 그냥 저장된 데이터 덩어리.
  • 예제: Todo CR 안에 task랑 done 값이 저장되어 있음
  • kubectl get todo / kubectl edit todo로 조회·수정 가능
  • 쿠버네티스는 데이터 구조를 검증하고 저장해주는 역할만 함
  • 실제 동작(자동 삭제, 알림 발송 등)은 안 일어남

6. CR + 컨트롤러의 연동

컨트롤러 / 오퍼레이터를 만들면 CR을 액션과 연결할 수 있다.

예를 들어:

CR 동작 컨트롤러 처리
done: true “이 Todo 삭제”
task: "Deploy app" 실제 Deployment 생성
새로운 Todo 생성 Slack 알림 전송

즉, CRD+CR은 데이터 정의와 데이터, 컨트롤러는 행동 정의라고 생각하면 이해가 쉽다

 

비유

  • CRD = “상품의 설계도”
  • CR = “실제 만든 상품”
  • 컨트롤러 = “상품이 만들어지면 자동으로 포장, 배송, 알림까지 처리해주는 로봇”

7.  Todo CRD + CR + 컨트롤러 예제

Todo가 done: true가 되면 자동으로 삭제하는 컨트롤러를 만들어보겠다.

Python과 kopf 라이브러리를 사용하면 쉽게 가능

# 파이썬 가상환경 셋업
python -m venv venv
source venv/bin/activate

# Kubernetes Operator Framework for Python 쿠버네티스 API 라이브러리
pip install kopf kubernetes

7-1. 위에서 만든 CRD인 todo-crd.yaml 생성:

$ kubectl apply -f todo-crd.yaml

 

7-2. 위에서 만든 CR인 todo-sample.yaml 생성:

$ kubectl apply -f todo-sample.yaml

 

7-3. 조회:

$ kubectl get todos

 

7-4. Python controller 생성

import kopf
from kubernetes import client, config

# 로컬 테스트 시
config.load_kube_config()

@kopf.on.create('example.com', 'v1', 'todos')
def todo_create(spec, name, namespace, **kwargs):
    print(f"New Todo created: {name} - {spec['task']}")

@kopf.on.update('example.com', 'v1', 'todos')
def todo_update(spec, name, namespace, **kwargs):
    if spec.get('done', False):
        print(f"Todo done! Deleting: {name}")
        api = client.CustomObjectsApi()
        api.delete_namespaced_custom_object(
            group="example.com",
            version="v1",
            namespace=namespace,
            plural="todos",
            name=name,
        )

 

7-5. 컨트롤러 실행 (Python)

$ kopf run todo_controller.py

kopf run todo_controller.py
/home/sunsh/k8s/study_crd/todo/venv/lib/python3.10/site-packages/kopf/_core/reactor/running.py:180: FutureWarning: Absence of either namespaces or cluster-wide flag will become an error soon. For now, switching to the cluster-wide mode for backward compatibility.
  warnings.warn("Absence of either namespaces or cluster-wide flag will become an error soon."
[2025-10-13 15:36:11,284] kopf._core.engines.a [INFO    ] Initial authentication has been initiated.
[2025-10-13 15:36:11,290] kopf.activities.auth [INFO    ] Activity 'login_via_client' succeeded.
[2025-10-13 15:36:11,290] kopf._core.engines.a [INFO    ] Initial authentication has finished.
New Todo created: buy-milk - Buy some milk
[2025-10-13 15:36:11,415] kopf.objects         [INFO    ] [default/buy-milk] Handler 'todo_create' succeeded.
[2025-10-13 15:36:11,415] kopf.objects         [INFO    ] [default/buy-milk] Creation is processed: 1 succeeded; 0 failed.


# buy-milk의 done: true로 변경
kubectl patch todo buy-milk --type merge -p '{"spec":{"done":true}}'

# controller에서 변경사항을 인지
Todo done! Deleting: buy-milk
[2025-10-14 10:09:34,806] kopf.objects         [INFO    ] [default/buy-milk] Handler 'todo_update' succeeded.
[2025-10-14 10:09:34,806] kopf.objects         [INFO    ] [default/buy-milk] Updating is processed: 1 succeeded; 0 failed.
    if spec.get('done', False):
    
# buy-milk CR 삭제 완료

CRD를 정의하고, CR을 생성하면 CR 값의 변화에 따라
Controller(Python kopf)에서 인지하고, 정의한 행위를 실행한다.

 

CRD + Controller를 합쳐서 Operator라고 한다.


8. 배포 방식

구성 요소 역할 예시
① CRD (CustomResourceDefinition) 새로운 리소스 타입 정의 Todo 타입 정의 (todos.example.com)
② Controller (Deployment or Pod) CR을 감시하고 처리하는 컨트롤러 kopf나 Go 기반 operator
③ CR (Custom Resource) 실제 데이터 객체 buy-milk, deploy-app 같은 실제 Todo

8-1. Helm 차트 / Kustomize / 매니페스트 묶음

보통은 Operator를 설치 단위(패키지)로 묶어 배포합니다.

예를 들어 디렉터리 구조는 이렇게 됩니다.

todo-operator/
├── crd/
│   └── todo-crd.yaml
├── controller/
│   └── deployment.yaml
└── examples/
    └── todo-sample.yaml
 

배포 명령:
(manifest 묶음) kubectl apply -f my-operator/ 

  • CRD 등록
  • 컨트롤러 Pod(Deployment) 생성
  • 샘플 CR 생성
    이 세 가지가 순서대로 한 번에 적용됩니다.

 

8-2. Operator Lifecycle Manager (OLM)

  • 쿠버네티스에서 Operator들을 앱처럼 관리할 수 있게 하는 공식 관리 시스템
  • OLM은 CRD, Controller, RBAC, ServiceAccount 등 모든 리소스를 묶어서 “하나의 Operator 번들(package)” 로 관리합니다.
  • 예시:이 한 줄로 전체 Operator를 설치할 수 있음.
  • kubectl apply -f todo-operator-bundle.yaml

 

비교 항목매니페스트 묶음Helm 차트

배포 명령 kubectl apply -f helm install
템플릿 변수 ❌ 없음 ✅ 지원 (values.yaml)
버전 관리 수동 자동 (helm upgrade, rollback 등)
패키징 단순 폴더 .tgz 패키지
실제 내부 동작 API 서버에 YAML 적용 동일 (렌더링 후 apply)