3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Crossplane: function-extra-resourcesで通常取得不能なカスタムリソースを取得する方法

Last updated at Posted at 2024-09-11

Crossplanefunction-extra-resourcesは、Composition内で追加のリソースを取得するための強力なツールです。しかし、デフォルトではCrossplane関連のリソース(XRなど)しか取得できません。この記事では、この制約を回避し、任意のカスタムリソースを取得する方法を紹介します。

前提条件

このチュートリアルはKCLを用いますが、YAMLでも同じようなことができます。

セットアップ

1. Crossplaneのインストール

まず、Helmfileを使用してCrossplaneをインストールします。

# helmfile.yaml
repositories:
  - name: crossplane-stable
    url: https://charts.crossplane.io/stable

releases:
  - name: crossplane
    namespace: crossplane-system
    chart: crossplane-stable/crossplane
    version: 1.16

以下のコマンドを実行してCrossplaneをインストールします:

helmfile sync --wait

2. カスタムリソースへのアクセス権限の設定

Crossplaneがカスタムリソースにアクセスできるように、特別なClusterRoleを作成します。

# 01-cluster-roles.k
import manifests
import k8s.api.rbac.v1 as rbac

manifests.yaml_stream([
    rbac.ClusterRole {
        metadata.name = "suin:crossplane:additional"
        rules = [
            {
                apiGroups = ["suin.jp"]
                resources = ["clusterresources"]
                verbs = ["*"]
            },
            {
                apiGroups = ["suin.jp"]
                resources = ["namespacedresources"]
                verbs = ["*"]
            }
        ]
    },
    rbac.ClusterRoleBinding {
        metadata.name = "suin:crossplane:additional"
        roleRef = {
            apiGroup = "rbac.authorization.k8s.io"
            kind = "ClusterRole"
            name = "suin:crossplane:additional"
        }
        subjects = [{
            kind = "ServiceAccount"
            name = "crossplane"
            namespace = "crossplane-system"
        }]
    }
])

このClusterRoleを適用します:

kcl run 01-cluster-roles.k | kubectl apply -f -

これによって、crossplaneサービスアカウントにカスタムリソースを操作する権限がつきます。これをしないと、デフォルトではfunction-extra-resourcesはカスタムリソースにアクセスできません。本チュートリアルで最も重要なステップのひとつです。

ちなみに、function-extra-resourcesを導入すると、この関数用のPodが作られます。このPodは、 function-extra-resources-fa66e8ab9f79のような固有のサービスアカウントに紐づきます。そう考えると、サービスアカウントfunction-extra-resources-fa66e8ab9f79にカスタムリソースのアクセス権限をつけるのが良さそうに思えますが、それではうまく動作しません。理由は、この関数はCrossplaneのAPIを経由してKubernetesのオブジェクトを取得しているからです。したがって、CrossplaneのAPIを提供しているPodのサービスアカウントに権限を付与する必要があります。

3. カスタムリソース定義(CRD)のインストール

テスト用のカスタムリソースを定義します。

# 02-crds.k
import manifests
import k8s.apiextensions_apiserver.pkg.apis.apiextensions.v1

manifests.yaml_stream([
    v1.CustomResourceDefinition {
        metadata.name = "clusterresources.suin.jp"
        spec = {
            group = "suin.jp"
            scope = "Cluster"
            names = {
                kind = "ClusterResource"
                plural = "clusterresources"
                singular = "clusterresource"
            }
            versions = [{
                name = "v1"
                served = True
                storage = True
                schema.openAPIV3Schema = v1.JSONSchemaProps {
                    type = "object"
                    properties = {
                        spec = {type = "object"}
                    }
                }
            }]
        }
    },
    v1.CustomResourceDefinition {
        metadata.name = "namespacedresources.suin.jp"
        spec = {
            group = "suin.jp"
            scope = "Namespaced"
            names = {
                kind = "NamespacedResource"
                plural = "namespacedresources"
                singular = "namespacedresource"
            }
            versions = [{
                name = "v1"
                served = True
                storage = True
                schema.openAPIV3Schema = v1.JSONSchemaProps {
                    type = "object"
                    properties = {
                        spec = {type = "object"}
                    }
                }
            }]
        }
    }
])

CRDを適用し、確立されるまで待ちます:

kcl run 02-crds.k | kubectl apply -f -
kubectl wait --for=condition=Established=true customresourcedefinition.apiextensions.k8s.io/clusterresources.suin.jp
kubectl wait --for=condition=Established=true customresourcedefinition.apiextensions.k8s.io/namespacedresources.suin.jp

ここで定義したkindのリソースを後で作り、function-extra-resourcesでそのオブジェクトを取ってくるということをやる予定です。function-extra-resourcesがクラスタースコープとネームスペーススコープどちらのカスタムリソースも取得可能であることを確かめるために、ここでは2つのCRDを作成しています。

4. Crossplane Functionsのインストール

必要なCrossplane Functionsをインストールします。

# 03-functions.k
import manifests

manifests.yaml_stream([
    {
        apiVersion = "pkg.crossplane.io/v1beta1"
        kind = "Function"
        metadata.name = "function-kcl"
        spec.package = "xpkg.upbound.io/crossplane-contrib/function-kcl:v0.9.0"
    },
    {
        apiVersion = "pkg.crossplane.io/v1beta1"
        kind = "Function"
        metadata.name = "function-auto-ready"
        spec.package = "xpkg.upbound.io/crossplane-contrib/function-auto-ready:v0.2.1"
    },
    {
        apiVersion = "pkg.crossplane.io/v1beta1"
        kind = "Function"
        metadata.name = "function-extra-resources"
        spec.package = "xpkg.upbound.io/crossplane-contrib/function-extra-resources:v0.0.3"
    }
])

Functionsをインストールし、準備が整うまで待ちます:

kcl run 03-functions.k | kubectl apply -f -
kubectl wait --for=condition=Healthy=true function.pkg.crossplane.io/function-kcl
kubectl wait --for=condition=Healthy=true function.pkg.crossplane.io/function-auto-ready
kubectl wait --for=condition=Healthy=true function.pkg.crossplane.io/function-extra-resources

5. Composite Resource Definition (XRD)の作成

テスト用のXRDを作成します。

# 04-xrds.k
import manifests
import crossplane.v1 as xp
import k8s.apiextensions_apiserver.pkg.apis.apiextensions.v1

manifests.yaml_stream([xp.CompositeResourceDefinition {
    metadata = {name = "xr1.suin.jp"}
    spec = {
        names = {
            kind = "XR1"
            plural = "xr1"
        }
        group = "suin.jp"
        versions = [{
            name = "v1"
            served = True
            referenceable = True
            schema.openAPIV3Schema = v1.JSONSchemaProps {}
        }]
    }
}])

XRDを適用し、確立されるまで待ちます:

kcl run 04-xrds.k | kubectl apply -f -
kubectl wait --for=condition=Established=true compositeresourcedefinitions.apiextensions.crossplane.io/xr1.suin.jp

6. Compositionの作成

function-extra-resourcesを使用してカスタムリソースを取得するCompositionを作成します。

# 05-compositions.k
import crossplane.v1 as xp
import outdent

xp.Composition {
    metadata = {name = "test.suin.jp"}
    spec = {
        compositeTypeRef = {
            apiVersion = "suin.jp/v1"
            kind = "XR1"
        }
        mode = "Pipeline"
        pipeline = [
            {
                step = "pull-cluster-resources"
                functionRef.name = "function-extra-resources"
                input = {
                    apiVersion = "extra-resources.fn.crossplane.io/v1beta1"
                    kind = "Input"
                    spec.extraResources = [
                        {
                            apiVersion = "suin.jp/v1"
                            kind = "ClusterResource"
                            into = "cluster_resources"
                            type = "Selector"
                            selector.matchLabels = [{
                                type = "Value"
                                key = "some-label-name"
                                value = "some-label-value"
                            }]
                        },
                        {
                            apiVersion = "suin.jp/v1"
                            kind = "NamespacedResource"
                            into = "namespaced_resources"
                            type = "Selector"
                            selector.matchLabels = [{
                                type = "Value"
                                key = "some-label-name"
                                value = "some-label-value"
                            }]
                        }
                    ]
                }
            },
            {
                step = "kcl"
                functionRef.name = "function-kcl"
                input = {
                    apiVersion = "krm.kcl.dev/v1alpha1"
                    kind = "KCLInput"
                    spec.source = outdent.outdent("""
                    import yaml
                    extra_resources = option("params")?.ctx["apiextensions.crossplane.io/extra-resources"]
                    print("=== found resources by function-extra-resources ===")
                    print("Found ${len(extra_resources.cluster_resources)} cluster resources and ${len(extra_resources.namespaced_resources)} namespaced resources.")
                    print(yaml.encode({
                        cluster_resources: [r.metadata.name for r in extra_resources.cluster_resources]
                        namespaced_resources: [{ 
                            name = r.metadata.name
                            namespace = r.metadata.namespace
                        } for r in extra_resources.namespaced_resources]
                    }))
                    print("===")
                    items = []
                    """)
                }
            }
        ]
    }
}

このCompositionは2つのステップからなります。

  1. function-extra-resourcesで2種類のkindのカスタムリソースを取得する
  2. KCLを使って上で取得したリソースをPodのログに出力する。要するにこのステップはデバッグ要員です。

Compositionを適用します:

kcl run 05-compositions.k | kubectl apply -f -

7. テスト用のリソースの作成

テスト用のNamespaceとカスタムリソースを作成します。

# 06-namespace.k
import k8s.api.core.v1

v1.Namespace {metadata.name = "my-namespace"}
# 07-crs.k
import manifests

manifests.yaml_stream([
    {
        apiVersion = "suin.jp/v1"
        kind = "ClusterResource"
        metadata.name = "cluster-resource-1"
        metadata.labels = {"some-label-name" = "some-label-value"}
        spec = {}
    },
    {
        apiVersion = "suin.jp/v1"
        kind = "NamespacedResource"
        metadata.name = "namespaced-resource-1"
        metadata.namespace = "default"
        metadata.labels = {"some-label-name" = "some-label-value"}
        spec = {}
    },
    {
        apiVersion = "suin.jp/v1"
        kind = "NamespacedResource"
        metadata.name = "namespaced-resource-1"
        metadata.namespace = "my-namespace"
        metadata.labels = {"some-label-name" = "some-label-value"}
        spec = {}
    }
])

Namespaceとカスタムリソースを適用します:

kcl run 06-namespace.k | kubectl apply -f -
kubectl wait --for=jsonpath=status.phase=Active namespace/my-namespace
kcl run 07-crs.k | kubectl apply -f -

8. Composite Resource (XR)の作成

テスト用のXRを作成します。

# 08-xrs.k
import manifests

manifests.yaml_stream([{
    apiVersion = "suin.jp/v1"
    kind = "XR1"
    metadata.name = "object1"
    spec = {}
}])

XRを適用し、Readyになるまで待ちます:

kcl run 08-xrs.k | kubectl apply -f -
kubectl wait --for=condition=Ready=true xr1.suin.jp/object1

9. 結果の確認

function-kclのログを確認して、カスタムリソースが正しく取得されていることを確認します:

kubectl logs -n crossplane-system -f $(kubectl get pods -o name -n crossplane-system | grep function-kcl)

ログに次のような出力が出てくれば成功です!

=== found resources by function-extra-resources ===
Found 1 cluster resources and 2 namespaced resources.
cluster_resources:
- cluster-resource-1
namespaced_resources:
- name: namespaced-resource-1
  namespace: default
- name: namespaced-resource-1
  namespace: my-namespace

===

まとめ

このチュートリアルでは、Crossplaneのfunction-extra-resourcesを使用して、通常はアクセスできないカスタムリソースを取得する方法を紹介しました。この技術を使用することで、Crossplaneの機能を拡張し、より柔軟なリソース管理が可能になります。

参考資料

このチュートリアルで使用したコードとリソースは、以下のGitHubリポジトリで参照できます
https://github.com/suinplayground/kubernetes-playground/blob/main/crossplane/12-function-extra-resources-to-pull-custom-resources/README.md

Crossplaneとfunction-extra-resourcesの詳細については、公式ドキュメントを参照してください。

一緒に働きませんか?

僕の会社(株式会社クラフトマンソフトウェア)では、Kubernetesエンジニアを募集しています!興味がある方はぜひ↓も御覧ください😌

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?