{"id":425926,"date":"2024-07-10T09:02:11","date_gmt":"2024-07-10T09:02:11","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=425926"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=425926","title":{"rendered":"<span>\u0420\u0430\u0431\u043e\u0442\u0430 \u0441 \u0438\u043d\u0444\u043e\u0440\u043c\u0435\u0440\u0430\u043c\u0438 Java kubernetes client<\/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\/w780q1\/getpro\/habr\/upload_files\/0e6\/8bf\/2c5\/0e68bf2c5b6f587d06d96e4582ae104c.jpg\" width=\"700\" height=\"850\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/0e6\/8bf\/2c5\/0e68bf2c5b6f587d06d96e4582ae104c.jpg\" data-blurred=\"true\"\/><\/figure>\n<p><strong>\u041e\u0433\u043b\u0430\u0432\u043b\u0435\u043d\u0438\u0435<\/strong><\/p>\n<ol>\n<li>\n<p><a href=\"#introduction\">\u0412\u0432\u0435\u0434\u0435\u043d\u0438\u0435<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#project-creation\">\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0441 \u043d\u0443\u043b\u044f<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#api-clients\">\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u0432 API \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 kubernetes<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#initialization\">\u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0438\u043d\u0444\u043e\u0440\u043c\u0435\u0440\u043e\u0432 \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f\u00a0 Pods, Nodes \u0438 Ingresses<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#listener\">\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 Listener \u0434\u043b\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0438\u043d\u0444\u043e\u0440\u043c\u0435\u0440\u043e\u0432<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#data-extraction\">\u0418\u0437\u0432\u043b\u0435\u0447\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438\u0437 \u0438\u043d\u0444\u043e\u0440\u043c\u0435\u0440\u043e\u0432 \u0438 \u0438\u0445 \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0430 \u0434\u043b\u044f \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u0447\u0435\u0440\u0435\u0437 API<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#result-check\">\u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430<\/a><\/p>\n<\/li>\n<\/ol>\n<p><strong>\u041d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0439 \u0434\u0438\u0441\u043a\u043b\u0435\u0439\u043c\u0435\u0440<\/strong><\/p>\n<p>\u041f\u0440\u043e\u0435\u043a\u0442 \u0441\u043e\u0437\u0434\u0430\u043d \u0434\u043b\u044f \u0443\u0447\u0435\u0431\u043d\u044b\u0445 \u0446\u0435\u043b\u0435\u0439, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043a\u043e\u0434 \u0432 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u043c\u0435\u0441\u0442\u0430\u0445 \u043d\u0430\u043c\u0435\u0440\u0435\u043d\u043d\u043e (\u0438\u043b\u0438 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e) \u0443\u043f\u0440\u043e\u0449\u0435\u043d.<\/p>\n<p>\u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, <strong>path<\/strong> \u0434\u043b\u044f <strong>ingress<\/strong> \u0432\u0437\u044f\u0442 \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u0435\u0440\u0432\u044b\u0439 \u0438\u0437 \u0441\u043f\u0438\u0441\u043a\u0430, \u043d\u0430 \u043a\u0430\u0436\u0434\u044b\u0439 \u0432\u044b\u0437\u043e\u0432 API \u0441\u043e\u0437\u0434\u0430\u0435\u0442\u0441\u044f \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u0438 \u0442. \u0434.<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/ceb\/ec6\/c9f\/cebec6c9f25eba7066ecfffafb14a04a.jpg\" width=\"480\" height=\"272\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/ceb\/ec6\/c9f\/cebec6c9f25eba7066ecfffafb14a04a.jpg\" data-blurred=\"true\"\/><\/figure>\n<p><a class=\"anchor\" name=\"introduction\" id=\"introduction\"><\/a><\/p>\n<ol>\n<li>\n<p><strong>\u0412\u0432\u0435\u0434\u0435\u043d\u0438\u0435<\/strong><\/p>\n<\/li>\n<\/ol>\n<p>\u041f\u0440\u0438\u0432\u0435\u0442, \u043c\u0435\u043d\u044f \u0437\u043e\u0432\u0443\u0442 \u0421\u0435\u0440\u0433\u0435\u0439, \u0441\u0442\u0430\u0440\u0448\u0438\u0439 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a 80 \u0443\u0440\u043e\u0432\u043d\u044f \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u0438 DataBlend (\u0433\u0440\u0443\u043f\u043f\u0430 \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u0439 GlowByte). \u041d\u0430\u0448\u0430 \u043a\u043e\u043c\u0430\u043d\u0434\u0430 \u0437\u0430\u043d\u0438\u043c\u0430\u0435\u0442\u0441\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u043e\u0439 \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u0430 ClusterManager, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435\u043c \u0438 \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u0442 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0442\u0430\u043a\u0438\u0445 \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u043e\u0432, \u043a\u0430\u043a GreenPlum, ClickHouse, DWH, Nova \u0438 \u0442. \u0434.<\/p>\n<p>\u041e\u043a\u043e\u043b\u043e \u043f\u043e\u043b\u0443\u0442\u043e\u0440\u0430 \u043b\u0435\u0442 \u043d\u0430\u0437\u0430\u0434 \u0443 \u043d\u0430\u0441 \u043f\u043e\u044f\u0432\u0438\u043b\u0430\u0441\u044c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u044c \u0441\u043e\u0431\u0438\u0440\u0430\u0442\u044c \u0438 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0442\u044c \u0432 \u0443\u0434\u043e\u0431\u043d\u043e\u043c \u0432\u0438\u0434\u0435 \u0438 \u0440\u0430\u0437\u0440\u0435\u0437\u0430\u0445 \u043c\u0435\u0442\u0440\u0438\u043a\u0438 \u0438 \u0434\u0430\u043d\u043d\u044b\u0435 \u043e\u0431 \u043e\u0431\u044a\u0435\u043a\u0442\u0430\u0445 \u043a\u043b\u0430\u0441\u0442\u0435\u0440\u043e\u0432 Kubernetes, \u0432 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0440\u0430\u0437\u0432\u0435\u0440\u043d\u0443\u0442 \u043f\u0440\u043e\u0434\u0443\u043a\u0442 Nova.\u00a0<\/p>\n<p>\u0414\u043b\u044f \u044d\u0442\u0438\u0445 \u0446\u0435\u043b\u0435\u0439 \u0431\u044b\u043b \u0432\u044b\u0431\u0440\u0430\u043d \u043e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0439 <strong>kubernetes-client<\/strong> \u0434\u043b\u044f <strong>Java.<\/strong><\/p>\n<p>\u041f\u043e\u043d\u0430\u0447\u0430\u043b\u0443 \u043c\u044b \u043f\u043e\u0448\u043b\u0438 \u043f\u043e \u043f\u0443\u0442\u0438 \u0441\u0431\u043e\u0440\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 \u043e \u043d\u043e\u0434\u0430\u0445, \u043f\u043e\u0434\u0430\u0445 \u0438 \u0442\u0430\u043a \u0434\u0430\u043b\u0435\u0435 \u043f\u043e \u0440\u0430\u0441\u043f\u0438\u0441\u0430\u043d\u0438\u044e \u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044e \u0438\u0445 \u0432 \u0411\u0414 \u0432 \u0443\u0434\u043e\u0431\u043d\u043e\u043c \u0432\u0438\u0434\u0435. \u041d\u043e, \u043a\u0430\u043a \u044d\u0442\u043e \u0447\u0430\u0441\u0442\u043e \u0431\u044b\u0432\u0430\u0435\u0442, \u0446\u0435\u043b\u0438 \u0438 \u0436\u0435\u043b\u0430\u043d\u0438\u044f \u0441\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0435\u043c \u043c\u0435\u043d\u044f\u044e\u0442\u0441\u044f, \u0438 \u0436\u0438\u0437\u043d\u044c \u0437\u0430\u0441\u0442\u0430\u0432\u0438\u043b\u0430 \u043f\u0435\u0440\u0435\u0439\u0442\u0438 \u043a \u043c\u0433\u043d\u043e\u0432\u0435\u043d\u043d\u043e\u043c\u0443 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044e \u0438 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044e \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439.<\/p>\n<p>\u041b\u0443\u0447\u0448\u0435 \u0432\u0441\u0435\u0433\u043e \u0434\u043b\u044f \u044d\u0442\u043e\u0439 \u0446\u0435\u043b\u0438 \u043f\u043e\u0434\u0445\u043e\u0434\u0438\u0442 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c \u0438\u043d\u0444\u043e\u0440\u043c\u0435\u0440\u043e\u0432 <strong>kubernetes-client.<\/strong><\/p>\n<p>\u0418 \u0441\u0435\u0439\u0447\u0430\u0441 \u043c\u044b \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c, \u0441 \u043a\u0430\u043a\u043e\u0439 \u0441\u0442\u043e\u0440\u043e\u043d\u044b \u0438\u0445 \u043b\u0443\u0447\u0448\u0435 \u043d\u0430\u0447\u0438\u043d\u0430\u0442\u044c \u0435\u0441\u0442\u044c.<\/p>\n<p>\u041d\u0430\u043f\u0438\u0448\u0435\u043c \u043f\u0440\u043e\u0441\u0442\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0432 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0435\u0442 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 <strong>Pods<\/strong>, <strong>Nodes<\/strong> \u0438 <strong>Ingresses<\/strong> \u0438 \u043f\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0443 \u043e\u0442\u0434\u0430\u0435\u0442 \u043d\u0430\u043c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u043d\u0438\u0445. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043c\u044b \u043f\u043e\u0432\u0435\u0441\u0438\u043c \u0438\u043d\u0444\u043e\u0440\u043c\u0435\u0440\u044b \u043d\u0430 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0435 \u0440\u0435\u0441\u0443\u0440\u0441\u044b <strong>Kubernetes<\/strong>.<\/p>\n<p>\u0415\u0441\u043b\u0438 \u043d\u0443\u0436\u043d\u043e \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0442\u044c CRD-\u0440\u0435\u0441\u0443\u0440\u0441\u044b, \u0442\u043e \u0438\u043d\u0444\u043e\u0440\u043c\u0435\u0440\u044b, \u043a \u0441\u043e\u0436\u0430\u043b\u0435\u043d\u0438\u044e, \u043d\u0435 \u043f\u043e\u0434\u043e\u0439\u0434\u0443\u0442.<\/p>\n<p>\u041f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u0438 \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u0440\u0435\u0441\u0443\u0440\u0441\u0430\u0445 <strong>Kubernetes <\/strong>\u0431\u0443\u0434\u0435\u043c \u0432 \u043f\u0430\u043c\u044f\u0442\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f.\u00a0<\/p>\n<blockquote>\n<p>&#171;\u0410 \u0443 \u043d\u0430\u0441 \u044d\u0442\u043e\u0439 \u043f\u0430\u043c\u044f\u0442\u0438 \u2013 \u0437\u0430\u0432\u0430\u043b\u0438\u0441\u044c, \u0443 \u043d\u0430\u0441 \u043f\u0430\u043f\u0430 \u043d\u0430 \u0444\u0430\u0431\u0440\u0438\u043a\u0435 \u043f\u043e \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0441\u0442\u0432\u0443 \u0447\u0438\u043f\u043e\u0432 \u043f\u0430\u043c\u044f\u0442\u0438 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442&#187;<\/p>\n<\/blockquote>\n<p> \u043a\u0430\u043a \u0441\u043a\u0430\u0437\u0430\u043b \u0431\u044b \u043a\u043e\u0442 \u041c\u0430\u0442\u0440\u043e\u0441\u043a\u0438\u043d.<\/p>\n<p><a class=\"anchor\" name=\"project-creation\" id=\"project-creation\"><\/a><\/p>\n<ol start=\"2\">\n<li>\n<p><strong>\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0441 \u043d\u0443\u043b\u044f<\/strong><\/p>\n<\/li>\n<\/ol>\n<p>\u042d\u0442\u043e\u0442 \u043f\u0443\u043d\u043a\u0442 \u043d\u0435 \u0438\u043c\u0435\u0435\u0442 \u043f\u0440\u044f\u043c\u043e\u0433\u043e \u043e\u0442\u043d\u043e\u0448\u0435\u043d\u0438\u044f \u043a <strong>kubernetes-client<\/strong>, \u0435\u0441\u043b\u0438 \u043d\u0435\u0442 \u043d\u0443\u0436\u0434\u044b \u043f\u043e\u0432\u0442\u043e\u0440\u044f\u0442\u044c \u043f\u0440\u043e\u0435\u043a\u0442, \u044d\u0442\u043e\u0442 \u043f\u0443\u043d\u043a\u0442 \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u043e\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0438 \u0441\u043a\u0430\u0447\u0430\u0442\u044c \u0443\u0436\u0435 \u0433\u043e\u0442\u043e\u0432\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442.<\/p>\n<p>\u0421\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 <a href=\"https:\/\/github.com\/KapustinS\/kubernetes-manager\"><u>GitHub<\/u><\/a> \u043f\u0440\u043e\u0435\u043a\u0442\u0430.<\/p>\n<p>\u0414\u043b\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0438\u0437 \u043f\u0430\u043f\u043a\u0438 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u043a\u043e\u043c\u0430\u043d\u0434\u0443:\u00a0<\/p>\n<p><strong><em>mvn clean generate-sources<\/em><\/strong><\/p>\n<p>\u0418 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043f\u0440\u043e\u0435\u043a\u0442 \u0441 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u043c:<\/p>\n<p><strong><em>-Dkubernetes.config-file.path=your-path\/config.kubeconfig<\/em><\/strong><\/p>\n<p>\u0417\u0430\u043c\u0435\u043d\u0438\u0432 <em>\u201c<\/em><strong><em>your-path\/config.kubeconfig<\/em><\/strong><em>\u201d<\/em> \u043d\u0430 \u043f\u0443\u0442\u044c \u043a \u0441\u0432\u043e\u0435\u043c\u0443 \u043a\u043e\u043d\u0444\u0438\u0433-\u0444\u0430\u0439\u043b\u0443 <strong>Kubernetes.<\/strong><\/p>\n<p>\u0421\u0442\u0435\u043a \u043f\u0440\u043e\u0435\u043a\u0442\u0430:<br \/><strong>Java 17, Spring Boot 3.3.0, Mapstruct, Lombok, Kubernetes-client 20.0.1, OpenApi 3.0.<\/strong><\/p>\n<details class=\"spoiler\">\n<summary>\u041f\u0440\u043e\u0446\u0435\u0441\u0441 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e<\/summary>\n<div class=\"spoiler__content\">\n<p>\u041f\u0435\u0440\u0432\u044b\u043c \u0434\u0435\u043b\u043e\u043c \u043c\u044b \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u043d\u043e\u0432\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 <strong>Spring Boot:<\/strong><\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/2aa\/7c6\/708\/2aa7c6708a8f2b63be1383962a1403f7.png\" width=\"512\" height=\"451\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/2aa\/7c6\/708\/2aa7c6708a8f2b63be1383962a1403f7.png\"\/><\/figure>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/2a4\/6e5\/414\/2a46e54148aa8869d5f0a2cf03bca8c3.png\" width=\"512\" height=\"440\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/2a4\/6e5\/414\/2a46e54148aa8869d5f0a2cf03bca8c3.png\"\/><\/figure>\n<p>\u0414\u0430\u043b\u0435\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0432 <strong>pom.xml<\/strong> \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u0434\u043b\u044f \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u043e\u0432 \u0438\u0437 \u0441\u043f\u0435\u0446\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 <strong>OpenApi 3.0<\/strong>, <strong>mapstruct<\/strong> \u0434\u043b\u044f \u043c\u0430\u043f\u043f\u0438\u043d\u0433\u0430 DTO \u0438, \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e, <strong>kubernetes-client<\/strong>:<\/p>\n<pre><code class=\"xml\">&lt;dependencies>    &lt;dependency>        &lt;groupId>org.springframework.boot&lt;\/groupId>        &lt;artifactId>spring-boot-starter-web&lt;\/artifactId>    &lt;\/dependency>      &lt;dependency>        &lt;groupId>org.projectlombok&lt;\/groupId>        &lt;artifactId>lombok&lt;\/artifactId>        &lt;optional>true&lt;\/optional>    &lt;\/dependency>    &lt;dependency>        &lt;groupId>org.springframework.boot&lt;\/groupId>        &lt;artifactId>spring-boot-starter-test&lt;\/artifactId>        &lt;scope>test&lt;\/scope>    &lt;\/dependency>      &lt;dependency>        &lt;groupId>io.swagger.core.v3&lt;\/groupId>        &lt;artifactId>swagger-annotations&lt;\/artifactId>        &lt;version>2.2.22&lt;\/version>    &lt;\/dependency>      &lt;dependency>        &lt;groupId>javax.validation&lt;\/groupId>        &lt;artifactId>validation-api&lt;\/artifactId>        &lt;version>2.0.1.Final&lt;\/version>    &lt;\/dependency>      &lt;dependency>        &lt;groupId>org.mapstruct&lt;\/groupId>        &lt;artifactId>mapstruct&lt;\/artifactId>        &lt;version>${mapstruct.version}&lt;\/version>    &lt;\/dependency>      &lt;dependency>        &lt;groupId>io.kubernetes&lt;\/groupId>        &lt;artifactId>client-java&lt;\/artifactId>        &lt;version>20.0.1&lt;\/version>    &lt;\/dependency> &lt;\/dependencies> <\/code><\/pre>\n<p>\u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043f\u043b\u0430\u0433\u0438\u043d\u044b \u0434\u043b\u044f \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u043e\u0432 \u0438 DTO \u0438\u0437 \u0441\u043f\u0435\u0446\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 <strong>OpenApi 3.0<\/strong>, \u0430 \u0442\u0430\u043a\u0436\u0435 <strong>maven plugin<\/strong>, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u043f\u0440\u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u043c \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0440\u044b <strong>lombok<\/strong> \u0438 <strong>mapstruct<\/strong>, \u043d\u0435 \u0437\u0430\u0431\u0443\u0434\u044c\u0442\u0435 \u0437\u0430\u043c\u0435\u043d\u0438\u0442\u044c \u043f\u0443\u0442\u0438 \u043d\u0430 \u0441\u0432\u043e\u0438:<\/p>\n<pre><code class=\"xml\">&lt;build>    &lt;plugins>        &lt;plugin>            &lt;groupId>org.apache.maven.plugins&lt;\/groupId>            &lt;artifactId>maven-compiler-plugin&lt;\/artifactId>            &lt;version>3.8.1&lt;\/version>            &lt;configuration>                &lt;source>${java.version}&lt;\/source>                &lt;target>${java.version}&lt;\/target>                &lt;annotationProcessorPaths>                    &lt;path>                        &lt;groupId>org.mapstruct&lt;\/groupId>                        &lt;artifactId>mapstruct-processor&lt;\/artifactId>                        &lt;version>${mapstruct.version}&lt;\/version>                    &lt;\/path>                    &lt;path>                        &lt;groupId>org.projectlombok&lt;\/groupId>                        &lt;artifactId>lombok&lt;\/artifactId>                        &lt;version>1.18.22&lt;\/version>                    &lt;\/path>                &lt;\/annotationProcessorPaths>            &lt;\/configuration>        &lt;\/plugin>        &lt;plugin>            &lt;groupId>org.openapitools&lt;\/groupId>            &lt;artifactId>openapi-generator-maven-plugin&lt;\/artifactId>            &lt;version>7.6.0&lt;\/version>            &lt;executions>                &lt;execution>                    &lt;id>core&lt;\/id>                    &lt;goals>                        &lt;goal>generate&lt;\/goal>                    &lt;\/goals>                    &lt;configuration>                        &lt;inputSpec>src\/main\/resources\/openapi.yml&lt;\/inputSpec>                        &lt;output>target\/generated-sources\/openapi&lt;\/output>                        &lt;generatorName>spring&lt;\/generatorName>                        &lt;library>spring-cloud&lt;\/library>                        &lt;apiPackage>ru.kapustin.kubernetesmanager.controller&lt;\/apiPackage>                        &lt;modelPackage>ru.kapustin.kubernetesmanager.model&lt;\/modelPackage>                        &lt;generateSupportingFiles>false&lt;\/generateSupportingFiles>                        &lt;templateDirectory>src\/main\/resources\/templates&lt;\/templateDirectory>                        &lt;configOptions>                            &lt;openApiNullable>false&lt;\/openApiNullable>                            &lt;interfaceOnly>true&lt;\/interfaceOnly>                            &lt;useTags>true&lt;\/useTags>                        &lt;\/configOptions>                    &lt;\/configuration>                &lt;\/execution>            &lt;\/executions>        &lt;\/plugin>    &lt;\/plugins> &lt;\/build> <\/code><\/pre>\n<p>\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0444\u0430\u0439\u043b <strong>openapi.yml<\/strong> \u0441\u043e \u0441\u043f\u0435\u0446\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0435\u0439 <strong>OpenApi 3.0,<\/strong> \u0438\u0437 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0431\u0443\u0434\u0443\u0442 \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u044b \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u044b \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u043e\u0432 \u0438 DTO\u0448\u043a\u0438:<\/p>\n<pre><code class=\"yaml\">openapi: 3.0.1 servers:  - url: '{protocol}:{domain}\/kubernetes-manager\/api'   info:  title: Kubernetes manager Service API  description: Kubernetes manager Service API  version: 1.0.0   paths:  \/pod\/list:    get:      tags:        - ResourceList      operationId: getPods      description: Get list of pods      responses:        200:          description: Get list of pods          content:            application\/json:              schema:                $ref: '#\/components\/schemas\/PodListResponse'    \/node\/list:    get:      tags:        - ResourceList      operationId: getNodes      description: Get list of nodes      responses:        200:          description: Get list of nodes          content:            application\/json:              schema:                $ref: '#\/components\/schemas\/NodeListResponse'    \/ingress\/{namespace}\/list:    get:      tags:        - ResourceList      operationId: getIngresses      description: Get list of ingresses      parameters:        - name: namespace          in: path          required: true          schema:              type: string      responses:        200:          description: Get list of ingresses          content:            application\/json:              schema:                $ref: '#\/components\/schemas\/IngressListResponse'   components:  schemas:    PodListResponse:      type: object      properties:        pods:          type: array          items:            $ref: '#\/components\/schemas\/Pod'        total:          type: integer          format: int32      Pod:      type: object      properties:        name:          type: string        namespace:          type: string        status:          type: string        restartCount:          type: integer          format: int32        creationTimestamp:          type: string          format: date-time        labels:          type: object          additionalProperties:            type: string        annotations:          type: object          additionalProperties:            type: string      NodeListResponse:      type: object      properties:        nodes:          type: array          items:            $ref: '#\/components\/schemas\/Node'        total:          type: integer          format: int32      Node:      type: object      properties:        name:          type: string        status:          type: string        labels:          type: object          additionalProperties:            type: string        annotations:          type: object          additionalProperties:            type: string      IngressListResponse:      type: object      properties:        ingresses:          type: array          items:            $ref: '#\/components\/schemas\/Ingress'        total:          type: integer          format: int32      Ingress:      type: object      properties:        name:          type: string        namespace:          type: string        host:          type: string        path:          type: string <\/code><\/pre>\n<p>\u0417\u0430\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u0444\u0430\u0439\u043b <strong>application.yml<\/strong> (\u043b\u0438\u0431\u043e <strong>application.properties<\/strong>, \u043a\u0430\u043a \u0443\u0434\u043e\u0431\u043d\u043e)<\/p>\n<pre><code class=\"yaml\">server:  servlet:    context-path: \/kubernetes-manager\/api  port: 8080   kubernetes:  config-file:    path: ${kubernetes.config-file.path}<\/code><\/pre>\n<p>\u0412 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e <strong>kubernetes.config-file.path <\/strong>\u043c\u044b \u0431\u0443\u0434\u0435\u043c \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0442\u044c \u043f\u0443\u0442\u044c \u043a \u043d\u0430\u0448\u0435\u043c\u0443 \u043a\u043e\u043d\u0444\u0438\u0433-\u0444\u0430\u0439\u043b\u0443 <strong>Kubernetes<\/strong>.<\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u043c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u044b \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u043e\u0432 \u0438 \u043c\u043e\u0434\u0435\u043b\u0438 \u043a\u043e\u043c\u0430\u043d\u0434\u043e\u0439:<br \/><strong><em>mvn clean generate-sources\u00a0<\/em><\/strong><\/p>\n<p>\u0415\u0441\u043b\u0438 \u0432\u0441\u0435 \u043f\u0440\u043e\u0448\u043b\u043e \u0443\u0441\u043f\u0435\u0448\u043d\u043e, \u0442\u043e \u0432 \u043f\u0430\u043f\u043a\u0435 <strong>target<\/strong> \u043c\u044b \u0443\u0432\u0438\u0434\u0438\u043c \u0442\u0430\u043a\u0443\u044e \u043a\u0430\u0440\u0442\u0438\u043d\u0443:<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/ba2\/f0e\/0c3\/ba2f0e0c3d00c47fa8f822f7582941f9.png\" width=\"462\" height=\"475\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/ba2\/f0e\/0c3\/ba2f0e0c3d00c47fa8f822f7582941f9.png\"\/><\/figure>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043f\u043e\u043c\u0435\u0447\u0430\u0435\u043c package <strong>generated-sources<\/strong> \u043a\u0430\u043a \u201c<strong><em>Generated Source Root<\/em><\/strong>\u201d, \u0432 <strong>Intellij IDEA<\/strong> \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u043c \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u043d\u043e\u0435 \u043c\u0435\u043d\u044e \u043d\u0430 \u043d\u0443\u0436\u043d\u043e\u0439 \u043f\u0430\u043f\u043a\u0435 \u0438 \u0432\u044b\u0431\u0438\u0440\u0430\u0435\u043c \u201c<strong><em>Mark Directory as\u201d\/ \u201cGenerated Source Root<\/em><\/strong>\u201d. \u041f\u0430\u043f\u043a\u0430 \u0432 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435 \u043f\u043e\u0441\u0438\u043d\u0435\u0435\u0442.<\/p>\n<p>\u0415\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u043d\u0435 <strong>IDEA<\/strong>, \u043c\u043e\u0438 \u043f\u043e\u043b\u043d\u043e\u043c\u043e\u0447\u0438\u044f \u0432\u0441\u0435, \u0440\u0430\u0437\u0431\u0438\u0440\u0430\u0439\u0442\u0435\u0441\u044c.<\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440 <strong>ResourceListController<\/strong>, \u0438\u043c\u043f\u043b\u0435\u043c\u0435\u043d\u0442\u0438\u0440\u0443\u0435\u043c \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 <strong>ResourceListApi<\/strong>:<\/p>\n<pre><code class=\"java\">@RestController @RequiredArgsConstructor public class ResourceListController implements ResourceListApi {    private final PodListService podListService;    private final NodeListService nodeListService;    private final IngressListService ingressListService;      @Override    public ResponseEntity&lt;IngressList> getIngresses() {        return ResponseEntity.ok(ingressListService.getIngresses());    }      @Override    public ResponseEntity&lt;NodeList> getNodes() {        return ResponseEntity.ok(nodeListService.getNodes());    }      @Override    public ResponseEntity&lt;PodList> getPods() {        return ResponseEntity.ok(podListService.getPods());    } }<\/code><\/pre>\n<p>\u041a\u043b\u0430\u0441\u0441\u044b <strong>PodListService, NodeListService<\/strong> \u0438 <strong>IngressListService<\/strong> \u0431\u0443\u0434\u0443\u0442 \u0441\u043e\u0437\u0434\u0430\u043d\u044b \u0432 \u043f\u0443\u043d\u043a\u0442\u0435 6.<\/p>\n<p>\u041d\u0430 \u044d\u0442\u043e\u043c \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0430, \u043c\u043e\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u0442\u044c \u043a \u0442\u043e\u043c\u0443, \u0440\u0430\u0434\u0438 \u0447\u0435\u0433\u043e \u044d\u0442\u043e \u0437\u0430\u0442\u0435\u0432\u0430\u043b\u043e\u0441\u044c.<\/p>\n<\/div>\n<\/details>\n<p><a class=\"anchor\" name=\"api-clients\" id=\"api-clients\"><\/a><\/p>\n<ol start=\"3\">\n<li>\n<p><strong>\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u0432 API \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 Kubernetes<\/strong><\/p>\n<\/li>\n<\/ol>\n<p>\u041d\u0430 \u043c\u043e\u043c\u0435\u043d\u0442 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0441\u0442\u0430\u0442\u044c\u0438 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u044f\u044f \u0432\u0435\u0440\u0441\u0438\u044f \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u2013 <strong>20.0.1<\/strong>. \u041e\u0442 \u0432\u0435\u0440\u0441\u0438\u0438 \u043a \u0432\u0435\u0440\u0441\u0438\u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438, \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043a\u043b\u0430\u0441\u0441\u043e\u0432, \u043c\u043e\u0434\u0435\u043b\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u0442. \u0434. \u0443 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u043c\u043e\u0436\u0435\u0442 \u043c\u0435\u043d\u044f\u0442\u044c\u0441\u044f, \u0443\u0447\u0438\u0442\u044b\u0432\u0430\u0439\u0442\u0435 \u044d\u0442\u043e.<br \/>\u0420\u0430\u0434\u0443\u0435\u0442, \u0447\u0442\u043e \u0432\u044b\u043f\u0443\u0441\u043a\u0430\u044e\u0442\u0441\u044f \u0442\u0430\u043a\u0436\u0435 \u043d\u043e\u0432\u044b\u0435 \u0432\u0435\u0440\u0441\u0438\u0438 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u0441 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u043e\u0439 \u0441\u0442\u0430\u0440\u043e\u0439 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u043c\u0435\u0442\u043e\u0434\u043e\u0432. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0432\u0435\u0440\u0441\u0438\u044f <strong>20.0.1-legacy<\/strong> \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u043a\u043e\u0434, \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u0439 \u0434\u043b\u044f <strong>18<\/strong> \u0432\u0435\u0440\u0441\u0438\u0438 \u043a\u043b\u0438\u0435\u043d\u0442\u0430.<\/p>\n<p>\u0422\u0430\u043a\u0436\u0435 \u0432\u0435\u0440\u0441\u0438\u044f <strong>20.0.1<\/strong> \u043f\u0440\u0435\u043a\u0440\u0430\u0442\u0438\u043b\u0430 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0443 <strong>Java 8<\/strong>.<\/p>\n<p>\u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0442\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/d5d\/a61\/faa\/d5da61faae7168bd03cd7a0c83cb691e.png\" width=\"421\" height=\"512\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/d5d\/a61\/faa\/d5da61faae7168bd03cd7a0c83cb691e.png\"\/><\/figure>\n<p>\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043a\u043b\u0430\u0441\u0441 <strong>KubernetesResourceService<\/strong>, \u043e\u0442\u0432\u0435\u0447\u0430\u044e\u0449\u0438\u0439 \u0437\u0430 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0438 \u0432\u044b\u0434\u0430\u0447\u0443 api \u0434\u043b\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a <strong>Kubernetes<\/strong><\/p>\n<pre><code class=\"java\">@Service public class KubernetesResourceService {      @Value(\"${kubernetes.config-file.path}\")    private String configFilePath;      private static final Logger LOGGER = LoggerFactory.getLogger(KubernetesResourceService.class);      public Optional&lt;CoreV1Api> getCoreV1Api() {        Optional&lt;ApiClient> clientOptional = getApiClient();        if (clientOptional.isEmpty()) {            return Optional.empty();        }        ApiClient client = clientOptional.get();        CoreV1Api api = new CoreV1Api(client);        return Optional.of(api);    }      public Optional&lt;SharedInformerFactory> getSharedInformerFactory() {        Optional&lt;ApiClient> clientOptional = getApiClient();        if (clientOptional.isEmpty()) {            LOGGER.warn(\"ApiClient is null.\");            return Optional.empty();        }        ApiClient client = clientOptional.get();        client.setReadTimeout(0);        SharedInformerFactory factory = new SharedInformerFactory(client);        return Optional.of(factory);    }      public Optional&lt;NetworkingV1Api> getNetworkingApi() {        Optional&lt;ApiClient> clientOptional = getApiClient();        if (clientOptional.isEmpty()) {            return Optional.empty();        }        ApiClient client = clientOptional.get();        NetworkingV1Api api = new NetworkingV1Api();        api.setApiClient(client);        return Optional.of(api);    }      private Optional&lt;String> configFile() {        try {            Path filePath = Paths.get(configFilePath);            byte[] fileBytes = Files.readAllBytes(filePath);            String configFile = new String(fileBytes);            return Optional.of(configFile);        } catch (IOException e) {            LOGGER.error(\"Error while getting Kubernetes configFile: {}\", e.getMessage());            return Optional.empty();        }    }      private Optional&lt;ApiClient> getApiClient() {        Optional&lt;String> configFileOptional = configFile();        if (configFileOptional.isEmpty()) {            LOGGER.error(\"Config file is empty or null.\");            return Optional.empty();        }        String configFile = configFileOptional.get();          try (Reader reader = new StringReader(configFile)){            KubeConfig kubeConfig = KubeConfig.loadKubeConfig(reader);            ApiClient client = ClientBuilder.kubeconfig(kubeConfig).build();            client.setReadTimeout(60000);            return Optional.ofNullable(client);        } catch (IOException e) {            LOGGER.error(\"Error while getting kubernetes client from config file\");            return Optional.empty();        }    } }        private Optional&lt;ApiClient> getApiClient() {        Optional&lt;String> configFileOptional = configFile();        if (configFileOptional.isEmpty()) {            LOGGER.error(\"Config file is empty or null.\");            return Optional.empty();        }        String configFile = configFileOptional.get();          try (Reader reader = new StringReader(configFile)){            KubeConfig kubeConfig = KubeConfig.loadKubeConfig(reader);            ApiClient client = ClientBuilder.kubeconfig(kubeConfig).build();            client.setReadTimeout(60000);            return Optional.ofNullable(client);        } catch (IOException e) {            LOGGER.error(\"Error while getting kubernetes client from config file\");            return Optional.empty();        }    } }<\/code><\/pre>\n<p>\u041f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0430\u044f <strong>configFilePath \u2013<\/strong> \u044d\u0442\u043e \u043d\u0430\u0448 \u043f\u0443\u0442\u044c \u043a \u043a\u043e\u043d\u0444\u0438\u0433-\u0444\u0430\u0439\u043b\u0443 <strong>Kubernetes<\/strong>, \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0438\u0437 \u0441\u0438\u0441\u0442\u0435\u043c\u043d\u043e\u0433\u043e \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430, \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u0433\u043e \u043f\u0440\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0435.<\/p>\n<p>\u041c\u0435\u0442\u043e\u0434 <strong>getConfig<\/strong> \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u0444\u0430\u0439\u043b\u0430.<\/p>\n<p>\u041c\u0435\u0442\u043e\u0434 <strong>getApiClient <\/strong>\u0441\u043e\u0437\u0434\u0430\u0435\u0442 \u043a\u043b\u0438\u0435\u043d\u0442 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u043a\u043e\u043d\u0444\u0438\u0433-\u0444\u0430\u0439\u043b\u0430.<\/p>\n<p><strong>ApiClient<\/strong>: \u0411\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u043b\u0438\u0435\u043d\u0442 \u0434\u043b\u044f \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0441 <strong>Kubernetes API<\/strong>. \u041e\u043d \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f\u043c\u0438, \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0435\u0439 \u0438 \u043e\u0431\u0449\u0438\u043c\u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u043c\u0438.<\/p>\n<p>\u041f\u0440\u0438 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u0432 \u043d\u0435\u043c \u043c\u043e\u0436\u043d\u043e \u0442\u0430\u043a\u0436\u0435 \u0443\u043a\u0430\u0437\u0430\u0442\u044c URL, Credentials \u0438 \u0442. \u0434.<\/p>\n<p>\u0421\u0442\u0440\u043e\u043a\u0430 <strong>\u201c<em>client.setReadTimeout(60000);<\/em>\u201d<\/strong> \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u0442 \u0432\u0440\u0435\u043c\u044f \u043e\u0436\u0438\u0434\u0430\u043d\u0438\u044f (timeout) \u0434\u043b\u044f \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439 \u0447\u0442\u0435\u043d\u0438\u044f \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0435 <strong>Kubernetes (ApiClient)<\/strong>. \u041c\u044b \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043b\u0438 \u0432\u0440\u0435\u043c\u044f \u043e\u0436\u0438\u0434\u0430\u043d\u0438\u044f 60 \u0441\u0435\u043a\u0443\u043d\u0434. \u0417\u043d\u0430\u0447\u0435\u043d\u0438\u0435 0 \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442, \u0447\u0442\u043e \u0432\u0440\u0435\u043c\u044f \u043e\u0436\u0438\u0434\u0430\u043d\u0438\u044f \u0431\u0443\u0434\u0435\u0442 \u0431\u0435\u0441\u043a\u043e\u043d\u0435\u0447\u043d\u044b\u043c.<\/p>\n<p>\u041f\u0440\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 <strong>SharedInformerFactory <\/strong>\u043d\u0443\u0436\u043d\u043e \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c <strong><em>client.setReadTimeout(0);<\/em><\/strong> \u2013 \u0442\u0430\u043a \u043a\u0430\u043a \u0435\u0433\u043e \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u0431\u0435\u0441\u0441\u0440\u043e\u0447\u043d\u044b\u043c.<\/p>\n<p>\u041d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 <strong>ApiClient<\/strong> \u0441\u043e\u0437\u0434\u0430\u044e\u0442\u0441\u044f \u0434\u0440\u0443\u0433\u0438\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b \u0432 \u043c\u0435\u0442\u043e\u0434\u0430\u0445 <strong>getCoreV1Api, getNetworkingApi, getSharedInformerFactory \u2013 <\/strong>\u0441 \u0438\u0445 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043c\u044b \u043e\u0431\u0440\u0430\u0449\u0430\u0435\u043c\u0441\u044f \u043a <strong>Kubernetes<\/strong>.<\/p>\n<p><a class=\"anchor\" name=\"initialization\" id=\"initialization\"><\/a><\/p>\n<ol start=\"4\">\n<li>\n<p><strong>\u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0438\u043d\u0444\u043e\u0440\u043c\u0435\u0440\u043e\u0432 \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f\u00a0 Pods, Nodes \u0438 Ingresses<\/strong><\/p>\n<\/li>\n<\/ol>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0438\u043d\u0444\u043e\u0440\u043c\u0435\u0440\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0431\u0443\u0434\u0443\u0442 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c\u0441\u044f \u0432\u043c\u0435\u0441\u0442\u0435 \u0441 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u043c, \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0442\u044c \u043d\u0443\u0436\u043d\u044b\u0435 \u043d\u0430\u043c \u0442\u0438\u043f\u044b \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u0438 \u0441\u043b\u0435\u0434\u0438\u0442\u044c \u0437\u0430 \u0438\u0445 \u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u044c\u044e.<\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043a\u043b\u0430\u0441\u0441 <strong>InitKubernetesResourceService<\/strong><\/p>\n<pre><code class=\"java\">@Service @RequiredArgsConstructor public class InitKubernetesResourceService {    private static final Logger LOGGER = LoggerFactory.getLogger(InitKubernetesResourceService.class);      private final KubernetesResourceService kubernetesResourceService;    private final KubernetesResourceInformerFactoryService informerFactoryService;    private final KubernetesResourceInformerContextBuilderService contextBuilderService;    private final KubernetesResourceInformerContextManager contextManager;      public void watchResources() {        Optional&lt;SharedInformerFactory> informerFactoryOptional = kubernetesResourceService.getSharedInformerFactory();        if (informerFactoryOptional.isEmpty()) {            LOGGER.error(\"Failed to initialize KubernetesApiFactory due to missing SharedInformerFactory.\");            return;        }        SharedInformerFactory informerFactory = informerFactoryOptional.get();          Optional&lt;CoreV1Api> coreV1ApiOptional = kubernetesResourceService.getCoreV1Api();        if (coreV1ApiOptional.isEmpty()) {            LOGGER.error(\"Failed to initialize KubernetesApiFactory due to missing CoreV1Api.\");            return;        }        CoreV1Api coreV1Api = coreV1ApiOptional.get();          Optional&lt;NetworkingV1Api> networkingV1ApiOptional = kubernetesResourceService.getNetworkingApi();        if (networkingV1ApiOptional.isEmpty()) {            LOGGER.error(\"Failed to initialize KubernetesApiFactory due to missing NetworkingV1Api.\");            return;        }        NetworkingV1Api networkingV1Api = networkingV1ApiOptional.get();          informerFactoryService.registerInformers(informerFactory, coreV1Api, networkingV1Api);          KubernetesResourceInformerContext context = contextBuilderService.buildContext(informerFactory);          contextManager.putContext(context);          informerFactory.startAllRegisteredInformers();    } } <\/code><\/pre>\n<p>\u0417\u0434\u0435\u0441\u044c \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043e\u0431\u044a\u0435\u043a\u0442\u044b <strong>SharedInformerFactory, CoreV1Api, NetworkingV1Api<\/strong>, \u0441 \u0438\u0445 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u043c \u0438\u043d\u0444\u043e\u0440\u043c\u0435\u0440\u044b \u0432 \u043a\u043b\u0430\u0441\u0441\u0435 <strong>KubernetesResourceInformerFactoryService<\/strong>, \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u043c \u0441\u0441\u044b\u043b\u043a\u0438 \u043d\u0430 \u043d\u0438\u0445 \u0432 \u043a\u043b\u0430\u0441\u0441\u0435 <strong>KubernetesResourceInformerContext<\/strong>. \u041f\u043e\u0441\u043b\u0435 \u0447\u0435\u0433\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0434\u0438\u043c \u043e\u0431\u044a\u0435\u043a\u0442 <strong>KubernetesResourceInformerContext <\/strong>\u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0438 \u0432\u044b\u0434\u0430\u0447\u0438 \u0432 <strong>KubernetesResourceInformerContextManager.<\/strong><\/p>\n<p>\u0412 \u043a\u043e\u043d\u0446\u0435 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u043c \u0432\u0441\u0435 \u0438\u043d\u0444\u043e\u0440\u043c\u0435\u0440\u044b <strong><em>informerFactory.startAllRegisteredInformers();<\/em><\/strong><\/p>\n<p>\u041f\u043e-\u0445\u043e\u0440\u043e\u0448\u0435\u043c\u0443, \u043a\u043e\u0433\u0434\u0430 \u0438\u043d\u0444\u043e\u0440\u043c\u0435\u0440\u044b \u0431\u043e\u043b\u044c\u0448\u0435 \u043d\u0435 \u043d\u0443\u0436\u043d\u044b, \u0438\u0445 \u043d\u0443\u0436\u043d\u043e \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u043c\u0435\u0442\u043e\u0434\u043e\u043c <strong>i<em>nformerFactory.stopAllRegisteredInformers(true);<\/em><\/strong>, \u043d\u043e \u044f \u0431\u0443\u0434\u0443 \u043f\u043b\u043e\u0445\u0438\u043c.<\/p>\n<p><strong>4.1 \u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0438 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f \u0438\u043d\u0444\u043e\u0440\u043c\u0435\u0440\u043e\u0432<\/strong><\/p>\n<pre><code class=\"java\">@Service @RequiredArgsConstructor public class KubernetesResourceInformerFactoryService {    private static final Long RESYNC_PERIOD_MILLISECONDS = 600000L;    private static final Integer TIMEOUT = 300;      private final ResourceEventHandlerBuilder handlerBuilder;      public void registerInformers(SharedInformerFactory informerFactory, CoreV1Api coreV1Api, NetworkingV1Api networkingV1Api) {        CallGenerator podCallGenerator = getPodCallGenerator(coreV1Api);        CallGenerator nodeCallGenerator = getNodeCallGenerator(coreV1Api);        CallGenerator ingressCallGenerator = getIngressCallGenerator(networkingV1Api);          informerFactory.sharedIndexInformerFor(podCallGenerator, V1Pod.class, V1PodList.class, RESYNC_PERIOD_MILLISECONDS);        informerFactory.sharedIndexInformerFor(nodeCallGenerator, V1Node.class, V1NodeList.class, RESYNC_PERIOD_MILLISECONDS);        informerFactory.sharedIndexInformerFor(ingressCallGenerator, V1Ingress.class, V1IngressList.class, RESYNC_PERIOD_MILLISECONDS);            SharedIndexInformer&lt;V1Pod> podInformer = informerFactory.getExistingSharedIndexInformer(V1Pod.class);            ResourceEventHandler&lt;V1Pod> podResourceEventHandler = handlerBuilder.getPodResourceEventHandler(podInformer);          podInformer.addEventHandler(podResourceEventHandler);    }      private CallGenerator getPodCallGenerator(CoreV1Api coreV1Api) {        return (CallGeneratorParams params) -> coreV1Api.listPodForAllNamespaces()                .resourceVersion(params.resourceVersion)                .watch(params.watch)                .timeoutSeconds(TIMEOUT)                .buildCall(null);    }      private CallGenerator getNodeCallGenerator(CoreV1Api coreV1Api) {        return (CallGeneratorParams params) -> coreV1Api.listNode()                .resourceVersion(params.resourceVersion)                .watch(params.watch)                .timeoutSeconds(TIMEOUT)                .buildCall(null);    }      private CallGenerator getIngressCallGenerator(NetworkingV1Api networkingV1Api) {        return (CallGeneratorParams params) -> networkingV1Api.listIngressForAllNamespaces()                .resourceVersion(params.resourceVersion)                .watch(params.watch)                .timeoutSeconds(TIMEOUT)                .buildCall(null);    } } <\/code><\/pre>\n<p>\u0414\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0438\u043d\u0444\u043e\u0440\u043c\u0435\u0440\u044b, \u043c\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043e\u0431\u044a\u0435\u043a\u0442\u044b <strong>CallGenerator<\/strong> \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0438\u043d\u0444\u043e\u0440\u043c\u0435\u0440\u0430.<\/p>\n<p>\u0422\u0430\u043a\u0436\u0435 \u043d\u0430\u043c \u043d\u0443\u0436\u0435\u043d \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0435\u0440 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0442\u0438\u043f\u0430 \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432.<\/p>\n<p>\u041f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0430\u044f <strong><em>RESYNC_PERIOD_MILLISECONDS<\/em><\/strong> \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0437\u0430 \u043f\u0435\u0440\u0438\u043e\u0434 \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0432 \u043c\u0438\u043b\u043b\u0438\u0441\u0435\u043a\u0443\u043d\u0434\u0430\u0445 \u043c\u0435\u0436\u0434\u0443 \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u044b\u043c\u0438 \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u044f\u043c\u0438.<\/p>\n<p>\u0418\u043d\u0444\u043e\u0440\u043c\u0435\u0440\u044b \u043c\u0435\u043d\u044f\u044e\u0442 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u0443 \u0441\u0435\u0431\u044f \u0432\u043d\u0443\u0442\u0440\u0438 \u0441\u0440\u0430\u0437\u0443 \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u0438\u0445 \u0432 <strong>Kubernetes<\/strong>, \u043d\u043e \u0442\u0430\u043a\u0436\u0435 \u0432\u0440\u0435\u043c\u044f \u043e\u0442 \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u043f\u0440\u043e\u0432\u043e\u0434\u044f\u0442 \u043f\u043e\u043b\u043d\u0443\u044e \u0440\u0435\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u044e \u043d\u0430 \u0441\u043b\u0443\u0447\u0430\u0439, \u0435\u0441\u043b\u0438 \u043a\u0430\u043a\u043e\u0435-\u0442\u043e \u0441\u043e\u0431\u044b\u0442\u0438\u0435 \u0431\u044b\u043b\u043e \u043f\u0440\u043e\u043f\u0443\u0449\u0435\u043d\u043e.<\/p>\n<p>\u0422\u0430\u043a\u0436\u0435 \u0445\u043e\u0447\u0443 \u0437\u0430\u043c\u0435\u0442\u0438\u0442\u044c, \u0447\u0442\u043e \u0432 \u0434\u0430\u043d\u043d\u043e\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u043c\u044b \u0432\u0435\u0448\u0430\u0435\u043c \u0438\u043d\u0444\u043e\u0440\u043c\u0435\u0440\u044b \u043d\u0430 \u0432\u0441\u0435 <strong>Pods, Nodes<\/strong>, <strong>Ingresses<\/strong> \u043a\u043b\u0430\u0441\u0442\u0435\u0440\u0430, \u043d\u043e \u0435\u0441\u0442\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0444\u0438\u043b\u044c\u0442\u0440\u043e\u0432\u0430\u0442\u044c \u0438\u0445 \u043f\u043e \u043d\u0435\u0439\u043c\u0441\u043f\u0435\u0439\u0441\u0430\u043c, \u043b\u0435\u0439\u0431\u043b\u0430\u043c \u0438 \u0442. \u0434. \u0435\u0449\u0435 \u043d\u0430 \u044d\u0442\u0430\u043f\u0435 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0438\u043d\u0444\u043e\u0440\u043c\u0435\u0440\u0430. \u0422\u0430\u043a\u0436\u0435 \u043c\u043e\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0438\u043d\u0444\u043e\u0440\u043c\u0435\u0440\u043e\u0432 \u0434\u043b\u044f \u043e\u0434\u043d\u043e\u0433\u043e \u0440\u0435\u0441\u0443\u0440\u0441\u0430, \u0447\u0442\u043e\u0431\u044b \u0437\u0430\u0431\u0438\u0440\u0430\u0442\u044c <strong>Pods<\/strong> \u0442\u043e\u043b\u044c\u043a\u043e \u0438\u0437 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0445 \u043d\u0435\u0439\u043c\u0441\u043f\u0435\u0439\u0441\u043e\u0432 \u0441 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u043c\u0438 \u043b\u0435\u0439\u0431\u043b\u0430\u043c\u0438, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440.<\/p>\n<p>\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u043c \u0438\u043d\u0444\u043e\u0440\u043c\u0435\u0440\u044b \u043c\u0435\u0442\u043e\u0434\u043e\u043c <strong><em>informerFactory.sharedIndexInformerFor(podCallGenerator, V1Pod.class, V1PodList.class, RESYNC_PERIOD_MILLISECONDS)<\/em><\/strong><em>,<\/em> \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u044f \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0435 \u0433\u0435\u043d\u0435\u0440\u0430\u0442\u043e\u0440\u044b \u0438 \u043a\u043b\u0430\u0441\u0441\u044b \u0434\u043b\u044f \u043d\u0438\u0445.<\/p>\n<p>\u0422\u0430\u043a\u0436\u0435 \u043c\u043e\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0438 \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u0434\u043b\u044f \u0438\u043d\u0444\u043e\u0440\u043c\u0435\u0440\u043e\u0432.<\/p>\n<p>\u0412 \u043d\u0430\u0448\u0435\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u043c\u044b \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0442\u0430\u043a\u043e\u0439 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0434\u043b\u044f \u0438\u043d\u0444\u043e\u0440\u043c\u0435\u0440\u0430 <strong>Pods<\/strong>, \u0447\u0442\u043e\u0431\u044b \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u043e\u0431\u044b\u0442\u0438\u044f, \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u044f\u0449\u0438\u0435 \u0441 \u043f\u043e\u0434\u0430\u043c\u0438.<\/p>\n<p>\u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u043c \u0441\u0430\u043c \u0438\u043d\u0444\u043e\u0440\u043c\u0435\u0440 <strong>podInformer <\/strong>\u0438\u0437 \u0444\u0430\u0431\u0440\u0438\u043a\u0438 \u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u0434\u0438\u043c \u0435\u0433\u043e \u0432 <strong>ResourceEventHandlerBuilder<\/strong>. \u041f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0439 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a <strong>podResourceEventHandler<\/strong> \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0432 \u0438\u043d\u0444\u043e\u0440\u043c\u0435\u0440 <strong>podInformer.addEventHandler(podResourceEventHandler)<\/strong><\/p>\n<pre><code class=\"java\">@Service @RequiredArgsConstructor public class ResourceEventHandlerBuilder {    private static final Logger LOGGER = LoggerFactory.getLogger(ResourceEventHandlerBuilder.class);      public ResourceEventHandler&lt;V1Pod> getPodResourceEventHandler(SharedIndexInformer&lt;V1Pod> podInformer) {        return new ResourceEventHandler&lt;V1Pod>() {            @Override            public void onAdd(V1Pod pod) {                if(podInformer.hasSynced()){                    LOGGER.info(\"Pod {} added\", pod.getMetadata().getName());                }            }              @Override            public void onUpdate(V1Pod oldPod, V1Pod newPod) {                if(podInformer.hasSynced()){                    LOGGER.info(\"Pod {} updated\", newPod.getMetadata().getName());                }            }              @Override            public void onDelete(V1Pod pod, boolean deletedFinalStateUnknown) {                if(podInformer.hasSynced()){                    LOGGER.info(\"Pod {} deleted\", pod.getMetadata().getName());                }            }        };    } }<\/code><\/pre>\n<p>\u0421\u0430\u043c \u0431\u0438\u043b\u0434\u0435\u0440 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u043f\u0440\u043e\u0441\u0442. \u041c\u044b \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u043d\u043e\u0432\u044b\u0439 \u043e\u0431\u044a\u0435\u043a\u0442 <strong>ResourceEventHandler<\/strong> \u0438 \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u0442\u0440\u0438 \u043c\u0435\u0442\u043e\u0434\u0430, \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u044f \u0432 \u043d\u0438\u0445 \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0441\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u0442\u043e\u0433\u0434\u0430, \u043a\u043e\u0433\u0434\u0430 \u0438\u043d\u0444\u043e\u0440\u043c\u0435\u0440 \u0443\u0436\u0435 \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d.<\/p>\n<p><strong>podInformer.hasSynced() <\/strong>\u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 <strong>true<\/strong> \u0432 \u0441\u043b\u0443\u0447\u0430\u0435, \u0435\u0441\u043b\u0438 \u0438\u043d\u0444\u043e\u0440\u043c\u0435\u0440 \u0443\u0436\u0435 \u0431\u044b\u043b \u043f\u0435\u0440\u0432\u043e\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d. \u042d\u0442\u043e \u0432\u0430\u0436\u043d\u043e, \u0435\u0441\u043b\u0438 \u043a\u043e\u0434, \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u043c\u044b\u0439 \u0432\u043d\u0443\u0442\u0440\u0438 \u043c\u0435\u0442\u043e\u0434\u043e\u0432 <strong>ResourceEventHandler<\/strong>, \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043e\u0442 \u0441\u043e\u0441\u0442\u0430\u0432\u0430 \u0441\u043e\u0431\u0438\u0440\u0430\u0435\u043c\u044b\u0445 \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432.<\/p>\n<p>\u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0443 \u043d\u0430\u0441 \u0432 \u043f\u0440\u043e\u0435\u043a\u0442\u0435 \u0432\u043d\u0443\u0442\u0440\u0438 \u0442\u0430\u043a\u043e\u0433\u043e \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u0441\u043b\u043e\u0436\u043d\u044b\u0439 \u043e\u0431\u0441\u0447\u0435\u0442 \u0441\u0442\u0430\u0442\u0443\u0441\u043e\u0432 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0437\u0430\u0432\u0438\u0441\u044f\u0442 \u043e\u0442 \u0441\u043e\u0441\u0442\u0430\u0432\u0430 \u043f\u043e\u0434\u043e\u0432 \u0438 \u043f\u0438\u0448\u0443\u0442\u0441\u044f \u0432 \u0438\u0441\u0442\u043e\u0440\u0438\u044e \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439. \u0411\u0435\u0437 \u0442\u0430\u043a\u043e\u0433\u043e \u0443\u0441\u043b\u043e\u0432\u0438\u044f \u043f\u0440\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0430\u043b\u0438 \u0441\u0442\u043e\u043f\u043a\u0443 \u0437\u0430\u043f\u0438\u0441\u0435\u0439 \u043e\u0431 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u0441\u0442\u0430\u0442\u0443\u0441\u043e\u0432, \u0432 \u0442\u043e \u0432\u0440\u0435\u043c\u044f \u043a\u0430\u043a \u043a\u043b\u0430\u0441\u0442\u0435\u0440 \u0441\u043f\u043e\u043a\u043e\u0439\u043d\u043e \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u043b \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u043d\u0435 \u043c\u0435\u043d\u044f\u044f\u0441\u044c.<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/d7d\/c17\/4e8\/d7dc174e8b98f159ac737c05c6907ba3.png\" alt=\"\u0414\u0435\u0432\u043e\u043f\u0441 \u043a\u043b\u0430\u0441\u0442\u0435\u0440\u0430 Nova, \u043a\u043e\u0433\u0434\u0430 \u0443\u0432\u0438\u0434\u0435\u043b 50 FAILED \u0438 WARNING \u0432 \u0438\u0441\u0442\u043e\u0440\u0438\u0438 \u0441\u0442\u0430\u0442\u0443\u0441\u043e\u0432\" title=\"\u0414\u0435\u0432\u043e\u043f\u0441 \u043a\u043b\u0430\u0441\u0442\u0435\u0440\u0430 Nova, \u043a\u043e\u0433\u0434\u0430 \u0443\u0432\u0438\u0434\u0435\u043b 50 FAILED \u0438 WARNING \u0432 \u0438\u0441\u0442\u043e\u0440\u0438\u0438 \u0441\u0442\u0430\u0442\u0443\u0441\u043e\u0432\" width=\"500\" height=\"304\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/d7d\/c17\/4e8\/d7dc174e8b98f159ac737c05c6907ba3.png\"\/><\/p>\n<div><figcaption><em>\u0414\u0435\u0432\u043e\u043f\u0441 \u043a\u043b\u0430\u0441\u0442\u0435\u0440\u0430 Nova, \u043a\u043e\u0433\u0434\u0430 \u0443\u0432\u0438\u0434\u0435\u043b 50 FAILED \u0438 WARNING \u0432 \u0438\u0441\u0442\u043e\u0440\u0438\u0438 \u0441\u0442\u0430\u0442\u0443\u0441\u043e\u0432<\/em><\/figcaption><\/div>\n<\/figure>\n<p><strong>4.2 \u0425\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u0441\u0441\u044b\u043b\u043e\u043a \u043d\u0430 \u0438\u043d\u0444\u043e\u0440\u043c\u0435\u0440\u044b<\/strong><\/p>\n<p>\u0421\u0441\u044b\u043b\u043a\u0438 \u043d\u0430 \u0438\u043d\u0444\u043e\u0440\u043c\u0435\u0440\u044b \u0445\u0440\u0430\u043d\u044f\u0442\u0441\u044f \u0432 record <strong>KubernetesResourceInformerContext<\/strong><\/p>\n<pre><code class=\"java\">public record KubernetesResourceInformerContext(        SharedIndexInformer&lt;V1Pod> podInformer,        SharedIndexInformer&lt;V1Node> nodeInformer,        SharedIndexInformer&lt;V1Ingress> ingressInformer ){ }<\/code><\/pre>\n<p>\u0412 \u043e\u0431\u0449\u0435\u043c-\u0442\u043e \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0441\u0441\u044b\u043b\u043a\u0443 \u043d\u0430 \u0441\u0430\u043c\u0443 \u0444\u0430\u0431\u0440\u0438\u043a\u0443 <strong>SharedInformerFactory<\/strong>,\u00a0 \u043f\u043e\u043b\u0443\u0447\u0430\u044f \u0438\u043d\u0444\u043e\u0440\u043c\u0435\u0440\u044b \u043d\u0435\u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0438\u0437 \u043d\u0435\u0435, \u043d\u043e \u044d\u0442\u043e \u043d\u0435 \u043e\u0447\u0435\u043d\u044c \u0443\u0434\u043e\u0431\u043d\u043e.<\/p>\n<p>\u0423\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u043c <strong>KubernetesResourceInformerContext <\/strong>\u043a\u043b\u0430\u0441\u0441 <strong>KubernetesResourceInformerContextManager<\/strong><\/p>\n<pre><code class=\"java\">@Service public class KubernetesResourceInformerContextManager {     private KubernetesResourceInformerContext informerContext;      public KubernetesResourceInformerContext getContext() {         return informerContext;     }      public void putContext(KubernetesResourceInformerContext context) {         informerContext = context;     } }<\/code><\/pre>\n<p><a class=\"anchor\" name=\"listener\" id=\"listener\"><\/a><\/p>\n<ol start=\"5\">\n<li>\n<p><strong>\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 Listener \u0434\u043b\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0438\u043d\u0444\u043e\u0440\u043c\u0435\u0440\u043e\u0432<\/strong><\/p>\n<\/li>\n<\/ol>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c <strong>Listener<\/strong>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0441\u043e\u0437\u0434\u0430\u0441\u0442 \u0438\u043d\u0444\u043e\u0440\u043c\u0435\u0440\u044b \u0438 \u0441\u043b\u043e\u0436\u0438\u0442 \u0438\u0445 \u0432 <strong>KubernetesResourceInformerContext<\/strong><\/p>\n<pre><code class=\"java\">@Service @RequiredArgsConstructor public class AppStartUpEventListener {    public static final Logger LOGGER = LoggerFactory.getLogger(AppStartUpEventListener.class);      private final InitKubernetesResourceService kubernetesResourceService;      @EventListener(ApplicationReadyEvent.class)    public void applicationReady() {        LOGGER.info(\"Start [{}]\", ApplicationReadyEvent.class.getName());        CompletableFuture                .runAsync(() -> {})                .thenRunAsync(() -> {                    try {                        kubernetesResourceService.watchResources();                    } catch (Exception e) {                        LOGGER.error(\"ERROR on application start up event\", e);                    }                });        LOGGER.info(\"Stop [{}]\", ApplicationReadyEvent.class.getName());    } }<\/code><\/pre>\n<p><a class=\"anchor\" name=\"data-extraction\" id=\"data-extraction\"><\/a><\/p>\n<ol start=\"6\">\n<li>\n<p><strong>\u0418\u0437\u0432\u043b\u0435\u0447\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438\u0437 \u0438\u043d\u0444\u043e\u0440\u043c\u0435\u0440\u043e\u0432 \u0438 \u0438\u0445 \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0430 \u0434\u043b\u044f \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u0447\u0435\u0440\u0435\u0437 API<\/strong><\/p>\n<\/li>\n<\/ol>\n<p>\u041c\u044b \u043c\u043e\u043b\u043e\u0434\u0446\u044b. \u0418\u043d\u0444\u043e\u0440\u043c\u0435\u0440\u044b \u0441\u043e\u0437\u0434\u0430\u044e\u0442\u0441\u044f \u043f\u0440\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0438 \u0441\u043e\u0431\u0438\u0440\u0430\u044e\u0442 \u0438\u043d\u0444\u0443 \u043e <strong>Pods<\/strong>, <strong>Nodes<\/strong>, <strong>Ingresses<\/strong>.<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/5fb\/926\/bbe\/5fb926bbe7ffa5eeec61f5406d9f17c0.png\" width=\"512\" height=\"288\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/5fb\/926\/bbe\/5fb926bbe7ffa5eeec61f5406d9f17c0.png\"\/><\/figure>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043a\u043b\u0430\u0441\u0441 <strong>KubernetesObjectsFetcherService<\/strong>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u0442\u044c \u044d\u0442\u0438 \u0432\u0430\u0436\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435.<\/p>\n<pre><code class=\"java\">@Service @RequiredArgsConstructor public class KubernetesObjectsFetcherService {    private static final Logger LOGGER = LoggerFactory.getLogger(KubernetesObjectsFetcherService.class);      private final KubernetesResourceInformerContextManager contextManager;      public List&lt;V1Pod> getPods() {        KubernetesResourceInformerContext context = contextManager.getContext();        if (context == null) {            LOGGER.error(\"PodInformerContext is null\");            return List.of();        }        return getV1Pods(context);    }      public List&lt;V1Node> getNodes() {        KubernetesResourceInformerContext context = contextManager.getContext();        if (context == null) {            LOGGER.error(\"NodeInformerContext is null\");            return List.of();        }        return getV1Nodes(context);    }      public List&lt;V1Ingress> getNamespacedIngresses(String namespace) {        KubernetesResourceInformerContext context = contextManager.getContext();        if (context == null) {            LOGGER.error(\"IngressInformerContext is null\");            return List.of();        }        List&lt;V1Ingress> ingresses = getV1Ingresses(context);        return filteredIngresses(namespace, ingresses);    }      protected List&lt;V1Ingress> filteredIngresses(String namespace, List&lt;V1Ingress> ingresses) {        return Optional.ofNullable(ingresses).orElse(List.of())                .stream()                .filter(ingress -> ingress.getMetadata() != null)                .filter(ingress -> ingress.getMetadata().getNamespace() != null)                .filter(ingress -> ingress.getMetadata().getNamespace().equals(namespace))                .toList();    }      protected List&lt;V1Pod> getV1Pods(KubernetesResourceInformerContext context) {        return Optional.ofNullable(context).map(KubernetesResourceInformerContext::podInformer).map(SharedIndexInformer::getIndexer).map(Store::list).orElse(List.of());    }      protected List&lt;V1Node> getV1Nodes(KubernetesResourceInformerContext context) {        return Optional.ofNullable(context).map(KubernetesResourceInformerContext::nodeInformer).map(SharedIndexInformer::getIndexer).map(Store::list).orElse(List.of());    }      protected List&lt;V1Ingress> getV1Ingresses(KubernetesResourceInformerContext context) {        return Optional.ofNullable(context).map(KubernetesResourceInformerContext::ingressInformer).map(SharedIndexInformer::getIndexer).map(Store::list).orElse(List.of());    } } <\/code><\/pre>\n<p>\u0414\u043b\u044f \u0438\u043d\u0433\u0440\u0435\u0441\u0441\u043e\u0432 \u044f \u0441\u043e\u0437\u0434\u0430\u043b \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u0443\u044e \u0444\u0438\u043b\u044c\u0442\u0440\u0430\u0446\u0438\u044e \u043f\u043e \u043d\u0435\u0439\u043c\u0441\u043f\u0435\u0439\u0441\u0430\u043c, \u0442\u0430\u043a \u043a\u0430\u043a \u043d\u0430\u0448\u0430 \u0441\u043f\u0435\u0446\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u043f\u0440\u0435\u0434\u043f\u043e\u043b\u0430\u0433\u0430\u0435\u0442 \u044d\u0442\u043e.<\/p>\n<p>\u0414\u0430\u043b\u0435\u0435 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c 3 \u043a\u043b\u0430\u0441\u0441\u0430 \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043e\u0442\u0432\u0435\u0447\u0430\u044e\u0442 \u0437\u0430 \u0438\u0437\u0432\u043b\u0435\u0447\u0435\u043d\u0438\u0435 \u0438 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u0432 \u043d\u0443\u0436\u043d\u044b\u0439 \u0432\u0438\u0434.<\/p>\n<p>\u041a\u043b\u0430\u0441\u0441 <strong>IngressListService<\/strong><\/p>\n<pre><code class=\"java\">@Service @RequiredArgsConstructor public class IngressListService {    private final ResourcesMapper mapper;    private final KubernetesObjectsFetcherService kubernetesObjectsFetcherService;      public IngressListResponse getIngresses(String namespace) {        List&lt;V1Ingress> v1Ingresses = kubernetesObjectsFetcherService.getNamespacedIngresses(namespace);        List&lt;Ingress> ingresses = getIngressItems(v1Ingresses);        Integer total = ingresses.size();        return getResponse(ingresses, total);    }      private IngressListResponse getResponse(List&lt;Ingress> ingresses, Integer total) {        return new IngressListResponse().ingresses(ingresses).total(total);    }      private List&lt;Ingress> getIngressItems(List&lt;V1Ingress> v1Ingresses) {        return v1Ingresses.stream()                .map(this::mapIngress)                .filter(Objects::nonNull)                .toList();    }      private Ingress mapIngress(V1Ingress v1Ingress) {        String name = getName(v1Ingress).orElse(null);        String namespace = getNamespace(v1Ingress).orElse(null);        String host = getHost(v1Ingress).orElse(null);        String path = getPath(v1Ingress).orElse(null);        return mapper.mapIngress(name, namespace, host, path);    }      private Optional&lt;String> getPath(V1Ingress v1Ingress) {        return Optional.ofNullable(v1Ingress)                .map(V1Ingress::getSpec)                .map(V1IngressSpec::getRules)                .stream()                .flatMap(Collection::stream)                .flatMap(this::getV1HTTPIngressPathStream)                .map(V1HTTPIngressPath::getPath)                .findFirst();    }      private Stream&lt;V1HTTPIngressPath> getV1HTTPIngressPathStream(V1IngressRule rule) {        return Optional.ofNullable(rule.getHttp())                .map(http -> http.getPaths().stream())                .orElseGet(Stream::empty);    }      private Optional&lt;String> getHost(V1Ingress v1Ingress) {        return Optional.ofNullable(v1Ingress)                .map(V1Ingress::getSpec)                .map(V1IngressSpec::getRules)                .map(List::stream)                .orElseGet(Stream::empty)                .map(V1IngressRule::getHost)                .findFirst();    }      private Optional&lt;String> getNamespace(V1Ingress v1Ingress) {        return Optional.ofNullable(v1Ingress)                .map(V1Ingress::getMetadata)                .map(V1ObjectMeta::getNamespace);    }      private Optional&lt;String> getName(V1Ingress v1Ingress) {        return Optional.ofNullable(v1Ingress)                .map(V1Ingress::getMetadata)                .map(V1ObjectMeta::getName);    } } <\/code><\/pre>\n<p>\u041a\u043b\u0430\u0441\u0441 <strong>NodeListService<\/strong><\/p>\n<pre><code class=\"java\">@Service @RequiredArgsConstructor public class NodeListService {    private final ResourcesMapper mapper;    private final KubernetesObjectsFetcherService kubernetesResourceFetcherService;    public NodeListResponse getNodes() {        List&lt;V1Node> v1Nodes = kubernetesResourceFetcherService.getNodes();        List&lt;Node> nodes = getNodeItems(v1Nodes);        Integer total = nodes.size();        return getResponse(nodes, total);    }      private NodeListResponse getResponse(List&lt;Node> nodes, Integer total) {        return new NodeListResponse().nodes(nodes).total(total);    }      private List&lt;Node> getNodeItems(List&lt;V1Node> v1Nodes) {        return v1Nodes.stream()                .map(this::mapNode)                .filter(Objects::nonNull)                .toList();    }      private Node mapNode(V1Node v1Node) {        String name = getName(v1Node).orElse(null);        String status = getStatus(v1Node).orElse(null);        Map&lt;String, String> labels = getLabels(v1Node);        Map&lt;String, String> annotations = getAnnotations(v1Node);        return mapper.mapNode(name, status, labels, annotations);    }      private Map&lt;String, String> getAnnotations(V1Node v1Node) {        return Optional.ofNullable(v1Node)                .map(V1Node::getMetadata)                .map(V1ObjectMeta::getAnnotations)                .orElse(Map.of());    }      private Map&lt;String, String> getLabels(V1Node v1Node) {        return Optional.ofNullable(v1Node)                .map(V1Node::getMetadata)                .map(V1ObjectMeta::getLabels)                .orElse(Map.of());    }      private Optional&lt;String> getStatus(V1Node v1Node) {        return Optional.ofNullable(v1Node)                .map(V1Node::getStatus)                .map(V1NodeStatus::getPhase);    }      private Optional&lt;String> getName(V1Node v1Node) {        return Optional.ofNullable(v1Node)                .map(V1Node::getMetadata)                .map(V1ObjectMeta::getName);    } } <\/code><\/pre>\n<p>\u041a\u043b\u0430\u0441\u0441 <strong>PodListService<\/strong><\/p>\n<pre><code class=\"java\">@Service @RequiredArgsConstructor public class PodListService {    private final ResourcesMapper mapper;    private final KubernetesObjectsFetcherService kubernetesObjectsFetcherService;      public PodListResponse getPods() {        List&lt;V1Pod> v1Pods = kubernetesObjectsFetcherService.getPods();        List&lt;Pod> pods = getPodItems(v1Pods);        Integer total = pods.size();        return getResponse(pods, total);    }      private PodListResponse getResponse(List&lt;Pod> pods, Integer total) {        return new PodListResponse().pods(pods).total(total);    }      private List&lt;Pod> getPodItems(List&lt;V1Pod> v1Pods) {        return v1Pods.stream()                .map(this::mapPod)                .filter(Objects::nonNull)                .toList();    }      private Pod mapPod(V1Pod v1Pod) {        String name = getName(v1Pod).orElse(null);        String namespace = getNamespace(v1Pod).orElse(null);        String status = getStatus(v1Pod).orElse(null);        Integer restartCount = getRestartCount(v1Pod).orElse(0);        OffsetDateTime creationTimestamp = geCreationTimestamp(v1Pod).orElse(null);        Map&lt;String, String> labels = getLabels(v1Pod);        Map&lt;String, String> annotations = getAnnotations(v1Pod);        return mapper.mapPod(name, namespace, status, restartCount, creationTimestamp, labels, annotations);    }      protected Map&lt;String, String> getAnnotations(V1Pod v1Pod) {        return Optional.ofNullable(v1Pod)                .map(V1Pod::getMetadata)                .map(V1ObjectMeta::getAnnotations)                .orElse(Map.of());    }      protected Map&lt;String, String> getLabels(V1Pod v1Pod) {        return Optional.ofNullable(v1Pod)                .map(V1Pod::getMetadata)                .map(V1ObjectMeta::getLabels)                .orElse(Map.of());    }      protected Optional&lt;String> getStatus(V1Pod v1Pod) {        return Optional.ofNullable(v1Pod)                .map(V1Pod::getStatus)                .map(V1PodStatus::getPhase);    }      protected Optional&lt;String> getNamespace(V1Pod v1Pod) {        return Optional.ofNullable(v1Pod)                .map(V1Pod::getMetadata)                .map(V1ObjectMeta::getNamespace);    }      protected Optional&lt;String> getName(V1Pod v1Pod) {        return Optional.ofNullable(v1Pod)                .map(V1Pod::getMetadata)                .map(V1ObjectMeta::getName);    }      protected Optional&lt;OffsetDateTime> geCreationTimestamp(V1Pod v1Pod) {        return Optional.ofNullable(v1Pod)                .map(V1Pod::getMetadata)                .map(V1ObjectMeta::getCreationTimestamp);    }      protected Optional&lt;Integer> getRestartCount(V1Pod pod) {        return Optional.ofNullable(pod)                .map(V1Pod::getStatus)                .map(V1PodStatus::getContainerStatuses)                .filter(statuses -> !statuses.isEmpty())                .map(statuses -> statuses.get(0))                .map(V1ContainerStatus::getRestartCount);    } } <\/code><\/pre>\n<p>\u0410 \u0442\u0430\u043a\u0436\u0435 \u043c\u0430\u043f\u043f\u0435\u0440 <strong>ResourcesMapper<\/strong><\/p>\n<pre><code class=\"java\">@Mapper(componentModel = \"spring\", nullValueMappingStrategy = NullValueMappingStrategy.RETURN_DEFAULT) public interface ResourcesMapper {      @Mapping(target = \"name\", source = \"name\")    @Mapping(target = \"namespace\", source = \"namespace\")    @Mapping(target = \"status\", source = \"status\")    @Mapping(target = \"restartCount\", source = \"restartCount\")    @Mapping(target = \"creationTimestamp\", source = \"creationTimestamp\")    @Mapping(target = \"labels\", source = \"labels\")    @Mapping(target = \"annotations\", source = \"annotations\")    Pod mapPod(String name,               String namespace,               String status,               Integer restartCount,               OffsetDateTime creationTimestamp,               Map&lt;String, String> labels,               Map&lt;String, String> annotations);      @Mapping(target = \"name\", source = \"name\")    @Mapping(target = \"status\", source = \"status\")    @Mapping(target = \"labels\", source = \"labels\")    @Mapping(target = \"annotations\", source = \"annotations\")    Node mapNode(String name, String status, Map&lt;String, String> labels, Map&lt;String, String> annotations);      @Mapping(target = \"name\", source = \"name\")    @Mapping(target = \"namespace\", source = \"namespace\")    @Mapping(target = \"host\", source = \"host\")    @Mapping(target = \"path\", source = \"path\")    Ingress mapIngress(String name, String namespace, String host, String path); }<\/code><\/pre>\n<p><a class=\"anchor\" name=\"result-check\" id=\"result-check\"><\/a><\/p>\n<ol start=\"7\">\n<li>\n<p><strong>\u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430<\/strong><\/p>\n<\/li>\n<\/ol>\n<p>\u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0441 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u043c:<\/p>\n<p><strong><em>-Dkubernetes.config-file.path=your-path\/config.kubeconfig<\/em><\/strong><\/p>\n<p>\u0417\u0430\u043c\u0435\u043d\u0438\u0432 <em>\u201c<\/em><strong><em>your-path\/config.kubeconfig<\/em><\/strong><em>\u201d<\/em> \u043d\u0430 \u043f\u0443\u0442\u044c \u043a \u0441\u0432\u043e\u0435\u043c\u0443 \u043a\u043e\u043d\u0444\u0438\u0433-\u0444\u0430\u0439\u043b\u0443 <strong>Kubernetes<\/strong><\/p>\n<p>\u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c <strong>Postman<\/strong> \u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u043c <strong>GET-<\/strong>\u0437\u0430\u043f\u0440\u043e\u0441:<\/p>\n<p><a href=\"http:\/\/localhost:8080\/kubernetes-manager\/api\/pod\/list\"><strong><em>http:\/\/localhost:8080\/kubernetes-manager\/api\/pod\/list<\/em><\/strong><\/a><\/p>\n<p>\u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c json \u0432\u0438\u0434\u0430:<\/p>\n<pre><code class=\"json\">{     \"pods\": [         {             \"name\": \"ip-masq-agent-tsnsk\",             \"namespace\": \"kube-system\",             \"status\": \"Running\",             \"restartCount\": 0,             \"creationTimestamp\": \"2024-01-29T12:15:42Z\",             \"labels\": {                 \"controller-revision-hash\": \"6d59d8409d\",                 \"k8s-app\": \"ip-masq-agent\",                 \"pod-template-generation\": \"1\"             },             \"annotations\": {}         },         {             \"name\": \"catalog-76756d44fc-b4gdj\",             \"namespace\": \"impala\",             \"status\": \"Running\",             \"restartCount\": 0,             \"creationTimestamp\": \"2024-06-14T16:29:41Z\",             \"labels\": {                 \"app\": \"catalog\",                 \"cm-role-type\": \"Catalog\",                 \"cm-service\": \"Impala-6\",                 \"nova-process-configmap\": \"true\",                 \"pod-template-hash\": \"74356df4fc\"             },             \"annotations\": {                 \"nova-master-secret\": \"master-secret\",                 \"seccomp.security.alpha.kubernetes.io\/pod\": \"runtime\/default\"             }         }     ],     \"total\": 350 } <\/code><\/pre>\n<p>\u041d\u0430 \u044d\u0442\u043e\u043c \u0443 \u043c\u0435\u043d\u044f \u0432\u0441\u0435.<\/p>\n<p>\u0421\u043f\u0430\u0441\u0438\u0431\u043e \u0432\u0441\u0435\u043c, \u043a\u0442\u043e \u043e\u0441\u0438\u043b\u0438\u043b.<\/p>\n<p><em>\u201c\u0421\u0442\u0430\u0432\u044c\u0442\u0435 \u043b\u0430\u0439\u043a\u0438, \u0437\u0432\u0435\u0437\u0434\u043e\u0447\u043a\u0438 \u0438 \u043a\u043e\u043b\u043e\u043a\u043e\u043b\u044c\u0447\u0438\u043a\u0438\u201d<\/em>, \u043a\u0430\u043a \u0441\u043a\u0430\u0437\u0430\u043b \u043c\u043d\u0435 \u043a\u043e\u0433\u0434\u0430-\u0442\u043e \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0439 \u043f\u043e\u043b\u0438\u0446\u0435\u0439\u0441\u043a\u0438\u0439 \u0432 \u043e\u0442\u0432\u0435\u0442 \u043d\u0430 \u0444\u0440\u0430\u0437\u0443 <em>\u201c\u0410\u0434\u0432\u043e\u043a\u0430\u0442 \u0433\u043e\u0432\u043e\u0440\u0438\u0442, \u0447\u0442\u043e \u0432\u044b \u043f\u0440\u0430\u0432\u044b\u201d<\/em>.<\/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\/827794\/\"> https:\/\/habr.com\/ru\/articles\/827794\/<\/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><strong>\u041e\u0433\u043b\u0430\u0432\u043b\u0435\u043d\u0438\u0435<\/strong><\/p>\n<ol>\n<li>\n<p><a href=\"#introduction\">\u0412\u0432\u0435\u0434\u0435\u043d\u0438\u0435<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#project-creation\">\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0441 \u043d\u0443\u043b\u044f<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#api-clients\">\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u0432 API \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 kubernetes<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#initialization\">\u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0438\u043d\u0444\u043e\u0440\u043c\u0435\u0440\u043e\u0432 \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f\u00a0 Pods, Nodes \u0438 Ingresses<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#listener\">\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 Listener \u0434\u043b\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0438\u043d\u0444\u043e\u0440\u043c\u0435\u0440\u043e\u0432<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#data-extraction\">\u0418\u0437\u0432\u043b\u0435\u0447\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438\u0437 \u0438\u043d\u0444\u043e\u0440\u043c\u0435\u0440\u043e\u0432 \u0438 \u0438\u0445 \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0430 \u0434\u043b\u044f \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u0447\u0435\u0440\u0435\u0437 API<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#result-check\">\u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430<\/a><\/p>\n<\/li>\n<\/ol>\n<p><strong>\u041d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0439 \u0434\u0438\u0441\u043a\u043b\u0435\u0439\u043c\u0435\u0440<\/strong><\/p>\n<p>\u041f\u0440\u043e\u0435\u043a\u0442 \u0441\u043e\u0437\u0434\u0430\u043d \u0434\u043b\u044f \u0443\u0447\u0435\u0431\u043d\u044b\u0445 \u0446\u0435\u043b\u0435\u0439, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043a\u043e\u0434 \u0432 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u043c\u0435\u0441\u0442\u0430\u0445 \u043d\u0430\u043c\u0435\u0440\u0435\u043d\u043d\u043e (\u0438\u043b\u0438 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e) \u0443\u043f\u0440\u043e\u0449\u0435\u043d.<\/p>\n<p>\u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, <strong>path<\/strong> \u0434\u043b\u044f <strong>ingress<\/strong> \u0432\u0437\u044f\u0442 \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u0435\u0440\u0432\u044b\u0439 \u0438\u0437 \u0441\u043f\u0438\u0441\u043a\u0430, \u043d\u0430 \u043a\u0430\u0436\u0434\u044b\u0439 \u0432\u044b\u0437\u043e\u0432 API \u0441\u043e\u0437\u0434\u0430\u0435\u0442\u0441\u044f \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u0438 \u0442. \u0434.<\/p>\n<figure class=\"\"><\/figure>\n<p><a class=\"anchor\" name=\"introduction\" id=\"introduction\"><\/a><\/p>\n<ol>\n<li>\n<p><strong>\u0412\u0432\u0435\u0434\u0435\u043d\u0438\u0435<\/strong><\/p>\n<\/li>\n<\/ol>\n<p>\u041f\u0440\u0438\u0432\u0435\u0442, \u043c\u0435\u043d\u044f \u0437\u043e\u0432\u0443\u0442 \u0421\u0435\u0440\u0433\u0435\u0439, \u0441\u0442\u0430\u0440\u0448\u0438\u0439 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a 80 \u0443\u0440\u043e\u0432\u043d\u044f \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u0438 DataBlend (\u0433\u0440\u0443\u043f\u043f\u0430 \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u0439 GlowByte). \u041d\u0430\u0448\u0430 \u043a\u043e\u043c\u0430\u043d\u0434\u0430 \u0437\u0430\u043d\u0438\u043c\u0430\u0435\u0442\u0441\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u043e\u0439 \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u0430 ClusterManager, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435\u043c \u0438 \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u0442 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0442\u0430\u043a\u0438\u0445 \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u043e\u0432, \u043a\u0430\u043a GreenPlum, ClickHouse, DWH, Nova \u0438 \u0442. \u0434.<\/p>\n<p>\u041e\u043a\u043e\u043b\u043e \u043f\u043e\u043b\u0443\u0442\u043e\u0440\u0430 \u043b\u0435\u0442 \u043d\u0430\u0437\u0430\u0434 \u0443 \u043d\u0430\u0441 \u043f\u043e\u044f\u0432\u0438\u043b\u0430\u0441\u044c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u044c \u0441\u043e\u0431\u0438\u0440\u0430\u0442\u044c \u0438 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0442\u044c \u0432 \u0443\u0434\u043e\u0431\u043d\u043e\u043c \u0432\u0438\u0434\u0435 \u0438 \u0440\u0430\u0437\u0440\u0435\u0437\u0430\u0445 \u043c\u0435\u0442\u0440\u0438\u043a\u0438 \u0438 \u0434\u0430\u043d\u043d\u044b\u0435 \u043e\u0431 \u043e\u0431\u044a\u0435\u043a\u0442\u0430\u0445 \u043a\u043b\u0430\u0441\u0442\u0435\u0440\u043e\u0432 Kubernetes, \u0432 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0440\u0430\u0437\u0432\u0435\u0440\u043d\u0443\u0442 \u043f\u0440\u043e\u0434\u0443\u043a\u0442 Nova.\u00a0<\/p>\n<p>\u0414\u043b\u044f \u044d\u0442\u0438\u0445 \u0446\u0435\u043b\u0435\u0439 \u0431\u044b\u043b \u0432\u044b\u0431\u0440\u0430\u043d \u043e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0439 <strong>kubernetes-client<\/strong> \u0434\u043b\u044f <strong>Java.<\/strong><\/p>\n<p>\u041f\u043e\u043d\u0430\u0447\u0430\u043b\u0443 \u043c\u044b \u043f\u043e\u0448\u043b\u0438 \u043f\u043e \u043f\u0443\u0442\u0438 \u0441\u0431\u043e\u0440\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 \u043e \u043d\u043e\u0434\u0430\u0445, \u043f\u043e\u0434\u0430\u0445 \u0438 \u0442\u0430\u043a \u0434\u0430\u043b\u0435\u0435 \u043f\u043e \u0440\u0430\u0441\u043f\u0438\u0441\u0430\u043d\u0438\u044e \u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044e \u0438\u0445 \u0432 \u0411\u0414 \u0432 \u0443\u0434\u043e\u0431\u043d\u043e\u043c \u0432\u0438\u0434\u0435. \u041d\u043e, \u043a\u0430\u043a \u044d\u0442\u043e \u0447\u0430\u0441\u0442\u043e \u0431\u044b\u0432\u0430\u0435\u0442, \u0446\u0435\u043b\u0438 \u0438 \u0436\u0435\u043b\u0430\u043d\u0438\u044f \u0441\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0435\u043c \u043c\u0435\u043d\u044f\u044e\u0442\u0441\u044f, \u0438 \u0436\u0438\u0437\u043d\u044c \u0437\u0430\u0441\u0442\u0430\u0432\u0438\u043b\u0430 \u043f\u0435\u0440\u0435\u0439\u0442\u0438 \u043a \u043c\u0433\u043d\u043e\u0432\u0435\u043d\u043d\u043e\u043c\u0443 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044e \u0438 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044e \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439.<\/p>\n<p>\u041b\u0443\u0447\u0448\u0435 \u0432\u0441\u0435\u0433\u043e \u0434\u043b\u044f \u044d\u0442\u043e\u0439 \u0446\u0435\u043b\u0438 \u043f\u043e\u0434\u0445\u043e\u0434\u0438\u0442 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c \u0438\u043d\u0444\u043e\u0440\u043c\u0435\u0440\u043e\u0432 <strong>kubernetes-client.<\/strong><\/p>\n<p>\u0418 \u0441\u0435\u0439\u0447\u0430\u0441 \u043c\u044b \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c, \u0441 \u043a\u0430\u043a\u043e\u0439 \u0441\u0442\u043e\u0440\u043e\u043d\u044b \u0438\u0445 \u043b\u0443\u0447\u0448\u0435 \u043d\u0430\u0447\u0438\u043d\u0430\u0442\u044c \u0435\u0441\u0442\u044c.<\/p>\n<p>\u041d\u0430\u043f\u0438\u0448\u0435\u043c \u043f\u0440\u043e\u0441\u0442\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0432 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0435\u0442 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 <strong>Pods<\/strong>, <strong>Nodes<\/strong> \u0438 <strong>Ingresses<\/strong> \u0438 \u043f\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0443 \u043e\u0442\u0434\u0430\u0435\u0442 \u043d\u0430\u043c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u043d\u0438\u0445. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043c\u044b \u043f\u043e\u0432\u0435\u0441\u0438\u043c \u0438\u043d\u0444\u043e\u0440\u043c\u0435\u0440\u044b \u043d\u0430 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0435 \u0440\u0435\u0441\u0443\u0440\u0441\u044b <strong>Kubernetes<\/strong>.<\/p>\n<p>\u0415\u0441\u043b\u0438 \u043d\u0443\u0436\u043d\u043e \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0442\u044c CRD-\u0440\u0435\u0441\u0443\u0440\u0441\u044b, \u0442\u043e \u0438\u043d\u0444\u043e\u0440\u043c\u0435\u0440\u044b, \u043a \u0441\u043e\u0436\u0430\u043b\u0435\u043d\u0438\u044e, \u043d\u0435 \u043f\u043e\u0434\u043e\u0439\u0434\u0443\u0442.<\/p>\n<p>\u041f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u0438 \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u0440\u0435\u0441\u0443\u0440\u0441\u0430\u0445 <strong>Kubernetes <\/strong>\u0431\u0443\u0434\u0435\u043c \u0432 \u043f\u0430\u043c\u044f\u0442\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f.\u00a0<\/p>\n<blockquote>\n<p>&#171;\u0410 \u0443 \u043d\u0430\u0441 \u044d\u0442\u043e\u0439 \u043f\u0430\u043c\u044f\u0442\u0438 \u2013 \u0437\u0430\u0432\u0430\u043b\u0438\u0441\u044c, \u0443 \u043d\u0430\u0441 \u043f\u0430\u043f\u0430 \u043d\u0430 \u0444\u0430\u0431\u0440\u0438\u043a\u0435 \u043f\u043e \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0441\u0442\u0432\u0443 \u0447\u0438\u043f\u043e\u0432 \u043f\u0430\u043c\u044f\u0442\u0438 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442&#187;<\/p>\n<\/blockquote>\n<p> \u043a\u0430\u043a \u0441\u043a\u0430\u0437\u0430\u043b \u0431\u044b \u043a\u043e\u0442 \u041c\u0430\u0442\u0440\u043e\u0441\u043a\u0438\u043d.<\/p>\n<p><a class=\"anchor\" name=\"project-creation\" id=\"project-creation\"><\/a><\/p>\n<ol start=\"2\">\n<li>\n<p><strong>\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0441 \u043d\u0443\u043b\u044f<\/strong><\/p>\n<\/li>\n<\/ol>\n<p>\u042d\u0442\u043e\u0442 \u043f\u0443\u043d\u043a\u0442 \u043d\u0435 \u0438\u043c\u0435\u0435\u0442 \u043f\u0440\u044f\u043c\u043e\u0433\u043e \u043e\u0442\u043d\u043e\u0448\u0435\u043d\u0438\u044f \u043a <strong>kubernetes-client<\/strong>, \u0435\u0441\u043b\u0438 \u043d\u0435\u0442 \u043d\u0443\u0436\u0434\u044b \u043f\u043e\u0432\u0442\u043e\u0440\u044f\u0442\u044c \u043f\u0440\u043e\u0435\u043a\u0442, \u044d\u0442\u043e\u0442 \u043f\u0443\u043d\u043a\u0442 \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u043e\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0438 \u0441\u043a\u0430\u0447\u0430\u0442\u044c \u0443\u0436\u0435 \u0433\u043e\u0442\u043e\u0432\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442.<\/p>\n<p>\u0421\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 <a href=\"https:\/\/github.com\/KapustinS\/kubernetes-manager\"><u>GitHub<\/u><\/a> \u043f\u0440\u043e\u0435\u043a\u0442\u0430.<\/p>\n<p>\u0414\u043b\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0438\u0437 \u043f\u0430\u043f\u043a\u0438 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u043a\u043e\u043c\u0430\u043d\u0434\u0443:\u00a0<\/p>\n<p><strong><em>mvn clean generate-sources<\/em><\/strong><\/p>\n<p>\u0418 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043f\u0440\u043e\u0435\u043a\u0442 \u0441 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u043c:<\/p>\n<p><strong><em>-Dkubernetes.config-file.path=your-path\/config.kubeconfig<\/em><\/strong><\/p>\n<p>\u0417\u0430\u043c\u0435\u043d\u0438\u0432 <em>\u201c<\/em><strong><em>your-path\/config.kubeconfig<\/em><\/strong><em>\u201d<\/em> \u043d\u0430 \u043f\u0443\u0442\u044c \u043a \u0441\u0432\u043e\u0435\u043c\u0443 \u043a\u043e\u043d\u0444\u0438\u0433-\u0444\u0430\u0439\u043b\u0443 <strong>Kubernetes.<\/strong><\/p>\n<p>\u0421\u0442\u0435\u043a \u043f\u0440\u043e\u0435\u043a\u0442\u0430:<br \/><strong>Java 17, Spring Boot 3.3.0, Mapstruct, Lombok, Kubernetes-client 20.0.1, OpenApi 3.0.<\/strong><\/p>\n<details class=\"spoiler\">\n<summary>\u041f\u0440\u043e\u0446\u0435\u0441\u0441 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e<\/summary>\n<div class=\"spoiler__content\">\n<p>\u041f\u0435\u0440\u0432\u044b\u043c \u0434\u0435\u043b\u043e\u043c \u043c\u044b \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u043d\u043e\u0432\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 <strong>Spring Boot:<\/strong><\/p>\n<figure class=\"\"><\/figure>\n<figure class=\"\"><\/figure>\n<p>\u0414\u0430\u043b\u0435\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0432 <strong>pom.xml<\/strong> \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u0434\u043b\u044f \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u043e\u0432 \u0438\u0437 \u0441\u043f\u0435\u0446\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 <strong>OpenApi 3.0<\/strong>, <strong>mapstruct<\/strong> \u0434\u043b\u044f \u043c\u0430\u043f\u043f\u0438\u043d\u0433\u0430 DTO \u0438, \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e, <strong>kubernetes-client<\/strong>:<\/p>\n<pre><code class=\"xml\">&lt;dependencies>    &lt;dependency>        &lt;groupId>org.springframework.boot&lt;\/groupId>        &lt;artifactId>spring-boot-starter-web&lt;\/artifactId>    &lt;\/dependency>      &lt;dependency>        &lt;groupId>org.projectlombok&lt;\/groupId>        &lt;artifactId>lombok&lt;\/artifactId>        &lt;optional>true&lt;\/optional>    &lt;\/dependency>    &lt;dependency>        &lt;groupId>org.springframework.boot&lt;\/groupId>        &lt;artifactId>spring-boot-starter-test&lt;\/artifactId>        &lt;scope>test&lt;\/scope>    &lt;\/dependency>      &lt;dependency>        &lt;groupId>io.swagger.core.v3&lt;\/groupId>        &lt;artifactId>swagger-annotations&lt;\/artifactId>        &lt;version>2.2.22&lt;\/version>    &lt;\/dependency>      &lt;dependency>        &lt;groupId>javax.validation&lt;\/groupId>        &lt;artifactId>validation-api&lt;\/artifactId>        &lt;version>2.0.1.Final&lt;\/version>    &lt;\/dependency>      &lt;dependency>        &lt;groupId>org.mapstruct&lt;\/groupId>        &lt;artifactId>mapstruct&lt;\/artifactId>        &lt;version>${mapstruct.version}&lt;\/version>    &lt;\/dependency>      &lt;dependency>        &lt;groupId>io.kubernetes&lt;\/groupId>        &lt;artifactId>client-java&lt;\/artifactId>        &lt;version>20.0.1&lt;\/version>    &lt;\/dependency> &lt;\/dependencies> <\/code><\/pre>\n<p>\u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043f\u043b\u0430\u0433\u0438\u043d\u044b \u0434\u043b\u044f \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u043e\u0432 \u0438 DTO \u0438\u0437 \u0441\u043f\u0435\u0446\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 <strong>OpenApi 3.0<\/strong>, \u0430 \u0442\u0430\u043a\u0436\u0435 <strong>maven plugin<\/strong>, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u043f\u0440\u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u043c \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0440\u044b <strong>lombok<\/strong> \u0438 <strong>mapstruct<\/strong>, \u043d\u0435 \u0437\u0430\u0431\u0443\u0434\u044c\u0442\u0435 \u0437\u0430\u043c\u0435\u043d\u0438\u0442\u044c \u043f\u0443\u0442\u0438 \u043d\u0430 \u0441\u0432\u043e\u0438:<\/p>\n<pre><code class=\"xml\">&lt;build>    &lt;plugins>        &lt;plugin>            &lt;groupId>org.apache.maven.plugins&lt;\/groupId>            &lt;artifactId>maven-compiler-plugin&lt;\/artifactId>            &lt;version>3.8.1&lt;\/version>            &lt;configuration>                &lt;source>${java.version}&lt;\/source>                &lt;target>${java.version}&lt;\/target>                &lt;annotationProcessorPaths>                    &lt;path>                        &lt;groupId>org.mapstruct&lt;\/groupId>                        &lt;artifactId>mapstruct-processor&lt;\/artifactId>                        &lt;version>${mapstruct.version}&lt;\/version>                    &lt;\/path>                    &lt;path>                        &lt;groupId>org.projectlombok&lt;\/groupId>                        &lt;artifactId>lombok&lt;\/artifactId>                        &lt;version>1.18.22&lt;\/version>                    &lt;\/path>                &lt;\/annotationProcessorPaths>            &lt;\/configuration>        &lt;\/plugin>        &lt;plugin>            &lt;groupId>org.openapitools&lt;\/groupId>            &lt;artifactId>openapi-generator-maven-plugin&lt;\/artifactId>            &lt;version>7.6.0&lt;\/version>            &lt;executions>                &lt;execution>                    &lt;id>core&lt;\/id>                    &lt;goals>                        &lt;goal>generate&lt;\/goal>                    &lt;\/goals>                    &lt;configuration>                        &lt;inputSpec>src\/main\/resources\/openapi.yml&lt;\/inputSpec>                        &lt;output>target\/generated-sources\/openapi&lt;\/output>                        &lt;generatorName>spring&lt;\/generatorName>                        &lt;library>spring-cloud&lt;\/library>                        &lt;apiPackage>ru.kapustin.kubernetesmanager.controller&lt;\/apiPackage>                        &lt;modelPackage>ru.kapustin.kubernetesmanager.model&lt;\/modelPackage>                        &lt;generateSupportingFiles>false&lt;\/generateSupportingFiles>                        &lt;templateDirectory>src\/main\/resources\/templates&lt;\/templateDirectory>                        &lt;configOptions>                            &lt;openApiNullable>false&lt;\/openApiNullable>                            &lt;interfaceOnly>true&lt;\/interfaceOnly>                            &lt;useTags>true&lt;\/useTags>                        &lt;\/configOptions>                    &lt;\/configuration>                &lt;\/execution>            &lt;\/executions>        &lt;\/plugin>    &lt;\/plugins> &lt;\/build> <\/code><\/pre>\n<p>\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0444\u0430\u0439\u043b <strong>openapi.yml<\/strong> \u0441\u043e \u0441\u043f\u0435\u0446\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0435\u0439 <strong>OpenApi 3.0,<\/strong> \u0438\u0437 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0431\u0443\u0434\u0443\u0442 \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u044b \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u044b \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u043e\u0432 \u0438 DTO\u0448\u043a\u0438:<\/p>\n<pre><code class=\"yaml\">openapi: 3.0.1 servers:  - url: '{protocol}:{domain}\/kubernetes-manager\/api'   info:  title: Kubernetes manager Service API  description: Kubernetes manager Service API  version: 1.0.0   paths:  \/pod\/list:    get:      tags:        - ResourceList      operationId: getPods      description: Get list of pods      responses:        200:          description: Get list of pods          content:            application\/json:              schema:                $ref: '#\/components\/schemas\/PodListResponse'    \/node\/list:    get:      tags:        - ResourceList      operationId: getNodes      description: Get list of nodes      responses:        200:          description: Get list of nodes          content:            application\/json:              schema:                $ref: '#\/components\/schemas\/NodeListResponse'    \/ingress\/{namespace}\/list:    get:      tags:        - ResourceList      operationId: getIngresses      description: Get list of ingresses      parameters:        - name: namespace          in: path          required: true          schema:              type: string      responses:        200:          description: Get list of ingresses          content:            application\/json:              schema:                $ref: '#\/components\/schemas\/IngressListResponse'   components:  schemas:    PodListResponse:      type: object      properties:        pods:          type: array          items:            $ref: '#\/components\/schemas\/Pod'        total:          type: integer          format: int32      Pod:      type: object      properties:        name:          type: string        namespace:          type: string        status:          type: string        restartCount:          type: integer          format: int32        creationTimestamp:          type: string          format: date-time        labels:          type: object          additionalProperties:            type: string        annotations:          type: object          additionalProperties:            type: string      NodeListResponse:      type: object      properties:        nodes:          type: array          items:            $ref: '#\/components\/schemas\/Node'        total:          type: integer          format: int32      Node:      type: object      properties:        name:          type: string        status:          type: string        labels:          type: object          additionalProperties:            type: string        annotations:          type: object          additionalProperties:            type: string      IngressListResponse:      type: object      properties:        ingresses:          type: array          items:            $ref: '#\/components\/schemas\/Ingress'        total:          type: integer          format: int32      Ingress:      type: object      properties:        name:          type: string        namespace:          type: string        host:          type: string        path:          type: string <\/code><\/pre>\n<p>\u0417\u0430\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u0444\u0430\u0439\u043b <strong>application.yml<\/strong> (\u043b\u0438\u0431\u043e <strong>application.properties<\/strong>, \u043a\u0430\u043a \u0443\u0434\u043e\u0431\u043d\u043e)<\/p>\n<pre><code class=\"yaml\">server:  servlet:    context-path: \/kubernetes-manager\/api  port: 8080   kubernetes:  config-file:    path: ${kubernetes.config-file.path}<\/code><\/pre>\n<p>\u0412 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e <strong>kubernetes.config-file.path <\/strong>\u043c\u044b \u0431\u0443\u0434\u0435\u043c \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0442\u044c \u043f\u0443\u0442\u044c \u043a \u043d\u0430\u0448\u0435\u043c\u0443 \u043a\u043e\u043d\u0444\u0438\u0433-\u0444\u0430\u0439\u043b\u0443 <strong>Kubernetes<\/strong>.<\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u043c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u044b \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u043e\u0432 \u0438 \u043c\u043e\u0434\u0435\u043b\u0438 \u043a\u043e\u043c\u0430\u043d\u0434\u043e\u0439:<br \/><strong><em>mvn clean generate-sources\u00a0<\/em><\/strong><\/p>\n<p>\u0415\u0441\u043b\u0438 \u0432\u0441\u0435 \u043f\u0440\u043e\u0448\u043b\u043e \u0443\u0441\u043f\u0435\u0448\u043d\u043e, \u0442\u043e \u0432 \u043f\u0430\u043f\u043a\u0435 <strong>target<\/strong> \u043c\u044b \u0443\u0432\u0438\u0434\u0438\u043c \u0442\u0430\u043a\u0443\u044e \u043a\u0430\u0440\u0442\u0438\u043d\u0443:<\/p>\n<figure class=\"\"><\/figure>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043f\u043e\u043c\u0435\u0447\u0430\u0435\u043c package <strong>generated-sources<\/strong> \u043a\u0430\u043a \u201c<strong><em>Generated Source Root<\/em><\/strong>\u201d, \u0432 <strong>Intellij <\/strong><\/p>\n<\/div>\n<\/details>\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-425926","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/425926","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=425926"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/425926\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=425926"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=425926"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=425926"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}