[k8s] operator-sdk(Go)로 memcahce 오퍼레이터 구축하기 - 1

 

[k8s] operator-sdk(Go)로 memcahce 오퍼레이터 구축하기 - 1

Quickstart for Go-based Operators | Operator SDK Quickstart for Go-based OperatorsA simple set of instructions to set up and run a Go-based operator.sdk.operatorframework.io 필수 체크 요소 - cluster-admin 권한이 있어야함 - example.com 이

sua-tech.tistory.com

 

Operator 배포와 CRD등록까진 했다 

 

 

api/v1alpha1/memcached_types.go 수정하기


spec에 size 부분을 추가하고 status에 nodes를 추가한다 

// MemcachedSpec defines the desired state of Memcached
type MemcachedSpec 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 Memcached. Edit memcached_types.go to remove/update
        Foo string `json:"foo,omitempty"`
        Size int32 `json:"size"` // sua added
}

// MemcachedStatus defines the observed state of Memcached
type MemcachedStatus struct {
        // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
        // Important: Run "make" to regenerate code after modifying this file
        Nodes []string `json:"nodes"`
}

- api 수정을 했다면 make  install 작업이 필요하다

 

 

make 수행 : CRD와 관련된 코드 다시 생성

[root@tech7 memcached-operator]# make
/root/k8s/Operator/memcached-operator/bin/controller-gen-v0.14.0 rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases
/root/k8s/Operator/memcached-operator/bin/controller-gen-v0.14.0 object:headerFile="hack/boilerplate.go.txt" paths="./..."
go fmt ./...
api/v1alpha1/memcached_types.go
go vet ./...
go: downloading github.com/onsi/gomega v1.30.0
go build -o bin/manager cmd/main.go

 

 

Controller 수정하기 

경로: memcached-operator/internal/controller

파일:  memcached_controller.go

 

memcached_controller.go

- import

- struct MemcachedReconciler 

    - client / runtime.schema

- func : Reconcile 

  - k8s 에서 CR (Memcached) 리소스를 생성, 수정, 삭제 할때마다 호출이 된다  

  - size 필드를 읽고 인스턴스 수를 맞추는 로직을 추가할 예정

- func : SetupWithManager

 

[root@tech7 controller]# cat memcached_controller.go
package controller

import (
	"context"
	"fmt"                     // fmt 패키지를 추가
	"github.com/go-logr/logr" // logr 패키지 import
	"k8s.io/apimachinery/pkg/runtime"
	ctrl "sigs.k8s.io/controller-runtime"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/log" // log 패키지 import

	cachev1alpha1 "github.com/example/memcached-operator/api/v1alpha1"
)

// MemcachedReconciler reconciles a Memcached object
type MemcachedReconciler struct {
	client.Client
	Scheme *runtime.Scheme
	Log    logr.Logger // logr.Logger 타입으로 정의
}

//+kubebuilder:rbac:groups=cache.example.com,resources=memcacheds,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=cache.example.com,resources=memcacheds/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=cache.example.com,resources=memcacheds/finalizers,verbs=update

// Reconcile is part of the main kubernetes reconciliation loop which aims to
// move the current state of the cluster closer to the desired state.
// TODO(user): Modify the Reconcile function to compare the state specified by
// the Memcached object against the actual cluster state, and then
// perform operations to make the cluster state reflect the state specified by
// the user.
//
// For more details, check Reconcile and its Result here:
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.17.3/pkg/reconcile
func (r *MemcachedReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
	log := log.FromContext(ctx) // 컨텍스트에서 로그 가져오기

	// 1. Memcached 인스턴스 가져오기
	var memcached cachev1alpha1.Memcached
	if err := r.Get(ctx, req.NamespacedName, &memcached); err != nil {
		log.Error(err, "unable to fetch Memcached")
		return ctrl.Result{}, client.IgnoreNotFound(err)
	}

	// 2. 현재 Memcached 인스턴스 수와 Size 비교하여 조정
	desiredSize := memcached.Spec.Size
	currentSize := memcached.Status.AvailableReplicas

	// 상태 비교 후 생성 및 삭제 처리
	if desiredSize > currentSize {
		// 인스턴스 생성 로직
		log.Info("Desired size is greater than current size, creating new instances")
		if err := r.createMemcachedInstances(ctx, &memcached, desiredSize-currentSize); err != nil {
			log.Error(err, "unable to create new Memcached instances")
			return ctrl.Result{}, err
		}
	} else if desiredSize < currentSize {
		// 인스턴스 삭제 로직
		log.Info("Desired size is less than current size, deleting extra instances")
		if err := r.deleteMemcachedInstances(ctx, &memcached, currentSize-desiredSize); err != nil {
			log.Error(err, "unable to delete extra Memcached instances")
			return ctrl.Result{}, err
		}
	} else {
		log.Info("Desired size matches current size, no changes needed")
	}

	return ctrl.Result{}, nil
}

// SetupWithManager sets up the controller with the Manager.
func (r *MemcachedReconciler) SetupWithManager(mgr ctrl.Manager) error {
	r.Log = ctrl.Log.WithName("controllers").WithName("Memcached") // 로깅 설정

	return ctrl.NewControllerManagedBy(mgr).
		For(&cachev1alpha1.Memcached{}).
		Complete(r)
}

// Memcached 인스턴스 생성
func (r *MemcachedReconciler) createMemcachedInstances(ctx context.Context, memcached *cachev1alpha1.Memcached, numInstances int32) error {
	log := log.FromContext(ctx)

	// StatefulSet 생성 로직
	// 실제 Memcached StatefulSet 생성하는 코드가 들어갑니다.
	for i := int32(0); i < numInstances; i++ {
		// StatefulSet을 생성하는 로직 작성
		log.Info(fmt.Sprintf("Creating Memcached instance %d", i+1))

		// 예시: StatefulSet을 생성하는 코드
		// 실제로는 여기서 clientset을 사용해 StatefulSet을 생성합니다.
	}

	return nil
}

// Memcached 인스턴스 삭제
func (r *MemcachedReconciler) deleteMemcachedInstances(ctx context.Context, memcached *cachev1alpha1.Memcached, numInstances int32) error {
	log := log.FromContext(ctx)

	// StatefulSet 삭제 로직
	for i := int32(0); i < numInstances; i++ {
		// StatefulSet 삭제하는 로직 작성
		log.Info(fmt.Sprintf("Deleting Memcached instance %d", i+1))

		// 예시: StatefulSet 삭제 코드
		// 실제로는 여기서 clientset을 사용해 StatefulSet을 삭제합니다.
	}

	return nil
}

 

 

 

make run 으로 Operator를 실행한다

[root@tech7 memcached-operator]# make run
/root/k8s/Operator/memcached-operator/bin/controller-gen-v0.14.0 rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases
/root/k8s/Operator/memcached-operator/bin/controller-gen-v0.14.0 object:headerFile="hack/boilerplate.go.txt" paths="./..."
go fmt ./...
internal/controller/memcached_controller.go
go vet ./...
go run ./cmd/main.go
2024-11-14T19:08:30+09:00	INFO	setup	starting manager
2024-11-14T19:08:30+09:00	INFO	controller-runtime.metrics	Starting metrics server
2024-11-14T19:08:30+09:00	INFO	starting server	{"kind": "health probe", "addr": "[::]:8081"}
2024-11-14T19:08:30+09:00	INFO	controller-runtime.metrics	Serving metrics server	{"bindAddress": ":8080", "secure": false}
2024-11-14T19:08:30+09:00	INFO	Starting EventSource	{"controller": "memcached", "controllerGroup": "cache.example.com", "controllerKind": "Memcached", "source": "kind source: *v1alpha1.Memcached"}
2024-11-14T19:08:30+09:00	INFO	Starting Controller	{"controller": "memcached", "controllerGroup": "cache.example.com", "controllerKind": "Memcached"}
2024-11-14T19:08:30+09:00	INFO	Starting workers	{"controller": "memcached", "controllerGroup": "cache.example.com", "controllerKind": "Memcached", "worker count": 1}

 

 

Memcached 리소스를 정의하는 파일 등록

경로 : memcached-operator/config/samples

파일 : cache_v1alpha1_memcached.yaml

[root@tech7 samples]# cat cache_v1alpha1_memcached.yaml
apiVersion: cache.example.com/v1alpha1
kind: Memcached
metadata:
  labels:
    app.kubernetes.io/name: memcached-operator
    app.kubernetes.io/managed-by: kustomize
  name: memcached-sample
spec:
  size : 2
  # TODO(user): Add fields here

 

k apply -f cache_v1alpha1_memcached.yaml

하면 reconcile 함수가 구동되어  아래와 같은 로그를 operator 에 남긴다

2024-11-14T19:14:01+09:00	INFO	Desired size is greater than current size, creating new instances	{"controller": "memcached", "controllerGroup": "cache.example.com", "controllerKind": "Memcached", "Memcached": {"name":"memcached-sample","namespace":"default"}, "namespace": "default", "name": "memcached-sample", "reconcileID": "efa97613-c682-4967-a8e0-2a6c37d54d3d"}
2024-11-14T19:14:01+09:00	INFO	Creating Memcached instance 1	{"controller": "memcached", "controllerGroup": "cache.example.com", "controllerKind": "Memcached", "Memcached": {"name":"memcached-sample","namespace":"default"}, "namespace": "default", "name": "memcached-sample", "reconcileID": "efa97613-c682-4967-a8e0-2a6c37d54d3d"}
2024-11-14T19:14:01+09:00	INFO	Creating Memcached instance 2	{"controller": "memcached", "controllerGroup": "cache.example.com", "controllerKind": "Memcached", "Memcached": {"name":"memcached-sample","namespace":"default"}, "namespace": "default", "name": "memcached-sample", "reconcileID": "efa97613-c682-4967-a8e0-2a6c37d54d3d"}