Оригинал: Crossplane v2 — Infrastructure as Code for Kubernetes Platform Teams Part 2 — Andy Welsh (tinfoilcipher), 14 октября 2025. Адаптивный перевод; все права на оригинал принадлежат автору. Примеры кода — в репозитории автора .
Продолжение Части 1. Тема — Composition, система шаблонов, которая позволяет платформенным командам создавать собственные Kubernetes-ресурсы без написания контроллеров.
Что уже есть после части 1
DeploymentRuntimeConfigс именемawsи аутентификацией через IRSA-роль;- Provider
provider-aws-s3, использующий эту конфигурацию; - ClusterRole и ClusterRoleBinding для прав ServiceAccount;
- мультитенантные namespace
tenant1иtenant2; ClusterProviderConfigдля аутентификации в AWS.
Composition: шаблоны на всех уровнях
Composition раскрывает выгоды «создания собственных Kubernetes custom resources без написания контроллеров». Четыре ключевых понятия:
- Composite Resource Definitions (XRD) — определяют схему и спецификацию платформенных кастомных ресурсов (cluster-wide или namespaced);
- Compositions — задают, как работает XRD: обрабатывают входные данные, шаблонизируют и определяют, какие именно Managed Resources создавать;
- Composite Resources (XR) — инстансы, создаваемые тенантами; они запускают pipeline Composition и провижинят Managed Resources;
- Functions — внешние пакеты, дающие Composition возможности шаблонизации и патчинга.
Практический пример: S3-сервис
Соберём переиспользуемое предложение «S3-бакет», которое:
- создаёт S3-бакет;
- включает KMS-шифрование по переданному key ID;
- проставляет теги по входу тенанта;
- включает/выключает versioning по запросу.
XRD
XRD заводит новый кастомный ресурс s3buckets.aws.platform.tinfoilcipher.com с kind S3Bucket. Обязательные входы — имя бакета, регион и KMS key ID; теги опциональны.
apiVersion: apiextensions.crossplane.io/v2
kind: CompositeResourceDefinition
metadata:
name: s3buckets.aws.platform.tinfoilcipher.com
spec:
group: aws.platform.tinfoilcipher.com
names:
kind: S3Bucket
plural: s3buckets
scope: Namespaced
versions:
- name: v1alpha1
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
parameters:
type: object
properties:
bucketName:
type: string
region:
type: string
versioning:
type: string
kmsKeyID:
type: number
required:
- bucketName
- region
- kmsKeyID
tags:
type: object
additionalProperties:
type: string
required:
- parametersУстановка Function
Function patch-and-transform даёт динамический патчинг YAML-манифестов:
apiVersion: pkg.crossplane.io/v1
kind: Function
metadata:
name: patch-and-transform
spec:
package: xpkg.upbound.io/upbound/function-patch-and-transform:v0.9.0Composition pipeline
Composition описывает три Managed Resources в pipeline:
- S3 Bucket — базовый бакет со статической конфигурацией; патчатся имя, регион и теги;
- Bucket Versioning — настраивает versioning, находя бакет по label-селектору;
- Bucket Encryption — применяет server-side encryption с KMS key ID пользователя, тоже через label-селектор.
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
name: s3bucket.aws.platform.tinfoilcipher.com
labels:
aws.platform.tinfoilcipher.com/composition: s3bucket
spec:
compositeTypeRef:
apiVersion: aws.platform.tinfoilcipher.com/v1alpha1
kind: S3Bucket
mode: Pipeline
pipeline:
- step: create-bucket
functionRef:
name: patch-and-transform
input:
apiVersion: pt.fn.crossplane.io/v1beta1
kind: Resources
resources:
- name: s3-bucket
base:
apiVersion: s3.aws.m.upbound.io/v1beta1
kind: Bucket
spec:
forProvider:
forceDestroy: false
providerConfigRef:
kind: ClusterProviderConfig
name: aws-s3
patches:
- fromFieldPath: "spec.parameters.bucketName"
toFieldPath: "metadata.name"
- fromFieldPath: "spec.parameters.bucketName"
toFieldPath: "metadata.labels['aws.platform.tinfoilcipher.com/bucket_name']"
- fromFieldPath: "spec.parameters.region"
toFieldPath: "spec.forProvider.region"
- fromFieldPath: "spec.tags"
toFieldPath: "spec.forProvider.tags"
- name: s3-bucket-versioning
base:
apiVersion: s3.aws.m.upbound.io/v1beta1
kind: BucketVersioning
spec:
providerConfigRef:
kind: ClusterProviderConfig
name: aws-s3
patches:
- fromFieldPath: "spec.parameters.region"
toFieldPath: "spec.forProvider.region"
- fromFieldPath: "spec.parameters.versioning"
toFieldPath: "spec.forProvider.versioningConfiguration.status"
- fromFieldPath: "spec.parameters.bucketName"
toFieldPath: "metadata.labels['aws.platform.tinfoilcipher.com/bucket_name']"
- fromFieldPath: "spec.parameters.bucketName"
toFieldPath: "spec.forProvider.bucketSelector.matchLabels['aws.platform.tinfoilcipher.com/bucket_name']"
- name: s3-bucket-encryption
base:
apiVersion: s3.aws.m.upbound.io/v1beta1
kind: BucketServerSideEncryptionConfiguration
spec:
forProvider:
rule:
- applyServerSideEncryptionByDefault:
sseAlgorithm: aws:kms
providerConfigRef:
kind: ClusterProviderConfig
name: aws-s3
patches:
- fromFieldPath: "spec.parameters.region"
toFieldPath: "spec.forProvider.region"
- fromFieldPath: "spec.parameters.bucketName"
toFieldPath: "metadata.labels['aws.platform.tinfoilcipher.com/bucket_name']"
- fromFieldPath: "spec.parameters.kmsKeyID"
toFieldPath: "spec.forProvider.rule[0].applyServerSideEncryptionByDefault.kmsMasterKeyId"
- fromFieldPath: "spec.parameters.bucketName"
toFieldPath: "spec.forProvider.bucketSelector.matchLabels['aws.platform.tinfoilcipher.com/bucket_name']"Зависимости через labels и selectors
Crossplane использует нативные Kubernetes labels и selectors для управления зависимостями между ресурсами внутри pipeline. Каждому Managed Resource при создании навешиваются лейблы, а нижестоящие ресурсы находят выходы предыдущих через selector. Это позволяет строить forward-зависимости, даже когда выход неизвестен до рантайма.
В примере бакету навешивается лейбл, а ресурсы versioning и encryption через селектор находят бакет по совпадению лейблов.
Создание Composite Resources (XR)
Когда XRD и Composition развёрнуты, тенанты создают XR для провижининга:
apiVersion: aws.platform.tinfoilcipher.com/v1alpha1
kind: S3Bucket
metadata:
name: tinfoil-example-bucket-14-10-25-tenant-1
namespace: tenant1
spec:
parameters:
bucketName: tinfoil-example-bucket-14-10-25-tenant-1
region: eu-west-2
versioning: Enabled
kmsKeyID: 15abcf4-15c5-1fe6-afbd-584bcfe57168
tags:
environment: dev
department: devopsПроверяем:
kubectl get s3bucket --all-namespaces
# NAME SYNCED READY COMPOSITION AGE
# tinfoil-example-bucket-14-10-25-tenant-1 True True s3bucket.aws.platform.tinfoilcipher.com 2m03s
# tinfoil-example-bucket-14-10-25-tenant-2 True True s3bucket.aws.platform.tinfoilcipher.com 2m06sPipeline Composition выполняется асинхронно — обычно провижининг занимает пару минут.
Выбор Composition
При создании XR явной привязки к конкретной Composition нет — XRD сам находит доступные. При одной Composition на XRD выбор автоматический. Если Composition несколько, лучше указать её явно, чтобы избежать непредсказуемого поведения:
apiVersion: aws.platform.tinfoilcipher.com/v1alpha1
kind: S3Bucket
metadata:
name: tinfoil-example-bucket-14-10-25-tenant-1
namespace: tenant1
spec:
crossplane:
compositionRef:
name: s3bucket.aws.platform.tinfoilcipher.comЧто ещё умеет Crossplane
За кадром осталось многое: запуск ресурсов по расписанию, исполнение кастомного кода, hardening workflow, передача данных между несколькими провайдерами и расширенная библиотека Functions.
Итог
Связка XRD + Composition + XR + Functions превращает Crossplane из «ещё одного способа создать бакет» в платформу самообслуживания: платформенная команда один раз описывает безопасный, преднастроенный сервис (бакет с обязательным KMS-шифрованием и тегами), а тенанты потребляют его простым YAML — не зная деталей AWS. Это и есть суть platform engineering, ради которой Crossplane создавался.