k8s のカスタムリソースを理解する

Apr 18, 2021 18:37 · 271 words · 2 minute read

k8s では、pod、service、deployment、config map … といった「リソース」のあるべき状態をコントロールプレーンの API を通じて管理する形になります。

そして、これらのリソース定義をもとに、「コントローラー」が実際のリソース状態を管理します。 より具体的には、コントローラーはコントロールプレーンの API を通じてリソースのあるべき状態を同期します。

そして、あるべき状態と実際の状態に乖離がある場合には、あるべき状態になるようにリソースに変更を加えます。

※ 厳密には、controller が直接リソースを変更するのではなくコントロールプレーンの API を経由して kubelet に処理させる場合もあります。
※ また、 control plane と controller を区別していますが、Deployment や Job などのビルトインコントローラーは、control plane の一部とみなされます。

k8s では、リソースおよびコントローラーを追加できる仕組みが提供されています。これによって、k8s 上で実行させるプロセスを柔軟に表現できるようになります。

たとえば、OSS の Argo CD では、application というリソースとそれを監視するコントローラを追加することで、 application で指定した github 上のレポジトリ/パスに定義されている k8s マニフェストを監視して変更があったら apply するような GitOps を実現するようになっています。

カスタムリソースの追加方法 🔗

Pod, Job などのビルトインリソース以外の拡張したリソースのことを「カスタムリソース」と呼びます。

カスタムリソースの追加方法には、大きく2通りの方法があります。

1つは、Custom Resource Definition (CRD) というカスタムリソースの定義をコントロールプレーンの API に渡すことで、Pod や Job などと同様に k8s がマニフェストを受け付けてくれるようになります。

もう1つは、API アグリゲーションという方法で、ざっくり言うとコントロールプレーンのエンドポイントを追加できるような仕組みです。

前者の場合、追加したリソースのマニフェスト管理は k8s の責務とすることができます。 後者の場合、API レベルで実装者側で管理する必要があります。つまり、追加されたマニフェストを管理するロジックを実装する必要があります。

(どちらの方法を使うかは 公式doc に記載がありますが、ほとんどの場合は CRD で要件を満たすように思います。)

CRD の追加方法 🔗

シチュエーション 🔗

例として、「MicroService」というリソースを定義してみます。

MicroService というリソースでは、以下のリソースを抽象化して宣言できるようなものを考えます。

  • API サーバーとしての Pod
  • データの永続化領域としての StatefulSet のクラスタ
  • API サーバー、永続化領域のクラスタにアクセスするためのサービス

そうすると、MicroService リソースでは以下のような情報を登録できるとよさそうです。

  • API サーバーのイメージ
  • API サーバーのレプリカ数
  • データベースの種類 (e.g., MySQL, PostgreSQL, …)
  • 永続化クラスタのサイズ
  • マイグレーションの定義

これらの情報を k8s マニフェストとして spec に記載できて、kind: MicroService として apply できるような形にします。

CRD で表現する 🔗

上記の MicroService リソースを k8s クラスタに追加するには、以下のような CRD マニフェストを apply します。

# microservice-crd.yaml
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  # name は <.spec.names.plural>/<.spec.group> と一致している必要があります。
  name: microservices.stable.example.com
spec:
  group: stable.example.com
  versions:
    - name: v1
      served: true
      storage: true
      schema:
        # データ構造は openAPI の記法で定義することができます。
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              properties:
                api:
                  type: object
                  properties:
                    image:
                      type: string
                    replicas:
                      type: integer
                db:
                  type: object
                  properties:
                    driver:
                      type: string
                    clusterSize:
                      type: integer
                    migrationFile:
                      type: string
  scope: Namespaced
  names:
    plural: microservices
    singular: microservice
    kind: MicroService
    shortNames:
    - ms

より詳しいフィールドの解説は、 公式の API リファレンス が参考になります。

上記のマニフェストを apply します。

$ kubectl apply -f ./microservice-crd.yaml

定義した microservice リソースを追加してみます。

$ kubectl apply -f ./microservice.yaml
# microservice.yaml
apiVersion: stable.example.com/v1
kind: MicroService
metadata:
  namespace: sandbox
  name: sample-ms
spec:
  api:
    image: nginx
    replicas: 2
  db:
    driver: MySQL
    clusterSize: 3
    migrationFile: http://manifest.example.com

リソースが作成されていることが確認できます。

$ kubectl get ms

NAME        AGE
sample-ms   6s

まとめ 🔗

今回は、k8s のリソースを拡張するカスタムリソースの概念と定義の仕方を確認しました。

1点注意すべき点として、CRD とカスタムリソースを追加しただけでは実際に Pod が作成されるわけではありません。

実際に Pod を配置するためには、この定義を取得して kubelet 経由で Pod を実行させるような処理(≒カスタムコントローラ)が必要になります。

サービスを運用するだけであれば、自身でカスタムリソースを追加する必要はあまりないかと思います。 しかし、k8s に追加できる OSS では、カスタムリソースが定義され、ユーザーがリソースの設定を記述するようなケースが多いです。

そういったマニフェストを書く際にスッと腹落ちする助けになれば幸いです。