[11. CRD / Operator]
출제 비중: 낮음 ~ 중간. 커리큘럼에 "CRD 와 Operator 이해"가 있다.
보통 목록 조회, CR 생성, explain 으로 필드 찾기 수준. CRD 를 직접 만드는 문제는 드묾.
관련 문제: 0022 (CRD 조회와 Custom Resource 생성) - 통과
1. 왜 CRD 인가
쿠버네티스에는 Pod, Deployment, Service 같은 기본 리소스가 있다.
"인증서", "Gateway", "DB 클러스터"처럼 기본에 없는 개념도
쿠버네티스 방식으로 관리하고 싶어진다.
CRD 는 apiserver 에 새 리소스 타입을 등록하는 기능이다.
등록하고 나면 kubectl get, apply, delete 가 기본 리소스와 똑같이 동작한다.
2. 세 가지 용어
CRD 새 리소스 타입의 정의. 이름, 필드, 검증 규칙 = 테이블 스키마
CR 그 타입으로 만든 실제 오브젝트 하나 = 테이블의 행 하나
Operator CR 을 감시하다가 실제 일을 하는 컨트롤러 = 행이 추가되면 동작하는 프로그램
보통 Deployment 로 뜬다
CRD 만 있으면 아무 일도 일어나지 않는다.
CR 을 만들어도 etcd 에 저장만 될 뿐이다.
Operator 가 CR 을 읽고 Pod 을 띄우든 설정을 바꾸든 실제 동작을 해야 의미가 생긴다.
Deployment 도 같은 구조다.
Deployment 오브젝트를 읽고 ReplicaSet 을 만드는 건 controller-manager 안의 컨트롤러다.
3. 이미 쓰고 있었던 것
회사 서버 helm list 에서
prometheus-crds, aieg-crd CRD 만 설치하는 차트
prometheus-operator ServiceMonitor 같은 CR 을 감시하는 Operator
workload-controller-manager, project-controller 이름으로 보면 회사에서 만든 Operator
kind 클러스터에도 이미 있다.
Gateway API 와 Calico 가 설치될 때 CRD 를 수십 개 등록했다.
4. 시험 명령
kubectl get crd 설치된 CRD 목록
kubectl get crd <이름> -o yaml group, versions, scope, names, schema
kubectl api-resources --api-group=<group> 그룹별 리소스, kind, 약칭
kubectl api-resources | grep -i <키워드>
kubectl explain <kind> CR 최상위 필드 + GROUP/KIND/VERSION
kubectl explain <kind>.spec spec 아래 필드
kubectl explain <kind> --recursive 전체 필드 구조를 한 번에
kubectl get <kind> -A CR 목록
--recursive 가 좋은 이유
설명 없이 필드 트리만 쭉 보여줘서 전체 틀을 한눈에 볼 수 있다.
YAML 을 어떤 depth 로 써야 하는지 바로 보인다.
기본 리소스에도 쓸 수 있다. 예) kubectl explain pod.spec.containers --recursive
출력이 길면 | less 또는 | grep -A5 <필드> 로 좁힌다.
5. CRD 이름과 CR apiVersion 읽는 법
CRD 이름은 항상 <복수형>.<group>
shirts.stable.example.com
| +-- group
+-- plural
CR 을 만들 때
CRD 필드 CR 에서 쓰는 곳
spec.group + versions[].name apiVersion: <group>/<version> 예) stable.example.com/v1
spec.names.kind kind: 대소문자 그대로 예) Shirt
spec.scope Namespaced 면 metadata.namespace 필요
apiVersion 을 빨리 찾는 두 곳
kubectl explain shirts GROUP: stable.example.com / VERSION: v1
kubectl api-resources | grep -i shirt
shirts sh stable.example.com/v1 true Shirt
(이름 약칭 APIVERSION NAMESPACED KIND)
api-resources 의 APIVERSION 열을 그대로 복사하면 된다.
1절 RBAC 의 "apiGroups 는 apiVersion 의 앞부분"과 같은 원리.
RBAC 에서 CR 권한을 줄 때도 apiGroups 에 이 group 을 쓴다.
apiGroups: ["stable.example.com"] resources: ["shirts"]
6. 내가 겪은 에러와 원인
1) kubectl explain shirts .spec
error: We accept only this format: explain RESOURCE
-> 공백 없이 점으로 잇는다. kubectl explain shirts.spec
2) kubectl create shirt -f sample.yaml
error: Unexpected args: [shirt]
-> kubectl create <종류> 형태는 deployment, service 처럼
kubectl 에 생성기가 내장된 기본 리소스만 된다.
CR 은 YAML 로만 만든다. kubectl create -f 또는 kubectl apply -f
3) apiVersion: stable.example.com 으로 적었을 때
error: resource mapping not found ... no matches for kind "Shirt"
in version "stable.example.com" / ensure CRDs are installed first
-> apiVersion 에 version 이 빠졌다. stable.example.com/v1
"CRD 를 먼저 설치하라"는 문구에 속지 말 것.
CRD 는 있는데 apiVersion 이나 kind 가 틀려도 같은 에러가 난다.
4) k get shirt
zsh: command not found: k
-> 맥북에는 k alias 가 없다. 실제 시험 환경에는 미리 설정돼 있다.
맥북에도 만들어 두면 시험과 같은 손버릇으로 연습할 수 있다.
echo "alias k=kubectl" >> ~/.zshrc && source ~/.zshrc
7. 함정
- CRD 이름만 저장하라는 문제에서 -o name 을 그대로 쓰면 접두사가 붙는다.
kubectl get crd -o name
-> customresourcedefinition.apiextensions.k8s.io/shirts.stable.example.com
이름만 남기는 방법
kubectl get crd -o name | grep stable.example.com | cut -d/ -f2
kubectl get crd -o custom-columns=NAME:.metadata.name --no-headers | grep stable.example.com
그냥 kubectl get crd | grep 결과를 저장하면 CREATED AT 열까지 들어간다.
(이 저장소 채점기는 포함 여부만 봐서 접두사가 있어도 통과했지만,
실제 시험에서 "이름만"을 엄격하게 보면 틀릴 수 있다)
- schema 의 enum. 허용 값 목록 밖의 값은 apply 가 거부된다.
size 는 S, M, L, XL 만. 소문자 m 도 거부.
- CRD 를 지우면 CR 도 전부 사라진다.
실무에서 제일 위험한 실수. 운영 클러스터에서 CRD 차트를 uninstall 하면 데이터가 날아간다.
- kind 대소문자. kind: shirt 처럼 쓰면 못 찾는다. CRD 의 names.kind 그대로.
8. 문제 0022 풀이
요구사항
1) group stable.example.com 의 CRD 이름을 /tmp/cncf-out/crds.txt 에 한 줄씩
2) 그 kind 로 default 에 CR blue-shirt. spec.color: blue, spec.size: M
1) CRD 이름 저장
kubectl get crd -o custom-columns=NAME:.metadata.name,GROUP:.spec.group | grep stable.example.com
kubectl get crd -o name | grep stable.example.com | cut -d/ -f2 > /tmp/cncf-out/crds.txt
cat /tmp/cncf-out/crds.txt -> shirts.stable.example.com
jsonpath 로 group 을 정확히 거르는 방법 (공식 풀이)
kubectl get crd -o jsonpath='{range .items[?(@.spec.group=="stable.example.com")]}{.metadata.name}{"\n"}{end}' \
> /tmp/cncf-out/crds.txt
2) 스키마 확인
kubectl explain shirts GROUP, KIND, VERSION
kubectl explain shirts.spec color <string>, size <string> enum: S, M, L, XL
3) CR 생성
# sample.yaml
apiVersion: stable.example.com/v1
kind: Shirt
metadata:
name: blue-shirt
spec:
color: blue
size: M
kubectl create -f sample.yaml -n default
-> shirt.stable.example.com/blue-shirt created
4) 확인
kubectl get shirt -n default
kubectl get shirt blue-shirt -n default -o yaml
5) 채점
./bin/q check 22