{"id":406095,"date":"2024-06-29T18:43:18","date_gmt":"2024-06-29T18:43:18","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=406095"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=406095","title":{"rendered":"<span>\u041c\u0438\u0440 \u043f\u0440\u0438\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0439 \u043f\u043e API-\u0441\u0435\u0440\u0432\u0435\u0440\u0443 Kubernetes. \u0427\u0430\u0441\u0442\u044c 2. \u041d\u0430\u0431\u043b\u044e\u0434\u0435\u043d\u0438\u0435 \u0438 \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/446\/913\/a17\/446913a171b09a149098ae918196b04b.png\" width=\"1080\" height=\"609\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/446\/913\/a17\/446913a171b09a149098ae918196b04b.png\"\/><\/figure>\n<p>\u0412 \u043d\u0430\u0448\u0435\u0439 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u043f\u0440\u043e \u043f\u0440\u0438\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u0441\u0435\u0440\u0432\u0435\u0440\u0430 Kubernetes API \u043c\u044b \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0435\u043b\u0438 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430 \u0438 \u0438\u0441\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u043b\u0438 \u0435\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u0443\u044e \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u0432 \u0434\u0435\u0440\u0435\u0432\u0435: etcd3. \u041e\u0434\u043d\u0430\u043a\u043e \u0432\u043d\u0438\u043c\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u043f\u0440\u043e\u0447\u0442\u0435\u043d\u0438\u0435 \u0441\u043d\u043e\u0441\u043e\u043a \u0432 \u044d\u0442\u043e\u043c \u043f\u043e\u0441\u0442\u0435 \u043f\u043e\u043a\u0430\u0437\u0430\u043b\u043e, \u0447\u0442\u043e \u043c\u044b \u0431\u044b\u043b\u0438 \u043d\u0435 \u0441\u043e\u0432\u0441\u0435\u043c \u0447\u0435\u0441\u0442\u043d\u044b\u043c\u0438, \u0433\u043e\u0432\u043e\u0440\u044f, \u0447\u0442\u043e etcd3 \u2014 \u0435\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u0430\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f.<\/p>\n<p>\u042d\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u043e\u0441\u043f\u043e\u0440\u0438\u0442\u044c, \u0432\u0435\u0434\u044c Cacher \u0442\u0430\u043a\u0436\u0435 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0442\u0435\u0445\u043d\u0438\u0447\u0435\u0441\u043a\u043e\u0439 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0435\u0439, \u0445\u043e\u0442\u044f \u0435\u043c\u0443 \u0438 \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0431\u0430\u0437\u043e\u0432\u0430\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f. \u041f\u043e\u0434\u0440\u043e\u0431\u043d\u043e \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0432 \u043e\u0434\u043d\u043e\u043c \u0438\u0437 \u0431\u0443\u0434\u0443\u0449\u0438\u0445 \u043f\u043e\u0441\u0442\u043e\u0432. \u0410 \u0441\u0435\u0439\u0447\u0430\u0441 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e \u0438\u0437\u0443\u0447\u0438\u043c \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0438 \u0435\u0433\u043e \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438.\u00a0<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/934\/15e\/43f\/93415e43f565a17db6ed85ce047607db.png\" width=\"1920\" height=\"1080\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/934\/15e\/43f\/93415e43f565a17db6ed85ce047607db.png\"\/><\/figure>\n<p><strong>\u041e \u0447\u0451\u043c \u043f\u043e\u0433\u043e\u0432\u043e\u0440\u0438\u043c \u0432 \u0441\u0442\u0430\u0442\u044c\u0435:<\/strong> <\/p>\n<ul>\n<li>\n<p>\u041d\u0430\u0431\u043b\u044e\u0434\u0435\u043d\u0438\u0435 (? \u00ab\u042f \u0445\u043e\u0447\u0443 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443 \u0438 \u0435\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435\u00bb);<\/p>\n<\/li>\n<li>\n<p>\u0412\u0441\u0435 \u0441\u043c\u043e\u0442\u0440\u044f\u0442;<\/p>\n<\/li>\n<li>\n<p>\u041a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 (? \u00ab\u041c\u043d\u0435 \u043f\u0440\u043e\u0441\u0442\u043e \u043d\u0443\u0436\u043d\u043e \u0440\u0435\u0448\u0435\u043d\u0438\u0435\u00bb);<\/p>\n<\/li>\n<li>\n<p><code>watchCache <\/code>;<\/p>\n<\/li>\n<li>\n<p><code>cacherListerWatcher <\/code>;<\/p>\n<\/li>\n<li>\n<p><code>Reflector<\/code>;<\/p>\n<\/li>\n<li>\n<p>\u0421\u0432\u044f\u0436\u0435\u043c \u0432\u0441\u0451 \u0432\u043c\u0435\u0441\u0442\u0435;\u00a0<\/p>\n<\/li>\n<li>\n<p>\u041c\u0435\u0442\u043e\u0434 <code>\u00a0Watch()<\/code>;<\/p>\n<\/li>\n<li>\n<p>\u0412\u0441\u0435 \u0441\u043c\u043e\u0442\u0440\u044f\u0442\u2026 \u0423\u0441\u043f\u0435\u0448\u043d\u043e (? \u00ab\u041f\u0440\u043e\u0441\u0442\u043e \u043f\u043e\u043a\u0430\u0436\u0438\u0442\u0435 \u043c\u043d\u0435 \u043a\u043e\u0434\u00bb).<\/p>\n<\/li>\n<\/ul>\n<h2>\u041d\u0430\u0431\u043b\u044e\u0434\u0435\u043d\u0438\u0435<\/h2>\n<p>\u041f\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043c \u043c\u044b \u0432\u0432\u0435\u0434\u0435\u043c \u043a\u0430\u043a\u043e\u0439-\u043b\u0438\u0431\u043e \u0442\u0438\u043f \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f, \u0441\u0442\u043e\u0438\u0442 \u0432\u0435\u0440\u043d\u0443\u0442\u044c\u0441\u044f \u043a \u0442\u043e\u043c\u0443 \u0444\u0430\u043a\u0442\u0443, \u0447\u0442\u043e <code>storage.Interface<\/code> \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442 \u0432 \u0441\u0435\u0431\u044f \u043c\u0435\u0442\u043e\u0434 <code>Watch<\/code>, \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f <code>etcd3<\/code>\u00a0<a href=\"https:\/\/github.com\/kubernetes\/kubernetes\/blob\/3154010eec3488dd64110e3ca6c4ba0b5ac61310\/staging\/src\/k8s.io\/apiserver\/pkg\/storaghttps:\/\/github.com\/kubernetes\/kubernetes\/blob\/3154010eec3488dd64110e3ca6c4ba0b5ac61310\/staging\/src\/k8s.io\/apiserver\/pkg\/storage\/etcd3\/store.go#L847e\/etcd3\/store.go#L847\"><u>\u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u0435\u0433\u043e<\/u><\/a>. \u0420\u0430\u043d\u0435\u0435 <a href=\"https:\/\/danielmangum.com\/posts\/k8s-asa-the-storage-interface\/#our-good-friend-etcd\"><u>\u043c\u044b \u0438\u0437\u0443\u0447\u0430\u043b\u0438<\/u><\/a> \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u043d\u0438\u044f \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439, \u043e\u0431\u0440\u0430\u0449\u0430\u044f\u0441\u044c \u043d\u0435\u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u043a <code>etcd<\/code> \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <code>etcdctl<\/code>. <code>etcd<\/code> \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u0435\u0442 <a href=\"https:\/\/etcd.io\/docs\/v3.5\/learning\/api\/#watch-api\"><u>Watch API<\/u><\/a>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0434\u0432\u0443\u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u0435 \u043f\u043e\u0442\u043e\u043a\u0438 gRPC \u0434\u043b\u044f \u0434\u043e\u0441\u0442\u0430\u0432\u043a\u0438 \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u043a\u043b\u0438\u0435\u043d\u0442\u0430\u043c \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0434\u043b\u044f <a href=\"https:\/\/grpc.io\/docs\/what-is-grpc\/core-concepts\/#bidirectional-streaming-rpc\"><u>\u043a\u043b\u044e\u0447\u0430<\/u><\/a>.<\/p>\n<p>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f etcd3 Watch <a href=\"https:\/\/github.com\/kubernetes\/kubernetes\/blob\/3154010eec3488dd64110e3ca6c4ba0b5ac61310\/staging\/src\/k8s.io\/apiserver\/pkg\/storage\/etcd3\/store.go#L856\"><u>\u0441\u043e\u0437\u0434\u0430\u0435\u0442 <\/u><\/a><code>watch.Interface<\/code>, \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u044b\u0439 <code>watcher<\/code>. \u041d\u043e\u0432\u044b\u0439 <code>watcher<\/code> \u0441\u043e\u0437\u0434\u0430\u0435\u0442\u0441\u044f \u043f\u0440\u0438 \u043a\u0430\u0436\u0434\u043e\u043c \u0432\u044b\u0437\u043e\u0432\u0435 <code>Watch<\/code>, \u0438 \u043a\u0430\u0436\u0434\u044b\u0439 <code>watcher<\/code>, \u043f\u043e \u0441\u0443\u0442\u0438, \u043f\u0440\u043e\u0441\u0442\u043e \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442 <code>etcd<\/code> Watch API \u0438 \u043f\u0435\u0440\u0435\u0441\u044b\u043b\u0430\u0435\u0442 \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u043f\u043e \u043a\u0430\u043d\u0430\u043b\u0443.<\/p>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0432\u043e\u0437\u044c\u043c\u0435\u043c \u043d\u0430\u0448\u0443 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0443 \u0438\u0437 <a href=\"https:\/\/habr.com\/ru\/companies\/southbridge\/articles\/727470\/\"><u>\u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u0433\u043e \u043f\u043e\u0441\u0442\u0430<\/u><\/a> \u0438 \u043c\u043e\u0434\u0438\u0444\u0438\u0446\u0438\u0440\u0443\u0435\u043c \u0435\u0451, \u0447\u0442\u043e\u0431\u044b \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0442\u044c \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0432\u043e \u0432\u0441\u0435\u0445 <code>ConfigMaps<\/code> \u0432 \u043a\u043b\u0430\u0441\u0442\u0435\u0440\u0435.<\/p>\n<p>\u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u043d\u0430\u0439\u0442\u0438 \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u0438 \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 PCI, \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u0434\u043b\u044f \u0441\u0432\u044f\u0437\u0438 \u0441 <code>etcd<\/code> \u0432 \u043a\u043b\u0430\u0441\u0442\u0435\u0440\u0435 <code>kind<\/code>, <a href=\"https:\/\/danielmangum.com\/posts\/k8s-asa-the-storage-interface\/#calling-the-storageinterface-directly\"><u>\u0437\u0434\u0435\u0441\u044c<\/u><\/a>.<\/p>\n<p><code>directwatch.go<\/code><\/p>\n<pre><code>package main  import ( \"context\" \"fmt\"  \"go.etcd.io\/etcd\/client\/pkg\/v3\/transport\" clientv3 \"go.etcd.io\/etcd\/client\/v3\" v1 \"k8s.io\/api\/core\/v1\" \"k8s.io\/apimachinery\/pkg\/runtime\" \"k8s.io\/apimachinery\/pkg\/runtime\/serializer\" \"k8s.io\/apiserver\/pkg\/storage\" \"k8s.io\/apiserver\/pkg\/storage\/etcd3\" \"k8s.io\/apiserver\/pkg\/storage\/value\/encrypt\/identity\" \"k8s.io\/kubernetes\/pkg\/apis\/core\" k8sv1 \"k8s.io\/kubernetes\/pkg\/apis\/core\/v1\" )  func main() { tlsConfig, err := (transport.TLSInfo{ CertFile:       \".\/pki\/etcd\/server.crt\", KeyFile:        \".\/pki\/etcd\/server.key\", TrustedCAFile:  \".\/pki\/etcd\/ca.crt\", ClientCertFile: \".\/pki\/apiserver-etcd-client.crt\", ClientKeyFile:  \".\/pki\/apiserver-etcd-client.key\", }).ClientConfig() if err != nil { panic(err) } c, err := clientv3.New(clientv3.Config{ Endpoints: []string{\"https:\/\/127.0.0.1:2379\"}, TLS:       tlsConfig, }) if err != nil { panic(err) }  scheme := runtime.NewScheme() k8sv1.AddToScheme(scheme) core.AddToScheme(scheme) f := serializer.NewCodecFactory(scheme) s := etcd3.New(c, f.CodecForVersions(nil, f.UniversalDecoder(), nil, k8sv1.SchemeGroupVersion), nil, \"registry\", v1.Resource(\"ConfigMap\"), identity.NewEncryptCheckTransformer(), true, etcd3.NewDefaultLeaseManagerConfig()) w, err := s.Watch(context.Background(), \"configmaps\", storage.ListOptions{ Predicate: storage.Everything, Recursive: true, }) if err != nil { panic(err) } for e := range w.ResultChan() { co, ok := e.Object.(*v1.ConfigMap) if !ok { panic(\"not a config map!\") } fmt.Printf(\"%s || %s\/%s\\n\", e.Type, co.Namespace, co.Name) } }<\/code><\/pre>\n<p>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043d\u0430 \u0442\u043e\u0442 \u0444\u0430\u043a\u0442, \u0447\u0442\u043e \u043c\u044b \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u043c \u0442\u0438\u043f\u044b \u0438\u0437 <code>k8s.io\/kubernetes<\/code> \u0441\u043a\u043e\u0440\u0435\u0435, \u0447\u0435\u043c <code>k8s.io\/api<\/code>. \u042d\u0442\u043e \u043d\u0443\u0436\u043d\u043e, \u0447\u0442\u043e\u0431\u044b \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u0435 <code>internal<\/code> \u0432\u0435\u0440\u0441\u0438\u0438 \u0432 \u043d\u0430\u0448\u0443 \u0436\u0435\u043b\u0430\u0435\u043c\u0443\u044e \u0432\u0435\u0440\u0441\u0438\u044e (<code>v1<\/code>). \u041c\u044b \u0442\u0430\u043a\u0436\u0435 \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u043c <code>nil \u00a0CodecForVersions,<\/code> \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u0434\u0435\u043a\u043e\u0434\u0438\u0440\u0443\u0435\u043c \u0438\u0445 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u0440\u0438 \u0447\u0442\u0435\u043d\u0438\u0438 \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u043f\u043e\u0442\u043e\u043a\u0430.<\/p>\n<p>\u0415\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u0437\u0430\u043f\u0443\u0449\u0435\u043d \u043a\u043b\u0430\u0441\u0442\u0435\u0440 <code>kind<\/code>, \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u043d\u0430\u0447\u0430\u0442\u044c \u043f\u0435\u0440\u0435\u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u043e\u0440\u0442\u043e\u0432 <code>etcd Pod<\/code>.<\/p>\n<pre><code>$ kubectl port-forward -n kube-system pod\/etcd-kind-control-plane 2379:2379<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043d\u0430\u0447\u0430\u0442\u044c \u043d\u0430\u0448\u0443 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0443.<\/p>\n<pre><code>$ go run directwatch.go ADDED || default\/kube-root-ca.crt ADDED || kube-node-lease\/kube-root-ca.crt ADDED || kube-public\/cluster-info ADDED || kube-public\/kube-root-ca.crt ADDED || kube-system\/coredns ADDED || kube-system\/extension-apiserver-authentication ADDED || kube-system\/kube-proxy ADDED || kube-system\/kube-root-ca.crt ADDED || kube-system\/kubeadm-config ADDED || kube-system\/kubelet-config ADDED || local-path-storage\/kube-root-ca.crt ADDED || local-path-storage\/local-path-config<\/code><\/pre>\n<p>\u041a\u0430\u043a \u0438 \u043e\u0436\u0438\u0434\u0430\u043b\u043e\u0441\u044c, \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c <code>ADDED<\/code> \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u0434\u043b\u044f \u0432\u0441\u0435\u0445 <code>ConfigMaps<\/code>, \u043d\u0430\u0445\u043e\u0434\u044f\u0449\u0438\u0445\u0441\u044f \u0432 \u0434\u0430\u043d\u043d\u044b\u0439 \u043c\u043e\u043c\u0435\u043d\u0442 \u0432 \u043a\u043b\u0430\u0441\u0442\u0435\u0440\u0435. \u041c\u043e\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c, \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0442\u044c \u0438 \u0443\u0434\u0430\u043b\u044f\u0442\u044c <code>ConfigMap<\/code>, \u0447\u0442\u043e\u0431\u044b \u0443\u0432\u0438\u0434\u0435\u0442\u044c, \u043a\u0430\u043a \u0434\u0440\u0443\u0433\u0438\u0435 \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u043e\u0442\u0440\u0430\u0436\u0430\u044e\u0442\u0441\u044f \u0432 \u043f\u043e\u0442\u043e\u043a\u0435 \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0430.<\/p>\n<pre><code>$ kubectl create configmap k8s-asa --from-literal hello=world configmap\/k8s-asa created  $ kubectl create configmap k8s-asa --from-literal hello=asa -o yaml --dry-run | kubectl apply -f - configmap\/k8s-asa configured  $ kubectl delete configmap k8s-asa configmap \"k8s-asa\" deleted<\/code><\/pre>\n<p>\u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043d\u0430 \u043a\u0430\u0436\u0434\u043e\u0435 \u0438\u0437 \u044d\u0442\u0438\u0445 \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u0432 \u043f\u043e\u0442\u043e\u043a\u0435 \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0430 \u0441 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c \u0442\u0438\u043f\u043e\u043c.<\/p>\n<pre><code>ADDED || default\/k8s-asa MODIFIED || default\/k8s-asa DELETED || default\/k8s-asa<\/code><\/pre>\n<h2>\u041e\u0431\u0449\u0435\u0435 \u043d\u0430\u0431\u043b\u044e\u0434\u0435\u043d\u0438\u0435<\/h2>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u0432 \u043b\u044e\u0431\u043e\u0435 \u0432\u0440\u0435\u043c\u044f, \u043a\u043e\u0433\u0434\u0430 \u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0443\u044e\u0449\u0438\u0439 \u043d\u0430\u0441 \u0442\u0438\u043f, \u0438 \u043d\u0435 \u043f\u043e\u0445\u043e\u0436\u0435, \u0447\u0442\u043e <code>etcd<\/code> \u0438\u043b\u0438 \u043d\u0430\u0448\u0430 \u043c\u0430\u043b\u0435\u043d\u044c\u043a\u0430\u044f \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0430 \u0441\u0438\u043b\u044c\u043d\u043e \u00ab\u043f\u043e\u0442\u0435\u044e\u0442\u00bb. \u041d\u043e \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0432\u0437\u0433\u043b\u044f\u043d\u0435\u043c \u043d\u0430 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u0435\u043b\u0438 <code>etcd<\/code>, \u0447\u0442\u043e\u0431\u044b \u0443\u0431\u0435\u0434\u0438\u0442\u044c\u0441\u044f \u0432 \u044d\u0442\u043e\u043c. \u041c\u044b \u043c\u043e\u0436\u0435\u043c \u043d\u0430\u0439\u0442\u0438 \u043e\u0442\u043a\u0440\u044b\u0442\u044b\u0439 \u0430\u0434\u0440\u0435\u0441 \u043c\u0435\u0442\u0440\u0438\u043a \u0434\u043b\u044f <code>etcd<\/code> \u0432 \u043d\u0430\u0448\u0435\u043c \u043a\u043b\u0430\u0441\u0442\u0435\u0440\u0435 <code>kind<\/code>, \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0432 \u043a\u043e\u043c\u0430\u043d\u0434\u0443 \u0434\u043b\u044f \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0430 \u0432 \u043c\u043e\u0434\u0443\u043b\u0435 <code>Pod<\/code>.<\/p>\n<pre><code>$ kubectl get pod -n kube-system etcd-kind-control-plane -o=jsonpath={.spec.containers[0].command} | jq .  [   \"etcd\",   \"--advertise-client-urls=https:\/\/172.19.0.2:2379\",   \"--cert-file=\/etc\/kubernetes\/pki\/etcd\/server.crt\",   \"--client-cert-auth=true\",   \"--data-dir=\/var\/lib\/etcd\",   \"--experimental-initial-corrupt-check=true\",   \"--experimental-watch-progress-notify-interval=5s\",   \"--initial-advertise-peer-urls=https:\/\/172.19.0.2:2380\",   \"--initial-cluster=kind-control-plane=https:\/\/172.19.0.2:2380\",   \"--key-file=\/etc\/kubernetes\/pki\/etcd\/server.key\",   \"--listen-client-urls=https:\/\/127.0.0.1:2379,https:\/\/172.19.0.2:2379\",   \"--listen-metrics-urls=http:\/\/127.0.0.1:2381\",   \"--listen-peer-urls=https:\/\/172.19.0.2:2380\",   \"--name=kind-control-plane\",   \"--peer-cert-file=\/etc\/kubernetes\/pki\/etcd\/peer.crt\",   \"--peer-client-cert-auth=true\",   \"--peer-key-file=\/etc\/kubernetes\/pki\/etcd\/peer.key\",   \"--peer-trusted-ca-file=\/etc\/kubernetes\/pki\/etcd\/ca.crt\",   \"--snapshot-count=10000\",   \"--trusted-ca-file=\/etc\/kubernetes\/pki\/etcd\/ca.crt\" ]<\/code><\/pre>\n<p><code>--listen-metrics-urls<\/code> \u00a0\u2014 \u0442\u043e, \u0447\u0442\u043e \u043c\u044b \u0438\u0449\u0435\u043c .<code>etcd<\/code> \u0431\u0443\u0434\u0435\u0442 \u043e\u0431\u0441\u043b\u0443\u0436\u0438\u0432\u0430\u0442\u044c <a href=\"https:\/\/danielmangum.com\/posts\/k8s-asa-watching-and-caching\/\"><u>\u043f\u043e\u043a\u0430\u0437\u0430\u0442\u0435\u043b\u0438 <\/u><\/a>Prometheus \u0432 \u043a\u043e\u043d\u0435\u0447\u043d\u043e\u0439 \u0442\u043e\u0447\u043a\u0435 <code>\/metrics<\/code> \u043f\u043e \u044d\u0442\u043e\u043c\u0443 \u0430\u0434\u0440\u0435\u0441\u0443. \u0421\u043e\u0437\u0434\u0430\u0451\u043c \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u0439 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0439 \u0444\u0430\u0439\u043b Prometheus, \u0447\u0442\u043e\u0431\u044b \u0434\u0430\u0442\u044c \u043a\u043e\u043c\u0430\u043d\u0434\u0443 \u0441\u0431\u043e\u0440\u0449\u0438\u043a\u0443 \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u043e\u0447\u0438\u0441\u0442\u043a\u0443 <code>etcd<\/code>.<\/p>\n<p><code>prometheus.yml<\/code><\/p>\n<pre><code>global:   scrape_interval: 1s scrape_configs:   - job_name: etcd     static_configs:     - targets: ['127.0.0.1:2379']<\/code><\/pre>\n<p>\u041f\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043c \u043c\u044b \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u043c Prometheus, \u0443\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u043a\u043e\u043d\u0435\u0447\u043d\u043e\u0439 \u0442\u043e\u0447\u043a\u0435 <code>etcd metrics<\/code>. \u041f\u0435\u0440\u0435\u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u043e\u0440\u0442\u043e\u0432 \u0432 \u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0441\u0442\u0432\u043e \u0438\u043c\u0435\u043d <code>host network<\/code> \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u0442 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c Prometheus \u0432 \u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0441\u0442\u0432\u0435 \u0438\u043c\u0435\u043d <code>host network<\/code>, \u0447\u0442\u043e \u0443\u043f\u0440\u043e\u0449\u0430\u0435\u0442 \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u043f\u0430\u043d\u0435\u043b\u0438 \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433\u0430 \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435.<\/p>\n<p>\u041f\u0435\u0440\u0435\u043d\u0435\u0441\u0438\u0442\u0435 \u043a\u043e\u043d\u0435\u0447\u043d\u0443\u044e \u0442\u043e\u0447\u043a\u0443 <code>etcd metrics<\/code>.<\/p>\n<pre><code>$ kubectl port-forward -n kube-system pod\/etcd-kind-control-plane 2381:2381<\/code><\/pre>\n<p>\u0417\u0430\u0442\u0435\u043c \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440 Prometheus, \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0451\u043d\u043d\u044b\u0439 \u043a \u0441\u0435\u0442\u0438 \u0445\u043e\u0441\u0442\u0430.  <\/p>\n<pre><code>$ docker run --net=host --rm -v $(pwd)\/prometheus.yml:\/etc\/prometheus\/prometheus.yml prom\/prometheus<\/code><\/pre>\n<p>\u041f\u0430\u043d\u0435\u043b\u044c \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433\u0430 Prometheus \u0442\u0435\u043f\u0435\u0440\u044c \u0434\u043e\u043b\u0436\u043d\u0430 \u0431\u044b\u0442\u044c \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u0430 \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435 \u043f\u043e \u0430\u0434\u0440\u0435\u0441\u0443 <code>127.0.0.1:9090<\/code>. \u041c\u044b \u043c\u043e\u0436\u0435\u043c \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0433\u0440\u0430\u0444\u0438\u043a \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u0435\u043b\u0435\u0439, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 <code>etcd<\/code>, \u0432\u0432\u0435\u0434\u044f \u0437\u0430\u043f\u0440\u043e\u0441. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, <code>irate(process_cpu_seconds_total{job=\"etcd\"}[5m])<\/code> \u043f\u043e\u043a\u0430\u0436\u0435\u0442 \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c \u0443\u0432\u0435\u043b\u0438\u0447\u0435\u043d\u0438\u044f \u043e\u0431\u0449\u0435\u0433\u043e \u0441\u0438\u0441\u0442\u0435\u043c\u043d\u043e\u0433\u043e \u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0433\u043e \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0440\u043d\u043e\u0433\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438. \u0415\u0441\u043b\u0438 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0443 \u0441\u0435\u0439\u0447\u0430\u0441, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442 \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u044b \u0434\u043b\u044f <code>ConfigMaps<\/code>, \u043c\u044b \u043d\u0435 \u0443\u0432\u0438\u0434\u0438\u043c \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0433\u043e \u0432\u043b\u0438\u044f\u043d\u0438\u044f \u043d\u0430 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0440.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/3a1\/e4c\/b70\/3a1e4cb705e9372e9e24451daed74cf6.png\" width=\"3836\" height=\"2000\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/3a1\/e4c\/b70\/3a1e4cb705e9372e9e24451daed74cf6.png\"\/><\/figure>\n<p>\u0414\u0438\u0430\u043f\u0430\u0437\u043e\u043d \u043f\u043e \u043e\u0441\u0438 Y \u0441\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043e\u0442 <code>0.03<\/code><em> \u0434\u043e <\/em><code>0.23<\/code>.<\/p>\n<p><strong>\u041f\u0440\u0438\u043c\u0435\u0447\u0430\u043d\u0438\u0435:<\/strong> \u041f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u043c\u044b \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c <code>etcd<\/code> \u0432 \u043a\u043b\u0430\u0441\u0442\u0435\u0440\u0435 <code>kind<\/code>, \u0441\u0435\u0440\u0432\u0435\u0440 API Kubernetes \u0438 \u0434\u0440\u0443\u0433\u0438\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0443\u044e\u0442 \u0441 \u043d\u0438\u043c \u0432 \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u043a \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0435 \u043e\u0442 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u044b.<\/p>\n<p>\u042d\u0442\u043e\u0433\u043e \u0438 \u0441\u043b\u0435\u0434\u043e\u0432\u0430\u043b\u043e \u043e\u0436\u0438\u0434\u0430\u0442\u044c: \u043e\u0434\u043d\u0438 \u0447\u0430\u0441\u044b \u0432\u0440\u044f\u0434 \u043b\u0438 \u043e\u043a\u0430\u0436\u0443\u0442 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0435 \u0432\u043b\u0438\u044f\u043d\u0438\u0435. \u041d\u043e \u0435\u0441\u043b\u0438 \u043c\u044b \u0443\u0432\u0435\u043b\u0438\u0447\u0438\u043c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0447\u0430\u0441\u043e\u0432, \u0442\u043e \u043d\u0430\u0447\u043d\u0435\u043c \u0441\u0442\u0430\u043b\u043a\u0438\u0432\u0430\u0442\u044c\u0441\u044f \u0441 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430\u043c\u0438. \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u043c \u043d\u0430\u0448\u0443 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0443 \u0442\u0430\u043a, \u0447\u0442\u043e\u0431\u044b \u043e\u043d\u0430 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u043b\u0430 10 000 \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u043e\u0432 \u043d\u0430 <code>ConfigMaps<\/code>.<\/p>\n<p><code>manydirectwatch.go<\/code><\/p>\n<pre><code>package main  import ( \"context\" \"fmt\" \"os\" \"os\/signal\"  \"go.etcd.io\/etcd\/client\/pkg\/v3\/transport\" clientv3 \"go.etcd.io\/etcd\/client\/v3\" v1 \"k8s.io\/api\/core\/v1\" \"k8s.io\/apimachinery\/pkg\/runtime\" \"k8s.io\/apimachinery\/pkg\/runtime\/serializer\" \"k8s.io\/apiserver\/pkg\/storage\" \"k8s.io\/apiserver\/pkg\/storage\/etcd3\" \"k8s.io\/apiserver\/pkg\/storage\/value\/encrypt\/identity\" \"k8s.io\/kubernetes\/pkg\/apis\/core\" k8sv1 \"k8s.io\/kubernetes\/pkg\/apis\/core\/v1\" )  func main() { tlsConfig, err := (transport.TLSInfo{ CertFile:       \".\/pki\/etcd\/server.crt\", KeyFile:        \".\/pki\/etcd\/server.key\", TrustedCAFile:  \".\/pki\/etcd\/ca.crt\", ClientCertFile: \".\/pki\/apiserver-etcd-client.crt\", ClientKeyFile:  \".\/pki\/apiserver-etcd-client.key\", }).ClientConfig() if err != nil { panic(err) } c, err := clientv3.New(clientv3.Config{ Endpoints: []string{\"https:\/\/127.0.0.1:2379\"}, TLS:       tlsConfig, }) if err != nil { panic(err) }  scheme := runtime.NewScheme() k8sv1.AddToScheme(scheme) core.AddToScheme(scheme) f := serializer.NewCodecFactory(scheme) s := etcd3.New(c, f.CodecForVersions(nil, f.UniversalDecoder(), nil, k8sv1.SchemeGroupVersion), nil, \"registry\", v1.Resource(\"ConfigMap\"), identity.NewEncryptCheckTransformer(), true, etcd3.NewDefaultLeaseManagerConfig()) for i := 0; i &lt; 10000; i++ { w, err := s.Watch(context.Background(), \"configmaps\", storage.ListOptions{ Predicate: storage.Everything, Recursive: true, }) if err != nil { panic(err) } go func() { for e := range w.ResultChan() { co, ok := e.Object.(*v1.ConfigMap) if !ok { panic(\"not a config map!\") } fmt.Printf(\"%s || %s\/%s\\n\", e.Type, co.Namespace, co.Name) } }() }  stop := make(chan os.Signal) signal.Notify(stop, os.Interrupt) &lt;-stop }<\/code><\/pre>\n<p>\u0415\u0441\u043b\u0438 \u043c\u044b \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u043c \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0443 \u0441\u0435\u0439\u0447\u0430\u0441, \u0442\u043e \u0434\u043e\u043b\u0436\u043d\u044b \u0443\u0432\u0438\u0434\u0435\u0442\u044c, \u0447\u0442\u043e \u0432\u0441\u0435 10 000 \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u043e\u0432 \u043f\u043e\u043b\u0443\u0447\u0430\u044e\u0442 \u0441\u043f\u0438\u0441\u043e\u043a \u0432\u0441\u0435\u0445 <code>ConfigMaps<\/code> \u0432 \u043a\u043b\u0430\u0441\u0442\u0435\u0440\u0435. \u041a\u043e\u0433\u0434\u0430 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043d\u043e\u0432\u0443\u044e <code>ConfigMap<\/code>, \u043c\u044b \u0441\u043c\u043e\u0436\u0435\u043c \u0443\u0432\u0438\u0434\u0435\u0442\u044c, \u0447\u0442\u043e \u0432\u0441\u0435 \u0447\u0430\u0441\u044b \u043f\u043e\u043b\u0443\u0447\u0430\u044e\u0442 \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u044f.<\/p>\n<pre><code>$ go run manysimplewatch.go ... ADDED || kube-system\/kube-proxy ADDED || kube-system\/kube-root-ca.crt ADDED || kube-system\/kubeadm-config ADDED || kube-system\/kubelet-config ADDED || local-path-storage\/kube-root-ca.crt ADDED || local-path-storage\/local-path-config ADDED || kube-system\/kube-root-ca.crt ADDED || kube-system\/kubeadm-config ADDED || kube-system\/kubelet-config ADDED || local-path-storage\/kube-root-ca.crt ADDED || local-path-storage\/local-path-config ...<\/code><\/pre>\n<pre><code>$ kubectl create configmap testing configmap\/testing created<\/code><\/pre>\n<pre><code>... ADDED || default\/testing ADDED || default\/testing ADDED || default\/testing ADDED || default\/testing ADDED || default\/testing ADDED || default\/testing ADDED || default\/testing ADDED || default\/testing ADDED || default\/testing ADDED || default\/testing ...<\/code><\/pre>\n<p>\u041e\u0433\u043b\u044f\u0434\u044b\u0432\u0430\u044f\u0441\u044c \u043d\u0430\u0437\u0430\u0434 \u043d\u0430 \u043d\u0430\u0448\u0438 \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u0435\u043b\u0438 <code>etcd<\/code>, \u043c\u044b \u0432\u0438\u0434\u0438\u043c \u0437\u043d\u0430\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u0441\u043a\u0430\u0447\u043e\u043a \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0440\u0430 \u043f\u0440\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0435 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u044b. \u042d\u0442\u043e \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442 \u0431\u0435\u0441\u043f\u043e\u043a\u043e\u0439\u0441\u0442\u0432\u043e, \u043d\u043e, \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e, \u0435\u0449\u0435 \u0431\u043e\u043b\u0435\u0435 \u043d\u0435\u043f\u0440\u0438\u044f\u0442\u043d\u044b\u043c \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043e\u0431\u044a\u0435\u043c \u0440\u0430\u0431\u043e\u0442\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u044b\u043b \u043f\u0440\u043e\u0434\u0435\u043b\u0430\u043d \u043f\u0440\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 \u043d\u043e\u0432\u043e\u0439 <code>ConfigMap<\/code>. \u041f\u043e\u043a\u0430 \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u043c\u043e\u0434\u0435\u043b\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0432 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0435, \u0441\u0435\u0440\u0432\u0435\u0440 Kubernetes API \u043e\u0431\u0441\u043b\u0443\u0436\u0438\u0432\u0430\u0435\u0442 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043d\u0430 \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440 \u043e\u0442 \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u0432. \u0423\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0442\u044c \u043d\u0430\u0431\u043b\u044e\u0434\u0435\u043d\u0438\u0435 \u0432 <code>etcd<\/code> \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0438\u0437 \u043d\u0438\u0445 \u043d\u0435\u0442 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438, \u0432\u0435\u0434\u044c \u0441\u0435\u0440\u0432\u0435\u0440\u0443 API \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u043d\u043e \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u0435, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0438\u043d\u0444\u043e\u0440\u043c\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0432\u0441\u0435\u0445 \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u0432.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/c9c\/ee7\/777\/c9cee777704cc14b9c2e085f1788b3d0.png\" width=\"3830\" height=\"2016\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/c9c\/ee7\/777\/c9cee777704cc14b9c2e085f1788b3d0.png\"\/><\/figure>\n<p>\u0414\u0438\u0430\u043f\u0430\u0437\u043e\u043d \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u043f\u043e \u043e\u0441\u0438 Y \u0441\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043e\u0442 <code>0,00<\/code> \u0434\u043e <code>3,00<\/code>.<\/p>\n<p>\u041f\u0435\u0440\u0432\u044b\u0439 \u0431\u043e\u043b\u044c\u0448\u043e\u0439 \u0432\u0441\u043f\u043b\u0435\u0441\u043a \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u043c\u043e\u043c\u0435\u043d\u0442\u0443 \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u043d\u0430\u0448\u0435\u0439 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u044b. \u0412\u0442\u043e\u0440\u043e\u0439 \u0432\u0441\u043f\u043b\u0435\u0441\u043a, \u0445\u043e\u0442\u044f \u0438 \u043c\u0435\u043d\u044c\u0448\u0438\u0439, \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044e \u0435\u0434\u0438\u043d\u043e\u0439 <code>ConfigMap<\/code> \u0441 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u044b\u043c\u0438 10 000 \u043d\u0430\u0431\u043b\u044e\u0434\u0435\u043d\u0438\u044f\u043c\u0438.<\/p>\n<h2>\u041a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435<\/h2>\n<p>\u041a\u0430\u043a \u0432\u044b \u0434\u043e\u0433\u0430\u0434\u0430\u043b\u0438\u0441\u044c, \u0441\u0435\u0440\u0432\u0435\u0440 API Kubernetes \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u044e \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0434\u043b\u044f \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u044d\u0442\u043e\u0439 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b, \u0438 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u0441\u043e \u0441\u043b\u043e\u0435\u043c \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0432\u043e \u043c\u043d\u043e\u0433\u043e\u043c \u043f\u043e\u0445\u043e\u0436\u0435 \u043d\u0430 \u043f\u0440\u044f\u043c\u043e\u0439 \u0432\u044b\u0437\u043e\u0432 <code>etcd3 storage.Interface<\/code>. \u0424\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438, \u0443\u0440\u043e\u0432\u0435\u043d\u044c \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u0442 <code>storage.Interface<\/code> \u0442\u043e\u0436\u0435.<\/p>\n<p>\u041f\u0440\u0438 \u043f\u043e\u0441\u0442\u0440\u043e\u0435\u043d\u0438\u0438 <code>cacher.Cacher<\/code> \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u0442\u0441\u044f \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 <code>Config<\/code>, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442 \u0432 \u0441\u0435\u0431\u044f <code>storage.Interface<\/code>. \u041a\u0430\u043a \u0443\u043a\u0430\u0437\u0430\u043d\u043e \u0432 <a href=\"https:\/\/github.com\/kubernetes\/kubernetes\/blob\/e818649c10f29bc80b874889a448da4023918330\/staging\/src\/k8s.io\/apiserver\/pkg\/storage\/cacher\/cacher.go#L241\"><u>\u0441\u0442\u0440\u043e\u043a\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430<\/u><\/a>.\u00a0<\/p>\n<pre><code>\/\/ Cacher implements storage.Interface (although most of the calls are just \/\/ delegated to the underlying storage).<\/code><\/pre>\n<p>\u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 <code>etcd<\/code> \u0438\u043d\u0444\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u0442 \u043d\u0430\u0431\u043b\u044e\u0434\u0430\u0442\u0435\u043b\u044f \u043e \u0441\u043e\u0431\u044b\u0442\u0438\u044f\u0445 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f (<code>ADDED<\/code>), <a href=\"https:\/\/github.com\/kubernetes\/kubernetes\/blob\/e818649c10f29bc80b874889a448da4023918330\/staging\/src\/k8s.io\/apiserver\/pkg\/storage\/cacher\/cacher.go#L460\"><u>\u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f<\/u> <\/a><code>Create() Cacher<\/code> \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442 <code>storage Create()<\/code>.<\/p>\n<pre><code>\/\/ Create implements storage.Interface. func (c *Cacher) Create(ctx context.Context, key string, obj, out runtime.Object, ttl uint64) error { return c.storage.Create(ctx, key, obj, out, ttl) }<\/code><\/pre>\n<p>\u0414\u0440\u0443\u0433\u0438\u0435 \u043c\u0435\u0442\u043e\u0434\u044b \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u044e\u0442 \u043d\u0435\u0437\u043d\u0430\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u043f\u0435\u0440\u0435\u0434 \u0432\u044b\u0437\u043e\u0432\u043e\u043c \u0431\u0430\u0437\u043e\u0432\u043e\u0433\u043e <code>storage<\/code>, \u043d\u043e <code>Watch()<\/code> \u0432\u043e\u043e\u0431\u0449\u0435 \u043d\u0435 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0443\u0435\u0442 \u0441 \u043d\u0438\u043c \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e. \u0412\u043c\u0435\u0441\u0442\u043e \u044d\u0442\u043e\u0433\u043e \u043e\u043d \u0441\u043e\u0437\u0434\u0430\u0435\u0442 \u043d\u043e\u0432\u044b\u0439 <code>cacheWatcher<\/code> \u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442 \u0441\u0432\u043e\u0439 \u0446\u0438\u043a\u043b \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0441\u043e\u0431\u044b\u0442\u0438\u0439. \u041f\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043c \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u0442\u044c\u0441\u044f \u0432 \u0442\u043e\u043c, \u043a\u0430\u043a \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 <code>cacheWatcher<\/code>, \u043d\u0443\u0436\u043d\u043e \u043f\u043e\u043d\u044f\u0442\u044c, \u043a\u0430\u043a \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0438\u0437 <code>etcd<\/code> \u0432\u0441\u0435\u043c \u043d\u0430\u0431\u043b\u044e\u0434\u0430\u0442\u0435\u043b\u044f\u043c, \u0437\u0430\u043f\u0443\u0449\u0435\u043d\u043d\u044b\u043c \u044d\u0442\u0438\u043c \u043c\u0435\u0442\u043e\u0434\u043e\u043c. \u041f\u0440\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 Cacher \u0441\u043e\u0437\u0434\u0430\u044e\u0442\u0441\u044f \u0442\u0440\u0438 \u0432\u0430\u0436\u043d\u044b\u0445 \u0431\u0430\u0437\u043e\u0432\u044b\u0445 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430, \u0434\u0432\u0430 \u0438\u0437 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u2014 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043e\u0431\u0449\u0438\u0445 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u043e\u0432 \u0432 \u043f\u0430\u043a\u0435\u0442\u0435 <code>client-go cache<\/code>:<\/p>\n<ul>\n<li>\n<p><code>watchCache<\/code> \u2014 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0435\u0439 <code>cache.Store<\/code>;<\/p>\n<\/li>\n<li>\n<p><code>cacherListerWatcher<\/code> \u2014 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0435\u0439 <code>cache.ListerWatcher<\/code>;<\/p>\n<\/li>\n<li>\n<p><code>Reflector<\/code> \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0434\u0432\u0443\u0445 \u0440\u0430\u043d\u0435\u0435 \u0443\u043f\u043e\u043c\u044f\u043d\u0443\u0442\u044b\u0445 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u043e\u0432 \u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0439 \u0434\u043b\u044f \u0437\u0430\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u043f\u0435\u0440\u0432\u043e\u0433\u043e.<\/p>\n<\/li>\n<\/ul>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043a\u0430\u0436\u0434\u044b\u0439 \u0438\u0437 \u043d\u0438\u0445 \u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e.<\/p>\n<h2>watchCache<\/h2>\n<p><code>watchCache<\/code> \u00a0\u2014 \u043c\u0435\u0441\u0442\u043e, \u0433\u0434\u0435 \u0445\u0440\u0430\u043d\u0438\u0442\u0441\u044f \u0444\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u043a\u044d\u0448 \u0441\u043e\u0431\u044b\u0442\u0438\u0439, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u0435\u0440\u0435\u0434\u0430\u044e\u0442\u0441\u044f \u043d\u0430\u0431\u043b\u044e\u0434\u0430\u0442\u0435\u043b\u044f\u043c. \u042d\u0442\u043e \u0441\u043a\u043e\u043b\u044c\u0437\u044f\u0449\u0435\u0435 \u043e\u043a\u043d\u043e \u0441 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c\u044e \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0440\u0430\u0437\u043c\u0435\u0440\u0430. \u042d\u0442\u043e\u00a0 \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442, \u0447\u0442\u043e \u043f\u0440\u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0438 \u043d\u043e\u0432\u043e\u0433\u043e \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430 \u043c\u044b \u043b\u0438\u0431\u043e \u0443\u0434\u0430\u043b\u044f\u0435\u043c \u0441\u0430\u043c\u044b\u0439 \u0441\u0442\u0430\u0440\u044b\u0439, \u043b\u0438\u0431\u043e \u0443\u0432\u0435\u043b\u0438\u0447\u0438\u0432\u0430\u0435\u043c \u0440\u0430\u0437\u043c\u0435\u0440 \u043a\u044d\u0448\u0430, \u0447\u0442\u043e\u0431\u044b \u0432\u043c\u0435\u0441\u0442\u0438\u0442\u044c \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043d\u043e\u0432\u043e\u0433\u043e \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430. \u041a\u0430\u043a \u0443\u043f\u043e\u043c\u0438\u043d\u0430\u043b\u043e\u0441\u044c \u0440\u0430\u043d\u0435\u0435, <code>watchCache<\/code> \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u0442 <code>cache.Store<\/code>, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u043c\u0435\u0442\u043e\u0434\u044b.  <\/p>\n<pre><code>type Store interface {  \/\/ Add adds the given object to the accumulator associated with the given object's key Add(obj interface{}) error  \/\/ Update updates the given object in the accumulator associated with the given object's key Update(obj interface{}) error  \/\/ Delete deletes the given object from the accumulator associated with the given object's key Delete(obj interface{}) error  \/\/ List returns a list of all the currently non-empty accumulators List() []interface{}  \/\/ ListKeys returns a list of all the keys currently associated with non-empty accumulators ListKeys() []string  \/\/ Get returns the accumulator associated with the given object's key Get(obj interface{}) (item interface{}, exists bool, err error)  \/\/ GetByKey returns the accumulator associated with the given key GetByKey(key string) (item interface{}, exists bool, err error)  \/\/ Replace will delete the contents of the store, using instead the \/\/ given list. Store takes ownership of the list, you should not reference \/\/ it after calling this function. Replace([]interface{}, string) error  \/\/ Resync is meaningless in the terms appearing here but has \/\/ meaning in some implementations that have non-trivial \/\/ additional behavior (e.g., DeltaFIFO). Resync() error }<\/code><\/pre>\n<p>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 <code>Add()<\/code>, <code>Update()<\/code>, \u0438 <code>Delete()<\/code> \u0441\u043e\u0437\u0434\u0430\u044e\u0442 <code>watch.Event<\/code>, \u0437\u0430\u0442\u0435\u043c \u043f\u0435\u0440\u0435\u0434\u0430\u0439\u0442\u0435 \u0435\u0433\u043e \u043c\u0435\u0442\u043e\u0434\u0443 <code>processEvent()<\/code>. \u0417\u0434\u0435\u0441\u044c \u0441\u043e\u0431\u044b\u0442\u0438\u0435 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u0435\u0442\u0441\u044f \u0432 <code>watchCacheEvent<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u0442\u0441\u044f <code>updateCache()<\/code>. \u041c\u0435\u0442\u043e\u0434 <code>updateCache()<\/code> \u0432\u043c\u0435\u0441\u0442\u0435 \u0441 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u043c\u044b\u043c \u0438\u043c \u043c\u0435\u0442\u043e\u0434\u043e\u043c <code>resizeCacheLocked()<\/code> \u2014 \u044d\u0442\u043e \u0442\u043e, \u0433\u0434\u0435 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u0430 \u043b\u043e\u0433\u0438\u043a\u0430 \u0441\u043a\u043e\u043b\u044c\u0437\u044f\u0449\u0435\u0433\u043e \u043e\u043a\u043d\u0430.<\/p>\n<pre><code>\/\/ Assumes that lock is already held for write. func (w *watchCache) updateCache(event *watchCacheEvent) { w.resizeCacheLocked(event.RecordTime) if w.isCacheFullLocked() { \/\/ Cache is full - remove the oldest element. w.startIndex++ } w.cache[w.endIndex%w.capacity] = event w.endIndex++ }  \/\/ resizeCacheLocked resizes the cache if necessary: \/\/ - increases capacity by 2x if cache is full and all cached events occurred within last eventFreshDuration. \/\/ - decreases capacity by 2x when recent quarter of events occurred outside of eventFreshDuration(protect watchCache from flapping). func (w *watchCache) resizeCacheLocked(eventTime time.Time) { if w.isCacheFullLocked() &amp;&amp; eventTime.Sub(w.cache[w.startIndex%w.capacity].RecordTime) &lt; eventFreshDuration { capacity := min(w.capacity*2, w.upperBoundCapacity) if capacity > w.capacity { w.doCacheResizeLocked(capacity) } return } if w.isCacheFullLocked() &amp;&amp; eventTime.Sub(w.cache[(w.endIndex-w.capacity\/4)%w.capacity].RecordTime) > eventFreshDuration { capacity := max(w.capacity\/2, w.lowerBoundCapacity) if capacity &lt; w.capacity { w.doCacheResizeLocked(capacity) } return } }<\/code><\/pre>\n<p>\u041f\u0435\u0440\u0435\u0434 \u0432\u043e\u0437\u0432\u0440\u0430\u0442\u043e\u043c \u0438\u0437 <code>processEvent()<\/code> \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f <code>eventHandler()<\/code>, \u0435\u0441\u043b\u0438 \u043e\u043d\u0430 \u0431\u044b\u043b\u0430 \u043f\u0435\u0440\u0435\u0434\u0430\u043d\u0430 \u0432\u043e \u0432\u0440\u0435\u043c\u044f \u043f\u043e\u0441\u0442\u0440\u043e\u0435\u043d\u0438\u044f <code>watchCache<\/code>. \u041c\u044b \u0432\u0435\u0440\u043d\u0435\u043c\u0441\u044f \u043a \u044d\u0442\u043e\u043c\u0443 \u0432\u0441\u043a\u043e\u0440\u0435, \u043a\u043e\u0433\u0434\u0430 \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c, \u043a\u0430\u043a \u043d\u0430\u0431\u043b\u044e\u0434\u0430\u0442\u0435\u043b\u0438 \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u044f\u044e\u0442\u0441\u044f \u043e \u0441\u043e\u0431\u044b\u0442\u0438\u044f\u0445.<\/p>\n<pre><code>\/\/ Avoid calling event handler under lock. \/\/ This is safe as long as there is at most one call to Add\/Update\/Delete and \/\/ UpdateResourceVersion in flight at any point in time, which is true now, \/\/ because reflector calls them synchronously from its main thread. if w.eventHandler != nil { w.eventHandler(wcEvent) }<\/code><\/pre>\n<p>\u0414\u0440\u0443\u0433\u0438\u0435 \u043c\u0435\u0442\u043e\u0434\u044b, \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u0442\u0435, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0432\u043a\u043b\u044e\u0447\u0430\u044e\u0442 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0438 \u043f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u0435, \u043f\u043e\u043b\u0430\u0433\u0430\u044e\u0442\u0441\u044f \u043d\u0430 \u0431\u0430\u0437\u043e\u0432\u044b\u0439 <code>cache.Indexer<\/code>. \u0412 \u0438\u0442\u043e\u0433\u0435 <a href=\"https:\/\/github.com\/kubernetes\/kubernetes\/blob\/e818649c10f29bc80b874889a448da4023918330\/staging\/src\/k8s.io\/client-go\/tools\/cache\/store.go#L271\"><u>\u043f\u0440\u043e\u0441\u0442\u044b\u0435 \u0438\u043d\u0434\u0435\u043a\u0441\u044b<\/u><\/a>, \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u044b\u0435 \u0432 <code>watchCache<\/code>, \u0441\u0432\u043e\u0434\u044f\u0442\u0441\u044f \u043a \u043f\u043e\u0442\u043e\u043a\u043e\u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0439 <code>map<\/code>. \u0412 \u0442\u043e \u0432\u0440\u0435\u043c\u044f \u043a\u0430\u043a \u0441\u043a\u043e\u043b\u044c\u0437\u044f\u0449\u0435\u0435 \u043e\u043a\u043d\u043e \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u0432 <code>watchCache<\/code> \u043f\u043e\u043c\u043e\u0433\u0430\u0435\u0442 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u043c\u043d\u043e\u0433\u0438\u043c \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u043c \u043d\u0430\u0431\u043b\u044e\u0434\u0430\u0442\u0435\u043b\u044f\u043c, \u0438\u043d\u0434\u0435\u043a\u0441\u0430\u0442\u043e\u0440 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043d\u043e\u0432\u044b\u043c \u043d\u0430\u0431\u043b\u044e\u0434\u0430\u0442\u0435\u043b\u044f\u043c \u043b\u0435\u0433\u043a\u043e \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432, \u0447\u0442\u043e \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043f\u0440\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0435 \u043d\u0430\u0431\u043b\u044e\u0434\u0435\u043d\u0438\u044f.<\/p>\n<p>\u0412\u043c\u0435\u0441\u0442\u0435 \u044d\u0442\u0438 \u0434\u0432\u0430 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c\u0430 \u043e\u0431\u0441\u043b\u0443\u0436\u0438\u0432\u0430\u044e\u0442 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0438\u0437 \u0438\u0445 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430 \u0432 \u043f\u0430\u043c\u044f\u0442\u0438, \u0432\u043c\u0435\u0441\u0442\u043e \u0442\u043e\u0433\u043e \u0447\u0442\u043e\u0431\u044b \u043e\u0431\u0440\u0430\u0449\u0430\u0442\u044c\u0441\u044f \u043a <code>etcd<\/code> \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e.<\/p>\n<h2>cacherListerWatcher<\/h2>\n<p><code>cacherListerWatcher<\/code> \u043d\u0430\u043c\u043d\u043e\u0433\u043e \u043f\u0440\u043e\u0449\u0435, \u0447\u0435\u043c <code>watchCache<\/code>, \u0442\u0430\u043a \u043a\u0430\u043a \u043e\u0431\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u0435\u0442 <code>storage.Interface<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u0442\u0441\u044f <code>Cacher<\/code> \u0438 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442 \u0431\u0430\u0437\u043e\u0432\u044b\u0435 \u043c\u0435\u0442\u043e\u0434\u044b <code>GetList()<\/code> \u0438 <code>Watch()<\/code>.<\/p>\n<pre><code>\/\/ NewCacherListerWatcher returns a storage.Interface backed ListerWatcher. func NewCacherListerWatcher(storage storage.Interface, resourcePrefix string, newListFunc func() runtime.Object) cache.ListerWatcher { return &amp;cacherListerWatcher{ storage:        storage, resourcePrefix: resourcePrefix, newListFunc:    newListFunc, } }  \/\/ Implements cache.ListerWatcher interface. func (lw *cacherListerWatcher) List(options metav1.ListOptions) (runtime.Object, error) { list := lw.newListFunc() pred := storage.SelectionPredicate{ Label:    labels.Everything(), Field:    fields.Everything(), Limit:    options.Limit, Continue: options.Continue, }  storageOpts := storage.ListOptions{ ResourceVersionMatch: options.ResourceVersionMatch, Predicate:            pred, Recursive:            true, } if err := lw.storage.GetList(context.TODO(), lw.resourcePrefix, storageOpts, list); err != nil { return nil, err } return list, nil }  \/\/ Implements cache.ListerWatcher interface. func (lw *cacherListerWatcher) Watch(options metav1.ListOptions) (watch.Interface, error) { opts := storage.ListOptions{ ResourceVersion: options.ResourceVersion, Predicate:       storage.Everything, Recursive:       true, ProgressNotify:  true, } return lw.storage.Watch(context.TODO(), lw.resourcePrefix, opts) }<\/code><\/pre>\n<p><code>cacherListerWatcher<\/code> \u2014 \u0442\u043e, \u0447\u0442\u043e \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u0435\u0442 \u0434\u0430\u043d\u043d\u044b\u0435, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u044e\u0442\u0441\u044f \u0432 <code>watchCache<\/code>. \u041d\u043e \u043d\u0443\u0436\u0435\u043d \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c, \u0447\u0442\u043e\u0431\u044b \u0441\u0432\u044f\u0437\u0430\u0442\u044c \u0438\u0445 \u0432\u043c\u0435\u0441\u0442\u0435.<\/p>\n<h2>Reflector<\/h2>\n<p><code>watchCache<\/code> \u0438 <code>cacherListerWatcher<\/code> \u0441\u043e\u0437\u0434\u0430\u044e\u0442 \u043f\u0435\u0440\u0435\u0434 <code>Reflector<\/code>, \u0447\u0442\u043e\u0431\u044b \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u0438\u0445 \u0435\u043c\u0443. <\/p>\n<pre><code>watchCache := newWatchCache( config.KeyFunc, cacher.processEvent, config.GetAttrsFunc, config.Versioner, config.Indexers, config.Clock, config.GroupResource) listerWatcher := NewCacherListerWatcher(config.Storage, config.ResourcePrefix, config.NewListFunc) reflectorName := \"storage\/cacher.go:\" + config.ResourcePrefix  reflector := cache.NewNamedReflector(reflectorName, listerWatcher, obj, watchCache, 0)<\/code><\/pre>\n<p><code>Reflector<\/code> \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d \u0432 \u043f\u0430\u043a\u0435\u0442\u0435 <code>client-go\/tools\/cache<\/code> \u0441\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0439 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u043b\u044c\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u043e\u0439.  <\/p>\n<pre><code>\/\/ Reflector watches a specified resource and causes all changes to be reflected in the given store.<\/code><\/pre>\n<p>\u0411\u043e\u043b\u044c\u0448\u0430\u044f \u0447\u0430\u0441\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0438\u0437 \u0435\u0433\u043e \u043c\u0435\u0442\u043e\u0434\u0430 <code>ListAndWatch()<\/code> \u041e\u043d \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442 \u0442\u043e, \u0447\u0442\u043e \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u0438\u0437 \u0435\u0433\u043e \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044f: \u043f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u044f\u0435\u0442, \u0430 \u0437\u0430\u0442\u0435\u043c \u043f\u0440\u043e\u0441\u043c\u0430\u0442\u0440\u0438\u0432\u0430\u0435\u0442. \u0421\u043e\u0431\u044b\u0442\u0438\u044f, \u043f\u043e\u0441\u0442\u0443\u043f\u0430\u044e\u0449\u0438\u0435 \u043e\u0442 <code>ListerWatcher (cacherListerWatcher)<\/code>, \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u0432 <code>watchHandler()<\/code>. \u0418\u043c\u0435\u043d\u043d\u043e \u0437\u0434\u0435\u0441\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u0435\u0440\u0435\u0434\u0430\u044e\u0442\u0441\u044f \u0432 <code>watchCache<\/code>.<\/p>\n<pre><code>case watch.Added: err := store.Add(event.Object) if err != nil { utilruntime.HandleError(fmt.Errorf(\"%s: unable to add watch event object (%#v) to store: %v\", name, event.Object, err)) } case watch.Modified: err := store.Update(event.Object) if err != nil { utilruntime.HandleError(fmt.Errorf(\"%s: unable to update watch event object (%#v) to store: %v\", name, event.Object, err)) } case watch.Deleted: \/\/ TODO: Will any consumers need access to the \"last known \/\/ state\", which is passed in event.Object? If so, may need \/\/ to change this. err := store.Delete(event.Object) if err != nil { utilruntime.HandleError(fmt.Errorf(\"%s: unable to delete watch event object (%#v) from store: %v\", name, event.Object, err)) }<\/code><\/pre>\n<h2>\u0421\u0432\u044f\u0437\u044b\u0432\u0430\u0435\u043c \u0432\u0441\u0451 \u0432\u043c\u0435\u0441\u0442\u0435<\/h2>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/966\/2c4\/00b\/9662c400bda4e392faa5d2aaf796fac5.png\" width=\"1920\" height=\"1080\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/966\/2c4\/00b\/9662c400bda4e392faa5d2aaf796fac5.png\"\/><\/figure>\n<p>\u041a\u043e\u0433\u0434\u0430 \u0432\u0441\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b \u043d\u0430 \u043c\u0435\u0441\u0442\u0435, \u043f\u043e\u0440\u0430 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c <code>Reflector<\/code>, \u0447\u0442\u043e\u0431\u044b \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u0437 <code>cacherListerWatcher<\/code> \u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0442\u044c \u0438\u0445 \u0432 <code>watchCache<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0438\u043d\u0444\u043e\u0440\u043c\u0438\u0440\u043e\u0432\u0430\u0442\u044c <code>Cacher<\/code> \u043e\u0431 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f\u0445.  <\/p>\n<pre><code>go cacher.dispatchEvents()  cacher.stopWg.Add(1) go func() { defer cacher.stopWg.Done() defer cacher.terminateAllWatchers() wait.Until( func() { if !cacher.isStopped() { cacher.startCaching(stopCh) } }, time.Second, stopCh, ) }()<\/code><\/pre>\n<p>\u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u0432\u0442\u043e\u0440\u043e\u0439 \u0432\u044b\u0437\u043e\u0432, <code>cacher.startCaching(stopCh)<\/code> \u2014 \u0442\u0430\u043a \u043c\u044b \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c <code>Reflector<\/code> \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0432\u044b\u0437\u043e\u0432\u0430 <code>ListAndWatch()<\/code>.  <\/p>\n<pre><code>func (c *Cacher) startCaching(stopChannel &lt;-chan struct{}) { \/\/ The 'usable' lock is always 'RLock'able when it is safe to use the cache. \/\/ It is safe to use the cache after a successful list until a disconnection. \/\/ We start with usable (write) locked. The below OnReplace function will \/\/ unlock it after a successful list. The below defer will then re-lock \/\/ it when this function exits (always due to disconnection), only if \/\/ we actually got a successful list. This cycle will repeat as needed. successfulList := false c.watchCache.SetOnReplace(func() { successfulList = true c.ready.set(true) klog.V(1).Infof(\"cacher (%v): initialized\", c.groupResource.String()) metrics.WatchCacheInitializations.WithLabelValues(c.groupResource.String()).Inc() }) defer func() { if successfulList { c.ready.set(false) } }()  c.terminateAllWatchers() \/\/ Note that since onReplace may be not called due to errors, we explicitly \/\/ need to retry it on errors under lock. \/\/ Also note that startCaching is called in a loop, so there's no need \/\/ to have another loop here. if err := c.reflector.ListAndWatch(stopChannel); err != nil { klog.Errorf(\"cacher (%v): unexpected ListAndWatch error: %v; reinitializing...\", c.groupResource.String(), err) } }<\/code><\/pre>\n<p>\u042d\u0442\u043e \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u0442 \u043a \u0442\u043e\u043c\u0443, \u0447\u0442\u043e <code>Reflector<\/code> \u043d\u0430\u0447\u043d\u0435\u0442 \u0437\u0430\u043f\u043e\u043b\u043d\u044f\u0442\u044c <code>watchCache<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0443\u0432\u0435\u0434\u043e\u043c\u0438\u0442 <code>Cacher<\/code>, \u0447\u0442\u043e\u0431\u044b \u043e\u043d \u043c\u043e\u0433 \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u0435\u0433\u043e \u0432\u0441\u0435\u043c \u043d\u0430\u0431\u043b\u044e\u0434\u0430\u0442\u0435\u043b\u044f\u043c. \u041c\u0435\u0442\u043e\u0434 <code>cacher.processEvent()<\/code> \u0431\u044b\u043b \u043f\u0435\u0440\u0435\u0434\u0430\u043d \u0432 <code>watchCache<\/code> \u043a\u0430\u043a <code>eventHandler()<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u044b \u0432\u0438\u0434\u0435\u043b\u0438 \u0440\u0430\u043d\u0435\u0435.<\/p>\n<pre><code>func (c *Cacher) processEvent(event *watchCacheEvent) { if curLen := int64(len(c.incoming)); c.incomingHWM.Update(curLen) { \/\/ Monitor if this gets backed up, and how much. klog.V(1).Infof(\"cacher (%v): %v objects queued in incoming channel.\", c.groupResource.String(), curLen) } c.incoming &lt;- *event }<\/code><\/pre>\n<p>\u0412\u0441\u0451 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043f\u043e \u043a\u0430\u043d\u0430\u043b\u0443 <code>c.incoming<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0437\u0430\u0442\u0435\u043c \u0441\u0447\u0438\u0442\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0432 \u043c\u0435\u0442\u043e\u0434\u0435 <code>cacher.dispatchEvents()<\/code>, \u043f\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043c \u0431\u044b\u0442\u044c \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u043c \u043a\u0430\u0436\u0434\u043e\u043c\u0443 \u043d\u0430\u0431\u043b\u044e\u0434\u0430\u0442\u0435\u043b\u044e.<\/p>\n<pre><code>case event, ok := &lt;-c.incoming: if !ok { return } \/\/ Don't dispatch bookmarks coming from the storage layer. \/\/ They can be very frequent (even to the level of subseconds) \/\/ to allow efficient watch resumption on kube-apiserver restarts, \/\/ and propagating them down may overload the whole system. \/\/ \/\/ TODO: If at some point we decide the performance and scalability \/\/ footprint is acceptable, this is the place to hook them in. \/\/ However, we then need to check if this was called as a result \/\/ of a bookmark event or regular Add\/Update\/Delete operation by \/\/ checking if resourceVersion here has changed. if event.Type != watch.Bookmark { c.dispatchEvent(&amp;event) } lastProcessedResourceVersion = event.ResourceVersion metrics.EventsCounter.WithLabelValues(c.groupResource.String()).Inc()<\/code><\/pre>\n<p>\u041a\u043e\u0433\u0434\u0430 \u043c\u044b \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u043c <code>c.dispatchEvent(&amp;event)<\/code>, \u0441\u043f\u0438\u0441\u043e\u043a <code>cacheWatcher<\/code> \u0444\u0438\u043b\u044c\u0442\u0440\u0443\u0435\u0442\u0441\u044f \u043f\u043e \u0442\u0435\u043c, \u043a\u0442\u043e \u0437\u0430\u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043e\u0432\u0430\u043d \u0432 \u0434\u0430\u043d\u043d\u043e\u043c \u0441\u043e\u0431\u044b\u0442\u0438\u0438, \u0438 \u0441\u043e\u0431\u044b\u0442\u0438\u0435 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043a\u0430\u0436\u0434\u043e\u043c\u0443.  <\/p>\n<h2>\u041c\u0435\u0442\u043e\u0434 Watch()<\/h2>\n<p>\u042d\u0442\u043e \u0431\u044b\u043b\u043e \u043d\u0435\u043f\u0440\u043e\u0441\u0442\u043e, \u043d\u043e \u0442\u0435\u043f\u0435\u0440\u044c, \u043a\u043e\u0433\u0434\u0430 \u043c\u044b \u0437\u043d\u0430\u0435\u043c, \u043a\u0430\u043a \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u0441\u043e\u0431\u044b\u0442\u0438\u044f, \u043f\u0440\u0438\u0448\u043b\u043e \u0432\u0440\u0435\u043c\u044f \u0432\u0435\u0440\u043d\u0443\u0442\u044c\u0441\u044f \u043a \u043c\u0435\u0442\u043e\u0434\u0443 <code>Watch()<\/code>. \u0412\u043c\u0435\u0441\u0442\u043e \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u043d\u043e\u0432\u043e\u0433\u043e \u043d\u0430\u0431\u043b\u044e\u0434\u0435\u043d\u0438\u044f <code>etcd<\/code> \u043f\u0440\u0438 \u0432\u044b\u0437\u043e\u0432\u0435 \u044d\u0442\u043e\u0433\u043e \u043c\u0435\u0442\u043e\u0434\u0430, \u0441\u043e\u0437\u0434\u0430\u0435\u043c <code>cacheWatcher<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043a \u043a\u0430\u043d\u0434\u0438\u0434\u0430\u0442\u0430\u043c, \u043a\u043e\u0433\u0434\u0430 <code>Cacher<\/code> \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u0441\u043e\u0431\u044b\u0442\u0438\u044f. \u041a\u0430\u0436\u0434\u044b\u0439 <code>cacheWatcher<\/code> \u0438\u043c\u0435\u0435\u0442 \u0441\u0432\u043e\u0439 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u0446\u0438\u043a\u043b \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438: <code>watcher.processInterval()<\/code>. \u041c\u0435\u0442\u043e\u0434<code> Watch()<\/code> \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442 \u0446\u0438\u043a\u043b \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0434\u043b\u044f \u043d\u0430\u0431\u043b\u044e\u0434\u0430\u0442\u0435\u043b\u044f, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u043d \u0441\u043e\u0437\u0434\u0430\u0435\u0442 \u043f\u043e\u0441\u043b\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0435\u0433\u043e \u0432 \u0441\u043f\u0438\u0441\u043e\u043a \u043d\u0430\u0431\u043b\u044e\u0434\u0430\u0442\u0435\u043b\u0435\u0439 \u0434\u043b\u044f <code>Cacher<\/code>.<\/p>\n<pre><code>func() { c.Lock() defer c.Unlock() \/\/ Update watcher.forget function once we can compute it. watcher.forget = forgetWatcher(c, watcher, c.watcherIdx, triggerValue, triggerSupported) c.watchers.addWatcher(watcher, c.watcherIdx, triggerValue, triggerSupported)  \/\/ Add it to the queue only when the client support watch bookmarks. if watcher.allowWatchBookmarks { c.bookmarkWatchers.addWatcher(watcher) } c.watcherIdx++ }()  go watcher.processInterval(ctx, cacheInterval, watchRV) return watcher, nil<\/code><\/pre>\n<p><code>cacheInterval<\/code> \u0441\u043e\u0437\u0434\u0430\u0435\u0442\u0441\u044f <a href=\"https:\/\/github.com\/kubernetes\/kubernetes\/blob\/e818649c10f29bc80b874889a448da4023918330\/staging\/src\/k8s.io\/apiserver\/pkg\/storage\/cacher\/cacher.go#L539\"><u>\u0440\u0430\u043d\u0435\u0435 \u0432 \u043c\u0435\u0442\u043e\u0434\u0435<\/u> <\/a>\u0438 \u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043b\u044e\u0431\u044b\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0435 \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u0432 <code>watchCache<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0443\u044e\u0442 <code>cacheWatcher<\/code>. \u041c\u0435\u0442\u043e\u0434 <code>watcher.processInterval()<\/code> \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u0432\u0441\u0435 \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u0438\u0437 <code>cacheInterval<\/code>, \u0437\u0430\u0442\u0435\u043c \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442 \u043e\u0436\u0438\u0434\u0430\u0442\u044c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0431\u0443\u0434\u0443\u0449\u0438\u0445 <a href=\"https:\/\/github.com\/kubernetes\/kubernetes\/blob\/e818649c10f29bc80b874889a448da4023918330\/staging\/src\/k8s.io\/apiserver\/pkg\/storage\/cacher\/cacher.go#L1504\"><u>\u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u0445 \u0441\u043e\u0431\u044b\u0442\u0438\u0439<\/u><\/a>.<\/p>\n<pre><code>for { event, err := cacheInterval.Next() if err != nil { klog.Warningf(\"couldn't retrieve watch event to serve: %#v\", err) return } if event == nil { break } c.sendWatchCacheEvent(event) \/\/ With some events already sent, update resourceVersion so that \/\/ events that were buffered and not yet processed won't be delivered \/\/ to this watcher second time causing going back in time. resourceVersion = event.ResourceVersion initEventCount++ }<\/code><\/pre>\n<p><strong>\u041f\u0440\u0438\u043c\u0435\u0447\u0430\u043d\u0438\u0435<\/strong>: \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0438 \u043a \u043a\u043e\u0434\u0443 \u0443\u0434\u0430\u043b\u0435\u043d\u044b \u0434\u043b\u044f \u043a\u0440\u0430\u0442\u043a\u043e\u0441\u0442\u0438.<\/p>\n<p>\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u0441\u043e\u0441\u0442\u043e\u0438\u0442 \u0438\u0437 \u0432\u044b\u0437\u043e\u0432\u0430 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 <code>sendWatchCacheEvent()<\/code>, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u0435\u0442 \u0435\u0433\u043e. \u0417\u0430\u0442\u0435\u043c \u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043f\u043e \u043a\u0430\u043d\u0430\u043b\u0443 result, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u0442\u0435\u043c \u0436\u0435 \u043a\u0430\u043d\u0430\u043b\u043e\u043c, \u0438\u0437 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u0432\u044b\u0437\u044b\u0432\u0430\u044e\u0449\u0438\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u044e <code>Watch()<\/code> \u043c\u043e\u0433\u0443\u0442 \u0441\u0447\u0438\u0442\u044b\u0432\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435!<\/p>\n<pre><code>func (c *cacheWatcher) sendWatchCacheEvent(event *watchCacheEvent) { watchEvent := c.convertToWatchEvent(event) if watchEvent == nil { \/\/ Watcher is not interested in that object. return }  select { case &lt;-c.done: return default: }  select { case c.result &lt;- *watchEvent: case &lt;-c.done: } }<\/code><\/pre>\n<p><strong>\u041f\u0440\u0438\u043c\u0435\u0447\u0430\u043d\u0438\u0435<\/strong>: \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0438 \u043a \u043a\u043e\u0434\u0443 \u0443\u0434\u0430\u043b\u0435\u043d\u044b \u0434\u043b\u044f \u043a\u0440\u0430\u0442\u043a\u043e\u0441\u0442\u0438.<\/p>\n<h2>\u0412\u0441\u0435 \u0441\u043c\u043e\u0442\u0440\u044f\u0442\u2026 \u041f\u0440\u043e\u0434\u0443\u043a\u0442\u0438\u0432\u043d\u043e  <\/h2>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043d\u0430 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e\u00a0<code>Cacher storage.Interface<\/code> \u0432 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0438. \u041c\u044b \u0431\u0435\u0440\u0451\u043c \u043d\u0430\u0448\u0443 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0443 \u0438 \u043c\u043e\u0434\u0438\u0444\u0438\u0446\u0438\u0440\u0443\u0435\u043c \u0435\u0451, \u0447\u0442\u043e\u0431\u044b \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430 <code>etcd Cacher<\/code>.<\/p>\n<p><code>cachewatch.go<\/code><\/p>\n<pre><code>package main  import ( \"context\" \"fmt\" \"os\" \"os\/signal\"  \"go.etcd.io\/etcd\/client\/pkg\/v3\/transport\" clientv3 \"go.etcd.io\/etcd\/client\/v3\" v1 \"k8s.io\/api\/core\/v1\" \"k8s.io\/apimachinery\/pkg\/fields\" \"k8s.io\/apimachinery\/pkg\/labels\" \"k8s.io\/apimachinery\/pkg\/runtime\" \"k8s.io\/apimachinery\/pkg\/runtime\/serializer\" \"k8s.io\/apiserver\/pkg\/storage\" \"k8s.io\/apiserver\/pkg\/storage\/cacher\" \"k8s.io\/apiserver\/pkg\/storage\/etcd3\" \"k8s.io\/apiserver\/pkg\/storage\/value\/encrypt\/identity\" \"k8s.io\/kubernetes\/pkg\/apis\/core\" k8sv1 \"k8s.io\/kubernetes\/pkg\/apis\/core\/v1\" \"k8s.io\/utils\/clock\" )  func main() { tlsConfig, err := (transport.TLSInfo{ CertFile:       \".\/pki\/etcd\/server.crt\", KeyFile:        \".\/pki\/etcd\/server.key\", TrustedCAFile:  \".\/pki\/etcd\/ca.crt\", ClientCertFile: \".\/pki\/apiserver-etcd-client.crt\", ClientKeyFile:  \".\/pki\/apiserver-etcd-client.key\", }).ClientConfig() if err != nil { panic(err) } c, err := clientv3.New(clientv3.Config{ Endpoints: []string{\"https:\/\/127.0.0.1:2379\"}, TLS:       tlsConfig, }) if err != nil { panic(err) }  scheme := runtime.NewScheme() k8sv1.AddToScheme(scheme) core.AddToScheme(scheme) f := serializer.NewCodecFactory(scheme) s := etcd3.New(c, f.CodecForVersions(nil, f.UniversalDecoder(), nil, k8sv1.SchemeGroupVersion), nil, \"registry\", v1.Resource(\"ConfigMap\"), identity.NewEncryptCheckTransformer(), true, etcd3.NewDefaultLeaseManagerConfig())  ca, err := cacher.NewCacherFromConfig(cacher.Config{ Storage:        s, Versioner:      storage.APIObjectVersioner{}, GroupResource:  v1.Resource(\"configmaps\"), ResourcePrefix: \"configmaps\", KeyFunc:        func(o runtime.Object) (string, error) { return storage.NamespaceKeyFunc(\"configmaps\", o) }, GetAttrsFunc: func(o runtime.Object) (label labels.Set, field fields.Set, err error) { return labels.Set{}, fields.Set{}, nil }, NewFunc: func() runtime.Object { return &amp;v1.ConfigMap{} }, NewListFunc: func() runtime.Object { return &amp;v1.ConfigMapList{} }, Codec: f.LegacyCodec(k8sv1.SchemeGroupVersion), Clock: clock.RealClock{}, }) if err != nil { panic(err) } for i := 0; i &lt; 10000; i++ { w, err := ca.Watch(context.Background(), \"configmaps\", storage.ListOptions{ Predicate: storage.Everything, Recursive: true, }) if err != nil { panic(err) } go func() { for e := range w.ResultChan() { switch o := e.Object.(type) { case *v1.ConfigMap: fmt.Printf(\"%s || %s\/%s\\n\", e.Type, o.Namespace, o.Name) default: co, ok := o.(runtime.CacheableObject) if !ok { panic(\"unknown event\") } c, ok := co.GetObject().(*v1.ConfigMap) if !ok { panic(\"unknown object\") } fmt.Printf(\"%s || %s\/%s\\n\", e.Type, c.Namespace, c.Name) } } }() }  stop := make(chan os.Signal) signal.Notify(stop, os.Interrupt) &lt;-stop }<\/code><\/pre>\n<p>\u041f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 <code>Prometheus<\/code> \u0432\u0441\u0435 \u0435\u0449\u0435 \u0437\u0430\u043f\u0443\u0449\u0435\u043d \u0438 \u043e\u0447\u0438\u0449\u0430\u0435\u0442 \u043d\u0430\u0448 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 <code>etcd<\/code> \u0432 \u043d\u0430\u0448\u0435\u043c \u043a\u043b\u0430\u0441\u0442\u0435\u0440\u0435 <code>kind<\/code>, \u043c\u044b \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u043d\u0430\u0448\u0443 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0443 \u0438 \u0432\u0438\u0434\u0438\u043c \u0432\u043b\u0438\u044f\u043d\u0438\u0435 \u043d\u0430 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0440.  <\/p>\n<pre><code>$ go run cachewatch.go ... ADDED || kube-system\/kube-proxy ADDED || kube-public\/cluster-info ADDED || kube-system\/coredns ADDED || default\/kube-root-ca.crt ADDED || kube-public\/kube-root-ca.crt ADDED || kube-system\/kubelet-config ADDED || kube-system\/kubeadm-config ADDED || kube-system\/kubeadm-config ADDED || local-path-storage\/kube-root-ca.crt ADDED || local-path-storage\/local-path-config ADDED || default\/kube-root-ca.crt ...<\/code><\/pre>\n<p>\u0425\u043e\u0442\u044f \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u043e 10 000 \u043d\u0430\u0431\u043b\u044e\u0434\u0430\u0442\u0435\u043b\u0435\u0439, \u0432 \u043d\u0430\u0448\u0435\u043c \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u0435 <code>etcd<\/code> \u043d\u0435\u0442 \u0437\u0430\u043c\u0435\u0442\u043d\u043e\u0433\u043e \u0432\u043b\u0438\u044f\u043d\u0438\u044f \u043d\u0430 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0440. \u0422\u043e \u0436\u0435 \u0441\u0430\u043c\u043e\u0435 \u0441\u043f\u0440\u0430\u0432\u0435\u0434\u043b\u0438\u0432\u043e, \u0435\u0441\u043b\u0438 \u043c\u044b \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043d\u043e\u0432\u0443\u044e <code>ConfigMap<\/code>.  <\/p>\n<pre><code>$ kubectl create configmap testing configmap\/testing created<\/code><\/pre>\n<pre><code>... ADDED || default\/testing2 ADDED || default\/testing2 ADDED || default\/testing2 ADDED || default\/testing2 ADDED || default\/testing2 ADDED || default\/testing2 ADDED || default\/testing2 ADDED || default\/testing2 ADDED || default\/testing2 ...<\/code><\/pre>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/4b4\/55f\/711\/4b455f711901c4aae77963b01c4330b3.png\" width=\"3680\" height=\"1956\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/4b4\/55f\/711\/4b455f711901c4aae77963b01c4330b3.png\"\/><\/figure>\n<p>\u0414\u0438\u0430\u043f\u0430\u0437\u043e\u043d \u043e\u0441\u0438<em> <\/em>Y\u00a0<code>0.00<\/code><em>\u0434\u043e\u00a0<\/em><code>0.18<\/code><em>.<\/em>  <\/p>\n<h2>\u0417\u0430\u043a\u043b\u044e\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u043c\u044b\u0441\u043b\u0438<\/h2>\n<p>\u042d\u0442\u043e \u0431\u044b\u043b\u0430 \u0432\u0442\u043e\u0440\u0430\u044f \u0447\u0430\u0441\u0442\u044c \u0441\u0435\u0440\u0438\u0438 \u043f\u0440\u0438\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0439 <a href=\"https:\/\/danielmangum.com\/categories\/kubernetes-api-server-adventures\/\"><u>\u0441\u0435\u0440\u0432\u0435\u0440\u0430 Kubernetes API<\/u><\/a>. \u041e\u0441\u043d\u043e\u0432\u044b\u0432\u0430\u044f\u0441\u044c \u043d\u0430 \u043d\u0430\u0448\u0435\u043c storage.Interface, \u043c\u044b \u0443\u0437\u043d\u0430\u043b\u0438, \u043a\u0430\u043a Kubernetes \u043e\u043f\u0442\u0438\u043c\u0438\u0437\u0438\u0440\u0443\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0443 watch \u043f\u0435\u0440\u0435\u0434 \u043b\u0438\u0446\u043e\u043c \u043f\u043e\u0442\u0435\u043d\u0446\u0438\u0430\u043b\u044c\u043d\u043e \u0431\u043e\u043b\u044c\u0448\u043e\u0433\u043e \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u0432. \u041a\u0430\u043a \u0438 \u0432 \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u0435 \u0441\u043b\u0443\u0447\u0430\u0435\u0432 \u0432 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u043d\u043e\u0433\u043e \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0435\u043d\u0438\u044f, \u044d\u0442\u0430 \u043e\u043f\u0442\u0438\u043c\u0438\u0437\u0430\u0446\u0438\u044f \u043d\u0435 \u043b\u0438\u0448\u0435\u043d\u0430 \u043a\u043e\u043c\u043f\u0440\u043e\u043c\u0438\u0441\u0441\u043e\u0432, \u043d\u043e \u043e\u043d\u0430 \u0434\u043e\u0441\u0442\u0438\u0433\u0430\u0435\u0442 \u0446\u0435\u043b\u0438 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043e\u0431\u0449\u0438\u0439 \u0441\u043b\u0443\u0447\u0430\u0439 \u0431\u043e\u043b\u0435\u0435 \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u044b\u043c. \u0412 <a href=\"https:\/\/slurm.club\/3KhyGtd\"><u>\u00abKubernetes: \u041c\u0435\u0433\u0430\u00bb<\/u> <\/a>\u043c\u044b \u0440\u0430\u0441\u0441\u043c\u0430\u0442\u0440\u0438\u0432\u0430\u0435\u043c \u0438 \u0437\u0430\u0442\u0440\u0430\u0433\u0438\u0432\u0430\u0435\u043c \u0442\u0430\u043a\u0438\u0435 \u043e\u0431\u043b\u0430\u0441\u0442\u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043e\u0439, \u0432 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u043a\u0430\u0436\u0434\u043e\u0435 \u0443\u043b\u0443\u0447\u0448\u0435\u043d\u0438\u0435 \u043c\u043e\u0436\u0435\u0442 \u043f\u043e\u043c\u043e\u0447\u044c \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u0438 \u0441\u044d\u043a\u043e\u043d\u043e\u043c\u0438\u0442\u044c \u0441\u043e\u0442\u043d\u0438 \u0442\u044b\u0441\u044f\u0447 \u0440\u0443\u0431\u043b\u0435\u0439.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/3a1\/637\/2e5\/3a16372e5f1d1eeb7e513e97e17ef83f.png\" alt=\"https:\/\/slurm.club\/3KhyGtd\" title=\"https:\/\/slurm.club\/3KhyGtd\" width=\"780\" height=\"100\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/3a1\/637\/2e5\/3a16372e5f1d1eeb7e513e97e17ef83f.png\"\/><\/p>\n<div><figcaption><a href=\"https:\/\/slurm.club\/3KhyGtd\">https:\/\/slurm.club\/3KhyGtd<\/a><\/figcaption><\/div>\n<\/figure>\n<p>\u041c\u044b \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c, \u0435\u0441\u0442\u044c \u043b\u0438 \u043a\u0430\u043a\u0438\u0435-\u043b\u0438\u0431\u043e \u0441\u043b\u0443\u0447\u0430\u0438, \u043a\u043e\u0433\u0434\u0430 \u044d\u0442\u043e\u0442 \u043a\u043e\u043c\u043f\u0440\u043e\u043c\u0438\u0441\u0441 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b, \u0432 \u0431\u0443\u0434\u0443\u0449\u0438\u0445 \u043f\u0443\u0431\u043b\u0438\u043a\u0430\u0446\u0438\u044f\u0445.<\/p>\n<\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/727510\/\"> https:\/\/habr.com\/ru\/articles\/727510\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<figure class=\"full-width\"><\/figure>\n<p>\u0412 \u043d\u0430\u0448\u0435\u0439 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u043f\u0440\u043e \u043f\u0440\u0438\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u0441\u0435\u0440\u0432\u0435\u0440\u0430 Kubernetes API \u043c\u044b \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0435\u043b\u0438 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430 \u0438 \u0438\u0441\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u043b\u0438 \u0435\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u0443\u044e \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u0432 \u0434\u0435\u0440\u0435\u0432\u0435: etcd3. \u041e\u0434\u043d\u0430\u043a\u043e \u0432\u043d\u0438\u043c\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u043f\u0440\u043e\u0447\u0442\u0435\u043d\u0438\u0435 \u0441\u043d\u043e\u0441\u043e\u043a \u0432 \u044d\u0442\u043e\u043c \u043f\u043e\u0441\u0442\u0435 \u043f\u043e\u043a\u0430\u0437\u0430\u043b\u043e, \u0447\u0442\u043e \u043c\u044b \u0431\u044b\u043b\u0438 \u043d\u0435 \u0441\u043e\u0432\u0441\u0435\u043c \u0447\u0435\u0441\u0442\u043d\u044b\u043c\u0438, \u0433\u043e\u0432\u043e\u0440\u044f, \u0447\u0442\u043e etcd3 \u2014 \u0435\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u0430\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f.<\/p>\n<p>\u042d\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u043e\u0441\u043f\u043e\u0440\u0438\u0442\u044c, \u0432\u0435\u0434\u044c Cacher \u0442\u0430\u043a\u0436\u0435 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0442\u0435\u0445\u043d\u0438\u0447\u0435\u0441\u043a\u043e\u0439 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0435\u0439, \u0445\u043e\u0442\u044f \u0435\u043c\u0443 \u0438 \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0431\u0430\u0437\u043e\u0432\u0430\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f. \u041f\u043e\u0434\u0440\u043e\u0431\u043d\u043e \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0432 \u043e\u0434\u043d\u043e\u043c \u0438\u0437 \u0431\u0443\u0434\u0443\u0449\u0438\u0445 \u043f\u043e\u0441\u0442\u043e\u0432. \u0410 \u0441\u0435\u0439\u0447\u0430\u0441 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e \u0438\u0437\u0443\u0447\u0438\u043c \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0438 \u0435\u0433\u043e \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438.\u00a0<\/p>\n<figure class=\"full-width\"><\/figure>\n<p><strong>\u041e \u0447\u0451\u043c \u043f\u043e\u0433\u043e\u0432\u043e\u0440\u0438\u043c \u0432 \u0441\u0442\u0430\u0442\u044c\u0435:<\/strong> <\/p>\n<ul>\n<li>\n<p>\u041d\u0430\u0431\u043b\u044e\u0434\u0435\u043d\u0438\u0435 (? \u00ab\u042f \u0445\u043e\u0447\u0443 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443 \u0438 \u0435\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435\u00bb);<\/p>\n<\/li>\n<li>\n<p>\u0412\u0441\u0435 \u0441\u043c\u043e\u0442\u0440\u044f\u0442;<\/p>\n<\/li>\n<li>\n<p>\u041a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 (? \u00ab\u041c\u043d\u0435 \u043f\u0440\u043e\u0441\u0442\u043e \u043d\u0443\u0436\u043d\u043e \u0440\u0435\u0448\u0435\u043d\u0438\u0435\u00bb);<\/p>\n<\/li>\n<li>\n<p><code>watchCache <\/code>;<\/p>\n<\/li>\n<li>\n<p><code>cacherListerWatcher <\/code>;<\/p>\n<\/li>\n<li>\n<p><code>Reflector<\/code>;<\/p>\n<\/li>\n<li>\n<p>\u0421\u0432\u044f\u0436\u0435\u043c \u0432\u0441\u0451 \u0432\u043c\u0435\u0441\u0442\u0435;\u00a0<\/p>\n<\/li>\n<li>\n<p>\u041c\u0435\u0442\u043e\u0434 <code>\u00a0Watch()<\/code>;<\/p>\n<\/li>\n<li>\n<p>\u0412\u0441\u0435 \u0441\u043c\u043e\u0442\u0440\u044f\u0442\u2026 \u0423\u0441\u043f\u0435\u0448\u043d\u043e (? \u00ab\u041f\u0440\u043e\u0441\u0442\u043e \u043f\u043e\u043a\u0430\u0436\u0438\u0442\u0435 \u043c\u043d\u0435 \u043a\u043e\u0434\u00bb).<\/p>\n<\/li>\n<\/ul>\n<h2>\u041d\u0430\u0431\u043b\u044e\u0434\u0435\u043d\u0438\u0435<\/h2>\n<p>\u041f\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043c \u043c\u044b \u0432\u0432\u0435\u0434\u0435\u043c \u043a\u0430\u043a\u043e\u0439-\u043b\u0438\u0431\u043e \u0442\u0438\u043f \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f, \u0441\u0442\u043e\u0438\u0442 \u0432\u0435\u0440\u043d\u0443\u0442\u044c\u0441\u044f \u043a \u0442\u043e\u043c\u0443 \u0444\u0430\u043a\u0442\u0443, \u0447\u0442\u043e <code>storage.Interface<\/code> \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442 \u0432 \u0441\u0435\u0431\u044f \u043c\u0435\u0442\u043e\u0434 <code>Watch<\/code>, \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f <code>etcd3<\/code>\u00a0<a href=\"https:\/\/github.com\/kubernetes\/kubernetes\/blob\/3154010eec3488dd64110e3ca6c4ba0b5ac61310\/staging\/src\/k8s.io\/apiserver\/pkg\/storaghttps:\/\/github.com\/kubernetes\/kubernetes\/blob\/3154010eec3488dd64110e3ca6c4ba0b5ac61310\/staging\/src\/k8s.io\/apiserver\/pkg\/storage\/etcd3\/store.go#L847e\/etcd3\/store.go#L847\"><u>\u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u0435\u0433\u043e<\/u><\/a>. \u0420\u0430\u043d\u0435\u0435 <a href=\"https:\/\/danielmangum.com\/posts\/k8s-asa-the-storage-interface\/#our-good-friend-etcd\"><u>\u043c\u044b \u0438\u0437\u0443\u0447\u0430\u043b\u0438<\/u><\/a> \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u043d\u0438\u044f \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439, \u043e\u0431\u0440\u0430\u0449\u0430\u044f\u0441\u044c \u043d\u0435\u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u043a <code>etcd<\/code> \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <code>etcdctl<\/code>. <code>etcd<\/code> \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u0435\u0442 <a href=\"https:\/\/etcd.io\/docs\/v3.5\/learning\/api\/#watch-api\"><u>Watch API<\/u><\/a>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0434\u0432\u0443\u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u0435 \u043f\u043e\u0442\u043e\u043a\u0438 gRPC \u0434\u043b\u044f \u0434\u043e\u0441\u0442\u0430\u0432\u043a\u0438 \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u043a\u043b\u0438\u0435\u043d\u0442\u0430\u043c \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0434\u043b\u044f <a href=\"https:\/\/grpc.io\/docs\/what-is-grpc\/core-concepts\/#bidirectional-streaming-rpc\"><u>\u043a\u043b\u044e\u0447\u0430<\/u><\/a>.<\/p>\n<p>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f etcd3 Watch <a href=\"https:\/\/github.com\/kubernetes\/kubernetes\/blob\/3154010eec3488dd64110e3ca6c4ba0b5ac61310\/staging\/src\/k8s.io\/apiserver\/pkg\/storage\/etcd3\/store.go#L856\"><u>\u0441\u043e\u0437\u0434\u0430\u0435\u0442 <\/u><\/a><code>watch.Interface<\/code>, \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u044b\u0439 <code>watcher<\/code>. \u041d\u043e\u0432\u044b\u0439 <code>watcher<\/code> \u0441\u043e\u0437\u0434\u0430\u0435\u0442\u0441\u044f \u043f\u0440\u0438 \u043a\u0430\u0436\u0434\u043e\u043c \u0432\u044b\u0437\u043e\u0432\u0435 <code>Watch<\/code>, \u0438 \u043a\u0430\u0436\u0434\u044b\u0439 <code>watcher<\/code>, \u043f\u043e \u0441\u0443\u0442\u0438, \u043f\u0440\u043e\u0441\u0442\u043e \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442 <code>etcd<\/code> Watch API \u0438 \u043f\u0435\u0440\u0435\u0441\u044b\u043b\u0430\u0435\u0442 \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u043f\u043e \u043a\u0430\u043d\u0430\u043b\u0443.<\/p>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0432\u043e\u0437\u044c\u043c\u0435\u043c \u043d\u0430\u0448\u0443 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0443 \u0438\u0437 <a href=\"https:\/\/habr.com\/ru\/companies\/southbridge\/articles\/727470\/\"><u>\u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u0433\u043e \u043f\u043e\u0441\u0442\u0430<\/u><\/a> \u0438 \u043c\u043e\u0434\u0438\u0444\u0438\u0446\u0438\u0440\u0443\u0435\u043c \u0435\u0451, \u0447\u0442\u043e\u0431\u044b \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0442\u044c \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0432\u043e \u0432\u0441\u0435\u0445 <code>ConfigMaps<\/code> \u0432 \u043a\u043b\u0430\u0441\u0442\u0435\u0440\u0435.<\/p>\n<p>\u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u043d\u0430\u0439\u0442\u0438 \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u0438 \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 PCI, \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u0434\u043b\u044f \u0441\u0432\u044f\u0437\u0438 \u0441 <code>etcd<\/code> \u0432 \u043a\u043b\u0430\u0441\u0442\u0435\u0440\u0435 <code>kind<\/code>, <a href=\"https:\/\/danielmangum.com\/posts\/k8s-asa-the-storage-interface\/#calling-the-storageinterface-directly\"><u>\u0437\u0434\u0435\u0441\u044c<\/u><\/a>.<\/p>\n<p><code>directwatch.go<\/code><\/p>\n<pre><code>package main  import ( \"context\" \"fmt\"  \"go.etcd.io\/etcd\/client\/pkg\/v3\/transport\" clientv3 \"go.etcd.io\/etcd\/client\/v3\" v1 \"k8s.io\/api\/core\/v1\" \"k8s.io\/apimachinery\/pkg\/runtime\" \"k8s.io\/apimachinery\/pkg\/runtime\/serializer\" \"k8s.io\/apiserver\/pkg\/storage\" \"k8s.io\/apiserver\/pkg\/storage\/etcd3\" \"k8s.io\/apiserver\/pkg\/storage\/value\/encrypt\/identity\" \"k8s.io\/kubernetes\/pkg\/apis\/core\" k8sv1 \"k8s.io\/kubernetes\/pkg\/apis\/core\/v1\" )  func main() { tlsConfig, err := (transport.TLSInfo{ CertFile:       \".\/pki\/etcd\/server.crt\", KeyFile:        \".\/pki\/etcd\/server.key\", TrustedCAFile:  \".\/pki\/etcd\/ca.crt\", ClientCertFile: \".\/pki\/apiserver-etcd-client.crt\", ClientKeyFile:  \".\/pki\/apiserver-etcd-client.key\", }).ClientConfig() if err != nil { panic(err) } c, err := clientv3.New(clientv3.Config{ Endpoints: []string{\"https:\/\/127.0.0.1:2379\"}, TLS:       tlsConfig, }) if err != nil { panic(err) }  scheme := runtime.NewScheme() k8sv1.AddToScheme(scheme) core.AddToScheme(scheme) f := serializer.NewCodecFactory(scheme) s := etcd3.New(c, f.CodecForVersions(nil, f.UniversalDecoder(), nil, k8sv1.SchemeGroupVersion), nil, \"registry\", v1.Resource(\"ConfigMap\"), identity.NewEncryptCheckTransformer(), true, etcd3.NewDefaultLeaseManagerConfig()) w, err := s.Watch(context.Background(), \"configmaps\", storage.ListOptions{ Predicate: storage.Everything, Recursive: true, }) if err != nil { panic(err) } for e := range w.ResultChan() { co, ok := e.Object.(*v1.ConfigMap) if !ok { panic(\"not a config map!\") } fmt.Printf(\"%s || %s\/%s\\n\", e.Type, co.Namespace, co.Name) } }<\/code><\/pre>\n<p>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043d\u0430 \u0442\u043e\u0442 \u0444\u0430\u043a\u0442, \u0447\u0442\u043e \u043c\u044b \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u043c \u0442\u0438\u043f\u044b \u0438\u0437 <code>k8s.io\/kubernetes<\/code> \u0441\u043a\u043e\u0440\u0435\u0435, \u0447\u0435\u043c <code>k8s.io\/api<\/code>. \u042d\u0442\u043e \u043d\u0443\u0436\u043d\u043e, \u0447\u0442\u043e\u0431\u044b \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u0435 <code>internal<\/code> \u0432\u0435\u0440\u0441\u0438\u0438 \u0432 \u043d\u0430\u0448\u0443 \u0436\u0435\u043b\u0430\u0435\u043c\u0443\u044e \u0432\u0435\u0440\u0441\u0438\u044e (<code>v1<\/code>). \u041c\u044b \u0442\u0430\u043a\u0436\u0435 \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u043c <code>nil \u00a0CodecForVersions,<\/code> \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u0434\u0435\u043a\u043e\u0434\u0438\u0440\u0443\u0435\u043c \u0438\u0445 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u0440\u0438 \u0447\u0442\u0435\u043d\u0438\u0438 \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u043f\u043e\u0442\u043e\u043a\u0430.<\/p>\n<p>\u0415\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u0437\u0430\u043f\u0443\u0449\u0435\u043d \u043a\u043b\u0430\u0441\u0442\u0435\u0440 <code>kind<\/code>, \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u043d\u0430\u0447\u0430\u0442\u044c \u043f\u0435\u0440\u0435\u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u043e\u0440\u0442\u043e\u0432 <code>etcd Pod<\/code>.<\/p>\n<pre><code>$ kubectl port-forward -n kube-system pod\/etcd-kind-control-plane 2379:2379<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043d\u0430\u0447\u0430\u0442\u044c \u043d\u0430\u0448\u0443 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0443.<\/p>\n<pre><code>$ go run directwatch.go ADDED || default\/kube-root-ca.crt ADDED || kube-node-lease\/kube-root-ca.crt ADDED || kube-public\/cluster-info ADDED || kube-public\/kube-root-ca.crt ADDED || kube-system\/coredns ADDED || kube-system\/extension-apiserver-authentication ADDED || kube-system\/kube-proxy ADDED || kube-system\/kube-root-ca.crt ADDED || kube-system\/kubeadm-config ADDED || kube-system\/kubelet-config ADDED || local-path-storage\/kube-root-ca.crt ADDED || local-path-storage\/local-path-config<\/code><\/pre>\n<p>\u041a\u0430\u043a \u0438 \u043e\u0436\u0438\u0434\u0430\u043b\u043e\u0441\u044c, \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c <code>ADDED<\/code> \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u0434\u043b\u044f \u0432\u0441\u0435\u0445 <code>ConfigMaps<\/code>, \u043d\u0430\u0445\u043e\u0434\u044f\u0449\u0438\u0445\u0441\u044f \u0432 \u0434\u0430\u043d\u043d\u044b\u0439 \u043c\u043e\u043c\u0435\u043d\u0442 \u0432 \u043a\u043b\u0430\u0441\u0442\u0435\u0440\u0435. \u041c\u043e\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c, \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0442\u044c \u0438 \u0443\u0434\u0430\u043b\u044f\u0442\u044c <code>ConfigMap<\/code>, \u0447\u0442\u043e\u0431\u044b \u0443\u0432\u0438\u0434\u0435\u0442\u044c, \u043a\u0430\u043a \u0434\u0440\u0443\u0433\u0438\u0435 \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u043e\u0442\u0440\u0430\u0436\u0430\u044e\u0442\u0441\u044f \u0432 \u043f\u043e\u0442\u043e\u043a\u0435 \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0430.<\/p>\n<pre><code>$ kubectl create configmap k8s-asa --from-literal hello=world configmap\/k8s-asa created  $ kubectl create configmap k8s-asa --from-literal hello=asa -o yaml --dry-run | kubectl apply -f - configmap\/k8s-asa configured  $ kubectl delete configmap k8s-asa configmap \"k8s-asa\" deleted<\/code><\/pre>\n<p>\u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043d\u0430 \u043a\u0430\u0436\u0434\u043e\u0435 \u0438\u0437 \u044d\u0442\u0438\u0445 \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u0432 \u043f\u043e\u0442\u043e\u043a\u0435 \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0430 \u0441 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c \u0442\u0438\u043f\u043e\u043c.<\/p>\n<pre><code>ADDED || default\/k8s-asa MODIFIED || default\/k8s-asa DELETED || default\/k8s-asa<\/code><\/pre>\n<h2>\u041e\u0431\u0449\u0435\u0435 \u043d\u0430\u0431\u043b\u044e\u0434\u0435\u043d\u0438\u0435<\/h2>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u0432 \u043b\u044e\u0431\u043e\u0435 \u0432\u0440\u0435\u043c\u044f, \u043a\u043e\u0433\u0434\u0430 \u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0443\u044e\u0449\u0438\u0439 \u043d\u0430\u0441 \u0442\u0438\u043f, \u0438 \u043d\u0435 \u043f\u043e\u0445\u043e\u0436\u0435, \u0447\u0442\u043e <code>etcd<\/code> \u0438\u043b\u0438 \u043d\u0430\u0448\u0430 \u043c\u0430\u043b\u0435\u043d\u044c\u043a\u0430\u044f \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0430 \u0441\u0438\u043b\u044c\u043d\u043e \u00ab\u043f\u043e\u0442\u0435\u044e\u0442\u00bb. \u041d\u043e \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0432\u0437\u0433\u043b\u044f\u043d\u0435\u043c \u043d\u0430 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u0435\u043b\u0438 <code>etcd<\/code>, \u0447\u0442\u043e\u0431\u044b \u0443\u0431\u0435\u0434\u0438\u0442\u044c\u0441\u044f \u0432 \u044d\u0442\u043e\u043c. \u041c\u044b \u043c\u043e\u0436\u0435\u043c \u043d\u0430\u0439\u0442\u0438 \u043e\u0442\u043a\u0440\u044b\u0442\u044b\u0439 \u0430\u0434\u0440\u0435\u0441 \u043c\u0435\u0442\u0440\u0438\u043a \u0434\u043b\u044f <code>etcd<\/code> \u0432 \u043d\u0430\u0448\u0435\u043c \u043a\u043b\u0430\u0441\u0442\u0435\u0440\u0435 <code>kind<\/code>, \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0432 \u043a\u043e\u043c\u0430\u043d\u0434\u0443 \u0434\u043b\u044f \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0430 \u0432 \u043c\u043e\u0434\u0443\u043b\u0435 <code>Pod<\/code>.<\/p>\n<pre><code>$ kubectl get pod -n kube-system etcd-kind-control-plane -o=jsonpath={.spec.containers[0].command} | jq .  [   \"etcd\",   \"--advertise-client-urls=https:\/\/172.19.0.2:2379\",   \"--cert-file=\/etc\/kubernetes\/pki\/etcd\/server.crt\",   \"--client-cert-auth=true\",   \"--data-dir=\/var\/lib\/etcd\",   \"--experimental-initial-corrupt-check=true\",   \"--experimental-watch-progress-notify-interval=5s\",   \"--initial-advertise-peer-urls=https:\/\/172.19.0.2:2380\",   \"--initial-cluster=kind-control-plane=https:\/\/172.19.0.2:2380\",   \"--key-file=\/etc\/kubernetes\/pki\/etcd\/server.key\",   \"--listen-client-urls=https:\/\/127.0.0.1:2379,https:\/\/172.19.0.2:2379\",   \"--listen-metrics-urls=http:\/\/127.0.0.1:2381\",   \"--listen-peer-urls=https:\/\/172.19.0.2:2380\",   \"--name=kind-control-plane\",   \"--peer-cert-file=\/etc\/kubernetes\/pki\/etcd\/peer.crt\",   \"--peer-client-cert-auth=true\",   \"--peer-key-file=\/etc\/kubernetes\/pki\/etcd\/peer.key\",   \"--peer-trusted-ca-file=\/etc\/kubernetes\/pki\/etcd\/ca.crt\",   \"--snapshot-count=10000\",   \"--trusted-ca-file=\/etc\/kubernetes\/pki\/etcd\/ca.crt\" ]<\/code><\/pre>\n<p><code>--listen-metrics-urls<\/code> \u00a0\u2014 \u0442\u043e, \u0447\u0442\u043e \u043c\u044b \u0438\u0449\u0435\u043c .<code>etcd<\/code> \u0431\u0443\u0434\u0435\u0442 \u043e\u0431\u0441\u043b\u0443\u0436\u0438\u0432\u0430\u0442\u044c <a href=\"https:\/\/danielmangum.com\/posts\/k8s-asa-watching-and-caching\/\"><u>\u043f\u043e\u043a\u0430\u0437\u0430\u0442\u0435\u043b\u0438 <\/u><\/a>Prometheus \u0432 \u043a\u043e\u043d\u0435\u0447\u043d\u043e\u0439 \u0442\u043e\u0447\u043a\u0435 <code>\/metrics<\/code> \u043f\u043e \u044d\u0442\u043e\u043c\u0443 \u0430\u0434\u0440\u0435\u0441\u0443. \u0421\u043e\u0437\u0434\u0430\u0451\u043c \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u0439 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0439 \u0444\u0430\u0439\u043b Prometheus, \u0447\u0442\u043e\u0431\u044b \u0434\u0430\u0442\u044c \u043a\u043e\u043c\u0430\u043d\u0434\u0443 \u0441\u0431\u043e\u0440\u0449\u0438\u043a\u0443 \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u043e\u0447\u0438\u0441\u0442\u043a\u0443 <code>etcd<\/code>.<\/p>\n<p><code>prometheus.yml<\/code><\/p>\n<pre><code>global:   scrape_interval: 1s scrape_configs:   - job_name: etcd     static_configs:     - targets: ['127.0.0.1:2379']<\/code><\/pre>\n<p>\u041f\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043c \u043c\u044b \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u043c Prometheus, \u0443\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u043a\u043e\u043d\u0435\u0447\u043d\u043e\u0439 \u0442\u043e\u0447\u043a\u0435 <code>etcd metrics<\/code>. \u041f\u0435\u0440\u0435\u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u043e\u0440\u0442\u043e\u0432 \u0432 \u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0441\u0442\u0432\u043e \u0438\u043c\u0435\u043d <code>host network<\/code> \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u0442 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c Prometheus \u0432 \u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0441\u0442\u0432\u0435 \u0438\u043c\u0435\u043d <code>host network<\/code>, \u0447\u0442\u043e \u0443\u043f\u0440\u043e\u0449\u0430\u0435\u0442 \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u043f\u0430\u043d\u0435\u043b\u0438 \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433\u0430 \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435.<\/p>\n<p>\u041f\u0435\u0440\u0435\u043d\u0435\u0441\u0438\u0442\u0435 \u043a\u043e\u043d\u0435\u0447\u043d\u0443\u044e \u0442\u043e\u0447\u043a\u0443 <code>etcd metrics<\/code>.<\/p>\n<pre><code>$ kubectl port-forward -n kube-system pod\/etcd-kind-control-plane 2381:2381<\/code><\/pre>\n<p>\u0417\u0430\u0442\u0435\u043c \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440 Prometheus, \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0451\u043d\u043d\u044b\u0439 \u043a \u0441\u0435\u0442\u0438 \u0445\u043e\u0441\u0442\u0430.  <\/p>\n<pre><code>$ docker run --net=host --rm -v $(pwd)\/prometheus.yml:\/etc\/prometheus\/prometheus.yml prom\/prometheus<\/code><\/pre>\n<p>\u041f\u0430\u043d\u0435\u043b\u044c \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433\u0430 Prometheus \u0442\u0435\u043f\u0435\u0440\u044c \u0434\u043e\u043b\u0436\u043d\u0430 \u0431\u044b\u0442\u044c \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u0430 \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435 \u043f\u043e \u0430\u0434\u0440\u0435\u0441\u0443 <code>127.0.0.1:9090<\/code>. \u041c\u044b \u043c\u043e\u0436\u0435\u043c \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0433\u0440\u0430\u0444\u0438\u043a \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u0435\u043b\u0435\u0439, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 <code>etcd<\/code>, \u0432\u0432\u0435\u0434\u044f \u0437\u0430\u043f\u0440\u043e\u0441. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, <code>irate(process_cpu_seconds_total{job=\"etcd\"}[5m])<\/code> \u043f\u043e\u043a\u0430\u0436\u0435\u0442 \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c \u0443\u0432\u0435\u043b\u0438\u0447\u0435\u043d\u0438\u044f \u043e\u0431\u0449\u0435\u0433\u043e \u0441\u0438\u0441\u0442\u0435\u043c\u043d\u043e\u0433\u043e \u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0433\u043e \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0440\u043d\u043e\u0433\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438. \u0415\u0441\u043b\u0438 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0443 \u0441\u0435\u0439\u0447\u0430\u0441, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442 \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u044b \u0434\u043b\u044f <code>ConfigMaps<\/code>, \u043c\u044b \u043d\u0435 \u0443\u0432\u0438\u0434\u0438\u043c \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0433\u043e \u0432\u043b\u0438\u044f\u043d\u0438\u044f \u043d\u0430 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0440.<\/p>\n<figure class=\"full-width\"><\/figure>\n<p>\u0414\u0438\u0430\u043f\u0430\u0437\u043e\u043d \u043f\u043e \u043e\u0441\u0438 Y \u0441\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043e\u0442 <code>0.03<\/code><em> \u0434\u043e <\/em><code>0.23<\/code>.<\/p>\n<p><strong>\u041f\u0440\u0438\u043c\u0435\u0447\u0430\u043d\u0438\u0435:<\/strong> \u041f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u043c\u044b \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c <code>etcd<\/code> \u0432 \u043a\u043b\u0430\u0441\u0442\u0435\u0440\u0435 <code>kind<\/code>, \u0441\u0435\u0440\u0432\u0435\u0440 API Kubernetes \u0438 \u0434\u0440\u0443\u0433\u0438\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0443\u044e\u0442 \u0441 \u043d\u0438\u043c \u0432 \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u043a \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0435 \u043e\u0442 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u044b.<\/p>\n<p>\u042d\u0442\u043e\u0433\u043e \u0438 \u0441\u043b\u0435\u0434\u043e\u0432\u0430\u043b\u043e \u043e\u0436\u0438\u0434\u0430\u0442\u044c: \u043e\u0434\u043d\u0438 \u0447\u0430\u0441\u044b \u0432\u0440\u044f\u0434 \u043b\u0438 \u043e\u043a\u0430\u0436\u0443\u0442 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0435 \u0432\u043b\u0438\u044f\u043d\u0438\u0435. \u041d\u043e \u0435\u0441\u043b\u0438 \u043c\u044b \u0443\u0432\u0435\u043b\u0438\u0447\u0438\u043c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0447\u0430\u0441\u043e\u0432, \u0442\u043e \u043d\u0430\u0447\u043d\u0435\u043c \u0441\u0442\u0430\u043b\u043a\u0438\u0432\u0430\u0442\u044c\u0441\u044f \u0441 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430\u043c\u0438. \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u043c \u043d\u0430\u0448\u0443 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0443 \u0442\u0430\u043a, \u0447\u0442\u043e\u0431\u044b \u043e\u043d\u0430 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u043b\u0430 10 000 \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u043e\u0432 \u043d\u0430 <code>ConfigMaps<\/code>.<\/p>\n<p><code>manydirectwatch.go<\/code><\/p>\n<pre><code>package main  import ( \"context\" \"fmt\" \"os\" \"os\/signal\"  \"go.etcd.io\/etcd\/client\/pkg\/v3\/transport\" clientv3 \"go.etcd.io\/etcd\/client\/v3\" v1 \"k8s.io\/api\/core\/v1\" \"k8s.io\/apimachinery\/pkg\/runtime\" \"k8s.io\/apimachinery\/pkg\/runtime\/serializer\" \"k8s.io\/apiserver\/pkg\/storage\" \"k8s.io\/apiserver\/pkg\/storage\/etcd3\" \"k8s.io\/apiserver\/pkg\/storage\/value\/encrypt\/identity\" \"k8s.io\/kubernetes\/pkg\/apis\/core\" k8sv1 \"k8s.io\/kubernetes\/pkg\/apis\/core\/v1\" )  func main() { tlsConfig, err := (transport.TLSInfo{ CertFile:       \".\/pki\/etcd\/server.crt\", KeyFile:        \".\/pki\/etcd\/server.key\", TrustedCAFile:  \".\/pki\/etcd\/ca.crt\", ClientCertFile: \".\/pki\/apiserver-etcd-client.crt\", ClientKeyFile:  \".\/pki\/apiserver-etcd-client.key\", }).ClientConfig() if err != nil { panic(err) } c, err := clientv3.New(clientv3.Config{ Endpoints: []string{\"https:\/\/127.0.0.1:2379\"}, TLS:       tlsConfig, }) if err != nil { panic(err) }  scheme := runtime.NewScheme() k8sv1.AddToScheme(scheme) core.AddToScheme(scheme) f := serializer.NewCodecFactory(scheme) s := etcd3.New(c, f.CodecForVersions(nil, f.UniversalDecoder(), nil, k8sv1.SchemeGroupVersion), nil, \"registry\", v1.Resource(\"ConfigMap\"), identity.NewEncryptCheckTransformer(), true, etcd3.NewDefaultLeaseManagerConfig()) for i := 0; i &lt; 10000; i++ { w, err := s.Watch(context.Background(), \"configmaps\", storage.ListOptions{ Predicate: storage.Everything, Recursive: true, }) if err != nil { panic(err) } go func() { for e := range w.ResultChan() { co, ok := e.Object.(*v1.ConfigMap) if !ok { panic(\"not a config map!\") } fmt.Printf(\"%s || %s\/%s\\n\", e.Type, co.Namespace, co.Name) } }() }  stop := make(chan os.Signal) signal.Notify(stop, os.Interrupt) &lt;-stop }<\/code><\/pre>\n<p>\u0415\u0441\u043b\u0438 \u043c\u044b \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u043c \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0443 \u0441\u0435\u0439\u0447\u0430\u0441, \u0442\u043e \u0434\u043e\u043b\u0436\u043d\u044b \u0443\u0432\u0438\u0434\u0435\u0442\u044c, \u0447\u0442\u043e \u0432\u0441\u0435 10 000 \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u043e\u0432 \u043f\u043e\u043b\u0443\u0447\u0430\u044e\u0442 \u0441\u043f\u0438\u0441\u043e\u043a \u0432\u0441\u0435\u0445 <code>ConfigMaps<\/code> \u0432 \u043a\u043b\u0430\u0441\u0442\u0435\u0440\u0435. \u041a\u043e\u0433\u0434\u0430 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043d\u043e\u0432\u0443\u044e <code>ConfigMap<\/code>, \u043c\u044b \u0441\u043c\u043e\u0436\u0435\u043c \u0443\u0432\u0438\u0434\u0435\u0442\u044c, \u0447\u0442\u043e \u0432\u0441\u0435 \u0447\u0430\u0441\u044b \u043f\u043e\u043b\u0443\u0447\u0430\u044e\u0442 \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u044f.<\/p>\n<pre><code>$ go run<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-406095","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/406095","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=406095"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/406095\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=406095"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=406095"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=406095"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}