Overview
Kubebuilder를 활용해서 Kubernetes Operator를 생성해본다.
Kubebuilder란?
Kubebuilder는 Kubernetes 운영자를 구축하기 위한 강력한 프레임워크이다. scaffolding, 코드 생성 및 Kubernetes 도구와의 통합을 제공하여 맞춤형 컨트롤러 및 API 개발을 단순화한다.
Kubernetes Operator는 커스텀 리소스(CRD)를 관리하고 애플리케이션 배포, 확장, 관리를 자동화하는 커스텀 컨트롤러이다.
Kubebuilder를 사용하는 이유는 무엇일까?
- 코드 생성: CRD 및 컨트롤러에 대한 상용구 코드를 자동으로 생성한다.
- 표준 기반: Kubernetes API 패턴을 준수하는 controller-runtime 라이브러리를 기반으로 구축되었다.
- scaffolding: 구성 파일, Dockerfile, Makefile을 포함하여 프로젝트의 구조를 제공한다.
- 테스트: Kubernetes용 'envtest'와 같은 테스트 도구와의 통합을 지원한다.
- 확장성: 특정 요구 사항에 맞게 컨트롤러를 쉽게 확장하고 사용자 정의할 수 있다.
Kubebuilder 워크플로
Kubebuilder 설치
curl -L -o kubebuilder <https://github.com/kubernetes-sigs/kubebuilder/releases/latest/download/kubebuilder_>_
chmod +x kubebuilder && sudo mv kubebuilder /usr/local/bin/
프로젝트 초기화(생성)
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 를 사용해 간단한 app을 생성해보았다. 프레임워크를 제공해주기 때문에 개발하기 상당히 용이하다. 다들 한번쯤 간단한 app이라도 생성해보길 바란다. 생성해보면, 복잡한 Kubernetes Application들이 어떤 구조로 개발되었는지에 대하여 약간이나마 알 수 도움이 될 것이다.
다음시간에는 생성한 app에 대하여 release 그리고 helm 패키징하여 github에 hosting 하는 방법에 대해서 알아보려고 한다.
Reference
https://github.com/kubernetes-sigs/kubebuilder
'Container Orchestration > Kubernetes' 카테고리의 다른 글
Helm Chart 생성 및 패키징 (gh-pages) (1) | 2024.12.20 |
---|---|
Kubernetes Operator 및 Custom Resource Definitions(CRDs) 이해하기 (0) | 2024.12.12 |
Kubernetes Headless Service란? (0) | 2024.12.03 |
Kubernetes Deployment Strategy (0) | 2024.11.26 |
Helm Chart Template 문법 (0) | 2024.11.15 |