Skip to content

Commit 50b3164

Browse files
committed
docs: add scalable composition guide and XRD scale subresource section
1 parent d58066f commit 50b3164

File tree

5 files changed

+272
-0
lines changed

5 files changed

+272
-0
lines changed

content/master/composition/composite-resource-definitions.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,13 @@ isolation and follows standard Kubernetes patterns. Use `Cluster` scope only
531531
for platform level resources like RBAC or cluster configuration.
532532
{{< /hint >}}
533533

534+
### Scale subresource
535+
536+
XRDs can expose the Kubernetes `scale` subresource on a composite resource,
537+
enabling `kubectl scale`, the Horizontal Pod Autoscaler, and KEDA to control
538+
the composite resource. For a full walkthrough see
539+
[Scalable Composition]({{<ref "../guides/scalable-composition">}}).
540+
534541
### Set composite resource defaults
535542
XRDs can set default parameters for composite resources.
536543

Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
---
2+
title: Scalable Composition
3+
weight: 84
4+
description: "Expose the Kubernetes scale subresource on a composite resource to
5+
enable kubectl scale, HPA, and KEDA"
6+
---
7+
8+
An XRD can expose the Kubernetes
9+
[`scale` subresource](https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#scale-subresource)
10+
on a composite resource. Exposing the `scale` subresource enables standard
11+
Kubernetes scaling tools, including `kubectl scale`, the
12+
[Horizontal Pod Autoscaler](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/),
13+
and [KEDA](https://keda.sh/), to control the composite resource without knowing
14+
its full schema.
15+
16+
## Example overview
17+
18+
This guide shows how to expose the `scale` subresource by creating a `MyApp`
19+
composite resource that wraps a Kubernetes `Deployment`.
20+
21+
When a user creates a `MyApp`, Crossplane provisions a `Deployment` and wires
22+
the replica count between the composite resource and the `Deployment`. Standard
23+
Kubernetes scaling tools can then drive the replica count without knowing the
24+
full schema of `MyApp`.
25+
26+
An example `MyApp` XR looks like this:
27+
28+
```yaml {copy-lines="none"}
29+
apiVersion: example.org/v1alpha1
30+
kind: MyApp
31+
metadata:
32+
name: my-app
33+
spec:
34+
replicas: 1
35+
```
36+
37+
**Behind the scenes, Crossplane:**
38+
39+
1. Creates a `Deployment` (the composed resource) with the requested replica
40+
count
41+
2. Writes back the observed replica count from the `Deployment` to
42+
`status.replicas` on the `MyApp`
43+
3. Marks `MyApp` as ready when the `Deployment` is healthy
44+
45+
Because `MyApp` exposes the `scale` subresource, you can scale it without
46+
knowing its full schema:
47+
48+
```shell {copy-lines="none"}
49+
kubectl scale myapp/my-app --replicas=3
50+
```
51+
52+
The Horizontal Pod Autoscaler and KEDA can also target `MyApp` directly using
53+
the same `scale` subresource.
54+
55+
## Prerequisites
56+
57+
This guide requires:
58+
59+
* A Kubernetes cluster
60+
* Crossplane [installed on the Kubernetes cluster]({{<ref "../get-started/install">}})
61+
62+
## Install the functions
63+
64+
Install `function-patch-and-transform` to compose resources and patch
65+
fields between the composite resource and its composed resources:
66+
67+
```yaml
68+
apiVersion: pkg.crossplane.io/v1
69+
kind: Function
70+
metadata:
71+
name: function-patch-and-transform
72+
spec:
73+
package: xpkg.crossplane.io/crossplane-contrib/function-patch-and-transform:v0.10.0
74+
```
75+
76+
Save the function as `fn-pat.yaml` and apply it:
77+
78+
```shell
79+
kubectl apply -f fn-pat.yaml
80+
```
81+
82+
Check that Crossplane installed the function:
83+
84+
```shell {copy-lines="1"}
85+
kubectl get -f fn-pat.yaml
86+
NAME INSTALLED HEALTHY PACKAGE AGE
87+
function-patch-and-transform True True xpkg.crossplane.io/crossplane-contrib/function-patch-and-transform:v0.10.0 8s
88+
```
89+
90+
This guide also uses `function-auto-ready`. This function automatically
91+
marks composed resources as ready when they're healthy:
92+
93+
```yaml
94+
apiVersion: pkg.crossplane.io/v1
95+
kind: Function
96+
metadata:
97+
name: function-auto-ready
98+
spec:
99+
package: xpkg.crossplane.io/crossplane-contrib/function-auto-ready:v0.6.0
100+
```
101+
102+
Save this as `fn-auto-ready.yaml` and apply it:
103+
104+
```shell
105+
kubectl apply -f fn-auto-ready.yaml
106+
```
107+
108+
## Configure the XRD
109+
110+
Configure the `scale` subresource per version in the XRD's
111+
{{<hover label="xrdscale" line="14">}}subresources{{</hover>}} field.
112+
113+
```yaml {label="xrdscale",copy-lines="none"}
114+
apiVersion: apiextensions.crossplane.io/v2
115+
kind: CompositeResourceDefinition
116+
metadata:
117+
name: myapps.example.org
118+
spec:
119+
group: example.org
120+
names:
121+
kind: MyApp
122+
plural: myapps
123+
scope: Namespaced
124+
versions:
125+
- additionalPrinterColumns:
126+
- jsonPath: .spec.replicas
127+
name: DESIRED
128+
type: string
129+
- jsonPath: .status.replicas
130+
name: CURRENT
131+
type: string
132+
name: v1alpha1
133+
served: true
134+
referenceable: true
135+
subresources:
136+
scale:
137+
specReplicasPath: .spec.replicas
138+
statusReplicasPath: .status.replicas
139+
labelSelectorPath: .status.labelSelector
140+
schema:
141+
openAPIV3Schema:
142+
properties:
143+
spec:
144+
properties:
145+
replicas:
146+
type: integer
147+
status:
148+
properties:
149+
replicas:
150+
type: integer
151+
labelSelector:
152+
type: string
153+
```
154+
155+
The `scale` block has three fields:
156+
157+
* {{<hover label="xrdscale" line="17">}}specReplicasPath{{</hover>}}: the
158+
JSON path to the field in the composite resource's `spec` that holds the
159+
desired replica count. This field must exist in the XRD schema.
160+
* {{<hover label="xrdscale" line="18">}}statusReplicasPath{{</hover>}}: the
161+
JSON path to the field in the composite resource's `status` that holds the
162+
observed replica count. This field must exist in the XRD schema.
163+
* {{<hover label="xrdscale" line="19">}}labelSelectorPath{{</hover>}}: an
164+
optional JSON path to a `string` field in `status` that holds a serialized
165+
label selector. Required by the Horizontal Pod Autoscaler.
166+
167+
{{<hint "important">}}
168+
Crossplane propagates the `scale` configuration to the generated CRD.
169+
The composition author must implement the scaling logic, for example
170+
by patching `spec.replicas` from the composite resource into a
171+
composed `Deployment`.
172+
{{</hint>}}
173+
174+
## Implement scaling in a Composition
175+
176+
After enabling the `scale` subresource on the XRD, wire the replica count into
177+
the composed resources in the Composition. The following example uses
178+
`function-patch-and-transform` to forward `spec.replicas` from the composite
179+
resource to a `Deployment`:
180+
181+
```yaml {copy-lines="none"}
182+
apiVersion: apiextensions.crossplane.io/v1
183+
kind: Composition
184+
metadata:
185+
name: myapp
186+
spec:
187+
compositeTypeRef:
188+
apiVersion: example.org/v1alpha1
189+
kind: MyApp
190+
mode: Pipeline
191+
pipeline:
192+
- step: patch-and-transform
193+
functionRef:
194+
name: function-patch-and-transform
195+
input:
196+
apiVersion: pt.fn.crossplane.io/v1beta1
197+
kind: Resources
198+
resources:
199+
- name: deployment
200+
base:
201+
apiVersion: apps/v1
202+
kind: Deployment
203+
spec:
204+
replicas: 1
205+
selector:
206+
matchLabels:
207+
app: nginx
208+
template:
209+
metadata:
210+
labels:
211+
app: nginx
212+
spec:
213+
containers:
214+
- name: nginx
215+
image: nginx:1.29.7-alpine
216+
ports:
217+
- containerPort: 80
218+
patches:
219+
- type: FromCompositeFieldPath
220+
fromFieldPath: spec.replicas
221+
toFieldPath: spec.replicas
222+
- type: ToCompositeFieldPath
223+
fromFieldPath: status.readyReplicas
224+
toFieldPath: status.replicas
225+
- step: automatically-detect-readiness
226+
functionRef:
227+
name: function-auto-ready
228+
```
229+
230+
The composition must also write back the current replica count to
231+
`status.replicas` (and `status.labelSelector` if used) so that autoscalers and
232+
`kubectl scale --current-replicas` read the correct replica count.
233+
234+
## Create a `MyApp` composite resource
235+
236+
With the XRD and Composition in place, create a `MyApp` composite resource:
237+
238+
```yaml
239+
apiVersion: example.org/v1alpha1
240+
kind: MyApp
241+
metadata:
242+
name: my-app
243+
spec:
244+
replicas: 1
245+
```
246+
247+
## Use the scale subresource
248+
249+
After applying the XRD and Composition, scale a composite resource with
250+
`kubectl scale`:
251+
252+
```shell
253+
kubectl scale myapp/my-app --replicas=3
254+
```
255+
256+
Verify the current replica count:
257+
258+
```shell
259+
user@user % kubectl get myapp/my-app
260+
NAME DESIRED CURRENT SYNCED READY COMPOSITION AGE
261+
my-app 3 3 True True myapp 3m26s
262+
```

utils/vale/styles/Crossplane/allowed-jargon.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ API's
44
APIs
55
ARM64
66
autoscaler
7+
autoscalers
78
backoff
89
backported
910
base64

utils/vale/styles/Crossplane/brands.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ CC-BY
55
CloudNativePG
66
CloudSQL
77
CNCF
8+
KEDA
89
Commonmark
910
DockerHub
1011
DocSearch

utils/vale/styles/Crossplane/spelling-exceptions.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ Service-specific
7777
space-delimited
7878
status-checking
7979
step-by-step
80+
subresource
8081
subresources
8182
System-level
8283
/tab

0 commit comments

Comments
 (0)