Files
container.training/slides/k8s/operators-example.md
Jérôme Petazzoni 94545f800a 📃 Add TOC item to nsplease
2022-04-06 22:01:22 +02:00

159 lines
3.6 KiB
Markdown

# Writing an tiny operator
- Let's look at a simple operator
- It does have:
- a control loop
- resource lifecycle management
- basic logging
- It doesn't have:
- CRDs (and therefore, resource versioning, conversion webhooks...)
- advanced observability (metrics, Kubernetes Events)
---
## Use case
*When I push code to my source control system, I want that code
to be built into a container image, and that image to be deployed
in a staging environment. I want each branch/tag/commit (depending
on my needs) to be deployed into its specific Kubernetes Namespace.*
- The last part requires the CI/CD pipeline to manage Namespaces
- ...And permissions in these Namespaces
- This requires elevated privileges for the CI/CD pipeline
(read: `cluster-admin`)
- If the CI/CD pipeline is compromised, this can lead to cluster compromise
- This can be a concern if the CI/CD pipeline is part of the repository
(which is the default modus operandi with GitHub, GitLab, Bitbucket...)
---
## Proposed solution
- On-demand creation of Namespaces
- Creation is triggered by creating a ConfigMap in a dedicated Namespace
- Namespaces are set up with basic permissions
- Credentials are generated for each Namespace
- Credentials only give access to their Namespace
- Credentials are exposed back to the dedicated configuration Namespace
- Operator implemented as a shell script
---
## An operator in shell... Really?
- About 150 lines of code
(including comments + white space)
- Performance doesn't matter
- operator work will be a tiny fraction of CI/CD pipeline work
- uses *watch* semantics to minimize control plane load
- Easy to understand, easy to audit, easy to tweak
---
## Show me the code!
- GitHub repository and documentation:
https://github.com/jpetazzo/nsplease
- Operator source code:
https://github.com/jpetazzo/nsplease/blob/main/nsplease.sh
---
## Main loop
```bash
info "Waiting for ConfigMap events in $REQUESTS_NAMESPACE..."
kubectl --namespace $REQUESTS_NAMESPACE get configmaps \
--watch --output-watch-events -o json \
| jq --unbuffered --raw-output '[.type,.object.metadata.name] | @tsv' \
| while read TYPE NAMESPACE; do
debug "Got event: $TYPE $NAMESPACE"
```
- `--watch` to avoid active-polling the control plane
- `--output-watch-events` to disregard e.g. resource deletion, edition
- `jq` to process JSON easily
---
## Resource ownership
- Check out the `kubectl patch` commands
- The created Namespace "owns" the corresponding ConfigMap and Secret
- This means that deleting the Namespace will delete the ConfigMap and Secret
- We don't need to watch for object deletion to clean up
- Clean up will we done automatically even if operator is not running
---
## Why no CRD?
- It's easier to create a ConfigMap
(e.g. `kubectl create configmap --from-literal=` one-liner)
- We don't need the features of CRDs
(schemas, printer columns, versioning...)
- “This CRD could have been a ConfigMap!”
(this doesn't mean *all* CRDs could be ConfigMaps, of course)
---
## Discussion
- A lot of simple, yet efficient logic, can be implemented in shell scripts
- These can be used to prototype more complex operators
- Not all use-cases require CRDs
(keep in mind that correct CRDs are *a lot* of work!)
- If the algorithms are correct, shell performance won't matter at all
(but it will be difficult to keep a resource cache in shell)
- Improvement idea: this operator could generate *events*
(visible with `kubectl get events` and `kubectl describe`)
???
:EN:- How to write a simple operator with shell scripts
:FR:- Comment écrire un opérateur simple en shell script