https://hub.docker.com/r/kainlite/gitops-operator/tags/)
Basically this is a GitOps controller working in pull mode (from the cluster), triggered by Kubernetes on a readiness check (this is a hack) to trigger our reconcile method, while this is a learning project maybe some day it will be able to handle serious workloads.
I personally use it in my cluster to manage the lifecycle of my blog and the operator itself in k3s (my production cluster), before this operator existed I used Argo CD image updater, I still use argo but git dictates which image is running.
You can read or watch the video here (coming soon tm)...
Run against your current Kubernetes context:
kind create cluster
## Apply the manifests from the gitops-operator-manifests to manage that repo (otherwise deploy your own app with the
## annotations)
# kustomize build . | kubectl apply -f -
cargo watch -- cargo run
# or handy to debug and be able to read logs and events from the tracer
RUST_LOG=info cargo watch -- cargo run | jq -R '. as $line | try (fromjson | .time + " " + .msg + " " + .target) catch $line'
# or using bunyan
RUST_LOG=info cargo watch -- cargo run | bunyan
# or from the deployed version
stern -o raw -n gitops-operator gitops | jq -R '. as $line | try (fromjson | .time + " " + .msg + " " + .target) catch $line'
# or using bunyan
stern -o raw -n gitops-operator gitops | bunyan
To run the observability stack run (note that these run on the host's ports due to we need to connect to the cluster using the local configuration):
docker compose up -d
To observe a deployment just add these annotations to your configuration file (this is what I'm using to self-observe and update the manifests repo for this project):
These are all required fields, or the deployment will be skipped:
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
gitops.operator.app_repository: [email protected]:kainlite/gitops-operator.git
gitops.operator.deployment_path: app/00-deployment.yaml
gitops.operator.enabled: 'true'
gitops.operator.image_name: kainlite/gitops-operator
gitops.operator.manifest_repository: [email protected]:kainlite/gitops-operator-manifests.git
gitops.operator.namespace: default
gitops.operator.ssh_key_name: ssh-key
gitops.operator.ssh_key_namespace: gitops-operator
gitops.operator.notifications_secret_name: 'webhook-secret'
gitops.operator.notifications_secret_namespace: 'gitops-operator'
labels:
app: gitops-operator
name: gitops-operator
namespace: default
spec:
replicas: 1
...
A bit more information about the annotations:
gitops.operator.enabled: # Wheter the operator should process this deployment or not
gitops.operator.app_repository: # Git repository with SSH format for the application repository
gitops.operator.manifest_repository: # Git repository with SSH format for the manifests repository
gitops.operator.deployment_path: # Location of the deployment file in the manifests repository
gitops.operator.image_name: # The complete image name that the operator should be looking for
gitops.operator.namespace: # The namespace where this deployment is currently running
gitops.operator.ssh_key_name: # The name of the secret containing the SSH key
gitops.operator.ssh_key_namespace: # The namespace of the secret containing the SSH key
gitops.operator.notifications_secret_name: # OPTIONAL: Wether to try to send a Slack notification to the provided endpoint via the secret (the data field needs to be webhook-url)
gitops.operator.notifications_secret_namespace: # OPTIONAL: Wether to try to send a Slack notification to the provided endpoint via the secret (the data field needs to be webhook-url)
Note: you can create the secret as follows:
kubectl -n gitops-operator create secret generic ssh-key --from-file=ssh-privatekey=/home/user/.ssh/id_rsa
If you don't want the operator to be able to read all secrets you can limit it with RBAC, it will attempt to read only what you tell it to anyway.
You might be wondering why do you need an SSH key? short answer to fetch and write to your repository, why SSH? well it is a secure authentication mechanism and it is widely adopted making the operator provider independent, it doesn't matter which hosting solution you prefer it should still work the very same way as long as it supports SSH authentication.
In order to be able to send notifications (following the Slack format), you can create a secret like that (You will need to create a secret per namespace, where you app is deployed):
kubectl create secret generic webhook-secret -n define_ns --from-literal=webhook-url=https://hooks.slack.com/services/...
Apply manifests from here, then you can trigger it manually using port-forward: kubectl port-forward service/gitops-operator 8000:80
You can trigger the reconcile method from the following URL (explanation in the post/video, this is a hack, not a real reconcile method however it does the trick for this case):
$ curl 0.0.0.0:8000/reconcile
[
{
"container": "kainlite/gitops-operator",
"name": "gitops-operator",
"namespace": "default",
"annotations": {
"deployment.kubernetes.io/revision": "34",
"gitops.operator.app_repository": "[email protected]:kainlite/gitops-operator.git",
"gitops.operator.deployment_path": "app/00-deployment.yaml",
"gitops.operator.enabled": "true",
"gitops.operator.image_name": "kainlite/gitops-operator",
"gitops.operator.manifest_repository": "[email protected]:kainlite/gitops-operator-manifests.git",
"gitops.operator.namespace": "default",
"kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"annotations\":{\"gitops.operator.app_repository\":\"[email protected]:kainlite/gitops-operator.git\",\"gitops.operator.deployment_path\":\"app/00-deployment.yaml\",\"gitops.operator.enabled\":\"true\",\"gitops.operator.image_name\":\"kainlite/gitops-operator\",\"gitops.operator.manifest_repository\":\"[email protected]:kainlite/gitops-operator-manifests.git\",\"gitops.operator.namespace\":\"default\"},\"labels\":{\"app\":\"gitops-operator\",\"argocd.argoproj.io/instance\":\"gitops-operator\"},\"name\":\"gitops-operator\",\"namespace\":\"default\"},\"spec\":{\"replicas\":1,\"selector\":{\"matchLabels\":{\"app\":\"gitops-operator\"}},\"template\":{\"metadata\":{\"labels\":{\"app\":\"gitops-operator\"}},\"spec\":{\"containers\":[{\"image\":\"kainlite/gitops-operator:a57e6e3a195464a8bbbdc1bff3a6f70ed236154d\",\"imagePullPolicy\":\"Always\",\"livenessProbe\":{\"failureThreshold\":5,\"httpGet\":{\"path\":\"/health\",\"port\":\"http\"},\"periodSeconds\":15},\"name\":\"gitops-operator\",\"ports\":[{\"containerPort\":8000,\"name\":\"http\",\"protocol\":\"TCP\"}],\"readinessProbe\":{\"httpGet\":{\"path\":\"/reconcile\",\"port\":\"http\"},\"initialDelaySeconds\":60,\"periodSeconds\":120,\"timeoutSeconds\":60},\"resources\":{\"limits\":{\"cpu\":\"1000m\",\"memory\":\"1024Mi\"},\"requests\":{\"cpu\":\"500m\",\"memory\":\"100Mi\"}},\"volumeMounts\":[{\"mountPath\":\"/home/nonroot/.ssh/id_rsa_demo\",\"name\":\"my-ssh-key\",\"readOnly\":true,\"subPath\":\"ssh-privatekey\"}]}],\"serviceAccountName\":\"gitops-operator\",\"volumes\":[{\"name\":\"my-ssh-key\",\"secret\":{\"items\":[{\"key\":\"ssh-privatekey\",\"path\":\"ssh-privatekey\"}],\"secretName\":\"my-ssh-key\"}}]}}}}\n"
},
"version": "a57e6e3a195464a8bbbdc1bff3a6f70ed236154d",
"config": {
"enabled": true,
"namespace": "default",
"app_repository": "[email protected]:kainlite/gitops-operator.git",
"manifest_repository": "[email protected]:kainlite/gitops-operator-manifests.git",
"image_name": "kainlite/gitops-operator",
"deployment_path": "app/00-deployment.yaml"
}
}
]
- Locally against a cluster:
cargo watch
- In-cluster: edit and
tilt up
* - Docker build & import to kind:
just build && just import