{"id":21712700,"url":"https://github.com/mhausenblas/operator-101","last_synced_at":"2025-09-10T14:45:08.136Z","repository":{"id":140043670,"uuid":"157555525","full_name":"mhausenblas/operator-101","owner":"mhausenblas","description":"A step-by-step walkthrough of bootstrapping a Kubernetes operator","archived":false,"fork":false,"pushed_at":"2018-11-16T10:01:49.000Z","size":4483,"stargazers_count":21,"open_issues_count":0,"forks_count":5,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-12T18:09:41.676Z","etag":null,"topics":["api","example","golang","kubernetes","operator","tutorial"],"latest_commit_sha":null,"homepage":"https://mhausenblas.info/operator-101/","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mhausenblas.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-11-14T13:48:59.000Z","updated_at":"2024-07-08T03:07:05.000Z","dependencies_parsed_at":null,"dependency_job_id":"9d20db2a-6d14-4fad-ab01-ebe6807a3d66","html_url":"https://github.com/mhausenblas/operator-101","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mhausenblas%2Foperator-101","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mhausenblas%2Foperator-101/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mhausenblas%2Foperator-101/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mhausenblas%2Foperator-101/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mhausenblas","download_url":"https://codeload.github.com/mhausenblas/operator-101/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248610338,"owners_count":21132921,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","example","golang","kubernetes","operator","tutorial"],"created_at":"2024-11-25T23:41:07.627Z","updated_at":"2025-04-12T18:09:49.005Z","avatar_url":"https://github.com/mhausenblas.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"## Bootstrapping a Kubernetes operator\n\nIn the following, I'm showing you how to bootstrap a Kubernetes operator using the [Operator SDK](https://github.com/operator-framework/operator-sdk).\nThe goal is to bootstrap an operator (custom resource and controller) that monitors a namespace for usage of the default service account.\nWe call this operator the no-defaults-policy operator, and its main task is to flag pods using the default service account (a security anti-pattern).\nStrictly speaking, this is not the best use case for an operator since it's not really about the life cycle management of an app: a number of alternative solutions, \nfrom a simple controller to a shell script using `kubectl` would do the same job. However, since it's conceptually easy to understand what the operator's job is and\nthe custom resources gives us a nice way to configure the actual behavior, the policy enforcement—for example, annotating the pod or a more extreme measure like deleting its owner, such as a deployment—we do it nevertheless.\nAlso, others also seem to be fine with applying the operator pattern in such a context, see for example the [rbac-manager/](https://reactiveops.github.io/rbac-manager/).\nMay the Kube gods have mercy on us.\n\nTable of contents:\n\n- [Setting the scene](#setting-the-scene)\n- [Deploying the operator locally](#local-deployment)\n- [Deploying the operator into a cluster](#in-cluster-deployment)\n- [Development of the custom controller](#development)\n- [References](#references)\n\n## Setting the scene\n\nAll the business below goes on in a dedicated namespace, we call it `ndp-demo`:\n\n```\n$ kubectl create ns ndp-demo\n```\n\nSTEP 1—As a preparation, [install the Operator SDK](https://github.com/operator-framework/operator-sdk#prerequisites) and then\nbootstrap the operator like so:\n\n```\n$ cd $GOPATH/src/github.com/mhausenblas\n$ operator-sdk new nodefpol-operator\n$ cd nodefpol-operator\n```\n\nAs a result we should see something like this:\n\n![STEP-1 outcome](img/STEP-1_outputof_new_app-operator.png)\n\nSee also [terminal output](STEP-1_outputof_new_app-operator.md) of above command.\n\nSTEP 2—Next, we add a new API for the custom resource `NoDefaultsPolicy`:\n\n```\n$ pwd\n/Users/mhausenblas/Dropbox/dev/work/src/github.com/mhausenblas/nodefpol-operator\n\n$ operator-sdk add api --api-version=nodefpol.k8space.io/v1alpha1 --kind=NoDefaultsPolicy\n```\n\nAs a result we should see something like this:\n\n![STEP-2 outcome](img/STEP-2_outputof_add_api.png)\n\nSee also [terminal output](STEP-2_outputof_add_api.md) of above command.\n\nSTEP 3—Now we add a new controller that watches for `NoDefaultsPolicy` resources:\n\n```\n$ operator-sdk add controller --api-version=nodefpol.k8space.io/v1alpha1 --kind=NoDefaultsPolicy\n```\n\nAs a result we should see something like this:\n\n![STEP-3 outcome](img/STEP-3_outputof_add_controller.png)\n\nSee also [terminal output](STEP-3_outputof_add_controller.md) of above command.\n\n## Local deployment\n\nSTEP 4—Install the custom resource definition (CRD) and launch the operator locally:\n\n```\n$ kubectl -n ndp-demo apply -f deploy/crds/nodefpol_v1alpha1_nodefaultspolicy_crd.yaml\n$ OPERATOR_NAME=nodefpol-operator operator-sdk up local --namespace \"ndp-demo\"\n```\n\nAs a result we should see something like this:\n\n![STEP-4 outcome](img/STEP-4_outputof_operator-sdk-up.png)\n\nSee also [terminal output](STEP-4_outputof_operator-sdk-up.md) of above command.\n\nNow it's time to create a `NoDefaultsPolicy` custom resource. Also, note that the default controller will watch for `NoDefaultsPolicy` objects for each custom resource we create:\n\n```\n$ kubectl -n ndp-demo apply -f deploy/crds/nodefpol_v1alpha1_nodefaultspolicy_cr.yaml\n```\n\n## In-cluster deployment\n\nIn order to deploy the operator into the cluster, we first build a container image and push it to a registry,\nand then create the necessary resources such as a service account, RBAC roles \u0026 bindings, the CRD itself, and a custom resource instance, \nand deploy the operator.\n\nSTEP 5—Starting off by building a container image and pushing it to a public registry (note: make sure you're logged into registry, in my case Quay.io, first):\n\n```\n$ operator-sdk build quay.io/mhausenblas/nodefpol\n$ docker push quay.io/mhausenblas/nodefpol\n```\n\nMake sure to update the operator manifest to use the built image name (`quay.io/mhausenblas/nodefpol` in my case):\n\n```\n$ sed -i \"\" 's|REPLACE_IMAGE|quay.io/mhausenblas/nodefpol|g' deploy/operator.yaml\n```\n\nSTEP 6—First, we set up a dedicated service account as well as the respective RBAC roles and bindings:\n\n```\n$ kubectl -n ndp-demo apply -f deploy/service_account.yaml\n$ kubectl -n ndp-demo apply -f deploy/role.yaml\n$ kubectl -n ndp-demo apply -f deploy/role_binding.yaml\n```\n\nSTEP 7—We install the custom resource definition (CRD), if not already done for local deployment:\n\n```\n$ kubectl -n ndp-demo apply -f deploy/crds/nodefpol_v1alpha1_nodefaultspolicy_crd.yaml\n```\n\nSTEP 8—Time to deploy the operator:\n\n```\n$ kubectl -n ndp-demo apply -f deploy/operator.yaml\n```\n\nSTEP 9—Now we create a `NoDefaultsPolicy` custom resource, if not already done for local deployment:\n\n```\n$ kubectl -n ndp-demo apply -f deploy/crds/nodefpol_v1alpha1_nodefaultspolicy_cr.yaml\n```\n\nTo clean up, getting rid of all the resources including the custom resources and CRD we installed, do:\n\n```\n$ kubectl delete ns ndp-demo\n```\n\n## Development\n\nThe Operator SDK creates a ton of boot-strapping code and config, however, now your work starts. \nTo start this process, find `//TODO(user)` annotations in the Go source code and start implementing your custom logic. \nFor example, in my VS Code environment, it looks something like this:\n\n![Starting points for coding the controller](img/custom-controller-dev.png)\n\n## References\n\nTo learn more on the topic, check out the following references:\n\n- [CLI reference](https://github.com/operator-framework/operator-sdk/blob/master/doc/sdk-cli-reference.md)\n- [User Guide](https://github.com/operator-framework/operator-sdk/blob/master/doc/user-guide.md)\n- [Building an operator for Kubernetes with operator-sdk](https://itnext.io/building-an-operator-for-kubernetes-with-operator-sdk-40a029ea056)\n- [A complete guide to Kubernetes Operator SDK](https://banzaicloud.com/blog/operator-sdk/)\n- [Make a Kubernetes Operator in 15 minutes with Helm](https://blog.openshift.com/make-a-kubernetes-operator-in-15-minutes-with-helm/)\n- [Operator Lifecycle Manager](https://itnext.io/wth-is-a-operator-lifecycle-manager-873cf1661b04) \n- [Building an Kubernetes Operator for Prometheus and Thanos](https://robszumski.com/building-an-operator/)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmhausenblas%2Foperator-101","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmhausenblas%2Foperator-101","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmhausenblas%2Foperator-101/lists"}