{"id":452853,"date":"2025-03-23T15:08:59","date_gmt":"2025-03-23T15:08:59","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=452853"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=452853","title":{"rendered":"<span>Ansible-pull \u0438 GitLab CI\/CD: \u043a\u043e\u0433\u0434\u0430 \u043b\u0443\u0447\u0448\u0435 \u0442\u044f\u043d\u0443\u0442\u044c, \u0447\u0435\u043c \u0442\u043e\u043b\u043a\u0430\u0442\u044c<\/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\/58f\/ce4\/aaa\/58fce4aaac4e198479691b381dc8539c.jpg\" width=\"894\" height=\"724\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/58f\/ce4\/aaa\/58fce4aaac4e198479691b381dc8539c.jpg\" data-blurred=\"true\"\/><\/figure>\n<p>\ud83d\udc4b \u041f\u0440\u0438\u0432\u0435\u0442! <\/p>\n<p>\u041c\u0435\u043d\u044f \u0437\u043e\u0432\u0443\u0442 \u0410\u043d\u0434\u0440\u0435\u0439, \u044f \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u0438\u0441\u0442 \u043f\u043e \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044e IT-\u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043e\u0439 \u0441 \u043e\u043f\u044b\u0442\u043e\u043c \u0440\u0430\u0431\u043e\u0442\u044b \u0441 Windows- \u0438 Linux-\u0441\u0438\u0441\u0442\u0435\u043c\u0430\u043c\u0438.<\/p>\n<p>\u0421\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u0430\u044f IT-\u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u2014 \u044d\u0442\u043e \u0436\u0438\u0432\u043e\u0439 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u043c, \u0441\u043e\u0441\u0442\u043e\u044f\u0449\u0438\u0439 \u0438\u0437 \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u0432, \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0445 \u043c\u0430\u0448\u0438\u043d \u0438 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u043e\u0432, \u0440\u0430\u0437\u0431\u0440\u043e\u0441\u0430\u043d\u043d\u044b\u0445 \u043f\u043e \u0440\u0430\u0437\u043d\u044b\u043c \u0434\u0430\u0442\u0430-\u0446\u0435\u043d\u0442\u0440\u0430\u043c \u0438 \u043e\u0431\u043b\u0430\u0447\u043d\u044b\u043c \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0430\u043c. <\/p>\n<p><strong>\u0423\u0437\u043b\u0430\u043c\u0438 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c:<\/strong> <\/p>\n<ul>\n<li>\n<p>\u0420\u0430\u0431\u043e\u0447\u0438\u0435 \u0441\u0442\u0430\u043d\u0446\u0438\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439,<\/p>\n<\/li>\n<li>\n<p>\u0421\u0435\u0440\u0432\u0435\u0440\u044b \u0437\u0430 NAT \u0432 \u0441\u0435\u0440\u044b\u0445 \u0437\u043e\u043d\u0430\u0445 \u0441\u0435\u0442\u0438,<\/p>\n<\/li>\n<li>\n<p>\u0425\u043e\u0441\u0442\u044b \u0432 \u043d\u0435\u0441\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u044b\u0445 \u0441\u0435\u0442\u0435\u0432\u044b\u0445 \u0443\u0441\u043b\u043e\u0432\u0438\u044f\u0445, \u043f\u0435\u0440\u0438\u043e\u0434\u0438\u0447\u0435\u0441\u043a\u0438 \u0438\u0441\u0447\u0435\u0437\u0430\u044e\u0449\u0438\u0435 \u0438\u0437 \u0434\u043e\u0441\u0442\u0443\u043f\u0430.<\/p>\n<\/li>\n<\/ul>\n<p><strong>\u041f\u0440\u0438 \u0440\u0430\u0431\u043e\u0442\u0435 \u0441 \u0442\u0430\u043a\u043e\u0439 \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u043d\u043e\u0439 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043e\u0439 \u043c\u043e\u0436\u043d\u043e \u0441\u0442\u043e\u043b\u043a\u043d\u0443\u0442\u044c\u0441\u044f \u0441 \u0440\u044f\u0434\u043e\u043c \u0441\u043b\u043e\u0436\u043d\u043e\u0441\u0442\u0435\u0439.<\/strong><\/p>\n<ul>\n<li>\n<p><strong>\u0421\u0435\u0442\u0438 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u043d\u0435\u043d\u0430\u0434\u0435\u0436\u043d\u044b\u043c\u0438 <\/strong>\u2013 \u0441\u0435\u0440\u0432\u0435\u0440 \u0438\u043b\u0438 \u0440\u0430\u0431\u043e\u0447\u0430\u044f \u0441\u0442\u0430\u043d\u0446\u0438\u044f \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u043d\u0430 \u0438\u0437-\u0437\u0430 \u0441\u0431\u043e\u044f \u0441\u0432\u044f\u0437\u0438, \u043f\u0435\u0440\u0435\u0433\u0440\u0443\u0437\u043a\u0438 \u0438\u043b\u0438 \u043f\u043e\u043b\u0438\u0442\u0438\u043a\u0438 \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u0438.   <\/p>\n<\/li>\n<li>\n<p><strong>\u0423\u0437\u043b\u044b \u0437\u0430 NAT \u0438\u043b\u0438 \u0432 \u0438\u0437\u043e\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u0437\u043e\u043d\u0430\u0445<\/strong> \u2013 Ansible Control Node \u043d\u0435 \u0438\u043c\u0435\u0435\u0442 \u043f\u0440\u044f\u043c\u043e\u0433\u043e \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u0442\u0430\u043a\u0438\u043c \u0443\u0437\u043b\u0430\u043c.<\/p>\n<\/li>\n<li>\n<p><strong>\u0423\u0434\u0430\u043b\u0451\u043d\u043d\u044b\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u043f\u0435\u0440\u0435\u043c\u0435\u0449\u0430\u044e\u0442\u0441\u044f<\/strong> \u2013 \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043d\u043e\u0443\u0442\u0431\u0443\u043a\u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432 \u0438\u043b\u0438 \u043e\u0431\u043e\u0440\u0443\u0434\u043e\u0432\u0430\u043d\u0438\u0435 \u0432 \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u044b\u0445 \u0434\u0430\u0442\u0430-\u0446\u0435\u043d\u0442\u0440\u0430\u0445.    <\/p>\n<\/li>\n<\/ul>\n<div class=\"persona\"><img decoding=\"async\" class=\"image persona__image\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/ba3\/313\/ebf\/ba3313ebf3fbf229f0147b0fbdb7b2af.jpg\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/ba3\/313\/ebf\/ba3313ebf3fbf229f0147b0fbdb7b2af.jpg\" data-blurred=\"true\"\/><\/p>\n<h5 class=\"persona__heading\">\u041c\u043e\u0439 \u0442\u0435\u043b\u0435\u0433\u0440\u0430\u043c\u043c \u043a\u0430\u043d\u0430\u043b &#8212; \u0441\u043e\u043e\u0431\u0449\u0435\u0441\u0442\u0432\u043e, \u0433\u0434\u0435 \u0434\u0435\u043b\u044f\u0442\u0441\u044f \u043e\u043f\u044b\u0442\u043e\u043c<\/h5>\n<p><a href=\"https:\/\/t.me\/IT_Chuyana\" rel=\"noopener noreferrer nofollow\"><strong>https:\/\/t.me\/IT_Chuyana<\/strong><\/a><a href=\"https:\/\/t.me\/IT_Chuyana\" rel=\"noopener noreferrer nofollow\"> <\/a><\/p>\n<\/div>\n<h2>\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u043a\u043b\u0430\u0441\u0441\u0438\u0447\u0435\u0441\u043a\u043e\u0439 push-\u043c\u043e\u0434\u0435\u043b\u0438 Ansible   <\/h2>\n<p>\u0412 push-\u043c\u043e\u0434\u0435\u043b\u0438 \u0446\u0435\u043d\u0442\u0440\u0430\u043b\u044c\u043d\u044b\u0439 \u0441\u0435\u0440\u0432\u0435\u0440 Ansible \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0437\u0430 \u0434\u043e\u0441\u0442\u0430\u0432\u043a\u0443 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0439 \u0443\u0437\u043b\u0430\u043c \u043f\u0440\u0438 \u0438\u0445 \u0441\u0435\u0442\u0435\u0432\u043e\u0439 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e\u0441\u0442\u0438.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/05b\/7e1\/38a\/05b7e138aaa4c16816b16eb840883a65.jpg\" alt=\"\u0421\u0435\u0440\u0432\u0435\u0440 \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u0442 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e \u043a\u0430\u0436\u0434\u043e\u043c\u0443 \u0443\u0437\u043b\u0443 \u043f\u0440\u0438 \u0435\u0433\u043e \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e\u0441\u0442\u0438\" title=\"\u0421\u0435\u0440\u0432\u0435\u0440 \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u0442 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e \u043a\u0430\u0436\u0434\u043e\u043c\u0443 \u0443\u0437\u043b\u0443 \u043f\u0440\u0438 \u0435\u0433\u043e \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e\u0441\u0442\u0438\" width=\"1260\" height=\"736\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/05b\/7e1\/38a\/05b7e138aaa4c16816b16eb840883a65.jpg\" data-blurred=\"true\"\/><\/p>\n<div><figcaption>\u0421\u0435\u0440\u0432\u0435\u0440 \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u0442 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e \u043a\u0430\u0436\u0434\u043e\u043c\u0443 \u0443\u0437\u043b\u0443 \u043f\u0440\u0438 \u0435\u0433\u043e \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e\u0441\u0442\u0438<\/figcaption><\/div>\n<\/figure>\n<p>\u0412 \u0443\u0441\u043b\u043e\u0432\u0438\u044f\u0445 \u043d\u0435\u0441\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u044b\u0445 \u0438 \u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u0451\u043d\u043d\u044b\u0445 \u0441\u0435\u0442\u0435\u0439 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0442\u0430\u043a\u043e\u0439 \u043c\u043e\u0434\u0435\u043b\u0438 \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442 \u043a \u0440\u044f\u0434\u0443 \u043f\u0440\u043e\u0431\u043b\u0435\u043c: <\/p>\n<h4>\u041d\u0435\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0441\u0442\u044c \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438    <\/h4>\n<ul>\n<li>\n<p>\u0415\u0441\u043b\u0438 \u0443\u0437\u0435\u043b \u0431\u044b\u043b \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d \u0432\u043e \u0432\u0440\u0435\u043c\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f Ansible-\u0437\u0430\u0434\u0430\u0447 (playbook), \u043e\u043d \u043e\u0441\u0442\u0430\u0451\u0442\u0441\u044f <strong>\u0443\u0441\u0442\u0430\u0440\u0435\u0432\u0448\u0438\u043c<\/strong> \u0438 \u043c\u043e\u0436\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u043d\u0435\u0432\u0435\u0440\u043d\u044b\u043c\u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u043c\u0438.    <\/p>\n<\/li>\n<li>\n<p>\u041d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u043e\u0434\u043d\u043e\u0437\u043d\u0430\u0447\u043d\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c, <strong>\u043a\u0430\u043a\u0430\u044f \u0432\u0435\u0440\u0441\u0438\u044f \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u0441\u0435\u0439\u0447\u0430\u0441 \u043d\u0430 \u0443\u0437\u043b\u0435<\/strong>, \u0435\u0441\u043b\u0438 \u043e\u043d \u043f\u0435\u0440\u0438\u043e\u0434\u0438\u0447\u0435\u0441\u043a\u0438 \u0438\u0441\u0447\u0435\u0437\u0430\u0435\u0442 \u0438\u0437 \u0441\u0435\u0442\u0438.     <\/p>\n<\/li>\n<li>\n<p><strong>\u0420\u0430\u0437\u043d\u044b\u0435 \u0432\u0435\u0440\u0441\u0438\u0438 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438<\/strong> \u0443 \u0440\u0430\u0437\u043d\u044b\u0445 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u0432 \u043c\u043e\u0433\u0443\u0442 \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u043d\u0435\u043e\u0436\u0438\u0434\u0430\u043d\u043d\u044b\u0435 \u0431\u0430\u0433\u0438 \u0438 \u0440\u0430\u0441\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u044e \u0440\u0430\u0431\u043e\u0442\u044b \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432.       <\/p>\n<\/li>\n<\/ul>\n<h4>\u041f\u0435\u0440\u0435\u0433\u0440\u0443\u0437\u043a\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f    <\/h4>\n<ul>\n<li>\n<p>\u041f\u0440\u0438 \u0442\u0440\u0430\u0434\u0438\u0446\u0438\u043e\u043d\u043d\u043e\u043c \u043f\u043e\u0434\u0445\u043e\u0434\u0435 Ansible <strong>\u043e\u0434\u0438\u043d \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u044e\u0449\u0438\u0439 \u0441\u0435\u0440\u0432\u0435\u0440 \u0434\u043e\u043b\u0436\u0435\u043d \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u0432\u0441\u0435 \u0443\u0437\u043b\u044b<\/strong>, \u0447\u0442\u043e \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u0443\u0437\u043a\u0438\u043c \u043c\u0435\u0441\u0442\u043e\u043c.    <\/p>\n<\/li>\n<li>\n<p>\u041c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432 \u0438 \u0443\u0441\u043b\u043e\u0436\u043d\u044f\u0435\u0442 \u043f\u0440\u043e\u0446\u0435\u0441\u0441.    <\/p>\n<\/li>\n<li>\n<p>\u0415\u0441\u043b\u0438 \u0446\u0435\u043d\u0442\u0440\u0430\u043b\u044c\u043d\u044b\u0439 \u0441\u0435\u0440\u0432\u0435\u0440 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0432\u044b\u0445\u043e\u0434\u0438\u0442 \u0438\u0437 \u0441\u0442\u0440\u043e\u044f, <strong>\u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u044f \u043e\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442\u0441\u044f<\/strong>.    <\/p>\n<\/li>\n<\/ul>\n<h4>\u0420\u0438\u0441\u043a \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u043e\u0433\u043e \u0434\u0440\u0435\u0439\u0444\u0430 <\/h4>\n<ul>\n<li>\n<p>\u0411\u0435\u0437 \u043f\u043e\u0441\u0442\u043e\u044f\u043d\u043d\u043e\u0433\u043e \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044f \u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 <strong>\u0443\u0437\u043b\u044b \u043c\u043e\u0433\u0443\u0442 &#171;\u0434\u0440\u0435\u0439\u0444\u043e\u0432\u0430\u0442\u044c&#187; \u043e\u0442 \u044d\u0442\u0430\u043b\u043e\u043d\u043d\u043e\u0439 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438<\/strong>. <\/p>\n<\/li>\n<li>\n<p>\u0415\u0441\u043b\u0438 \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440 \u0432\u0440\u0443\u0447\u043d\u0443\u044e \u0438\u0437\u043c\u0435\u043d\u0438\u043b \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438, \u0430 \u043f\u043e\u0442\u043e\u043c Ansible \u043d\u0435 \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u043b\u0441\u044f \u0434\u043e\u043b\u0433\u043e\u0435 \u0432\u0440\u0435\u043c\u044f \u2013 \u043c\u043e\u0433\u0443\u0442 \u0432\u043e\u0437\u043d\u0438\u043a\u043d\u0443\u0442\u044c \u043d\u0435\u043e\u0436\u0438\u0434\u0430\u043d\u043d\u044b\u0435 \u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442\u044b.<\/p>\n<\/li>\n<li>\n<p>\u0412 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0435 \u0447\u0430\u0441\u0442\u044c \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u0432 \u043c\u043e\u0436\u0435\u0442 \u043e\u043a\u0430\u0437\u0430\u0442\u044c\u0441\u044f \u0432 <strong>\u043d\u0435\u043f\u0440\u0435\u0434\u0441\u043a\u0430\u0437\u0443\u0435\u043c\u043e\u043c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0438 \u0434\u043b\u044f \u043a\u043e\u043c\u0430\u043d\u0434\u044b DevOps<\/strong>  <\/p>\n<\/li>\n<\/ul>\n<h2>\u041a\u0430\u043a \u0440\u0435\u0448\u0438\u0442\u044c \u044d\u0442\u0438 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b   <\/h2>\n<p>\u041e\u043f\u0442\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u043c \u0440\u0435\u0448\u0435\u043d\u0438\u0435\u043c \u0434\u043b\u044f \u0442\u0430\u043a\u0438\u0445 \u0443\u0441\u043b\u043e\u0432\u0438\u0439 \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f <code><strong>ansible-pull<\/strong><\/code> \u2013 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u0435\u043d\u044f\u0435\u0442 \u0442\u0440\u0430\u0434\u0438\u0446\u0438\u043e\u043d\u043d\u0443\u044e \u043c\u043e\u0434\u0435\u043b\u044c \u0440\u0430\u0431\u043e\u0442\u044b Ansible. \u0412\u043c\u0435\u0441\u0442\u043e \u0442\u043e\u0433\u043e \u0447\u0442\u043e\u0431\u044b \u00ab\u0442\u043e\u043b\u043a\u0430\u0442\u044c\u00bb \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e \u043d\u0430 \u0443\u0434\u0430\u043b\u0451\u043d\u043d\u044b\u0435 \u0443\u0437\u043b\u044b, <strong>\u043a\u0430\u0436\u0434\u044b\u0439 \u0441\u0435\u0440\u0432\u0435\u0440 \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u0442 \u0438 \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u0442 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e<\/strong>.   <\/p>\n<p>\u041e\u0433\u043e\u0432\u043e\u0440\u044e\u0441\u044c \u0441\u0440\u0430\u0437\u0443: \u043f\u043e \u0441\u0443\u0442\u0438, ansible-pull \u2013 \u044d\u0442\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u043a\u043e\u043c\u0430\u043d\u0434\u044b:<br \/><code> git pull &amp;&amp; ansible-playbook -i inventory_with...<\/code><\/p>\n<p>\u041e\u043d\u0430 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442\u0441\u044f \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u043a\u043e\u0434, \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u043d\u044b\u0439 \u0438\u0437 \u0443\u0434\u0430\u043b\u0451\u043d\u043d\u043e\u0433\u043e \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f.<\/p>\n<p>\u041e\u0434\u043d\u0430\u043a\u043e, \u0432 \u043e\u0442\u043b\u0438\u0447\u0438\u0435 \u043e\u0442 Puppet, \u0440\u0430\u0431\u043e\u0442\u0430 \u0441 YAML-\u0441\u0438\u043d\u0442\u0430\u043a\u0441\u0438\u0441\u043e\u043c, \u043d\u0430 \u043c\u043e\u0439 \u0432\u0437\u0433\u043b\u044f\u0434, \u0433\u043e\u0440\u0430\u0437\u0434\u043e \u0443\u0434\u043e\u0431\u043d\u0435\u0435. \u041a\u0440\u043e\u043c\u0435 \u0442\u043e\u0433\u043e, \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 Ansible \u0448\u0438\u0440\u0435, \u0447\u0442\u043e \u0434\u0435\u043b\u0430\u0435\u0442 \u0435\u0433\u043e \u0431\u043e\u043b\u0435\u0435 \u0433\u0438\u0431\u043a\u0438\u043c \u0438 \u043c\u043e\u0449\u043d\u044b\u043c \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u043c \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u0438.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/ffd\/640\/8ef\/ffd6408efe140575652da13e55393ca0.jpg\" alt=\"\u041a\u0430\u0436\u0434\u044b\u0439 \u0443\u0437\u0435\u043b \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e \u0437\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0435\u0442 \u0438 \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u0442 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e\" title=\"\u041a\u0430\u0436\u0434\u044b\u0439 \u0443\u0437\u0435\u043b \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e \u0437\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0435\u0442 \u0438 \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u0442 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e\" width=\"1216\" height=\"752\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/ffd\/640\/8ef\/ffd6408efe140575652da13e55393ca0.jpg\" data-blurred=\"true\"\/><\/p>\n<div><figcaption>\u041a\u0430\u0436\u0434\u044b\u0439 \u0443\u0437\u0435\u043b \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e \u0437\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0435\u0442 \u0438 \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u0442 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e<\/figcaption><\/div>\n<\/figure>\n<h4>\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 ansible-pull<\/h4>\n<ul>\n<li>\n<p><strong>\u041e\u0431\u043e\u0440\u0443\u0434\u043e\u0432\u0430\u043d\u0438\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f \u043d\u0435\u0440\u0435\u0433\u0443\u043b\u044f\u0440\u043d\u043e<\/strong> \u2014 \u0441\u0430\u043c\u043e \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u0442 \u043a\u043e\u043d\u0444\u0438\u0433 \u043f\u043e\u0441\u043b\u0435 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f.<\/p>\n<\/li>\n<li>\n<p><strong>\u041d\u0435 \u043d\u0443\u0436\u0435\u043d \u0446\u0435\u043d\u0442\u0440\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u044b\u0439 Ansible Control Node<\/strong> (\u043a\u0430\u0436\u0434\u044b\u0439 \u0443\u0437\u0435\u043b \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0441\u0430\u043c \u0437\u0430 \u0441\u0435\u0431\u044f). <\/p>\n<\/li>\n<li>\n<p><strong>\u0420\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0434\u0430\u0436\u0435 \u0437\u0430 NAT \/ Firewall<\/strong> \u2014 \u0442\u044f\u043d\u0435\u0442 \u043a\u043e\u043d\u0444\u0438\u0433 \u0438\u0437 \u043e\u0431\u043b\u0430\u0447\u043d\u043e\u0433\u043e \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f (Git).  <\/p>\n<\/li>\n<li>\n<p><strong>\u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0440\u0430\u0441\u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u044f\u044e\u0442\u0441\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438<\/strong> \u2014 \u043d\u0435\u0442 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u0438\u0432\u043d\u043e\u0433\u043e \u0434\u043e\u0441\u0442\u0443\u043f\u0430.<\/p>\n<\/li>\n<li>\n<p><strong>\u041b\u0435\u0433\u043a\u043e \u043e\u0442\u043a\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f<\/strong> \u2014 \u0440\u0435\u0432\u0435\u0440\u0442 \u0447\u0435\u0440\u0435\u0437 Git \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u043e.<\/p>\n<\/li>\n<\/ul>\n<h4>\u041f\u0440\u0438\u043c\u0435\u0440\u044b \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u044f ansible-pull<\/h4>\n<ul>\n<li>\n<p><strong>\u0420\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u0451\u043d\u043d\u044b\u0435 \u0441\u0442\u0430\u043d\u0446\u0438\u0438 \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043f\u0438\u0442\u0430\u043d\u0438\u044f, \u043c\u0435\u0442\u0435\u043e\u0441\u0442\u0430\u043d\u0446\u0438\u0438 \u0438 \u0434\u0430\u0442\u0447\u0438\u043a\u0438<\/strong><\/p>\n<\/li>\n<li>\n<p><strong>\u0413\u0435\u043e\u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u0451\u043d\u043d\u044b\u0435 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u0442\u0435\u043b\u0435\u0444\u043e\u043d\u0438\u0438 (IP-\u0442\u0435\u043b\u0435\u0444\u043e\u043d\u044b, Asterisk)<\/strong>    <\/p>\n<\/li>\n<li>\n<p><strong>\u0420\u0430\u0431\u043e\u0447\u0438\u0435 \u0441\u0442\u0430\u043d\u0446\u0438\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 (\u043d\u043e\u0443\u0442\u0431\u0443\u043a\u0438, \u043a\u0430\u0441\u0441\u043e\u0432\u044b\u0435 \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b\u044b)<\/strong>  <\/p>\n<\/li>\n<li>\n<p><strong>\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043d\u043e\u0432\u044b\u0445 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u0432 (\u0430\u0432\u0442\u043e\u043f\u0440\u043e\u0432\u0438\u0436\u043d\u0438\u043d\u0433)<\/strong>    <\/p>\n<\/li>\n<li>\n<p><strong>IoT-\u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0438 \u0441\u0435\u0442\u0435\u0432\u043e\u0435 \u043e\u0431\u043e\u0440\u0443\u0434\u043e\u0432\u0430\u043d\u0438\u0435<\/strong>   <\/p>\n<\/li>\n<\/ul>\n<p>\u0414\u043e\u0432\u043e\u0434\u044b \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d\u044b, \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u043e\u0434\u0443\u043c\u0430\u0435\u043c \u043a\u0430\u043a \u043d\u0430\u043c \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u044d\u0442\u043e\u0442 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c.<\/p>\n<h2>\u0425\u043e\u0434 \u0440\u0430\u0431\u043e\u0442\u044b<\/h2>\n<p>\u041d\u0430\u0448\u0443 \u0437\u0430\u0434\u0430\u0447\u0443 \u043c\u044b \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u043c \u043d\u0430 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u0434\u0437\u0430\u0434\u0430\u0447:<\/p>\n<ol>\n<li>\n<p>\u041f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0430 \u0442\u0435\u0441\u0442\u043e\u0432\u043e\u0433\u043e \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f \u043d\u0430 \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0445 \u043c\u0430\u0448\u0438\u043d\u0430\u0445 \u0447\u0435\u0440\u0435\u0437<strong> Vagrant<\/strong><\/p>\n<\/li>\n<li>\n<p>\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0443\u0437\u043b\u043e\u0432 \u043a <strong>\u0440\u0435\u0436\u0438\u043c\u0443 Ansible-pull<\/strong>, \u0432\u044b\u0434\u0430\u0447\u0430 \u0438\u043c <strong>\u0440\u043e\u043b\u0435\u0439 <\/strong>\u0438 <strong>RSA-\u043a\u043b\u044e\u0447\u0435\u0439<\/strong><\/p>\n<\/li>\n<li>\n<p>\u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0441\u0440\u0435\u0434\u044b \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0430 \u043a\u043e\u0434\u0430 \u0432<strong> Docker-\u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0435<\/strong><\/p>\n<\/li>\n<li>\n<p><strong>\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 CI\/CD-\u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d\u0430<\/strong> \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430 \u0440\u0430\u0437\u0432\u0435\u0440\u0442\u044b\u0432\u0430\u043d\u0438\u044f \u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0432 \u043a\u043e\u043d\u0432\u0435\u0439\u0435\u0440\u0435<\/p>\n<\/li>\n<li>\n<p><strong>\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0440\u043e\u043b\u0435\u0439<\/strong> \u0434\u043b\u044f Ansible-pull.<\/p>\n<\/li>\n<\/ol>\n<p>CI\/CD<strong>&#8212;<\/strong>\u043a\u043e\u043d\u0432\u0435\u0439\u0435\u0440 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0443\u0435\u043c \u043f\u043e \u0441\u0445\u0435\u043c\u0435:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/35c\/2ed\/8ea\/35c2ed8eae860ccbfbb578efc794ce0b.jpg\" width=\"1104\" height=\"266\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/35c\/2ed\/8ea\/35c2ed8eae860ccbfbb578efc794ce0b.jpg\" data-blurred=\"true\"\/><\/figure>\n<h3>1. \u041f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0430 \u0442\u0435\u0441\u0442\u043e\u0432\u043e\u0433\u043e \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f \u043d\u0430 \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0445 \u043c\u0430\u0448\u0438\u043d\u0430\u0445 \u0447\u0435\u0440\u0435\u0437 Vagrant<\/h3>\n<p>Vagrant \u044d\u0442\u043e \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u0434\u043b\u044f \u0431\u044b\u0441\u0442\u0440\u043e\u0433\u043e \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u043c\u0438 \u0441\u0440\u0435\u0434\u0430\u043c\u0438. \u041e\u043d \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043f\u043e\u0434\u043d\u0438\u043c\u0430\u0442\u044c \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0435 \u043c\u0430\u0448\u0438\u043d\u044b \u0441 \u0437\u0430\u0440\u0430\u043d\u0435\u0435 \u0437\u0430\u0434\u0430\u043d\u043d\u043e\u0439 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0435\u0439.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/044\/ea3\/ca5\/044ea3ca55420f0cbf6085cc6eab8b1b.jpg\" width=\"961\" height=\"725\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/044\/ea3\/ca5\/044ea3ca55420f0cbf6085cc6eab8b1b.jpg\" data-blurred=\"true\"\/><\/figure>\n<p>\u0412 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043c\u044b \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u0438\u043c \u0442\u0435\u0441\u0442\u043e\u0432\u043e\u0435 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u0435 \u0438\u0437 <strong>\u0447\u0435\u0442\u044b\u0440\u0451\u0445 \u0443\u0437\u043b\u043e\u0432<\/strong>:   <\/p>\n<ul>\n<li>\n<p><strong>webserver<\/strong> (\u0432\u0435\u0431-\u0441\u0435\u0440\u0432\u0435\u0440) Debian 12, <\/p>\n<\/li>\n<li>\n<p><strong>application<\/strong> (\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435) Debian 12,<\/p>\n<\/li>\n<li>\n<p><strong>client-1 <\/strong>Ubuntu 18.04<\/p>\n<\/li>\n<li>\n<p><strong>client-2 <\/strong>CentOS 7. <\/p>\n<\/li>\n<\/ul>\n<p>\u041a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u0438\u0445 \u0443\u0437\u043b\u043e\u0432 <strong>\u043d\u0435 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u043e<\/strong>, \u0438\u0445 \u0447\u0438\u0441\u043b\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442\u0441\u044f \u0440\u0430\u0437\u043b\u0438\u0447\u0438\u0435\u043c \u0432 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u0445 \u0434\u0438\u0441\u0442\u0440\u0438\u0431\u0443\u0442\u0438\u0432\u0430\u0445 \u0438 \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u044f\u0445 \u0438\u0445 \u0440\u0430\u0431\u043e\u0442\u044b. <\/p>\n<p>\u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430 \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0445 \u043c\u0430\u0448\u0438\u043d \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <strong>libvirt<\/strong>. \u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0439 \u0444\u0430\u0439\u043b <strong>Vagrantfile<\/strong> \u0434\u043b\u044f <code>vagrant\/libvirt\/<\/code> \u0431\u0443\u0434\u0435\u0442 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442\u044c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0434\u043b\u044f \u0440\u0430\u0437\u0432\u0435\u0440\u0442\u044b\u0432\u0430\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u043e\u0433\u043e \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f.  <\/p>\n<p><strong>\u0421\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u043c Vagrantfile:<\/strong><\/p>\n<details class=\"spoiler\">\n<summary>\u0421\u043a\u0440\u044b\u0442\u044b\u0439 \u0442\u0435\u043a\u0441\u0442<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"bash\"># vagrant\\libvirt\\Vagrantfile # \u0418\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0434\u043b\u044f \u043c\u043e\u0441\u0442\u0430 BRIDGE_NET = \"192.168.2.\" BRIDGE_NAME = \"br0\" BRIDGE_DNS = \"192.168.2.1\"  # \u041f\u0443\u0442\u044c \u043a \u043e\u0431\u0440\u0430\u0437\u0430\u043c \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0445 \u043c\u0430\u0448\u0438\u043d IMAGE_PATH = \"\/kvm\/images\"   # \u0414\u043e\u043c\u0435\u043d \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u043b\u044f \u0432\u0441\u0435\u0439 \u043f\u043b\u043e\u0449\u0430\u0434\u043a\u0438 DOMAIN = \"ch.ap\"  # debian 12 BOX_1 = \"d12\"     # debian 12 BOX_2 = \"u1804\"   # ubuntu 18.04 BOX_3 = \"c7\"      # centos 7  # \u0423\u043a\u0430\u0436\u0438\u0442\u0435 \u043f\u0443\u0442\u044c \u043a \u043e\u0431\u0449\u0435\u0439 \u043f\u0430\u043f\u043a\u0435 \u043d\u0430 \u0445\u043e\u0441\u0442\u0435 \u0438 \u043f\u0443\u0442\u044c \u043a \u0442\u043e\u0447\u043a\u0435 \u043c\u043e\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0432 \u0412\u041c HOST_SHARED_FOLDER = \".\"   VM_SHARED_FOLDER = \"\/home\/vagrant\/shared\"  # \u0418\u043c\u0435\u043d\u0430 \u0445\u043e\u0441\u0442\u043e\u0432 HOSTNAME_1 = \"t-webserver\" HOSTNAME_2 = \"t-application\" HOSTNAME_3 = \"t-client-1\" HOSTNAME_4 = \"t-client-2\"  # \u041c\u0430\u0441\u0441\u0438\u0432 \u0438\u0437 \u0445\u0435\u0448\u0435\u0439, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0437\u0430\u0434\u0430\u044e\u0442\u0441\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0439 \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0439 \u043c\u0430\u0448\u0438\u043d\u044b. MACHINES = {   HOSTNAME_1.to_sym =&gt; {     :box_name     =&gt; BOX_1,     :host_name    =&gt; HOSTNAME_1,      :ip_brig      =&gt; BRIDGE_NET + \"211\",     :ip_brig_dns  =&gt; BRIDGE_DNS,     :disk_size    =&gt; \"20G\",     :int_model_type =&gt; \"e1000\",     :cpu          =&gt; 1,     :ram          =&gt; 512,     :vnc          =&gt; 5971,     :host_role    =&gt; \"webserver\",   },   HOSTNAME_2.to_sym =&gt; {     :box_name     =&gt; BOX_1,     :host_name    =&gt; HOSTNAME_2,      :ip_brig      =&gt; BRIDGE_NET + \"212\",     :ip_brig_dns  =&gt; BRIDGE_DNS,     :disk_size    =&gt; \"20G\",     :int_model_type =&gt; \"e1000\",     :cpu          =&gt; 1,     :ram          =&gt; 1024,     :vnc          =&gt; 5972,     :host_role    =&gt; \"application\",   },   HOSTNAME_3.to_sym =&gt; {     :box_name     =&gt; BOX_2,     :host_name    =&gt; HOSTNAME_3,      :ip_brig      =&gt; BRIDGE_NET + \"213\",     :ip_brig_dns  =&gt; BRIDGE_DNS,     :disk_size    =&gt; \"20G\",     :int_model_type =&gt; \"e1000\",     :cpu          =&gt; 1,     :ram          =&gt; 512,     :vnc          =&gt; 5973,     :host_role    =&gt; \"client\",   },   HOSTNAME_4.to_sym =&gt; {     :box_name     =&gt; BOX_3,     :host_name    =&gt; HOSTNAME_4,      :ip_brig      =&gt; BRIDGE_NET + \"214\",     :ip_brig_dns  =&gt; BRIDGE_DNS,     :disk_size    =&gt; \"20G\",     :int_model_type =&gt; \"e1000\",     :cpu          =&gt; 1,     :ram          =&gt; 512,     :vnc          =&gt; 5974,     :host_role    =&gt; \"client\",   }, }  ## \u043c\u043e\u0434\u0443\u043b\u044c \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a Vagrant.configure(\"2\") do |config|   MACHINES.each do |boxname, boxconfig|     config.vm.define boxname do |box|       box.vm.box = boxconfig[:box_name]                      box.vm.hostname = boxconfig[:host_name]              # \u0423\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u043f\u0443\u0431\u043b\u0438\u0447\u043d\u0443\u044e \u0441\u0435\u0442\u044c \u0441 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\u043c\u0438 \u043c\u043e\u0441\u0442\u0430       box.vm.network \"public_network\",                        bridge: BRIDGE_NAME,                        dev: BRIDGE_NAME,                        type: \"bridge\",                        ip: boxconfig[:ip_brig]        box.vm.provider \"libvirt\" do |libvirt|         libvirt.cpus = boxconfig[:cpu]         libvirt.memory = boxconfig[:ram]         libvirt.nic_model_type = boxconfig[:int_model_type]         libvirt.management_network_name = BRIDGE_NAME         libvirt.storage_pool_name = \"images\"         libvirt.graphics_port = boxconfig[:vnc]         libvirt.graphics_autoport = false       end        # # \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0439 \u043f\u0430\u043f\u043a\u0438       box.vm.synced_folder \".\", VM_SHARED_FOLDER, type: \"nfs\", nfs_version: 4, nfs_udp: false       # --- rsa \u043a\u043b\u044e\u0447\u0438       box.vm.provision \"file\", source: \"#{ENV['HOME']}\/.ssh\/id_rsa.pub\", destination: \"\/tmp\/authorized_keys\"       box.vm.provision \"shell\", inline: &lt;&lt;-SHELL       # \u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u0434\u0438\u0441\u0442\u0440\u0438\u0431\u0443\u0442\u0438\u0432       OS_NAME=$(grep -Eoi 'ubuntu|debian|centos|rhel' \/etc\/os-release | head -1)        # \ud83d\udccc \u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0441\u0438\u0441\u0442\u0435\u043c\u044b       if [[ \"$OS_NAME\" == \"Ubuntu\" ]] || [[ \"$OS_NAME\" == \"Debian\" ]]; then         sudo apt-get update           # sudo apt-get upgrade -y        elif [[ \"$OS_NAME\" == \"CentOS\" ]] || [[ \"$OS_NAME\" == \"rhel\" ]]; then         OS_VERSION=$(grep -oP '(?&lt;=VERSION_ID=\")[0-9]+' \/etc\/os-release)          # \ud83d\udd04 \u041f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0435\u0432 \u043d\u0430 \u0430\u0440\u0445\u0438\u0432\u043d\u044b\u0435 \u0434\u043b\u044f CentOS 7 \u0438 8         if [[ \"$OS_VERSION\" == \"7\" ]] || [[ \"$OS_VERSION\" == \"8\" ]]; then           echo \"\ud83d\udd04 \u041f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0430\u044e CentOS $OS_VERSION \u043d\u0430 \u0430\u0440\u0445\u0438\u0432\u043d\u044b\u0439 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439...\"           sudo sed -i 's|^mirrorlist=.*|#mirrorlist removed|' \/etc\/yum.repos.d\/CentOS-Base.repo           sudo sed -i 's|^#baseurl=http:\/\/mirror.centos.org|baseurl=http:\/\/vault.centos.org|' \/etc\/yum.repos.d\/CentOS-Base.repo         fi          # \u2705 \u041e\u0447\u0438\u0441\u0442\u043a\u0430 \u043a\u044d\u0448\u0430 \u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u0430\u043a\u0435\u0442\u043e\u0432         sudo yum clean all         sudo yum makecache         # sudo yum update -y       fi        # \ud83d\udccc RSA \u043a\u043b\u044e\u0447\u0438       if [ -f \/tmp\/authorized_keys ]; then         cat \/tmp\/authorized_keys &gt;&gt; \/home\/vagrant\/.ssh\/authorized_keys       fi        # \ud83d\udccc \u0422\u0430\u0439\u043c\u0437\u043e\u043d\u0430       sudo timedatectl set-timezone Europe\/Moscow        # \ud83d\udccc Ansible \u0444\u0430\u043a\u0442       sudo mkdir -p \/etc\/ansible\/facts.d       echo '{ \"host_role\": \"#{boxconfig[:host_role]}\" }' | sudo tee \/etc\/ansible\/facts.d\/custom.fact &gt; \/dev\/null       sudo chmod 0644 \/etc\/ansible\/facts.d\/custom.fact        echo \"\u2705 \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0430\"       SHELL        if !boxconfig[:prov].nil?         box.vm.provision \"shell\", path: boxconfig[:prov]       end     end   end end <\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<h3>2. \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0443\u0437\u043b\u043e\u0432 \u0432 \u0440\u0435\u0436\u0438\u043c\u0435 Ansible-pull, \u0432\u044b\u0434\u0430\u0447\u0430 \u0440\u043e\u043b\u0435\u0439 \u0438 RSA-\u043a\u043b\u044e\u0447\u0435\u0439 <\/h3>\n<p>Ansible playbook \u0434\u043b\u044f <strong>pull-\u0440\u0435\u0436\u0438\u043c\u0430<\/strong> \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0442\u044c \u0440\u043e\u043b\u0438 \u043a \u0443\u0437\u043b\u0430\u043c, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f <strong>\u0437\u0430\u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0435 \u0444\u0430\u043a\u0442\u044b<\/strong>.<\/p>\n<p><strong>\u0424\u0430\u0439\u043b \u043f\u043b\u0435\u0439\u0431\u0443\u043a\u0430<\/strong>:    <\/p>\n<details class=\"spoiler\">\n<summary>\u0421\u043a\u0440\u044b\u0442\u044b\u0439 \u0442\u0435\u043a\u0441\u0442<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"yaml\"># ansible\/ansible\/infra_ansible_pull.yaml - name: Apply roles dynamically from inventory   hosts: \"{{ target_hosts | default('localhost') }}\"   gather_facts: true    tasks:     - name: Explicitly gather custom facts       ansible.builtin.setup:         filter: ansible_local      - name: Include web role       ansible.builtin.include_role:         name: web       when: ansible_local.custom.host_role is defined and ansible_local.custom.host_role == \"webserver\"      - name: Include app role       ansible.builtin.include_role:         name: app       when: ansible_local.custom.host_role is defined and ansible_local.custom.host_role == \"application\"      - name: Include client role       ansible.builtin.include_role:         name: client       when: ansible_local.custom.host_role is defined and ansible_local.custom.host_role == \"client\" <\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0427\u0442\u043e\u0431\u044b \u0443\u0437\u043b\u044b <strong>\u043f\u043e\u043d\u0438\u043c\u0430\u043b\u0438<\/strong>, \u043a\u0430\u043a\u0438\u0435 \u0440\u043e\u043b\u0438 \u0438\u043c \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0442\u044c, \u0441\u043e\u0437\u0434\u0430\u0451\u043c <strong>\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0435 \u0444\u0430\u043a\u0442\u044b Ansible<\/strong>.   <\/p>\n<p>\u0412\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u043e\u043d\u0438 \u0431\u0443\u0434\u0443\u0442 \u0442\u0430\u043a:    <\/p>\n<details class=\"spoiler\">\n<summary>\u0421\u043a\u0440\u044b\u0442\u044b\u0439 \u0442\u0435\u043a\u0441\u0442<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"json\"># \/etc\/ansible\/facts.d\/custom.fact {   \"host_role\": \"{{ host_role }}\" }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p><strong>\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044e<\/strong>:<\/p>\n<ul>\n<li>\n<p>\u0413\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u043c \u0435\u0434\u0438\u043d\u0443\u044e \u043f\u0430\u0440\u0443 <strong>RSA-\u043a\u043b\u044e\u0447\u0435\u0439<\/strong>.<\/p>\n<\/li>\n<li>\n<p>\u041f\u0440\u0438\u0432\u0430\u0442\u043d\u044b\u0439 \u043a\u043b\u044e\u0447 <strong>\u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u043c<\/strong> \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u0438\u0435 \u0443\u0437\u043b\u044b.    <\/p>\n<\/li>\n<li>\n<p>\u041f\u0443\u0431\u043b\u0438\u0447\u043d\u044b\u0439 \u043a\u043b\u044e\u0447 <strong>\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u043c \u0432 GitLab<\/strong> \u0434\u043b\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044e.<\/p>\n<\/li>\n<\/ul>\n<details class=\"spoiler\">\n<summary>\u0421\u043a\u0440\u044b\u0442\u044b\u0439 \u0442\u0435\u043a\u0441\u0442<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"bash\">ssh-keygen -t rsa -C \"id_ansible_pull\" -f ~\/.ssh\/id_ansible_pull cat ~\/.ssh\/id_ansible_pull.pub<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p><strong>\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0438\u043d\u0432\u0435\u043d\u0442\u0430\u0440\u044f \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u0438\u0445 \u0443\u0437\u043b\u043e\u0432<\/strong><\/p>\n<p>\u041d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c <strong>\u0444\u0430\u0439\u043b \u0438\u043d\u0432\u0435\u043d\u0442\u0430\u0440\u044f<\/strong>, \u0433\u0434\u0435 \u0431\u0443\u0434\u0443\u0442 \u043f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u0435\u043d\u044b:<\/p>\n<ul>\n<li>\n<p>\u0441\u043f\u0438\u0441\u043e\u043a \u0443\u0437\u043b\u043e\u0432<\/p>\n<\/li>\n<li>\n<p>\u043b\u043e\u0433\u0438\u043d\u044b, \u043f\u0430\u0440\u043e\u043b\u0438. \u041e\u043d\u0438 \u043f\u0440\u0438\u0435\u043c\u043b\u0435\u043c\u044b \u0434\u043b\u044f \u0441\u0442\u0435\u043d\u0434\u043e\u0432\u043e\u0433\u043e \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f, \u043e\u0434\u043d\u0430\u043a\u043e \u0432 \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u043e\u0432\u043e\u0439 \u0441\u0440\u0435\u0434\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c <strong>\u0442\u043e\u043b\u044c\u043a\u043e RSA-\u043a\u043b\u044e\u0447\u0438!<\/strong><\/p>\n<p>\u0422\u0430\u043a\u0436\u0435 \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u044e \u0437\u0430\u0448\u0438\u0444\u0440\u043e\u0432\u0430\u0442\u044c \u044d\u0442\u043e\u0442 \u0444\u0430\u0439\u043b \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <strong>Ansible Vault.<\/strong><\/p>\n<\/li>\n<\/ul>\n<details class=\"spoiler\">\n<summary>\u0421\u043a\u0440\u044b\u0442\u044b\u0439 \u0442\u0435\u043a\u0441\u0442<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"yaml\"># ansible\\node_setting_to_pull\\inventory.yml all:   hosts:     webserver:       ansible_host: 192.168.2.221       ansible_user: vagrant       ansible_password: vagrant       ansible_become_pass: vagrant       host_role: webserver      application:       ansible_host: 192.168.2.222       ansible_user: vagrant       ansible_password: vagrant       ansible_become_pass: vagrant       host_role: application      client_1:       ansible_host: 192.168.2.223       ansible_user: vagrant       ansible_password: vagrant       ansible_become_pass: vagrant       host_role: client      client_2:       ansible_host: 192.168.2.224       ansible_user: vagrant       ansible_password: vagrant       ansible_become_pass: vagrant       host_role: client<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0427\u0442\u043e\u0431\u044b Ansible \u043c\u043e\u0433 <strong>\u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0447\u0435\u0440\u0435\u0437 \u043f\u0430\u0440\u043e\u043b\u044c<\/strong>, \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u043f\u0430\u043a\u0435\u0442 <strong>sshpass<\/strong> \u043d\u0430 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u044e\u0449\u0438\u0439 \u0443\u0437\u0435\u043b.<\/p>\n<p><strong>\u0417\u0430\u043f\u0443\u0441\u043a \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0443\u0437\u043b\u043e\u0432 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 Ansible-pull<\/strong>   <\/p>\n<p>\u0424\u0430\u0439\u043b <strong>\u043f\u043b\u0435\u0439\u0431\u0443\u043a\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0443\u0437\u043b\u043e\u0432 <\/strong>\u0441\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d \u043c\u043d\u043e\u044e \u0442\u0430\u043a, \u0447\u0442\u043e\u0431\u044b \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u043c\u0438 \u0434\u0438\u0441\u0442\u0440\u0438\u0431\u0443\u0442\u0438\u0432\u0430\u043c\u0438, \u0447\u0442\u043e \u0434\u043e\u0441\u0442\u0438\u0433\u0430\u0435\u0442\u0441\u044f \u0443\u0441\u043b\u043e\u0432\u0438\u044f\u043c\u0438  <code>when: ansible_os_family == \"Debian\"<\/code> \u0438  <code>when: ansible_os_family == \"RedHat\"<\/code><\/p>\n<h4>\u041e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438:<\/h4>\n<p>\u2705 \u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f Ansible \u2013 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f ansible \u0441 root-\u0434\u043e\u0441\u0442\u0443\u043f\u043e\u043c \u0431\u0435\u0437 \u043f\u0430\u0440\u043e\u043b\u044f.<br \/> \u2705 \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0445 \u043f\u0430\u043a\u0435\u0442\u043e\u0432 \u2013 \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442 Ansible \u0438 Git.<br \/> \u2705 \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 SSH-\u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a Git-\u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044e \u2013 \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u0442 \u043a\u043b\u044e\u0447\u0438 \u0438 Known Hosts \u0434\u043b\u044f \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0433\u043e \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f.<br \/> \u2705 \u0414\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435 cron-\u0437\u0430\u0434\u0430\u043d\u0438\u044f \u2013 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442 ansible-pull \u0434\u0432\u0430\u0436\u0434\u044b \u0432 \u0447\u0430\u0441 \u0434\u043b\u044f \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u0438 \u0441 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0435\u043c.<br \/> \u2705 \u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0444\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0439 \u0440\u043e\u043b\u0438 \u0445\u043e\u0441\u0442\u0430 \u2013 \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442 host_role \u0432 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0435 facts.<br \/> \u2705 \u0422\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u2013 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 ansible-pull \u0438 cron-\u0437\u0430\u0434\u0430\u0447\u0438.<\/p>\n<details class=\"spoiler\">\n<summary>\u0421\u043a\u0440\u044b\u0442\u044b\u0439 \u0442\u0435\u043a\u0441\u0442<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"yaml\"># ansible\\node_setting_to_pull\\setup_ansible_pull.yml --- - name: Setup ansible-pull on Debian, Ubuntu, and CentOS   hosts: all   vars:     run_ansible_user: \"ansible\"     git_playbook_path: \"ansible\/infra_ansible_pull.yml\"       git_branch: \"release\" # \u0432\u0435\u0442\u043a\u0430 \u0441 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 ansible \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u0434\u0442\u044f\u0433\u0438\u0432\u0430\u0442\u044c \u043a\u043e\u0434, \u043e\u0431\u044b\u0447\u043d\u043e \"release\"     ssh_key_src: \"~\/.ssh\/id_ansible_pull\"      ssh_key_dest: \"\/home\/{{ run_ansible_user }}\/.ssh\/id_ansible_pull\"      git_server: \"gitlab.ch.ap\"     git_server_ip: \"192.168.2.34\"     git_repo: \"git@gitlab.ch.ap:AndreyChuyan\/ansible_pull_cicd.git\"   tasks:      ### \ud83d\udd39 \u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f ansible     - block:         - name: Ensure ansible user exists (Debian-based)           user:             name: \"{{ run_ansible_user }}\"             shell: \/bin\/bash             create_home: yes             groups: sudo             append: yes           become: yes           when: ansible_facts['os_family'] == \"Debian\"          - name: Ensure ansible user exists (RHEL-based)           user:             name: \"{{ run_ansible_user }}\"             shell: \/bin\/bash             create_home: yes             groups: wheel             append: yes           become: yes           when: ansible_facts['os_family'] == \"RedHat\"          - name: Allow ansible user to run ansible-pull without password           copy:             dest: \"\/etc\/sudoers.d\/ansible\"             content: \"{{ run_ansible_user }} ALL=(ALL) NOPASSWD: ALL\"             mode: '0440'           become: yes       tags: user_setup      ### \ud83d\udd39 \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u043f\u0430\u043a\u0435\u0442\u043e\u0432 (Ansible \u0438 Git)     - block:         - name: Install Ansible and Git on Debian\/Ubuntu           apt:             name:                - ansible               - git             state: present             update_cache: yes           become: yes           when: ansible_os_family == \"Debian\"          - name: Install Ansible and Git on CentOS\/RedHat           package:             name:                - epel-release               - ansible               - git             state: present           become: yes           when: ansible_os_family == \"RedHat\"      # \ud83d\udd39 \u0423\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u043a\u043e\u043b\u043b\u0435\u043a\u0446\u0438\u044e ansible.posix, \u0435\u0441\u043b\u0438 \u043e\u043d\u0430 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442         # - name: Ensure ansible.posix collection is installed         #   ansible.builtin.command:         #     cmd: ansible-galaxy collection install ansible.posix --ignore-errors         #   changed_when: false       tags: ansible_install      ### \ud83d\udd39 \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 SSH-\u043a\u043b\u044e\u0447\u0435\u0439 \u0434\u043b\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a Git     - block:         - name: Ensure GitLab server IP is in \/etc\/hosts           lineinfile:             path: \/etc\/hosts             line: \"{{ git_server_ip }} {{ git_server }}\"             regexp: \".*\\\\s+{{ git_server }}$\"             state: present           become: yes                  - name: Ensure SSH directory exists for ansible user           file:             path: \"\/home\/{{ run_ansible_user }}\/.ssh\"             state: directory             mode: '0700'             owner: \"{{ run_ansible_user }}\"             group: \"{{ run_ansible_user }}\"           become: yes          - name: Copy private SSH key for ansible user           copy:             src: \"{{ ssh_key_src }}\"             dest: \"{{ ssh_key_dest }}\"             owner: \"{{ run_ansible_user }}\"             group: \"{{ run_ansible_user }}\"             mode: '0600'           no_log: true           become: yes          - name: Add Git server fingerprint to known_hosts           known_hosts:             path: \"\/home\/{{ run_ansible_user }}\/.ssh\/known_hosts\"             name: \"{{ git_server }}\"             key: \"{{ lookup('pipe', 'ssh-keyscan -H ' + git_server) }}\"           become: yes           # become_user: \"{{ run_ansible_user }}\"       tags: ssh_setup       ### \ud83d\udd39 \u0414\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435 Cron-\u0437\u0430\u0434\u0430\u0447\u0438 \u0434\u043b\u044f ansible-pull     - block:         - name: Calculate random cron minute per host           set_fact:             random_minute_offset: \"{{ (inventory_hostname | hash('md5') | int(base=16) % 30) | int }}\"          - name: Add ansible-pull cron job           cron:             name: \"Run ansible-pull\"             minute: \"{{ (random_minute_offset | int + 0) % 60 }},{{ (random_minute_offset | int + 30) % 60 }}\"             job: &gt;               \/usr\/bin\/ansible-pull -U {{ git_repo }}               -C {{ git_branch }}               --private-key {{ ssh_key_dest }}               {{ git_playbook_path }}               | tee \/var\/log\/ansible-pull.log             user: \"{{ run_ansible_user }}\"           become: yes          - name: Ensure ansible-pull log file exists           file:             path: \/var\/log\/ansible-pull.log             state: touch             owner: \"{{ run_ansible_user }}\"             group: \"{{ run_ansible_user }}\"             mode: '0644'           become: yes       tags: cron_setup      ### \ud83d\udd39 \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0440\u043e\u043b\u0435\u0439 \u0434\u043b\u044f \u0445\u043e\u0441\u0442\u043e\u0432     - block:         - name: Create directoryfor custom fact           file:             path: \/etc\/ansible\/facts.d             state: directory             mode: '0755'           become: true          - name: Create JSON-file with fact \"host_role\"           copy:             dest: \/etc\/ansible\/facts.d\/custom.fact             content: |               {                 \"host_role\": \"{{ host_role }}\"               }             mode: '0644'           become: true       tags: host_role       ### \ud83d\udd39 \u0422\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435     - block:         - name: Check ansible-pull process           shell: \"pgrep -fa ansible-pull || true\"           register: ansible_pull_status           changed_when: false          - name: Show running ansible-pull status           debug:             msg: \"\ud83d\udccc [INFO] - Ansible-pull running: {{ansible_pull_status.stdout_lines }}\"           when: ansible_pull_status.stdout | length &gt; 0          # cronjob         - name: Check ansible-pull cron job           shell: \"crontab -l -u {{ run_ansible_user }} | grep ansible-pull || true\"           register: cron_job_status           changed_when: false           failed_when: false  # \u0418\u0437\u0431\u0435\u0436\u0430\u0442\u044c \u043e\u0448\u0438\u0431\u043a\u0438, \u0435\u0441\u043b\u0438 crontab \u043f\u0443\u0441\u0442\u043e\u0439           become: true          - name: Show cron job status           debug:             msg: \"\ud83d\udccc [INFO] - Ansible-pull cron job: {{ cron_job_status.stdout_lines }}\"           when: cron_job_status.stdout | length &gt; 0          # \u041f\u0440\u0438\u043d\u0443\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0444\u0430\u043a\u0442\u043e\u0432 (\u0447\u0442\u043e\u0431\u044b \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u043b\u0441\u044f \u043d\u043e\u0432\u044b\u0439 \u0444\u0430\u0439\u043b)         - name: Update ansible_facts           ansible.builtin.setup:             filter: ansible_local          # \u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u043d\u0430\u043b\u0438\u0447\u0438\u044f \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u043e\u0433\u043e \u0444\u0430\u043a\u0442\u0430 \"host_role\"         - name: Show custom fact           ansible.builtin.debug:             msg: \"\u0420\u043e\u043b\u044c \u0445\u043e\u0441\u0442\u0430 - host_role: {{ ansible_local.custom.host_role }}\"       tags: test <\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u041e\u0431\u0440\u0430\u0449\u0430\u044e \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043d\u0430 \u043b\u044e\u0431\u043e\u043f\u044b\u0442\u043d\u044b\u0439 \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0440\u0430\u043d\u0434\u043e\u043c\u043d\u043e\u0433\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0432 \u0437\u0430\u0434\u0430\u0447\u0430\u0445 <strong>Calculate random cron minute per host <\/strong>\u0438<strong> Add ansible-pull cron job.<\/strong> \u0413\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u043e\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043b\u044f\u0435\u0442\u0441\u044f <strong>\u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u0445\u0435\u0448-\u0441\u0443\u043c\u043c\u044b \u0438\u043c\u0435\u043d\u0438 \u0445\u043e\u0441\u0442\u0430<\/strong>, \u0447\u0442\u043e \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u0435\u0442 <strong>\u0438\u0434\u0435\u043c\u043f\u043e\u0442\u0435\u043d\u0442\u043d\u043e\u0441\u0442\u044c <\/strong>\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439. \u041a\u0430\u0436\u0434\u044b\u0439 \u0440\u0430\u0437 \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u043e\u0435 \u0432\u0440\u0435\u043c\u044f \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0443\u0437\u043b\u0430 \u0431\u0443\u0434\u0435\u0442 \u0438\u043c\u0435\u0442\u044c \u043e\u0434\u043d\u043e \u0438 \u0442\u043e \u0436\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435. \u041b\u044e\u0431\u043b\u044e \u0442\u0430\u043a\u0438\u0435 \u044d\u043b\u0435\u0433\u0430\u043d\u0442\u043d\u044b\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u044f.<\/p>\n<p>\u0412 \u0438\u0442\u043e\u0433\u0435 \u0443 \u043d\u0430\u0441 \u0434\u043e\u043b\u0436\u043d\u0430 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u0441\u044f<strong> \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430:<\/strong><\/p>\n<p>.<br \/> \u251c\u2500\u2500 inventory.yml<br \/> \u2514\u2500\u2500 setup_ansible_pull.yml<\/p>\n<p><strong>\u0418\u0433\u043d\u043e\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0444\u0438\u043d\u0433\u0435\u0440\u043f\u0440\u0438\u043d\u0442\u043e\u0432 SSH<\/strong> <\/p>\n<ul>\n<li>\n<p>\u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 <strong>StrictHostKeyChecking=no<\/strong> \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f.  <\/p>\n<\/li>\n<li>\n<p>\u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u043f\u043b\u0435\u0439\u0431\u0443\u043a \u0434\u043b\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0443\u0437\u043b\u043e\u0432.<\/p>\n<\/li>\n<\/ul>\n<details class=\"spoiler\">\n<summary>\u0421\u043a\u0440\u044b\u0442\u044b\u0439 \u0442\u0435\u043a\u0441\u0442<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"bash\">ansible-playbook -i inventory.yml setup_ansible_pull.yml<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0415\u0441\u043b\u0438 \u0442\u0435\u0441\u0442\u044b \u043f\u043b\u0435\u0439\u0431\u0443\u043a\u0430 \u043f\u0440\u043e\u0448\u043b\u0438 \u0443\u0441\u043f\u0435\u0448\u043d\u043e, \u043c\u044b \u0443\u0432\u0438\u0434\u0438\u043c \u0442\u0430\u043a\u0443\u044e \u043a\u0430\u0440\u0442\u0438\u043d\u0443.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/963\/247\/28a\/96324728a246c6b1a971b981ee228b9a.jpg\" alt=\"\u0423\u0431\u0435\u0436\u0434\u0430\u0435\u043c\u0441\u044f \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u0441\u043e\u0437\u0434\u0430\u043d\u044b cron \u0437\u0430\u0434\u0430\u0447\u0438\" title=\"\u0423\u0431\u0435\u0436\u0434\u0430\u0435\u043c\u0441\u044f \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u0441\u043e\u0437\u0434\u0430\u043d\u044b cron \u0437\u0430\u0434\u0430\u0447\u0438\" width=\"1100\" height=\"477\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/963\/247\/28a\/96324728a246c6b1a971b981ee228b9a.jpg\" data-blurred=\"true\"\/><\/p>\n<div><figcaption>\u0423\u0431\u0435\u0436\u0434\u0430\u0435\u043c\u0441\u044f \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u0441\u043e\u0437\u0434\u0430\u043d\u044b cron \u0437\u0430\u0434\u0430\u0447\u0438<\/figcaption><\/div>\n<\/figure>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/19b\/ecb\/f41\/19becbf41703088ea6f3655f9a9394c0.jpg\" alt=\"\u0423\u0431\u0435\u0436\u0434\u0430\u0435\u043c\u0441\u044f \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u0445\u043e\u0441\u0442\u0430\u043c \u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u044b \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0435 \u0440\u043e\u043b\u0438\" title=\"\u0423\u0431\u0435\u0436\u0434\u0430\u0435\u043c\u0441\u044f \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u0445\u043e\u0441\u0442\u0430\u043c \u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u044b \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0435 \u0440\u043e\u043b\u0438\" width=\"1084\" height=\"315\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/19b\/ecb\/f41\/19becbf41703088ea6f3655f9a9394c0.jpg\" data-blurred=\"true\"\/><\/p>\n<div><figcaption>\u0423\u0431\u0435\u0436\u0434\u0430\u0435\u043c\u0441\u044f \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u0445\u043e\u0441\u0442\u0430\u043c \u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u044b \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0435 \u0440\u043e\u043b\u0438<\/figcaption><\/div>\n<\/figure>\n<h3>3. \u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0441\u0440\u0435\u0434\u044b \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0430 \u043a\u043e\u0434\u0430 \u0432 Docker-\u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0435<\/h3>\n<p><strong>\u0412\u044b\u0431\u043e\u0440 \u0442\u0435\u0441\u0442\u043e\u0432\u043e\u0439 \u0441\u0440\u0435\u0434\u044b<\/strong><\/p>\n<ul>\n<li>\n<p>\u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 <strong>\u0441\u0440\u0435\u0434\u044b \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f<\/strong> \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440 <strong>Docker<\/strong>.  <\/p>\n<\/li>\n<li>\n<p>\u041a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440 \u0431\u0443\u0434\u0435\u0442 <strong>\u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Docker Compose<\/strong>.  <\/p>\n<\/li>\n<li>\n<p>\u0422\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0431\u0443\u0434\u0435\u0442 \u043e\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043b\u044f\u0442\u044c\u0441\u044f \u0441 \u043f\u043e\u0434\u043e\u0448\u044c\u044e <strong>Ansible-lint.<\/strong><\/p>\n<\/li>\n<\/ul>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/a91\/53a\/f3d\/a9153af3dba3867a14a04d5590d41984.jpg\" width=\"827\" height=\"619\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/a91\/53a\/f3d\/a9153af3dba3867a14a04d5590d41984.jpg\" data-blurred=\"true\"\/><\/figure>\n<p>\u0424\u0430\u0439\u043b <strong>Docker-Compose:<\/strong><\/p>\n<details class=\"spoiler\">\n<summary>\u0421\u043a\u0440\u044b\u0442\u044b\u0439 \u0442\u0435\u043a\u0441\u0442<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"yaml\"># docker-compose_control.yml version: '3.9'  services:   ansible_control:     build:       context: .\/docker       dockerfile: Dockerfile.control       args:         DEBIAN_VERSION: ${DEBIAN_VERSION}          HTTP_PROXY: ${HTTP_PROXY}         HTTPS_PROXY: ${HTTP_PROXY}         NO_PROXY: ${NO_PROXY}     environment:       HTTP_PROXY: ${HTTP_PROXY}       HTTPS_PROXY: ${HTTP_PROXY}       NO_PROXY: ${NO_PROXY}     container_name: ansible_control     hostname: ansible_control     networks:       test_network:         ipv4_address: 192.168.100.200     volumes:       - .\/ansible:\/ansible       - \/var\/run\/docker.sock:\/var\/run\/docker.sock       - \/usr\/bin\/docker:\/usr\/bin\/docker     tty: true     stdin_open: true     restart: unless-stopped  networks:   test_network:     driver: bridge     ipam:       config:         - subnet: 192.168.100.0\/24<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0414\u043b\u044f \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u043f\u0430\u043a\u0435\u0442\u043e\u0432 \u0438 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439, \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0445 \u0432 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0435, \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c <strong>Dockerfile<\/strong>:<\/p>\n<details class=\"spoiler\">\n<summary>\u0421\u043a\u0440\u044b\u0442\u044b\u0439 \u0442\u0435\u043a\u0441\u0442<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"bash\"># docker\\Dockerfile.control ARG DEBIAN_VERSION FROM debian:12  # \u0417\u0430\u0434\u0430\u0435\u043c \u0443\u0432\u0435\u043b\u0438\u0447\u0435\u043d\u043d\u044b\u0439 \u0442\u0430\u0439\u043c\u0430\u0443\u0442 \u0438 \u0447\u0438\u0441\u043b\u043e \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u044b\u0445 \u043f\u043e\u043f\u044b\u0442\u043e\u043a \u0434\u043b\u044f apt-get RUN echo 'Acquire::http::Timeout \"600\";' &gt;&gt; \/etc\/apt\/apt.conf.d\/99timeout &amp;&amp; \\     echo 'Acquire::Retries \"5\";' &gt;&gt; \/etc\/apt\/apt.conf.d\/99timeout  # \u0423\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c Ansible, ansible-lint \u0438 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 RUN apt-get update &amp;&amp; apt-get install -y \\     ansible \\     ansible-lint \\     &amp;&amp; apt-get clean \\     &amp;&amp; rm -rf \/var\/lib\/apt\/lists\/*  # \u0421\u043e\u0437\u0434\u0430\u0435\u043c ansible-\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0441 sudo \u0431\u0435\u0437 \u043f\u0430\u0440\u043e\u043b\u044f RUN useradd -m ansible \\     &amp;&amp; echo \"ansible ALL=(ALL) NOPASSWD:ALL\" &gt; \/etc\/sudoers.d\/ansible \\     &amp;&amp; chmod 0440 \/etc\/sudoers.d\/ansible  # \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0443 RUN ansible --version &amp;&amp; ansible-lint --version  # \u0423\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u0440\u0430\u0431\u043e\u0447\u0443\u044e \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044e WORKDIR \/ansible  CMD [\"\/bin\/bash\"]<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u041e\u0442\u043c\u0435\u0447\u0443, \u0447\u0442\u043e \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442 <strong>Molecule<\/strong> \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0440\u043e\u043b\u0435\u0439 Ansible, \u043d\u043e <strong>\u044f \u0440\u0435\u0434\u043a\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e \u044d\u0442\u043e\u0442 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442<\/strong>. \u041f\u0440\u0438\u0447\u0438\u043d\u0430 \u2014 <strong>\u0438\u0437\u0431\u044b\u0442\u043e\u0447\u043d\u0430\u044f \u0441\u043b\u043e\u0436\u043d\u043e\u0441\u0442\u044c<\/strong> \u043f\u0440\u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438 <strong>\u0446\u0435\u043b\u043e\u0433\u043e \u043f\u043b\u0435\u0439\u0431\u0443\u043a\u0430<\/strong>.  <\/p>\n<h2>4. \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 CI\/CD-\u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d\u0430 \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430 \u0440\u0430\u0437\u0432\u0435\u0440\u0442\u044b\u0432\u0430\u043d\u0438\u044f \u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f<\/h2>\n<p>\u041f\u0440\u043e\u0446\u0435\u0441\u0441 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043a\u043e\u0434\u043e\u043c \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u043e\u0432\u0430\u043d \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:  <\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/7a6\/ca8\/274\/7a6ca827488c51d683caa2842f9ecc7d.jpg\" width=\"839\" height=\"685\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/7a6\/ca8\/274\/7a6ca827488c51d683caa2842f9ecc7d.jpg\" data-blurred=\"true\"\/><\/figure>\n<h3>\u041f\u0440\u043e\u0446\u0435\u0441\u0441 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438 \u0432\u044b\u043f\u0443\u0441\u043a\u0430 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430<\/h3>\n<h4>\ud83d\udd39 \u0420\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0430<\/h4>\n<p>\u0420\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0438 \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0442 \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0445 \u0432\u0435\u0442\u043a\u0430\u0445, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440:<\/p>\n<ul>\n<li>\n<p>app \u2013 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439<\/p>\n<\/li>\n<li>\n<p>web \u2013 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0432\u0435\u0431-\u0441\u0435\u0440\u0432\u0435\u0440\u043e\u0432<\/p>\n<\/li>\n<li>\n<p>client \u2013 \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u0438\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b<\/p>\n<\/li>\n<\/ul>\n<h4>\ud83d\udd39 \u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u043a\u043e\u0434\u0430<\/h4>\n<p>\u041f\u0440\u0438 \u043a\u0430\u0436\u0434\u043e\u043c \u043a\u043e\u043c\u043c\u0438\u0442\u0435 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u044e\u0442\u0441\u044f:<\/p>\n<p>\u2705 \u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0441\u0438\u043d\u0442\u0430\u043a\u0441\u0438\u0441\u0430 \u2013 \u0430\u043d\u0430\u043b\u0438\u0437 \u043a\u043e\u0434\u0430 \u043d\u0430 \u043e\u0448\u0438\u0431\u043a\u0438<br \/>\u2705 \u0422\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0432 \u0442\u0435\u0441\u0442\u043e\u0432\u043e\u0439 \u0441\u0440\u0435\u0434\u0435 \u2013 \u0440\u0430\u0437\u0432\u0435\u0440\u0442\u044b\u0432\u0430\u043d\u0438\u0435 \u0438 \u043e\u0446\u0435\u043d\u043a\u0430 \u0440\u0430\u0431\u043e\u0442\u043e\u0441\u043f\u043e\u0441\u043e\u0431\u043d\u043e\u0441\u0442\u0438<\/p>\n<h4>\ud83d\udd39 \u041e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439<\/h4>\n<p>\u041f\u0435\u0440\u0435\u0434 \u0444\u0438\u043d\u0430\u043b\u044c\u043d\u043e\u0439 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0435\u0439 \u0438\u0442\u043e\u0433\u043e\u0432\u044b\u0439 \u043f\u043b\u0435\u0439\u0431\u0443\u043a \u0441 \u0440\u043e\u043b\u044f\u043c\u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u043e, \u0447\u0442\u043e\u0431\u044b \u0438\u0441\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442\u044b \u0438 \u043e\u0448\u0438\u0431\u043a\u0438<\/p>\n<h4>\ud83d\udd39 \u0412\u044b\u043f\u0443\u0441\u043a \u0440\u0435\u043b\u0438\u0437\u0430<\/h4>\n<p>\u0415\u0441\u043b\u0438 \u0432\u0441\u0435 \u0442\u0435\u0441\u0442\u044b \u043f\u0440\u043e\u0439\u0434\u0435\u043d\u044b \u0443\u0441\u043f\u0435\u0448\u043d\u043e:<\/p>\n<p>\u2705 \u041f\u043b\u0435\u0439\u0431\u0443\u043a \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432 \u0432\u0435\u0442\u043a\u0443 release<br \/>\u2705 \u0418\u0437 \u0432\u0435\u0442\u043a\u0438 release \u043f\u043b\u0435\u0439\u0431\u0443\u043a \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d \u043a\u043b\u0438\u0435\u043d\u0442\u0430\u043c \u0447\u0435\u0440\u0435\u0437 ansible-pull<\/p>\n<p>\u0414\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u043d\u0430\u0448\u0435\u0433\u043e \u043a\u043e\u043d\u0432\u0435\u0439\u0435\u0440\u0430 \u043c\u044b \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c <strong>gitlab-ci<\/strong> \u0444\u0430\u0439\u043b, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0442\u043e\u043a\u0435\u043d \u0434\u043b\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044e, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u044b \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u0432 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 GitLab \u043f\u043e\u0434 \u0438\u043c\u0435\u043d\u0435\u043c <strong>RUNNER_TOKEN<\/strong><\/p>\n<p><strong>\u0420\u0430\u0437\u0431\u0435\u0440\u0435\u043c \u0447\u0442\u043e \u0434\u0435\u043b\u0430\u0435\u0442 \u043a\u0430\u0436\u0434\u044b\u0439 job <\/strong><\/p>\n<p>\ud83d\udca0 check-ansible-syntax<\/p>\n<ul>\n<li>\n<p>\u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440 ansible_control \u0447\u0435\u0440\u0435\u0437 docker-compose.<\/p>\n<\/li>\n<li>\n<p>\u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 \u0441\u0438\u043d\u0442\u0430\u043a\u0441\u0438\u0441 infra_ansible_pull.yml.<\/p>\n<\/li>\n<li>\n<p>\u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442 ansible-lint, \u043e\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442 \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u043f\u0440\u0438 \u043a\u0440\u0438\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u043e\u0448\u0438\u0431\u043a\u0430\u0445.<\/p>\n<\/li>\n<\/ul>\n<p>\ud83d\udca0 test-deployment<\/p>\n<ul>\n<li>\n<p>\u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u0441 \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u043c\u0438 \u0441\u0435\u0440\u0432\u0435\u0440\u0430\u043c\u0438 (ansible all -m ping).<\/p>\n<\/li>\n<li>\n<p>\u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442 ansible-playbook \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u043e\u0432\u043e\u0433\u043e \u0440\u0430\u0437\u0432\u0435\u0440\u0442\u044b\u0432\u0430\u043d\u0438\u044f.<\/p>\n<\/li>\n<li>\n<p>\u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0443 Nginx \u0438 \u043e\u0442\u0434\u0430\u0447\u0443 \u0442\u0435\u0441\u0442\u043e\u0432\u043e\u0433\u043e \u0448\u0430\u0431\u043b\u043e\u043d\u0430 \u0447\u0435\u0440\u0435\u0437 curl.<\/p>\n<\/li>\n<\/ul>\n<p>\ud83d\udca0 deploy-ansible-role<\/p>\n<ul>\n<li>\n<p>\u0415\u0441\u043b\u0438 check \u0438 test \u043f\u0440\u043e\u0448\u043b\u0438 \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u2192 \u043f\u0443\u0448\u0438\u0442 \u043a\u043e\u0434 \u0432 \u0432\u0435\u0442\u043a\u0443 release.<\/p>\n<\/li>\n<\/ul>\n<details class=\"spoiler\">\n<summary>\u0421\u043a\u0440\u044b\u0442\u044b\u0439 \u0442\u0435\u043a\u0441\u0442<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"yaml\"># .gitlab-ci.yml variables:   CLIENT_IP: 192.168.2.213   CLIENT_SSH_PASSWORD: vagrant   APP_TEMPLATE: \"\u042d\u0442\u043e \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0439 Flask-\u0441\u0435\u0440\u0432\u0435\u0440\"   APP_IP: \"192.168.2.212\"   WEBSERVER_IP: \"192.168.2.211\"  stages:   - check   - test   - deploy   # \ud83d\udd39 \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u0441\u0438\u043d\u0442\u0430\u043a\u0441\u0438\u0441 \u0438 Ansible-lint \u043f\u0435\u0440\u0435\u0434 \u0442\u0435\u0441\u0442\u0430\u043c\u0438 check-ansible-syntax:   stage: check   tags:     - shell    script:     - echo \"\ud83d\udc33 Building Ansible control node...\"     # - docker-compose -f docker-compose_control.yml down --remove-orphans     # - docker-compose -f docker-compose_control.yml up -d --build --force-recreate     - docker-compose -f docker-compose_control.yml up -d      - echo \"\ud83d\udd01 Waiting for containers to be ready...\"     - until docker ps | grep \"ansible_control\"; do sleep 2; done      - echo \"\u23f3 Waiting for 5 seconds to ensure all services are up...\"     - sleep 5      - echo \"\ud83d\udccc Checking Ansible playbook syntax...\"     - |       docker exec -i ansible_control ansible-playbook infra_ansible_pull.yml --syntax-check       if [ $? -eq 0 ]; then         echo \"\u2705 \u0421\u0438\u043d\u0442\u0430\u043a\u0441\u0438\u0441 \u043f\u043b\u0435\u0439\u0431\u0443\u043a\u0430 infra_ansible_pull.yml \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u0435\u043d!\"       else         echo -e \"\\033[1;31m\u274c ERROR: \u041e\u0448\u0438\u0431\u043a\u0430 \u0432 \u0441\u0438\u043d\u0442\u0430\u043a\u0441\u0438\u0441\u0435 playbook!\\033[0m\"         exit 1   # \u041f\u0440\u0435\u0440\u044b\u0432\u0430\u0435\u043c job, \u0435\u0441\u043b\u0438 \u0435\u0441\u0442\u044c \u043e\u0448\u0438\u0431\u043a\u0438       fi       - echo \"\ud83d\udccc Linting Ansible playbook and roles...\"     - |       set -o pipefail # --- \u0432\u044b\u0432\u043e\u0434 ansible-lint ---       # \u0414\u043b\u044f \u043e\u0442\u043b\u0430\u0434\u043a\u0438 \u043c\u043e\u0436\u043d\u043e \u0432\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u043e\u0442\u043b\u0430\u0434\u043e\u0447\u043d\u044b\u0439 \u0440\u0435\u0436\u0438\u043c:       # set -x        lint_log=\"lint_output.txt\"        # \u0412\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 ansible-lint, \u0432\u044b\u0432\u043e\u0434 \u043b\u043e\u0433\u0438\u0440\u0443\u0435\u043c \u0432 \u0444\u0430\u0439\u043b.       if ! docker exec -i ansible_control ansible-lint infra_ansible_pull.yml | tee \"$lint_log\"; then         echo -e \"\\033[1;31m\u274c ERROR: ansible-lint \u0432\u044b\u044f\u0432\u0438\u043b \u043a\u0440\u0438\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u043e\u0448\u0438\u0431\u043a\u0438! \u0423\u0441\u0442\u0430\u0440\u0430\u043d\u0438\u0442\u0435 \u0438\u0445 \u0441\u043e\u0433\u043b\u0430\u0441\u043d\u043e \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0430\u0446\u0438\u044f\u043c \u0432\u044b\u0448\u0435!\\033[0m\"         exit 1       fi        echo \"\"  # \u0414\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u0430\u044f \u043f\u0443\u0441\u0442\u0430\u044f \u0441\u0442\u0440\u043e\u043a\u0430 \u0434\u043b\u044f \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e\u0433\u043e \u0441\u043a\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f grep        # \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u043d\u0430\u043b\u0438\u0447\u0438\u0435 \u043f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0436\u0434\u0435\u043d\u0438\u0439 \u0432 \u0432\u044b\u0432\u043e\u0434\u0435       if grep -q \"WARNING\" \"$lint_log\"; then         echo -e \"\\033[1;33m\u26a0\ufe0f  WARNING: \u041e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u044b \u043f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0436\u0434\u0435\u043d\u0438\u044f \u0432 ansible-lint!\\033[0m\"       else         echo -e \"\\033[1;32m\u2705 Ansible-lint \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u043f\u0440\u043e\u0439\u0434\u0435\u043d!\\033[0m\"       fi   except:     - release  # \u0418\u0441\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u0432 \u0432\u0435\u0442\u043a\u0435 release  # \ud83d\udd39 \u0420\u0430\u0437\u0432\u0435\u0440\u0442\u044b\u0432\u0430\u043d\u0438\u0435 Ansible playbook \u0432 \u0442\u0435\u0441\u0442\u043e\u0432\u043e\u043c \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u0438 test-deployment:   stage: test   before_script:     - echo \"\u267b\ufe0f \u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f...\"   tags:     - shell   needs:     - check-ansible-syntax  # \u2705 \u0416\u0434\u0435\u043c \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0433\u043e \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u044f \u0441\u0438\u043d\u0442\u0430\u043a\u0441-\u0447\u0435\u043a\u0430    script:     - echo \"\ud83d\udccc Running Ansible tests - checking connection...\"     - export ANSIBLE_HOST_KEY_CHECKING=False     - ansible all -i ansible\/inventory_test.yml -m ping --extra-vars 'ansible_ssh_extra_args=\"-o StrictHostKeyChecking=no -o UserKnownHostsFile=\/dev\/null\"'      - echo \"\ud83d\udccc Running Ansible playbook execution...\"     - ansible-playbook -i ansible\/inventory_test.yml ansible\/infra_ansible_pull.yml -e \"target_hosts=all ansible_ssh_extra_args='-o StrictHostKeyChecking=no -o UserKnownHostsFile=\/dev\/null'\" # \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u0432 \u0438\u043d\u0432\u0435\u043d\u0442\u0430\u0440\u0435 \u0446\u0435\u043b\u0435\u0432\u044b\u0435 \u0445\u043e\u0441\u0442\u044b      # \ud83d\udd39 \u0422\u0435\u0441\u0442\u044b     # \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e\u0441\u0442\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043d\u0430 \u0443\u0437\u043b\u0435     - echo \"\ud83e\uddea --- Run tests ---\"     # --- \u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e\u0441\u0442\u0438 Nginx     - echo \"\ud83d\udd0d --- Checking Nginx status\"     - |       NGINX_STATUS=$(sshpass -p \"$CLIENT_SSH_PASSWORD\" ssh -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=\/dev\/null \"vagrant@$WEBSERVER_IP\" \"systemctl is-active nginx\" 2&gt;\/dev\/null)       if [[ \"$NGINX_STATUS\" == \"active\" ]]; then           echo \"\u2705 Nginx service is running\"       else           echo \"\u274c Nginx service is NOT running\"           exit 1       fi      - echo \"\ud83d\udd0d --- Checking app content for client\"     - |         OUTPUT=$(sshpass -p \"$CLIENT_SSH_PASSWORD\" ssh -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=\/dev\/null \"vagrant@$CLIENT_IP\" \"echo \u0423\u0437\u0435\u043b:; hostname; echo \ud83c\udf0d HTTP-\u043e\u0442\u0432\u0435\u0442:; curl -s http:\/\/$WEBSERVER_IP\")         if echo \"$OUTPUT\" | grep -q \"\u0423\u0437\u0435\u043b:\" &amp;&amp; echo \"$OUTPUT\" | grep -q \"\ud83c\udf0d HTTP-\u043e\u0442\u0432\u0435\u0442:\" &amp;&amp; echo \"$OUTPUT\" | grep -q \"$APP_TEMPLATE\"; then             echo \"\u2705 \u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u0430! HTTP-\u043e\u0442\u0432\u0435\u0442 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0442\u0435\u043a\u0441\u0442: \"$APP_TEMPLATE\"\"             exit 0         else             echo \"\u274c \u041e\u0448\u0438\u0431\u043a\u0430! HTTP-\u043e\u0442\u0432\u0435\u0442 \u043d\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0439 \u0448\u0430\u0431\u043b\u043e\u043d! \u041e\u0442\u0432\u0435\u0442: \"$OUTPUT\"\"             exit 1         fi      - echo \"\u2705 All tests passed successfully!\"   except:     - release  # \u0418\u0441\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u0432 \u0432\u0435\u0442\u043a\u0435 release   # \ud83d\udd39 \u0414\u0435\u043f\u043b\u043e\u0439 deploy-ansible-role:   stage: deploy   tags:     - shell   needs:     - check-ansible-syntax  # \u2705 \u0416\u0434\u0435\u043c \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0433\u043e \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u044f \u0441\u0438\u043d\u0442\u0430\u043a\u0441-\u0447\u0435\u043a\u0430     - test-deployment     # \u2705 \u0416\u0434\u0435\u043c \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0433\u043e \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u043a\u043e\u0434\u0430 \u043d\u0430 \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0445 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0430\u0445    script:     - echo \"\ud83d\udc33 Building and starting test Docker containers...\"     # create $RUNNER_TOKEN - roles - developer - read_repository, write_repository. -&gt; Add variable to project     - git config --global user.email \"your-email@example.com\"     - git config --global user.name \"andreychuyan\"     - git push --force https:\/\/oauth2:$RUNNER_TOKEN@${CI_SERVER_HOST}\/${CI_PROJECT_PATH}.git  HEAD:refs\/heads\/release   only:     - main <\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<h2>5. \u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0440\u043e\u043b\u0435\u0439 \u0434\u043b\u044f Ansible-pull.<\/h2>\n<p>\u0414\u043b\u044f \u043f\u0440\u0438\u043c\u0435\u0440\u0430 \u0440\u0430\u0437\u0432\u0435\u0440\u043d\u0435\u043c \u0432 \u043d\u0430\u0448\u0435\u043c \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u0438 \u043f\u0440\u043e\u0441\u0442\u043e\u0435 \u0442\u0435\u0441\u0442\u043e\u0432\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043d\u0430 Flask \u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u043c \u0432\u0435\u0431-\u0441\u0435\u0440\u0432\u0435\u0440 \u0434\u043b\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0442\u0440\u0430\u0444\u0438\u043a\u0430.<\/p>\n<p>\u041d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u0438\u0445 \u043c\u0430\u0448\u0438\u043d\u0430\u0445 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u043e:<br \/> \u2705 \u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0441\u0435\u0440\u0432\u0438\u0441\u043d\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f<br \/> \u2705 \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043f\u043e RSA-\u043a\u043b\u044e\u0447\u0430\u043c<br \/> \u2705 \u0412\u044b\u0432\u043e\u0434 \u0431\u0430\u043d\u043d\u0435\u0440\u0430 \u043f\u0440\u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 \u043f\u043e SSH<\/p>\n<p><strong>\u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 Ansible-\u0440\u043e\u043b\u0435\u0439<\/strong><br \/>\u041a\u0430\u0442\u0430\u043b\u043e\u0433\u0438 \u0438 \u0444\u0430\u0439\u043b\u044b \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u043e\u0432\u0430\u043d\u044b \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<details class=\"spoiler\">\n<summary>\u0421\u043a\u0440\u044b\u0442\u044b\u0439 \u0442\u0435\u043a\u0441\u0442<\/summary>\n<div class=\"spoiler__content\">\n<p>.<br \/> \u251c\u2500\u2500 ansible.cfg<br \/> \u251c\u2500\u2500 group_vars<br \/> \u2502   \u2514\u2500\u2500 all.yml<br \/> \u251c\u2500\u2500 infra_ansible_pull.yml<br \/> \u251c\u2500\u2500 inventory_test.yml<br \/> \u251c\u2500\u2500 node_setting_to_pull<br \/> \u2502   \u251c\u2500\u2500 inventory.yml<br \/> \u2502   \u2514\u2500\u2500 setup_ansible_pull.yml<br \/> \u2514\u2500\u2500 roles<br \/> \u251c\u2500\u2500 app<br \/> \u2502   \u251c\u2500\u2500 files<br \/> \u2502   \u2502   \u2514\u2500\u2500 <a href=\"http:\/\/app.py\" rel=\"noopener noreferrer nofollow\">app.py<\/a><br \/> \u2502   \u251c\u2500\u2500 handlers<br \/> \u2502   \u2502   \u2514\u2500\u2500 main.yml<br \/> \u2502   \u251c\u2500\u2500 <a href=\"http:\/\/README.md\" rel=\"noopener noreferrer nofollow\">README.md<\/a><br \/> \u2502   \u251c\u2500\u2500 tasks<br \/> \u2502   \u2502   \u2514\u2500\u2500 main.yml<br \/> \u2502   \u251c\u2500\u2500 templates<br \/> \u2502   \u2502   \u2514\u2500\u2500 hello-app.service.j2<br \/> \u2502   \u2514\u2500\u2500 vars<br \/> \u2502       \u2514\u2500\u2500 main.yml<br \/> \u251c\u2500\u2500 client<br \/> \u2502   \u251c\u2500\u2500 files<br \/> \u2502   \u2502   \u251c\u2500\u2500 id_<a href=\"http:\/\/rsa.pub\" rel=\"noopener noreferrer nofollow\">rsa.pub<\/a><br \/> \u2502   \u2502   \u2514\u2500\u2500 ssh_banner<br \/> \u2502   \u251c\u2500\u2500 <a href=\"http:\/\/README.md\" rel=\"noopener noreferrer nofollow\">README.md<\/a><br \/> \u2502   \u251c\u2500\u2500 tasks<br \/> \u2502   \u2502   \u251c\u2500\u2500 main.yml<br \/> \u2502   \u2502   \u251c\u2500\u2500 motd_setup.yml<br \/> \u2502   \u2502   \u2514\u2500\u2500 user_setup.yml<br \/> \u2502   \u2514\u2500\u2500 templates<br \/> \u2502       \u2514\u2500\u2500 motd.j2<br \/> \u2514\u2500\u2500 web<br \/> \u251c\u2500\u2500 handlers<br \/> \u2502   \u2514\u2500\u2500 main.yml<br \/> \u251c\u2500\u2500 <a href=\"http:\/\/README.md\" rel=\"noopener noreferrer nofollow\">README.md<\/a><br \/> \u251c\u2500\u2500 tasks<br \/> \u2502   \u2514\u2500\u2500 main.yml<br \/> \u2514\u2500\u2500 templates<br \/> \u2514\u2500\u2500 nginx_application.conf.j2<\/p>\n<\/div>\n<\/details>\n<p>\u0418\u043d\u0432\u0435\u043d\u0442\u0430\u0440\u044c \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u043e\u0432\u043e\u0433\u043e \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f:<\/p>\n<details class=\"spoiler\">\n<summary>\u0421\u043a\u0440\u044b\u0442\u044b\u0439 \u0442\u0435\u043a\u0441\u0442<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"yaml\"># ansible\/inventory_test.yml all:   vars:     network_prefix: \"192.168.2\"    hosts:     webserver:       host_role: webserver       ansible_host: \"{{ network_prefix }}.211\"       ansible_user: vagrant       ansible_password: vagrant       ansible_become: yes       ansible_become_method: sudo      application:       host_role: application       ansible_host: \"{{ network_prefix }}.212\"       ansible_user: vagrant       ansible_password: vagrant       ansible_become: yes       ansible_become_method: sudo    children:     clients:       hosts:         client_1:           host_role: client           ansible_host: \"{{ network_prefix }}.213\"           ansible_user: vagrant           ansible_password: vagrant           ansible_become: yes           ansible_become_method: sudo          client_2:           host_role: client           ansible_host: \"{{ network_prefix }}.214\"           ansible_user: vagrant           ansible_password: vagrant           ansible_become: yes           ansible_become_method: sudo<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u041d\u0435 \u0437\u0430\u0431\u0443\u0434\u0435\u043c \u043f\u0440\u043e <strong>\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0439 \u0444\u0430\u0439\u043b<\/strong> Ansible<\/p>\n<details class=\"spoiler\">\n<summary>\u0421\u043a\u0440\u044b\u0442\u044b\u0439 \u0442\u0435\u043a\u0441\u0442<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"bash\"># ansible\/ansible.cfg [defaults] remote_tmp = \/tmp\/.ansible\/tmp roles_path = roles remote_user = ansible become = True become_method = sudo host_key_checking = False forks = 10 timeout = 30 retry_files_enabled = False log_path = .\/ansible.log gathering = smart fact_caching = jsonfile fact_caching_connection = \/tmp\/ansible_facts fact_caching_timeout = 600  [ssh_connection] pipelining = True scp_if_ssh = True<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0412 \u0444\u0430\u0439\u043b\u0435 \u0433\u0440\u0443\u043f\u043f\u043e\u0432\u044b\u0445 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u0443\u043a\u0430\u0436\u0435\u043c \u0442\u043e\u0447\u043a\u0443 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u043d\u0430\u0448\u0435\u043c\u0443 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044e<\/p>\n<details class=\"spoiler\">\n<summary>\u0421\u043a\u0440\u044b\u0442\u044b\u0439 \u0442\u0435\u043a\u0441\u0442<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"yaml\"># ansible\/group_vars\/all.yml app_endpoint: \"http:\/\/192.168.2.212:8080\"<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0412\u0435\u0440\u043d\u0435\u043c\u0441\u044f \u043a \u043d\u0430\u0448\u0435\u043c\u0443 \u043a\u043e\u0440\u043d\u0435\u0432\u043e\u043c\u0443 \u043f\u043b\u0435\u0439\u0431\u0443\u043a\u0443 \u0438 \u0438\u0437\u0443\u0447\u0438\u043c \u0435\u0433\u043e \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435.<\/p>\n<p>\u041e\u0431\u0440\u0430\u0449\u0430\u044f\u0441\u044c \u043a \u043d\u0435\u043c\u0443, Ansible \u0434\u0435\u0439\u0441\u0442\u0432\u0443\u0435\u0442 \u043f\u043e <strong>\u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c\u0443<\/strong>:<\/p>\n<ol>\n<li>\n<p><strong>\u0421\u0431\u043e\u0440 \u0444\u0430\u043a\u0442\u043e\u0432<\/strong>: Ansible \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u0445\u043e\u0441\u0442\u0435, \u0432\u043a\u043b\u044e\u0447\u0430\u044f \u0435\u0433\u043e \u0440\u043e\u043b\u044c<\/p>\n<\/li>\n<li>\n<p><strong>\u0414\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0440\u043e\u043b\u0435\u0439: <\/strong><\/p>\n<ul>\n<li>\n<p>\u0415\u0441\u043b\u0438 \u0440\u043e\u043b\u044c \u0445\u043e\u0441\u0442\u0430 \u2014 webserver, \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f \u0440\u043e\u043b\u044c web<\/p>\n<\/li>\n<li>\n<p>\u0415\u0441\u043b\u0438 \u0440\u043e\u043b\u044c application, \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u0442\u0441\u044f \u0440\u043e\u043b\u044c app<\/p>\n<\/li>\n<li>\n<p>\u0415\u0441\u043b\u0438 \u0440\u043e\u043b\u044c client, \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0430<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p><strong>\u0410\u0432\u0442\u043e\u043d\u043e\u043c\u043d\u043e\u0435 \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u0435<\/strong> \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u0441 Ansible-pull<\/p>\n<\/li>\n<\/ol>\n<details class=\"spoiler\">\n<summary>\u0421\u043a\u0440\u044b\u0442\u044b\u0439 \u0442\u0435\u043a\u0441\u0442<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"yaml\"># ansible\/ansible\/infra_ansible_pull.yaml - name: Apply roles dynamically from inventory   hosts: \"{{ target_hosts | default('localhost') }}\"   gather_facts: true    tasks:     - name: Explicitly gather custom facts       ansible.builtin.setup:         filter: ansible_local      - name: Include web role       ansible.builtin.include_role:         name: web       when: ansible_local.custom.host_role is defined and ansible_local.custom.host_role == \"webserver\"      - name: Include app role       ansible.builtin.include_role:         name: app       when: ansible_local.custom.host_role is defined and ansible_local.custom.host_role == \"application\"      - name: Include client role       ansible.builtin.include_role:         name: client       when: ansible_local.custom.host_role is defined and ansible_local.custom.host_role == \"client\" <\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p><strong>\u041a\u0440\u0430\u0442\u043a\u043e \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043a\u0430\u043a \u043d\u0430\u0448\u0438 \u0440\u043e\u043b\u0438 \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0442<\/strong><\/p>\n<p>\u0420\u043e\u043b\u044c <strong>web <\/strong>\u0440\u0430\u0437\u0432\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u0435\u0442 nginx \u0434\u043b\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u0432\u043d\u0435\u0448\u043d\u0438\u0445 \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u0432 \u043a \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044e:<\/p>\n<p>\u0423\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442 Nginx (Ubuntu\/Debian)<br \/> \u2705 \u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442 \u0438 \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442 \u0441\u0435\u0440\u0432\u0438\u0441<br \/> \u2705 \u0420\u0430\u0437\u043c\u0435\u0449\u0430\u0435\u0442 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0439 \u0444\u0430\u0439\u043b \u0438\u0437 \u0448\u0430\u0431\u043b\u043e\u043d\u0430<br \/> \u2705 \u0412\u043a\u043b\u044e\u0447\u0430\u0435\u0442 \u0441\u0430\u0439\u0442, \u0443\u0434\u0430\u043b\u044f\u0435\u0442 \u0434\u0435\u0444\u043e\u043b\u0442\u043d\u044b\u0439 \u043a\u043e\u043d\u0444\u0438\u0433<br \/> \u2705 \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e \u0438 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442 Nginx<\/p>\n<details class=\"spoiler\">\n<summary>\u0421\u043a\u0440\u044b\u0442\u044b\u0439 \u0442\u0435\u043a\u0441\u0442<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"yaml\"># ansible\/roles\/web\/handlers\/main.yml - name: Reload Nginx   ansible.builtin.service:     name: nginx     state: reloaded   become: true <\/code><\/pre>\n<pre><code class=\"yaml\"># ansible\/roles\/web\/tasks\/main.yml # \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 nginx - name: Ensure Nginx is installed (Ubuntu\/Debian)   ansible.builtin.apt:     name: nginx     state: present     update_cache: true   become: true   when: ansible_os_family == \"Debian\"  - name: Ensure Nginx is enabled and running   ansible.builtin.service:     name: nginx     state: started     enabled: true   become: true   when: ansible_os_family == \"Debian\"  - name: Create Nginx configuration file for application   ansible.builtin.template:     src: nginx_application.conf.j2  # \u041f\u0443\u0442\u044c \u043a \u0432\u0430\u0448\u0435\u043c\u0443 \u0448\u0430\u0431\u043b\u043e\u043d\u0443 Jinja2     dest: \/etc\/nginx\/sites-available\/application     mode: '0644'   become: true  - name: Enable Nginx site configuration   ansible.builtin.file:     src: \/etc\/nginx\/sites-available\/application     dest: \/etc\/nginx\/sites-enabled\/application     state: link   become: true   notify: Reload Nginx  # \u274c \u0423\u0434\u0430\u043b\u044f\u0435\u043c \u0434\u0435\u0444\u043e\u043b\u0442\u043d\u044b\u0439 \u043a\u043e\u043d\u0444\u0438\u0433 Nginx - name: Remove default Nginx site configuration   ansible.builtin.file:     path: \/etc\/nginx\/sites-enabled\/default     state: absent   become: true  - name: Test Nginx configuration   ansible.builtin.command: nginx -t   become: true   changed_when: false  # &lt;- \u0421\u043e\u043e\u0431\u0449\u0430\u0435\u0442 Ansible, \u0447\u0442\u043e \u044d\u0442\u043e \u043d\u0435 \u0438\u0437\u043c\u0435\u043d\u044f\u044e\u0449\u0435\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435  - name: Restart Nginx to apply changes   ansible.builtin.service:     name: nginx     state: restarted     enabled: true   become: true <\/code><\/pre>\n<pre><code class=\"julia\"># ansible\/roles\/web\/templates\/nginx_application.conf.j2 server {     listen 80;     server_name _;      location \/ {         # \u0423\u043a\u0430\u0436\u0438\u0442\u0435 IP-\u0430\u0434\u0440\u0435\u0441 \u0438\u043b\u0438 \u0434\u043e\u043c\u0435\u043d\u043d\u043e\u0435 \u0438\u043c\u044f \u0432\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0438 \u043f\u043e\u0440\u0442:         proxy_pass {{ app_endpoint }};         proxy_http_version 1.1;         proxy_set_header Host $host;         proxy_set_header X-Real-IP $remote_addr;         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;         proxy_set_header X-Forwarded-Proto $scheme;     } }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0420\u043e\u043b\u044c <strong>app <\/strong>\u0440\u0430\u0437\u0432\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u0435\u0442 \u043f\u0440\u043e\u0441\u0442\u043e\u0435 \u0432\u0435\u0431 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0430\u0446\u0438\u0438:<\/p>\n<p>\u2705 \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 Python \u0438 pip<br \/>\u2705 \u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f<br \/>\u2705 \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 Flask \u0438 requests<br \/>\u2705 \u0420\u0430\u0437\u0432\u0451\u0440\u0442\u044b\u0432\u0430\u043d\u0438\u0435 Flask-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f<br \/>\u2705 \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 systemd-\u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0434\u043b\u044f \u0435\u0433\u043e \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u0437\u0430\u043f\u0443\u0441\u043a\u0430<\/p>\n<details class=\"spoiler\">\n<summary>\u0421\u043a\u0440\u044b\u0442\u044b\u0439 \u0442\u0435\u043a\u0441\u0442<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"yaml\"># ansible\/roles\/app\/tasks\/main.yml # *\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 Python \u0438 Pip \u043d\u0430 Debian\/Ubuntu* - name: Install Python and Pip (Debian)   become: true   ansible.builtin.apt:     name:       - python3-apt       - python3       - python3-pip       - python3-venv     state: present     update_cache: true   when: ansible_os_family == \"Debian\"  # \u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f - name: Create Python virtual environment   ansible.builtin.command: \"python3 -m venv {{ venv_path }}\"   args:     creates: \"{{ venv_path }}\"  # \u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 pip \u0432\u043d\u0443\u0442\u0440\u0438 \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f - name: Upgrade pip inside virtual environment   ansible.builtin.pip:     name: pip     state: present     extra_args: --upgrade     virtualenv: \"{{ venv_path }}\"  # \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u043f\u0430\u043a\u0435\u0442\u043e\u0432 (Flask \u0438 requests) \u0432 \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0435 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u0435 - name: Install required Python packages in virtual environment   ansible.builtin.pip:     name:       - requests       - flask     virtualenv: \"{{ venv_path }}\"  # *\u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 Python* - name: Verify Python installation   ansible.builtin.command: python3 --version   register: python_version   changed_when: false  - name: Debug Python version   ansible.builtin.debug:     msg: \"Installed Python version: {{ python_version.stdout }}\"  # *\u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 Pip (\u0441\u0438\u0441\u0442\u0435\u043c\u043d\u043e\u0433\u043e)* - name: Verify Pip installation   ansible.builtin.command: pip3 --version   register: pip_version   changed_when: false  - name: Debug Pip version   ansible.builtin.debug:     msg: \"Installed Pip version: {{ pip_version.stdout }}\"  # *\u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430, \u0447\u0442\u043e \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0435 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u043d\u043e* - name: Verify virtual environment   ansible.builtin.stat:     path: \"{{ venv_path }}\/bin\/activate\"   register: venv_check  - name: Debug virtual environment status   ansible.builtin.debug:     msg: \"Virtual environment exists at {{ venv_path }}\"   when: venv_check.stat.exists  # ====== \u0420\u0430\u0437\u0432\u0451\u0440\u0442\u044b\u0432\u0430\u043d\u0438\u0435 Flask-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f ======  # \u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 \u0434\u043b\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f - name: Create application directory   ansible.builtin.file:     path: \"{{ app_dir }}\"     state: directory     mode: '0755'  # \u0420\u0430\u0437\u0432\u0451\u0440\u0442\u044b\u0432\u0430\u043d\u0438\u0435 \u0444\u0430\u0439\u043b\u0430 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f app.py - name: Deploy Flask application (app.py)   ansible.builtin.copy:     src: app.py     dest: \"{{ app_dir }}\/app.py\"     mode: '0755'  # \u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 systemd-\u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f - name: Create systemd service for Flask app   become: true   ansible.builtin.template:     src: hello-app.service.j2     dest: \/etc\/systemd\/system\/hello-app.service     mode: '0644'   notify: Reload systemd  # \u0412\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0438 \u0437\u0430\u043f\u0443\u0441\u043a Flask \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0447\u0435\u0440\u0435\u0437 systemd - name: Ensure hello-app service is enabled and started   become: true   ansible.builtin.systemd:     name: hello-app     state: restarted     enabled: true <\/code><\/pre>\n<pre><code class=\"yaml\">#!\/usr\/bin\/env python3 # -*- coding: utf-8 -*- # ansible\/roles\/app\/files  from flask import Flask, render_template_string from datetime import datetime import random  app = Flask(__name__)  QUOTES = [     \"\u0416\u0438\u0437\u043d\u044c \u2014 \u044d\u0442\u043e \u0442\u043e, \u0447\u0442\u043e \u0441 \u0442\u043e\u0431\u043e\u0439 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442, \u043f\u043e\u043a\u0430 \u0442\u044b \u0441\u0442\u0440\u043e\u0438\u0448\u044c \u043f\u043b\u0430\u043d\u044b. \u2014 \u0414\u0436\u043e\u043d \u041b\u0435\u043d\u043d\u043e\u043d\",     \"\u0421\u0435\u043a\u0440\u0435\u0442 \u0443\u0441\u043f\u0435\u0445\u0430 \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e\u0431\u044b \u043d\u0430\u0447\u0430\u0442\u044c. \u2014 \u041c\u0430\u0440\u043a \u0422\u0432\u0435\u043d\",     \"\u0421\u043b\u043e\u0436\u043d\u044b\u0435 \u0434\u043e\u0440\u043e\u0433\u0438 \u0432\u0435\u0434\u0443\u0442 \u043a \u043a\u0440\u0430\u0441\u0438\u0432\u044b\u043c \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f\u043c. \u2014 \u041d\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043d\u044b\u0439 \u0430\u0432\u0442\u043e\u0440\",     \"\u041d\u0430\u0447\u043d\u0438 \u0434\u0435\u043b\u0430\u0442\u044c \u2014 \u0438 \u044d\u043d\u0435\u0440\u0433\u0438\u044f \u043f\u043e\u044f\u0432\u0438\u0442\u0441\u044f. \u2014 \u0414\u0436\u0435\u0439\u043c\u0441 \u041a\u043b\u0438\u0440\",     \"\u0411\u0443\u0434\u0443\u0449\u0435\u0435 \u043f\u0440\u0438\u043d\u0430\u0434\u043b\u0435\u0436\u0438\u0442 \u0442\u0435\u043c, \u043a\u0442\u043e \u0432\u0435\u0440\u0438\u0442 \u0432 \u043a\u0440\u0430\u0441\u043e\u0442\u0443 \u0441\u0432\u043e\u0435\u0439 \u043c\u0435\u0447\u0442\u044b. \u2014 \u042d\u043b\u0435\u043e\u043d\u043e\u0440 \u0420\u0443\u0437\u0432\u0435\u043b\u044c\u0442\" ]  def get_current_time():     return datetime.now().strftime(\"%Y-%m-%d %H:%M:%S\")  def get_random_quote():     return random.choice(QUOTES)  HTML_TEMPLATE = \"\"\" &lt;!DOCTYPE html&gt; &lt;html lang=\"ru\"&gt; &lt;head&gt;     &lt;meta charset=\"UTF-8\"&gt;     &lt;meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"&gt;     &lt;title&gt;\u0422\u0435\u0441\u0442\u043e\u0432\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435&lt;\/title&gt;     &lt;link          href=\"https:\/\/cdn.jsdelivr.net\/npm\/bootstrap@5.3.0\/dist\/css\/bootstrap.min.css\"          rel=\"stylesheet\"&gt;     &lt;style&gt;         body { background-color: #f8f9fa; text-align: center; padding: 5rem; }         .container { background: white; padding: 2rem; border-radius: 10px; box-shadow: 0 0 10px rgba(0,0,0,0.1); }         h1 { color: #007bff; }         p { font-size: 1.2rem; }         .quote { font-style: italic; color: #6c757d; }     &lt;\/style&gt; &lt;\/head&gt; &lt;body&gt;     &lt;div class=\"container\"&gt;         &lt;h1&gt;\ud83d\ude80 \u0414\u043e\u0431\u0440\u043e \u043f\u043e\u0436\u0430\u043b\u043e\u0432\u0430\u0442\u044c!&lt;\/h1&gt;         &lt;p&gt;\u042d\u0442\u043e \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0439 Flask-\u0441\u0435\u0440\u0432\u0435\u0440.&lt;\/p&gt;         &lt;p&gt;&lt;strong&gt;\u0422\u0435\u043a\u0443\u0449\u0435\u0435 \u0432\u0440\u0435\u043c\u044f:&lt;\/strong&gt; {{ time }}&lt;\/p&gt;         &lt;p class=\"quote\"&gt;\ud83d\udcdc \u0426\u0438\u0442\u0430\u0442\u0430 \u0434\u043d\u044f: \"{{ quote }}\"&lt;\/p&gt;         &lt;a href=\"\/time\" class=\"btn btn-primary mt-3\"&gt;\u041f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u0432\u0440\u0435\u043c\u044f&lt;\/a&gt;     &lt;\/div&gt; &lt;\/body&gt; &lt;\/html&gt; \"\"\"  @app.route('\/') def home():     return render_template_string(HTML_TEMPLATE, time=get_current_time(), quote=get_random_quote())  @app.route('\/time') def show_time():     return {\"current_time\": get_current_time()}  if __name__ == '__main__':     app.run(host='0.0.0.0', port=8080, debug=True)<\/code><\/pre>\n<pre><code class=\"yaml\"># ansible\/roles\/app\/handlers\/main.yml - name: Reload systemd   become: true   ansible.builtin.systemd:     daemon_reload: true <\/code><\/pre>\n<pre><code># ansible\/roles\/app\/templates\/hello-app.service.j2 [Unit] Description=Hello Flask App Service After=network.target  [Service] User={{ ansible_user_id }} WorkingDirectory={{ app_dir }} ExecStart={{ venv_path }}\/bin\/python app.py Restart=always  [Install] WantedBy=multi-user.target<\/code><\/pre>\n<pre><code class=\"yaml\"># ansible\/roles\/app\/vars\/main.yml venv_path: \"{{ ansible_env.HOME }}\/.venv\" app_dir: \"{{ ansible_env.HOME }}\/hello_app\" <\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0420\u043e\u043b\u044c <strong>client <\/strong>\u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u0442 \u0441\u0435\u0440\u0432\u0438\u0441\u043d\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, rsa-\u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044e \u0438 ssh \u0431\u0430\u043d\u043d\u0435\u0440. \u041d\u0435 \u0437\u0430\u0431\u044b\u0432\u0430\u0435\u043c \u043f\u0435\u0440\u0435\u043d\u0435\u0441\u0442\u0438 \u043f\u0443\u0431\u043b\u0438\u0447\u043d\u044b\u0439 \u043a\u043b\u044e\u0447  \u0432 <code><strong>ansible\/roles\/client\/files\/id_rsa.pub<\/strong><\/code><\/p>\n<details class=\"spoiler\">\n<summary>\u0421\u043a\u0440\u044b\u0442\u044b\u0439 \u0442\u0435\u043a\u0441\u0442<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"yaml\"># ansible\/roles\/client\/tasks\/main.yml --- - name: \u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 SSH   ansible.builtin.import_tasks: user_setup.yml  - name: \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 MOTD \/ SSH \u0431\u0430\u043d\u043d\u0435\u0440\u0430   ansible.builtin.import_tasks: motd_setup.yml <\/code><\/pre>\n<pre><code class=\"yaml\"># ansible\/roles\/client\/tasks\/motd_setup.yml --- - name: Setup MOTD (message day)   ansible.builtin.template:     src: templates\/motd.j2     dest: \/etc\/motd     owner: root     group: root     mode: '0644'  # \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043c \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u044b\u0435 \u043f\u0440\u0430\u0432\u0430   become: true  - name: Install SSH banner   ansible.builtin.copy:     src: files\/ssh_banner     dest: \/etc\/issue.net     owner: root     group: root     mode: '0644'   become: true  - name: Enable SSH banner   ansible.builtin.lineinfile:     path: \/etc\/ssh\/sshd_config     regexp: '^#?Banner'     line: 'Banner \/etc\/issue.net'     state: present   notify: Restart SSH   become: true <\/code><\/pre>\n<pre><code class=\"yaml\"># ansible\/roles\/client\/tasks\/user_setup.yml --- - name: Create user ladmin   ansible.builtin.user:     name: ladmin     shell: \/bin\/bash     groups: \"{{ 'sudo' if ansible_os_family == 'Debian' else 'wheel' }}\"     append: true     create_home: true     state: present   become: true  - name: Copy SSH-keys for user ladmin   ansible.posix.authorized_key:     user: ladmin     state: present     key: \"{{ lookup('file', 'files\/id_rsa.pub') }}\"  - name: Create sudo no pass user ladmin   ansible.builtin.copy:     dest: \/etc\/sudoers.d\/ladmin     content: \"ladmin ALL=(ALL) NOPASSWD:ALL\"     mode: \"0440\"   become: true <\/code><\/pre>\n<p><strong>ansible\/roles\/client\/files\/ssh_banner<\/strong><\/p>\n<pre><code>\ud83d\udd12 \u0412\u043d\u0438\u043c\u0430\u043d\u0438\u0435! \u042d\u0442\u043e \u0447\u0430\u0441\u0442\u043d\u044b\u0439 \u0441\u0435\u0440\u0432\u0435\u0440. \u041d\u0435\u0430\u0432\u0442\u043e\u0440\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0434\u043e\u0441\u0442\u0443\u043f \u0437\u0430\u043f\u0440\u0435\u0449\u0451\u043d! \ud83d\udd12 <\/code><\/pre>\n<pre><code class=\"yaml\"># ansible\/roles\/client\/handlers\/main.yml - name: Restart SSH   ansible.builtin.service:     name: \"{{ 'sshd' if ansible_os_family == 'RedHat' else 'ssh' }}\"     state: restarted  - name: Reload environment   ansible.builtin.command: source \/etc\/environment   changed_when: false <\/code><\/pre>\n<p><strong>ansible\/roles\/client\/templates\/motd.j2<\/strong><\/p>\n<pre><code class=\"julia\">\u0414\u043e\u0431\u0440\u043e \u043f\u043e\u0436\u0430\u043b\u043e\u0432\u0430\u0442\u044c \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440 {{ ansible_hostname }}! \u0414\u0430\u0442\u0430 \u0438 \u0432\u0440\u0435\u043c\u044f: {{ ansible_date_time.date }}, {{ ansible_date_time.time }} \u041e\u0421: {{ ansible_distribution }} {{ ansible_distribution_version }} CPU: {{ ansible_processor_cores }} \u044f\u0434\u0435\u0440 RAM: {{ ansible_memtotal_mb }} MB<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<h3>\u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0440\u0430\u0431\u043e\u0442\u044b ansible-pull<\/h3>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/b2c\/908\/c20\/b2c908c208a5e4f40fbe1c155e6894f5.jpg\" alt=\"\u0414\u0435\u043b\u0430\u0435\u043c \u043a\u043e\u043c\u043c\u0438\u0442 \u0438 \u043f\u0443\u0448, \u0438 \u043d\u0430\u0441\u043b\u0430\u0436\u0434\u0430\u0435\u043c\u0441\u044f \u0443\u0441\u043f\u0435\u0448\u043d\u044b\u043c\u0438 \u0442\u0435\u0441\u0442\u0430\u043c\u0438 CI\/CD!\" title=\"\u0414\u0435\u043b\u0430\u0435\u043c \u043a\u043e\u043c\u043c\u0438\u0442 \u0438 \u043f\u0443\u0448, \u0438 \u043d\u0430\u0441\u043b\u0430\u0436\u0434\u0430\u0435\u043c\u0441\u044f \u0443\u0441\u043f\u0435\u0448\u043d\u044b\u043c\u0438 \u0442\u0435\u0441\u0442\u0430\u043c\u0438 CI\/CD!\" width=\"1252\" height=\"89\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/b2c\/908\/c20\/b2c908c208a5e4f40fbe1c155e6894f5.jpg\" data-blurred=\"true\"\/><\/p>\n<div><figcaption>\u0414\u0435\u043b\u0430\u0435\u043c \u043a\u043e\u043c\u043c\u0438\u0442 \u0438 \u043f\u0443\u0448, \u0438 \u043d\u0430\u0441\u043b\u0430\u0436\u0434\u0430\u0435\u043c\u0441\u044f \u0443\u0441\u043f\u0435\u0448\u043d\u044b\u043c\u0438 \u0442\u0435\u0441\u0442\u0430\u043c\u0438 CI\/CD!<\/figcaption><\/div>\n<\/figure>\n<p>\u0421\u043f\u0443\u0441\u0442\u044f 30 \u043c\u0438\u043d\u0443\u0442, \u0432\u0441\u0435 \u043d\u0430\u0448\u0438 \u043a\u043b\u0438\u0435\u043d\u0442\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e ansible-pull \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043f\u0440\u0438\u043c\u0435\u043d\u0438\u0442\u044c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u0443\u044e \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e \u0438\u0437 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f.<\/p>\n<p>\u041f\u0435\u0440\u0435\u0439\u0434\u044f \u043d\u0430 \u0432\u0435\u0431-\u0441\u0435\u0440\u0432\u0435\u0440 \u0432 \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u0435\u043d\u0435, \u043c\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0443\u0432\u0438\u0434\u0435\u0442\u044c \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0443\u044e \u043d\u0430\u0448\u0438\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u043c <strong>\u0432\u0435\u0431-\u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443<\/strong>.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/3ad\/82a\/9e4\/3ad82a9e47f9278899bd4ad02b7c0eb3.jpg\" width=\"770\" height=\"373\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/3ad\/82a\/9e4\/3ad82a9e47f9278899bd4ad02b7c0eb3.jpg\" data-blurred=\"true\"\/><\/figure>\n<p>\u0427\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u043a\u0430\u043a \u043a\u043b\u0438\u0435\u043d\u0442\u044b \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u044e\u0442 \u043f\u043b\u0435\u0439\u0431\u0443\u043a\u0438 \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u043b\u043e\u0433 \u043e\u0434\u043d\u043e\u0439 \u0438\u0437 \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u0438\u0445 \u043c\u0430\u0448\u0438\u043d<strong> \/var\/log\/ansible-pull.log<\/strong><\/p>\n<details class=\"spoiler\">\n<summary>\u0421\u043a\u0440\u044b\u0442\u044b\u0439 \u0442\u0435\u043a\u0441\u0442<\/summary>\n<div class=\"spoiler__content\">\n<p>\/var\/log\/ansible-pull.log<\/p>\n<p>ansible@p-application:~$ cat \/var\/log\/ansible-pull.log<br \/> [WARNING]: Could not match supplied host pattern, ignoring: p-application<br \/> <a href=\"http:\/\/localhost\" rel=\"noopener noreferrer nofollow\">localhost<\/a> | SUCCESS =&gt; {<br \/> &#171;after&#187;: &#171;ba7fba1e999fe5e3a4f49d09834601e4c2481e6e&#187;,<br \/> &#171;before&#187;: &#171;ba7fba1e999fe5e3a4f49d09834601e4c2481e6e&#187;,<br \/> &#171;changed&#187;: false,<br \/> &#171;remote_url_changed&#187;: false<br \/> }<br \/> [WARNING]: No inventory was parsed, only implicit <a href=\"http:\/\/localhost\" rel=\"noopener noreferrer nofollow\">localhost<\/a> is available<br \/> [WARNING]: provided hosts list is empty, only <a href=\"http:\/\/localhost\" rel=\"noopener noreferrer nofollow\">localhost<\/a> is available. Note that<br \/> the implicit <a href=\"http:\/\/localhost\" rel=\"noopener noreferrer nofollow\">localhost<\/a> does not match &#8216;all&#8217;<br \/> [WARNING]: Could not match supplied host pattern, ignoring: p-application<\/p>\n<p>PLAY [Apply roles dynamically from inventory] **********************************<\/p>\n<p>TASK [Gathering Facts] *********************************************************<br \/> ok: [<a href=\"http:\/\/localhost\" rel=\"noopener noreferrer nofollow\">localhost<\/a>]<\/p>\n<p>TASK [Explicitly gather custom facts] ******************************************<br \/> ok: [<a href=\"http:\/\/localhost\" rel=\"noopener noreferrer nofollow\">localhost<\/a>]<\/p>\n<p>TASK [Include web role] ********************************************************<br \/> skipping: [<a href=\"http:\/\/localhost\" rel=\"noopener noreferrer nofollow\">localhost<\/a>]<\/p>\n<p>TASK [Include app role] ********************************************************<\/p>\n<p>TASK [app : Install Python and Pip (Debian)] ***********************************<br \/> ok: [<a href=\"http:\/\/localhost\" rel=\"noopener noreferrer nofollow\">localhost<\/a>]<\/p>\n<p>TASK [app : Create Python virtual environment] *********************************<br \/> ok: [<a href=\"http:\/\/localhost\" rel=\"noopener noreferrer nofollow\">localhost<\/a>]<\/p>\n<p>TASK [app : Upgrade pip inside virtual environment] ****************************<br \/> ok: [<a href=\"http:\/\/localhost\" rel=\"noopener noreferrer nofollow\">localhost<\/a>]<\/p>\n<p>TASK [app : Install required Python packages in virtual environment] ***********<br \/> ok: [<a href=\"http:\/\/localhost\" rel=\"noopener noreferrer nofollow\">localhost<\/a>]<\/p>\n<p>TASK [app : Verify Python installation] ****************************************<br \/> ok: [<a href=\"http:\/\/localhost\" rel=\"noopener noreferrer nofollow\">localhost<\/a>]<\/p>\n<p>TASK [app : Debug Python version] **********************************************<br \/> ok: [<a href=\"http:\/\/localhost\" rel=\"noopener noreferrer nofollow\">localhost<\/a>] =&gt; {<br \/> &#171;msg&#187;: &#171;Installed Python version: Python 3.11.2&#187;<br \/> }<\/p>\n<p>TASK [app : Verify Pip installation] *******************************************<br \/> ok: [<a href=\"http:\/\/localhost\" rel=\"noopener noreferrer nofollow\">localhost<\/a>]<\/p>\n<p>TASK [app : Debug Pip version] *************************************************<br \/> ok: [<a href=\"http:\/\/localhost\" rel=\"noopener noreferrer nofollow\">localhost<\/a>] =&gt; {<br \/> &#171;msg&#187;: &#171;Installed Pip version: pip 23.0.1 from \/usr\/lib\/python3\/dist-packages\/pip (python 3.11)&#187;<br \/> }<\/p>\n<p>TASK [app : Verify virtual environment] ****************************************<br \/> ok: [<a href=\"http:\/\/localhost\" rel=\"noopener noreferrer nofollow\">localhost<\/a>]<\/p>\n<p>TASK [app : Debug virtual environment status] **********************************<br \/> ok: [<a href=\"http:\/\/localhost\" rel=\"noopener noreferrer nofollow\">localhost<\/a>] =&gt; {<br \/> &#171;msg&#187;: &#171;Virtual environment exists at \/home\/ansible\/.venv&#187;<br \/> }<\/p>\n<p>TASK [app : Create application directory] **************************************<br \/> ok: [<a href=\"http:\/\/localhost\" rel=\"noopener noreferrer nofollow\">localhost<\/a>]<\/p>\n<p>TASK [app : Deploy Flask application (<a href=\"http:\/\/app.py\" rel=\"noopener noreferrer nofollow\">app.py<\/a>)] *********************************<br \/> ok: [<a href=\"http:\/\/localhost\" rel=\"noopener noreferrer nofollow\">localhost<\/a>]<\/p>\n<p>TASK [app : Create systemd service for Flask app] ******************************<br \/> ok: [<a href=\"http:\/\/localhost\" rel=\"noopener noreferrer nofollow\">localhost<\/a>]<\/p>\n<p>TASK [app : Ensure hello-app service is enabled and started] *******************<br \/> changed: [<a href=\"http:\/\/localhost\" rel=\"noopener noreferrer nofollow\">localhost<\/a>]<\/p>\n<p>TASK [Include client role] *****************************************************<br \/> skipping: [<a href=\"http:\/\/localhost\" rel=\"noopener noreferrer nofollow\">localhost<\/a>]<\/p>\n<p>PLAY RECAP *********************************************************************<br \/> <a href=\"http:\/\/localhost\" rel=\"noopener noreferrer nofollow\">localhost<\/a>                  : ok=16   changed=1    unreachable=0    failed=0    skipped=2    rescued=0    ignored=0<\/p>\n<p>Starting Ansible Pull at 2025-03-14 23:29:53<br \/> \/usr\/bin\/ansible-pull -U <a href=\"mailto:git@gitlab.ch\" rel=\"noopener noreferrer nofollow\">git@gitlab.ch<\/a>.ap:AndreyChuyan\/ansible_pull_cicd.git -C release &#8212;private-key \/home\/ansible\/.ssh\/id_ansible_pull ansible\/infra_ansible_pull.yml<\/p>\n<\/div>\n<\/details>\n<p>\u0418\u0437 \u0432\u044b\u0432\u043e\u0434\u0430 \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0431\u043b\u044e\u0434\u0430\u0442\u044c, \u0447\u0442\u043e \u0432\u0441\u0435 \u0437\u0430\u0434\u0430\u0447\u0438 <strong>\u0443\u0441\u043f\u0435\u0448\u043d\u043e <\/strong>\u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u044b.<\/p>\n<p><strong>\u041c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433 \u043e\u0448\u0438\u0431\u043e\u043a \u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0439<\/strong><br \/>\u0412 \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0435\u043c \u043c\u043e\u0436\u043d\u043e \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0442\u044c \u043e\u0448\u0438\u0431\u043a\u0438 \u0438 \u0441\u0442\u0430\u0442\u0443\u0441 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0439 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0445 \u044d\u043a\u0441\u043f\u043e\u0440\u0442\u0451\u0440\u043e\u0432 <strong>Prometheus <\/strong>\u0438\u043b\u0438 <strong>ELK<\/strong>, \u043d\u043e \u044d\u0442\u043e \u0443\u0436\u0435 \u0442\u0435\u043c\u0430 \u0434\u043b\u044f \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438.<\/p>\n<p><strong>\u0421\u043f\u0430\u0441\u0438\u0431\u043e<\/strong>, \u0447\u0442\u043e \u0434\u043e\u0447\u0438\u0442\u0430\u043b\u0438 \u0441\u0442\u0430\u0442\u044c\u044e \u0434\u043e \u043a\u043e\u043d\u0446\u0430! \u0411\u0443\u0434\u0443 \u0440\u0430\u0434 \u0432\u0430\u0448\u0435\u0439 \u043e\u0431\u0440\u0430\u0442\u043d\u043e\u0439 \u0441\u0432\u044f\u0437\u0438.<\/p>\n<\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><\/p>\n<div class=\"tm-article-poll-container\"><!--[--><\/p>\n<div class=\"tm-article-poll tm-article-poll_variant-bordered\">\n<div class=\"tm-notice tm-notice_positive tm-article-poll__notice\"><!----><\/p>\n<div class=\"tm-notice__inner\"><!----><\/p>\n<div class=\"tm-notice__content\" data-test-id=\"notice-content\"><!--[--><span>\u0422\u043e\u043b\u044c\u043a\u043e \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438 \u043c\u043e\u0433\u0443\u0442 \u0443\u0447\u0430\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c \u0432 \u043e\u043f\u0440\u043e\u0441\u0435. <a rel=\"nofollow\" href=\"\/kek\/v1\/auth\/habrahabr\/?back=\/ru\/articles\/890276\/&#038;hl=ru\">\u0412\u043e\u0439\u0434\u0438\u0442\u0435<\/a>, \u043f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430.<\/span><!--]--><\/div>\n<\/div>\n<\/div>\n<p><!--[--><\/p>\n<div class=\"tm-article-poll__header\">\u041a\u0430\u043a\u043e\u0439 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0439 \u0432\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0435 \u0434\u043b\u044f pull-\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0439?<\/div>\n<div class=\"tm-article-poll__answers\"><!--[--><\/p>\n<div class=\"tm-article-poll__answer\">\n<div class=\"tm-article-poll__answer-data\"><span class=\"tm-article-poll__answer-percent\">31.25% <\/span><span class=\"tm-article-poll__answer-label\">Ansible-pull<\/span><span class=\"tm-article-poll__answer-votes\">5<\/span><\/div>\n<div class=\"tm-article-poll__answer-bar\">\n<div class=\"tm-article-poll__answer-progress\" style=\"width: 31.25%\"><\/div>\n<\/div>\n<\/div>\n<div class=\"tm-article-poll__answer\">\n<div class=\"tm-article-poll__answer-data\"><span class=\"tm-article-poll__answer-percent\">18.75% <\/span><span class=\"tm-article-poll__answer-label\">SaltStack<\/span><span class=\"tm-article-poll__answer-votes\">3<\/span><\/div>\n<div class=\"tm-article-poll__answer-bar\">\n<div class=\"tm-article-poll__answer-progress\" style=\"width: 18.75%\"><\/div>\n<\/div>\n<\/div>\n<div class=\"tm-article-poll__answer\">\n<div class=\"tm-article-poll__answer-data\"><span class=\"tm-article-poll__answer-percent\">18.75% <\/span><span class=\"tm-article-poll__answer-label\">Puppet<\/span><span class=\"tm-article-poll__answer-votes\">3<\/span><\/div>\n<div class=\"tm-article-poll__answer-bar\">\n<div class=\"tm-article-poll__answer-progress\" style=\"width: 18.75%\"><\/div>\n<\/div>\n<\/div>\n<div class=\"tm-article-poll__answer\">\n<div class=\"tm-article-poll__answer-data\"><span class=\"tm-article-poll__answer-percent\">6.25% <\/span><span class=\"tm-article-poll__answer-label\">Chef<\/span><span class=\"tm-article-poll__answer-votes\">1<\/span><\/div>\n<div class=\"tm-article-poll__answer-bar\">\n<div class=\"tm-article-poll__answer-progress\" style=\"width: 6.25%\"><\/div>\n<\/div>\n<\/div>\n<div class=\"tm-article-poll__answer\">\n<div class=\"tm-article-poll__answer-data\"><span class=\"tm-article-poll__answer-percent\">12.5% <\/span><span class=\"tm-article-poll__answer-label\">Custom Bash\/Python-\u0441\u043a\u0440\u0438\u043f\u0442\u044b<\/span><span class=\"tm-article-poll__answer-votes\">2<\/span><\/div>\n<div class=\"tm-article-poll__answer-bar\">\n<div class=\"tm-article-poll__answer-progress\" style=\"width: 12.5%\"><\/div>\n<\/div>\n<\/div>\n<div class=\"tm-article-poll__answer\">\n<div class=\"tm-article-poll__answer-data\"><span class=\"tm-article-poll__answer-percent tm-article-poll__answer-percent_winning\">31.25% <\/span><span class=\"tm-article-poll__answer-label\">\u0414\u0440\u0443\u0433\u0438\u0435 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b? \u041d\u0430\u043f\u0438\u0448\u0438\u0442\u0435 \u0432 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u044f\u0445!<\/span><span class=\"tm-article-poll__answer-votes\">5<\/span><\/div>\n<div class=\"tm-article-poll__answer-bar\">\n<div class=\"tm-article-poll__answer-progress tm-article-poll__answer-progress_winning\" style=\"width: 31.25%\"><\/div>\n<\/div>\n<\/div>\n<p><!--]--><\/div>\n<div class=\"tm-article-poll__stats\"> \u041f\u0440\u043e\u0433\u043e\u043b\u043e\u0441\u043e\u0432\u0430\u043b\u0438 16 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439.   \u0412\u043e\u0437\u0434\u0435\u0440\u0436\u0430\u043b\u0438\u0441\u044c 10 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439. <\/div>\n<p><!--]--><\/div>\n<p><!--]--><\/div>\n<p> \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\/890276\/\"> https:\/\/habr.com\/ru\/articles\/890276\/<\/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>\ud83d\udc4b \u041f\u0440\u0438\u0432\u0435\u0442! <\/p>\n<p>\u041c\u0435\u043d\u044f \u0437\u043e\u0432\u0443\u0442 \u0410\u043d\u0434\u0440\u0435\u0439, \u044f \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u0438\u0441\u0442 \u043f\u043e \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044e IT-\u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043e\u0439 \u0441 \u043e\u043f\u044b\u0442\u043e\u043c \u0440\u0430\u0431\u043e\u0442\u044b \u0441 Windows- \u0438 Linux-\u0441\u0438\u0441\u0442\u0435\u043c\u0430\u043c\u0438.<\/p>\n<p>\u0421\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u0430\u044f IT-\u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u2014 \u044d\u0442\u043e \u0436\u0438\u0432\u043e\u0439 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u043c, \u0441\u043e\u0441\u0442\u043e\u044f\u0449\u0438\u0439 \u0438\u0437 \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u0432, \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0445 \u043c\u0430\u0448\u0438\u043d \u0438 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u043e\u0432, \u0440\u0430\u0437\u0431\u0440\u043e\u0441\u0430\u043d\u043d\u044b\u0445 \u043f\u043e \u0440\u0430\u0437\u043d\u044b\u043c \u0434\u0430\u0442\u0430-\u0446\u0435\u043d\u0442\u0440\u0430\u043c \u0438 \u043e\u0431\u043b\u0430\u0447\u043d\u044b\u043c \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0430\u043c. <\/p>\n<p><strong>\u0423\u0437\u043b\u0430\u043c\u0438 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c:<\/strong> <\/p>\n<ul>\n<li>\n<p>\u0420\u0430\u0431\u043e\u0447\u0438\u0435 \u0441\u0442\u0430\u043d\u0446\u0438\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439,<\/p>\n<\/li>\n<li>\n<p>\u0421\u0435\u0440\u0432\u0435\u0440\u044b \u0437\u0430 NAT \u0432 \u0441\u0435\u0440\u044b\u0445 \u0437\u043e\u043d\u0430\u0445 \u0441\u0435\u0442\u0438,<\/p>\n<\/li>\n<li>\n<p>\u0425\u043e\u0441\u0442\u044b \u0432 \u043d\u0435\u0441\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u044b\u0445 \u0441\u0435\u0442\u0435\u0432\u044b\u0445 \u0443\u0441\u043b\u043e\u0432\u0438\u044f\u0445, \u043f\u0435\u0440\u0438\u043e\u0434\u0438\u0447\u0435\u0441\u043a\u0438 \u0438\u0441\u0447\u0435\u0437\u0430\u044e\u0449\u0438\u0435 \u0438\u0437 \u0434\u043e\u0441\u0442\u0443\u043f\u0430.<\/p>\n<\/li>\n<\/ul>\n<p><strong>\u041f\u0440\u0438 \u0440\u0430\u0431\u043e\u0442\u0435 \u0441 \u0442\u0430\u043a\u043e\u0439 \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u043d\u043e\u0439 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043e\u0439 \u043c\u043e\u0436\u043d\u043e \u0441\u0442\u043e\u043b\u043a\u043d\u0443\u0442\u044c\u0441\u044f \u0441 \u0440\u044f\u0434\u043e\u043c \u0441\u043b\u043e\u0436\u043d\u043e\u0441\u0442\u0435\u0439.<\/strong><\/p>\n<ul>\n<li>\n<p><strong>\u0421\u0435\u0442\u0438 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u043d\u0435\u043d\u0430\u0434\u0435\u0436\u043d\u044b\u043c\u0438 <\/strong>\u2013 \u0441\u0435\u0440\u0432\u0435\u0440 \u0438\u043b\u0438 \u0440\u0430\u0431\u043e\u0447\u0430\u044f \u0441\u0442\u0430\u043d\u0446\u0438\u044f \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u043d\u0430 \u0438\u0437-\u0437\u0430 \u0441\u0431\u043e\u044f \u0441\u0432\u044f\u0437\u0438, \u043f\u0435\u0440\u0435\u0433\u0440\u0443\u0437\u043a\u0438 \u0438\u043b\u0438 \u043f\u043e\u043b\u0438\u0442\u0438\u043a\u0438 \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u0438.   <\/p>\n<\/li>\n<li>\n<p><strong>\u0423\u0437\u043b\u044b \u0437\u0430 NAT \u0438\u043b\u0438 \u0432 \u0438\u0437\u043e\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u0437\u043e\u043d\u0430\u0445<\/strong> \u2013 Ansible Control Node \u043d\u0435 \u0438\u043c\u0435\u0435\u0442 \u043f\u0440\u044f\u043c\u043e\u0433\u043e \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u0442\u0430\u043a\u0438\u043c \u0443\u0437\u043b\u0430\u043c.<\/p>\n<\/li>\n<li>\n<p><strong>\u0423\u0434\u0430\u043b\u0451\u043d\u043d\u044b\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u043f\u0435\u0440\u0435\u043c\u0435\u0449\u0430\u044e\u0442\u0441\u044f<\/strong> \u2013 \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043d\u043e\u0443\u0442\u0431\u0443\u043a\u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432 \u0438\u043b\u0438 \u043e\u0431\u043e\u0440\u0443\u0434\u043e\u0432\u0430\u043d\u0438\u0435 \u0432 \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u044b\u0445 \u0434\u0430\u0442\u0430-\u0446\u0435\u043d\u0442\u0440\u0430\u0445.    <\/p>\n<\/li>\n<\/ul>\n<div class=\"persona\">\n<h5 class=\"persona__heading\">\u041c\u043e\u0439 \u0442\u0435\u043b\u0435\u0433\u0440\u0430\u043c\u043c \u043a\u0430\u043d\u0430\u043b &#8212; \u0441\u043e\u043e\u0431\u0449\u0435\u0441\u0442\u0432\u043e, \u0433\u0434\u0435 \u0434\u0435\u043b\u044f\u0442\u0441\u044f \u043e\u043f\u044b\u0442\u043e\u043c<\/h5>\n<p><a href=\"https:\/\/t.me\/IT_Chuyana\" rel=\"noopener noreferrer nofollow\"><strong>https:\/\/t.me\/IT_Chuyana<\/strong><\/a><a href=\"https:\/\/t.me\/IT_Chuyana\" rel=\"noopener noreferrer nofollow\"> <\/a><\/p>\n<\/div>\n<h2>\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u043a\u043b\u0430\u0441\u0441\u0438\u0447\u0435\u0441\u043a\u043e\u0439 push-\u043c\u043e\u0434\u0435\u043b\u0438 Ansible   <\/h2>\n<p>\u0412 push-\u043c\u043e\u0434\u0435\u043b\u0438 \u0446\u0435\u043d\u0442\u0440\u0430\u043b\u044c\u043d\u044b\u0439 \u0441\u0435\u0440\u0432\u0435\u0440 Ansible \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0437\u0430 \u0434\u043e\u0441\u0442\u0430\u0432\u043a\u0443 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0439 \u0443\u0437\u043b\u0430\u043c \u043f\u0440\u0438 \u0438\u0445 \u0441\u0435\u0442\u0435\u0432\u043e\u0439 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e\u0441\u0442\u0438.<\/p>\n<figure class=\"full-width\">\n<div><figcaption>\u0421\u0435\u0440\u0432\u0435\u0440 \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u0442 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e \u043a\u0430\u0436\u0434\u043e\u043c\u0443 \u0443\u0437\u043b\u0443 \u043f\u0440\u0438 \u0435\u0433\u043e \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e\u0441\u0442\u0438<\/figcaption><\/div>\n<\/figure>\n<p>\u0412 \u0443\u0441\u043b\u043e\u0432\u0438\u044f\u0445 \u043d\u0435\u0441\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u044b\u0445 \u0438 \u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u0451\u043d\u043d\u044b\u0445 \u0441\u0435\u0442\u0435\u0439 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0442\u0430\u043a\u043e\u0439 \u043c\u043e\u0434\u0435\u043b\u0438 \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442 \u043a \u0440\u044f\u0434\u0443 \u043f\u0440\u043e\u0431\u043b\u0435\u043c: <\/p>\n<h4>\u041d\u0435\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0441\u0442\u044c \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438    <\/h4>\n<ul>\n<li>\n<p>\u0415\u0441\u043b\u0438 \u0443\u0437\u0435\u043b \u0431\u044b\u043b \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d \u0432\u043e \u0432\u0440\u0435\u043c\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f Ansible-\u0437\u0430\u0434\u0430\u0447 (playbook), \u043e\u043d \u043e\u0441\u0442\u0430\u0451\u0442\u0441\u044f <strong>\u0443\u0441\u0442\u0430\u0440\u0435\u0432\u0448\u0438\u043c<\/strong> \u0438 \u043c\u043e\u0436\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u043d\u0435\u0432\u0435\u0440\u043d\u044b\u043c\u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u043c\u0438.    <\/p>\n<\/li>\n<li>\n<p>\u041d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u043e\u0434\u043d\u043e\u0437\u043d\u0430\u0447\u043d\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c, <strong>\u043a\u0430\u043a\u0430\u044f \u0432\u0435\u0440\u0441\u0438\u044f \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u0441\u0435\u0439\u0447\u0430\u0441 \u043d\u0430 \u0443\u0437\u043b\u0435<\/strong>, \u0435\u0441\u043b\u0438 \u043e\u043d \u043f\u0435\u0440\u0438\u043e\u0434\u0438\u0447\u0435\u0441\u043a\u0438 \u0438\u0441\u0447\u0435\u0437\u0430\u0435\u0442 \u0438\u0437 \u0441\u0435\u0442\u0438.     <\/p>\n<\/li>\n<li>\n<p><strong>\u0420\u0430\u0437\u043d\u044b\u0435 \u0432\u0435\u0440\u0441\u0438\u0438 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438<\/strong> \u0443 \u0440\u0430\u0437\u043d\u044b\u0445 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u0432 \u043c\u043e\u0433\u0443\u0442 \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u043d\u0435\u043e\u0436\u0438\u0434\u0430\u043d\u043d\u044b\u0435 \u0431\u0430\u0433\u0438 \u0438 \u0440\u0430\u0441\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u044e \u0440\u0430\u0431\u043e\u0442\u044b \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432.       <\/p>\n<\/li>\n<\/ul>\n<h4>\u041f\u0435\u0440\u0435\u0433\u0440\u0443\u0437\u043a\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f    <\/h4>\n<ul>\n<li>\n<p>\u041f\u0440\u0438 \u0442\u0440\u0430\u0434\u0438\u0446\u0438\u043e\u043d\u043d\u043e\u043c \u043f\u043e\u0434\u0445\u043e\u0434\u0435 Ansible <strong>\u043e\u0434\u0438\u043d \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u044e\u0449\u0438\u0439 \u0441\u0435\u0440\u0432\u0435\u0440 \u0434\u043e\u043b\u0436\u0435\u043d \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u0432\u0441\u0435 \u0443\u0437\u043b\u044b<\/strong>, \u0447\u0442\u043e \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u0443\u0437\u043a\u0438\u043c \u043c\u0435\u0441\u0442\u043e\u043c.    <\/p>\n<\/li>\n<li>\n<p>\u041c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432 \u0438 \u0443\u0441\u043b\u043e\u0436\u043d\u044f\u0435\u0442 \u043f\u0440\u043e\u0446\u0435\u0441\u0441.    <\/p>\n<\/li>\n<li>\n<p>\u0415\u0441\u043b\u0438 \u0446\u0435\u043d\u0442\u0440\u0430\u043b\u044c\u043d\u044b\u0439 \u0441\u0435\u0440\u0432\u0435\u0440 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0432\u044b\u0445\u043e\u0434\u0438\u0442 \u0438\u0437 \u0441\u0442\u0440\u043e\u044f, <strong>\u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u044f \u043e\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442\u0441\u044f<\/strong>.    <\/p>\n<\/li>\n<\/ul>\n<h4>\u0420\u0438\u0441\u043a \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u043e\u0433\u043e \u0434\u0440\u0435\u0439\u0444\u0430 <\/h4>\n<ul>\n<li>\n<p>\u0411\u0435\u0437 \u043f\u043e\u0441\u0442\u043e\u044f\u043d\u043d\u043e\u0433\u043e \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044f \u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 <strong>\u0443\u0437\u043b\u044b \u043c\u043e\u0433\u0443\u0442 &#171;\u0434\u0440\u0435\u0439\u0444\u043e\u0432\u0430\u0442\u044c&#187; \u043e\u0442 \u044d\u0442\u0430\u043b\u043e\u043d\u043d\u043e\u0439 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438<\/strong>. <\/p>\n<\/li>\n<li>\n<p>\u0415\u0441\u043b\u0438 \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440 \u0432\u0440\u0443\u0447\u043d\u0443\u044e \u0438\u0437\u043c\u0435\u043d\u0438\u043b \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438, \u0430 \u043f\u043e\u0442\u043e\u043c Ansible \u043d\u0435 \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u043b\u0441\u044f \u0434\u043e\u043b\u0433\u043e\u0435 \u0432\u0440\u0435\u043c\u044f \u2013 \u043c\u043e\u0433\u0443\u0442 \u0432\u043e\u0437\u043d\u0438\u043a\u043d\u0443\u0442\u044c \u043d\u0435\u043e\u0436\u0438\u0434\u0430\u043d\u043d\u044b\u0435 \u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442\u044b.<\/p>\n<\/li>\n<li>\n<p>\u0412 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0435 \u0447\u0430\u0441\u0442\u044c \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u0432 \u043c\u043e\u0436\u0435\u0442 \u043e\u043a\u0430\u0437\u0430\u0442\u044c\u0441\u044f \u0432 <strong>\u043d\u0435\u043f\u0440\u0435\u0434\u0441\u043a\u0430\u0437\u0443\u0435\u043c\u043e\u043c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0438 \u0434\u043b\u044f \u043a\u043e\u043c\u0430\u043d\u0434\u044b DevOps<\/strong>  <\/p>\n<\/li>\n<\/ul>\n<h2>\u041a\u0430\u043a \u0440\u0435\u0448\u0438\u0442\u044c \u044d\u0442\u0438 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b   <\/h2>\n<p>\u041e\u043f\u0442\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u043c \u0440\u0435\u0448\u0435\u043d\u0438\u0435\u043c \u0434\u043b\u044f \u0442\u0430\u043a\u0438\u0445 \u0443\u0441\u043b\u043e\u0432\u0438\u0439 \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f <code><strong>ansible-pull<\/strong><\/code> \u2013 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u0435\u043d\u044f\u0435\u0442 \u0442\u0440\u0430\u0434\u0438\u0446\u0438\u043e\u043d\u043d\u0443\u044e \u043c\u043e\u0434\u0435\u043b\u044c \u0440\u0430\u0431\u043e\u0442\u044b Ansible. \u0412\u043c\u0435\u0441\u0442\u043e \u0442\u043e\u0433\u043e \u0447\u0442\u043e\u0431\u044b \u00ab\u0442\u043e\u043b\u043a\u0430\u0442\u044c\u00bb \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e \u043d\u0430 \u0443\u0434\u0430\u043b\u0451\u043d\u043d\u044b\u0435 \u0443\u0437\u043b\u044b, <strong>\u043a\u0430\u0436\u0434\u044b\u0439 \u0441\u0435\u0440\u0432\u0435\u0440 \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u0442 \u0438 \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u0442 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e<\/strong>.   <\/p>\n<p>\u041e\u0433\u043e\u0432\u043e\u0440\u044e\u0441\u044c \u0441\u0440\u0430\u0437\u0443: \u043f\u043e \u0441\u0443\u0442\u0438, ansible-pull \u2013 \u044d\u0442\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u043a\u043e\u043c\u0430\u043d\u0434\u044b:<br \/><code> git pull &amp;&amp; ansible-playbook -i inventory_with...<\/code><\/p>\n<p>\u041e\u043d\u0430 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442\u0441\u044f \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u043a\u043e\u0434, \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u043d\u044b\u0439 \u0438\u0437 \u0443\u0434\u0430\u043b\u0451\u043d\u043d\u043e\u0433\u043e \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f.<\/p>\n<p>\u041e\u0434\u043d\u0430\u043a\u043e, \u0432 \u043e\u0442\u043b\u0438\u0447\u0438\u0435 \u043e\u0442 Puppet, \u0440\u0430\u0431\u043e\u0442\u0430 \u0441 YAML-\u0441\u0438\u043d\u0442\u0430\u043a\u0441\u0438\u0441\u043e\u043c, \u043d\u0430 \u043c\u043e\u0439 \u0432\u0437\u0433\u043b\u044f\u0434, \u0433\u043e\u0440\u0430\u0437\u0434\u043e \u0443\u0434\u043e\u0431\u043d\u0435\u0435. \u041a\u0440\u043e\u043c\u0435 \u0442\u043e\u0433\u043e, \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 Ansible \u0448\u0438\u0440\u0435, \u0447\u0442\u043e \u0434\u0435\u043b\u0430\u0435\u0442 \u0435\u0433\u043e \u0431\u043e\u043b\u0435\u0435 \u0433\u0438\u0431\u043a\u0438\u043c \u0438 \u043c\u043e\u0449\u043d\u044b\u043c \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u043c \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u0438.<\/p>\n<figure class=\"full-width\">\n<div><figcaption>\u041a\u0430\u0436\u0434\u044b\u0439 \u0443\u0437\u0435\u043b \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e \u0437\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0435\u0442 \u0438 \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u0442 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e<\/figcaption><\/div>\n<\/figure>\n<h4>\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 ansible-pull<\/h4>\n<ul>\n<li>\n<p><strong>\u041e\u0431\u043e\u0440\u0443\u0434\u043e\u0432\u0430\u043d\u0438\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f \u043d\u0435\u0440\u0435\u0433\u0443\u043b\u044f\u0440\u043d\u043e<\/strong> \u2014 \u0441\u0430\u043c\u043e \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u0442 \u043a\u043e\u043d\u0444\u0438\u0433 \u043f\u043e\u0441\u043b\u0435 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f.<\/p>\n<\/li>\n<li>\n<p><strong>\u041d\u0435 \u043d\u0443\u0436\u0435\u043d \u0446\u0435\u043d\u0442\u0440\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u044b\u0439 Ansible Control Node<\/strong> (\u043a\u0430\u0436\u0434\u044b\u0439 \u0443\u0437\u0435\u043b \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0441\u0430\u043c \u0437\u0430 \u0441\u0435\u0431\u044f). <\/p>\n<\/li>\n<li>\n<p><strong>\u0420\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0434\u0430\u0436\u0435 \u0437\u0430 NAT \/ Firewall<\/strong> \u2014 \u0442\u044f\u043d\u0435\u0442 \u043a\u043e\u043d\u0444\u0438\u0433 \u0438\u0437 \u043e\u0431\u043b\u0430\u0447\u043d\u043e\u0433\u043e \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f (Git).  <\/p>\n<\/li>\n<li>\n<p><strong>\u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0440\u0430\u0441\u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u044f\u044e\u0442\u0441\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438<\/strong> \u2014 \u043d\u0435\u0442 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u0438\u0432\u043d\u043e\u0433\u043e \u0434\u043e\u0441\u0442\u0443\u043f\u0430.<\/p>\n<\/li>\n<li>\n<p><strong>\u041b\u0435\u0433\u043a\u043e \u043e\u0442\u043a\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f<\/strong> \u2014 \u0440\u0435\u0432\u0435\u0440\u0442 \u0447\u0435\u0440\u0435\u0437 Git \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u043e.<\/p>\n<\/li>\n<\/ul>\n<h4>\u041f\u0440\u0438\u043c\u0435\u0440\u044b \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u044f ansible-pull<\/h4>\n<ul>\n<li>\n<p><strong>\u0420\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u0451\u043d\u043d\u044b\u0435 \u0441\u0442\u0430\u043d\u0446\u0438\u0438 \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043f\u0438\u0442\u0430\u043d\u0438\u044f, \u043c\u0435\u0442\u0435\u043e\u0441\u0442\u0430\u043d\u0446\u0438\u0438 \u0438 \u0434\u0430\u0442\u0447\u0438\u043a\u0438<\/strong><\/p>\n<\/li>\n<li>\n<p><strong>\u0413\u0435\u043e\u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u0451\u043d\u043d\u044b\u0435 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u0442\u0435\u043b\u0435\u0444\u043e\u043d\u0438\u0438 (IP-\u0442\u0435\u043b\u0435\u0444\u043e\u043d\u044b, Asterisk)<\/strong>    <\/p>\n<\/li>\n<li>\n<p><strong>\u0420\u0430\u0431\u043e\u0447\u0438\u0435 \u0441\u0442\u0430\u043d\u0446\u0438\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 (\u043d\u043e\u0443\u0442\u0431\u0443\u043a\u0438, \u043a\u0430\u0441\u0441\u043e\u0432\u044b\u0435 \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b\u044b)<\/strong>  <\/p>\n<\/li>\n<li>\n<p><strong>\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043d\u043e\u0432\u044b\u0445 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u0432 (\u0430\u0432\u0442\u043e\u043f\u0440\u043e\u0432\u0438\u0436\u043d\u0438\u043d\u0433)<\/strong>    <\/p>\n<\/li>\n<li>\n<p><strong>IoT-\u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0438 \u0441\u0435\u0442\u0435\u0432\u043e\u0435 \u043e\u0431\u043e\u0440\u0443\u0434\u043e\u0432\u0430\u043d\u0438\u0435<\/strong>   <\/p>\n<\/li>\n<\/ul>\n<p>\u0414\u043e\u0432\u043e\u0434\u044b \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d\u044b, \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u043e\u0434\u0443\u043c\u0430\u0435\u043c \u043a\u0430\u043a \u043d\u0430\u043c \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u044d\u0442\u043e\u0442 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c.<\/p>\n<h2>\u0425\u043e\u0434 \u0440\u0430\u0431\u043e\u0442\u044b<\/h2>\n<p>\u041d\u0430\u0448\u0443 \u0437\u0430\u0434\u0430\u0447\u0443 \u043c\u044b \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u043c \u043d\u0430 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u0434\u0437\u0430\u0434\u0430\u0447:<\/p>\n<ol>\n<li>\n<p>\u041f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0430 \u0442\u0435\u0441\u0442\u043e\u0432\u043e\u0433\u043e \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f \u043d\u0430 \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0445 \u043c\u0430\u0448\u0438\u043d\u0430\u0445 \u0447\u0435\u0440\u0435\u0437<strong> Vagrant<\/strong><\/p>\n<\/li>\n<li>\n<p>\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0443\u0437\u043b\u043e\u0432 \u043a <strong>\u0440\u0435\u0436\u0438\u043c\u0443 Ansible-pull<\/strong>, \u0432\u044b\u0434\u0430\u0447\u0430 \u0438\u043c <strong>\u0440\u043e\u043b\u0435\u0439 <\/strong>\u0438 <strong>RSA-\u043a\u043b\u044e\u0447\u0435\u0439<\/strong><\/p>\n<\/li>\n<li>\n<p>\u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0441\u0440\u0435\u0434\u044b \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0430 \u043a\u043e\u0434\u0430 \u0432<strong> Docker-\u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0435<\/strong><\/p>\n<\/li>\n<li>\n<p><strong>\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 CI\/CD-\u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d\u0430<\/strong> \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430 \u0440\u0430\u0437\u0432\u0435\u0440\u0442\u044b\u0432\u0430\u043d\u0438\u044f \u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0432 \u043a\u043e\u043d\u0432\u0435\u0439\u0435\u0440\u0435<\/p>\n<\/li>\n<li>\n<p><strong>\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0440\u043e\u043b\u0435\u0439<\/strong> \u0434\u043b\u044f Ansible-pull.<\/p>\n<\/li>\n<\/ol>\n<p>CI\/CD<strong>&#8212;<\/strong>\u043a\u043e\u043d\u0432\u0435\u0439\u0435\u0440 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0443\u0435\u043c \u043f\u043e \u0441\u0445\u0435\u043c\u0435:<\/p>\n<figure class=\"full-width\"><\/figure>\n<h3>1. \u041f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0430 \u0442\u0435\u0441\u0442\u043e\u0432\u043e\u0433\u043e \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f \u043d\u0430 \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0445 \u043c\u0430\u0448\u0438\u043d\u0430\u0445 \u0447\u0435\u0440\u0435\u0437 Vagrant<\/h3>\n<p>Vagrant \u044d\u0442\u043e \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u0434\u043b\u044f \u0431\u044b\u0441\u0442\u0440\u043e\u0433\u043e \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u043c\u0438 \u0441\u0440\u0435\u0434\u0430\u043c\u0438. \u041e\u043d \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043f\u043e\u0434\u043d\u0438\u043c\u0430\u0442\u044c \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0435 \u043c\u0430\u0448\u0438\u043d\u044b \u0441 \u0437\u0430\u0440\u0430\u043d\u0435\u0435 \u0437\u0430\u0434\u0430\u043d\u043d\u043e\u0439 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0435\u0439.<\/p>\n<figure class=\"full-width\"><\/figure>\n<p>\u0412 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043c\u044b \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u0438\u043c \u0442\u0435\u0441\u0442\u043e\u0432\u043e\u0435 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u0435 \u0438\u0437 <strong>\u0447\u0435\u0442\u044b\u0440\u0451\u0445 \u0443\u0437\u043b\u043e\u0432<\/strong>:   <\/p>\n<ul>\n<li>\n<p><strong>webserver<\/strong> (\u0432\u0435\u0431-\u0441\u0435\u0440\u0432\u0435\u0440) Debian 12, <\/p>\n<\/li>\n<li>\n<p><strong>application<\/strong> (\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435) Debian 12,<\/p>\n<\/li>\n<li>\n<p><strong>client-1 <\/strong>Ubuntu 18.04<\/p>\n<\/li>\n<li>\n<p><strong>client-2 <\/strong>CentOS 7. <\/p>\n<\/li>\n<\/ul>\n<p>\u041a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u0438\u0445 \u0443\u0437\u043b\u043e\u0432 <strong>\u043d\u0435 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u043e<\/strong>, \u0438\u0445 \u0447\u0438\u0441\u043b\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442\u0441\u044f \u0440\u0430\u0437\u043b\u0438\u0447\u0438\u0435\u043c \u0432 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u0445 \u0434\u0438\u0441\u0442\u0440\u0438\u0431\u0443\u0442\u0438\u0432\u0430\u0445 \u0438 \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u044f\u0445 \u0438\u0445 \u0440\u0430\u0431\u043e\u0442\u044b. <\/p>\n<p>\u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430 \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0445 \u043c\u0430\u0448\u0438\u043d \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <strong>libvirt<\/strong>. \u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0439 \u0444\u0430\u0439\u043b <strong>Vagrantfile<\/strong> \u0434\u043b\u044f <code>vagrant\/libvirt\/<\/code> \u0431\u0443\u0434\u0435\u0442 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442\u044c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0434\u043b\u044f \u0440\u0430\u0437\u0432\u0435\u0440\u0442\u044b\u0432\u0430\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u043e\u0433\u043e \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f.  <\/p>\n<p><strong>\u0421\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u043c Vagrantfile:<\/strong><\/p>\n<details class=\"spoiler\">\n<summary>\u0421\u043a\u0440\u044b\u0442\u044b\u0439 \u0442\u0435\u043a\u0441\u0442<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"bash\"># vagrant\\libvirt\\Vagrantfile # \u0418\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0434\u043b\u044f \u043c\u043e\u0441\u0442\u0430 BRIDGE_NET = \"192.168.2.\" BRIDGE_NAME = \"br0\" BRIDGE_DNS = \"192.168.2.1\"  # \u041f\u0443\u0442\u044c \u043a \u043e\u0431\u0440\u0430\u0437\u0430\u043c \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0445 \u043c\u0430\u0448\u0438\u043d IMAGE_PATH = \"\/kvm\/images\"   # \u0414\u043e\u043c\u0435\u043d \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u043b\u044f \u0432\u0441\u0435\u0439 \u043f\u043b\u043e\u0449\u0430\u0434\u043a\u0438 DOMAIN = \"ch.ap\"  # debian 12 BOX_1 = \"d12\"     # debian 12 BOX_2 = \"u1804\"   # ubuntu 18.04 BOX_3 = \"c7\"      # centos 7  # \u0423\u043a\u0430\u0436\u0438\u0442\u0435 \u043f\u0443\u0442\u044c \u043a \u043e\u0431\u0449\u0435\u0439 \u043f\u0430\u043f\u043a\u0435 \u043d\u0430 \u0445\u043e\u0441\u0442\u0435 \u0438 \u043f\u0443\u0442\u044c \u043a \u0442\u043e\u0447\u043a\u0435 \u043c\u043e\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0432 \u0412\u041c HOST_SHARED_FOLDER = \".\"   VM_SHARED_FOLDER = \"\/home\/vagrant\/shared\"  # \u0418\u043c\u0435\u043d\u0430 \u0445\u043e\u0441\u0442\u043e\u0432 HOSTNAME_1 = \"t-webserver\" HOSTNAME_2 = \"t-application\" HOSTNAME_3 = \"t-client-1\" HOSTNAME_4 = \"t-client-2\"  # \u041c\u0430\u0441\u0441\u0438\u0432 \u0438\u0437 \u0445\u0435\u0448\u0435\u0439, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0437\u0430\u0434\u0430\u044e\u0442\u0441\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0439 \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0439 \u043c\u0430\u0448\u0438\u043d\u044b. MACHINES = {   HOSTNAME_1.to_sym =&gt; {     :box_name     =&gt; BOX_1,     :host_name    =&gt; HOSTNAME_1,      :ip_brig      =&gt; BRIDGE_NET + \"211\",     :ip_brig_dns  =&gt; BRIDGE_DNS,     :disk_size    =&gt; \"20G\",     :int_model_type =&gt; \"e1000\",     :cpu          =&gt; 1,     :ram          =&gt; 512,     :vnc          =&gt; 5971,     :host_role    =&gt; \"webserver\",   },   HOSTNAME_2.to_sym =&gt; {     :box_name     =&gt; BOX_1,     :host_name    =&gt; HOSTNAME_2,      :ip_brig      =&gt; BRIDGE_NET + \"212\",     :ip_brig_dns  =&gt; BRIDGE_DNS,     :disk_size    =&gt; \"20G\",     :int_model_type =&gt; \"e1000\",     :cpu          =&gt; 1,     :ram          =&gt; 1024,     :vnc          =&gt; 5972,     :host_role    =&gt; \"application\",   },   HOSTNAME_3.to_sym =&gt; {     :box_name     =&gt; BOX_2,     :host_name    =&gt; HOSTNAME_3,      :ip_brig      =&gt; BRIDGE_NET + \"213\",     :ip_brig_dns  =&gt; BRIDGE_DNS,     :disk_size    =&gt; \"20G\",     :int_model_type =&gt; \"e1000\",     :cpu          =&gt; 1,     :ram          =&gt; 512,     :vnc          =&gt; 5973,     :host_role    =&gt; \"client\",   },   HOSTNAME_4.to_sym =&gt; {     :box_name     =&gt; BOX_3,     :host_name    =&gt; HOSTNAME_4,      :ip_brig      =&gt; BRIDGE_NET + \"214\",     :ip_brig_dns  =&gt; BRIDGE_DNS,     :disk_size    =&gt; \"20G\",     :int_model_type =&gt; \"e1000\",     :cpu          =&gt; 1,     :ram          =&gt; 512,     :vnc          =&gt; 5974,     :host_role    =&gt; \"client\",   }, }  ## \u043c\u043e\u0434\u0443\u043b\u044c \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a Vagrant.configure(\"2\") do |config|   MACHINES.each do |boxname, boxconfig|     config.vm.define boxname do |box|       box.vm.box = boxconfig[:box_name]                      box.vm.hostname = boxconfig[:host_name]              # \u0423\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u043f\u0443\u0431\u043b\u0438\u0447\u043d\u0443\u044e \u0441\u0435\u0442\u044c \u0441 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\u043c\u0438 \u043c\u043e\u0441\u0442\u0430       box.vm.network \"public_network\",                        bridge: BRIDGE_NAME,                        dev: BRIDGE_NAME,                        type: \"bridge\",                        ip: boxconfig[:ip_brig]        box.vm.provider \"libvirt\" do |libvirt|         libvirt.cpus = boxconfig[:cpu]         libvirt.memory = boxconfig[:ram]         libvirt.nic_model_type = boxconfig[:int_model_type]         libvirt.management_network_name = BRIDGE_NAME         libvirt.storage_pool_name = \"images\"         libvirt.graphics_port = boxconfig[:vnc]         libvirt.graphics_autoport = false       end        # # \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0439 \u043f\u0430\u043f\u043a\u0438       box.vm.synced_folder \".\", VM_SHARED_FOLDER, type: \"nfs\", nfs_version: 4, nfs_udp: false       # --- rsa \u043a\u043b\u044e\u0447\u0438       box.vm.provision \"file\", source: \"#{ENV['HOME']}\/.ssh\/id_rsa.pub\", destination: \"\/tmp\/authorized_keys\"       box.vm.provision \"shell\", inline: &lt;&lt;-SHELL       # \u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u0434\u0438\u0441\u0442\u0440\u0438\u0431\u0443\u0442\u0438\u0432       OS_NAME=$(grep -Eoi 'ubuntu|debian|centos|rhel' \/etc\/os-release | head -1)        # \ud83d\udccc \u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0441\u0438\u0441\u0442\u0435\u043c\u044b       if [[ \"$OS_NAME\" == \"Ubuntu\" ]] || [[ \"$OS_NAME\" == \"Debian\" ]]; then         sudo apt-get update           # sudo apt-get upgrade -y        elif [[ \"$OS_NAME\" == \"CentOS\" ]] || [[ \"$OS_NAME\" == \"rhel\" ]]; then         OS_VERSION=$(grep -oP '(?&lt;=VERSION_ID=\")[0-9]+' \/etc\/os-release)          # \ud83d\udd04 \u041f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0435\u0432 \u043d\u0430 \u0430\u0440\u0445\u0438\u0432\u043d\u044b\u0435 \u0434\u043b\u044f CentOS 7 \u0438 8         if [[ \"$OS_VERSION\" == \"7\" ]] || [[ \"$OS_VERSION\" == \"8\" ]]; then           echo \"\ud83d\udd04 \u041f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0430\u044e CentOS $OS_VERSION \u043d\u0430 \u0430\u0440\u0445\u0438\u0432\u043d\u044b\u0439 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439...\"           sudo sed -i 's|^mirrorlist=.*|#mirrorlist removed|' \/etc\/yum.repos.d\/CentOS-Base.repo           sudo sed -i 's|^#baseurl=http:\/\/mirror.centos.org|baseurl=http:\/\/vault.centos.org|' \/etc\/yum.repos.d\/CentOS-Base.repo         fi          # \u2705 \u041e\u0447\u0438\u0441\u0442\u043a\u0430 \u043a\u044d\u0448\u0430 \u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u0430\u043a\u0435\u0442\u043e\u0432         sudo yum clean all         sudo yum makecache         # sudo yum update -y       fi        # \ud83d\udccc RSA \u043a\u043b\u044e\u0447\u0438       if [ -f \/tmp\/authorized_keys ]; then         cat \/tmp\/authorized_keys &gt;&gt; \/home\/vagrant\/.ssh\/authorized_keys       fi        # \ud83d\udccc \u0422\u0430\u0439\u043c\u0437\u043e\u043d\u0430       sudo timedatectl set-timezone Europe\/Moscow        # \ud83d\udccc Ansible \u0444\u0430\u043a\u0442       sudo mkdir -p \/etc\/ansible\/facts.d       echo '{ \"host_role\": \"#{boxconfig[:host_role]}\" }' | sudo tee \/etc\/ansible\/facts.d\/custom.fact &gt; \/dev\/null       sudo chmod 0644 \/etc\/ansible\/facts.d\/custom.fact        echo \"\u2705 \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0430\"       SHELL        if<\/code><\/pre>\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-452853","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/452853","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=452853"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/452853\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=452853"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=452853"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=452853"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}