일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- operator
- Argo
- Kubeflow
- Litmus
- CANARY
- mlops
- 오퍼레이터
- argo rollout
- kubernetes operator
- gitea
- knative
- keda
- CI/CD
- Model Serving
- Continuous Deployment
- tekton
- MLflow
- opensearch
- gitops
- blue/green
- Pulumi
- 카오스 엔지니어링
- argocd
- Kubernetes
- opentelemetry
- serving
- seldon core
- Kopf
- nginx ingress
- Kubernetes 인증
- Today
- Total
Kubernetes 이야기
Kubebuilder ( Kubernetes operator ) 본문
오퍼레이터(Operator)는 사용자 정의 리소스 (CR)를 사용하여 애플리케이션 및 해당 컴포넌트를 관리하는 쿠버네티스의 소프트웨어 확장 기능이다. 오퍼레이터는 쿠버네티스 원칙, 특히 컨트롤 루프를 따른다.
오퍼레이터 패턴은 서비스 또는 서비스 셋을 관리하는 운영자의 주요 목표를 포착하는 것을 목표로 한다. 특정 애플리케이션 및 서비스를 돌보는 운영자는 시스템의 작동 방식, 배포 방법 및 문제가 있는 경우 대처 방법에 대해 깊이 알고 있다.
예를 들어, Kubernetes에서 MySQL , Elasticsearch 또는 Gitlab 실행기 와 같은 도구를 배포하고 유지 관리하는 운영자를 상상할 수 있다 . 운영자는 이러한 도구를 구성하고 이벤트에 따라 시스템 상태를 조정하고 장애에 대응할 수 있다.
Operator는 사용자 지정 리소스 정의(CRD)와 컨트롤러의 두 가지 구성 요소로 구성된다. CRD는 사양 및 상태를 설명하는 데 사용되는 "Kubernetes 사용자 지정 유형" 또는 리소스의 청사진이다. 사용자 지정 리소스(또는 CR)라고 하는 CRD의 인스턴스를 정의할 수 있다. 컨트롤러(제어 루프라고도 함)는 클러스터의 상태를 지속적으로 모니터링하고 이벤트에 따라 변경한다. 목표는 리소스의 현재 상태를 사용자 지정 리소스 사양 (CR)에서 사용자가 정의한 원하는 상태로 가져오는 것이다.
일반적으로 컨트롤러는 리소스 유형에 따라 다르지만 다양한 리소스 집합에 대해 CRUD(Create, Read, Update 및 Delete) 작업을 수행할 수 있다.
개발자가 쉽게 오퍼레이터를 개발하기 위해서 Go 언어는 주로 Kubebuilder 나 OperatorSDK를 사용하여 개발한다.
참고로 Python에서는 KOPF (https://github.com/nolar/kopf) 를 사용하여 개발한다.
Kubebuilder 를 사용하여 Operator를 만들어 보자.
설치
Kubebuilder를 설치 전 사전 설치 조건을 살펴보자.
- 버전 v1.17.9 이상 으로 이동
- 도커 버전 17.03 이상.
- kubectl 버전 v1.11.3 이상.
- Kubernetes v1.11.3+ 클러스터에 대한 액세스.
# curl -L -o kubebuilder https://go.kubebuilder.io/dl/latest/$(go env GOOS)/$(go env GOARCH)
# chmod +x kubebuilder && mv kubebuilder /usr/local/bin/
# kubebuilder version
Version: main.version{KubeBuilderVersion:"3.6.0", KubernetesVendor:"1.24.1", GitCommit:"f20414648f1851ae97997f4a5f8eb4329f450f6d", BuildDate:"2022-08-03T11:47:17Z", GoOs:"linux", GoArch:"amd64"}
# kubebuilder
CLI tool for building Kubernetes extensions and tools.
Usage:
kubebuilder [flags]
kubebuilder [command]
Examples:
The first step is to initialize your project:
kubebuilder init [--plugins=<PLUGIN KEYS> [--project-version=<PROJECT VERSION>]]
<PLUGIN KEYS> is a comma-separated list of plugin keys from the following table
and <PROJECT VERSION> a supported project version for these plugins.
Plugin keys | Supported project versions
------------------------------------------+----------------------------
base.go.kubebuilder.io/v3 | 3
declarative.go.kubebuilder.io/v1 | 2, 3
deploy-image.go.kubebuilder.io/v1-alpha | 3
go.kubebuilder.io/v2 | 2, 3
go.kubebuilder.io/v3 | 3
go.kubebuilder.io/v4-alpha | 3
grafana.kubebuilder.io/v1-alpha | 3
kustomize.common.kubebuilder.io/v1 | 3
kustomize.common.kubebuilder.io/v2-alpha | 3
For more specific help for the init command of a certain plugins and project version
configuration please run:
kubebuilder init --help --plugins=<PLUGIN KEYS> [--project-version=<PROJECT VERSION>]
Default plugin keys: "go.kubebuilder.io/v3"
Default project version: "3"
Available Commands:
alpha Alpha-stage subcommands
completion Load completions for the specified shell
create Scaffold a Kubernetes API or webhook
edit Update the project configuration
help Help about any command
init Initialize a new project
version Print the kubebuilder version
Flags:
-h, --help help for kubebuilder
--plugins strings plugin keys to be used for this subcommand execution
--project-version string project version (default "3")
Use "kubebuilder [command] --help" for more information about a command.
설치가 완료되었으니 이제 샘플 프로그램을 제작해 보자.
샘플 Operator
1) 프로젝트 생성
먼저 프로젝트를 생성해 보자. 프로젝트는 다음과 같이 만든다.
# mkdir -p ~/projects/echo
# cd ~/projects/echo
# kubebuilder init --domain my.domain --repo my.domain/echo
생성된 파일 리스트는 다음과 같다.
# ls -al
합계 228
drwxr-xr-x. 4 root root 180 8월 11 18:24 .
drwxr-xr-x. 3 root root 23 8월 11 18:23 ..
-rw-------. 1 root root 129 8월 11 18:23 .dockerignore
-rw-------. 1 root root 367 8월 11 18:23 .gitignore
-rw-------. 1 root root 776 8월 11 18:23 Dockerfile
-rw-------. 1 root root 5077 8월 11 18:23 Makefile
-rw-------. 1 root root 111 8월 11 18:24 PROJECT
-rw-------. 1 root root 2721 8월 11 18:23 README.md
drwx------. 6 root root 66 8월 11 18:23 config
-rw-------. 1 root root 3752 8월 11 18:24 go.mod
-rw-r--r--. 1 root root 95177 8월 11 18:24 go.sum
drwx------. 2 root root 32 8월 11 18:23 hack
-rw-------. 1 root root 3483 8월 11 18:23 main.go
- main.go : 프로젝트의 시작점이다. operator를 설정하고 실행한다.
- config/ : operator를 배포하기 위한 manifest가 포함되어 있다.
- Dockerfile : container 이미지를 빌드하는데 사용되는 파일이다.
2) API 생성
다음 명령을 실행하여 새 API(그룹/버전) v1과 새 CRD 를 만든다.
# kubebuilder create api --group app --version v1 --kind Echo
실행 후에 2개의 폴더가 생성되었다.
- api/v1 : 여기에는 CRD 가 포함되어 있다. ( echo_types.go )
- controllers : echo 컨트롤러를 포함한다. ( echo_controller.go )
3) CRD 및 Controller
우선 만들고자 하는 CRD의 사양은 다음과 같다.
apiVersion: app.my.domain/v1
kind: Echo
metadata:
name: echo-sample
spec:
command: "echo hello world!"
이제 CRD를 구현해 보자. CRD의 구현체는 api/v1/echo_types.go 이다.
EchoSpec과 EchoStatus 구조체를 다음과 같이 수정한다.
type EchoSpec struct {
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
// Important: Run "make" to regenerate code after modifying this file
// Foo is an example field of Echo. Edit echo_types.go to remove/update
Command string `json:"command,omitempty"`
}
// EchoStatus defines the observed state of Echo
type EchoStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
// Important: Run "make" to regenerate code after modifying this file
Phase string `json:"phase,omitempty"`
}
API 정의를 편집하는 경우 다음을 사용하여 사용자 지정 리소스(CR) 또는 사용자 지정 리소스 정의(CRD)와 같은 매니페스트를 생성한다.
# make manifests
K8S 에 CRD를 설치하려면 다음의 명령을 수행한다.
# make install
K8S에서 CRD가 생성되었는지 확인해 보자.
# k get crds
...
echoes.app.my.domain 2022-08-11T10:52:27Z
...
이제 한번 CR을 생성해 보자
apiVersion: app.my.domain/v1
kind: Echo
metadata:
name: echo-sample
spec:
command: "echo hello world!"
생성 후 조회해 보자.
# k get echo
NAME AGE
echo-sample 10s
지금까지는 CRD 의 대한 정의를 하였지만, 아직 컨트롤러가 없기 때문에 CR에 대한 어떠한 액션도 없다.
이제 컨트롤러 로직을 구현해보자. 컨트롤러는 우리가 생성한 CRD로 무엇을 할 것인지 kubernetes에게 알려주는 기능을 한다. 이번 컨트롤러에서는 CR 생성 시 Pod를 생성한 후 command를 실행해 보는 것을 해 보자.
대부분의 scaffolding은 이미 main.go 와 controllers/ 파일안에 있다. 사용자는 Reconcile 함수에 로직을 넣으면 된다고 생각하면 된다.
컨트롤러는 기본적으로 해당 객체에 대해 주기적 루프를 실행하고 해당 객체에 대한 변경 사항을 감시한다.
변경 사항이 발생하면 루프 본문이 트리거되고 관련 로직이 실행될 수 있다. 이 Reconcile 함수는 모든 컨트롤러 로직의 진입점이다.
이미 생성된 controller ( controllers/echo_controller.go ) 의 reconcile 함수를 살펴보자.
func (r *EchoReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
_ = log.FromContext(ctx)
// TODO(user): your logic here
return ctrl.Result{}, nil
}
이제 Pod를 생성하고 command 를 수행하는 로직을 구현해 보자.
func (r *EchoReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
log := log.FromContext(ctx)
var instance appv1.Echo
errGet := r.Get(ctx, req.NamespacedName, &instance)
if errGet != nil {
log.Error(errGet, "Error getting instance")
return ctrl.Result{}, client.IgnoreNotFound(errGet)
}
pod := NewPod(&instance)
_, errCreate := ctrl.CreateOrUpdate(ctx, r.Client, pod, func() error {
return ctrl.SetControllerReference(&instance, pod, r.Scheme)
})
if errCreate != nil {
log.Error(errCreate, "Error creating pod")
return ctrl.Result{}, nil
}
err := r.Status().Update(context.TODO(), &instance)
if err != nil {
return ctrl.Result{}, err
}
return ctrl.Result{}, nil
}
func NewPod(cr *appv1.Echo) *corev1.Pod {
labels := map[string]string{
"app": cr.Name,
}
return &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: cr.Name,
Namespace: cr.Namespace,
Labels: labels,
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "busybox",
Image: "busybox",
Command: strings.Split(cr.Spec.Command, " "),
},
},
RestartPolicy: corev1.RestartPolicyOnFailure,
},
}
}
그리고, SetupWithManager함수에 Owns(&corev1.Pod{}) 를 추가한다. 이것은 컨트롤러 관리자에게 이 컨트롤러에 의해 생성된 포드도 변경 사항을 감시해야 한다고 알려주는 기능이다.
func (r *EchoReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&appv1.Echo{}).
Owns(&corev1.Pod{}).
Complete(r)
}
마지막으로 import 구문에 다음을 추가한다.
import (
"strings"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
...
)
이제 실행해 보자.
# make run
이렇게 하면 console 에 포그라운드로 실행되면서 로그가 출력된다.
test -s /root/project/echo/bin/controller-gen || GOBIN=/root/project/echo/bin go install sigs.k8s.io/controller-tools/cmd/controller-gen@v0.9.2
/root/project/echo/bin/controller-gen rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases
/root/project/echo/bin/controller-gen object:headerFile="hack/boilerplate.go.txt" paths="./..."
go fmt ./...
go vet ./...
go run ./main.go
1.6602180333173583e+09 INFO controller-runtime.metrics Metrics server is starting to listen {"addr": ":8080"}
1.6602180333181918e+09 INFO setup starting manager
1.6602180333185756e+09 INFO Starting server {"path": "/metrics", "kind": "metrics", "addr": "[::]:8080"}
1.6602180333186262e+09 INFO Starting EventSource {"controller": "echo", "controllerGroup": "app.my.domain", "controllerKind": "Echo", "source": "kind source: *v1.Echo"}
1.6602180333186808e+09 INFO Starting EventSource {"controller": "echo", "controllerGroup": "app.my.domain", "controllerKind": "Echo", "source": "kind source: *v1.Pod"}
1.6602180333186896e+09 INFO Starting Controller {"controller": "echo", "controllerGroup": "app.my.domain", "controllerKind": "Echo"}
1.6602180333187807e+09 INFO Starting server {"kind": "health probe", "addr": "[::]:8081"}
1.6602180334198806e+09 INFO Starting workers {"controller": "echo", "controllerGroup": "app.my.domain", "controllerKind": "Echo", "worker count": 1}
Pod가 실행되었는지 확인해 보자.
# k get po -n default
NAME READY STATUS RESTARTS AGE
echo-sample 0/1 Completed 0 43s
# k logs -f echo-sample
hello world!
클러스터에 배포
다음에서 지정한 위치에 이미지를 빌드하고 푸시한다.
# make docker-build docker-push IMG=<some-registry>/<project-name>:tag
다음으로 지정된 이미지를 사용하여 컨트롤러를 클러스터에 배포한다.
# make deploy IMG=<some-registry>/<project-name>:tag
CRD 제거
클러스터에서 CRD를 삭제하려면
# make uninstall
Controller 배포 취소
배포된 컨트롤러를 취소하려면
# make undeploy
'개발 > go' 카테고리의 다른 글
GoDS ( Go Data Structures ) (0) | 2022.08.24 |
---|---|
Viper (0) | 2022.08.23 |
cobra library 사용법 (0) | 2022.08.22 |
모둘과 패키지 (0) | 2022.08.22 |
Golang - Gin Framework (0) | 2022.08.08 |