{"id":22885045,"url":"https://github.com/mark8s/count","last_synced_at":"2026-04-11T08:06:35.430Z","repository":{"id":112106816,"uuid":"497625815","full_name":"mark8s/count","owner":"mark8s","description":"k8s controller  demo","archived":false,"fork":false,"pushed_at":"2022-05-30T10:31:17.000Z","size":747,"stargazers_count":0,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-31T17:59:42.524Z","etag":null,"topics":["code-generator","controller","crd","kubernetes"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mark8s.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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":"2022-05-29T15:12:02.000Z","updated_at":"2022-05-29T15:13:13.000Z","dependencies_parsed_at":"2023-07-31T22:15:06.700Z","dependency_job_id":null,"html_url":"https://github.com/mark8s/count","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/mark8s/count","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mark8s%2Fcount","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mark8s%2Fcount/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mark8s%2Fcount/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mark8s%2Fcount/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mark8s","download_url":"https://codeload.github.com/mark8s/count/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mark8s%2Fcount/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31673083,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-10T17:19:37.612Z","status":"online","status_checked_at":"2026-04-11T02:00:05.776Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["code-generator","controller","crd","kubernetes"],"created_at":"2024-12-13T19:31:15.888Z","updated_at":"2026-04-11T08:06:35.395Z","avatar_url":"https://github.com/mark8s.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# count\nk8s controller demo\n\n## controller\ncontroller的作用就是监听指定对象的新增、删除、修改等变化，针对这些变化做出相应的响应。\n\n![controller](./images/controller.png)\n\n如上图，API对象的变化会通过Informer存入队列（WorkQueue），在Controller中消费队列的数据做出响应，响应相关的具体代码就是我们要做的真正业务逻辑。\n\n## 编写一个controller的步骤\n\n1. 创建crd.yaml ，也就是你要想好你的crd到底要干什么\n\n2. 使用code-generator生成client、informers、listers代码\n\n3. 编写controller逻辑\n\n## How to use code-generator\n\n执行 hack/update-codegen.sh \n\n`update-codegen.sh` 命令含义，\n\n```shell\n../vendor/k8s.io/code-generator/generate-groups.sh \\\n  \"deepcopy,client,informer,lister\" \\\n  count/generated \\\n  count/pkg/apis \\\n  count:v1 \\\n  --go-header-file $(pwd)/boilerplate.go.txt \\\n  --output-base $(pwd)/../../\n```\n\n`../vendor/k8s.io/code-generator/generate-groups.sh \"deepcopy,client,informer,lister\"`: 使用vendor中的shell脚本生成deepcopy,client,informer,lister相关内容\n\n`count/generated`: `${MODULE}/${OUTPUT_PKG}`,`${MODULE}==go.mod中module,${OUTPUT_PKG}` 自己定义的生成代码的包名\n\n`count/pkg/apis`: `${MODULE}/${APIS_PKG},${APIS_PKG}`和apis目录保持一致\n\n`count:v1`: `${GROUP_VERSION}，GROUP==count,VERSION==v1`\n\n## 编译\n```shell\ngo build .\n./count -alsologtostderr=true\n```\n启动后，先后创建 crd和cr资源，然后再删除cr资源\n\n日志情况：\n```shell\n[root@biz-master-48 count]# ./count -alsologtostderr=true\nI0530 07:57:55.721030    4866 controller.go:63] Setting up event handlers\nI0530 07:57:55.721432    4866 controller.go:87] 开始controller业务，开始一次缓存数据同步\nW0530 07:57:55.732493    4866 reflector.go:324] pkg/mod/k8s.io/client-go@v0.24.1/tools/cache/reflector.go:167: failed to list *v1.Count: the server could not find the requested resource (get counts.demo.mark8s.io)\nE0530 07:57:55.732615    4866 reflector.go:138] pkg/mod/k8s.io/client-go@v0.24.1/tools/cache/reflector.go:167: Failed to watch *v1.Count: failed to list *v1.Count: the server could not find the requested resource (get counts.demo.mark8s.io)\nW0530 07:57:57.018605    4866 reflector.go:324] pkg/mod/k8s.io/client-go@v0.24.1/tools/cache/reflector.go:167: failed to list *v1.Count: the server could not find the requested resource (get counts.demo.mark8s.io)\nE0530 07:57:57.018646    4866 reflector.go:138] pkg/mod/k8s.io/client-go@v0.24.1/tools/cache/reflector.go:167: Failed to watch *v1.Count: failed to list *v1.Count: the server could not find the requested resource (get counts.demo.mark8s.io)\nW0530 07:58:00.125599    4866 reflector.go:324] pkg/mod/k8s.io/client-go@v0.24.1/tools/cache/reflector.go:167: failed to list *v1.Count: the server could not find the requested resource (get counts.demo.mark8s.io)\nE0530 07:58:00.125639    4866 reflector.go:138] pkg/mod/k8s.io/client-go@v0.24.1/tools/cache/reflector.go:167: Failed to watch *v1.Count: failed to list *v1.Count: the server could not find the requested resource (get counts.demo.mark8s.io)\nW0530 07:58:05.454530    4866 reflector.go:324] pkg/mod/k8s.io/client-go@v0.24.1/tools/cache/reflector.go:167: failed to list *v1.Count: the server could not find the requested resource (get counts.demo.mark8s.io)\nE0530 07:58:05.454561    4866 reflector.go:138] pkg/mod/k8s.io/client-go@v0.24.1/tools/cache/reflector.go:167: Failed to watch *v1.Count: failed to list *v1.Count: the server could not find the requested resource (get counts.demo.mark8s.io)\nW0530 07:58:14.658313    4866 reflector.go:324] pkg/mod/k8s.io/client-go@v0.24.1/tools/cache/reflector.go:167: failed to list *v1.Count: the server could not find the requested resource (get counts.demo.mark8s.io)\nE0530 07:58:14.658411    4866 reflector.go:138] pkg/mod/k8s.io/client-go@v0.24.1/tools/cache/reflector.go:167: Failed to watch *v1.Count: failed to list *v1.Count: the server could not find the requested resource (get counts.demo.mark8s.io)\nI0530 07:58:33.024376    4866 controller.go:92] worker启动\nI0530 07:58:33.024459    4866 controller.go:97] worker已经启动\nenqueueCount: obj \u0026{{ } {test-count  default  f702028e-363c-4959-9bc9-bee146b1d746 11980682 1 2022-05-30 07:58:42 +0000 UTC \u003cnil\u003e \u003cnil\u003e map[] map[kubectl.kubernetes.io/last-applied-configuration:{\"apiVersion\":\"demo.mark8s.io/v1\",\"kind\":\"Count\",\"metadata\":{\"annotations\":{},\"name\":\"test-count\",\"namespace\":\"default\"},\"spec\":{\"count\":3,\"name\":\"nginx\"}}\n] [] []  [{kubectl-client-side-apply Update demo.mark8s.io/v1 2022-05-30 07:58:42 +0000 UTC FieldsV1 {\"f:metadata\":{\"f:annotations\":{\".\":{},\"f:kubectl.kubernetes.io/last-applied-configuration\":{}}},\"f:spec\":{\".\":{},\"f:count\":{},\"f:name\":{}}} }]} { 0} {false}}\nI0530 07:58:42.136970    4866 controller.go:167] 这里是Count对象的期望状态: \u0026v1.Count{TypeMeta:v1.TypeMeta{Kind:\"\", APIVersion:\"\"}, ObjectMeta:v1.ObjectMeta{Name:\"test-count\", GenerateName:\"\", Namespace:\"default\", SelfLink:\"\", UID:\"f702028e-363c-4959-9bc9-bee146b1d746\", ResourceVersion:\"11980682\", Generation:1, CreationTimestamp:time.Date(2022, time.May, 30, 7, 58, 42, 0, time.Local), DeletionTimestamp:\u003cnil\u003e, DeletionGracePeriodSeconds:(*int64)(nil), Labels:map[string]string(nil), Annotations:map[string]string{\"kubectl.kubernetes.io/last-applied-configuration\":\"{\\\"apiVersion\\\":\\\"demo.mark8s.io/v1\\\",\\\"kind\\\":\\\"Count\\\",\\\"metadata\\\":{\\\"annotations\\\":{},\\\"name\\\":\\\"test-count\\\",\\\"namespace\\\":\\\"default\\\"},\\\"spec\\\":{\\\"count\\\":3,\\\"name\\\":\\\"nginx\\\"}}\\n\"}, OwnerReferences:[]v1.OwnerReference(nil), Finalizers:[]string(nil), ZZZ_DeprecatedClusterName:\"\", ManagedFields:[]v1.ManagedFieldsEntry{v1.ManagedFieldsEntry{Manager:\"kubectl-client-side-apply\", Operation:\"Update\", APIVersion:\"demo.mark8s.io/v1\", Time:time.Date(2022, time.May, 30, 7, 58, 42, 0, time.Local), FieldsType:\"FieldsV1\", FieldsV1:(*v1.FieldsV1)(0xc0001b5830), Subresource:\"\"}}}, Spec:v1.CountSpec{name:\"\", count:0}, Status:v1.CountStatus{healthy:false}} ...\nI0530 07:58:42.137272    4866 controller.go:168] 实际状态是从业务层面得到的，此处应该去的实际状态，与期望状态做对比，并根据差异做出响应(新增或者删除)\nI0530 07:58:42.137366    4866 controller.go:134] Successfully synced 'default/test-count'\nI0530 07:58:42.137926    4866 event.go:285] Event(v1.ObjectReference{Kind:\"Count\", Namespace:\"default\", Name:\"test-count\", UID:\"f702028e-363c-4959-9bc9-bee146b1d746\", APIVersion:\"demo.mark8s.io/v1\", ResourceVersion:\"11980682\", FieldPath:\"\"}): type: 'Normal' reason: 'Synced' Student synced successfully\nI0530 07:58:51.462978    4866 controller.go:159] Count对象被删除，请在这里执行实际的删除业务: default/test-count ...\nI0530 07:58:51.463008    4866 controller.go:134] Successfully synced 'default/test-count'\n^CI0530 07:58:53.802445    4866 controller.go:99] worker已经结束\n[root@biz-master-48 count]#\n```\n\n## 补充\n\n### k8s日志处理机制\n当集群中的 node 或 pod 异常时，大部分用户会使用 kubectl 查看对应的 events，那么 events 是从何而来？ ...\n\n我们可以使用`EventRecorder`为我们创建的自定义资源，添加Event记录，方便排查问题。效果如：\n```shell\n[root@biz-master-48 crd]# kubectl describe count test-count \nName:         test-count\nNamespace:    default\nLabels:       \u003cnone\u003e\nAnnotations:  \u003cnone\u003e\nAPI Version:  demo.mark8s.io/v1\nKind:         Count\nMetadata:\n  Creation Timestamp:  2022-05-30T08:49:40Z\n  Generation:          1\n  Managed Fields:\n    API Version:  demo.mark8s.io/v1\n    Fields Type:  FieldsV1\n    fieldsV1:\n      f:metadata:\n        f:annotations:\n          .:\n          f:kubectl.kubernetes.io/last-applied-configuration:\n      f:spec:\n        .:\n        f:count:\n        f:name:\n    Manager:         kubectl-client-side-apply\n    Operation:       Update\n    Time:            2022-05-30T08:49:40Z\n  Resource Version:  11989649\n  UID:               6e9e998c-e288-443e-ab5f-b70f156f598c\nSpec:\n  Count:  3\n  Name:   nginx\nEvents:\n  Type    Reason  Age   From              Message\n  ----    ------  ----  ----              -------\n  Normal  Synced  39s   count-controller  Count synced successfully\n```\n\n那么如何使用`EventRecorder`呢？ 先看下图\n\n![eventRecorder](./images/eventRecorder.png)\n\n代码逻辑：\n```shell\n    eventBroadcaster := record.NewBroadcaster()\n\teventBroadcaster.StartLogging(glog.Infof)\n\teventBroadcaster.StartRecordingToSink(\u0026typeCoreV1.EventSinkImpl{\n\t\tInterface: kubeClientSet.CoreV1().Events(\"\"),\n\t})\n\n\trecorder := eventBroadcaster.NewRecorder(scheme.Scheme, coreV1.EventSource{Component: \"count-controller\"})\n    recorder.Event(count, coreV1.EventTypeNormal, \"Synced\", \"Count synced successfully\")\n```\n`eventBroadcaster`是一个事件广播器，通过它提供了StartLogging和StartRecordingToSink两个事件处理函数，分别将event发送给log和apiserver。\n\nNewRecorder创建了recorder实例，它提供了Event方法供事件记录。\n\n\n### glog\n```go\nglog.V(4).Info(\"Creating event broadcaster\")\n```\nglog 可以指定日志的级别，如执行的时候指定 -v=4,此时，日志级别小于或等于 4 的日志将被打印出来\n\n完整的命令如：\n```shell\n$ ./count -alsologtostderr -v=4\n```\n\n### \n\n\n## 问题\n1.使用vendor\n```shell\nimport _ \"k8s.io/code-generator\"\n```\n```shell\ngo mod vendor\nchmod -R 777 vendor\n```\n\n## Reference\n[k8s自定义controller三部曲之一:创建CRD（Custom Resource Definition）](https://blog.csdn.net/boling_cavalry/article/details/88917818)\n\n[k8s自定义controller三部曲之二:自动生成代码](https://blog.csdn.net/boling_cavalry/article/details/88924194?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522165383645816781685390100%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D\u0026request_id=165383645816781685390100\u0026biz_id=0\u0026utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-3-88924194-null-null.nonecase\u0026utm_term=controller\u0026spm=1018.2226.3001.4450)\n\n[k8s自定义controller三部曲之三：编写controller代码](https://blog.csdn.net/boling_cavalry/article/details/88934063)\n\n[使用code-generator生成crd的clientset、informer、listers](https://xieys.club/code-generator-crd/)\n\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmark8s%2Fcount","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmark8s%2Fcount","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmark8s%2Fcount/lists"}