連載: Kubernetesでカスタムコントローラを作ろう! ~第4回 Kubernetesのカスタムリソースについて~
この記事は1年以上前に投稿されました。情報が古い可能性がありますので、ご注意ください。
前回はネイティブなKubernetesリソースをAPIを通じて操作しました。 KubernetesにはAPIを拡張する仕組みがあり、その一つとしてカスタムリソースがあります。 カスタムリソースはCustomResourceDefinitionリソースを通じて定義できます。 定義されたカスタムリソースは、通常のKubernetesのDeploymentやStatefulSetなどと同じようなリソースとしてユーザが利用できます。 今回は、このカスタムリソースに触れていきます。
カスタムリソース
カスタムリソースはCustomResourceDefinitionリソースにより定義できます。 作成されたカスタムリソースに対しては、前回までで実施したようなAPIによる操作が可能となります。 このCustomResourceDefinitionは、kubebuilderで利用されているcontroller-toolsを利用すると、Goのコードから自動生成できるため、そういったツールを利用している場合、ゼロから記述する機会は少ないかもしれません。 しかし、そこで自動生成されているものがどういうものかを理解しておくことは重要です。
以下はCustomResourceDefinitionの例です。 ここではPodを簡易化したSimplePodリソースを定義した例です。 SimplePodはたった2つだけのシンプルなフィールドを持ちます。.spec.imageフィールドにコンテナのイメージ名、.spec.nameにコンテナ名を定義するものとしました。
# crd.yaml
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: simplepods.example.creationline.com
spec:
group: example.creationline.com
scope: Namespaced
names:
plural: simplepods
singular: simplepod
kind: SimplePod
versions:
- name: v1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
image:
type: string
description: container image name
name:
type: string
description: container name
.spec.groupはこのカスタムリソースで利用するグループを指定します。 Deploymentが所属しているappsグループやCronJobが所属しているbatchグループのようなものです。 .names.pluralや.names.singularはリソースの複数形・単数形を指定します。 .names.kindはDeploymentやPod, StatefulSetのようなKindを指定し、ここではSimplePodです。 .spec.versions内にそのリソースのバージョン毎の定義を記述します。 定義方法の詳細が気になる方は、公式ドキュメントのExtend the Kubernetes API with CustomResourceDefinitionsやAPIリファレンスをご参照ください。
上記のYAMLをcrd.yamlというファイル名に保存して実際にクラスタに適用し、クラスタでSimplePodが利用可能となっていることを確認します。
$ kubectl apply -f crd.yaml
customresourcedefinition.apiextensions.k8s.io/simplepods.example.creationline.com created
# simplepodのフィールドを確認します。
$ kubectl explain simplepod
KIND: SimplePod
VERSION: example.creationline.com/v1
DESCRIPTION:
<empty>
FIELDS:
apiVersion <string>
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
kind <string>
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
metadata <Object>
Standard object's metadata. More info:
https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
spec <Object>
# spec内のフィールドを確認します。
$ kubectl explain simplepod.spec
KIND: SimplePod
VERSION: example.creationline.com/v1
RESOURCE: spec <Object>
DESCRIPTION:
<empty>
FIELDS:
image <string>
container image name
name <string>
container name
クラスタにSimplePodが定義されているようです。 では実際にSimplePodオブジェクトを作成してみましょう。 以下のマニフェストをクラスタ適用しpod1という名前のSimplePodを作成してください。
apiVersion: example.creationline.com/v1
kind: SimplePod
metadata:
name: pod1
namespace: default
spec:
name: nginx
image: nginx
実際に作成されているか確認します。
$ kubectl apply -f pod1.yaml
simplepod.example.creationline.com/pod1 created
$ kubectl get simplepod
NAME AGE
pod1 17s
$ kubectl get simplepod pod1 -oyaml
apiVersion: example.creationline.com/v1
kind: SimplePod
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"example.creationline.com/v1","kind":"SimplePod","metadata":{"annotations":{},"name":"pod1","namespace":"default"},"spec":{"image":"nginx","name":"nginx"}}
creationTimestamp: "2023-01-15T16:43:35Z"
generation: 1
name: pod1
namespace: default
resourceVersion: "38333"
uid: ab155898-5cf3-4b36-9d5d-3a7540109e9b
spec:
image: nginx
name: nginx
マニフェストで定義した内容がきちんと反映されています:tada:
フィールドのValidation
CustomResourceDefinitionではOpenAPIV3Schemaを用いて、簡単なフィールドのValidationであれば、この定義内で実施できます。 例えば先ほどのSimplePodの例ではnameとimageフィールドは必須で、かつnameはfoo-から始まらなければならないとします。 このValidationを定義します。
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: simplepods.example.creationline.com
spec:
group: example.creationline.com
scope: Namespaced
names:
plural: simplepods
singular: simplepod
kind: SimplePod
versions:
- name: v1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
required: # 必須フィールドの指定
- image
- name
properties:
image:
type: string
description: container image name
name:
type: string
pattern: ^foo- # 許容する値の指定
description: container name
上記をkubectl applyで適用すると、下記のようにnameとimageフィールドがrequiredとなっていることがわかります。
$ kubectl explain simplepod.spec
KIND: SimplePod
VERSION: example.creationline.com/v1
RESOURCE: spec <Object>
DESCRIPTION:
<empty>
FIELDS:
image <string> -required-
container image name
name <string> -required-
container name
# spec.nameがfoo- から始まらないリソースは作成できない
$ cat <<EOF | kubectl apply -f-
apiVersion: example.creationline.com/v1
kind: SimplePod
metadata:
name: pod2
namespace: default
spec:
name: nginx
image: nginx
EOF
The SimplePod "pod2" is invalid: spec.name: Invalid value: "nginx": spec.name in body should match '^foo-'
# spec.nameがfoo- から始まらないリソースは作成できる
$ cat <<EOF | kubectl apply -f-
apiVersion: example.creationline.com/v1
kind: SimplePod
metadata:
name: pod3
namespace: default
spec:
name: foo-nginx
image: nginx
EOF
simplepod.example.creationline.com/pod3 created
Kubernetes v1.23以降であればCEL(Common Expression Language)を用いたValidationも可能です。 こちらはv1.25からはデフォルト有効化されていますが、それ以前のバージョンではCustomResourceValidationExpressions FeatureGatesを有効化する必要があります。 詳細はこちらをご参照ください。
それ以外にカスタムリソースをValidationする方法としてはAdmission WebhookやKubernetes v1.26以降であれば、Validating Admission PolicyによるValidationも実装可能です。
カスタムリソースをcurlで操作する
冒頭に軽く記載しましたが、カスタムリソースもKubernetesネイティブのリソースと同じようにAPIを用いて操作可能です。 今回作成したSimplePodは.spec.scopeがNamespacedとして作成しており、この場合ネームスペース毎にSimplePodオブジェクトが作成されます。 先ほどの作成したpod1はdefaultネームスペースに作成しており、このリソースをcurlを用いてGETしましょう。 前回と同様に、curlを用いて操作してみましょう。
$ kubectl proxy
上記コマンドを実行したまま別のターミナルで以下のコマンドを実行し、ネイティブリソースと同様にAPIへアクセスできることを確認します。 ネイティブリソースとアクセスするパスのルールは変わからず/apis/GROUP名/VESRION/namespaces/NAMESPACE名でアクセスできます。
$ curl http://localhost:8001/apis/example.creationline.com/v1/namespaces/default/simplepods/pod1
{
"apiVersion": "example.creationline.com/v1",
"kind": "SimplePod",
"metadata": {
"annotations": {
"kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"example.creationline.com/v1\",\"kind\":\"SimplePod\",\"metadata\":{\"annotations\":{},\"name\":\"pod1\",\"namespace\":\"default\"},\"spec\":{\"image\":\"nginx\",\"name\":\"nginx\"}}\n"
},
"creationTimestamp": "2023-01-15T16:45:10Z",
"generation": 1,
"managedFields": [
{
"apiVersion": "example.creationline.com/v1",
"fieldsType": "FieldsV1",
"fieldsV1": {
"f:metadata": {
"f:annotations": {
".": {},
"f:kubectl.kubernetes.io/last-applied-configuration": {}
}
},
"f:spec": {
".": {},
"f:image": {},
"f:name": {}
}
},
"manager": "kubectl-client-side-apply",
"operation": "Update",
"time": "2023-01-15T16:45:10Z"
}
],
"name": "pod1",
"namespace": "default",
"resourceVersion": "38449",
"uid": "4d7e8d06-b0eb-459b-8d38-64ef83e6a20b"
},
"spec": {
"image": "nginx",
"name": "nginx"
}
}
ネイティブリソースと同様にGETができました!
最後に
CustomResourceDifinitionを用いてカスタムリソースを作成する方法を学びました。 そしてカスタムリソースもネイティブのリソースと同様に操作できることも学びました。 今回は直接CustomResourceDifinitionを記述しましたが、kubebuilder・controller-toolsを利用すると、これらの定義は自動生成できます。 これらを使ったカスタムコントローラについては今後説明を進めていきます。
クリエーションラインではKubernetesに関する様々なトレーニングを提供しております。 詳しくはこちらをご参照ください。
また、この連載の更新通知を受け取りたい方は、Twitterアカウントのフォローや、このページ下部にリンクのあるCL LAB MAIL MAGAZINEへの登録をぜひお願いします!