CRDマニフェストの生成
コントローラーでカスタムリソースを扱うためには、そのリソースのCRD(Custom Resource Definition)を定義する必要があります。 CRDのマニフェストは複雑で、手書きで作成するにはかなりの手間がかかります。
そこでKubebuilderではcontroller-genというツールを提供しており、Goで記述したstructからCRDを生成できます。
まずはkubebuilder create api
コマンドで生成された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
がカスタムリソースの本体となる構造体です。MarkdownViewList
はMarkdownView
のリストを表す構造体です。これら2つの構造体は基本的に変更することはありません。
MarkdownViewSpec
とMarkdownViewStatus
はMarkdownView
構造体を構成する要素です。この2つの構造体を書き換えてカスタムリソースを定義していきます。
一般的にカスタムリソースのSpec
はユーザーが記述するもので、システムのあるべき状態をユーザーからコントローラーに伝える用途として利用されます。
一方のStatus
は、コントローラーが処理した結果をユーザーや他のシステムに伝える用途として利用されます。
MarkdownViewSpec
さっそくMarkdownViewSpec
を書き換えていきましょう。
作成するカスタムコントローラにおいて、MarkdownViewコントローラーが扱うカスタムリソースとして下記のようなマニフェストを例示しました。
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"
上記のマニフェストを扱うための構造体を用意しましょう。
// 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
マーカーが付与されています。
これはこのフィールドが必須項目であることを示しており、ユーザーがマニフェストを記述する際にこの項目を省略できません。
一方のReplicas
とViewerImage
には+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
を書き換えます。
// 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
構造体のマーカーを見てみましょう。
//+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では任意のフィールドをサブリソースにはできず、status
とscale
の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のマニフェストファイルが生成されます。
---
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: {}