본문 바로가기
golang

[golang] kubernetes Operator 만들기 Kubebuilder Controller-runtime 3 - Manager

by weq155 2023. 11. 14.
반응형

[golang] kubernetes Operator 만들기 Controller-runtime 1 - Controller

[golang] kubernetes Operator 만들기 Controller-runtime 2 - Overview

[golang] kubernetes Operator 만들기 Controller-runtime 3 - Manager

[golang] kubernetes Operator 만들기 Controller-runtime 4 - Builder

Overview

이번에는 controller-runtime에 대해 딥다이브 해볼 것이다.

controller-runtime에 중요한 부분인 Manager component에 대해 집중해서 진행해보고자 한다.

만약 이전 글을 읽지 않았다면 이전 글부터 보는 것이 이해에 도움 될 것이다.

 

Manager란 뭘까?

Package manager는 controller를 생성하는데 필요하며,

client나 cash, schemes와 같은 디펜던시를 제공한다.

또한 Controller는 반드시 Manager.Start로 시작해야한다.

 

다시 말해서 Manager의 주요 역할은 controller를 구성하고 라이프 사이클을 관리한다.

여기서 컨트롤러 관리에 의미에는 세 가지 측면이 있다.

  1. Controller의 라이프사이클을 관리하는 것은 컨트롤러의 시작과 중단 등록 등을 포함한다.
  2. Kubernetes API 서버나 클라이언트, 캐시, 이벤트 로더 등과 같이 controller에서 사용하는 리소스들에 대한 접근을 제공한다.
  3. leader election이라고 불리는 Controller의 고가용성을 관리한다.

 

Manager는 인터페이스이다.

// Manager initializes shared dependencies such as Caches and Clients, and provides them to Runnables.
// A Manager is required to create Controllers.
type Manager interface {
 cluster.Cluster
 Add(Runnable) error
 Elected() <-chan struct{}
 AddMetricsExtraHandler(path string, handler http.Handler) error
 AddHealthzCheck(name string, check healthz.Checker) error
 AddReadyzCheck(name string, check healthz.Checker) error
 Start(ctx context.Context) error
 GetWebhookServer() *webhook.Server
 GetLogger() logr.Logger
 GetControllerOptions() v1alpha1.ControllerConfigurationSpec
}

 

이 코드에서 중요하게 볼 부분은 cluster, Add(Runnable)과 Start(ctx context.Context) error 부분이다.

 

Manager를 어떻게 사용할까?

우리는 첫 번째 글에서 Manager를 NewManager를 통해 초기화하는 것을 진행했었다.

manager, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{})

 

그리고 마지막으로 Manager는 등록된 모든 controller를 실행한다.

manager.Start(ctrl.SetupSignalHandler())

 

그럼 Manager안에서 어떤 일이 일어나는지 살펴보자

 

 

NewManager

New function는 cluster라는 공유될 리소스(kubernetes API 서버, 캐시 등 이전글에서 다룸)를 구성하는

다른 컴포넌트를 구성한다.

 

그다음 New function은 Manager로 작동하는 instance인 controllerManager를 초기화한다.

manager/internal.go에서 controllerManager를 선언한 코드

var _ Runnable = &controllerManager{}

type controllerManager struct {
 sync.Mutex
 started bool

 ...

 runnables            *runnables

 // cluster holds a variety of methods to interact with a cluster. Required.
 cluster cluster.Cluster

 ...
}

 

전체 코드 중에 중요 필드

  1. runnables : *runnables는 등록된 controller set을 저장한다.
  2. cluster : cluster는 공유된 메서드들과 상호작용을 제공한다. (client, cash 등)
  3. leader election 관련 필드
  4. HA 구성

다음은 runnables에 대해 정확하게 알아보자

// runnables handles all the runnables for a manager by grouping them accordingly to their
// type (webhooks, caches etc.).
type runnables struct {
 Webhooks       *runnableGroup
 Caches         *runnableGroup
 LeaderElection *runnableGroup
 Others         *runnableGroup
}

 

runnables는 네 가지 유형의 runnableGroup을 구성하는 struct이다.

runnablescontroller manager에 등록될 수 있는 여러 유형의 컴포넌트를 그룹화한다.

runnableGroup은 같은 유형의 컴포넌트 목록을 구성하고 추가, 삭제 실행 등을 제공한다.

다른 그룹을 사용해 컴포넌트의 실행 순서를 확인하고 다른 유형의 컴포넌트를 개별적으로 관리한다.

  1. WebHook : 컨트롤러에 정의된 웹훅서버의 그룹
  2. Caches : cluster가 이 그룹에 추가되며 GetCache() 메서드로 실행 가능한 그룹
  3. LeaderElection : NeedLeaderElection() 메소드로 실행 가능한 그룹
  4. Others : Controller를 등록할 때 컨트롤러는 이 그룹에 추가됨

Add function을 이용한 runnables 구성

func (r *runnables) Add(fn Runnable) error {
 switch runnable := fn.(type) {
 case hasCache:
  return r.Caches.Add(fn, func(ctx context.Context) bool {
   return runnable.GetCache().WaitForCacheSync(ctx)
  })
 case *webhook.Server:
  return r.Webhooks.Add(fn, nil)
 case LeaderElectionRunnable:
  if !runnable.NeedLeaderElection() {
   return r.Others.Add(fn, nil)
  }
  return r.LeaderElection.Add(fn, nil)
 default:
  return r.LeaderElection.Add(fn, nil)
 }
}

 

 

Add function은 Runnables를 가져와 Runnables의 유형에 따라 해당 그룹에 추가합니다.

hasCache와 LeaderElectionRunnable 인터페이스는 컴포넌트를 각각 cashes 나 LeaderElection 그룹에 추가할지를

결정하는 역할이다,

controllerManager가 직접 다른 Runnable 인터페이스를 구현하기도 한다.

var _ Runnable = &controllerManager{}

 

 

Runnable은 아주 간단한 인터페이스이며, Context만을 인수로 갖는 Start function을 가진다.

실행 or 에러 반환

// Runnable allows a component to be started.
// It's very important that Start blocks until
// it's done running.
type Runnable interface {
 // Start starts running the component.  The component will stop running
 // when the context is closed. Start blocks until the context is closed or
 // an error occurs.
 Start(context.Context) error
}

 

 

NewControllerManagedBy

builder 패키지 안에 있는 NewControllerManagedBy 함수는 여기서 확인할 수 있다.

https://pkg.go.dev/sigs.k8s.io/controller-runtime#pkg-variables

// NewControllerManagedBy returns a new controller builder that will be started by the provided Manager.
 NewControllerManagedBy = builder.ControllerManagedBy

 

다음에 builder를 조사한 글에서 다시 다뤄 보도록 하고 빌더가 어떻게 구성되는지를 보자

 

 

Builder

// Builder builds a Controller.
type Builder struct {
 forInput         ForInput
 ownsInput        []OwnsInput
 watchesInput     []WatchesInput
 mgr              manager.Manager
 globalPredicates []predicate.Predicate
 ctrl             controller.Controller
 ctrlOptions      controller.Options
 name             string
}

 

 

ContrllerManagedBy는 mgr이라는 특정한 manager 필드로 구성됨

// ControllerManagedBy returns a new controller builder that will be started by the provided Manager.
func ControllerManagedBy(m manager.Manager) *Builder {
 return &Builder{mgr: m}
}

 

 

Complete function에서는 Build function을 호출하고 내부적으로 doController와 doWatch를 호출함

doController에서는 관리자의 공유 리소스를 사용하여 컨트롤러를 초기화하고 doWatch는 컨트롤러를 시작한다.

NewControllerManagedBy는 각 컨트롤러가 Manager에 컨트롤러를 등록할 때 사용함

 

 

Start()

마지막으로 우리는 manager를 start 한다. Manager 인터페이스는 Start(context.Context) error 구문을

무조건 가지고 있어야 한다.

type Manager interface {
  ...
  Start(ctx context.Context) error
  ..
}

 

 

 

controllerManager는 구현된 Manager 인터페이스이다.

Start() function은 주로 아래의 과정을 따른다.

  1. runnablescluster를 추가 (cluster 또한 runnables.Cache에 추가됨)
  2. Start runnables.Webhooks, runnables.Caches, runnables.Others, and runnables.LeaderElection.
// (1) Add the cluster runnable.
if err := cm.add(cm.cluster); err != nil {
...

// (2) First start any webhook servers
if err := cm.runnables.Webhooks.Start(cm.internalCtx); err != nil {
...

// (3) Start and wait for caches.
if err := cm.runnables.Caches.Start(cm.internalCtx); err != nil {
...

// (4) Start the non-leaderelection Runnables after the cache has synced.
if err := cm.runnables.Others.Start(cm.internalCtx); err != nil {

// (5) Start the leader election and all required runnables.
if err := cm.startLeaderElection(ctx); err != nil {
...
if err := cm.startLeaderElectionRunnables(); err != nil {
...

 

Start()는 manager에 등록된 모든 runnables를 시작한다.

 

Summary

  1. Manager는 controller의 등록, 시작, 중단을 구성하고 수명주기를 관리함
  2. controllerManager는 Manager 인터페이스의 구현체이며, cluster와 runnables를 가진다.
  3. cluster는 Kubernetes API server, cache, schema과 같은 공유 리소스를 가지며 Manager.New function으로 초기화된다.
  4. runnablesWebhooks, Caches, LeaderElection, and Others 네 가지 유형으로 구성됨
  5. Controller에 Manager를 바인딩하려면 builder가 필요하다.
  6. NewControllerManagedBy는 Manager를 builder에 구성하는 데 사용되며, 빌드 시 Manager에게 새로운 controller가 등록됨 (new controller는 runnables.Others에 등록됨)

 

 

 

 

Reperence

* 본 글은  Masato Naka의 medium 게시글을 참조 & 번역함

https://nakamasato.medium.com/kubernetes-operator-series-2-overview-of-controller-runtime-f8454522a539

 

Kubernetes Operator series 2 — Overview of controller-runtime

Overview of Kubernetes Operator with controller-runtime

nakamasato.medium.com

반응형
반응형