Administrator guide
Architecture and operational limits
The Python controller loads in-cluster credentials when KUBERNETES_PORT is set;
otherwise it uses the current kubeconfig. It reads /crd.yaml in the container
or crd.yaml relative to the working directory for local execution. At startup
it lists CRDs and creates its definition if no CRD with kind Crate exists.
It does not update an existing definition and its existence check compares only
the kind name, not the API group.
One controller watches crates.k8s.tlspu.com across all namespaces. An ADDED
event creates the StatefulSet, client Service, and headless Service, in that
order. A DELETED event attempts to delete those three resources individually.
MODIFIED events are ignored.
Account for these limits when operating it:
- There is no periodic reconciliation, resource adoption, leader election, or automatic repair. Run a single controller replica for this API group.
spec.createdis a string marker, not status. It is set to"True"even if creation failed. Existing marked requests are skipped when seen again.- Generated resources have no owner references. There is no finalizer or durable deletion queue; do not rely on cleanup for requests deleted while the controller is unavailable.
- There is no built-in scaling, upgrade, storage expansion, backup, restore, database credential, or TLS workflow.
- API failures are printed to standard output. There is no health endpoint, metrics endpoint, or custom-resource status subresource.
Prerequisites
- A Kubernetes cluster supporting
apiextensions.k8s.io/v1CRDs,apps/v1StatefulSets, and persistent volume claims. - Administrative credentials to install a CRD, ClusterRole, ClusterRoleBinding, namespace, and Deployment.
- A default StorageClass or compatible pre-provisioned storage for
ReadWriteOnceclaims. The operator cannot select a StorageClass. - Worker nodes and admission policies that permit a privileged BusyBox init
container to run
sysctl -w vm.max_map_count=262144. This changes a host kernel setting; the workload cannot run unchanged under a policy that forbids it. - Cluster DNS with the
cluster.localsuffix, which is embedded in discovery arguments, and network connectivity between database pods on port4300. - Registry access for the operator, the selected CrateDB image, and
busybox.
The generated database container requests 100m CPU and 512Mi memory, sets
CRATE_HEAP_SIZE=256m, and has no resource limits. There are no probes, placement
constraints, anti-affinity rules, disruption budgets, or explicit pod security
settings beyond the privileged init container. Capacity planning, database
security, backup policy, and availability requirements need separate review.
The repository does not define a tested Kubernetes or CrateDB version matrix.
Build and install
Run these commands from the repository root. Substitute a registry and version tag that your cluster can pull. Building and pushing require Docker and registry credentials.
export OPERATOR_IMAGE=registry.example.com/platform/crate-operator:1.0.0
docker build -t "$OPERATOR_IMAGE" .
docker run --rm --entrypoint python "$OPERATOR_IMAGE" -c 'import kubernetes, yaml'
docker push "$OPERATOR_IMAGE"
The Dockerfile uses python:alpine and installs the Kubernetes Python client
without an upper version bound. It does not pin a dependency set; the import
check above checks that the built image includes both runtime libraries.
The checked-in deployment template still grants access to the legacy
crate-operator.limbicly.com group. Render a local installation manifest with the
current group and your image:
sed \
-e 's/crate-operator\.limbicly\.com/k8s.tlspu.com/g' \
-e "s|{{IMAGE}}|${OPERATOR_IMAGE}|g" \
manifests/01-operator.yaml.jinja2 > /tmp/crate-operator-install.yaml
kubectl apply -f crd.yaml
kubectl wait --for=condition=Established \
crd/crates.k8s.tlspu.com --timeout=60s
kubectl apply -f /tmp/crate-operator-install.yaml
kubectl rollout status deployment/crate-controller \
-n crate-operator --timeout=300s
kubectl logs -n crate-operator deployment/crate-controller
Review the rendered manifest before applying it in your environment. Applying
the CRD explicitly makes its schema available before the controller starts. The
Deployment is named crate-controller, runs one replica, and uses the Recreate
strategy. Its log should reach Waiting for Crate Cluster requests.
Keep the rendered manifest, image reference, and any environment-specific changes
in your deployment configuration. The commands above create a local file; they
do not change the checked-in template. The legacy
test fixture also needs its API version changed
to k8s.tlspu.com/v1 before use. Prefer the complete example in the
user guide.
Controller permissions
The template binds ClusterRole cc-clusterrole to the default ServiceAccount
in namespace crate-operator. The controller needs:
| API group | Resources | Operations used by the controller |
|---|---|---|
k8s.tlspu.com |
crates |
List/watch across namespaces; update requests using a namespaced replace |
apiextensions.k8s.io |
customresourcedefinitions |
List and create |
Core ("") |
services |
Create and delete |
apps |
statefulsets |
Create and delete |
The template grants some additional verbs. If you use a dedicated ServiceAccount or reduce permissions, change both the Deployment and binding consistently. The controller does not directly create pods or claims; the StatefulSet controller creates them. Application users need separate namespace-level access to custom resources and whatever inspection or port forwarding their role needs.
For example, an administrator allowed to impersonate ServiceAccounts can check:
kubectl auth can-i watch crates.k8s.tlspu.com --all-namespaces \
--as=system:serviceaccount:crate-operator:default
kubectl auth can-i update crates.k8s.tlspu.com -n test-crate \
--as=system:serviceaccount:crate-operator:default
kubectl auth can-i create statefulsets.apps -n test-crate \
--as=system:serviceaccount:crate-operator:default
Validate an installation
Create the three-node request from the user guide in a disposable namespace. Check that the CRD reports the current API group and that all resources appear:
kubectl get crd crates.k8s.tlspu.com
kubectl get crates.k8s.tlspu.com simple -n test-crate -o yaml
kubectl get statefulset/simple -n test-crate
kubectl get pods -n test-crate -l app=simple -o wide
kubectl get service/simple service/simple-headless -n test-crate
kubectl get pvc -n test-crate
kubectl logs -n crate-operator deployment/crate-controller
Verify three database pods, bound claims, and Service endpoints. Use port forwarding as described in the user guide to verify database access and inspect node membership. A successful rollout alone is insufficient because no database readiness probe is configured. Delete the test request while the controller is running, verify workload cleanup, and separately remove test data only when it is no longer needed.
Maintenance and data lifecycle
Controller updates
Build and validate a new image, update OPERATOR_IMAGE, render the installation
manifest again, and apply it. Watch the Deployment rollout and controller logs.
Applying a new controller image does not upgrade existing database containers.
Schema changes must be reviewed and applied explicitly with crd.yaml; startup
does not migrate an existing CRD. Avoid deleting requests during controller
downtime because cleanup depends on observed deletion events.
Database changes
Changes to a Crate spec do not reach existing resources. Plan database upgrades,
scaling, or storage changes as manual administrative work with a tested backup
and recovery procedure. Changing StatefulSet replicas alone also leaves the
generated EXPECTED_NODES environment variable and bootstrap node list at their
original values. Review CrateDB's requirements for the deployed version before
changing these settings.
There is no supported configuration field for heap size, CPU/memory settings, StorageClass, authentication, TLS, or probes. Such changes require maintaining controller templates or explicitly managing the generated resources. Keep any manual changes in deployment records, since the request no longer fully describes the workload.
Retained data and deletion
The controller deletes the StatefulSet and Services but never explicitly deletes PVCs. Its StatefulSet template does not set a PVC retention policy; under the default retention behavior the claims remain. Check the live workload and your storage policies, including admission-time changes, before relying on retention. Deleting a PVC can also delete its backing volume according to the volume's reclaim policy.
For simple in test-crate, retained claims are named data-simple-<ordinal>.
Inspect them explicitly before cleanup:
kubectl get pvc -n test-crate
kubectl get pv
If a request is recreated with the same name, the StatefulSet may reuse retained claims. This reuses existing data; it is not a fresh database. Verify data ownership, image compatibility, and recovery requirements first. Do not delete and recreate a request as a routine upgrade or retry mechanism.
Troubleshooting
| Symptom | Checks and next action |
|---|---|
no matches for kind Crate |
Confirm crates.k8s.tlspu.com is established and the request uses k8s.tlspu.com/v1. The old test fixture uses the wrong group. |
Controller logs show Forbidden |
Inspect the ClusterRole group, binding subject, and actual Deployment ServiceAccount. Check cluster-wide watch and namespaced update/create/delete permissions. |
| Controller never creates the expected CRD | Its existence check matches any kind named Crate. Inspect installed CRDs and explicitly apply this repository's CRD. |
created is "True" but resources are absent |
Inspect controller logs. The marker is set even after creation errors; there is no automatic retry or repair. |
| StatefulSet exists but one or both Services are missing | Creation is sequential and can stop partway through. Inspect API errors and repair the missing resources under an administrator's recovery plan. |
| Pods remain Pending | Inspect pod events, quota, scheduling capacity, default StorageClass, PVC status, and volume provisioning. |
| Init container fails or pod creation is denied | Check privileged-container admission policy, busybox pulls, and whether vm.max_map_count can be set on those nodes. |
| Database pods start but do not form a usable cluster | Check database logs, Service endpoints, DNS/SRV lookup, port 4300 connectivity, image compatibility, and the fixed two-node recovery threshold. |
| Editing a request has no effect | MODIFIED events are ignored. Plan an administrative change to the actual workload. |
| Resources remain after request deletion | Check controller availability and deletion logs. There is no owner-reference garbage collection or finalizer; verify and clean up the named resources manually. |
Useful diagnostics for the user-guide example:
kubectl logs -n crate-operator deployment/crate-controller
kubectl describe pod simple-0 -n test-crate
kubectl logs simple-0 -n test-crate -c init-sysctl
kubectl logs simple-0 -n test-crate -c simple
kubectl get endpoints simple simple-headless -n test-crate
kubectl get events -n test-crate --sort-by=.metadata.creationTimestamp
For a partial creation, preserve the request, logs, resource definitions, and
claims while determining what succeeded. Fix the underlying API, admission,
image, or storage problem, then plan a targeted repair against the controller's
templates. Replaying creation against existing resources can fail with
AlreadyExists: the StatefulSet is created first, and an error there prevents
Service creation. Setting created to "False" does not trigger repair because
the resulting modification event is ignored. Do not use marker changes as a
health check or recovery procedure.
Local controller execution
For development, use a Python virtual environment with kubernetes and PyYAML
installed, a valid kubeconfig, and the permissions described above. Run from the
repository root so that crd.yaml is found:
python3 -m venv .venv
.venv/bin/pip install kubernetes PyYAML
.venv/bin/python -u controller.py
Ensure KUBERNETES_PORT is unset when running outside a pod. Stop the deployed
controller before running a local instance against the same cluster. Local
execution uses the current kubeconfig context and has the same provisioning and
deletion effects as the deployed controller.
Remove the operator
While the controller is still running, inventory requests across namespaces, back up data as required, and delete the requests whose clusters should be removed. Verify StatefulSet and Service cleanup, and decide separately how to handle each retained claim.
kubectl get crates.k8s.tlspu.com --all-namespaces
When no requests remain and data handling is complete, remove the controller's installation resources. These names follow the supplied template:
kubectl delete deployment crate-controller -n crate-operator
kubectl delete clusterrolebinding cc-binding
kubectl delete clusterrole cc-clusterrole
kubectl delete crd crates.k8s.tlspu.com
Deleting the CRD removes its custom resources. If requests remain, do not assume
their workloads will be cleaned up as part of CRD removal. Delete namespace
crate-operator only after confirming it contains no resources you need to keep.
