Golang에서 Kubernetes용 연산자 작성

메모. 번역: Operator는 특정 이벤트가 발생할 때 클러스터 개체에 대한 일상적인 작업 실행을 자동화하도록 설계된 Kubernetes용 보조 소프트웨어입니다. 우리는 이미 연산자에 대해 썼습니다. 이 기사, 그들은 작업의 기본 아이디어와 원칙에 대해 이야기했습니다. 그러나 해당 자료가 Kubernetes용 기성 구성 요소를 운영하는 측면에서 보는 관점에 가깝다면, 현재 제안된 새 기사의 번역은 이미 새로운 운영자의 구현으로 인해 당황한 개발자/DevOps 엔지니어의 비전입니다.

Golang에서 Kubernetes용 연산자 작성

Kubernetes용 연산자를 만드는 방법에 대한 문서를 찾다가 코드 연구를 거쳐 실제 사례를 사용하여 이 게시물을 작성하기로 결정했습니다.

설명할 예는 다음과 같습니다. Kubernetes 클러스터에서 각 Namespace 팀의 샌드박스 환경을 나타내며, 우리는 팀이 자신의 샌드박스에서만 플레이할 수 있도록 이에 대한 액세스를 제한하고 싶었습니다.

다음과 같은 그룹을 사용자에게 할당하여 원하는 것을 얻을 수 있습니다. RoleBinding 특정 Namespace и ClusterRole 편집 권한이 있습니다. YAML 표현은 다음과 같습니다.

---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: kubernetes-team-1
  namespace: team-1
subjects:
- kind: Group
  name: kubernetes-team-1
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: edit
apiGroup: rbac.authorization.k8s.io

(역할바인딩.yaml살갗이 벗어 진)

하나 만들기 RoleBinding 수동으로 수행할 수도 있지만 XNUMX개의 네임스페이스 표시를 넘은 후에는 지루한 작업이 됩니다. 여기에서 Kubernetes 운영자가 유용합니다. 이를 통해 리소스 변경 사항에 따라 Kubernetes 리소스 생성을 자동화할 수 있습니다. 우리의 경우 우리는 만들고 싶습니다 RoleBinding 만드는 동안 Namespace.

먼저 함수를 정의해보자. main명령문을 실행하는 데 필요한 설정을 수행한 다음 명령문 작업을 호출합니다.

(메모. 번역: 여기와 아래의 코드 주석은 러시아어로 번역됩니다. 또한 Habr 레이아웃 내에서 가독성을 높이기 위해 들여쓰기를 [Go에서 권장] 탭 대신 공백으로 수정했습니다. 각 목록 뒤에는 영어 댓글과 탭이 저장되어 있는 GitHub의 원본 링크가 있습니다.

func main() {
  // Устанавливаем вывод логов в консольный STDOUT
  log.SetOutput(os.Stdout)

  sigs := make(chan os.Signal, 1) // Создаем канал для получения сигналов ОС
  stop := make(chan struct{})     // Создаем канал для получения стоп-сигнала

  // Регистрируем получение SIGTERM в канале sigs
  signal.Notify(sigs, os.Interrupt, syscall.SIGTERM, syscall.SIGINT) 

  // Goroutines могут сами добавлять себя в WaitGroup,
 // чтобы завершения их выполнения дожидались
  wg := &sync.WaitGroup{} 

  runOutsideCluster := flag.Bool("run-outside-cluster", false, "Set this flag when running outside of the cluster.")
  flag.Parse()
  // Создаем clientset для взаимодействия с кластером Kubernetes
  clientset, err := newClientSet(*runOutsideCluster)

  if err != nil {
    panic(err.Error())
  }

  controller.NewNamespaceController(clientset).Run(stop, wg)

  <-sigs // Ждем сигналов (до получения сигнала более ничего не происходит)
  log.Printf("Shutting down...")

  close(stop) // Говорим goroutines остановиться
  wg.Wait()   // Ожидаем, что все остановлено
}

(메인.고살갗이 벗어 진)

우리는 다음을 수행합니다:

  1. 특정 운영 체제 신호에 대한 처리기를 구성하여 연산자를 정상적으로 종료합니다.
  2. 우리는 사용 WaitGroup애플리케이션을 종료하기 전에 모든 고루틴을 정상적으로 중지합니다.
  3. 우리는 다음을 생성하여 클러스터에 대한 액세스를 제공합니다. clientset.
  4. 시작하다 NamespaceController, 여기에 모든 논리가 위치합니다.

이제 우리는 논리의 기초가 필요하며, 우리의 경우에는 이것이 언급된 것입니다. NamespaceController:

// NamespaceController следит через Kubernetes API за изменениями
// в пространствах имен и создает RoleBinding для конкретного namespace.
type NamespaceController struct {
  namespaceInformer cache.SharedIndexInformer
  kclient           *kubernetes.Clientset
}

// NewNamespaceController создает новый NewNamespaceController
func NewNamespaceController(kclient *kubernetes.Clientset) *NamespaceController {
  namespaceWatcher := &NamespaceController{}

  // Создаем информер для слежения за Namespaces
  namespaceInformer := cache.NewSharedIndexInformer(
    &cache.ListWatch{
      ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
        return kclient.Core().Namespaces().List(options)
      },
      WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
        return kclient.Core().Namespaces().Watch(options)
      },
    },
    &v1.Namespace{},
    3*time.Minute,
    cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc},
  )

  namespaceInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
    AddFunc: namespaceWatcher.createRoleBinding,
  })

  namespaceWatcher.kclient = kclient
  namespaceWatcher.namespaceInformer = namespaceInformer

  return namespaceWatcher
}

(컨트롤러.이동살갗이 벗어 진)

여기서는 구성합니다. SharedIndexInformer, 이는 (캐시를 사용하여) 네임스페이스의 변경을 효과적으로 기다립니다. (“기사에서 정보 제공자에 대해 자세히 알아보십시오.Kubernetes 스케줄러는 실제로 어떻게 작동하나요?- 대략. 번역). 이 후에 우리는 연결합니다 EventHandler 네임스페이스를 추가할 때(Namespace) 함수가 호출됩니다. createRoleBinding.

다음 단계는 이 함수를 정의하는 것입니다. createRoleBinding:

func (c *NamespaceController) createRoleBinding(obj interface{}) {
  namespaceObj := obj.(*v1.Namespace)
  namespaceName := namespaceObj.Name

  roleBinding := &v1beta1.RoleBinding{
    TypeMeta: metav1.TypeMeta{
      Kind:       "RoleBinding",
      APIVersion: "rbac.authorization.k8s.io/v1beta1",
    },
    ObjectMeta: metav1.ObjectMeta{
      Name:      fmt.Sprintf("ad-kubernetes-%s", namespaceName),
      Namespace: namespaceName,
    },
    Subjects: []v1beta1.Subject{
      v1beta1.Subject{
        Kind: "Group",
        Name: fmt.Sprintf("ad-kubernetes-%s", namespaceName),
      },
    },
    RoleRef: v1beta1.RoleRef{
      APIGroup: "rbac.authorization.k8s.io",
        Kind:     "ClusterRole",
        Name:     "edit",
    },
  }

  _, err := c.kclient.Rbac().RoleBindings(namespaceName).Create(roleBinding)

  if err != nil {
    log.Println(fmt.Sprintf("Failed to create Role Binding: %s", err.Error()))
  } else {
    log.Println(fmt.Sprintf("Created AD RoleBinding for Namespace: %s", roleBinding.Name))
  }
}

(컨트롤러.이동살갗이 벗어 진)

우리는 네임스페이스를 다음과 같이 얻습니다. obj 그리고 그것을 객체로 변환 Namespace. 그런 다음 우리는 정의합니다 RoleBinding, 처음에 언급한 YAML 파일을 기반으로 제공된 객체를 사용하여 Namespace 그리고 창조 RoleBinding. 마지막으로 생성이 성공했는지 기록합니다.

마지막으로 정의할 함수는 다음과 같습니다. Run:

// Run запускает процесс ожидания изменений в пространствах имён
// и действия в соответствии с этими изменениями.
func (c *NamespaceController) Run(stopCh <-chan struct{}, wg *sync.WaitGroup) {
  // Когда эта функция завершена, пометим как выполненную
  defer wg.Done()

  // Инкрементируем wait group, т.к. собираемся вызвать goroutine
  wg.Add(1)

  // Вызываем goroutine
  go c.namespaceInformer.Run(stopCh)

  // Ожидаем получения стоп-сигнала
  <-stopCh
}

(컨트롤러.이동살갗이 벗어 진)

여기서 우리는 이야기하고 있습니다 WaitGroup고루틴을 시작한 다음 호출합니다. namespaceInformer, 이는 이전에 정의된 것입니다. 정지 신호가 도착하면 기능이 종료되고 이를 알려줍니다. WaitGroup는 더 이상 실행되지 않으며 이 함수는 종료됩니다.

Kubernetes 클러스터에서 이 명령문을 빌드하고 실행하는 방법에 대한 정보는 다음에서 찾을 수 있습니다. GitHub의 리포지토리.

그게 바로 운영자를 위한 것입니다. RoleBinding 언제 Namespace Kubernetes 클러스터에서 준비되었습니다.

출처 : habr.com

코멘트를 추가