fbpx

連載: 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 CustomResourceDefinitionsAPIリファレンスをご参照ください。

上記の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の例ではnameimageフィールドは必須で、かつnamefoo-から始まらなければならないとします。 この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で適用すると、下記のようにnameimageフィールドが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.scopeNamespacedとして作成しており、この場合ネームスペース毎にSimplePodオブジェクトが作成されます。 先ほどの作成したpod1defaultネームスペースに作成しており、このリソースを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への登録をぜひお願いします!

新規CTA