CRDマニフェストの生成

コントローラーでカスタムリソースを扱うためには、そのリソースのCRD(Custom Resource Definition)を定義する必要があります。 CRDのマニフェストは複雑で、手書きで作成するにはかなりの手間がかかります。

そこでKubebuilderではcontroller-genというツールを提供しており、Goで記述したstructからCRDを生成できます。

まずはkubebuilder create apiコマンドで生成されたmarkdownview_types.goを見てみましょう。

markdownview_types.go

/*
Copyright 2023.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package v1

import (
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// EDIT THIS FILE!  THIS IS SCAFFOLDING FOR YOU TO OWN!
// NOTE: json tags are required.  Any new fields you add must have json tags for the fields to be serialized.

// MarkdownViewSpec defines the desired state of MarkdownView
type MarkdownViewSpec 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 MarkdownView. Edit markdownview_types.go to remove/update
    Foo string `json:"foo,omitempty"`
}

// MarkdownViewStatus defines the observed state of MarkdownView
type MarkdownViewStatus struct {
    // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
    // Important: Run "make" to regenerate code after modifying this file
}

//+kubebuilder:object:root=true
//+kubebuilder:subresource:status

// MarkdownView is the Schema for the markdownviews API
type MarkdownView struct {
    metav1.TypeMeta   `json:",inline"`
    metav1.ObjectMeta `json:"metadata,omitempty"`

    Spec   MarkdownViewSpec   `json:"spec,omitempty"`
    Status MarkdownViewStatus `json:"status,omitempty"`
}

//+kubebuilder:object:root=true

// MarkdownViewList contains a list of MarkdownView
type MarkdownViewList struct {
    metav1.TypeMeta `json:",inline"`
    metav1.ListMeta `json:"metadata,omitempty"`
    Items           []MarkdownView `json:"items"`
}

func init() {
    SchemeBuilder.Register(&MarkdownView{}, &MarkdownViewList{})
}

MarkdownViewSpec, MarkdownViewStatus, MarkdownView, MarkdownViewListという構造体が定義されており、//+kubebuilder:から始まるマーカーコメントがいくつか付与されています。 controller-genは、これらの構造体とマーカーを頼りにCRDの生成をおこないます。

MarkdownViewがカスタムリソースの本体となる構造体です。MarkdownViewListMarkdownViewのリストを表す構造体です。これら2つの構造体は基本的に変更することはありません。 MarkdownViewSpecMarkdownViewStatusMarkdownView構造体を構成する要素です。この2つの構造体を書き換えてカスタムリソースを定義していきます。

一般的にカスタムリソースのSpecはユーザーが記述するもので、システムのあるべき状態をユーザーからコントローラーに伝える用途として利用されます。 一方のStatusは、コントローラーが処理した結果をユーザーや他のシステムに伝える用途として利用されます。

MarkdownViewSpec

さっそくMarkdownViewSpecを書き換えていきましょう。

作成するカスタムコントローラにおいて、MarkdownViewコントローラーが扱うカスタムリソースとして下記のようなマニフェストを例示しました。

view_v1_markdownview.yaml

apiVersion: view.zoetrope.github.io/v1
kind: MarkdownView
metadata:
  labels:
    app.kubernetes.io/name: markdownview
    app.kubernetes.io/instance: markdownview-sample
    app.kubernetes.io/part-of: markdown-view
    app.kubernetes.io/managed-by: kustomize
    app.kubernetes.io/created-by: markdown-view
  name: markdownview-sample
spec:
  markdowns:
    SUMMARY.md: |
      # Summary

      - [Page1](page1.md)
    page1.md: |
      # Page 1

      一ページ目のコンテンツです。
  replicas: 1
  viewerImage: "peaceiris/mdbook:latest"

上記のマニフェストを扱うための構造体を用意しましょう。

markdownview_types.go

// MarkdownViewSpec defines the desired state of MarkdownView
type MarkdownViewSpec struct {
    // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
    // Important: Run "make" to regenerate code after modifying this file

    // Markdowns contain the markdown files you want to display.
    // The key indicates the file name and must not overlap with the keys.
    // The value is the content in markdown format.
    //+kubebuilder:validation:Required
    //+kubebuilder:validation:MinProperties=1
    Markdowns map[string]string `json:"markdowns,omitempty"`

    // Replicas is the number of viewers.
    // +kubebuilder:default=1
    // +optional
    Replicas int32 `json:"replicas,omitempty"`

    // ViewerImage is the image name of the viewer.
    // +optional
    ViewerImage string `json:"viewerImage,omitempty"`
}

まず下記の3つのフィールドを定義します。

  • Markdowns: 表示したいマークダウンファイルの一覧
  • Replicas: Viewerのレプリカ数
  • ViewerImage: Markdownの表示に利用するViewerのイメージ名

各フィールドの上に// +kubebuilderという文字列から始まるマーカーと呼ばれるコメントが記述されています。 これらのマーカーによって、生成されるCRDの内容を制御できます。

付与できるマーカーはcontroller-gen crd -wコマンドで確認できます。

Required/Optional

Markdownsフィールドには+kubebuiler:validation:Requiredマーカーが付与されています。 これはこのフィールドが必須項目であることを示しており、ユーザーがマニフェストを記述する際にこの項目を省略できません。 一方のReplicasViewerImageには+optionalが付与されており、この項目が省略可能であることを示しています。

マーカーを指定しなかった場合はデフォルトでRequiredなフィールドになります。

なお、ファイル内に下記のマーカーを配置すると、デフォルトの挙動をOptionalに変更できます。

// +kubebuilder:validation:Optional

+optionalマーカーを付与しなくても、フィールドの後ろのJSONタグにomitemptyを付与した場合は、自動的にOptionalなフィールドとなります。

type SampleSpec struct {
    Value string `json:"value,omitempty"`
}

Optionalなフィールドは、以下のようにフィールドの型をポインタにできます。 これによりマニフェストで値を指定しなかった場合の挙動が異なります。 ポインタ型にした場合はnullが入り、実体にした場合はその型の初期値(intの場合は0)が入ります。

type SampleSpec struct {
    // +optional
    Value1 int  `json:"value1"`
    // +optional
    Value2 *int `json:"value2"`
}

Validation

KubebuilderにはRequired以外にも様々なバリデーションが用意されています。 詳しくはcontroller-gen crd -wコマンドで確認してください。

  • リストの最小要素数、最大要素数
  • 文字列の最小長、最大長
  • 数値の最小値、最大値
  • 正規表現にマッチするかどうか
  • リスト内の要素がユニークかどうか

MarkdownViewStatus

次にMarkdownViewリソースの状態を表現するためのMarkdownViewStatusを書き換えます。

markdownview_types.go

// MarkdownViewStatus defines the observed state of MarkdownView
// +kubebuilder:validation:Enum=NotReady;Available;Healthy
type MarkdownViewStatus string

const (
    MarkdownViewNotReady  = MarkdownViewStatus("NotReady")
    MarkdownViewAvailable = MarkdownViewStatus("Available")
    MarkdownViewHealthy   = MarkdownViewStatus("Healthy")
)

今回のカスタムコントローラーでは、MarkdownViewStatusを文字列型とし、NotReady,Available,Healtyの3つの状態をあらわすようにしました。

//+kubebuilder:validation:Enumを利用すると、指定した文字列以外の値を設定できないようになります。

MarkdownView

続いてMarkdownView構造体のマーカーを見てみましょう。

markdownview_types.go

//+kubebuilder:object:root=true
//+kubebuilder:subresource:status
//+kubebuilder:printcolumn:name="REPLICAS",type="integer",JSONPath=".spec.replicas"
//+kubebuilder:printcolumn:name="STATUS",type="string",JSONPath=".status"

// MarkdownView is the Schema for the markdownviews API
type MarkdownView struct {
    metav1.TypeMeta   `json:",inline"`
    metav1.ObjectMeta `json:"metadata,omitempty"`

    Spec   MarkdownViewSpec   `json:"spec,omitempty"`
    Status MarkdownViewStatus `json:"status,omitempty"`
}

Kubebuilderが生成した初期状態では、+kubebuilder:object:root=true+kubebuilder:subresourceの2つのマーカーが指定されています。 ここではさらに+kubebuilder:printcolumnを追加することとします。

+kubebuilder:object:root=trueは、MarkdownView構造体がカスタムリソースのrootオブジェクトであることを表すマーカーです。

+kubebuilder:subresource+kubebuilder:printcolumnマーカーについて、以降で解説します。

subresource

+kubebuilder:subresource:statusというマーカーを追加すると、statusフィールドがサブリソースとして扱われるようになります。

Kubernetesでは、すべてのリソースはそれぞれ独立したAPIエンドポイントを持っており、APIサーバー経由でリソースの取得・作成・変更・削除をおこなうことができます。

サブリソースを有効にするとstatusフィールドがメインのリソースと独立したAPIエンドポイントを持つようになります。

これによりメインのリソース全体を取得・更新しなくても、statusのみの取得や更新が可能になります。 ただし、あくまでもメインのリソースに属するサブのリソースなので、個別の作成や削除はできません。

ユーザーがspecフィールドを記述し、コントローラーがstatusフィールドを記述するという役割分担を明確にできるので、基本的にはstatusはサブリソースにしておくのがよいでしょう。 なおKubebuilder v3では、statusフィールドがサブリソースに指定するマーカーが最初から指定されるようになりました。

CRDでは任意のフィールドをサブリソースにはできず、statusscaleの2つのフィールドのみに対応しています。

printcolumn

+kubebuilder:printcolumnマーカーを付与すると、kubectlでカスタムリソースを取得したときに表示する項目を指定できます。

表示対象のフィールドはJSONPathで指定可能です。 例えば、JSONPath=".spec.replicas"と記述すると、kubectl getしたときに.spec.replicasの値が表示されます。

kubectlでMarkdownViewリソースを取得すると、下記のようにREPLICASやSTATUSの値が表示されていることが確認できます。

$ kubectl get markdownview
NAME                  REPLICAS   STATUS
MarkdownView-sample   1          healthy

CRDマニフェストの生成

最後にGoで記述したstructからCRDを生成してみましょう。

以下のコマンドを実行してください。

$ make manifests

すると、以下のようなCRDのマニフェストファイルが生成されます。

view.zoetrope.github.io_markdownviews.yaml

---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  annotations:
    controller-gen.kubebuilder.io/version: v0.12.0
  name: markdownviews.view.zoetrope.github.io
spec:
  group: view.zoetrope.github.io
  names:
    kind: MarkdownView
    listKind: MarkdownViewList
    plural: markdownviews
    singular: markdownview
  scope: Namespaced
  versions:
  - additionalPrinterColumns:
    - jsonPath: .spec.replicas
      name: REPLICAS
      type: integer
    - jsonPath: .status
      name: STATUS
      type: string
    name: v1
    schema:
      openAPIV3Schema:
        description: MarkdownView is the Schema for the markdownviews API
        properties:
          apiVersion:
            description: 'APIVersion defines the versioned schema of this representation
              of an object. Servers should convert recognized schemas to the latest
              internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
            type: string
          kind:
            description: 'Kind is a string value representing the REST resource this
              object represents. Servers may infer this from the endpoint the client
              submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
            type: string
          metadata:
            type: object
          spec:
            description: MarkdownViewSpec defines the desired state of MarkdownView
            properties:
              markdowns:
                additionalProperties:
                  type: string
                description: Markdowns contain the markdown files you want to display.
                  The key indicates the file name and must not overlap with the keys.
                  The value is the content in markdown format.
                minProperties: 1
                type: object
              replicas:
                default: 1
                description: Replicas is the number of viewers.
                format: int32
                type: integer
              viewerImage:
                description: ViewerImage is the image name of the viewer.
                type: string
            type: object
          status:
            description: MarkdownViewStatus defines the observed state of MarkdownView
            enum:
            - NotReady
            - Available
            - Healthy
            type: string
        type: object
    served: true
    storage: true
    subresources:
      status: {}

results matching ""

    No results matching ""