mirror of
https://github.com/jpetazzo/container.training.git
synced 2026-07-29 17:51:09 +00:00
378 lines
7.7 KiB
Markdown
378 lines
7.7 KiB
Markdown
# Custom Resource Definitions
|
|
|
|
- CRDs are one of the (many) ways to extend the API
|
|
|
|
- CRDs can be defined dynamically
|
|
|
|
(no need to recompile or reload the API server)
|
|
|
|
- A CRD is defined with a CustomResourceDefinition resource
|
|
|
|
(CustomResourceDefinition is conceptually similar to a *metaclass*)
|
|
|
|
---
|
|
|
|
## Creating a CRD
|
|
|
|
- We will create a CRD to represent different recipes of pizzas
|
|
|
|
- We will be able to run `kubectl get pizzas` and it will list the recipes
|
|
|
|
- Creating/deleting recipes won't do anything else
|
|
|
|
(because we won't implement a *controller*)
|
|
|
|
---
|
|
|
|
## First slice of pizza
|
|
|
|
```yaml
|
|
@@INCLUDE[k8s/pizza-1.yaml]
|
|
```
|
|
|
|
---
|
|
|
|
## The joys of API deprecation
|
|
|
|
- Unfortunately, the CRD manifest on the previous slide is deprecated!
|
|
|
|
- It is using `apiextensions.k8s.io/v1beta1`, which is dropped in Kubernetes 1.22
|
|
|
|
- We need to use `apiextensions.k8s.io/v1`, which is a little bit more complex
|
|
|
|
(a few optional things become mandatory, see [this guide](https://kubernetes.io/docs/reference/using-api/deprecation-guide/#customresourcedefinition-v122) for details)
|
|
|
|
- `apiextensions.k8s.io/v1beta1` is available since Kubernetes 1.16
|
|
|
|
---
|
|
|
|
## Second slice of pizza
|
|
|
|
- The next slide will show file @@LINK[k8s/pizza-2.yaml]
|
|
|
|
- Note the `spec.versions` list
|
|
|
|
- we need exactly one version with `storage: true`
|
|
|
|
- we can have multiple versions with `served: true`
|
|
|
|
- `spec.versions[].schema.openAPI3Schema` is required
|
|
|
|
(and must be a valid OpenAPI schema; here it's a trivial one)
|
|
|
|
---
|
|
|
|
```yaml
|
|
@@INCLUDE[k8s/pizza-2.yaml]
|
|
```
|
|
|
|
---
|
|
|
|
## Baking some pizza
|
|
|
|
- Let's create the Custom Resource Definition for our Pizza resource
|
|
|
|
.lab[
|
|
|
|
- Load the CRD:
|
|
```bash
|
|
kubectl apply -f ~/container.training/k8s/pizza-2.yaml
|
|
```
|
|
|
|
- Confirm that it shows up:
|
|
```bash
|
|
kubectl get crds
|
|
```
|
|
|
|
]
|
|
|
|
---
|
|
|
|
## Creating custom resources
|
|
|
|
The YAML below defines a resource using the CRD that we just created:
|
|
|
|
```yaml
|
|
kind: Pizza
|
|
apiVersion: container.training/v1alpha1
|
|
metadata:
|
|
name: napolitana
|
|
spec:
|
|
toppings: [ mozzarella ]
|
|
```
|
|
|
|
.lab[
|
|
|
|
- Try to create a few pizza recipes:
|
|
```bash
|
|
kubectl apply -f ~/container.training/k8s/pizzas.yaml
|
|
```
|
|
|
|
]
|
|
|
|
---
|
|
|
|
## Type validation
|
|
|
|
- Older versions of Kubernetes will accept our pizza definition as is
|
|
|
|
- Newer versions, however, will issue warnings about unknown fields
|
|
|
|
(and if we use `--validate=false`, these fields will simply be dropped)
|
|
|
|
- We need to improve our OpenAPI schema
|
|
|
|
(to add e.g. the `spec.toppings` field used by our pizza resources)
|
|
|
|
---
|
|
|
|
## Third slice of pizza
|
|
|
|
- Let's add a full OpenAPI v3 schema to our Pizza CRD
|
|
|
|
- We'll require a field `spec.sauce` which will be a string
|
|
|
|
- And a field `spec.toppings` which will have to be a list of strings
|
|
|
|
.lab[
|
|
|
|
- Update our pizza CRD:
|
|
```bash
|
|
kubectl apply -f ~/container.training/k8s/pizza-3.yaml
|
|
```
|
|
|
|
- Load our pizza recipes:
|
|
```bash
|
|
kubectl apply -f ~/container.training/k8s/pizzas.yaml
|
|
```
|
|
|
|
]
|
|
|
|
---
|
|
|
|
## Viewing custom resources
|
|
|
|
- By default, `kubectl get` only shows name and age of custom resources
|
|
|
|
.lab[
|
|
|
|
- View the pizza recipes that we just created:
|
|
```bash
|
|
kubectl get pizzas
|
|
```
|
|
|
|
]
|
|
|
|
- Let's see how we can improve that display!
|
|
|
|
---
|
|
|
|
## Additional printer columns
|
|
|
|
- We can tell Kubernetes which columns to show:
|
|
```yaml
|
|
additionalPrinterColumns:
|
|
- jsonPath: .spec.sauce
|
|
name: Sauce
|
|
type: string
|
|
- jsonPath: .spec.toppings
|
|
name: Toppings
|
|
type: string
|
|
```
|
|
|
|
- There is an updated CRD in @@LINK[k8s/pizza-4.yaml]
|
|
|
|
---
|
|
|
|
## Using additional printer columns
|
|
|
|
- Let's update our CRD!
|
|
|
|
.lab[
|
|
|
|
- Update the CRD:
|
|
```bash
|
|
kubectl apply -f ~/container.training/k8s/pizza-4.yaml
|
|
```
|
|
|
|
- Look at our Pizza resources:
|
|
```bash
|
|
kubectl get pizzas
|
|
```
|
|
|
|
]
|
|
|
|
Note: we can update a CRD without having to re-create the corresponding resources.
|
|
|
|
(Good news, right?)
|
|
|
|
---
|
|
|
|
## Better data validation
|
|
|
|
- Let's change the data schema so that the sauce can only be `red` or `white`
|
|
|
|
- This will be implemented by @@LINK[k8s/pizza-5.yaml]
|
|
|
|
.lab[
|
|
|
|
- Update the Pizza CRD:
|
|
```bash
|
|
kubectl apply -f ~/container.training/k8s/pizza-5.yaml
|
|
```
|
|
|
|
]
|
|
|
|
---
|
|
|
|
## Validation *a posteriori*
|
|
|
|
- Some of the pizzas that we defined earlier *do not* pass validation
|
|
|
|
- How is that possible?
|
|
|
|
--
|
|
|
|
- Validation happens at *admission*
|
|
|
|
(when resources get written into the database)
|
|
|
|
- Therefore, we can have "invalid" resources in etcd
|
|
|
|
(they are invalid from the CRD perspective, but the CRD can be changed)
|
|
|
|
🤔 How should we handle that ?
|
|
|
|
---
|
|
|
|
## Versions
|
|
|
|
- If the data format changes, we can roll out a new version of the CRD
|
|
|
|
(e.g. go from `v1alpha1` to `v1alpha2`)
|
|
|
|
- In a CRD we can specify the versions that exist, that are *served*, and *stored*
|
|
|
|
- multiple versions can be *served*
|
|
|
|
- only one can be *stored*
|
|
|
|
- Kubernetes doesn't automatically migrate the content of the database
|
|
|
|
- However, it can convert between versions when resources are read/written
|
|
|
|
---
|
|
|
|
## Conversion
|
|
|
|
- When *creating* a new resource, the *stored* version is used
|
|
|
|
(if we create it with another version, it gets converted)
|
|
|
|
- When *getting* or *watching* resources, the *requested* version is used
|
|
|
|
(if it is stored with another version, it gets converted)
|
|
|
|
- By default, "conversion" only changes the `apiVersion` field
|
|
|
|
- ... But we can register *conversion webhooks*
|
|
|
|
(see [that doc page](https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definition-versioning/#webhook-conversion) for details)
|
|
|
|
---
|
|
|
|
## Migrating database content
|
|
|
|
- We need to *serve* a version as long as we *store* objects in that version
|
|
|
|
(=as long as the database has at least one object with that version)
|
|
|
|
- If we want to "retire" a version, we need to migrate these objects first
|
|
|
|
- All we have to do is to read and re-write them
|
|
|
|
(the [kube-storage-version-migrator](https://github.com/kubernetes-sigs/kube-storage-version-migrator) tool can help)
|
|
|
|
---
|
|
|
|
## Even better data validation
|
|
|
|
- If we need more complex data validation, we can use a validating webhook
|
|
|
|
- Use cases:
|
|
|
|
- validating a "version" field for a database engine
|
|
|
|
- validating that the number of e.g. coordination nodes is even
|
|
|
|
- preventing inconsistent or dangerous changes
|
|
<br/>
|
|
(e.g. major version downgrades)
|
|
|
|
- checking a key or certificate format or validity
|
|
|
|
- and much more!
|
|
|
|
---
|
|
|
|
## CRDs in the wild
|
|
|
|
- [gitkube](https://storage.googleapis.com/gitkube/gitkube-setup-stable.yaml)
|
|
|
|
- [A redis operator](https://github.com/amaizfinance/redis-operator/blob/master/deploy/crds/k8s_v1alpha1_redis_crd.yaml)
|
|
|
|
- [cert-manager](https://github.com/jetstack/cert-manager/releases/download/v1.0.4/cert-manager.yaml)
|
|
|
|
*How big are these YAML files?*
|
|
|
|
*What's the size (e.g. in lines) of each resource?*
|
|
|
|
---
|
|
|
|
## CRDs in practice
|
|
|
|
- Production-grade CRDs can be extremely verbose
|
|
|
|
(because of the openAPI schema validation)
|
|
|
|
- This can (and usually will) be managed by a framework
|
|
|
|
---
|
|
|
|
## (Ab)using the API server
|
|
|
|
- If we need to store something "safely" (as in: in etcd), we can use CRDs
|
|
|
|
- This gives us primitives to read/write/list objects (and optionally validate them)
|
|
|
|
- The Kubernetes API server can run on its own
|
|
|
|
(without the scheduler, controller manager, and kubelets)
|
|
|
|
- By loading CRDs, we can have it manage totally different objects
|
|
|
|
(unrelated to containers, clusters, etc.)
|
|
|
|
---
|
|
|
|
## What's next?
|
|
|
|
- Creating a basic CRD is relatively straightforward
|
|
|
|
- But CRDs generally require a *controller* to do anything useful
|
|
|
|
- The controller will typically *watch* our custom resources
|
|
|
|
(and take action when they are created/updated)
|
|
|
|
- Most serious use-cases will also require *validation web hooks*
|
|
|
|
- When our CRD data format evolves, we'll also need *conversion web hooks*
|
|
|
|
- Doing all that work manually is tedious; use a framework!
|
|
|
|
???
|
|
|
|
:EN:- Custom Resource Definitions (CRDs)
|
|
:FR:- Les CRDs *(Custom Resource Definitions)*
|