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 オペレーターを使用すると、リソースへの変更に基づいて 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
}

(コントローラー.go)

ここで設定します 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))
  }
}

(コントローラー.go)

名前空間を次のように取得します。 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
}

(コントローラー.go)

ここで話しています WaitGroupgoroutine を起動してから呼び出すこと namespaceInformer、これは以前に定義されています。 停止信号が到着すると機能を終了し、通知します。 WaitGroup、実行されなくなり、この関数は終了します。

Kubernetes クラスター上でのこのステートメントの構築と実行に関する情報は、次の場所にあります。 GitHub 上のリポジトリ.

作成するオペレーターはこれで終わりです RoleBinding いつ Namespace Kubernetes クラスター内で準備完了です。

出所: habr.com

コメントを追加します