Container Orchestration/Kubernetes

Kubernetes Operator(CRD, CR) 생성(With kubebuilder)

Somaz 2024. 12. 17. 10:25
728x90
반응형

Overview

이번 글에서는 Kubebuilder를 활용하여 Kubernetes Operator를 개발하는 전반적인 과정을 살펴본다.
Kubebuilder는 Kubernetes의 표준 API 패턴을 따르며, CRD 생성부터 컨트롤러 구현, 테스트, 빌드, 배포까지 효율적인 워크플로우를 제공한다.


직접 Operator를 만들고 Docker 이미지로 빌드하여 배포하는 방법까지 다루므로,

실제 프로젝트에 적용하기 위한 기반을 마련할 수 있다.

 

k8s-namespace-sync, helios-lb 등 실 사례를 통해 Operator 구조를 이해하고,

마커 기반의 CRD 정의, Makefile을 통한 테스트 및 빌드 자동화 등 실무에 꼭 필요한 요소들도 함께 다뤘다.

 

 

 

 

 

📅 관련 글

2024.04.15 - [Container Orchestration/Kubernetes] - Kubernetes Operator 및 Custom Resource Definitions(CRDs) 이해하기

2024.12.20 - [Container Orchestration/Kubernetes] - Helm Chart 생성 및 패키징 (gh-pages)

 

 

 


 

 

 

 

Kubebuilder란?

Kubebuilder는 Kubernetes 운영자를 구축하기 위한 강력한 프레임워크이다. scaffolding, 코드 생성 및 Kubernetes 도구와의 통합을 제공하여 맞춤형 컨트롤러 및 API 개발을 단순화한다.

Kubernetes Operator는 커스텀 리소스(CRD)를 관리하고 애플리케이션 배포, 확장, 관리를 자동화하는 커스텀 컨트롤러이다.

 

 

Kubebuilder를 사용하는 이유는 무엇일까?

  • 코드 생성: CRD 및 컨트롤러에 대한 상용구 코드를 자동으로 생성한다.
  • 표준 기반: Kubernetes API 패턴을 준수하는 controller-runtime 라이브러리를 기반으로 구축되었다.
  • scaffolding: 구성 파일, Dockerfile, Makefile을 포함하여 프로젝트의 구조를 제공한다.
  • 테스트: Kubernetes용 'envtest'와 같은 테스트 도구와의 통합을 지원한다.
  • 확장성: 특정 요구 사항에 맞게 컨트롤러를 쉽게 확장하고 사용자 정의할 수 있다.

 

 

 


 

 

 

 

 

Kubebuilder 워크플로

 

Kubebuilder 설치

# linux
curl -L -o kubebuilder https://github.com/kubernetes-sigs/kubebuilder/releases/latest/download/kubebuilder
chmod +x kubebuilder && sudo mv kubebuilder /usr/local/bin/

# mac
brew install kubebuilder

 

 

프로젝트 초기화(생성)

mkdir my-operator
cd my-operator
kubebuilder init --domain example.com --repo my-operator
  • -domain: API 도메인(예: example.com)을 지정
  • --repo: 프로젝트의 Go 모듈 경로를 설정

 

새로운 API 및 컨트롤러에 대한 코드를 생성한한다.

kubebuilder create api --group apps --version v1 --kind MyApp
  • -group: API 그룹(예: apps).
  • --version: API 버전(예: v1).
  • --kind: 사용자 정의 리소스의 이름(예: MyApp).
  • config/crd/에 CRD 정의
  • api/ 및 controllers/의 API 및 컨트롤러 파일

 

 

프로젝트 구조

tree -d
.
├── api
│   └── v1
├── bin
├── cmd
├── config
│   ├── crd
│   ├── default
│   ├── manager
│   ├── network-policy
│   ├── prometheus
│   ├── rbac
│   └── samples
├── hack
├── internal
│   └── controller
└── test
    ├── e2e
    └── utils

 

 

 

 

 


 

 

 

 

 

Kubebuilder 사용하여 Operator 생성

 

내가 만든 Kubernetes Operator이다.

https://github.com/somaz94/k8s-namespace-sync

https://github.com/somaz94/helios-lb

 

 

사전 작업

# kubebuilder 설치
brew install kubebuilder

# git repo 생성 후 clone
git clone git@github.com-somaz94:somaz94/k8s-namespace-sync.git

# kube builder 프로젝트 초기화
kubebuilder init --domain nsync.dev --repo github.com/somaz94/k8s-namespace-sync

# api 생성
kubebuilder create api --group sync --version v1 --kind NamespaceSync

 

 

 

파일 설명 및 구조 설명

 

crd 스키마 정의는 아래의 파일에 해준다.

  • `api/v1/namespacesync_types.go`

 

main.go는 아래의 경로에 정의 되어있다.

  • `cmd/main.go`

 

controller는 해당 파일에 정의해준다. 필자는 해당 파일 이외에도 다양한 기능을 위한 파일들을 구성하였다.

  • `internal/controller/namespacesync_controller.go`
ls
backup					filters.go				namespacesync_controller.go		namespacesync_controller_target_test.go	resources.go				sync.go
events.go				metrics.go				namespacesync_controller_filter_test.go	namespacesync_controller_test.go	suite_test.go				utils.go

 

 

 

 

테스트 코드도 동일한 경로에 정의해주면 되고 make test 명령어를 사용하여 test 가능하다.

자세한 내용이 궁금하다면, makefile을 확인해주면 된다.

 

모든 기능 구현이 끝났다면, 이미지를 빌드 후 업로드 할 dockerhub 계정을 만들어준다.

  • Docker Hub 웹사이트에서 Access Token 생성
  • https://hub.docker.com/settings/security 접속
  • "New Access Token" 클릭
  • 토큰 이름 입력 (예: "k8s-namespace-sync")
  • 토큰이 생성되면 복사 (이 토큰은 한 번만 표시되므로 잘 저장해두세요)

 

 

 

터미널에서 로그인해준다.

docker login -u somaz940
# token을 비밀번호로 입력한다.
  • make test를 진행하기 전에 설명해야 할 내용이 남아있다.

 

 

 

kubebuilder 마커

Kubernetes CRD(Custom Resource Definition)의 스키마와 표시 방식을 정의하는 kubebuilder 마커(marker)이다.

 

`namespacesync_types.go` 파일을 확인해보면 아래와 같이 마커가 정의되어 있다.

// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:printcolumn:name="Source",type="string",JSONPath=".spec.sourceNamespace"
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.conditions[?(@.type=='Ready')].status"
// +kubebuilder:printcolumn:name="Message",type="string",JSONPath=".status.conditions[?(@.type=='Ready')].message"
  • +kubebuilder:object:root=true
    • 이 타입이 CRD의 루트 객체임을 나타낸다.

 

  • +kubebuilder:subresource:status
    • status 서브리소스를 활성화한다.
    • 상태 업데이트를 위한 별도의 엔드포인트를 제공한다.

 

  • +kubebuilder:printcolumn:...
    • kubectl get 명령어 실행 시 표시될 커스텀 컬럼을 정의한다.
    • 정의된 컬럼들
      • Source: spec.sourceNamespace 값을 표시
      • Age: 생성 시간 표시
      • Status: Ready 컨디션의 상태 표시
      • Message: Ready 컨디션의 메시지 표시

 

 

 

make manifests 사용 시

  • controller-gen이 Go 코드의 마커들을 읽어서 CRD YAML 파일을 생성
  • RBAC 권한 설정 파일 생성
  • config/crd/bases 디렉토리에 결과물 생성

 

make generate 사용 시

  • Go 타입에 대한 DeepCopy 메서드 생성
  • 쿠버네티스 클라이언트 코드에 필요한 타입 변환 코드 생성

 

makefile을 확인해보면 아래와 같이 정의되어있다. 모든 필요한 작업을 수행한 후 test 까지 진행해준다.

.PHONY: test
test: manifests generate fmt vet envtest ## Run tests.
	KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test $$(go list ./... | grep -v /e2e) -coverprofile cover.out

 

 

그리고 makefile에서 IMG 업로드 경로와 K8S 테스트 버전을 지정해 줄 수 있다.

# Image URL to use all building/pushing image targets
IMG ?= somaz940/k8s-namespace-sync:v0.1.6
# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary.
ENVTEST_K8S_VERSION = 1.31.0

 

 

테스트가 성공적으로 끝낫다면, make dokcer-buildx 를 사용해서 다양한 플랫폼으로 동시에 build 해준다.

필자는 makefile을 수정해서, latest, versiontag 그리고 latest+versiontag 환경으로 build 할 수 있게 makefile을 수정했다.

PLATFORMS ?= linux/arm64,linux/amd64,linux/s390x,linux/ppc64le
.PHONY: docker-buildx-tag
docker-buildx-tag: ## Build and push docker image for the manager for cross-platform support with specific version
	# copy existing Dockerfile and insert --platform=${BUILDPLATFORM} into Dockerfile.cross
	sed -e '1 s/\\(^FROM\\)/FROM --platform=\\$$\\{BUILDPLATFORM\\}/; t' -e ' 1,// s//FROM --platform=\\$$\\{BUILDPLATFORM\\}/' Dockerfile > Dockerfile.cross
	- $(CONTAINER_TOOL) buildx create --name k8s-namespace-sync-builder
	$(CONTAINER_TOOL) buildx use k8s-namespace-sync-builder
	# Build and push version-specific tag
	- $(CONTAINER_TOOL) buildx build --push --platform=$(PLATFORMS) \\
		--tag ${IMG} \\
		-f Dockerfile.cross .
	- $(CONTAINER_TOOL) buildx rm k8s-namespace-sync-builder
	rm Dockerfile.cross

.PHONY: docker-buildx-latest
docker-buildx-latest: ## Build and push docker image for the manager for cross-platform support with latest tag
	# copy existing Dockerfile and insert --platform=${BUILDPLATFORM} into Dockerfile.cross
	sed -e '1 s/\\(^FROM\\)/FROM --platform=\\$$\\{BUILDPLATFORM\\}/; t' -e ' 1,// s//FROM --platform=\\$$\\{BUILDPLATFORM\\}/' Dockerfile > Dockerfile.cross
	- $(CONTAINER_TOOL) buildx create --name k8s-namespace-sync-builder
	$(CONTAINER_TOOL) buildx use k8s-namespace-sync-builder
	# Build and push latest tag
	- $(CONTAINER_TOOL) buildx build --push --platform=$(PLATFORMS) \\
		--tag $(shell echo ${IMG} | cut -f1 -d:):latest \\
		-f Dockerfile.cross .
	- $(CONTAINER_TOOL) buildx rm k8s-namespace-sync-builder
	rm Dockerfile.cross

.PHONY: docker-buildx
docker-buildx: ## Build and push both version-specific and latest tags
docker-buildx: docker-buildx-tag docker-buildx-latest

 

 

build가 성공적으로 완료되었다면 make deploy 명령어를 사용해서 app을 배포해준다.

✔ ~/PrivateWork/k8s-namespace-sync [main|✔] 
18:40 $ make deploy 
/Users/somaz/PrivateWork/k8s-namespace-sync/bin/controller-gen rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases
cd config/manager && /Users/somaz/PrivateWork/k8s-namespace-sync/bin/kustomize edit set image controller=somaz940/k8s-namespace-sync:v0.1.6
/Users/somaz/PrivateWork/k8s-namespace-sync/bin/kustomize build config/default | kubectl apply -f -
namespace/k8s-namespace-sync-system created
customresourcedefinition.apiextensions.k8s.io/namespacesyncs.sync.nsync.dev created
serviceaccount/k8s-namespace-sync-controller-manager created
role.rbac.authorization.k8s.io/k8s-namespace-sync-leader-election-role created
clusterrole.rbac.authorization.k8s.io/k8s-namespace-sync-manager-role created
clusterrole.rbac.authorization.k8s.io/k8s-namespace-sync-metrics-auth-role created
clusterrole.rbac.authorization.k8s.io/k8s-namespace-sync-metrics-reader created
clusterrole.rbac.authorization.k8s.io/k8s-namespace-sync-namespacesync-editor-role created
clusterrole.rbac.authorization.k8s.io/k8s-namespace-sync-namespacesync-viewer-role created
rolebinding.rbac.authorization.k8s.io/k8s-namespace-sync-leader-election-rolebinding created
clusterrolebinding.rbac.authorization.k8s.io/k8s-namespace-sync-manager-rolebinding created
clusterrolebinding.rbac.authorization.k8s.io/k8s-namespace-sync-metrics-auth-rolebinding created
service/k8s-namespace-sync-controller-manager-metrics-service created
deployment.apps/k8s-namespace-sync-controller-manager created

 

 

api와 crd를 확인해준다.

k get apiservices.apiregistration.k8s.io |grep nsync
v1.sync.nsync.dev                      Local                        True        25s

k get crd |grep nsync
namespacesyncs.sync.nsync.dev                2024-12-16T09:41:00Z

 

 

필자는 다양한 sample crd를 생성하였다.

ls config/samples/
kustomization.yaml			sync_v1_namespacesync_exclude.yaml	sync_v1_namespacesync_target.yaml	test-configmap2.yaml			test-secret2.yaml
sync_v1_namespacesync.yaml		sync_v1_namespacesync_filter.yaml	test-configmap.yaml			test-secret.yaml

 

 

App 기능이 궁금한다면 아래의 링크를 확인해주길 바란다.

https://github.com/somaz94/k8s-namespace-sync

https://github.com/somaz94/helios-lb

 

 

app의 시작로그를 확인해보면 아래와 같다.

2024-12-16T09:41:01.541Z        ESC[34minfoESC[0m       setup   creating controller     {"controller": "NamespaceSync", "scheme": "pkg/runtime/scheme.go:100"}
2024-12-16T09:41:01.541Z        ESC[34minfoESC[0m       namespacesync-controller        Setting up controller manager
2024-12-16T09:41:01.545Z        ESC[34minfoESC[0m       setup   controller created successfully {"controller": "NamespaceSync"}
2024-12-16T09:41:01.545Z        ESC[34minfoESC[0m       setup   starting manager
2024-12-16T09:41:01.545Z        ESC[34minfoESC[0m       controller-runtime.metrics      Starting metrics server
2024-12-16T09:41:01.545Z        ESC[34minfoESC[0m       setup   disabling http/2
2024-12-16T09:41:01.545Z        ESC[34minfoESC[0m       starting server {"name": "health probe", "addr": "[::]:8081"}
I1216 09:41:01.645802       1 leaderelection.go:254] attempting to acquire leader lease k8s-namespace-sync-system/47c2149c.nsync.dev...
I1216 09:41:01.659829       1 leaderelection.go:268] successfully acquired lease k8s-namespace-sync-system/47c2149c.nsync.dev
2024-12-16T09:41:01.659Z        ESC[35mdebugESC[0m      manager.events  k8s-namespace-sync-controller-manager-5874b756c8-h9ng6_2a3d0c37-b1f0-46c3-bcff-e28ccc2750e9 became leader       {"type": "Normal", "object": {"kind":"Lease","namespace":"k8s-namespace-sync-system","name":"47c21
49c.nsync.dev","uid":"385be981-f090-4b4e-a341-7888c2858e4b","apiVersion":"coordination.k8s.io/v1","resourceVersion":"12611207"}, "reason": "LeaderElection"}
2024-12-16T09:41:01.660Z        ESC[34minfoESC[0m       manager Starting EventSource    {"controller": "namespacesync", "controllerGroup": "sync.nsync.dev", "controllerKind": "NamespaceSync", "source": "kind source: *v1.NamespaceSync"}
2024-12-16T09:41:01.660Z        ESC[34minfoESC[0m       manager Starting EventSource    {"controller": "namespacesync", "controllerGroup": "sync.nsync.dev", "controllerKind": "NamespaceSync", "source": "kind source: *v1.Namespace"}
2024-12-16T09:41:01.660Z        ESC[34minfoESC[0m       manager Starting EventSource    {"controller": "namespacesync", "controllerGroup": "sync.nsync.dev", "controllerKind": "NamespaceSync", "source": "kind source: *v1.Secret"}
2024-12-16T09:41:01.660Z        ESC[34minfoESC[0m       manager Starting EventSource    {"controller": "namespacesync", "controllerGroup": "sync.nsync.dev", "controllerKind": "NamespaceSync", "source": "kind source: *v1.ConfigMap"}
2024-12-16T09:41:01.660Z        ESC[34minfoESC[0m       manager Starting Controller     {"controller": "namespacesync", "controllerGroup": "sync.nsync.dev", "controllerKind": "NamespaceSync"}

 

 

 

 

 


 

 

 

 

마치며

Kubebuilder는 복잡한 Kubernetes 리소스를 쉽고 일관되게 다룰 수 있도록 도와주는 매우 강력한 도구이다.
프레임워크가 잘 갖춰져 있어 초보자도 빠르게 커스텀 리소스를 만들고 운영 자동화를 경험할 수 있다.


Operator를 직접 만들어보면 Kubernetes 생태계가 어떻게 확장 가능하게 설계되어 있는지 더 깊게 이해할 수 있다.

 

다음 글에서는 생성한 Operator를 Helm Chart로 패키징하고 GitHub Pages를 통해 배포하는 방법을 다룰 예정이다.

2024.12.20 - [Container Orchestration/Kubernetes] - Helm Chart 생성 및 패키징 (gh-pages)

 

 


Reference

https://github.com/kubernetes-sigs/kubebuilder

https://github.com/somaz94/k8s-namespace-sync

https://medium.com/@SabujJanaCodes/building-a-kubernetes-operator-in-go-reconciling-our-pdfdoc-crd-for-converting-text-to-pdf-files-d0c0c7da98be

https://github.com/somaz94/helios-lb

728x90
반응형