<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>DevOps, Productivity, Career // etogeek blog</title>
    <link>https://etogeek.dev/</link>
    <description>Recent content on DevOps, Productivity, Career // etogeek blog</description>
    <generator>Hugo</generator>
    <language>ru</language>
    <lastBuildDate>Mon, 27 Oct 2025 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://etogeek.dev/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Maintenance-режим в Nginx</title>
      <link>https://etogeek.dev/posts/nginx-maintenance-mode/</link>
      <pubDate>Mon, 27 Oct 2025 00:00:00 +0000</pubDate>
      <guid>https://etogeek.dev/posts/nginx-maintenance-mode/</guid>
      <description>&#xA;  &#xA;&#xA;&#xA;&#xA;&lt;div&#xA;  &#xA;    class=&#34;flex px-4 py-3 rounded-md bg-primary-100 dark:bg-primary-900&#34;&#xA;  &gt;&#xA;&#xA;  &lt;span&#xA;    &#xA;      class=&#34;text-primary-400 ltr:pr-3 rtl:pl-3 flex items-center&#34;&#xA;    &gt;&#xA;&#xA;    &#xA;&#xA;  &lt;span class=&#34;relative block icon&#34;&gt;&#xA;    &lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; viewBox=&#34;0 0 512 512&#34;&gt;&lt;path fill=&#34;currentColor&#34; d=&#34;M506.3 417l-213.3-364c-16.33-28-57.54-28-73.98 0l-213.2 364C-10.59 444.9 9.849 480 42.74 480h426.6C502.1 480 522.6 445 506.3 417zM232 168c0-13.25 10.75-24 24-24S280 154.8 280 168v128c0 13.25-10.75 24-23.1 24S232 309.3 232 296V168zM256 416c-17.36 0-31.44-14.08-31.44-31.44c0-17.36 14.07-31.44 31.44-31.44s31.44 14.08 31.44 31.44C287.4 401.9 273.4 416 256 416z&#34;/&gt;&lt;/svg&gt;&#xA;&#xA;  &lt;/span&gt;&#xA;&#xA;&#xA;  &lt;/span&gt;&#xA;&#xA;  &lt;span&#xA;    &#xA;      class=&#34;dark:text-neutral-300&#34;&#xA;    &gt;This post is also &lt;a href=&#34;https://etogeek.dev/en/posts/nginx-maintenance-mode/&#34;   target=&#34;_blank&#34;&gt;&#xA;    available in English&lt;/a&gt;.&lt;/span&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Что это такое? &#xA;    &lt;div id=&#34;%D1%87%D1%82%D0%BE-%D1%8D%D1%82%D0%BE-%D1%82%D0%B0%D0%BA%D0%BE%D0%B5&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D1%87%D1%82%D0%BE-%D1%8D%D1%82%D0%BE-%D1%82%D0%B0%D0%BA%D0%BE%D0%B5&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Прям такого режима в Nginx нет, я просто хочу так называть &lt;strong&gt;возможность вручную включить выдачу определенной страницы вместо всей остальной выдачи.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;Сейчас мы умеем раздавать определенную html-страничку вместо стандартной страницы ошибки.&lt;/p&gt;&#xA;&lt;p&gt;Например вместо дефолтной 504 ошибки мы можем отдавать красивую страницу с логотипом компании и вежливой понятной фразой вроде “Сейчас сервис недоступен, мы уже знаем о проблеме, попробуйте через минутку”.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-nginx&#34; data-lang=&#34;nginx&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;error_page&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;500&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;501&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;503&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;502&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;504&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;/static/errors/5xx.html&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;А что если мы знаем, что сегодня с 20 вечера будут проводиться работы, которые повлекут даунтайм сервиса. Включим maintenance-режим с информацией о проводимых работах, сроке завершения.&lt;/p&gt;&#xA;&lt;p&gt;Вторая ситуация: сервис жестко упал, инженеры решают инцедент, а клиенты в это время видят то 500-ки, то 503, то 504-ки, то таймауты. Включим maintenance-режим и всем будет спокойнее.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Неудачная попытка &#xA;    &lt;div id=&#34;%D0%BD%D0%B5%D1%83%D0%B4%D0%B0%D1%87%D0%BD%D0%B0%D1%8F-%D0%BF%D0%BE%D0%BF%D1%8B%D1%82%D0%BA%D0%B0&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%BD%D0%B5%D1%83%D0%B4%D0%B0%D1%87%D0%BD%D0%B0%D1%8F-%D0%BF%D0%BE%D0%BF%D1%8B%D1%82%D0%BA%D0%B0&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Первое что нашлось в интернете на эту тему: через if проверяйте наличие файла на сервере (например &lt;code&gt;/etc/nginx/maintenance.flag&lt;/code&gt;), если файл есть - отдаем страницу, если нет - идем по стандартному nginx-конфигу. Что-то типа такого:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-nginx&#34; data-lang=&#34;nginx&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;server&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &#x9;&lt;span class=&#34;kn&#34;&gt;....&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &#x9;&lt;span class=&#34;s&#34;&gt;error_page&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;503&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;/maintenance.html&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &#x9;&lt;span class=&#34;kn&#34;&gt;location&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;/maintenance.html&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &#x9;    &lt;span class=&#34;kn&#34;&gt;root&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;/etc/nginx/maintenance/&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &#x9;    &lt;span class=&#34;kn&#34;&gt;internal&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &#x9;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &#x9;&lt;span class=&#34;kn&#34;&gt;location&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &#x9;  &lt;span class=&#34;kn&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;(-f&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;/etc/nginx/maintenance.flag)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &#x9;     &lt;span class=&#34;kn&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;503&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &#x9;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &#x9;  &lt;span class=&#34;kn&#34;&gt;proxy_pass&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;http://127.0.0.1:3000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &#x9;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Это вполне рабочий вариант, но здесь &lt;strong&gt;наличие файла проверяется на КАЖДЫЙ запрос&lt;/strong&gt;. Это может быть ресурсозатратным. На dev-стенде всё прошло хорошо, а на проде начало вызывать тормоза.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Почему return 503? &#xA;    &lt;div id=&#34;%D0%BF%D0%BE%D1%87%D0%B5%D0%BC%D1%83-return-503&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%BF%D0%BE%D1%87%D0%B5%D0%BC%D1%83-return-503&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Почему нужно возвращать клиенту 503 код ответа, несмотря на то, что мы отдаем живую работающую html-страницу?&lt;/p&gt;&#xA;&lt;p&gt;503 код обычно означает, что сервис временно недоступен, там проводятся работы или он перегружен. &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/503&#34;   target=&#34;_blank&#34;&gt;&#xA;    Подробнее&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;503 код ответа не индексируется поисковиками. Представь, если робот гугла в момент даунтайма спарсит страницу твоего проекта и запишет у себя “Under maintenance”.&lt;/p&gt;&#xA;&lt;p&gt;Так же клиентским браузерам надо запрещать кэшировать такие страницы, чтобы после завершения даунтайма пользователь не ходил на maintenance-страничку из кэша.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Как можно сделать через include конфигурации? &#xA;    &lt;div id=&#34;%D0%BA%D0%B0%D0%BA-%D0%BC%D0%BE%D0%B6%D0%BD%D0%BE-%D1%81%D0%B4%D0%B5%D0%BB%D0%B0%D1%82%D1%8C-%D1%87%D0%B5%D1%80%D0%B5%D0%B7-include-%D0%BA%D0%BE%D0%BD%D1%84%D0%B8%D0%B3%D1%83%D1%80%D0%B0%D1%86%D0%B8%D0%B8&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%BA%D0%B0%D0%BA-%D0%BC%D0%BE%D0%B6%D0%BD%D0%BE-%D1%81%D0%B4%D0%B5%D0%BB%D0%B0%D1%82%D1%8C-%D1%87%D0%B5%D1%80%D0%B5%D0%B7-include-%D0%BA%D0%BE%D0%BD%D1%84%D0%B8%D0%B3%D1%83%D1%80%D0%B0%D1%86%D0%B8%D0%B8&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Общая логика такая:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;у нас есть файл &lt;code&gt;maintenance_on.conf&lt;/code&gt; в котором одна строка: &lt;code&gt;return 503;&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;по необходимости исклюдим этот файл в нужные locations череш &lt;code&gt;include maintenance_on.conf&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;мягко рестартим Nginx (через reload)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Теперь подробности реализации:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Создаем директорию, чтобы не было мусора: &lt;code&gt;mkdir /etc/nginx/maintenance&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;В нее кладем нашу &lt;code&gt;maintenance.html&lt;/code&gt; страничку. Навайбкодьте…&lt;/li&gt;&#xA;&lt;li&gt;Создаем конфиг-файл &lt;code&gt;maintenance_on.conf&lt;/code&gt;:&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-nginx&#34; data-lang=&#34;nginx&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;503&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#xA;&lt;li&gt;Создаем пустой конфиг-файл &lt;code&gt;maintenance_off.conf&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;Создаем симлинк &lt;code&gt;maintenance_current.conf&lt;/code&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ln -sfn /etc/nginx/maintenance/maintenance_off.conf /etc/nginx/maintenance/maintenance_current.conf&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Нужно добавить немного строк в nginx-конфиг в блок server:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-nginx&#34; data-lang=&#34;nginx&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;server&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;kn&#34;&gt;...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s&#34;&gt;error_page&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;503&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;/maintenance.html&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;location&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;/maintenance.html&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kn&#34;&gt;internal&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kn&#34;&gt;root&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;/etc/nginx/maintenance/maintenance.html&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kn&#34;&gt;add_header&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;Cache-Control&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;no-store&amp;#34;&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;always&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kn&#34;&gt;add_header&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;Retry-After&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;3600&amp;#34;&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;always&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;location&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kn&#34;&gt;include&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;/etc/nginx/maintenance/maintenance_current.conf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kn&#34;&gt;...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;err&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Что здесь происходит:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Мы говорим, чтобы Nginx редиректил на &lt;code&gt;/maintenance.html&lt;/code&gt; при 503 ошибке&lt;/li&gt;&#xA;&lt;li&gt;Настраиваем локейшен &lt;code&gt;/maintenance.html&lt;/code&gt;, который отвечает за раздачу нашей сервисной странички. &lt;code&gt;internal&lt;/code&gt; нужен, чтобы клиент не мог вообще никак запросить &lt;code&gt;/maintenance_on.conf&lt;/code&gt; или любой другой файл.&lt;/li&gt;&#xA;&lt;li&gt;Добавляем заголовки, чтобы клиентский браузер не кэшировал страничку, а также &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Retry-After&#34;   target=&#34;_blank&#34;&gt;&#xA;    Retry-After&lt;/a&gt;.&lt;/li&gt;&#xA;&lt;li&gt;В основной location (или в любой другой) добавляем &lt;code&gt;include&lt;/code&gt; нашего симлинка.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Таким образом, чтобы включить maintenance-режим нам нужно только поменять симлинк и мягко перезапустить Nginx:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ln -sfn /etc/nginx/maintenance/maintenance_on.conf /etc/nginx/maintenance/maintenance_current.conf&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;nginx -t &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; systemctl reload nginx&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Для выключения создаем линк на &lt;code&gt;_off&lt;/code&gt;-файл и опять перезапускаем Nginx.&lt;/p&gt;&#xA;&lt;p&gt;Как автоматизировать - сами придумайте.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Sentry сожрал все место на диске</title>
      <link>https://etogeek.dev/posts/sentry-self-hosted-ram-and-disk/</link>
      <pubDate>Wed, 01 Oct 2025 00:00:00 +0000</pubDate>
      <guid>https://etogeek.dev/posts/sentry-self-hosted-ram-and-disk/</guid>
      <description>&lt;p&gt;Если вы когда-нибудь поднимали &lt;a href=&#34;https://develop.sentry.dev/self-hosted/&#34;   target=&#34;_blank&#34;&gt;&#xA;    self-hosted (on premise) версию Sentry&lt;/a&gt;, то знаете, что лезть туда очередной раз сильно не хочется: там ~38 контейнеров. Но иногда возникают нештатные ситуации.&lt;/p&gt;&#xA;&lt;p&gt;Пару раз у меня случалось так, что какой-то сервис начинает сбоить и без конца отправлять в Sentry ошибки и евенты. Огромное количество ошибок (десятки и сотни тысяч). В такой ситуации я наблюдал два варианта развития событий:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Стремитально заканчивается место на диске:&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/sentry-self-hosted-ram-and-disk/image.png&#34;&#xA;        alt=&#34;alt text&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Запредельно возрастает нагрузка на оперативную память:&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/sentry-self-hosted-ram-and-disk/image-1.png&#34;&#xA;        alt=&#34;alt text&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Закончилось место на диске &#xA;    &lt;div id=&#34;%D0%B7%D0%B0%D0%BA%D0%BE%D0%BD%D1%87%D0%B8%D0%BB%D0%BE%D1%81%D1%8C-%D0%BC%D0%B5%D1%81%D1%82%D0%BE-%D0%BD%D0%B0-%D0%B4%D0%B8%D1%81%D0%BA%D0%B5&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%B7%D0%B0%D0%BA%D0%BE%D0%BD%D1%87%D0%B8%D0%BB%D0%BE%D1%81%D1%8C-%D0%BC%D0%B5%D1%81%D1%82%D0%BE-%D0%BD%D0%B0-%D0%B4%D0%B8%D1%81%D0%BA%D0%B5&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Проверяем место на дисках, смотрим какие директории(вольюмы) больше всего жрут места:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;du -Sh / &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; sort -rh &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; head -&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Если в выводе на первых местах будут вольюмы &lt;code&gt;sentry-kafka&lt;/code&gt; - значит двигаемся в верном направлении. Получается, что наши эвенты попали в Kafka, но еще не пошли дальше в сторону основной базы.&lt;/p&gt;&#xA;&#xA;  &#xA;&#xA;&#xA;&#xA;&lt;div&#xA;  &#xA;    class=&#34;flex px-4 py-3 rounded-md bg-primary-100 dark:bg-primary-900&#34;&#xA;  &gt;&#xA;&#xA;  &lt;span&#xA;    &#xA;      class=&#34;text-primary-400 ltr:pr-3 rtl:pl-3 flex items-center&#34;&#xA;    &gt;&#xA;&#xA;    &#xA;&#xA;  &lt;span class=&#34;relative block icon&#34;&gt;&#xA;    &lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; viewBox=&#34;0 0 512 512&#34;&gt;&lt;path fill=&#34;currentColor&#34; d=&#34;M506.3 417l-213.3-364c-16.33-28-57.54-28-73.98 0l-213.2 364C-10.59 444.9 9.849 480 42.74 480h426.6C502.1 480 522.6 445 506.3 417zM232 168c0-13.25 10.75-24 24-24S280 154.8 280 168v128c0 13.25-10.75 24-23.1 24S232 309.3 232 296V168zM256 416c-17.36 0-31.44-14.08-31.44-31.44c0-17.36 14.07-31.44 31.44-31.44s31.44 14.08 31.44 31.44C287.4 401.9 273.4 416 256 416z&#34;/&gt;&lt;/svg&gt;&#xA;&#xA;  &lt;/span&gt;&#xA;&#xA;&#xA;  &lt;/span&gt;&#xA;&#xA;  &lt;span&#xA;    &#xA;      class=&#34;dark:text-neutral-300&#34;&#xA;    &gt;Я во все команды &lt;code&gt;docker compose&lt;/code&gt; добавляю &lt;code&gt;--env-file .env.custom&lt;/code&gt;, так как он используется для запуска моего инстанса Sentry.&lt;/span&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&lt;p&gt;Если вы перед этим выключили Sentry полностью, то сейчас стоит включить только Kafka:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker compose --env-file .env.custom up -d kafka&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Заходим внутрь контейнера с Kafka и смотрим размер топиков:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker compose --env-file .env.custom &lt;span class=&#34;nb&#34;&gt;exec&lt;/span&gt; -ti kafka bash&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;kafka-log-dirs --describe --bootstrap-server localhost:9092 --topic-list ingest-events&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;kafka-log-dirs --describe --bootstrap-server localhost:9092 --topic-list events&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Чтобы освободить место занятое евентами, которые лежат в топиках, надо пойти немного хитрым способом: мы установим retention на очень маленькое значение (1 секунда), а потом вернем обратно.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# устанавливаем retention&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;kafka-configs --zookeeper zookeeper:2181 --entity-type topics --alter --entity-name ingest-events --add-config retention.ms&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1000&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;kafka-configs --zookeeper zookeeper:2181 --entity-type topics --alter --entity-name events --add-config retention.ms&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1000&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# ждем пока освободится место (1-10 минут)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# откатываем назад&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;kafka-configs --zookeeper zookeeper:2181 --entity-type topics --alter --entity-nameingest-events--delete-config retention.ms&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;kafka-configs --zookeeper zookeeper:2181 --entity-type topics --alter --entity-nameevents--delete-config retention.ms&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Теперь нужно сбросить offset-ы в топиках:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;kafka-consumer-groups --bootstrap-server kafka:9092 &lt;span class=&#34;se&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;  --group snuba-consumers --topic ingest-events &lt;span class=&#34;se&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;  --reset-offsets --to-latest --execute&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;kafka-consumer-groups --bootstrap-server kafka:9092 &lt;span class=&#34;se&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;  --group ingest-consumer --topic ingest-events &lt;span class=&#34;se&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;  --reset-offsets --to-latest --execute&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;kafka-consumer-groups --bootstrap-server kafka:9092 &lt;span class=&#34;se&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;  --group post-process-forwarder --topic events &lt;span class=&#34;se&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;  --reset-offsets --to-latest --execute&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;  &#xA;&#xA;&#xA;&#xA;&lt;div&#xA;  &#xA;    class=&#34;flex px-4 py-3 rounded-md bg-primary-100 dark:bg-primary-900&#34;&#xA;  &gt;&#xA;&#xA;  &lt;span&#xA;    &#xA;      class=&#34;text-primary-400 ltr:pr-3 rtl:pl-3 flex items-center&#34;&#xA;    &gt;&#xA;&#xA;    &#xA;&#xA;  &lt;span class=&#34;relative block icon&#34;&gt;&#xA;    &lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; viewBox=&#34;0 0 512 512&#34;&gt;&lt;path fill=&#34;currentColor&#34; d=&#34;M506.3 417l-213.3-364c-16.33-28-57.54-28-73.98 0l-213.2 364C-10.59 444.9 9.849 480 42.74 480h426.6C502.1 480 522.6 445 506.3 417zM232 168c0-13.25 10.75-24 24-24S280 154.8 280 168v128c0 13.25-10.75 24-23.1 24S232 309.3 232 296V168zM256 416c-17.36 0-31.44-14.08-31.44-31.44c0-17.36 14.07-31.44 31.44-31.44s31.44 14.08 31.44 31.44C287.4 401.9 273.4 416 256 416z&#34;/&gt;&lt;/svg&gt;&#xA;&#xA;  &lt;/span&gt;&#xA;&#xA;&#xA;  &lt;/span&gt;&#xA;&#xA;  &lt;span&#xA;    &#xA;      class=&#34;dark:text-neutral-300&#34;&#xA;    &gt;Но сброс оффсетов может не помочь. Читай раздел ниже про траблшутинг.&lt;/span&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&lt;p&gt;На этом вроде всё.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Забита вся оперативная память &#xA;    &lt;div id=&#34;%D0%B7%D0%B0%D0%B1%D0%B8%D1%82%D0%B0-%D0%B2%D1%81%D1%8F-%D0%BE%D0%BF%D0%B5%D1%80%D0%B0%D1%82%D0%B8%D0%B2%D0%BD%D0%B0%D1%8F-%D0%BF%D0%B0%D0%BC%D1%8F%D1%82%D1%8C&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%B7%D0%B0%D0%B1%D0%B8%D1%82%D0%B0-%D0%B2%D1%81%D1%8F-%D0%BE%D0%BF%D0%B5%D1%80%D0%B0%D1%82%D0%B8%D0%B2%D0%BD%D0%B0%D1%8F-%D0%BF%D0%B0%D0%BC%D1%8F%D1%82%D1%8C&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Это ситуация, когда евенты прошли дальше Kafka, уже в Redis. Это видно по тому, что, собственно, Redis начинает использовать очень много оперативки. Можно посмотреть через &lt;code&gt;htop&lt;/code&gt;, &lt;code&gt;docker stats&lt;/code&gt; или &lt;code&gt;ctop&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Проблема в том, что Redis - это in-memory хранилище, и если значений становится очень много, они перестают умещаться в память, а дальше цепная реакция приводящая к приходу OOM-Killer.&lt;/p&gt;&#xA;&lt;p&gt;Здесь решение комплексное:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# идем в контейнер с Redis&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker compose --env-file .env.custom &lt;span class=&#34;nb&#34;&gt;exec&lt;/span&gt; -ti redis sh&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# смотрим какой сейчас eviction policy настроен&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;redis-cli info &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; grep maxmemory_policy&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# скорее всего там стоит noeviction, что значит, что Redis будет использовать всю доступную оперативку, а потом перестанет обрабатывать новые запросы&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# поменяем тип политики, и установим лимит maxmemory, на что-то разумное&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;redis-cli CONFIG SETmaxmemory-policy volatile-ttl&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;redis-cli CONFIG SETmaxmemory 7G&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;  &#xA;&#xA;&#xA;&#xA;&lt;div&#xA;  &#xA;    class=&#34;flex px-4 py-3 rounded-md bg-primary-100 dark:bg-primary-900&#34;&#xA;  &gt;&#xA;&#xA;  &lt;span&#xA;    &#xA;      class=&#34;text-primary-400 ltr:pr-3 rtl:pl-3 flex items-center&#34;&#xA;    &gt;&#xA;&#xA;    &#xA;&#xA;  &lt;span class=&#34;relative block icon&#34;&gt;&#xA;    &lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; viewBox=&#34;0 0 512 512&#34;&gt;&lt;path fill=&#34;currentColor&#34; d=&#34;M506.3 417l-213.3-364c-16.33-28-57.54-28-73.98 0l-213.2 364C-10.59 444.9 9.849 480 42.74 480h426.6C502.1 480 522.6 445 506.3 417zM232 168c0-13.25 10.75-24 24-24S280 154.8 280 168v128c0 13.25-10.75 24-23.1 24S232 309.3 232 296V168zM256 416c-17.36 0-31.44-14.08-31.44-31.44c0-17.36 14.07-31.44 31.44-31.44s31.44 14.08 31.44 31.44C287.4 401.9 273.4 416 256 416z&#34;/&gt;&lt;/svg&gt;&#xA;&#xA;  &lt;/span&gt;&#xA;&#xA;&#xA;  &lt;/span&gt;&#xA;&#xA;  &lt;span&#xA;    &#xA;      class=&#34;dark:text-neutral-300&#34;&#xA;    &gt;Эти настройки слетят после перезапуска контейнера, поэтому их следует закрепить в конфиг файле.&lt;/span&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;h3 class=&#34;relative group&#34;&gt;Добавляем конфиг-файл для Redis &#xA;    &lt;div id=&#34;%D0%B4%D0%BE%D0%B1%D0%B0%D0%B2%D0%BB%D1%8F%D0%B5%D0%BC-%D0%BA%D0%BE%D0%BD%D1%84%D0%B8%D0%B3-%D1%84%D0%B0%D0%B9%D0%BB-%D0%B4%D0%BB%D1%8F-redis&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%B4%D0%BE%D0%B1%D0%B0%D0%B2%D0%BB%D1%8F%D0%B5%D0%BC-%D0%BA%D0%BE%D0%BD%D1%84%D0%B8%D0%B3-%D1%84%D0%B0%D0%B9%D0%BB-%D0%B4%D0%BB%D1%8F-redis&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h3&gt;&#xA;&lt;p&gt;Создаем директорию и файл для конфига:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mkdir redis&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;nano redis/redis.conf&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# туда вставляем настройки&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;maxmemory 7G&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;maxmemory-policy volatile-ttl&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Теперь надо этот файл подмонтировать в контейнер с Redis. Сделаем это через создание override файла &lt;code&gt;nano docker-compose.override.yml&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yml&#34; data-lang=&#34;yml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;services&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;redis&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;command&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;redis-server&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;/usr/local/etc/redis/redis.conf&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;volumes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;./redis/redis.conf:/usr/local/etc/redis/redis.conf&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Теперь перезапускаем Redis и проверяем, что настройки подцепились. Можно через docker inspect, но еще лучше будет зайти внутри контейнера и поgrep-ать их из &lt;code&gt;redis-cli info&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Я еще делал &lt;code&gt;redis-cli flushall&lt;/code&gt; чтобы очистить весь кэш и освободить память.&lt;/p&gt;&#xA;&lt;p&gt;После всех процедур обязательно проверяйте, что контейнеры запущены и не рестартуются, что новые эвенты прилетают.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Траблшутинг Kafka после очистки топиков &#xA;    &lt;div id=&#34;%D1%82%D1%80%D0%B0%D0%B1%D0%BB%D1%88%D1%83%D1%82%D0%B8%D0%BD%D0%B3-kafka-%D0%BF%D0%BE%D1%81%D0%BB%D0%B5-%D0%BE%D1%87%D0%B8%D1%81%D1%82%D0%BA%D0%B8-%D1%82%D0%BE%D0%BF%D0%B8%D0%BA%D0%BE%D0%B2&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D1%82%D1%80%D0%B0%D0%B1%D0%BB%D1%88%D1%83%D1%82%D0%B8%D0%BD%D0%B3-kafka-%D0%BF%D0%BE%D1%81%D0%BB%D0%B5-%D0%BE%D1%87%D0%B8%D1%81%D1%82%D0%BA%D0%B8-%D1%82%D0%BE%D0%BF%D0%B8%D0%BA%D0%BE%D0%B2&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;После всех этих процедур эпопея с Kafka не закончилась. Некоторые consumers со временем начинали вылетать с ошибками в логе:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;arroyo.errors.OffsetOutOfRange: KafkaError{code=_AUTO_OFFSET_RESET,val=-140,str=&amp;#34;fetch failed due to requested offset not available on the broker: Broker: Offset out of range (broker 1001)&amp;#34;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Как бы в этой ситуации надо остановить consumer-ы, сбросить значение оффсетов как мы делали выше, запуститься обратно. Но мне не помогло. Пошел другим путем и начал удалять группы полностью:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;kafka-consumer-groups --bootstrap-server kafka:9092 --delete --group ingest-consumer&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;kafka-consumer-groups --bootstrap-server kafka:9092 --delete --group snuba-consumers&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;kafka-consumer-groups --bootstrap-server kafka:9092 --delete --group post-process-forwarder&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;kafka-consumer-groups --bootstrap-server kafka:9092 --delete --group post-process-forwarder-errors&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;kafka-consumer-groups --bootstrap-server kafka:9092 --delete --group post-process-forwarder-transactions&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Тоже не помогло, через некоторое время консюмеры начали рестартиться.&lt;/p&gt;&#xA;&lt;p&gt;Более мощный способ - это грохнуть volume &lt;strong&gt;kafka и zookeeper&lt;/strong&gt; полностью через &lt;code&gt;docker volume rm&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker compose --env-file .env.custom down&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker volume rm sentry-kafka sentry-zookeeper&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker volume rm ah_sentry_sentry-kafka-log ah_sentry_sentry-zookeeper-log&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;После этого надо создавать топики заново, и в моем случае &lt;strong&gt;самым простым способом оказалось прогнать &lt;code&gt;./install.sh&lt;/code&gt; скрипт заново&lt;/strong&gt;. Он учитывает наличие .env.custom, не трогает Postgres и в целом очень мягко бутстрапит заново все что нужно.&lt;/p&gt;&#xA;&lt;p&gt;Reference: часть гайда взята из &lt;a href=&#34;https://yuretspro.medium.com/on-premise-sentry-under-the-high-load-how-to-stay-up-running-be6a0a33791f&#34;   target=&#34;_blank&#34;&gt;&#xA;    статьи Юрия на Medium&lt;/a&gt;. Спасибо ему большое. Статья уже не раз мне помогала.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Как я использую Ansible для управления инфраструктурой?</title>
      <link>https://etogeek.dev/posts/ansible-iac/</link>
      <pubDate>Tue, 22 Apr 2025 00:00:00 +0000</pubDate>
      <guid>https://etogeek.dev/posts/ansible-iac/</guid>
      <description>&lt;p&gt;Если вы давно читаете этот канал, то точно знаете, что я люблю Ansible и использую его для управления инфрой на текущих проектах.&lt;/p&gt;&#xA;&lt;p&gt;Здесь я хотел рассказать как именно я структурирую inventory-файлы, переменные, роли и как это все запускаю. Может это не супер-бест-практисы, но это работает довольно давно, неплохо поддерживается. У Ansible есть свои минусы, но тут не об этом.&lt;/p&gt;&#xA;&#xA;  &#xA;&#xA;&#xA;&#xA;&lt;div&#xA;  &#xA;    class=&#34;flex px-4 py-3 rounded-md bg-primary-100 dark:bg-primary-900&#34;&#xA;  &gt;&#xA;&#xA;  &lt;span&#xA;    &#xA;      class=&#34;text-primary-400 ltr:pr-3 rtl:pl-3 flex items-center&#34;&#xA;    &gt;&#xA;&#xA;    &#xA;&#xA;  &lt;span class=&#34;relative block icon&#34;&gt;&#xA;    &lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; viewBox=&#34;0 0 512 512&#34;&gt;&lt;path fill=&#34;currentColor&#34; d=&#34;M506.3 417l-213.3-364c-16.33-28-57.54-28-73.98 0l-213.2 364C-10.59 444.9 9.849 480 42.74 480h426.6C502.1 480 522.6 445 506.3 417zM232 168c0-13.25 10.75-24 24-24S280 154.8 280 168v128c0 13.25-10.75 24-23.1 24S232 309.3 232 296V168zM256 416c-17.36 0-31.44-14.08-31.44-31.44c0-17.36 14.07-31.44 31.44-31.44s31.44 14.08 31.44 31.44C287.4 401.9 273.4 416 256 416z&#34;/&gt;&lt;/svg&gt;&#xA;&#xA;  &lt;/span&gt;&#xA;&#xA;&#xA;  &lt;/span&gt;&#xA;&#xA;  &lt;span&#xA;    &#xA;      class=&#34;dark:text-neutral-300&#34;&#xA;    &gt;&lt;strong&gt;Это НЕ гайд «как надо делать».&lt;/strong&gt;&lt;br&gt;&#xA;Здесь не будет пошаговых инструкций. Здесь — демонстрация того, как делаю я. Может быть это даст вам каких-нибудь идей, или вы захотите поделиться своим мнением в комментариях в Телеграм-чате.&lt;/span&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&lt;p&gt;В интернете over9000 гайдов типа создаете группу хостов web-servers, создаете плейбук, который запускаете на этой группе и устанавливаете nginx. Для базы данных тоже самое. &lt;strong&gt;Сколько таких плейбуков и групп получится, если у вас 100 серверов с разным наполнением&lt;/strong&gt;? Нужно что-то другое.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Роли &#xA;    &lt;div id=&#34;%D1%80%D0%BE%D0%BB%D0%B8&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D1%80%D0%BE%D0%BB%D0%B8&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Во-первых: роли. &lt;strong&gt;Роль — это набор заданий, параменных, шаблонов которые объединены в отдельный модуль.&lt;/strong&gt; Например роль для установки и настройки Nginx, роль для управления пользователями, роль для установки и настройки PostgreSQL.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Роль должна выполнять одну задачу.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Роль должна быть универсальной&lt;/strong&gt;: мы можем запустить ее на любом хосте и она сделает то, что должна.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;С помощью переменных в роль можно передавать различные настройки.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;В основном все роли я пишу сам, а не использую готовые из коммьюнити. Мне кажется, что коммьюнити-роли перегружены дополнительными параметрами. Я могу взять что-нибудь интересное из них, это да. Но в основном — пишу сам под свои требования.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Inventory &#xA;    &lt;div id=&#34;inventory&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#inventory&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Во-вторых: inventory. Про него я уже писал. Вот &lt;a href=&#34;https://etogeek.dev/posts/best-practice-ansible/&#34;   target=&#34;_blank&#34;&gt;&#xA;    тут&lt;/a&gt;. Инвентарь — это файлы, где мы описываем нашу инфраструктуру. Базово — это список групп и хостов, на которых будут запускаться наши плейбуки. Но так же в inventory мы можем указывать переменные для наших хостов. Тут и начинается iNfRaStRuCtUrE aS cOdE.&lt;/p&gt;&#xA;&lt;p&gt;Напомню структуру инвентори здорового человека:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ansible/inventories&#xA;├── dev&#xA;│   ├── dev.yml&#xA;│   ├── group_vars&#xA;│   │   ├── all.yml&#xA;│   └── host_vars&#xA;│       ├── server00dev.et.yml&#xA;│       └── frontend01dev.et&#xA;│           ├── common.yml&#xA;│           ├── logrotate.yml&#xA;│           └── promtail.yml&#xA;├── dwh_cluster&#xA;│   ├── dwh.yml&#xA;│   ├── group_vars&#xA;│   │   ├── all.yml&#xA;│   │   ├── dwh_cluster.yml&#xA;│   │   └── dwh_cluster_wo_dwh00.yml&#xA;│   └── host_vars&#xA;│       ├── dwh00.et&#xA;│       │   ├── backups.yml&#xA;│       │   ├── blackbox-exporter.yml&#xA;│       │   └── common.yml&#xA;│       ├── dwh01.et.yml&#xA;│       └── dwh50.et.yml&#xA;...&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Я держу поддиректории под окружения. В каждой поддиректории есть &lt;code&gt;.yml&lt;/code&gt; файл, который содержит только список хостов. Максимум туда добавляем настройки подключения, например &lt;code&gt;ansible_host&lt;/code&gt;, etc&amp;hellip;:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;all:&#xA;  children:&#xA;    server00:&#xA;      hosts:&#xA;        server00dev.et:&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Есть директории &lt;code&gt;host_vars&lt;/code&gt; и &lt;code&gt;group_vars&lt;/code&gt; в которых хранятся файлы с названиями хостов из инвентори, в них находятся все переменные-настройки хостов. Так же если &lt;code&gt;host_var&lt;/code&gt;-файл разрастается и становится сложно его поддерживать, его можно превратить в директорию с несколькими файлами (см. выше в примере).&lt;/p&gt;&#xA;&lt;p&gt;Что в host_var-файле у меня есть интересного? Одна из самых важных, на мой взгляд, переменных:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;host_roles:&#xA;  - openjdk&#xA;  - promtail&#xA;  - haproxy&#xA;  - postgresql&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Она указывает КАКИЕ РОЛИ нужно запустить на ЭТОМ хосте.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;Так же есть, например, переменная &lt;code&gt;users&lt;/code&gt;, в которой я перечисляю дополнительных пользователей, которых нужно создать на сервере. В этом же файле я указываю &amp;ldquo;настройки&amp;rdquo; для моих ролей, например:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;postgresql_version: 16&#xA;postgresql_global_config_options:&#xA;  - { option: listen_addresses, value: &amp;#39;0.0.0.0&amp;#39; }&#xA;  - { option: max_connections, value: &amp;#39;500&amp;#39; }&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;или&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;iptables_additional_rules:&#xA;  - -A INPUT -s 172.16.0.0/12 -m comment --comment &amp;#34;allow from docker containers&amp;#34; -j ACCEPT&#xA;  - -A INPUT -s 10.40.12.0/24 -m comment --comment &amp;#34;allow from vpn network&amp;#34; -j ACCEPT&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;В последнее время я иду к тому, что в inventory указываю полноценные конфиги для сервисов, например для promtail. Прямо внутри переменной описываю конфигурацию, а роль уже добавит этот конфиг к дефолтному:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;promtail_config_scrape_configs:&#xA;  - job_name: backend_dev_rest&#xA;    static_configs:&#xA;      - targets:&#xA;          - localhost&#xA;        labels:&#xA;          job: backend_dev_rest&#xA;...&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Это позволяет мне иметь при необходимости уникальный (кастомный) конфиг для какого-либо сервиса вместо оригинально (или в дополнение к нему). Например на всех хостах у меня есть дефолтный конфиг promtail - он указан в &lt;code&gt;roles/promtail/defaults&lt;/code&gt;, а в &lt;code&gt;host_vars&lt;/code&gt; я его дополняю.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Плейбук &#xA;    &lt;div id=&#34;%D0%BF%D0%BB%D0%B5%D0%B9%D0%B1%D1%83%D0%BA&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%BF%D0%BB%D0%B5%D0%B9%D0%B1%D1%83%D0%BA&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;В-третьих: &lt;strong&gt;как это запускать?&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;У меня есть &amp;ldquo;общий&amp;rdquo; плейбук &lt;code&gt;common.yml&lt;/code&gt;, в котором по сути есть всего один таск &lt;code&gt;include_role&lt;/code&gt;. В этот таск предается список ролей содержащий:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;обязательные роли&lt;/strong&gt;, которые должны быть прокатаны на всех хостах, например &lt;code&gt;node_exporter&lt;/code&gt;, &lt;code&gt;zabbix_agent&lt;/code&gt;, настройки ssh, установка правильного хостнейма, настройки DNS, NTP и так далее.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;роли из переменной &lt;code&gt;host_roles&lt;/code&gt;&lt;/strong&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Так как в host_roles у меня лежит &lt;strong&gt;список&lt;/strong&gt; &lt;em&gt;(тип данных)&lt;/em&gt; ролей, то этот список просто расширит уже имеющийся список дефолтных. Я добавляю jinja-фильтр &lt;code&gt;default([])&lt;/code&gt;, чтобы смержить туда пустой список, если переменная &lt;code&gt;host_roles&lt;/code&gt; вообще не задана, иначе будет ошибка. Итогово это выглядит так:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;- name: All hosts playbook&#xA;  hosts: all&#xA;  become: true&#xA;  gather_subset: [&amp;#39;default_ipv4&amp;#39;, &amp;#39;distribution_release&amp;#39;]&#xA;  ignore_unreachable: false&#xA;&#xA;  tasks:&#xA;    - name: Include roles&#xA;      ansible.builtin.include_role:&#xA;        name: &amp;#34;{{ item }}&amp;#34;&#xA;      loop:&#xA;        - common_software&#xA;        - users&#xA;        - &amp;#34;{{ host_roles | default([]) }}&amp;#34;&#xA;        - &amp;#34;{{ group_roles | default([]) }}&amp;#34;&#xA;        - ntp&#xA;        - node-exporter&#xA;        - zabbix_agent&#xA;        - sshd&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Теперь, если я хочу настроить сервер с нуля &lt;em&gt;(ну, не совсем с нуля, конечно)&lt;/em&gt;, то я просто запускаю:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ansible-playbook common.yml -i inventories/dev -l server00dev.et -D&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;И он прокатится только на одном сервере. Уберу &lt;code&gt;-l server00dev.et&lt;/code&gt;, и плейбук запустится на всех дев-серверах.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Автоматизация &#xA;    &lt;div id=&#34;%D0%B0%D0%B2%D1%82%D0%BE%D0%BC%D0%B0%D1%82%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%B0%D0%B2%D1%82%D0%BE%D0%BC%D0%B0%D1%82%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;В-четвертых: как это автоматизировать?&#xA;Это немного не укладывается в рамки стандартного CI/CD (где сборка/загрузка/деплой) - я скорее про автоматическую раскатку этого поделия на серверы.&lt;/p&gt;&#xA;&lt;p&gt;Предположим, что мы описали и добавили все наши серверы в inventory, разделили его на окружения: &lt;code&gt;inventories/dev/dev.yml&lt;/code&gt;, &lt;code&gt;inventories/dev/prod.yml&lt;/code&gt;. В host_vars и group_vars добавили нужные нам роли для хостов, конфиги и параметры для сервисов. Как будем это запускать?&lt;/p&gt;&#xA;&lt;p&gt;Весь инфраструтурный код я храню в отдельном репозитории, который гордо обозвал &lt;code&gt;ansible&lt;/code&gt;. Я хочу, чтобы при изменении кода у меня автоматически запускался &lt;code&gt;ansible-playbook common.yml&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Я себе выбрал такие условия:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;если меняется что-то в &lt;code&gt;inventories/dev&lt;/code&gt;, то запустить common.yml с параметром -i inventories.dev&lt;/li&gt;&#xA;&lt;li&gt;аналогично для prod-окружения&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Так как я использую Gitlab, то у меня есть &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; файл. В нем я описываю условия, которые стриггерят запуск плейбука:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;deploy_dev:&#xA;  stage: deploy&#xA;  before_script:&#xA;    - mkdir -p ~/.ssh&#xA;    - echo &amp;#34;$ADMIN_SSH_PRIVATE_KEY&amp;#34; | base64 -d &amp;gt; ~/.ssh/id_rsa&#xA;    - chmod 600 ~/.ssh/id_rsa&#xA;  script:&#xA;    - ansible-playbook -i inventories/dev common.yml --diff&#xA;  rules:&#xA;    - if: &amp;#39;$CI_PIPELINE_SOURCE == &amp;#34;web&amp;#34;&amp;#39;&#xA;      when: manual&#xA;    - if: &amp;#39;$CI_COMMIT_BRANCH == &amp;#34;master&amp;#34;&amp;#39;&#xA;      changes:&#xA;        - &amp;#39;inventories/dev/**/*&amp;#39;&#xA;        - &amp;#39;roles/node-exporter/**/*&amp;#39;&#xA;        - &amp;#39;roles/users/**/*&amp;#39;&#xA;        ...&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Таким образом этот пайплайн запустится автоматически в случае изменений в файлах под &lt;code&gt;inventories/dev&lt;/code&gt;, а так же при изменениях в некоторых ролях — например я глобально обновил версию node-exporter в &lt;code&gt;defaults&lt;/code&gt;.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Общий workflow (реальный и идеальный) &#xA;    &lt;div id=&#34;%D0%BE%D0%B1%D1%89%D0%B8%D0%B9-workflow-%D1%80%D0%B5%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D0%B9-%D0%B8-%D0%B8%D0%B4%D0%B5%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D0%B9&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%BE%D0%B1%D1%89%D0%B8%D0%B9-workflow-%D1%80%D0%B5%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D0%B9-%D0%B8-%D0%B8%D0%B4%D0%B5%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D0%B9&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Инженер клонирует себе репозиторий&lt;/li&gt;&#xA;&lt;li&gt;Создает отдельную ветку, вносит необходимые изменения.&lt;/li&gt;&#xA;&lt;li&gt;Проверяет через check-mode со своего компьютера — вот этот пункт - плохой паттерн, так как он требует доступа на сервера, и может вызывать configuration drift. Проверку должен выполнять CI.&lt;/li&gt;&#xA;&lt;li&gt;Пушит код в Gitlab, создает MR.&lt;/li&gt;&#xA;&lt;li&gt;В Gitlab проходят тесты (идеальный вариант), другой инженер проводит код-ревью.&lt;/li&gt;&#xA;&lt;li&gt;MR мержится в master-ветку&lt;/li&gt;&#xA;&lt;li&gt;Gitlab запускает соответствующий пайплайн, чтобы привести инфрастркутуру к целевому состоянию&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Что надо добавлять? &#xA;    &lt;div id=&#34;%D1%87%D1%82%D0%BE-%D0%BD%D0%B0%D0%B4%D0%BE-%D0%B4%D0%BE%D0%B1%D0%B0%D0%B2%D0%BB%D1%8F%D1%82%D1%8C&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D1%87%D1%82%D0%BE-%D0%BD%D0%B0%D0%B4%D0%BE-%D0%B4%D0%BE%D0%B1%D0%B0%D0%B2%D0%BB%D1%8F%D1%82%D1%8C&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Тестирование. Как минимум — прогон через check-mode. Как идеал — тестирование ролей на временной инфрастркутуре.&lt;/li&gt;&#xA;&lt;li&gt;Линтеры. Нужно прогонять код, чтобы он соответствовал правильному синтаксису.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;Было полезно? Может появились какие-то идеи? Или вы делаете у себя в инфрастркутуре по-другому? Поделись этим в 👉 &lt;a href=&#34;https://t.me/etogeekchat&#34;   target=&#34;_blank&#34;&gt;&#xA;    телеграм-чате&lt;/a&gt;! Мне очень интересно было бы почитать.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Настройка ansible.cfg. Зачем и как?</title>
      <link>https://etogeek.dev/posts/ansible-cfg/</link>
      <pubDate>Mon, 13 Jan 2025 00:00:00 +0000</pubDate>
      <guid>https://etogeek.dev/posts/ansible-cfg/</guid>
      <description>&lt;p&gt;&lt;code&gt;ansible.cfg&lt;/code&gt; — это файл с различными настройками для Ansible. По умолчанию Ansible &lt;a href=&#34;https://docs.ansible.com/ansible/latest/reference_appendices/config.html#the-configuration-file&#34;   target=&#34;_blank&#34;&gt;&#xA;    будет искать&lt;/a&gt; этот файл в разных местах.&lt;/p&gt;&#xA;&lt;p&gt;Если мы храним наши ansible-плейбуки в git-репозитории и прокатываем их автоматизированно, с помощью CI-системы (Jenkins, Gitlab CI, etc), то мы можем хранить конфиг-файл прямо в корне нашего репозитория.&lt;/p&gt;&#xA;&lt;p&gt;С помощью команды ansible-config init &amp;ndash;disabled &amp;gt; ansible.cfg можно сгенерить файл со всеми настройками, но в закомментированном виде.&lt;/p&gt;&#xA;&lt;p&gt;Конфиг описывается в ini-формате и разбит на категории, вроде [defaults]. Все эти настройки можно так же передать программе через переменные окружения, типа &lt;code&gt;ANSIBLE_PIPELINING=True&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;Вот пример моего &lt;code&gt;ansible.cfg&lt;/code&gt; файла:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[defaults]&#xA;host_key_checking=False&#xA;pipelining = True&#xA;strategy = free&#xA;callbacks_enabled = timer, profile_roles&#xA;forks = 40&#xA;deprecation_warnings=False&#xA;stdout_callback = yaml&#xA;&#xA;[ssh_connection]&#xA;ssh_args = -o ControlMaster=auto -o ControlPersist=60s&#xA;retries=3&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;host_key_checking&lt;/code&gt; - отвечает за проверку host keys. ⚠️ Небезопасно, используйте с осторожностью. (ну, я должен это написать)&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;pipelining&lt;/code&gt; - ansible поднимает ssh-сессию для каждой команды. Pipelining позволяет протолкнуть сразу несколько команд через одну сессию. Это существенно может ускорить выполнение заданий, но &lt;a href=&#34;https://docs.ansible.com/ansible/latest/reference_appendices/config.html#ansible-pipelining&#34;   target=&#34;_blank&#34;&gt;&#xA;    разработчики предупреждают&lt;/a&gt;, что это может вызывать проблемы, если на серверах включен requiretty в /etc/sudoers.&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;strategy&lt;/code&gt; - &lt;a href=&#34;https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_strategies.html#selecting-a-strategy&#34;   target=&#34;_blank&#34;&gt;&#xA;    стратегия выполнения тасок&lt;/a&gt;. &lt;code&gt;free&lt;/code&gt; позволяет выполнять таски на разных хостах асинхронно. Например у тебя есть task1, task2, task3. По умолчанию Ansible будет ждать, пока task1 завершится на всех хостах.&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;callbacks_enabled&lt;/code&gt; - включает различные &lt;a href=&#34;https://docs.ansible.com/ansible/latest/plugins/callback.html&#34;   target=&#34;_blank&#34;&gt;&#xA;    callback плагины&lt;/a&gt;. Например, для отображения времени выполнения тасок.&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;forks&lt;/code&gt; - указываем максимальное количество параллельных подключений. По умолчанию - 5, что довольно мало. Увеличение этого числа сильно ускоряет выполнение плейбуков на больших инвентори, но и увеличивает нагрузку на процессор.&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;deprecation_warnings&lt;/code&gt; - отключаем различные уведомления о deprecated функционале.&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;stdout_callback = yaml&lt;/code&gt; - помогает лучше отформатировать вывод ошибок и дебаг-команд. &lt;a href=&#34;https://www.jeffgeerling.com/blog/2018/use-ansibles-yaml-callback-plugin-better-cli-experience&#34;   target=&#34;_blank&#34;&gt;&#xA;    Подробнее&lt;/a&gt;.&lt;/li&gt;&#xA;&lt;li&gt;Под группой &lt;code&gt;[ssh_connection]&lt;/code&gt; содержатся различные настройки для SSH-подключений, примерно такие же какие мы можем указать в ssh-config файле.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Так же в переменных окружения я передаю:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;ANSIBLE_VAULT_PASSWORD_FILE: &#39;/tmp/vault.pass&#39;&lt;/code&gt; - путь к файлу, где лежит пароль от ansible-vault. Мы же не храним его в репозитории, а прокидываем в среду исполнения извне (например из Gitlab Variables или Infisical).&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;ANSIBLE_DISPLAY_OK_HOSTS: &#39;False&#39;&lt;/code&gt; и ANSIBLE_DISPLAY_SKIPPED_HOSTS: &amp;lsquo;False&amp;rsquo;, чтобы не выводить информацию об ОК- и SKIPPED-хостах, зачем этот мусор&amp;hellip;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;В общем и целом, использование &lt;code&gt;ansible.cfg&lt;/code&gt; — это довольно удобный способ указать глобальные настройки при работе с репозиторием несколькими инженерами или с помощью систем автоматизации.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Как установить Gitlab Runner в Docker Swarm</title>
      <link>https://etogeek.dev/posts/gitlab-runner-swarm-cluster/</link>
      <pubDate>Sun, 18 Aug 2024 10:06:32 +0000</pubDate>
      <guid>https://etogeek.dev/posts/gitlab-runner-swarm-cluster/</guid>
      <description>&#xA;  &#xA;&#xA;&#xA;&#xA;&lt;div&#xA;  &#xA;    class=&#34;flex px-4 py-3 rounded-md bg-primary-100 dark:bg-primary-900&#34;&#xA;  &gt;&#xA;&#xA;  &lt;span&#xA;    &#xA;      class=&#34;text-primary-400 ltr:pr-3 rtl:pl-3 flex items-center&#34;&#xA;    &gt;&#xA;&#xA;    &#xA;&#xA;  &lt;span class=&#34;relative block icon&#34;&gt;&#xA;    &lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; viewBox=&#34;0 0 512 512&#34;&gt;&lt;path fill=&#34;currentColor&#34; d=&#34;M506.3 417l-213.3-364c-16.33-28-57.54-28-73.98 0l-213.2 364C-10.59 444.9 9.849 480 42.74 480h426.6C502.1 480 522.6 445 506.3 417zM232 168c0-13.25 10.75-24 24-24S280 154.8 280 168v128c0 13.25-10.75 24-23.1 24S232 309.3 232 296V168zM256 416c-17.36 0-31.44-14.08-31.44-31.44c0-17.36 14.07-31.44 31.44-31.44s31.44 14.08 31.44 31.44C287.4 401.9 273.4 416 256 416z&#34;/&gt;&lt;/svg&gt;&#xA;&#xA;  &lt;/span&gt;&#xA;&#xA;&#xA;  &lt;/span&gt;&#xA;&#xA;  &lt;span&#xA;    &#xA;      class=&#34;dark:text-neutral-300&#34;&#xA;    &gt;This post is also &lt;a href=&#34;https://etogeek.dev/en/posts/gitlab-runner-swarm-cluster/&#34;   target=&#34;_blank&#34;&gt;&#xA;    available in English&lt;/a&gt;.&lt;/span&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&lt;p&gt;У меня в проекте есть кластер Docker Swarm, в котором динамически запускаются Jenkins Agents, на которых выполняются все джобы из Jenkins. Почему не в Kubernetes? Потому что держать Кубер-кластер только для джобов - неоптимально, Swarm сильно проще в поддержке. Вот даже есть пост &amp;ldquo;&lt;a href=&#34;https://etogeek.dev/posts/dont-complicate/&#34;   target=&#34;_blank&#34;&gt;&#xA;    Не усложняй&lt;/a&gt;&amp;rdquo;.&lt;/p&gt;&#xA;&lt;p&gt;Недавно разворачивал Self-Hosted Gitlab, но не нашел официальной инструкции, чтобы запустить gitlab runner в swarm-кластере, чтобы еще autoscaling работал — то есть чтобы runner сам поднимал дополнительные контейнеры на swarm-нодах. Пришлось собирать по крупицам информацию. Вот, делюсь способом.&lt;/p&gt;&#xA;&lt;p&gt;Инструкция состоит из трех этапов:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Получить токен для регистрации&lt;/li&gt;&#xA;&lt;li&gt;Разложить конфигурации раннеров на машины&lt;/li&gt;&#xA;&lt;li&gt;Задеплоить раннеры&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Получить Runner Authentication Token &#xA;    &lt;div id=&#34;%D0%BF%D0%BE%D0%BB%D1%83%D1%87%D0%B8%D1%82%D1%8C-runner-authentication-token&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%BF%D0%BE%D0%BB%D1%83%D1%87%D0%B8%D1%82%D1%8C-runner-authentication-token&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Раньше для регистрации раннеров использовался runner registration token, но он перешел в статус Deprecated. Gitlab начал использовать &lt;a href=&#34;https://docs.gitlab.com/runner/register/#register-with-a-runner-authentication-token&#34;   target=&#34;_blank&#34;&gt;&#xA;    Runner Authentication Token&lt;/a&gt; для подключения раннеров.&lt;/p&gt;&#xA;&lt;p&gt;Идем в настройки проекта, группы или всего инстанса, чтобы зарегистрировать новый Runner: Admin Settings -&amp;gt; CI/CD -&amp;gt; Runners -&amp;gt; New instance runner:&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/gitlab-runner-swarm-cluster/image.png&#34;&#xA;        alt=&#34;new instance runner&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Указываем Tags. В последствие мы будем указывать тэги в наших gitlab-ci пайплайнах, чтобы они запускались на определенных раннерах. Например, у меня это тэг &lt;code&gt;swarm&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;На следующей странице копируем токен и сохраняем где-нибудь:&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/gitlab-runner-swarm-cluster/image-1.png&#34;&#xA;        alt=&#34;runner authentication token&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Теперь Gitlab будет ждать, что какой-нибудь раннер зарегистрируется с этим токеном.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Конфигурация раннеров &#xA;    &lt;div id=&#34;%D0%BA%D0%BE%D0%BD%D1%84%D0%B8%D0%B3%D1%83%D1%80%D0%B0%D1%86%D0%B8%D1%8F-%D1%80%D0%B0%D0%BD%D0%BD%D0%B5%D1%80%D0%BE%D0%B2&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%BA%D0%BE%D0%BD%D1%84%D0%B8%D0%B3%D1%83%D1%80%D0%B0%D1%86%D0%B8%D1%8F-%D1%80%D0%B0%D0%BD%D0%BD%D0%B5%D1%80%D0%BE%D0%B2&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Мы можем запустить контейнер с gitlab runner, зайти в него и выполнить указанную команду &lt;code&gt;register&lt;/code&gt;. Gitlab Runner при регистрации сохранит токен и минимальные настройки в файл &lt;code&gt;/etc/gitlab/config.toml&lt;/code&gt; внутри контейнера. Но чтобы не ходить на каждой машине в контейнер и не делать все руками, давай сделаем шаблон конфигурации и разложим его на swarm-ноды.&lt;/p&gt;&#xA;&lt;p&gt;Готовим конфигурационный файл:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-toml&#34; data-lang=&#34;toml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;concurrent&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt; &lt;span class=&#34;c&#34;&gt;# Concurrent containers per node&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;check_interval&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;[[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;runners&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;docker-swarm-runner&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;url&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;https://&amp;lt;YOUR-GITLAB-INSTANCE&amp;gt;/&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;token&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;lt;GITLAB AUTHENTICATION TOKEN&amp;gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;executor&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;docker&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;runners&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;custom_build_dir&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;runners&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;docker&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;tls_verify&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;image&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;alpine:latest&amp;#34;&lt;/span&gt;  &lt;span class=&#34;c&#34;&gt;# Default image for jobs&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;privileged&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;  &lt;span class=&#34;c&#34;&gt;# If we need Docker-in-Docker&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;disable_entrypoint_overwrite&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;oom_kill_disable&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;disable_cache&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;volumes&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;/cache&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;shm_size&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;allowed_pull_policies&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;always&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;if-not-present&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;never&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;runners&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;cache&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;runners&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;cache&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;s3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;runners&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;cache&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;gcs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Меняем token на тот, который получили в прошлом этапе. Так же указываем адрес нашего Gitlab-инстанса.&lt;/p&gt;&#xA;&lt;p&gt;Теперь на каждой swarm-ноде нужно создать директорию для конфигурации и положить туда этот файл. Как это сделать — решайте сами, я использую Ansible.&lt;/p&gt;&#xA;&#xA;  &#xA;  &#xA;  &#xA;  &#xA;&#xA;&#xA;&#xA;&lt;div&#xA;  &#xA;    class=&#34;flex px-4 py-3 rounded-md&#34;&#xA;    style=&#34;background-color: #e63946&#34;&#xA;  &gt;&#xA;&#xA;  &lt;span&#xA;    &#xA;      class=&#34;ltr:pr-3 rtl:pl-3 flex items-center&#34;&#xA;      style=&#34;color: #1d3557&#34;&#xA;    &gt;&#xA;&#xA;    &#xA;&#xA;  &lt;span class=&#34;relative block icon&#34;&gt;&#xA;    &lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; viewBox=&#34;0 0 448 512&#34;&gt;&#xA;&lt;path fill=&#34;currentColor&#34;  d=&#34;M159.3 5.4c7.8-7.3 19.9-7.2 27.7 .1c27.6 25.9 53.5 53.8 77.7 84c11-14.4 23.5-30.1 37-42.9c7.9-7.4 20.1-7.4 28 .1c34.6 33 63.9 76.6 84.5 118c20.3 40.8 33.8 82.5 33.8 111.9C448 404.2 348.2 512 224 512C98.4 512 0 404.1 0 276.5c0-38.4 17.8-85.3 45.4-131.7C73.3 97.7 112.7 48.6 159.3 5.4zM225.7 416c25.3 0 47.7-7 68.8-21c42.1-29.4 53.4-88.2 28.1-134.4c-2.8-5.6-5.6-11.2-9.8-16.8l-50.6 58.8s-81.4-103.6-87.1-110.6C133.1 243.8 112 273.2 112 306.8C112 375.4 162.6 416 225.7 416z&#34;/&gt;&lt;/svg&gt;&#xA;  &lt;/span&gt;&#xA;&#xA;&#xA;  &lt;/span&gt;&#xA;&#xA;  &lt;span&#xA;    &#xA;      style=&#34;color: #f1faee&#34;&#xA;    &gt;Главное, помните, что хранить токен в git-репозитории небезопасно. Это плохая практика. В случае Ansible вы можете воспользоваться Ansible Vault или сторонними решениями, типа Hashicorp Vault или Insfisical.&lt;/span&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;h3 class=&#34;relative group&#34;&gt;Копируем конфигурационный файл на ноды &#xA;    &lt;div id=&#34;%D0%BA%D0%BE%D0%BF%D0%B8%D1%80%D1%83%D0%B5%D0%BC-%D0%BA%D0%BE%D0%BD%D1%84%D0%B8%D0%B3%D1%83%D1%80%D0%B0%D1%86%D0%B8%D0%BE%D0%BD%D0%BD%D1%8B%D0%B9-%D1%84%D0%B0%D0%B9%D0%BB-%D0%BD%D0%B0-%D0%BD%D0%BE%D0%B4%D1%8B&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%BA%D0%BE%D0%BF%D0%B8%D1%80%D1%83%D0%B5%D0%BC-%D0%BA%D0%BE%D0%BD%D1%84%D0%B8%D0%B3%D1%83%D1%80%D0%B0%D1%86%D0%B8%D0%BE%D0%BD%D0%BD%D1%8B%D0%B9-%D1%84%D0%B0%D0%B9%D0%BB-%D0%BD%D0%B0-%D0%BD%D0%BE%D0%B4%D1%8B&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h3&gt;&#xA;&lt;p&gt;Вот пример Ansible-кода для копирования конфигурационного файла:&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;templates/config.toml.j2&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-jinja&#34; data-lang=&#34;jinja&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;x&#34;&gt;concurrent = 5&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;x&#34;&gt;check_interval = 0&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;x&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;x&#34;&gt;[[runners]]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;x&#34;&gt;  name = &amp;#34;docker-swarm-runner&amp;#34;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;x&#34;&gt;  url = &amp;#34;&lt;/span&gt;&lt;span class=&#34;cp&#34;&gt;{{&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;gitlab_instance_url&lt;/span&gt; &lt;span class=&#34;cp&#34;&gt;}}&lt;/span&gt;&lt;span class=&#34;x&#34;&gt;&amp;#34;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;x&#34;&gt;  token = &amp;#34;&lt;/span&gt;&lt;span class=&#34;cp&#34;&gt;{{&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;gitlab_runner_swarm_auth_token.value&lt;/span&gt; &lt;span class=&#34;cp&#34;&gt;}}&lt;/span&gt;&lt;span class=&#34;x&#34;&gt;&amp;#34;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;x&#34;&gt;  executor = &amp;#34;docker&amp;#34;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;x&#34;&gt;  [runners.custom_build_dir]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;x&#34;&gt;  [runners.docker]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;x&#34;&gt;    tls_verify = false&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;x&#34;&gt;    image = &amp;#34;alpine:latest&amp;#34;  # Default image for jobs, can be overridden in .gitlab-ci.yml&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;x&#34;&gt;    privileged = true  # Enable if you need to run Docker-in-Docker or privileged containers&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;x&#34;&gt;    disable_entrypoint_overwrite = false&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;x&#34;&gt;    oom_kill_disable = false&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;x&#34;&gt;    disable_cache = false&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;x&#34;&gt;    volumes = [&amp;#34;/cache&amp;#34;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;x&#34;&gt;    shm_size = 0&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;x&#34;&gt;    allowed_pull_policies = [&amp;#34;always&amp;#34;, &amp;#34;if-not-present&amp;#34;, &amp;#34;never&amp;#34;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;x&#34;&gt;  [runners.cache]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;x&#34;&gt;    [runners.cache.s3]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;x&#34;&gt;    [runners.cache.gcs]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;tasks/main.yml&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;Create directories&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ansible.builtin.file&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;/etc/gitlab-runner/config&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;state&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;directory&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;recurse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;Copy config file&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ansible.builtin.template&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;src&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;config.toml.j2&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;dest&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;/etc/gitlab-runner/config/config.toml&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Конфигурационный файл должен быть на всех нодах swarm-кластера.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Деплой Gitlab Runner в Swarm &#xA;    &lt;div id=&#34;%D0%B4%D0%B5%D0%BF%D0%BB%D0%BE%D0%B9-gitlab-runner-%D0%B2-swarm&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%B4%D0%B5%D0%BF%D0%BB%D0%BE%D0%B9-gitlab-runner-%D0%B2-swarm&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Swarm работает с привычными docker-compose файлами, поэтому создаем такой. Для копирования я так же пользуюсь Ansible, поэтому вот пример jinja-шаблона:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-jinja&#34; data-lang=&#34;jinja&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;x&#34;&gt;services:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;x&#34;&gt;  gitlab-runner:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;x&#34;&gt;    image: gitlab/gitlab-runner:&lt;/span&gt;&lt;span class=&#34;cp&#34;&gt;{{&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;gitlab_runner_image_version&lt;/span&gt; &lt;span class=&#34;cp&#34;&gt;}}&lt;/span&gt;&lt;span class=&#34;x&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;x&#34;&gt;    deploy:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;x&#34;&gt;      mode: global&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;x&#34;&gt;    volumes:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;x&#34;&gt;      - /var/run/docker.sock:/var/run/docker.sock&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;x&#34;&gt;      - /etc/gitlab-runner/config:/etc/gitlab-runner&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;x&#34;&gt;    networks:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;x&#34;&gt;      - gitlab-net&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;x&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;x&#34;&gt;networks:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;x&#34;&gt;  gitlab-net:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;x&#34;&gt;    external: false&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Обратите внимание:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;мы указываем deploy mode: global, чтобы контейнер запустился на всех нодах кластера. Это как DaemonSet в Kubernetes.&lt;/li&gt;&#xA;&lt;li&gt;мы подключаем директорию с &lt;code&gt;config.toml&lt;/code&gt; файлом&lt;/li&gt;&#xA;&lt;li&gt;дополнительно мы монтируем docker socket, чтобы gitlab runner мог управлять нашим docker engine и создавать новые контейнеры (это нужно для работы autoscaling)&lt;/li&gt;&#xA;&lt;li&gt;указываем версию gitlab-runner контейнера, посмотреть доступные можно в &lt;a href=&#34;https://hub.docker.com/r/gitlab/gitlab-runner/tags&#34;   target=&#34;_blank&#34;&gt;&#xA;    Docker Hub&lt;/a&gt;.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Я копирую этот файл с помощью Ansible:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;Copy compose file&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ansible.builtin.template&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;src&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;docker-compose.yml.j2&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;dest&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;/etc/gitlab-runner/docker-compose.yml&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Запускать swarm stack нужно с менеджер-ноды&lt;/strong&gt; (главной ноды кластера), но сам файл будет лежать на всех нодах. Избыточно, но мне так проще, чтобы не добавлять условия в Ansible.&lt;/p&gt;&#xA;&lt;p&gt;Идем на менеджер-ноду и выполняем:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker stack deploy -c docker-compose.yml gitlab-runner&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Через минутку проверяем:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;root@swarm-node01:/home/ysemyenkov# docker stack ps gitlab-runner&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ID             NAME                                                    IMAGE                                 NODE           DESIRED STATE   CURRENT STATE        ERROR     PORTS&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;pnkxq7yf3wm3   gitlab-runner.1tlong-name   gitlab/gitlab-runner:ubuntu-v17.3.0   swarm-node01   Running         Running &lt;span class=&#34;m&#34;&gt;2&lt;/span&gt; days ago&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ih4822qch9ew   gitlab-runner.3tlong-name   gitlab/gitlab-runner:ubuntu-v17.3.0   swarm-node03   Running         Running &lt;span class=&#34;m&#34;&gt;2&lt;/span&gt; days ago&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;xzj5yt1cv5ae   gitlab-runner.2tlong-name   gitlab/gitlab-runner:ubuntu-v17.3.0   swarm-node02   Running         Running &lt;span class=&#34;m&#34;&gt;2&lt;/span&gt; days ago&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Если все нормально, то в Gitlab мы увидем зарегистрированный Runner:&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/gitlab-runner-swarm-cluster/image-2.png&#34;&#xA;        alt=&#34;registered swarm gitlab runners&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Проверяем работу &#xA;    &lt;div id=&#34;%D0%BF%D1%80%D0%BE%D0%B2%D0%B5%D1%80%D1%8F%D0%B5%D0%BC-%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D1%83&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%BF%D1%80%D0%BE%D0%B2%D0%B5%D1%80%D1%8F%D0%B5%D0%BC-%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D1%83&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Можем создать тестовый пайплайн, который запустит десяток джобов одновременно:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-gitlab&#34; data-lang=&#34;gitlab&#34;&gt;stages:&#xA;  - test&#xA;&#xA;parallel-jobs:&#xA;  stage: test&#xA;  tags:&#xA;    - swarm&#xA;  script:&#xA;    - echo &amp;#34;Starting sleep for 600 seconds&amp;#34;&#xA;    - sleep 600&#xA;  parallel:&#xA;    matrix:&#xA;      - JOB_NAME: &amp;#34;job1&amp;#34;&#xA;      - JOB_NAME: &amp;#34;job2&amp;#34;&#xA;      - JOB_NAME: &amp;#34;job3&amp;#34;&#xA;      - JOB_NAME: &amp;#34;job4&amp;#34;&#xA;      - JOB_NAME: &amp;#34;job5&amp;#34;&#xA;      - JOB_NAME: &amp;#34;job6&amp;#34;&#xA;      - JOB_NAME: &amp;#34;job7&amp;#34;&#xA;      - JOB_NAME: &amp;#34;job8&amp;#34;&#xA;      - JOB_NAME: &amp;#34;job9&amp;#34;&#xA;      - JOB_NAME: &amp;#34;job10&amp;#34;&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Запускаем пайплайн и видим, что контейнеры запустились в нужном количестве и на разных нодах. Посмотреть можно как через &lt;code&gt;docker ps&lt;/code&gt; на нодах, так и через средства мониторинга. Например я смотрю метрики cAdvisor в Grafana:&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/gitlab-runner-swarm-cluster/image-3.png&#34;&#xA;        alt=&#34;containers in grafana from cadvisor metrics&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Видим, что три верхних контейнера — это основные процессы gitlab-runner, которые получают задания и создают динамические контейнеры под джобы. А ниже, наши контейнеры с sleep джобами запущенны параллельно на разных нодах.&lt;/p&gt;&#xA;&lt;p&gt;Вот и всё.&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;🙋‍♂️С комментариями можно смело приходить в наш &#xA;&#xA;  &lt;span class=&#34;relative inline-block align-text-bottom icon&#34;&gt;&#xA;    &lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; viewBox=&#34;0 0 496 512&#34;&gt;&lt;path fill=&#34;currentColor&#34; d=&#34;M248,8C111.033,8,0,119.033,0,256S111.033,504,248,504,496,392.967,496,256,384.967,8,248,8ZM362.952,176.66c-3.732,39.215-19.881,134.378-28.1,178.3-3.476,18.584-10.322,24.816-16.948,25.425-14.4,1.326-25.338-9.517-39.287-18.661-21.827-14.308-34.158-23.215-55.346-37.177-24.485-16.135-8.612-25,5.342-39.5,3.652-3.793,67.107-61.51,68.335-66.746.153-.655.3-3.1-1.154-4.384s-3.59-.849-5.135-.5q-3.283.746-104.608,69.142-14.845,10.194-26.894,9.934c-8.855-.191-25.888-5.006-38.551-9.123-15.531-5.048-27.875-7.717-26.8-16.291q.84-6.7,18.45-13.7,108.446-47.248,144.628-62.3c68.872-28.647,83.183-33.623,92.511-33.789,2.052-.034,6.639.474,9.61,2.885a10.452,10.452,0,0,1,3.53,6.716A43.765,43.765,0,0,1,362.952,176.66Z&#34;/&gt;&lt;/svg&gt;&#xA;&#xA;  &lt;/span&gt;&#xA;&#xA;&lt;a href=&#34;https://t.me/etogeekchat&#34;   target=&#34;_blank&#34;&gt;&#xA;    телеграм-чат&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;🌀Так же рекомендую подписаться на &#xA;&#xA;  &lt;span class=&#34;relative inline-block align-text-bottom icon&#34;&gt;&#xA;    &lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; viewBox=&#34;0 0 496 512&#34;&gt;&lt;path fill=&#34;currentColor&#34; d=&#34;M248,8C111.033,8,0,119.033,0,256S111.033,504,248,504,496,392.967,496,256,384.967,8,248,8ZM362.952,176.66c-3.732,39.215-19.881,134.378-28.1,178.3-3.476,18.584-10.322,24.816-16.948,25.425-14.4,1.326-25.338-9.517-39.287-18.661-21.827-14.308-34.158-23.215-55.346-37.177-24.485-16.135-8.612-25,5.342-39.5,3.652-3.793,67.107-61.51,68.335-66.746.153-.655.3-3.1-1.154-4.384s-3.59-.849-5.135-.5q-3.283.746-104.608,69.142-14.845,10.194-26.894,9.934c-8.855-.191-25.888-5.006-38.551-9.123-15.531-5.048-27.875-7.717-26.8-16.291q.84-6.7,18.45-13.7,108.446-47.248,144.628-62.3c68.872-28.647,83.183-33.623,92.511-33.789,2.052-.034,6.639.474,9.61,2.885a10.452,10.452,0,0,1,3.53,6.716A43.765,43.765,0,0,1,362.952,176.66Z&#34;/&gt;&lt;/svg&gt;&#xA;&#xA;  &lt;/span&gt;&#xA;&#xA;&lt;a href=&#34;https://t.me/etogeek&#34;   target=&#34;_blank&#34;&gt;&#xA;    телеграм-канал&lt;/a&gt;, чтобы не пропускать классные посты и анонсы новых статей.&lt;/p&gt;&#xA;&lt;p&gt;🎬 А видео выходят на Youtube, канал &#xA;&#xA;  &lt;span class=&#34;relative inline-block align-text-bottom icon&#34;&gt;&#xA;    &lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; viewBox=&#34;0 0 576 512&#34;&gt;&lt;path fill=&#34;currentColor&#34; d=&#34;M549.655 124.083c-6.281-23.65-24.787-42.276-48.284-48.597C458.781 64 288 64 288 64S117.22 64 74.629 75.486c-23.497 6.322-42.003 24.947-48.284 48.597-11.412 42.867-11.412 132.305-11.412 132.305s0 89.438 11.412 132.305c6.281 23.65 24.787 41.5 48.284 47.821C117.22 448 288 448 288 448s170.78 0 213.371-11.486c23.497-6.321 42.003-24.171 48.284-47.821 11.412-42.867 11.412-132.305 11.412-132.305s0-89.438-11.412-132.305zm-317.51 213.508V175.185l142.739 81.205-142.739 81.201z&#34;/&gt;&lt;/svg&gt;&#xA;&#xA;  &lt;/span&gt;&#xA;&#xA;&lt;a href=&#34;https://www.youtube.com/@etogeek&#34;   target=&#34;_blank&#34;&gt;&#xA;    @etogeek&lt;/a&gt;.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Аутентификация и авторизация. В чем разница?</title>
      <link>https://etogeek.dev/posts/auth-auth/</link>
      <pubDate>Thu, 18 Apr 2024 00:00:00 +0000</pubDate>
      <guid>https://etogeek.dev/posts/auth-auth/</guid>
      <description>&lt;p&gt;&lt;strong&gt;Авторизация и аутентификация&lt;/strong&gt; — похожие слова с различными значениями, которые легко спутать. Давайте разберемся, в чем же разница.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Аутентификация &#xA;    &lt;div id=&#34;%D0%B0%D1%83%D1%82%D0%B5%D0%BD%D1%82%D0%B8%D1%84%D0%B8%D0%BA%D0%B0%D1%86%D0%B8%D1%8F&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%B0%D1%83%D1%82%D0%B5%D0%BD%D1%82%D0%B8%D1%84%D0%B8%D0%BA%D0%B0%D1%86%D0%B8%D1%8F&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;&lt;strong&gt;Аутентификация&lt;/strong&gt; — это процесс, который позволяет удостовериться, что вы тот, кем себя представляете. Примером аутентификации служит ввод логина и пароля. Система проверяет эти данные и подтверждает вашу идентичность.&lt;/p&gt;&#xA;&lt;p&gt;В системах на базе &lt;strong&gt;Linux&lt;/strong&gt;, аутентификация происходит с помощью файлов:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;/etc/passwd&lt;/code&gt; — хранит информацию о пользователях.&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;/etc/shadow&lt;/code&gt; — содержит зашифрованные пароли пользователей.&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;/etc/nsswitch.conf&lt;/code&gt; — указывает, где искать информацию о пользователях, группах и других ресурсах, включая DNS или сервисы сетевых файловых систем. Этот файл направляет систему искать данные в локальных файлах или, например, в LDAP.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Авторизация &#xA;    &lt;div id=&#34;%D0%B0%D0%B2%D1%82%D0%BE%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%B0%D0%B2%D1%82%D0%BE%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;&lt;strong&gt;Авторизация&lt;/strong&gt; наступает после аутентификации и определяет, к каким ресурсам или действиям у вас есть доступ. Она проверяет, разрешено ли вам доступ к определенным разделам сервиса.&lt;/p&gt;&#xA;&lt;p&gt;В Linux права доступа определяются через:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;/etc/group&lt;/code&gt; — описывает группы пользователей и их членов.&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;/etc/sudoers&lt;/code&gt; — указывает, кто может исполнять команды с правами суперпользователя.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Пример из жизни &#xA;    &lt;div id=&#34;%D0%BF%D1%80%D0%B8%D0%BC%D0%B5%D1%80-%D0%B8%D0%B7-%D0%B6%D0%B8%D0%B7%D0%BD%D0%B8&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%BF%D1%80%D0%B8%D0%BC%D0%B5%D1%80-%D0%B8%D0%B7-%D0%B6%D0%B8%D0%B7%D0%BD%D0%B8&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Когда вы показываете паспорт на границе, это &lt;strong&gt;аутентификация&lt;/strong&gt; — вы подтверждаете, что вы Вася Петров. Определение того, можно ли вам выезжать из страны, является вопросом &lt;strong&gt;авторизации&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Важно помнить&lt;/strong&gt;: аутентификация всегда предшествует авторизации. Сначала система должна узнать, кто вы, а затем решить, что вы можете делать.&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;Прокомментировать статью, поделиться идеями, поболтать и задать вопрос можно в 👉 &lt;a href=&#34;https://t.me/etogeekchat&#34;   target=&#34;_blank&#34;&gt;&#xA;    телеграм-чате&lt;/a&gt;, а так же обязательно подписаться на 👉 &lt;a href=&#34;https://t.me/etogeek&#34;   target=&#34;_blank&#34;&gt;&#xA;    телеграм-канал&lt;/a&gt;!&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>SLI, SLO и SLA. В чем разница?</title>
      <link>https://etogeek.dev/posts/sli-slo-sla/</link>
      <pubDate>Sun, 18 Feb 2024 00:00:00 +0000</pubDate>
      <guid>https://etogeek.dev/posts/sli-slo-sla/</guid>
      <description>&lt;p&gt;Эти три аббривеатуры максимально плотно связаны между собой.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;SLI — Service Level Indicator &#xA;    &lt;div id=&#34;sli--service-level-indicator&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#sli--service-level-indicator&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Это числовое значение какой-то конкретной характеристики, метрика: &lt;em&gt;время отклика, задержка запросов, частота ошибок&lt;/em&gt;. Обычно берутся значения за период времени, усредняются и переводятся в проценты.&lt;/p&gt;&#xA;&lt;p&gt;Это наша мера успеха.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;«Наш сайт был доступен 99% времени за прошлый месяц» — это SLI&lt;/strong&gt;. Внезапно, это не SLA, как многие привыкли говорить.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;SLO — Service Level Objective &#xA;    &lt;div id=&#34;slo--service-level-objective&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#slo--service-level-objective&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Это наша цель. Числовое значение, которого мы хотим достичь и не проваливаться ниже этой границы. Измеряем через SLI. Задает уровень ожиданий от сервиса.&#xA;Например, &lt;em&gt;SLO = среднее время отклика должно быть ≤100ms&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;«Мы стремимся, чтобы наш сайт был доступен 99% времени» — это SLO.&lt;/strong&gt;&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;SLA — Service Level Agreement &#xA;    &lt;div id=&#34;sla--service-level-agreement&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#sla--service-level-agreement&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Отвечает на вопрос &lt;em&gt;«Что случится если SLO не соблюдаются?»&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Это явный или неявный контракт с клиентами или пользователями системы в котором описаны последствия несоответствия SLO.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;«Будет предоставлена компенсация в случае, если наш сайт будет доступен менее 99% времени» — это SLA&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;Зачастую используются внутренние SLO, более жесткие, чем те, что мы показываем пользователям. Например, мы утверждаем, что система доступна 99,9% времени, а инженерная команда стремится обеспечивать 99,99%, таким образом появляется некий буфер перед штрафами по SLA.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;TL;DR; &#xA;    &lt;div id=&#34;tldr&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#tldr&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Помните, что &lt;strong&gt;SLI — это измерение&lt;/strong&gt;, &lt;strong&gt;SLO — это цель&lt;/strong&gt;, а &lt;strong&gt;SLA — это обязательство&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;🙋‍♂️С комментариями можно смело приходить в наш &#xA;&#xA;  &lt;span class=&#34;relative inline-block align-text-bottom icon&#34;&gt;&#xA;    &lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; viewBox=&#34;0 0 496 512&#34;&gt;&lt;path fill=&#34;currentColor&#34; d=&#34;M248,8C111.033,8,0,119.033,0,256S111.033,504,248,504,496,392.967,496,256,384.967,8,248,8ZM362.952,176.66c-3.732,39.215-19.881,134.378-28.1,178.3-3.476,18.584-10.322,24.816-16.948,25.425-14.4,1.326-25.338-9.517-39.287-18.661-21.827-14.308-34.158-23.215-55.346-37.177-24.485-16.135-8.612-25,5.342-39.5,3.652-3.793,67.107-61.51,68.335-66.746.153-.655.3-3.1-1.154-4.384s-3.59-.849-5.135-.5q-3.283.746-104.608,69.142-14.845,10.194-26.894,9.934c-8.855-.191-25.888-5.006-38.551-9.123-15.531-5.048-27.875-7.717-26.8-16.291q.84-6.7,18.45-13.7,108.446-47.248,144.628-62.3c68.872-28.647,83.183-33.623,92.511-33.789,2.052-.034,6.639.474,9.61,2.885a10.452,10.452,0,0,1,3.53,6.716A43.765,43.765,0,0,1,362.952,176.66Z&#34;/&gt;&lt;/svg&gt;&#xA;&#xA;  &lt;/span&gt;&#xA;&#xA;&lt;a href=&#34;https://t.me/etogeekchat&#34;   target=&#34;_blank&#34;&gt;&#xA;    телеграм-чат&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;🌀Так же рекомендую подписаться на &#xA;&#xA;  &lt;span class=&#34;relative inline-block align-text-bottom icon&#34;&gt;&#xA;    &lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; viewBox=&#34;0 0 496 512&#34;&gt;&lt;path fill=&#34;currentColor&#34; d=&#34;M248,8C111.033,8,0,119.033,0,256S111.033,504,248,504,496,392.967,496,256,384.967,8,248,8ZM362.952,176.66c-3.732,39.215-19.881,134.378-28.1,178.3-3.476,18.584-10.322,24.816-16.948,25.425-14.4,1.326-25.338-9.517-39.287-18.661-21.827-14.308-34.158-23.215-55.346-37.177-24.485-16.135-8.612-25,5.342-39.5,3.652-3.793,67.107-61.51,68.335-66.746.153-.655.3-3.1-1.154-4.384s-3.59-.849-5.135-.5q-3.283.746-104.608,69.142-14.845,10.194-26.894,9.934c-8.855-.191-25.888-5.006-38.551-9.123-15.531-5.048-27.875-7.717-26.8-16.291q.84-6.7,18.45-13.7,108.446-47.248,144.628-62.3c68.872-28.647,83.183-33.623,92.511-33.789,2.052-.034,6.639.474,9.61,2.885a10.452,10.452,0,0,1,3.53,6.716A43.765,43.765,0,0,1,362.952,176.66Z&#34;/&gt;&lt;/svg&gt;&#xA;&#xA;  &lt;/span&gt;&#xA;&#xA;&lt;a href=&#34;https://t.me/etogeek&#34;   target=&#34;_blank&#34;&gt;&#xA;    телеграм-канал&lt;/a&gt;, чтобы не пропускать классные посты и анонсы новых статей.&lt;/p&gt;&#xA;&lt;p&gt;🎬 А видео выходят на Youtube, канал &#xA;&#xA;  &lt;span class=&#34;relative inline-block align-text-bottom icon&#34;&gt;&#xA;    &lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; viewBox=&#34;0 0 576 512&#34;&gt;&lt;path fill=&#34;currentColor&#34; d=&#34;M549.655 124.083c-6.281-23.65-24.787-42.276-48.284-48.597C458.781 64 288 64 288 64S117.22 64 74.629 75.486c-23.497 6.322-42.003 24.947-48.284 48.597-11.412 42.867-11.412 132.305-11.412 132.305s0 89.438 11.412 132.305c6.281 23.65 24.787 41.5 48.284 47.821C117.22 448 288 448 288 448s170.78 0 213.371-11.486c23.497-6.321 42.003-24.171 48.284-47.821 11.412-42.867 11.412-132.305 11.412-132.305s0-89.438-11.412-132.305zm-317.51 213.508V175.185l142.739 81.205-142.739 81.201z&#34;/&gt;&lt;/svg&gt;&#xA;&#xA;  &lt;/span&gt;&#xA;&#xA;&lt;a href=&#34;https://www.youtube.com/@etogeek&#34;   target=&#34;_blank&#34;&gt;&#xA;    @etogeek&lt;/a&gt;.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Как закоммитить только часть файла?</title>
      <link>https://etogeek.dev/posts/commit-part-of-the-file/</link>
      <pubDate>Thu, 08 Feb 2024 00:00:00 +0000</pubDate>
      <guid>https://etogeek.dev/posts/commit-part-of-the-file/</guid>
      <description>&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;TL;DR; &#xA;    &lt;div id=&#34;tldr&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#tldr&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;&lt;code&gt;git add -p&lt;/code&gt;&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Чуть подробнее &#xA;    &lt;div id=&#34;%D1%87%D1%83%D1%82%D1%8C-%D0%BF%D0%BE%D0%B4%D1%80%D0%BE%D0%B1%D0%BD%D0%B5%D0%B5&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D1%87%D1%83%D1%82%D1%8C-%D0%BF%D0%BE%D0%B4%D1%80%D0%BE%D0%B1%D0%BD%D0%B5%D0%B5&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Команда &lt;code&gt;git add -p&lt;/code&gt; (или &lt;code&gt;--patch&lt;/code&gt;) проходит по всем файлам и спрашивает по каждому изменению, что ты хочешь с ним сделать:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;l&#34;&gt;❯ git add -p&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;l&#34;&gt;diff --git a/ansible/file.yml b/ansible/file.yml&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;l&#34;&gt;index 73f6b4d..899610f 100644&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;---&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;a/ansible/file.yml&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;l&#34;&gt;+++ b/ansible/file.yml&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;@@&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-&lt;span class=&#34;m&#34;&gt;52&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;+52,8 @@&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;       &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;loop&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;- &lt;span class=&#34;w&#34;&gt;       &lt;/span&gt;- &lt;span class=&#34;s2&#34;&gt;&amp;#34;service&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;l&#34;&gt;+        - &amp;#34;node_modules&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;l&#34;&gt;+        - &amp;#34;systemd_templates&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;       &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;l&#34;&gt;(1/1) Stage this hunk [y,n,q,a,d,e,?]?&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;А дальше интерактивный ввод. Ну, самое важное — это “&lt;code&gt;y&lt;/code&gt;” и “&lt;code&gt;n&lt;/code&gt;&amp;quot; — добавить эти изменения в индекс или пропустить. Есть варианты с пропустить или добавить “до конца файла”. Можно использовать как &lt;code&gt;git add -p filename&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Напомню, &lt;strong&gt;индекс в git&lt;/strong&gt; — это список файлов, которые будут закоммичены.&lt;/p&gt;&#xA;&lt;p&gt;Важно, это действие добавляет наше изменение только в индекс. Его еще нужно закоммитить.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Как еще можно использовать? &#xA;    &lt;div id=&#34;%D0%BA%D0%B0%D0%BA-%D0%B5%D1%89%D0%B5-%D0%BC%D0%BE%D0%B6%D0%BD%D0%BE-%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D1%82%D1%8C&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%BA%D0%B0%D0%BA-%D0%B5%D1%89%D0%B5-%D0%BC%D0%BE%D0%B6%D0%BD%D0%BE-%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D1%82%D1%8C&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Как писал вот здесь, я всегда пользовался либо &lt;code&gt;git add .&lt;/code&gt;, либо &lt;code&gt;git add &amp;lt;filename&amp;gt;&lt;/code&gt;.  Но тут есть загвоздка:&lt;/p&gt;&#xA;&lt;p&gt;при &lt;code&gt;git add .&lt;/code&gt; в индекс, а затем и в коммит, могут попасть лишние файлы про редактирование которых ты мог и забыть уже&lt;/p&gt;&#xA;&lt;p&gt;Вот &lt;code&gt;git add -p&lt;/code&gt; позволит более осознанно (преисполненно) пройтись по файлам перед тем как добавить их в индекс.&lt;/p&gt;&#xA;&lt;p&gt;Так же напомню, что для удаления файла из индекса можно воспользоваться командой &lt;code&gt;git reset filename&lt;/code&gt;.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Что я слушаю, когда работаю?</title>
      <link>https://etogeek.dev/posts/what-i-listen/</link>
      <pubDate>Wed, 07 Feb 2024 00:00:00 +0000</pubDate>
      <guid>https://etogeek.dev/posts/what-i-listen/</guid>
      <description>&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;На чем слушаю? &#xA;    &lt;div id=&#34;%D0%BD%D0%B0-%D1%87%D0%B5%D0%BC-%D1%81%D0%BB%D1%83%D1%88%D0%B0%D1%8E&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%BD%D0%B0-%D1%87%D0%B5%D0%BC-%D1%81%D0%BB%D1%83%D1%88%D0%B0%D1%8E&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;&lt;strong&gt;AirPods Pro 2&lt;/strong&gt; — мощная активная шумоизоляция, отлично держатся в ушах, все еще хорошо держат зарядку, правильно переключаются между девайсами. Им уже больше года, использую каждый день. Я плотно подсел на экосистему Apple и не думаю, что в обозримом будущем уйду к другому бренду наушников.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Что слушаю? &#xA;    &lt;div id=&#34;%D1%87%D1%82%D0%BE-%D1%81%D0%BB%D1%83%D1%88%D0%B0%D1%8E&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D1%87%D1%82%D0%BE-%D1%81%D0%BB%D1%83%D1%88%D0%B0%D1%8E&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Всё очень зависит от настроения, задачи, уровня энергии, но вот основные плейлисты из закладок:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Открытие года — под хип-хоп круто работается, собрал такой &lt;a href=&#34;https://open.spotify.com/playlist/0kXTxy3b7TKRhM63IgEi2X&#34;   target=&#34;_blank&#34;&gt;&#xA;    плейлист в Spotify&lt;/a&gt;. А вот попробовал перенести его в Яндекс.Музыку - &lt;a href=&#34;https://music.yandex.ru/users/etoosamoe/playlists/1039&#34;   target=&#34;_blank&#34;&gt;&#xA;    ссылка&lt;/a&gt;. Очевидно, что не все треки перенеслись, но рекомендательная система должна справиться с наполнением.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Если хочется чуть более спокойной музыки, то включаю lo-fi girl. Либо в &lt;a href=&#34;https://open.spotify.com/playlist/0vvXsWCC9xrXsKd4FyS8kM?si=9bda45677aa54b34&#34;   target=&#34;_blank&#34;&gt;&#xA;    Spotify&lt;/a&gt;, либо на &lt;a href=&#34;https://www.youtube.com/watch?v=jfKfPfyJRdk&#34;   target=&#34;_blank&#34;&gt;&#xA;    Youtube&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Вечно угораю с того, что саундтрек от игры Stronghold Crusader второй год подряд оказывается в топе моего Spotify Wrapped, но это не отменяет того, что это очень классный 40-минутный плейлист. &lt;a href=&#34;https://open.spotify.com/album/50F2mUOZU5YFPcFCNi13Xv?si=04a0ca19832d4e55&#34;   target=&#34;_blank&#34;&gt;&#xA;    Spotify&lt;/a&gt;, &lt;a href=&#34;https://www.youtube.com/watch?v=9foO7qWlNds&#34;   target=&#34;_blank&#34;&gt;&#xA;    Youtube&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Длиннющий, одновременно расслабляющий и держащий в фокусе плейлист с ambient-ремиксом &lt;a href=&#34;https://www.youtube.com/watch?v=hShxsAlJmfw&#34;   target=&#34;_blank&#34;&gt;&#xA;    саундтрека к фильму Dune&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Тоже длинный плейлист, повторяющийся, но более расслабленный — музыка из игры Witcher 3. &lt;a href=&#34;https://www.youtube.com/watch?v=-MJi7T4lX80&#34;   target=&#34;_blank&#34;&gt;&#xA;    Youtube&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Когда настроение повеселее, то могу включить мой retro-плейлист - &lt;a href=&#34;https://open.spotify.com/playlist/3jWwoZ9iSjAuZuT7raBuup?si=b76ce8f2d9ae4f3f&#34;   target=&#34;_blank&#34;&gt;&#xA;    Spotify&lt;/a&gt;, &lt;a href=&#34;https://music.yandex.ru/users/etoosamoe/playlists/1047&#34;   target=&#34;_blank&#34;&gt;&#xA;    ЯндексМузыка&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Два плейлиста по 15-20 минут — две радиостанции из игры GTA 3 на Youtube: &lt;a href=&#34;https://www.youtube.com/watch?v=HsU7Yi6qxe4&#34;   target=&#34;_blank&#34;&gt;&#xA;    Game Radio&lt;/a&gt;, и &lt;a href=&#34;https://www.youtube.com/watch?v=7jHUddL3xfg&#34;   target=&#34;_blank&#34;&gt;&#xA;    Head Radio&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Довольно бодрая музыка, может отвлекать, подходит для менее сосредоточенной и рутинной работы — 40 минут, альбом группы The Medics (NL) &lt;a href=&#34;https://open.spotify.com/album/1qZpvQGNyR2R0uqTsWQA3T?si=d94352ed343546d7&#34;   target=&#34;_blank&#34;&gt;&#xA;    Spotify&lt;/a&gt;, &lt;a href=&#34;https://youtube.com/playlist?list=OLAK5uy_lI1HuztruEJvdpOqT2Cs76tKgz1_p1I9k&amp;amp;si=W3icsd7wEBr5Fr62&#34;   target=&#34;_blank&#34;&gt;&#xA;    Youtube&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</description>
    </item>
    <item>
      <title>Одинарные и двойные кавычки в Bash?</title>
      <link>https://etogeek.dev/posts/shell-quotes/</link>
      <pubDate>Thu, 01 Feb 2024 00:00:00 +0000</pubDate>
      <guid>https://etogeek.dev/posts/shell-quotes/</guid>
      <description>&lt;p&gt;После информации про &lt;a href=&#34;https://etogeek.dev/posts/shell-variables/&#34;   target=&#34;_blank&#34;&gt;&#xA;    фигурные скобки&lt;/a&gt; нельзя не сказать про кавычки — где использовать одинарные кавычки&lt;code&gt;echo &#39;URL = https://$VAR.ru&#39;&lt;/code&gt;, а где двойные кавычки&lt;code&gt;echo &amp;quot;URL = https://$VAR.ru&amp;quot;&lt;/code&gt;.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Одинарные кавычки (&lt;code&gt;&#39;some&#39;&lt;/code&gt;): &#xA;    &lt;div id=&#34;%D0%BE%D0%B4%D0%B8%D0%BD%D0%B0%D1%80%D0%BD%D1%8B%D0%B5-%D0%BA%D0%B0%D0%B2%D1%8B%D1%87%D0%BA%D0%B8-some&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%BE%D0%B4%D0%B8%D0%BD%D0%B0%D1%80%D0%BD%D1%8B%D0%B5-%D0%BA%D0%B0%D0%B2%D1%8B%D1%87%D0%BA%D0%B8-some&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Строки внутри таких кавычек будут считаться буквальными и переменные внутри не будут раскрыты. Все спецсимволы, кроме самой кавычки, трактуются буквально.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;VAR&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;etogeek&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;URL = https://$VAR.ru&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;URL&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; https://&lt;span class=&#34;nv&#34;&gt;$VAR&lt;/span&gt;.ru&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Двойные кавычки (&lt;code&gt;&amp;quot;some&amp;quot;&lt;/code&gt;): &#xA;    &lt;div id=&#34;%D0%B4%D0%B2%D0%BE%D0%B9%D0%BD%D1%8B%D0%B5-%D0%BA%D0%B0%D0%B2%D1%8B%D1%87%D0%BA%D0%B8-some&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%B4%D0%B2%D0%BE%D0%B9%D0%BD%D1%8B%D0%B5-%D0%BA%D0%B0%D0%B2%D1%8B%D1%87%D0%BA%D0%B8-some&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Переменные в строках в таких кавычках будут развернуты:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;VAR&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;etogeek&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;URL = https://&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$VAR&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;.ru&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;URL&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; https://etogeek.ru&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Обработка спецсимволов: &#xA;    &lt;div id=&#34;%D0%BE%D0%B1%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%BA%D0%B0-%D1%81%D0%BF%D0%B5%D1%86%D1%81%D0%B8%D0%BC%D0%B2%D0%BE%D0%BB%D0%BE%D0%B2&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%BE%D0%B1%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%BA%D0%B0-%D1%81%D0%BF%D0%B5%D1%86%D1%81%D0%B8%D0%BC%D0%B2%D0%BE%D0%BB%D0%BE%D0%B2&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Внутри двойных кавычек некоторые спецсимволы (например, &lt;code&gt;$&lt;/code&gt;, &lt;code&gt;\&lt;/code&gt;) могут иметь специальное значение и будут интерпретироваться. Например, &lt;code&gt;\n&lt;/code&gt; будет заменено на символ новой строки.&lt;/li&gt;&#xA;&lt;li&gt;Внутри одинарных кавычек спецсимволы трактуются буквально, и они не имеют особого значения.&lt;/li&gt;&#xA;&lt;li&gt;Спецсимволы можно экранировать (escape) с помощью backslash (&lt;code&gt;\&lt;/code&gt;)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;VAR&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;etogeek&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;URL:\nhttps://&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$VAR&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;.ru&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;URL:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;https://etogeek.ru&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Тройные кавычки (&lt;code&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/code&gt;, &lt;code&gt;&#39;&#39;&#39;&lt;/code&gt;): &#xA;    &lt;div id=&#34;%D1%82%D1%80%D0%BE%D0%B9%D0%BD%D1%8B%D0%B5-%D0%BA%D0%B0%D0%B2%D1%8B%D1%87%D0%BA%D0%B8--&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D1%82%D1%80%D0%BE%D0%B9%D0%BD%D1%8B%D0%B5-%D0%BA%D0%B0%D0%B2%D1%8B%D1%87%D0%BA%D0%B8--&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Обычно тройные кавычки используются для создания многострочных строк (&lt;em&gt;каламбур, но да&lt;/em&gt;). Так же в тройных кавычках можно использовать обычные не экранируя символы:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;VAR&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;etogeek&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&amp;#34;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;This is my site &amp;#39;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$VAR&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#39;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;URL = https://&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$VAR&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;.ru&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;Keep learning! &amp;#34;&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;$VAR&amp;#39;&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;This is my site &lt;span class=&#34;s1&#34;&gt;&amp;#39;etogeek&amp;#39;&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;URL&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; https://etogeek.ru&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Keep learning! &lt;span class=&#34;nv&#34;&gt;$VAR&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;&#xA;&lt;p&gt;🙋‍♂️С комментариями можно смело приходить в наш &#xA;&#xA;  &lt;span class=&#34;relative inline-block align-text-bottom icon&#34;&gt;&#xA;    &lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; viewBox=&#34;0 0 496 512&#34;&gt;&lt;path fill=&#34;currentColor&#34; d=&#34;M248,8C111.033,8,0,119.033,0,256S111.033,504,248,504,496,392.967,496,256,384.967,8,248,8ZM362.952,176.66c-3.732,39.215-19.881,134.378-28.1,178.3-3.476,18.584-10.322,24.816-16.948,25.425-14.4,1.326-25.338-9.517-39.287-18.661-21.827-14.308-34.158-23.215-55.346-37.177-24.485-16.135-8.612-25,5.342-39.5,3.652-3.793,67.107-61.51,68.335-66.746.153-.655.3-3.1-1.154-4.384s-3.59-.849-5.135-.5q-3.283.746-104.608,69.142-14.845,10.194-26.894,9.934c-8.855-.191-25.888-5.006-38.551-9.123-15.531-5.048-27.875-7.717-26.8-16.291q.84-6.7,18.45-13.7,108.446-47.248,144.628-62.3c68.872-28.647,83.183-33.623,92.511-33.789,2.052-.034,6.639.474,9.61,2.885a10.452,10.452,0,0,1,3.53,6.716A43.765,43.765,0,0,1,362.952,176.66Z&#34;/&gt;&lt;/svg&gt;&#xA;&#xA;  &lt;/span&gt;&#xA;&#xA;&lt;a href=&#34;https://t.me/etogeekchat&#34;   target=&#34;_blank&#34;&gt;&#xA;    телеграм-чат&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;🌀Так же рекомендую подписаться на &#xA;&#xA;  &lt;span class=&#34;relative inline-block align-text-bottom icon&#34;&gt;&#xA;    &lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; viewBox=&#34;0 0 496 512&#34;&gt;&lt;path fill=&#34;currentColor&#34; d=&#34;M248,8C111.033,8,0,119.033,0,256S111.033,504,248,504,496,392.967,496,256,384.967,8,248,8ZM362.952,176.66c-3.732,39.215-19.881,134.378-28.1,178.3-3.476,18.584-10.322,24.816-16.948,25.425-14.4,1.326-25.338-9.517-39.287-18.661-21.827-14.308-34.158-23.215-55.346-37.177-24.485-16.135-8.612-25,5.342-39.5,3.652-3.793,67.107-61.51,68.335-66.746.153-.655.3-3.1-1.154-4.384s-3.59-.849-5.135-.5q-3.283.746-104.608,69.142-14.845,10.194-26.894,9.934c-8.855-.191-25.888-5.006-38.551-9.123-15.531-5.048-27.875-7.717-26.8-16.291q.84-6.7,18.45-13.7,108.446-47.248,144.628-62.3c68.872-28.647,83.183-33.623,92.511-33.789,2.052-.034,6.639.474,9.61,2.885a10.452,10.452,0,0,1,3.53,6.716A43.765,43.765,0,0,1,362.952,176.66Z&#34;/&gt;&lt;/svg&gt;&#xA;&#xA;  &lt;/span&gt;&#xA;&#xA;&lt;a href=&#34;https://t.me/etogeek&#34;   target=&#34;_blank&#34;&gt;&#xA;    телеграм-канал&lt;/a&gt;, чтобы не пропускать классные посты и анонсы новых статей.&lt;/p&gt;&#xA;&lt;p&gt;🎬 А видео выходят на Youtube, канал &#xA;&#xA;  &lt;span class=&#34;relative inline-block align-text-bottom icon&#34;&gt;&#xA;    &lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; viewBox=&#34;0 0 576 512&#34;&gt;&lt;path fill=&#34;currentColor&#34; d=&#34;M549.655 124.083c-6.281-23.65-24.787-42.276-48.284-48.597C458.781 64 288 64 288 64S117.22 64 74.629 75.486c-23.497 6.322-42.003 24.947-48.284 48.597-11.412 42.867-11.412 132.305-11.412 132.305s0 89.438 11.412 132.305c6.281 23.65 24.787 41.5 48.284 47.821C117.22 448 288 448 288 448s170.78 0 213.371-11.486c23.497-6.321 42.003-24.171 48.284-47.821 11.412-42.867 11.412-132.305 11.412-132.305s0-89.438-11.412-132.305zm-317.51 213.508V175.185l142.739 81.205-142.739 81.201z&#34;/&gt;&lt;/svg&gt;&#xA;&#xA;  &lt;/span&gt;&#xA;&#xA;&lt;a href=&#34;https://www.youtube.com/@etogeek&#34;   target=&#34;_blank&#34;&gt;&#xA;    @etogeek&lt;/a&gt;.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>В чем разница между $VAR и ${VAR}?</title>
      <link>https://etogeek.dev/posts/shell-variables/</link>
      <pubDate>Sun, 28 Jan 2024 00:00:00 +0000</pubDate>
      <guid>https://etogeek.dev/posts/shell-variables/</guid>
      <description>&lt;p&gt;Это реально частый вопрос. Рассказываю в дополнение к &lt;a href=&#34;https://t.me/etogeek/224&#34;   target=&#34;_blank&#34;&gt;&#xA;    посту&lt;/a&gt; про подстановку переменных в файл.&lt;/p&gt;&#xA;&lt;p&gt;Тут нет никакого rocket-science и разница только в том, как командный интерпретатор (Bash, zsh, sh, тысячи их) определит границы названия переменной:&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;$VAR&lt;/code&gt; — хорошо, когда название переменной отделено от другого текста неиспользуемыми символами, например пробелом, или backslash. А вот если переменную вставим в слово, то она будет интепретирована неверно:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;VAR&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;eto&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$VAR&lt;/span&gt;      &lt;span class=&#34;c1&#34;&gt;# Выведет eto&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$VARgeek&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;# Выведет значение VARgeek, если такое есть, иначе — пусто&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;${VAR}&lt;/code&gt; — надежнее. Помогает точно отделить название переменной от другого текста:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;VAR&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;eto&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;VAR&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;     &lt;span class=&#34;c1&#34;&gt;# Выведет eto&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;VAR&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;geek &lt;span class=&#34;c1&#34;&gt;# Выведет etogeek&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;&#xA;&lt;p&gt;🙋‍♂️С комментариями можно смело приходить в наш &#xA;&#xA;  &lt;span class=&#34;relative inline-block align-text-bottom icon&#34;&gt;&#xA;    &lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; viewBox=&#34;0 0 496 512&#34;&gt;&lt;path fill=&#34;currentColor&#34; d=&#34;M248,8C111.033,8,0,119.033,0,256S111.033,504,248,504,496,392.967,496,256,384.967,8,248,8ZM362.952,176.66c-3.732,39.215-19.881,134.378-28.1,178.3-3.476,18.584-10.322,24.816-16.948,25.425-14.4,1.326-25.338-9.517-39.287-18.661-21.827-14.308-34.158-23.215-55.346-37.177-24.485-16.135-8.612-25,5.342-39.5,3.652-3.793,67.107-61.51,68.335-66.746.153-.655.3-3.1-1.154-4.384s-3.59-.849-5.135-.5q-3.283.746-104.608,69.142-14.845,10.194-26.894,9.934c-8.855-.191-25.888-5.006-38.551-9.123-15.531-5.048-27.875-7.717-26.8-16.291q.84-6.7,18.45-13.7,108.446-47.248,144.628-62.3c68.872-28.647,83.183-33.623,92.511-33.789,2.052-.034,6.639.474,9.61,2.885a10.452,10.452,0,0,1,3.53,6.716A43.765,43.765,0,0,1,362.952,176.66Z&#34;/&gt;&lt;/svg&gt;&#xA;&#xA;  &lt;/span&gt;&#xA;&#xA;&lt;a href=&#34;https://t.me/etogeekchat&#34;   target=&#34;_blank&#34;&gt;&#xA;    телеграм-чат&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;🌀Так же рекомендую подписаться на &#xA;&#xA;  &lt;span class=&#34;relative inline-block align-text-bottom icon&#34;&gt;&#xA;    &lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; viewBox=&#34;0 0 496 512&#34;&gt;&lt;path fill=&#34;currentColor&#34; d=&#34;M248,8C111.033,8,0,119.033,0,256S111.033,504,248,504,496,392.967,496,256,384.967,8,248,8ZM362.952,176.66c-3.732,39.215-19.881,134.378-28.1,178.3-3.476,18.584-10.322,24.816-16.948,25.425-14.4,1.326-25.338-9.517-39.287-18.661-21.827-14.308-34.158-23.215-55.346-37.177-24.485-16.135-8.612-25,5.342-39.5,3.652-3.793,67.107-61.51,68.335-66.746.153-.655.3-3.1-1.154-4.384s-3.59-.849-5.135-.5q-3.283.746-104.608,69.142-14.845,10.194-26.894,9.934c-8.855-.191-25.888-5.006-38.551-9.123-15.531-5.048-27.875-7.717-26.8-16.291q.84-6.7,18.45-13.7,108.446-47.248,144.628-62.3c68.872-28.647,83.183-33.623,92.511-33.789,2.052-.034,6.639.474,9.61,2.885a10.452,10.452,0,0,1,3.53,6.716A43.765,43.765,0,0,1,362.952,176.66Z&#34;/&gt;&lt;/svg&gt;&#xA;&#xA;  &lt;/span&gt;&#xA;&#xA;&lt;a href=&#34;https://t.me/etogeek&#34;   target=&#34;_blank&#34;&gt;&#xA;    телеграм-канал&lt;/a&gt;, чтобы не пропускать классные посты и анонсы новых статей.&lt;/p&gt;&#xA;&lt;p&gt;🎬 А видео выходят на Youtube, канал &#xA;&#xA;  &lt;span class=&#34;relative inline-block align-text-bottom icon&#34;&gt;&#xA;    &lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; viewBox=&#34;0 0 576 512&#34;&gt;&lt;path fill=&#34;currentColor&#34; d=&#34;M549.655 124.083c-6.281-23.65-24.787-42.276-48.284-48.597C458.781 64 288 64 288 64S117.22 64 74.629 75.486c-23.497 6.322-42.003 24.947-48.284 48.597-11.412 42.867-11.412 132.305-11.412 132.305s0 89.438 11.412 132.305c6.281 23.65 24.787 41.5 48.284 47.821C117.22 448 288 448 288 448s170.78 0 213.371-11.486c23.497-6.321 42.003-24.171 48.284-47.821 11.412-42.867 11.412-132.305 11.412-132.305s0-89.438-11.412-132.305zm-317.51 213.508V175.185l142.739 81.205-142.739 81.201z&#34;/&gt;&lt;/svg&gt;&#xA;&#xA;  &lt;/span&gt;&#xA;&#xA;&lt;a href=&#34;https://www.youtube.com/@etogeek&#34;   target=&#34;_blank&#34;&gt;&#xA;    @etogeek&lt;/a&gt;.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Борьба с продуктивностью. Или за.</title>
      <link>https://etogeek.dev/posts/productivity-notes/</link>
      <pubDate>Sat, 20 Jan 2024 11:15:16 +0000</pubDate>
      <guid>https://etogeek.dev/posts/productivity-notes/</guid>
      <description>&lt;div class=&#34;lead text-neutral-500 dark:text-neutral-400 !mb-9 text-xl&#34;&gt;&#xA;  Эта статья — объединение двух постов, вышедших в &lt;a href=&#34;https://t.me/etogeek&#34;   target=&#34;_blank&#34;&gt;&#xA;    Телеграм-канале&lt;/a&gt;. Чтобы не пропускать подобное, подписывайся 💎&#xA;&lt;/div&gt;&#xA;&#xA;&lt;p&gt;⌚️ Привет, друзья! Хочу поделиться своим опытом в борьбе за увеличение продуктивности. Одна из проблем, которую я решаю:&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;🟠 Сложность в контроле дедлайнов &#xA;    &lt;div id=&#34;-%D1%81%D0%BB%D0%BE%D0%B6%D0%BD%D0%BE%D1%81%D1%82%D1%8C-%D0%B2-%D0%BA%D0%BE%D0%BD%D1%82%D1%80%D0%BE%D0%BB%D0%B5-%D0%B4%D0%B5%D0%B4%D0%BB%D0%B0%D0%B9%D0%BD%D0%BE%D0%B2&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#-%D1%81%D0%BB%D0%BE%D0%B6%D0%BD%D0%BE%D1%81%D1%82%D1%8C-%D0%B2-%D0%BA%D0%BE%D0%BD%D1%82%D1%80%D0%BE%D0%BB%D0%B5-%D0%B4%D0%B5%D0%B4%D0%BB%D0%B0%D0%B9%D0%BD%D0%BE%D0%B2&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Я могу долго откладывать задачу до последнего момента, или наоборот, могу засидеться с одной задачей и не сделать другую.&lt;/p&gt;&#xA;&lt;p&gt;С этим мне реально помогает один из методов тайм-менеджмента &lt;strong&gt;«calendar blocking»&lt;/strong&gt;. В его основе лежит первый закон Паркинсона:&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;➡ Работа заполняет время, отпущенное на нее.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;Если более простыми словами — мы будем делать задачу ровно столько, сколько на нее отведено времени. Значит нам нужно ограничить время выполнения задачи, поставить ей искусственный дедлайн.&lt;/p&gt;&#xA;&lt;p&gt;Нашему мозгу гораздо проще сфокусированно работать, когда он понимает, что работа конечна, когда есть четкие временные рамки, ведь через условные 60 минут можно будет переключиться на другую задачу или отдохнуть.&lt;/p&gt;&#xA;&lt;p&gt;Таким образом, мы можем запланировать день в календаре в виде блоков времени под каждый типа задач, например:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;8:00 - 9:00 — Пробуждение, завтрак, зарядка, новости&lt;/li&gt;&#xA;&lt;li&gt;9:00 - 9:30 — Написать утреннюю часть ежедневной заметки&lt;/li&gt;&#xA;&lt;li&gt;9:30 - 10:30 — Основная работа&lt;/li&gt;&#xA;&lt;li&gt;10:30 - 11:00 — Митап с командой&lt;/li&gt;&#xA;&lt;li&gt;11:00 - 12:00 — Консультация в чате&#xA;Очень важно менять виды деятельности, эффективнее всего — каждый час. Если ты час писал код, то в следующий блок почитай статью и законспектируй её.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Планирование дней в календаре помогает визуально наблюдать сколько времени тратится на определенные типы задач, структурирует день. Можно группировать задачи: выделить блок на коммуникативные задачи, чтобы сделать их сразу вместе — проверить почту и очистить ящик, актуализировать задачи в спринте, позвонить родителям. Так мы уменьшаем количество переключений контекста.&lt;/p&gt;&#xA;&lt;p&gt;Не стоит быть слишком жестким по отношению к плану дня. Все мы люди и у всех нас возникают непредвиденные дела или изменения планов — смело двигай тайм-блоки. Если задача не укладывается в тайм-блок — планируй новый, заодно так будет тренироваться умение оценки задач по времени.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;🟠 Постоянные отвлечения &#xA;    &lt;div id=&#34;-%D0%BF%D0%BE%D1%81%D1%82%D0%BE%D1%8F%D0%BD%D0%BD%D1%8B%D0%B5-%D0%BE%D1%82%D0%B2%D0%BB%D0%B5%D1%87%D0%B5%D0%BD%D0%B8%D1%8F&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#-%D0%BF%D0%BE%D1%81%D1%82%D0%BE%D1%8F%D0%BD%D0%BD%D1%8B%D0%B5-%D0%BE%D1%82%D0%B2%D0%BB%D0%B5%D1%87%D0%B5%D0%BD%D0%B8%D1%8F&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Некоторым людям сложно сфокусированно работать над чем-то, потому что их мозг постоянно находит способ отвлечься — ссылка в Википедии увела в дебри истории Эритреи, одно уведомление в Телеграме привело к дум-скроллингу по всем каналам, мусор на столе заставляет прибраться.. Да что угодно. Все это заставляет менять контекст, а это мало того, что тормозит прогресс, так еще и выматывает физически.&lt;/p&gt;&#xA;&lt;p&gt;🍅 Мне &lt;em&gt;действительно&lt;/em&gt; в этом помогает &lt;strong&gt;метод&lt;/strong&gt; &lt;strong&gt;Pomodoro&lt;/strong&gt;. Это когда ты работаешь сфокусированно блоками по 20-25 минут, а между ними с чистой совестью отдыхаешь.&lt;/p&gt;&#xA;&lt;p&gt;☝️ Важно заранее выбрать задачу над которой будешь работать. Не должно быть так, что ты запустил таймер и десять минут выбираешь, чем бы заняться.&lt;/p&gt;&#xA;&lt;p&gt;Дальше нужно заставить себя работать не отвлекаясь весь этот небольшой блок времени. Сразу после него ты сможешь несколько минут отдыхать  — например проверить телегу, размяться, налить чай. Ну, или начать следующий «помидор».&lt;/p&gt;&#xA;&lt;p&gt;Если я ловлю себя на том, что отвлекся — ставлю таймер на паузу. Если понимаю, что я прям сильно выпал из контекста задачи — сбрасываю таймер, тогда это время не запишется в статистику. Об этом я смогу поразмышлять позже, покритиковать себя же.&lt;/p&gt;&#xA;&lt;p&gt;Это НЕ серебряная пуля, и это НЕ полностью решает проблему. Но частично помогает мне.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;🟢 Как еще можно бороться с отвлечениями? &#xA;    &lt;div id=&#34;-%D0%BA%D0%B0%D0%BA-%D0%B5%D1%89%D0%B5-%D0%BC%D0%BE%D0%B6%D0%BD%D0%BE-%D0%B1%D0%BE%D1%80%D0%BE%D1%82%D1%8C%D1%81%D1%8F-%D1%81-%D0%BE%D1%82%D0%B2%D0%BB%D0%B5%D1%87%D0%B5%D0%BD%D0%B8%D1%8F%D0%BC%D0%B8&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#-%D0%BA%D0%B0%D0%BA-%D0%B5%D1%89%D0%B5-%D0%BC%D0%BE%D0%B6%D0%BD%D0%BE-%D0%B1%D0%BE%D1%80%D0%BE%D1%82%D1%8C%D1%81%D1%8F-%D1%81-%D0%BE%D1%82%D0%B2%D0%BB%D0%B5%D1%87%D0%B5%D0%BD%D0%B8%D1%8F%D0%BC%D0%B8&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Помимо хороших способов вроде отключения всех уведомлений, освобождения стола от лишнего, шумоподавления в наушниках, стоит еще рассмотреть усидчивость как навык. А навыки можно тренировать.&lt;/p&gt;&#xA;&lt;p&gt;Для меня бывает реально сложно проработать полчаса не отвлекаясь ни на что, поэтому я немного меняю схему, начинаю тренировать «выдержку»:&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;🟠 Ставлю таймер на меньшее время.&lt;/strong&gt; У меня были даже пятиминутные блоки. Нужно выбрать такое время, которое ты точно сможешь не отвлекаться. Самое сложное — ловить себя на отвлечениях, честно тормозить и сбрасывать таймер.&lt;/p&gt;&#xA;&lt;p&gt;🟠 Если я понимаю, что хочу и готов проработать следующий блок без перерыва — &lt;strong&gt;начинаю его сразу&lt;/strong&gt;. Со временем временные блоки увеличиваются.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;🟠 Вечером я смотрю статистику&lt;/strong&gt; сколько сфокусированно проработал и делаю выводы. Общую статистику можно обсудить с самим собой на Weekly Review — еще одна техника, которую я хочу попробовать.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;🟢 Где следить? &#xA;    &lt;div id=&#34;-%D0%B3%D0%B4%D0%B5-%D1%81%D0%BB%D0%B5%D0%B4%D0%B8%D1%82%D1%8C&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#-%D0%B3%D0%B4%D0%B5-%D1%81%D0%BB%D0%B5%D0%B4%D0%B8%D1%82%D1%8C&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Несмотря на огромное количество приложений-таймеров, поиск нужного функционала и подходящего интерфейса чуть не привел меня к написанию своего.. Но, я нашел Tomito.&#xA;В нем есть все, что мне нужно: статистика, постановка таймера на паузу, виджет «поверх всех окон». Наверное не хватает только мобильного приложения с виджетом для часов.&lt;/p&gt;&#xA;&lt;p&gt;🔥 Кто-то может обратить внимание и сказать — это же не pomodoro, а обычный таймер. Ну, да ¯&lt;em&gt;(ツ)&lt;/em&gt;/¯ Из этого метода по сути я использую только наличие таймера, как дополнительный стимул не отвлекаться. Меня устраивает.&lt;/p&gt;&#xA;&lt;p&gt;Немного позже у меня будет будет пост-размышление, где я пытаюсь разобраться, что лучше: сфокусированно проработать над крутой задачей или следовать запланированным временным блокам. Stay tuned.&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;🙋‍♂️С комментариями можно смело приходить в наш &#xA;&#xA;  &lt;span class=&#34;relative inline-block align-text-bottom icon&#34;&gt;&#xA;    &lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; viewBox=&#34;0 0 496 512&#34;&gt;&lt;path fill=&#34;currentColor&#34; d=&#34;M248,8C111.033,8,0,119.033,0,256S111.033,504,248,504,496,392.967,496,256,384.967,8,248,8ZM362.952,176.66c-3.732,39.215-19.881,134.378-28.1,178.3-3.476,18.584-10.322,24.816-16.948,25.425-14.4,1.326-25.338-9.517-39.287-18.661-21.827-14.308-34.158-23.215-55.346-37.177-24.485-16.135-8.612-25,5.342-39.5,3.652-3.793,67.107-61.51,68.335-66.746.153-.655.3-3.1-1.154-4.384s-3.59-.849-5.135-.5q-3.283.746-104.608,69.142-14.845,10.194-26.894,9.934c-8.855-.191-25.888-5.006-38.551-9.123-15.531-5.048-27.875-7.717-26.8-16.291q.84-6.7,18.45-13.7,108.446-47.248,144.628-62.3c68.872-28.647,83.183-33.623,92.511-33.789,2.052-.034,6.639.474,9.61,2.885a10.452,10.452,0,0,1,3.53,6.716A43.765,43.765,0,0,1,362.952,176.66Z&#34;/&gt;&lt;/svg&gt;&#xA;&#xA;  &lt;/span&gt;&#xA;&#xA;&lt;a href=&#34;https://t.me/etogeekchat&#34;   target=&#34;_blank&#34;&gt;&#xA;    телеграм-чат&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;🌀Так же рекомендую подписаться на &#xA;&#xA;  &lt;span class=&#34;relative inline-block align-text-bottom icon&#34;&gt;&#xA;    &lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; viewBox=&#34;0 0 496 512&#34;&gt;&lt;path fill=&#34;currentColor&#34; d=&#34;M248,8C111.033,8,0,119.033,0,256S111.033,504,248,504,496,392.967,496,256,384.967,8,248,8ZM362.952,176.66c-3.732,39.215-19.881,134.378-28.1,178.3-3.476,18.584-10.322,24.816-16.948,25.425-14.4,1.326-25.338-9.517-39.287-18.661-21.827-14.308-34.158-23.215-55.346-37.177-24.485-16.135-8.612-25,5.342-39.5,3.652-3.793,67.107-61.51,68.335-66.746.153-.655.3-3.1-1.154-4.384s-3.59-.849-5.135-.5q-3.283.746-104.608,69.142-14.845,10.194-26.894,9.934c-8.855-.191-25.888-5.006-38.551-9.123-15.531-5.048-27.875-7.717-26.8-16.291q.84-6.7,18.45-13.7,108.446-47.248,144.628-62.3c68.872-28.647,83.183-33.623,92.511-33.789,2.052-.034,6.639.474,9.61,2.885a10.452,10.452,0,0,1,3.53,6.716A43.765,43.765,0,0,1,362.952,176.66Z&#34;/&gt;&lt;/svg&gt;&#xA;&#xA;  &lt;/span&gt;&#xA;&#xA;&lt;a href=&#34;https://t.me/etogeek&#34;   target=&#34;_blank&#34;&gt;&#xA;    телеграм-канал&lt;/a&gt;, чтобы не пропускать классные посты и анонсы новых статей.&lt;/p&gt;&#xA;&lt;p&gt;🎬 А видео выходят на Youtube, канал &#xA;&#xA;  &lt;span class=&#34;relative inline-block align-text-bottom icon&#34;&gt;&#xA;    &lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; viewBox=&#34;0 0 576 512&#34;&gt;&lt;path fill=&#34;currentColor&#34; d=&#34;M549.655 124.083c-6.281-23.65-24.787-42.276-48.284-48.597C458.781 64 288 64 288 64S117.22 64 74.629 75.486c-23.497 6.322-42.003 24.947-48.284 48.597-11.412 42.867-11.412 132.305-11.412 132.305s0 89.438 11.412 132.305c6.281 23.65 24.787 41.5 48.284 47.821C117.22 448 288 448 288 448s170.78 0 213.371-11.486c23.497-6.321 42.003-24.171 48.284-47.821 11.412-42.867 11.412-132.305 11.412-132.305s0-89.438-11.412-132.305zm-317.51 213.508V175.185l142.739 81.205-142.739 81.201z&#34;/&gt;&lt;/svg&gt;&#xA;&#xA;  &lt;/span&gt;&#xA;&#xA;&lt;a href=&#34;https://www.youtube.com/@etogeek&#34;   target=&#34;_blank&#34;&gt;&#xA;    @etogeek&lt;/a&gt;.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Как создать свой блог на Hugo</title>
      <link>https://etogeek.dev/posts/how-to-hugo-p1/</link>
      <pubDate>Wed, 23 Aug 2023 06:00:29 +0000</pubDate>
      <guid>https://etogeek.dev/posts/how-to-hugo-p1/</guid>
      <description>&lt;p&gt;Мне кажется, что &lt;strong&gt;DevOps — это про творчество&lt;/strong&gt;. У нас есть огромное количество способов достичь одной цели, и зачастую не будет единственного правильного решения. Что-то более оптимальное, что-то сложнее, что-то удобнее поддерживать.&lt;/p&gt;&#xA;&lt;p&gt;Мы можем собрать бинарник и положить в контейнер, а можем собрать и закинуть файл по ssh. О, а как запустить — можем просто &lt;code&gt;docker run&lt;/code&gt; сделать, а можем завернуть в compose. Или обернуть всё в systemd-сервис, или может в init.d&amp;hellip; Ой, а если еще затронуть Kubernetes и Helm&amp;hellip;&lt;/p&gt;&#xA;&lt;p&gt;Мне захотелось на досуге попробовать &lt;strong&gt;поднять блог на Hugo&lt;/strong&gt;. Я раньше это делал, но так нормально и не разобрался с порядком работы. Поднимать буду прямо на виртуалке, сделаю CI/CD пайплайн с разными окружениями и покажу как тестировать все локально.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Что такое Hugo и почему именно он? &#xA;    &lt;div id=&#34;%D1%87%D1%82%D0%BE-%D1%82%D0%B0%D0%BA%D0%BE%D0%B5-hugo-%D0%B8-%D0%BF%D0%BE%D1%87%D0%B5%D0%BC%D1%83-%D0%B8%D0%BC%D0%B5%D0%BD%D0%BD%D0%BE-%D0%BE%D0%BD&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D1%87%D1%82%D0%BE-%D1%82%D0%B0%D0%BA%D0%BE%D0%B5-hugo-%D0%B8-%D0%BF%D0%BE%D1%87%D0%B5%D0%BC%D1%83-%D0%B8%D0%BC%D0%B5%D0%BD%D0%BD%D0%BE-%D0%BE%D0%BD&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/how-to-hugo-p1/image-2.png&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://gohugo.io/&#34;   target=&#34;_blank&#34;&gt;&#xA;    Hugo&lt;/a&gt; — это так называемый генератор статических вебсайтов. Это приложение на языке Go, которое из определенного набора файлов сгенерирует нам &lt;strong&gt;&lt;a href=&#34;https://ru.wikipedia.org/wiki/%D0%A1%D1%82%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9_%D1%81%D0%B0%D0%B9%D1%82&#34;   target=&#34;_blank&#34;&gt;&#xA;    статику&lt;/a&gt;&lt;/strong&gt;, а ее мы будем раздавать пользователям с помощью &lt;em&gt;вебсервера&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Классная особенность статических страниц в том, что мы можем раздавать их хоть с s3-бакета, хоть с &lt;a href=&#34;https://pages.github.com/&#34;   target=&#34;_blank&#34;&gt;&#xA;    GitHub Pages&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;Почему же именно &lt;strong&gt;Hugo&lt;/strong&gt;? На самом деле генераторов статики несколько: Jekyll, Hugo, Gatsby&amp;hellip; Hugo — бесплатный, посты мы готовим в знакомым многим &lt;a href=&#34;https://www.markdownguide.org/&#34;   target=&#34;_blank&#34;&gt;&#xA;    Markdown&lt;/a&gt; формате, есть встроенный веб-сервер в не-продакшн режиме&amp;hellip; ну и другие я не пробовал.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h3 class=&#34;relative group&#34;&gt;Выбираем стек &#xA;    &lt;div id=&#34;%D0%B2%D1%8B%D0%B1%D0%B8%D1%80%D0%B0%D0%B5%D0%BC-%D1%81%D1%82%D0%B5%D0%BA&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%B2%D1%8B%D0%B1%D0%B8%D1%80%D0%B0%D0%B5%D0%BC-%D1%81%D1%82%D0%B5%D0%BA&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h3&gt;&#xA;&lt;p&gt;Чтобы было повеселее сегодня я сделаю следующее:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Покажу &lt;strong&gt;как установить Hugo&lt;/strong&gt; на виртуальную машину, локально и как запускать его&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Настрою Nginx для раздачи статики&lt;/strong&gt;, добавлю &lt;strong&gt;бесплатный SSL сертификат&lt;/strong&gt;&lt;/li&gt;&#xA;&lt;li&gt;Установлю &lt;strong&gt;self-hosted GitHub Actions Runner&lt;/strong&gt; на виртуальную машину&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Настрою пайплайн со сборкой и доставкой статики&lt;/strong&gt; на наш сервер&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Устанавливаем Hugo на виртуальную машину &#xA;    &lt;div id=&#34;%D1%83%D1%81%D1%82%D0%B0%D0%BD%D0%B0%D0%B2%D0%BB%D0%B8%D0%B2%D0%B0%D0%B5%D0%BC-hugo-%D0%BD%D0%B0-%D0%B2%D0%B8%D1%80%D1%82%D1%83%D0%B0%D0%BB%D1%8C%D0%BD%D1%83%D1%8E-%D0%BC%D0%B0%D1%88%D0%B8%D0%BD%D1%83&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D1%83%D1%81%D1%82%D0%B0%D0%BD%D0%B0%D0%B2%D0%BB%D0%B8%D0%B2%D0%B0%D0%B5%D0%BC-hugo-%D0%BD%D0%B0-%D0%B2%D0%B8%D1%80%D1%82%D1%83%D0%B0%D0%BB%D1%8C%D0%BD%D1%83%D1%8E-%D0%BC%D0%B0%D1%88%D0%B8%D0%BD%D1%83&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Тут официальная документация &lt;a href=&#34;https://gohugo.io/installation/linux/&#34;   target=&#34;_blank&#34;&gt;&#xA;    предлагает&lt;/a&gt; нам несколько вариантов, вплоть до установки из стандартных репозиториев Ubuntu (&lt;code&gt;apt install hugo&lt;/code&gt;), но там устанавливается очень старая версия.&lt;/p&gt;&#xA;&lt;p&gt;Мы же скачаем &lt;code&gt;.deb&lt;/code&gt; пакет и установим его. Идем в репозиторий на GitHub и открываем &lt;a href=&#34;https://github.com/gohugoio/hugo/releases/latest&#34;   target=&#34;_blank&#34;&gt;&#xA;    страницу с последним релизом&lt;/a&gt;. Качаем &lt;code&gt;.deb&lt;/code&gt; файл с amd64 архитектурой.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Скачиваем пакет&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;wget -P /tmp https://github.com/gohugoio/hugo/releases/download/v0.117.0/hugo_extended_0.117.0_linux-amd64.deb&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Устанавливаем пакет&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;dpkg -i /tmp/hugo_extended_0.117.0_linux-amd64.deb&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Проверяем установку&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;hugo version&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Устанавливаем Hugo локально &#xA;    &lt;div id=&#34;%D1%83%D1%81%D1%82%D0%B0%D0%BD%D0%B0%D0%B2%D0%BB%D0%B8%D0%B2%D0%B0%D0%B5%D0%BC-hugo-%D0%BB%D0%BE%D0%BA%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D1%83%D1%81%D1%82%D0%B0%D0%BD%D0%B0%D0%B2%D0%BB%D0%B8%D0%B2%D0%B0%D0%B5%D0%BC-hugo-%D0%BB%D0%BE%D0%BA%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Параллельно с этим я устанавливаю Hugo себе локально, чтобы было удобнее отлаживать код, а так же чтобы не мучиться с иницализацией репозитория.&lt;/p&gt;&#xA;&lt;p&gt;Так как у меня MacOS я могу установить Hugo через менеджер пакетов &lt;a href=&#34;https://brew.sh/&#34;   target=&#34;_blank&#34;&gt;&#xA;    brew&lt;/a&gt;:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;brew install hugo&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Разбираемся как оно работает &#xA;    &lt;div id=&#34;%D1%80%D0%B0%D0%B7%D0%B1%D0%B8%D1%80%D0%B0%D0%B5%D0%BC%D1%81%D1%8F-%D0%BA%D0%B0%D0%BA-%D0%BE%D0%BD%D0%BE-%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%B0%D0%B5%D1%82&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D1%80%D0%B0%D0%B7%D0%B1%D0%B8%D1%80%D0%B0%D0%B5%D0%BC%D1%81%D1%8F-%D0%BA%D0%B0%D0%BA-%D0%BE%D0%BD%D0%BE-%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%B0%D0%B5%D1%82&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Эти действия я предлагаю выполнять на своей локальной машине&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;Для начала создадим структуру для нового сайта. Предположим, что мы у себя в домашней директории:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;❯ hugo new site superblog&#xA;&#xA;Congratulations! Your new Hugo site is created in /Users/admin/superblog.&#xA;&#xA;Just a few more steps and you&amp;#39;re ready to go:&#xA;&#xA;1. Download a theme into the same-named folder.&#xA;   Choose a theme from https://themes.gohugo.io/ or&#xA;   create your own with the &amp;#34;hugo new theme &amp;lt;THEMENAME&amp;gt;&amp;#34; command.&#xA;2. Perhaps you want to add some content. You can add single files&#xA;   with &amp;#34;hugo new &amp;lt;SECTIONNAME&amp;gt;/&amp;lt;FILENAME&amp;gt;.&amp;lt;FORMAT&amp;gt;&amp;#34;.&#xA;3. Start the built-in live server via &amp;#34;hugo server&amp;#34;.&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Переходим в созданную директорию и смотрим, что лежит внутри:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;❯ cd superblog&#xA;❯ tree .&#xA;.&#xA;├── archetypes&#xA;│   └── default.md&#xA;├── assets&#xA;├── content  &amp;lt;~~ здесь все посты&#xA;├── data&#xA;├── hugo.toml  &amp;lt;~~ конфигурационный файл сайта&#xA;├── layouts&#xA;├── static&#xA;└── themes  &amp;lt;~~ темы&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Теперь нужно установить тему. Повыбирать можно &lt;a href=&#34;https://themes.gohugo.io/&#34;   target=&#34;_blank&#34;&gt;&#xA;    тут&lt;/a&gt;, а в качестве примера воспользуемся темой &lt;a href=&#34;https://themes.gohugo.io/themes/hugo-theme-stack/&#34;   target=&#34;_blank&#34;&gt;&#xA;    Stack&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Документация предлагает два варианта — установить через git submodules, либо зашить тему вглубь директории layouts (установка через модули Hugo). Первый вариант мне кажется более подходящим — он делает простым смену тем.&lt;/p&gt;&#xA;&lt;p&gt;Сначала инициализируем git-репозиторий, а затем добавим туда тему как submodule.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;git init&#xA;git submodule add https://github.com/CaiJimmy/hugo-theme-stack.git themes/stack&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;И добавим нашу тему в конфигурационный файл:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;echo &amp;#34;theme = &amp;#39;stack&amp;#39;&amp;#34; &amp;gt;&amp;gt; hugo.toml&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;На этом моменте мы можем уже попробовать запустить Hugo в не-продакшен режиме: в этом случае нам не нужно настраивать Nginx — это очень удобно для локальной отладки нашего сайта и этим мы будем пользоваться постоянно. К тому же оно поддерживает горячее изменение файлов без перезапуска.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;❯ hugo server&#xA;&#xA;Watching for changes in /Users/admin/superblog/{archetypes,assets,content,data,layouts,static,themes}&#xA;Watching for config changes in /Users/admin/superblog/hugo.toml, /Users/admin/superblog/themes/stack/config.yaml&#xA;Start building sites …&#xA;hugo v0.117.0-b2f0696cad918fb61420a6aff173eb36662b406e+extended darwin/amd64 BuildDate=2023-08-07T12:49:48Z VendorInfo=brew&#xA;&#xA;&#xA;                   | EN&#xA;-------------------+-----&#xA;  Pages            |  7&#xA;  Paginator pages  |  0&#xA;  Non-page files   |  0&#xA;  Static files     |  0&#xA;  Processed images |  3&#xA;  Aliases          |  3&#xA;  Sitemaps         |  1&#xA;  Cleaned          |  0&#xA;&#xA;Built in 56 ms&#xA;Environment: &amp;#34;development&amp;#34;&#xA;Serving pages from memory&#xA;Running in Fast Render Mode. For full rebuilds on change: hugo server --disableFastRender&#xA;Web Server is available at http://localhost:1313/ (bind address 127.0.0.1)&#xA;Press Ctrl+C to stop&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Ого, оно что-то сделало и предлагает нам зайти на &lt;code&gt;http://127.0.0.1:1313&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/how-to-hugo-p1/hugo-image1.png&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Ну, можно выйти и продолжить: &lt;code&gt;Ctrl-C&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Будем предполагать, что именно таким образом мы будем локально тестировать наш блог: отредактировали, запустили hugo server, посмотрели локально, а затем пушим в репозиторий&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&#xA;&#xA;&lt;h3 class=&#34;relative group&#34;&gt;Создаем первый пост &#xA;    &lt;div id=&#34;%D1%81%D0%BE%D0%B7%D0%B4%D0%B0%D0%B5%D0%BC-%D0%BF%D0%B5%D1%80%D0%B2%D1%8B%D0%B9-%D0%BF%D0%BE%D1%81%D1%82&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D1%81%D0%BE%D0%B7%D0%B4%D0%B0%D0%B5%D0%BC-%D0%BF%D0%B5%D1%80%D0%B2%D1%8B%D0%B9-%D0%BF%D0%BE%D1%81%D1%82&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h3&gt;&#xA;&lt;p&gt;Общая структура файлов с постами будет следующей:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;❯ tree content&#xA;content&#xA;└── posts &amp;lt;~ общая категория posts, может быть любой другой&#xA;    ├── first-post &amp;lt;~ директория под файлы одной статьи&#xA;    │   ├── image1.png &amp;lt;~ картинка-аттачмент для первого поста&#xA;    │   └── index.md &amp;lt;~ markdown файл первого поста&#xA;    └── second-post &amp;lt;~ директория под второй пост&#xA;        └── index.md&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Перезапустим наш веб-сервер &lt;code&gt;hugo server&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/how-to-hugo-p1/hugo-image2.png&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Сейчас нашу статику раздает встроенный веб-сервер hugo. Это так называемый &amp;ldquo;не-продакшн&amp;rdquo; режим.&lt;/p&gt;&#xA;&lt;p&gt;С одной стороны мы можем написать systemd-сервис, который будет запускать команду &lt;code&gt;hugo server&lt;/code&gt;. Затем мы можем либо опубликовать этот веб-сервер на 0.0.0.0, либо настроить наш Nginx на обратной проксирование на 127.0.0.1:1313. Но так не было задумано разработчиками.&lt;/p&gt;&#xA;&lt;p&gt;👉 С помощью команды &lt;code&gt;hugo&lt;/code&gt;, приложение сгенерирует статическую версию нашего сайта из всех файлов и положит ее в поддиректорию &lt;code&gt;public&lt;/code&gt;.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h3 class=&#34;relative group&#34;&gt;Установим Github Actions Runner &#xA;    &lt;div id=&#34;%D1%83%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%B8%D0%BC-github-actions-runner&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D1%83%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%B8%D0%BC-github-actions-runner&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h3&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;У нас есть возможность хостить наши статические файлы даже в GitHub Pages, и это совершенно несложно. Но не так интересно.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;Я буду хостить сайт прямо на виртуальной машине, и Github Actions Runner тоже установлю как self-hosted runner туда же.&lt;/p&gt;&#xA;&lt;p&gt;Предположим, что мы уже создали отдельный пустой репозиторий в GitHub, к которому мы потом привяжем тот локальный, что мы создавали командой &lt;code&gt;git init&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Переходим в &lt;strong&gt;Settings&lt;/strong&gt;, затем слева &lt;strong&gt;Actions&lt;/strong&gt; - &lt;strong&gt;Runners&lt;/strong&gt;, &lt;strong&gt;New self-hosted runner&lt;/strong&gt;:&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/how-to-hugo-p1/hugo-image3.png&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Дальше после выбора нужной платформы нам предоставят список команд для установки и инициализации раннера. В конце я рекомендую установить раннер как сервис, для этого можно обратиться к &lt;a href=&#34;https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/configuring-the-self-hosted-runner-application-as-a-service&#34;   target=&#34;_blank&#34;&gt;&#xA;    документации&lt;/a&gt;, где скажут запустить его командой:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo ./svc.sh install&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Вот такая картина говорит нам о том, что наш раннер успешно подключился к Github Actions и ждет работы:&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/how-to-hugo-p1/hugo-image4.png&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h3 class=&#34;relative group&#34;&gt;Создаем CI/CD пайплайн &#xA;    &lt;div id=&#34;%D1%81%D0%BE%D0%B7%D0%B4%D0%B0%D0%B5%D0%BC-cicd-%D0%BF%D0%B0%D0%B9%D0%BF%D0%BB%D0%B0%D0%B9%D0%BD&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D1%81%D0%BE%D0%B7%D0%B4%D0%B0%D0%B5%D0%BC-cicd-%D0%BF%D0%B0%D0%B9%D0%BF%D0%BB%D0%B0%D0%B9%D0%BD&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h3&gt;&#xA;&lt;p&gt;Документация для референса: &lt;a href=&#34;https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions&#34;   target=&#34;_blank&#34;&gt;&#xA;    https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;Возвращаемся в наш репозиторий, создаем директорию &lt;code&gt;.github&lt;/code&gt;, а в ней поддиректорию &lt;code&gt;workflows&lt;/code&gt;. Workflows это отдельные пайплайны в Github Actions. Поэтому создаем файл &lt;code&gt;dev-cicd.yml&lt;/code&gt; и начинаем творчество. Я сделал так:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;dev-etogeek cicd&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;on&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;workflow_dispatch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;push&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;branches&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;dev&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;jobs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;deploy-dev&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;runs-on&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;self-hosted&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;environment&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;dev&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;https://dev.mysite.tech&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;steps&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;Checkout repo&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;uses&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;actions/checkout@v3&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;with&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;          &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;submodules&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;recursive&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;Deploy Dev&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;          hugo&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;          sudo rm -rf /opt/dev-etogeek&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;          sudo mkdir -p /opt/dev-etogeek/static&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;          sudo cp -r public/* /opt/dev-etogeek/static/&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;          &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Теперь по порядку о том, что здесь происходит:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;on: workflow_dispatch:&lt;/code&gt; — позволяет нам запустить пайплайн вручную, через Run Workflow&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;on: push: branches: -dev&lt;/code&gt; — пайплайн будет запущен автоматически при пуше кода в dev-ветку&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;runs-on: self-hosted&lt;/code&gt; — указывает, где будет запущен наш пайплайн; здесь указываются теги self-hosted раннеров, либо название github-actions образа&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;environment:&lt;/code&gt; — позволит более-менее удобно следить деплоями, не самая обязательная красивость&lt;/li&gt;&#xA;&lt;li&gt;Этап &lt;code&gt;checkout&lt;/code&gt; — склонирует наш репозиторий в рабочую директорию runner-а, а параметр &lt;code&gt;with: submodules: recursive&lt;/code&gt; скачает дополнительно нашу тему, которую мы установили как git-submodule. По умолчанию рабочая директория очищается при каждом деплое.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Этап деплоя я сделал таким образом:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Команда &lt;code&gt;hugo&lt;/code&gt; генерирует в директорию &lt;code&gt;public&lt;/code&gt; статику из нашего hugo-репозитория. Команда, конечно же, требует установленного hugo на виртуальной машине, что мы сделали выше.&lt;/li&gt;&#xA;&lt;li&gt;Далее мы удаляем директорию с нашей статикой, чтобы каждый раз копировать туда актуальные файлы. Сразу после удаления мы создаем ее заново.&lt;/li&gt;&#xA;&lt;li&gt;Копируем с ключем &lt;code&gt;-r&lt;/code&gt; (recursive) все файлы из директории &lt;code&gt;public&lt;/code&gt; в нашу директорию, на которую в будущем будет ссылаться Nginx&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Делаем полностью аналогичный файл, только для production-версии нашего блога. Меняем названия, путь к директории и ветку для триггера на &lt;code&gt;main&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Обязательно добавляем в файл &lt;code&gt;.gitignore&lt;/code&gt; нашу директорию &lt;code&gt;public&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;❯ cat .gitignore&#xA;/public/&#xA;.hugo_build.lock&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Коммитим наш код. Первый раз, думаю, проще будет задеплоиться из основной ветки, чтобы все проверить. А про общий воркфлоу я напишу ниже. Смотрим, что наш пайплайн будет зеленым. Идем на виртуальную машину, проверяем, что все файлы на месте в нужной директории и мы можем приступить к настройке Nginx.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h3 class=&#34;relative group&#34;&gt;Настройка Nginx &#xA;    &lt;div id=&#34;%D0%BD%D0%B0%D1%81%D1%82%D1%80%D0%BE%D0%B9%D0%BA%D0%B0-nginx&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%BD%D0%B0%D1%81%D1%82%D1%80%D0%BE%D0%B9%D0%BA%D0%B0-nginx&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h3&gt;&#xA;&lt;p&gt;На виртуальной машине создаем файл с конфигурацией Nginx &lt;code&gt;/etc/nginx/sites-available/prod.conf&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-nginx&#34; data-lang=&#34;nginx&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;server&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;server_name&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;mysite.tech&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;root&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;/opt/prod-etogeek/static/&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;index&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;index.html&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;location&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;kn&#34;&gt;try_files&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$uri&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$uri/&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;404&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;listen&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;80&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Затем создаем symlink в директорию &lt;code&gt;sites-enabled&lt;/code&gt; с конфигурациями, которая считывается Nginx-ом:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ln -s /etc/nginx/sites-available/prod.conf /etc/nginx/sites-enabled/prod.conf&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Проверяем конфигурацию и перезапускаем Nginx:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;nginx -t&#xA;systemctl restart nginx&#xA;# или systemctl reload nginx &#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Уже сейчас можем зайти по доменному имени &lt;code&gt;dev.mysite.tech&lt;/code&gt;, которое мы конечно же поменяли на свое реальное, и увидим наш сайт. Но, добавить на сайт SSL-сертификат. Нет ничего проще, чем обратиться за помощью к Let&amp;rsquo;s Encrypt и скрипту certbot:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;apt install certbot&#xA;certbot&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Выбираем наш конфигурационный файл в интерактивном меню, а в конце выбираем Redirect. Можно будет увидеть, что certbot добавил в нашу nginx-конфигурацию несколько новых строк и переадресацию с http на https.&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Если и пока у тебя нет доменного имени, можно схитрить и добавить адрес виртуальной машины в локальный &lt;code&gt;/etc/hosts&lt;/code&gt; — тогда Nginx будет нормально обрабатывать имя из адресной строки&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&#xA;&#xA;&lt;h3 class=&#34;relative group&#34;&gt;Процесс работы &#xA;    &lt;div id=&#34;%D0%BF%D1%80%D0%BE%D1%86%D0%B5%D1%81%D1%81-%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D1%8B&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%BF%D1%80%D0%BE%D1%86%D0%B5%D1%81%D1%81-%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D1%8B&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h3&gt;&#xA;&lt;p&gt;Основная часть работы позади, автоматизированная доставка кода настроена, стоит подытожить планируемый процесс работы:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Разработку веду в локальном репозитории. Создаю статьи как отдельные &lt;code&gt;.md&lt;/code&gt; файлы.&lt;/li&gt;&#xA;&lt;li&gt;Тестирую работоспособность сайта, визуальную составляющую с помощью встроенного веб-сервера командой &lt;code&gt;hugo server&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;li&gt;Вливаю свою код в &lt;code&gt;dev&lt;/code&gt; ветку, спустя минуту-другую наблюдаю изменения на dev-версии блога&lt;/li&gt;&#xA;&lt;li&gt;Делаю Pull Request из &lt;code&gt;dev&lt;/code&gt;-ветки в &lt;code&gt;main&lt;/code&gt;-ветку, вливаю его, наблюдаю изменения на production версии блога&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/how-to-hugo-p1/image-1.png&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h3 class=&#34;relative group&#34;&gt;Что можно усовершенствовать? &#xA;    &lt;div id=&#34;%D1%87%D1%82%D0%BE-%D0%BC%D0%BE%D0%B6%D0%BD%D0%BE-%D1%83%D1%81%D0%BE%D0%B2%D0%B5%D1%80%D1%88%D0%B5%D0%BD%D1%81%D1%82%D0%B2%D0%BE%D0%B2%D0%B0%D1%82%D1%8C&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D1%87%D1%82%D0%BE-%D0%BC%D0%BE%D0%B6%D0%BD%D0%BE-%D1%83%D1%81%D0%BE%D0%B2%D0%B5%D1%80%D1%88%D0%B5%D0%BD%D1%81%D1%82%D0%B2%D0%BE%D0%B2%D0%B0%D1%82%D1%8C&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h3&gt;&#xA;&lt;p&gt;&lt;strong&gt;Немного размышлений&lt;/strong&gt;: это сложно назвать прямо идеальным девопсерским ci/cd пайплайном как минимум потому что у нас никак &lt;strong&gt;не отделены этапы сборки и выкатки кода&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Мы можем выполнять команду hugo в отдельном этапе, хоть даже на отдельной виртуальной машине или в контейнере. Затем собирать статику в архив, помечать его некой версией, например хэшом коммита и сохранять как артефакт. Затем этот артефакт уносить на dev-машину, и таким же образом — на prod-инстанс.&lt;/p&gt;&#xA;&lt;p&gt;Но я не уверен, что на текущем этапе мне нужны эти усложнения — я все делаю на одной и той же виртуальной машине, и даже dev и prod окружения у меня лежат просто в соседних директориях. Зачем большее для блога?&lt;/p&gt;&#xA;&lt;p&gt;Так же мы можем собирать это все не в self-hosted раннере, а в cloud-режиме, который предоставляет GitHub. В конце концов нужно будет просто скопировать собранную статику на нашу виртуалку.&lt;/p&gt;&#xA;&lt;p&gt;Я писал однажды небольшой пост «Не усложняй» как раз про использование более сложных технологий, чем требует продукт.&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;🙋‍♂️&lt;strong&gt;С комментариями&lt;/strong&gt; можно смело приходить в наш &lt;strong&gt;телеграм-чат&lt;/strong&gt; — &lt;a href=&#34;https://t.me/etogeekchat&#34;   target=&#34;_blank&#34;&gt;&#xA;    https://t.me/etogeekchat&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;🌀Так же рекомендую &lt;strong&gt;подписаться на телеграм-канал&lt;/strong&gt;, чтобы не пропускать классные посты и анонсы новых статей — &lt;a href=&#34;https://t.me/etogeek&#34;   target=&#34;_blank&#34;&gt;&#xA;    https://t.me/etogeek&lt;/a&gt;&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Бест-практисы Ansible</title>
      <link>https://etogeek.dev/posts/best-practice-ansible/</link>
      <pubDate>Mon, 07 Aug 2023 06:27:19 +0000</pubDate>
      <guid>https://etogeek.dev/posts/best-practice-ansible/</guid>
      <description>&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/best-practice-ansible/feature.jpg&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;В своей текущей работе я очень часто использую Ansible:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;раскладываю конфигурации Nginx на серверах,&lt;/li&gt;&#xA;&lt;li&gt;управляю пользователями&lt;/li&gt;&#xA;&lt;li&gt;настраиваю серверы&lt;/li&gt;&#xA;&lt;li&gt;управляю конфигами Hadoop и Solr кластеров&lt;/li&gt;&#xA;&lt;li&gt;и многое другое.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Это отличный инструмент, который при должном творческом подходе дает прекрасные результаты. Разработчики говорят спасибо, за то что код остается читаемым.&lt;/p&gt;&#xA;&lt;p&gt;Но не без минусов, конечно. Например, на больших объемах серверов и заданий Ansible становится довольно медлительным и решение подобных проблем становится очень индивидуальной болью. Зачастую приходится переписывать роли, менять порядок выполнения заданий, тюнинговать SSH подключения.&lt;/p&gt;&#xA;&lt;p&gt;За время работы я сформировал несколько хороших практик, которыми хочу поделиться с вами.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h3 class=&#34;relative group&#34;&gt;✍️ Используй FQCN &#xA;    &lt;div id=&#34;-%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D1%83%D0%B9-fqcn&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#-%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D1%83%D0%B9-fqcn&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;Ensure the directory is created&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ansible.builtin.file&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;&amp;lt;---&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;/tmp/directory&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;state&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;directory&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Модули, которые мы вызываем в Ansible хранятся в коллекциях. &lt;strong&gt;FQCN — fully-qualified collection names&lt;/strong&gt;, это как полное доменное имя, только для модулей ансибла. А точнее оно указывает из какой коллекции взят требуемый модуль.&lt;/p&gt;&#xA;&lt;p&gt;Ansible сейчас поставляется в двух вариантах, которые мы можем установить:&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;ansible-core&lt;/strong&gt; - только исполняемые файлы ansible, дополнительные коллекции&#xA;&lt;strong&gt;ansible community&lt;/strong&gt; - это ansible-core вместе с пакетом стандартных коллекций, которых чаще всего достаточно для большинства задач. Именно это мы уставливаем при pip install ansible.&lt;/p&gt;&#xA;&lt;p&gt;Разработчики рекомендуют всегда указывать названия модулей вместе с названиями коллекций для упрощения обращений к документации, соответствию общепринятому стилю кода и.. чтобы ansible-lint не ругался&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h3 class=&#34;relative group&#34;&gt;🗂️ Описывай переменные в host_vars и group_vars &#xA;    &lt;div id=&#34;-%D0%BE%D0%BF%D0%B8%D1%81%D1%8B%D0%B2%D0%B0%D0%B9-%D0%BF%D0%B5%D1%80%D0%B5%D0%BC%D0%B5%D0%BD%D0%BD%D1%8B%D0%B5-%D0%B2-host_vars-%D0%B8-group_vars&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#-%D0%BE%D0%BF%D0%B8%D1%81%D1%8B%D0%B2%D0%B0%D0%B9-%D0%BF%D0%B5%D1%80%D0%B5%D0%BC%D0%B5%D0%BD%D0%BD%D1%8B%D0%B5-%D0%B2-host_vars-%D0%B8-group_vars&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h3&gt;&#xA;&lt;p&gt;Мы часто используем различные &lt;a href=&#34;https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_variables.html&#34;   target=&#34;_blank&#34;&gt;&#xA;    переменные&lt;/a&gt;. Например, мы можем на уровне инвентори указать, что на сервер &lt;code&gt;gateway-01&lt;/code&gt; мы хотим установить Java версии 8, а на &lt;code&gt;supply01&lt;/code&gt; — Java 11. Или указать нестандартный путь к логам для сервиса &lt;code&gt;sausage-store-backend&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;❯ tree inventories/&#xA;inventories/&#xA;├── group_vars&#xA;│   ├── dev.yml        &amp;lt;~ здесь — переменные для группы dev&#xA;├── host_vars&#xA;│   ├── gateway-01.yml &amp;lt;~ здесь — переменные для конкретных хостов&#xA;│   ├── landing-01.yml&#xA;│   ├── prod-01.yml&#xA;│   ├── supply01.yml&#xA;│   └── web-01.yml&#xA;└── hosts.yml          &amp;lt;~ здесь — только хосты&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Да, мы можем описать переменные прямо в общем inventory-файле. И на это можно закрыть глаза, если у нас всего 2-3 хоста, но это плохая практика, которая сильно усложнит поддержку инвентаря при расширении инфраструктуры.&lt;/p&gt;&#xA;&lt;p&gt;Так же в group_vars или host_vars можно создать директорию с названием хоста или группы (прямо как в инвентори), а внутрь положить отдельные файлы с переменными, для лучшей организации хранения переменных:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;inventories/group_vars/dev/db_settings&#xA;inventories/group_vars/dev/cluster_settings&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;h3 class=&#34;relative group&#34;&gt;🤔 Помни, что переменные не мержатся, а заменяются &#xA;    &lt;div id=&#34;-%D0%BF%D0%BE%D0%BC%D0%BD%D0%B8-%D1%87%D1%82%D0%BE-%D0%BF%D0%B5%D1%80%D0%B5%D0%BC%D0%B5%D0%BD%D0%BD%D1%8B%D0%B5-%D0%BD%D0%B5-%D0%BC%D0%B5%D1%80%D0%B6%D0%B0%D1%82%D1%81%D1%8F-%D0%B0-%D0%B7%D0%B0%D0%BC%D0%B5%D0%BD%D1%8F%D1%8E%D1%82%D1%81%D1%8F&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#-%D0%BF%D0%BE%D0%BC%D0%BD%D0%B8-%D1%87%D1%82%D0%BE-%D0%BF%D0%B5%D1%80%D0%B5%D0%BC%D0%B5%D0%BD%D0%BD%D1%8B%D0%B5-%D0%BD%D0%B5-%D0%BC%D0%B5%D1%80%D0%B6%D0%B0%D1%82%D1%81%D1%8F-%D0%B0-%D0%B7%D0%B0%D0%BC%D0%B5%D0%BD%D1%8F%D1%8E%D1%82%D1%81%D1%8F&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h3&gt;&#xA;&lt;p&gt;В дополнение к предыдущему пункту хочу просто напомнить, что одинаковые переменный из разных файлов не сливаются, а заменяются. Например, у вас есть переменная-список users в файле `group_vars/dev.yml``:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;❯ cat inventories/group_vars/dev.yml&#xA;users:&#xA;  - vpupkin&#xA;  - ppetrovic&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;А рядом лежит такая же переменная, но с другим списком и в другом файле `host_vars/supply01.yml``:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;❯ cat inventories/host_vars/supply01.yml&#xA;users:&#xA;  - iivanov&#xA;  - kpomoev&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;При выполнении заданий на сервере `supply01``, как уже стоило догадаться, они не сольются в единый список из четырех элементов, а будут &lt;strong&gt;заменены&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;p&gt;А какой приоритет? От наименьшего к наибольшему:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;all group (because it is the ‘parent’ of all other groups)&lt;/li&gt;&#xA;&lt;li&gt;parent group&lt;/li&gt;&#xA;&lt;li&gt;child group&lt;/li&gt;&#xA;&lt;li&gt;host&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Финальной переменной будет список из файла &lt;code&gt;host_vars/supply01.yml&lt;/code&gt;. Вот раздел документации описывающий это — &lt;a href=&#34;https://docs.ansible.com/ansible/latest/inventory_guide/intro_inventory.html#how-variables-are-merged&#34;   target=&#34;_blank&#34;&gt;&#xA;    ссылка&lt;/a&gt;.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h3 class=&#34;relative group&#34;&gt;☢️ Не используй shell и command &#xA;    &lt;div id=&#34;-%D0%BD%D0%B5-%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D1%83%D0%B9-shell-%D0%B8-command&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#-%D0%BD%D0%B5-%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D1%83%D0%B9-shell-%D0%B8-command&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h3&gt;&#xA;&lt;p&gt;Основная затея Ansible (как и вообще подхода &lt;em&gt;infrastructure as code&lt;/em&gt;) — это &lt;strong&gt;описание &lt;em&gt;целевого состояния системы&lt;/em&gt; в виде кода&lt;/strong&gt;. Также Ansible — это про &lt;a href=&#34;https://ru.wikipedia.org/wiki/%D0%98%D0%B4%D0%B5%D0%BC%D0%BF%D0%BE%D1%82%D0%B5%D0%BD%D1%82%D0%BD%D0%BE%D1%81%D1%82%D1%8C&#34;   target=&#34;_blank&#34;&gt;&#xA;    идемпотентность&lt;/a&gt;. Это когда повторный запуск задания не принесет никаких изменений, то есть приведет к тому же самому состоянию.&lt;/p&gt;&#xA;&lt;p&gt;Если мы прокатим наш плейбук второй, третий, десятый, сотый раз — ничего не изменится, &lt;strong&gt;потому что мы описали там целевое состояние&lt;/strong&gt;. Например указали что файл &lt;code&gt;foobar.txt&lt;/code&gt; должен присутствовать в директории (&lt;code&gt;state: present&lt;/code&gt;), пакет &lt;code&gt;ncdu&lt;/code&gt; должен быть установлен, а строка &lt;code&gt;XYZ&lt;/code&gt; должна быть в файле &lt;code&gt;/tmp/foobar&lt;/code&gt;. Повторный прокат плейбука не создаст нового файла, повторный прокат не добавит новую строку. &lt;strong&gt;За этим следят встроенные модули&lt;/strong&gt; &lt;code&gt;file&lt;/code&gt;, &lt;code&gt;copy&lt;/code&gt;, &lt;code&gt;lineinfile&lt;/code&gt;, &lt;code&gt;apt&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Да, у модулей &lt;code&gt;shell&lt;/code&gt;/&lt;code&gt;command&lt;/code&gt; есть параметр, который позволяет не выполнять команду, если на сервере, к примеру, есть определенный файл. Но, в целом, они не следят за состоянием системы. Просто выполняют то что сказано. Не приводят к целевому состоянию.&lt;/p&gt;&#xA;&lt;p&gt;Понятно, что простой &lt;code&gt;shell: ls -lah&lt;/code&gt; никому не навредит, но более сложные команды - могут. Вот например запуск скрипта для установки nodejs.. что там делается.. кто будет следить за статусом выполнения&amp;hellip;&lt;/p&gt;&#xA;&lt;p&gt;Старайся максимально избегать использования command и shell модулей на столько на сколько это возможно. Большинство задач можно решить тем, что уже создано для Ansible и будет контролировать состояние задания.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h3 class=&#34;relative group&#34;&gt;🌚 Не перебарщивай с переменными &#xA;    &lt;div id=&#34;-%D0%BD%D0%B5-%D0%BF%D0%B5%D1%80%D0%B5%D0%B1%D0%B0%D1%80%D1%89%D0%B8%D0%B2%D0%B0%D0%B9-%D1%81-%D0%BF%D0%B5%D1%80%D0%B5%D0%BC%D0%B5%D0%BD%D0%BD%D1%8B%D0%BC%D0%B8&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#-%D0%BD%D0%B5-%D0%BF%D0%B5%D1%80%D0%B5%D0%B1%D0%B0%D1%80%D1%89%D0%B8%D0%B2%D0%B0%D0%B9-%D1%81-%D0%BF%D0%B5%D1%80%D0%B5%D0%BC%D0%B5%D0%BD%D0%BD%D1%8B%D0%BC%D0%B8&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h3&gt;&#xA;&lt;p&gt;Есть множество ролей на GitHub, в которых файлы с заданиями буквально на 70% состоят из фигурных скобок и переменных. Это позволяет многие параметры роли сделать настраиваемыми пользователем. Но стоит смотреть правде в глаза — это визуально усложняет поддержку роли, а я уверен, что &lt;strong&gt;нужно стараться поддерживать свои инструменты максимально возможно простыми&lt;/strong&gt;.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h3 class=&#34;relative group&#34;&gt;🏎️ Тюнингуй ansible.cfg &#xA;    &lt;div id=&#34;-%D1%82%D1%8E%D0%BD%D0%B8%D0%BD%D0%B3%D1%83%D0%B9-ansiblecfg&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#-%D1%82%D1%8E%D0%BD%D0%B8%D0%BD%D0%B3%D1%83%D0%B9-ansiblecfg&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h3&gt;&#xA;&lt;p&gt;Даже простой тюнинг конфигурационного файла можно довольно сильно ускорить выполнение твоих плейбуков. Только будь осторожен с &lt;code&gt;strategy = free&lt;/code&gt;, потому что такой режим позволяет выполнять следующую таску &lt;a href=&#34;https://docs.ansible.com/ansible/latest/collections/ansible/builtin/free_strategy.html&#34;   target=&#34;_blank&#34;&gt;&#xA;    не дожидаясь&lt;/a&gt; пока предыдущая выполнится на другом хосте.&lt;/p&gt;&#xA;&lt;p&gt;Вот пример базового тюнинга конфигурации Ansible.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;[defaults]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;host_key_checking&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;False&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;pipelining&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;True&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;strategy&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;free&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;ansible_ssh_private_key_file&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;~/.ssh/id_rsa&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;ansible_python_interpreter&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;/usr/bin/python3&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;callbacks_enabled&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;timer, profile_tasks, profile_roles&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;forks&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;30&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;[ssh_connection]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;ssh_args&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;-o ControlMaster=auto -o ControlPersist=60s&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;retries&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;3&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;А вот — &lt;a href=&#34;https://docs.ansible.com/ansible/latest/reference_appendices/config.html&#34;   target=&#34;_blank&#34;&gt;&#xA;    документация&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Можно попробовать еще установить &lt;a href=&#34;https://mitogen.networkgenomics.com/ansible_detailed.html&#34;   target=&#34;_blank&#34;&gt;&#xA;    плагин Mitogen&lt;/a&gt;, который меняет способ взаимодействия Ansible и SSH.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h3 class=&#34;relative group&#34;&gt;🤐 Никаких кредов в файлах &#xA;    &lt;div id=&#34;-%D0%BD%D0%B8%D0%BA%D0%B0%D0%BA%D0%B8%D1%85-%D0%BA%D1%80%D0%B5%D0%B4%D0%BE%D0%B2-%D0%B2-%D1%84%D0%B0%D0%B9%D0%BB%D0%B0%D1%85&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#-%D0%BD%D0%B8%D0%BA%D0%B0%D0%BA%D0%B8%D1%85-%D0%BA%D1%80%D0%B5%D0%B4%D0%BE%D0%B2-%D0%B2-%D1%84%D0%B0%D0%B9%D0%BB%D0%B0%D1%85&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h3&gt;&#xA;&lt;p&gt;Пользуемся &lt;a href=&#34;https://docs.ansible.com/ansible/latest/vault_guide/index.html&#34;   target=&#34;_blank&#34;&gt;&#xA;    ansible-vault&lt;/a&gt;. Он умеет шифровать как целые файлы - удобно для сертификатов, приватных ключей, так и отдельные переменные, которые можно будет вставить в общий файл.&lt;/p&gt;&#xA;&lt;p&gt;Записываем пароль в файл, а затем выполняем команду, которая зашифрует файл целиком:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ansible-vault encrypt --vault-password-file ~/vault.pass private-key.crt&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;А можно зашифровать только одну переменную. Вводим команду и передаем ей наш пароль и название переменной:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ansible-vault encrypt_string --vault-password-file ~/vault.pass &amp;#39;SECRET&amp;#39; --name VARIABLE&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;s3cmd_secret_key&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;!&lt;span class=&#34;l&#34;&gt;vault |&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;          &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;$ANSIBLE_VAULT;1.1;AES256&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;          &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;33663339333062616634353663636237396336396434326538316335623062383535393736643937&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;          &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;3738623538373438373962333631386366623166393934660a363931373165336462633361373835&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&lt;h3 class=&#34;relative group&#34;&gt;🕵️‍♂️ Применяй линтер &#xA;    &lt;div id=&#34;-%D0%BF%D1%80%D0%B8%D0%BC%D0%B5%D0%BD%D1%8F%D0%B9-%D0%BB%D0%B8%D0%BD%D1%82%D0%B5%D1%80&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#-%D0%BF%D1%80%D0%B8%D0%BC%D0%B5%D0%BD%D1%8F%D0%B9-%D0%BB%D0%B8%D0%BD%D1%82%D0%B5%D1%80&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h3&gt;&#xA;&lt;p&gt;Очень интересные вещи можно узнать про себя просто запустив ansible-lint по всем своим файлам, например:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;❯ ansible-lint -p roles&#xA;.......&#xA;Finished with 305 failure(s), 126 warning(s) on 429 files.&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Он тоже настраивается и часть проверок можно отключить.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h3 class=&#34;relative group&#34;&gt;🧑🏻‍💻Пользуйся handlers &#xA;    &lt;div id=&#34;%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D1%83%D0%B9%D1%81%D1%8F-handlers&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D1%83%D0%B9%D1%81%D1%8F-handlers&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h3&gt;&#xA;&lt;p&gt;Если тебе нужно перезапускать systemd-сервис не нужно делать это при каждом прогоне плейбука. С помощью &lt;strong&gt;handlers&lt;/strong&gt; можно настроить перезапуск сервиса только в определенном случае, например, при изменении конфигурационного файла.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;Solr_exporter | daemon-reload&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ansible.builtin.systemd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;daemon_reload&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;Public_solr_exporter | restart&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ansible.builtin.systemd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;public_solr_exporter&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;daemon_reload&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;state&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;restarted&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;enabled&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;&#xA;&lt;p&gt;💁‍♂️ Вот такой набор советов получился. Прокомментировать статью, поделиться идеями, поболтать и задать вопрос можно в 👉 &lt;a href=&#34;https://t.me/etogeekchat&#34;   target=&#34;_blank&#34;&gt;&#xA;    телеграм-чате&lt;/a&gt;, а так же обязательно подписаться на 👉 &lt;a href=&#34;https://t.me/etogeek&#34;   target=&#34;_blank&#34;&gt;&#xA;    телеграм-канал&lt;/a&gt;!&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Мониторинг SSL сертификатов в Zabbix</title>
      <link>https://etogeek.dev/posts/ssl-monitoring-zabbix/</link>
      <pubDate>Sat, 13 May 2023 20:32:02 +0000</pubDate>
      <guid>https://etogeek.dev/posts/ssl-monitoring-zabbix/</guid>
      <description>&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &lt;img class=&#34;my-0 rounded-md&#34; loading=&#34;lazy&#34; src=&#34;feature.png&#34; alt=&#34;Руководство по настройке мониторинга SSL сертфикатов в Zabbix с помощью Website certificate by Zabbix agent 2 без скриптов. Создание виджета для дашборда&#34; /&gt;&#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Как можно вообще пропустить дату перевыпуска SSL-сертификата? Пфф, да вообще легко.&lt;/p&gt;&#xA;&lt;p&gt;Если ты пользуешься &lt;strong&gt;&lt;a href=&#34;https://certbot.eff.org/&#34;   target=&#34;_blank&#34;&gt;&#xA;    certbot&lt;/a&gt;&lt;/strong&gt;, то скорее всего он продляет сертификат автоматически. Но вдруг сломается? Ну или ты забыл при очередном ручном продлении сертификата поставить себе напоминалку.&lt;/p&gt;&#xA;&lt;p&gt;Короче, нужно настроить мониторинг. В этой статье я покажу как настроить мониторинг SSL сертификатов в Zabbix. А в другой статье я покажу вариант для Prometheus и Grafana. &lt;em&gt;(здесь будет ссылка, когда выйдет вторая статья)&lt;/em&gt;&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h1 class=&#34;relative group&#34;&gt;План и схема мониторинга &#xA;    &lt;div id=&#34;%D0%BF%D0%BB%D0%B0%D0%BD-%D0%B8-%D1%81%D1%85%D0%B5%D0%BC%D0%B0-%D0%BC%D0%BE%D0%BD%D0%B8%D1%82%D0%BE%D1%80%D0%B8%D0%BD%D0%B3%D0%B0&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%BF%D0%BB%D0%B0%D0%BD-%D0%B8-%D1%81%D1%85%D0%B5%D0%BC%D0%B0-%D0%BC%D0%BE%D0%BD%D0%B8%D1%82%D0%BE%D1%80%D0%B8%D0%BD%D0%B3%D0%B0&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h1&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/ssl-monitoring-zabbix/SCR-20230513-tvym.png&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;План:&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Установить в Zabbix нужный &lt;a href=&#34;https://www.zabbix.com/integrations/ssl&#34;   target=&#34;_blank&#34;&gt;&#xA;    &lt;strong&gt;шаблон&lt;/strong&gt;&lt;/a&gt;.&lt;/li&gt;&#xA;&lt;li&gt;На любой из хостов, которые у нас &lt;strong&gt;уже&lt;/strong&gt; мониторятся установить &lt;strong&gt;&lt;a href=&#34;https://www.zabbix.com/download?zabbix=6.4&amp;amp;os_distribution=ubuntu&amp;amp;os_version=20.04&amp;amp;components=agent_2&amp;amp;db=&amp;amp;ws=&#34;   target=&#34;_blank&#34;&gt;&#xA;    Zabbix Agent 2&lt;/a&gt;&lt;/strong&gt;.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;&lt;strong&gt;Схема мониторинга&lt;/strong&gt;:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;У нас есть &lt;em&gt;Host&lt;/em&gt; в Zabbix-е, к которому прикреплен &lt;em&gt;Template&lt;/em&gt;.&lt;/li&gt;&#xA;&lt;li&gt;В &lt;em&gt;Host&lt;/em&gt; указан &lt;em&gt;адрес реального хоста&lt;/em&gt;, с установленным &lt;em&gt;Zabbix Agent 2&lt;/em&gt;.&lt;/li&gt;&#xA;&lt;li&gt;В &lt;em&gt;Template&lt;/em&gt; указан &lt;em&gt;адрес веб-сайта&lt;/em&gt;, на котором нужно мониторить SSL сертификат.&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;Zabbix Agent 2&lt;/em&gt; с одного из хостов собирает данные об этом сертификате.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&#xA;&lt;h1 class=&#34;relative group&#34;&gt;Установить Template &#xA;    &lt;div id=&#34;%D1%83%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%B8%D1%82%D1%8C-template&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D1%83%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%B8%D1%82%D1%8C-template&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h1&gt;&#xA;&lt;p&gt;Есть вероятность, &lt;strong&gt;что у тебя этот шаблон уже установлен&lt;/strong&gt;, проверь в списке &lt;strong&gt;&lt;em&gt;Configuration&lt;/em&gt;&lt;/strong&gt; — &lt;strong&gt;&lt;em&gt;Templates&lt;/em&gt;&lt;/strong&gt;. Он в оригинале называется &lt;strong&gt;Website certificate by Zabbix agent 2&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Если нет, то его можно скачать и импортировать вручную. (на некоторых скриншотах у меня шаблон переименован, но поверьте — это он) У меня шаблона не было, потому что я ранее обновлял старую версию Zabbix до новой.&lt;/p&gt;&#xA;&lt;p&gt;👉 &lt;a href=&#34;https://www.zabbix.com/integrations/ssl&#34;   target=&#34;_blank&#34;&gt;&#xA;    Здесь&lt;/a&gt; можно взять ссылку на шаблон для нужной тебе версии.&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/ssl-monitoring-zabbix/image-1.png&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/ssl-monitoring-zabbix/image-2.png&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/ssl-monitoring-zabbix/image-3.png&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Теперь идем в Zabbix и импортируем шаблон. Заходим в &lt;strong&gt;&lt;em&gt;Configuration&lt;/em&gt;&lt;/strong&gt; - &lt;strong&gt;&lt;em&gt;Templates&lt;/em&gt;&lt;/strong&gt; и нажимаем &lt;strong&gt;&lt;em&gt;Import&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/ssl-monitoring-zabbix/image-4.png&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Там выбираем наш файл, нажимаем &lt;strong&gt;&lt;em&gt;Import&lt;/em&gt;&lt;/strong&gt;. Всё.&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Иногда после нажатия Import может появиться ошибка &lt;code&gt;Invalid tag &amp;quot;/zabbix_export/version&amp;quot;: unsupported version number.&lt;/code&gt;  В таком случае просто в yaml файле шаблона вручную меняем версию на нужную, например - 6.2.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&#xA;&#xA;&lt;h1 class=&#34;relative group&#34;&gt;Установка Zabbix Agent 2 &#xA;    &lt;div id=&#34;%D1%83%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%BA%D0%B0-zabbix-agent-2&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D1%83%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%BA%D0%B0-zabbix-agent-2&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h1&gt;&#xA;&lt;p&gt;Установить его можно на любой хост. &lt;strong&gt;Именно с него мы будем мониторить наши SSL сертификаты&lt;/strong&gt;, так что он должен иметь стабильный доступ в интернет.&lt;/p&gt;&#xA;&lt;p&gt;Для начала хорошо бы выключить старый Zabbix Agent, если он установлен. Я вообще под шумок обновил агенты на всех машинах, заодно &lt;strong&gt;во второй версии&lt;/strong&gt; привезли &lt;strong&gt;мониторинг SMART у дисков&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/ssl-monitoring-zabbix/image-20.png&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;👉 Актуальные и корректные ссылки для установки лучше взять с&lt;/strong&gt; &lt;a href=&#34;https://www.zabbix.com/download?zabbix=6.4&amp;amp;os_distribution=ubuntu&amp;amp;os_version=20.04&amp;amp;components=agent_2&amp;amp;db=&amp;amp;ws=&#34;   target=&#34;_blank&#34;&gt;&#xA;    &lt;strong&gt;официального сайта&lt;/strong&gt;&lt;/a&gt;, я лишь приведу ниже пример для Ubuntu 20.04, Zabbix 6.4 на дату выхода статьи:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;wget https://repo.zabbix.com/zabbix/6.4/ubuntu/pool/main/z/zabbix-release/zabbix-release_6.4-1+ubuntu20.04_all.deb&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;dpkg -i zabbix-release_6.4-1+ubuntu20.04_all.deb&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;apt update&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;apt install zabbix-agent2 zabbix-agent2-plugin-*&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;systemctl restart zabbix-agent2&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;systemctl &lt;span class=&#34;nb&#34;&gt;enable&lt;/span&gt; zabbix-agent2&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&lt;h1 class=&#34;relative group&#34;&gt;Добавляем хост для мониторинга &#xA;    &lt;div id=&#34;%D0%B4%D0%BE%D0%B1%D0%B0%D0%B2%D0%BB%D1%8F%D0%B5%D0%BC-%D1%85%D0%BE%D1%81%D1%82-%D0%B4%D0%BB%D1%8F-%D0%BC%D0%BE%D0%BD%D0%B8%D1%82%D0%BE%D1%80%D0%B8%D0%BD%D0%B3%D0%B0&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%B4%D0%BE%D0%B1%D0%B0%D0%B2%D0%BB%D1%8F%D0%B5%D0%BC-%D1%85%D0%BE%D1%81%D1%82-%D0%B4%D0%BB%D1%8F-%D0%BC%D0%BE%D0%BD%D0%B8%D1%82%D0%BE%D1%80%D0%B8%D0%BD%D0%B3%D0%B0&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h1&gt;&#xA;&lt;p&gt;Возвращаемся в Zabbix и добавляем новый хост &lt;strong&gt;&lt;em&gt;Configuration&lt;/em&gt;&lt;/strong&gt; - &lt;strong&gt;&lt;em&gt;Hosts&lt;/em&gt;&lt;/strong&gt; - &lt;strong&gt;&lt;em&gt;Create new host&lt;/em&gt;&lt;/strong&gt;:&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/ssl-monitoring-zabbix/image-5.png&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Затем указываем настройки:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Название хоста&lt;/strong&gt; любое, но удобнее, если оно будет равно сайту, который мы мониторим&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Линкуем наш Template&lt;/strong&gt; (шаблон)&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Добавляем наш хост в Host Group&lt;/strong&gt; (группа хостов)&lt;/li&gt;&#xA;&lt;li&gt;Выбираем &lt;strong&gt;&lt;em&gt;Add Interface&lt;/em&gt;&lt;/strong&gt; → &lt;strong&gt;&lt;em&gt;Agent&lt;/em&gt;&lt;/strong&gt;, указываем &lt;strong&gt;адрес&lt;/strong&gt; (или &lt;strong&gt;DNS&lt;/strong&gt;) &lt;strong&gt;хоста с Zabbix Agent 2&lt;/strong&gt;, &lt;strong&gt;откуда&lt;/strong&gt; мы будем мониторить сайт.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/ssl-monitoring-zabbix/image-7.png&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;На этом же экране переходим во вкладку &lt;strong&gt;&lt;em&gt;Macros&lt;/em&gt;&lt;/strong&gt;, там переходим на вкладку &lt;strong&gt;&lt;em&gt;Inherited and host macros&lt;/em&gt;&lt;/strong&gt;, там будут макросы из шаблона. Рядом с макросом &lt;code&gt;{$CERT.WEBSITE.HOSTNAME}&lt;/code&gt; нажимаем &lt;strong&gt;&lt;em&gt;Change&lt;/em&gt;&lt;/strong&gt; и вводим в поле наш сайт для мониторинга:&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/ssl-monitoring-zabbix/image-8.png&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Сохраняем хост и идем в &lt;strong&gt;&lt;em&gt;Latest Data&lt;/em&gt;&lt;/strong&gt; для проверки:&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/ssl-monitoring-zabbix/image-9.png&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/ssl-monitoring-zabbix/image-10.png&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h1 class=&#34;relative group&#34;&gt;Меняем порог срабатывания триггера &#xA;    &lt;div id=&#34;%D0%BC%D0%B5%D0%BD%D1%8F%D0%B5%D0%BC-%D0%BF%D0%BE%D1%80%D0%BE%D0%B3-%D1%81%D1%80%D0%B0%D0%B1%D0%B0%D1%82%D1%8B%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F-%D1%82%D1%80%D0%B8%D0%B3%D0%B3%D0%B5%D1%80%D0%B0&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%BC%D0%B5%D0%BD%D1%8F%D0%B5%D0%BC-%D0%BF%D0%BE%D1%80%D0%BE%D0%B3-%D1%81%D1%80%D0%B0%D0%B1%D0%B0%D1%82%D1%8B%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F-%D1%82%D1%80%D0%B8%D0%B3%D0%B3%D0%B5%D1%80%D0%B0&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h1&gt;&#xA;&lt;p&gt;Триггер срабатывает когда срок действия сертификата подходит к концу. Срабатывание триггера основывается на макросе из шаблона. Этот триггер встроен в шаблон.&lt;/p&gt;&#xA;&lt;p&gt;Как мы можем видеть в меню &lt;strong&gt;&lt;em&gt;Templates&lt;/em&gt;&lt;/strong&gt; — &lt;strong&gt;&lt;em&gt;Website certificate by Zabbix agent&lt;/em&gt; — &lt;em&gt;Triggers&lt;/em&gt;&lt;/strong&gt; наш триггер базируется на некой переменной &lt;code&gt;CERT.EXPIRY.WARN&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/ssl-monitoring-zabbix/image-14.png&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Есть две опции как ее поменять — &lt;strong&gt;глобально&lt;/strong&gt; или &lt;strong&gt;специфично для хоста:&lt;/strong&gt;&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Глобальная переменная &#xA;    &lt;div id=&#34;%D0%B3%D0%BB%D0%BE%D0%B1%D0%B0%D0%BB%D1%8C%D0%BD%D0%B0%D1%8F-%D0%BF%D0%B5%D1%80%D0%B5%D0%BC%D0%B5%D0%BD%D0%BD%D0%B0%D1%8F&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%B3%D0%BB%D0%BE%D0%B1%D0%B0%D0%BB%D1%8C%D0%BD%D0%B0%D1%8F-%D0%BF%D0%B5%D1%80%D0%B5%D0%BC%D0%B5%D0%BD%D0%BD%D0%B0%D1%8F&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;&lt;strong&gt;&lt;em&gt;Templates&lt;/em&gt;&lt;/strong&gt; — &lt;strong&gt;&lt;em&gt;Website certificate by Zabbix agent&lt;/em&gt; — &lt;em&gt;Macros.&lt;/em&gt;&lt;/strong&gt; Таким образом это значение сменится для всех хостов, к которым привязан этот шаблон.&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/ssl-monitoring-zabbix/image-15.png&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Host-specific macros &#xA;    &lt;div id=&#34;host-specific-macros&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#host-specific-macros&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;С помощью этого варианта можно поменять значение для конкретного хоста. Заходим в &lt;strong&gt;&lt;em&gt;Configuration&lt;/em&gt;&lt;/strong&gt; - &lt;strong&gt;&lt;em&gt;Hosts&lt;/em&gt;&lt;/strong&gt;, находим &lt;strong&gt;&lt;em&gt;нужный хост&lt;/em&gt;&lt;/strong&gt;, переходим на вкладку &lt;strong&gt;&lt;em&gt;Macros&lt;/em&gt;&lt;/strong&gt;, выбираем &lt;strong&gt;&lt;em&gt;Inherited and host macros&lt;/em&gt;&lt;/strong&gt;, нажимаем &lt;strong&gt;&lt;em&gt;Change&lt;/em&gt;&lt;/strong&gt;, меняем, ???, PROFIT.&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/ssl-monitoring-zabbix/image-16.png&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Теперь триггер будет срабатывать на измененное количество дней.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h1 class=&#34;relative group&#34;&gt;Добавляем поле Days Left &#xA;    &lt;div id=&#34;%D0%B4%D0%BE%D0%B1%D0%B0%D0%B2%D0%BB%D1%8F%D0%B5%D0%BC-%D0%BF%D0%BE%D0%BB%D0%B5-days-left&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%B4%D0%BE%D0%B1%D0%B0%D0%B2%D0%BB%D1%8F%D0%B5%D0%BC-%D0%BF%D0%BE%D0%BB%D0%B5-days-left&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h1&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/ssl-monitoring-zabbix/SCR-20230513-thou-2.png&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Я хотел сделать себе виджет для дашборда, на котором видно &lt;strong&gt;сколько дней осталось до конца срока действия сертификата&lt;/strong&gt;. К сожалению в изначальном шаблоне нет такого поля; &lt;strong&gt;только поле с датой окончания&lt;/strong&gt;. Я решил этот вопрос добавлением  _&lt;strong&gt;Calculated field&lt;/strong&gt; _ — это тип item в Zabbix, который рассчитывается на основе других items.&lt;/p&gt;&#xA;&lt;p&gt;Идем в наш основной шаблон &lt;strong&gt;&lt;em&gt;Configuration&lt;/em&gt;&lt;/strong&gt; - &lt;strong&gt;&lt;em&gt;Templates&lt;/em&gt;&lt;/strong&gt; - &lt;strong&gt;&lt;em&gt;Website certificate by Zabbix agent 2&lt;/em&gt;&lt;/strong&gt;. Там переходим во вкладку &lt;strong&gt;&lt;em&gt;Items&lt;/em&gt;&lt;/strong&gt; и нажимаем вверху справа &lt;strong&gt;&lt;em&gt;Create Item&lt;/em&gt;&lt;/strong&gt;. Вводим настройки нового Item:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Name&lt;/strong&gt;: &lt;code&gt;Cert: Days left&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Type&lt;/strong&gt;: &lt;code&gt;Calculated&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Key&lt;/strong&gt;: &lt;code&gt;cert.days_left&lt;/code&gt; — ключ нашего item&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Type of information&lt;/strong&gt;: &lt;code&gt;Numeric (float)&lt;/code&gt; — у нас будут не целые дни&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Formula&lt;/strong&gt;: &lt;code&gt;round(((last(//cert.not_after) - now()) / 86400),1)&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Units&lt;/strong&gt;: &lt;code&gt;days&lt;/code&gt; — считаем в днях&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Update interval&lt;/strong&gt; — любой, у меня &lt;code&gt;1m&lt;/code&gt; — 1 минута&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/ssl-monitoring-zabbix/image-11.png&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Теперь в &lt;strong&gt;&lt;em&gt;Latest Data&lt;/em&gt;&lt;/strong&gt; у нас появился новый &lt;em&gt;Item,&lt;/em&gt; по которому мы уже сможем создать &lt;em&gt;Trigger&lt;/em&gt;:&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/ssl-monitoring-zabbix/image-12.png&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h1 class=&#34;relative group&#34;&gt;Создаем виджет для дашборда &#xA;    &lt;div id=&#34;%D1%81%D0%BE%D0%B7%D0%B4%D0%B0%D0%B5%D0%BC-%D0%B2%D0%B8%D0%B4%D0%B6%D0%B5%D1%82-%D0%B4%D0%BB%D1%8F-%D0%B4%D0%B0%D1%88%D0%B1%D0%BE%D1%80%D0%B4%D0%B0&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D1%81%D0%BE%D0%B7%D0%B4%D0%B0%D0%B5%D0%BC-%D0%B2%D0%B8%D0%B4%D0%B6%D0%B5%D1%82-%D0%B4%D0%BB%D1%8F-%D0%B4%D0%B0%D1%88%D0%B1%D0%BE%D1%80%D0%B4%D0%B0&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h1&gt;&#xA;&lt;p&gt;Рекомендую заранее &lt;strong&gt;объединить&lt;/strong&gt; наши хоста для мониторинга SSL сертификатов &lt;strong&gt;в одну Host Group&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Создадим новый дашборд&lt;/strong&gt; или добавим виджет на имеющийся.&lt;/p&gt;&#xA;&lt;p&gt;Переходим в левом меню в &lt;strong&gt;&lt;em&gt;Monitoring&lt;/em&gt;&lt;/strong&gt; — &lt;strong&gt;&lt;em&gt;Dashboard,&lt;/em&gt;&lt;/strong&gt; нажимаем &lt;strong&gt;&lt;em&gt;Create Dashboard.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/ssl-monitoring-zabbix/image-17.png&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;После ввода имени и других данных (не особо важных) нажимаем &lt;strong&gt;&lt;em&gt;Add widget&lt;/em&gt;&lt;/strong&gt;:&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/ssl-monitoring-zabbix/image-18.png&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Вводим данные как на скриншоте:&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/ssl-monitoring-zabbix/image-19.png&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;To je to! Мы молодцы. 🎉&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;🙋‍♂️&lt;strong&gt;С вопросами и комментариями&lt;/strong&gt; можно смело приходить в наш &lt;strong&gt;телеграм-чат&lt;/strong&gt; — &lt;a href=&#34;https://t.me/etogeekchat&#34;   target=&#34;_blank&#34;&gt;&#xA;    https://t.me/etogeekchat&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;🌀Так же рекомендую &lt;strong&gt;подписаться на телеграм-канал&lt;/strong&gt;, чтобы не пропускать классные посты и анонсы новых статей — &lt;a href=&#34;https://t.me/etogeek&#34;   target=&#34;_blank&#34;&gt;&#xA;    https://t.me/etogeek&lt;/a&gt;&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Дайджест шортиков — Апрель 2023</title>
      <link>https://etogeek.dev/posts/digest-april-23/</link>
      <pubDate>Tue, 25 Apr 2023 09:02:08 +0000</pubDate>
      <guid>https://etogeek.dev/posts/digest-april-23/</guid>
      <description>&lt;p&gt;Здравствуйте, друзья! Иногда я буду выкладывать подобные посты-дайджесты с крутыми постами из моего &lt;a href=&#34;https://t.me/etogeek&#34;   target=&#34;_blank&#34;&gt;&#xA;    Telegram-канала&lt;/a&gt;.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Использование .new доменов &#xA;    &lt;div id=&#34;%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5-new-%D0%B4%D0%BE%D0%BC%D0%B5%D0%BD%D0%BE%D0%B2&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5-new-%D0%B4%D0%BE%D0%BC%D0%B5%D0%BD%D0%BE%D0%B2&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Мне было сегодня лет, когда я узнал, что:&lt;/p&gt;&#xA;&lt;p&gt;если ввести в адресной строке браузера &lt;a href=&#34;https://doc.new/&#34;   target=&#34;_blank&#34;&gt;&#xA;    doc.new&lt;/a&gt;, то откроется создание нового документа в Google Docs.&lt;/p&gt;&#xA;&lt;p&gt;Еще варианты:🟢 &lt;a href=&#34;http://slide.new/&#34;   target=&#34;_blank&#34;&gt;&#xA;    slide.new&lt;/a&gt; — презентация🟢 &lt;a href=&#34;http://sheet.new/&#34;   target=&#34;_blank&#34;&gt;&#xA;    sheet.new&lt;/a&gt; — таблица🟢 &lt;a href=&#34;http://cal.new/&#34;   target=&#34;_blank&#34;&gt;&#xA;    cal.new&lt;/a&gt; — событие в календаре&lt;/p&gt;&#xA;&lt;p&gt;Да и полный список тут — &lt;a href=&#34;https://whats.new/shortcuts/&#34;   target=&#34;_blank&#34;&gt;&#xA;    https://whats.new/shortcuts/&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Слишком сложный список задач &#xA;    &lt;div id=&#34;%D1%81%D0%BB%D0%B8%D1%88%D0%BA%D0%BE%D0%BC-%D1%81%D0%BB%D0%BE%D0%B6%D0%BD%D1%8B%D0%B9-%D1%81%D0%BF%D0%B8%D1%81%D0%BE%D0%BA-%D0%B7%D0%B0%D0%B4%D0%B0%D1%87&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D1%81%D0%BB%D0%B8%D1%88%D0%BA%D0%BE%D0%BC-%D1%81%D0%BB%D0%BE%D0%B6%D0%BD%D1%8B%D0%B9-%D1%81%D0%BF%D0%B8%D1%81%D0%BE%D0%BA-%D0%B7%D0%B0%D0%B4%D0%B0%D1%87&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;📖 Одна из причин, почему мы не следуем нашему списку задач или плану на день — в нем слишком сложные задачи.&lt;/p&gt;&#xA;&lt;p&gt;Мозг реально боится в него заходить и думает «да чего  смотреть, там сложные штуки написаны». Конечно, нашему разуму намного проще воспринять и начать делать задачку вроде «назначить синк-встречу с Ваней», чем более абстрактную — «спланировать спринт».&lt;/p&gt;&#xA;&lt;p&gt;Да, задача со встречей — это всего лишь первый этап большой задачи по планированию, но именно декомпозиция на более мелкие задачи помогает нашему мозгу не бояться списка, а смотреть в него и делать по чуть-чуть.&lt;/p&gt;&#xA;&lt;p&gt;А если ты делаешь по чуть-чуть, но регулярно, ты достигаешь успеха с перспективе. Это касается как крупных задач, так и привычек.&lt;/p&gt;&#xA;&lt;p&gt;➡ «&lt;a href=&#34;__GHOST_URL__/zakon-homyachka/&#34;  &gt;&#xA;    Кто не роет, у того нет норки&lt;/a&gt;»&lt;/p&gt;&#xA;&lt;p&gt;Еще, кстати, помогает правильное формирование названия задачи в списке.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Плагин Todoist для Raycast &#xA;    &lt;div id=&#34;%D0%BF%D0%BB%D0%B0%D0%B3%D0%B8%D0%BD-todoist-%D0%B4%D0%BB%D1%8F-raycast&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%BF%D0%BB%D0%B0%D0%B3%D0%B8%D0%BD-todoist-%D0%B4%D0%BB%D1%8F-raycast&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Продолжаем путешествие по плагинам для Raycast.&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/digest-april-23/image.png&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Кто-то знает, что я все личные и рабочие задачки записываю в Todoist. Конечно же на автомате я установил и плагин для него в Raycast. Честно, так ни разу и не воспользовался, потому что Natural Language нет.&lt;/p&gt;&#xA;&lt;p&gt;НО затем я нашел команду Menu Bar Tasks. Короче, только ради нее стоит ставить этот плагин:&lt;/p&gt;&#xA;&lt;p&gt;➡ &lt;a href=&#34;https://www.raycast.com/thomaslombart/todoist&#34;   target=&#34;_blank&#34;&gt;&#xA;    Todoist Plugin&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;Команда закидывает иконку Тудуиста в Menu Bar и отображает там нужные тебе задачи, с возможностью управлять. Выглядит симпатично, а главное удобно. Этим мы пользуемся.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Автоматизируй &#xA;    &lt;div id=&#34;%D0%B0%D0%B2%D1%82%D0%BE%D0%BC%D0%B0%D1%82%D0%B8%D0%B7%D0%B8%D1%80%D1%83%D0%B9&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%B0%D0%B2%D1%82%D0%BE%D0%BC%D0%B0%D1%82%D0%B8%D0%B7%D0%B8%D1%80%D1%83%D0%B9&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;👍 Сегодня железные человечки из дата-центра наконец-то донастроили нам три новых сервера (уф, там неплохое железо). Заказал новые, чтобы проапгрейдить старые. Вот хочу немного верхнеуровнево осветить процесс автоматизации настройки.&lt;/p&gt;&#xA;&lt;p&gt;И вроде задача несложная:🟢&lt;strong&gt;добавить в инфраструктуру&lt;/strong&gt; и &lt;strong&gt;базово настроить&lt;/strong&gt; несколько новых серверов.&lt;/p&gt;&#xA;&lt;p&gt;Это могут быть как железные серверы (так называемые bare metal - голое железо), так и свеже созданные виртуальные машины.&lt;/p&gt;&#xA;&lt;p&gt;Сначала на них надо установить операционную систему. В моем случае сотрудники дата-центра делают это за меня во время настройки и монтажа железа, но когда я учился на мейл-рушном «SRE-квесте» мы практиковались в установке ОС по сети (через PXE), и максимально старались автоматизировать установку через Kickstart.&lt;/p&gt;&#xA;&lt;p&gt;Для ускорения настройки виртуальных машин можно использовать клонирование машин из некого стандартного образа диска.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;PXE&lt;/strong&gt; — это когда загрузочный образ подключается не как iso с USB, а как набор файлов по сети.&lt;strong&gt;Kickstart&lt;/strong&gt; — это определенный набор скриптов, который позволяет не вводить никакие данные во время установки, а получить стандартизированный настроенный сервер через несколько минут. Короче, автоматическая установка ОС.&lt;/p&gt;&#xA;&lt;p&gt;Дальше ОС нужно донастроить — создать пользователей, установить базовый необходимый софт, установить агенты для мониторинга. Конечно ты можешь и вручную все сделать, но мы тут не для этого работаем. Простор для творчества огромный, но по сути все сводится к Infrastructure as Code, когда ты в файлах описываешь желаемое состояние твоей системы, а специальный инструмент приводит систему к этому состоянию.&lt;/p&gt;&#xA;&lt;p&gt;Да-да, Ansible, Puppet, Chef, Salt… вот эти все ребята. Степень автоматизации тоже меняется: у тебя могут быть просто плейбуки, которые ты руками запускаешь для полной настройки, а может быть полный набор ролей или модулей, и в общем манифесте ты указываешь что должно быть установлено и настроено на сервере.&lt;/p&gt;&#xA;&lt;p&gt;Те же плейбуки могут запускаться и вручную с твоего ноутбука, через Job в Jenkins, через специализированные инструменты вроде Ansible Tower и даже просто в runner после коммита в master-ветку в git.&lt;/p&gt;&#xA;&lt;p&gt;У меня к примеру есть небольшой плейбук, который я руками запускаю своего ноутбука: он базово настраивает сервисного пользователя, ставит обязательный софт. Затем я добавляю новый сервер в общий инвентори-файл, и после коммита в git автоматически запустится магия, которая донастроит все что мне нужно.&lt;/p&gt;&#xA;&lt;p&gt;Подобную систему автоматизации установки и настройки серверов довольно сложно и трудозатратно настраивать с нуля, ведь нужно предусмотреть множество факторов: версии ОС, разное железо, сетевые настройки, firewall, разные версии софта… НО, какой же ты получаешь кайф, когда видишь свой первый настроенный «по кнопке» сервер 🔥&lt;/p&gt;&#xA;&lt;p&gt;➡Автоматизируй.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Guess the game &#xA;    &lt;div id=&#34;guess-the-game&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#guess-the-game&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/digest-april-23/image-1.png&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Так, сегодня пятница, поэтому будем угадывать игры 🎮&lt;/p&gt;&#xA;&lt;p&gt;Как Wordle, только про игры. Осторожно, залипательно!&lt;/p&gt;&#xA;&lt;p&gt;➡ &lt;a href=&#34;https://guessthe.game/&#34;   target=&#34;_blank&#34;&gt;&#xA;    https://guessthe.game/&lt;/a&gt; Энджой!&lt;/p&gt;&#xA;&lt;p&gt;p.s. там внизу есть кнопка, чтобы поиграть в &amp;ldquo;предыдущие дни&amp;rdquo;.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Не усложняй</title>
      <link>https://etogeek.dev/posts/dont-complicate/</link>
      <pubDate>Tue, 25 Apr 2023 09:01:04 +0000</pubDate>
      <guid>https://etogeek.dev/posts/dont-complicate/</guid>
      <description>&lt;p&gt;🤔 &lt;strong&gt;Так ли нужны нам сложные решения?&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;Всегда ли нужен Кубер в современных проектах? Обязательно ли разворачивать ELK-стек для сбора логов?&lt;/p&gt;&#xA;&lt;p&gt;Обычно рабочие инструменты не внедряются просто так — они призваны решить какую-то проблему. Нормальный человек не будет настраивать Kubernetes, когда проект еще даже не завернут в контейнеры, аргументируя тем, что это модно. А может Kubernetes в проекте вообще не нужен и достаточно Compose или Swarm? ¯&lt;em&gt;(ツ)&lt;/em&gt;/¯&lt;/p&gt;&#xA;&lt;p&gt;👉 &lt;strong&gt;Нужно понимать какой инструмент подходит лучше для решения конкретной проблемы&lt;/strong&gt;, &lt;strong&gt;в чем его плюсы и минусы&lt;/strong&gt;, &lt;strong&gt;насколько «дорогим» выйдет его внедрение&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Причем под дороговизной я понимаю косвенно-финансовую составляющую чего-либо: сколько времени и сил команда потратит на внедрение, обучение, поддержку решения. Человеко-часы тоже платные.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;🥲 Один из запомнившихся случаев&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;В свое время у меня был опыт установки Kubernetes. Ну, на самом деле, не так сложно поднять простой кластер на виртуальных машинах, базово настроить его и задеплоить что-нибудь. И вот на новом месте появилась задача 🟢 &lt;strong&gt;динамически запускать тесты на разных машинах&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Совместно с разработчиком собрали в контейнеры эти тесты, которые раньше запускались на специальных виртуалках. Затем я поднял небольшой кластер Kubernetes на виртуалках, просто потому, что я уже умел это делать и это выглядело хорошим решением для оркестрации (управления) контейнерами на разных машинах.&lt;/p&gt;&#xA;&lt;p&gt;Потратил день, чтобы интегрировать Jenkins с Кубером, затем поразбирался как джобы создавать. Перевел тесты на новую схему, проверил. И знаешь, все отлично работает и по сей день. Тесты теперь гоняются одинаково по времени, нормально распределены по машинам — то что и хотели. 🦾&lt;/p&gt;&#xA;&lt;p&gt;Только вот не через чур ли сложное решение — поднимать кубер для запуска тестов? Это же виртуалка под мастер-ноду, виртуалки под воркеров. Благо разработчиков не пришлось учить — мы же только тесты запускаем, а это я настраиваю. (привет, бас-фактор 🚌)&lt;/p&gt;&#xA;&lt;p&gt;А в целом пользоваться им кто-то кроме меня умеет? Контейнерами еще худо бедно многие разработчики умеют пользоваться, а вот Kubernetes зачастую видят в первый раз. Какие там деплойменты, сервисы, ингрессы, вы чего…&lt;/p&gt;&#xA;&lt;p&gt;И вот другая задача — примерно тоже самое, 🟢 &lt;strong&gt;динамически запускать контейнеры на нескольких машинах&lt;/strong&gt;, иметь возможность &lt;strong&gt;горизонтально масштабироваться&lt;/strong&gt;. Решил попробовать поставить &lt;strong&gt;Docker Swarm&lt;/strong&gt; — ни разу раньше не пользовался 😬.&lt;/p&gt;&#xA;&lt;p&gt;Оказалось, что кластер инициализируется за минуту одной командой, ноды в него влетают другой командой, а задеплоить сервис можно просто подкинув уже имеющийся docker-compose.yml. Так еще и нет пердолинга с сетью — сервис будет доступен по указанному порту на любой из нод кластера.&lt;/p&gt;&#xA;&lt;p&gt;Добавил к нему &lt;a href=&#34;https://swarmpit.io/&#34;   target=&#34;_blank&#34;&gt;&#xA;    Swarmpit&lt;/a&gt;, чтобы кластером можно было управлять из веб-интерфейса.&lt;/p&gt;&#xA;&lt;p&gt;Крутим там сейчас сервисы, которые иногда требуют масштабирования — все отлично работает. Разработчикам проще — есть веб-интерфейс, это более понятные «просто контейнеры», еще и docker-compose синтаксис. Затрат на поддержку меньше.&lt;/p&gt;&#xA;&lt;p&gt;Следующий этап 🟠 &lt;strong&gt;выпилить к чертям Kubernetes&lt;/strong&gt;, о котором тут речь, и поднять на его месте свежий Swarm-кластер. Заодно попробую Portainer для удобного взаимодействия с кластером, вместо Swarmpit.&lt;/p&gt;&#xA;&lt;p&gt;Подумай, а так ли тебе нужен сложный комплексный инструмент, когда можно обойтись более простым?&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Сканер открытых портов в Raycast</title>
      <link>https://etogeek.dev/posts/raycast-open-ports-scanner/</link>
      <pubDate>Thu, 30 Mar 2023 18:30:12 +0000</pubDate>
      <guid>https://etogeek.dev/posts/raycast-open-ports-scanner/</guid>
      <description>&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/raycast-open-ports-scanner/feature.jpg&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;В этой статьей я покажу, как можно добавить свой скрипт в Raycast на примере проверки открытых портов на серверах.&lt;/p&gt;&#xA;&lt;p&gt;Я очень часто пользуюсь сервисами для проверки открытых портов на серверах, чтобы быстро понять закрыт ли у меня определенный порт на сервере или нет. Для этого в закладках держал вот этот сайт &lt;a href=&#34;https://www.yougetsignal.com/tools/open-ports/&#34;   target=&#34;_blank&#34;&gt;&#xA;    https://www.yougetsignal.com/tools/open-ports/&lt;/a&gt; (один из многих, на которых можно проверять порты)&lt;/p&gt;&#xA;&lt;p&gt;Не так давно &lt;a href=&#34;https://t.me/etogeek/156&#34;   target=&#34;_blank&#34;&gt;&#xA;    писал про Raycast&lt;/a&gt; как замену для Alfred, и про то, что я довольно быстро разобрался как добавить свой скрипт в быстрый запуск. Собственно, даже упоминал этот скрипт в том посте, но хочу рассказать чуть подробнее.&lt;/p&gt;&#xA;&lt;p&gt;В общем, так как мне нравится &lt;a href=&#34;https://www.yougetsignal.com/tools/open-ports/&#34;   target=&#34;_blank&#34;&gt;&#xA;    тот&lt;/a&gt; сайт, я посмотрел через «консоль разработчика» какой запрос делается в момент нажатия на кнопку Check.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Создаем скрипт &#xA;    &lt;div id=&#34;%D1%81%D0%BE%D0%B7%D0%B4%D0%B0%D0%B5%D0%BC-%D1%81%D0%BA%D1%80%D0%B8%D0%BF%D1%82&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D1%81%D0%BE%D0%B7%D0%B4%D0%B0%D0%B5%D0%BC-%D1%81%D0%BA%D1%80%D0%B8%D0%BF%D1%82&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Оставалось только немного поэкспериментировать с параметрами curl запроса и получился такой:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;curl -s -X POST https://ports.yougetsignal.com/check-port.php&lt;span class=&#34;se&#34;&gt;\?&lt;/span&gt;remoteAddress&lt;span class=&#34;se&#34;&gt;\=&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$1&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\&amp;amp;&lt;/span&gt;portNumber&lt;span class=&#34;se&#34;&gt;\=&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$2&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;$1&lt;/code&gt; и &lt;code&gt;$2&lt;/code&gt; это аргументы, которые были переданы .sh скрипту при запуске (например &lt;code&gt;scripts.sh foo bar&lt;/code&gt;. &lt;code&gt;$1 = foo&lt;/code&gt;, &lt;code&gt;$2 = bar&lt;/code&gt; и так далее. &lt;code&gt;$0&lt;/code&gt; это всегда название файла)&lt;/p&gt;&#xA;&lt;p&gt;Нам остается только «выцепить» Open или Closed из ответа. Делаю через &lt;code&gt;grep -o &amp;quot;Open\|Closed&amp;quot;&lt;/code&gt;. Параметр &lt;code&gt;-o&lt;/code&gt; выведет только ту строку, которая матчится с регуляркой, а у нас там «или Open или Closed».&lt;/p&gt;&#xA;&lt;p&gt;Объединяю две команды (&lt;code&gt;curl&lt;/code&gt; и &lt;code&gt;grep&lt;/code&gt;)  через пайп (символ &lt;code&gt;|&lt;/code&gt;). Пайп (&lt;em&gt;pipeline&lt;/em&gt;) переводит &lt;strong&gt;стандартный вывод&lt;/strong&gt; (&lt;em&gt;stdout&lt;/em&gt;) первой команды на &lt;strong&gt;стандартный ввод&lt;/strong&gt; (&lt;em&gt;stdin&lt;/em&gt;) второй.&lt;/p&gt;&#xA;&lt;p&gt;Добавляем эту команду в наш &lt;code&gt;.sh&lt;/code&gt; файл, не забывая &lt;a href=&#34;https://ru.wikipedia.org/wiki/%d0%a8%d0%b5%d0%b1%d0%b0%d0%bd%d0%b3_%28Unix%29&#34;   target=&#34;_blank&#34;&gt;&#xA;    шебанг&lt;/a&gt;  &lt;code&gt;#!/bin/bash&lt;/code&gt;.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Добавляем скрипт в Raycast &#xA;    &lt;div id=&#34;%D0%B4%D0%BE%D0%B1%D0%B0%D0%B2%D0%BB%D1%8F%D0%B5%D0%BC-%D1%81%D0%BA%D1%80%D0%B8%D0%BF%D1%82-%D0%B2-raycast&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%B4%D0%BE%D0%B1%D0%B0%D0%B2%D0%BB%D1%8F%D0%B5%D0%BC-%D1%81%D0%BA%D1%80%D0%B8%D0%BF%D1%82-%D0%B2-raycast&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Но, что сделать, чтобы Raycast «увидел» наш скрипт?&lt;/p&gt;&#xA;&lt;p&gt;Нужно добавить некоторую мета-информацию — &lt;strong&gt;название&lt;/strong&gt;, &lt;strong&gt;поля для интерактивного ввода&lt;/strong&gt;, &lt;strong&gt;документацию&lt;/strong&gt;&amp;hellip; Внимательнее можно посмотреть на нее в &lt;a href=&#34;https://github.com/etoosamoe/raycast-scripts/blob/master/check-open-port.sh&#34;   target=&#34;_blank&#34;&gt;&#xA;    моем скрипте&lt;/a&gt;, должно быть все понятно. Или в официальных &lt;a href=&#34;https://www.raycast.com/blog/getting-started-with-script-commands&#34;   target=&#34;_blank&#34;&gt;&#xA;    примерах&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/raycast-open-ports-scanner/image-4.png&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Затем заходим в &lt;strong&gt;настройки Raycast&lt;/strong&gt; (&lt;code&gt;Cmd&lt;/code&gt; + &lt;code&gt;,&lt;/code&gt;).&lt;/p&gt;&#xA;&lt;p&gt;Открываем вкладку &lt;strong&gt;Extensions&lt;/strong&gt;, ищем там пункт &lt;strong&gt;Script Command&lt;/strong&gt; (&lt;em&gt;через поиск почему-то не находится&lt;/em&gt;) и нажимаем на него. Справа будет &lt;strong&gt;Add directories&lt;/strong&gt;, в ней и добавляем директорию с нашим скриптом.&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/raycast-open-ports-scanner/raycast1.jpg&#34;&#xA;        alt=&#34;dsadsa&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/raycast-open-ports-scanner/raycast2.jpg&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Сразу вешаем на него какой-нибудь короткий &lt;strong&gt;Alias&lt;/strong&gt;:&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/raycast-open-ports-scanner/image-5.png&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Если добавить новые скрипты в эту директорию, то они автоматически появятся в Raycast. Максимум — нужно будет запустить команду &lt;strong&gt;Reload Script Directories&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Теперь пробуем запустить:&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/raycast-open-ports-scanner/image-6.png&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Единственное с чем я не заморачивался — это цвет «лампочки» внизу — он базируется на коде ответа скрипта, поэтому всегда зеленый, если curl был успешен.&lt;/p&gt;&#xA;&lt;p&gt;Как видишь, добавить свой скрипт в Raycast совсем не сложно. Главное, чтобы была фантазия.&lt;/p&gt;&#xA;&lt;p&gt;Ты можешь скачать мой репозиторий со скриптами и добавить его по инструкции из README. Вот ссылка 👉 &lt;a href=&#34;https://github.com/etoosamoe/raycast-scripts&#34;   target=&#34;_blank&#34;&gt;&#xA;    https://github.com/etoosamoe/raycast-scripts&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;А подписавшись на &lt;a href=&#34;https://t.me/etogeek&#34;   target=&#34;_blank&#34;&gt;&#xA;    мой телеграм-канал&lt;/a&gt; ты точно не пропустишь новые статьи и интересности из интернета ✌️&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Split DNS в MacOS, OpenVPN и Tunnelblick</title>
      <link>https://etogeek.dev/posts/split-dns-macos-openvpn-tunnelblick/</link>
      <pubDate>Wed, 29 Mar 2023 08:34:18 +0000</pubDate>
      <guid>https://etogeek.dev/posts/split-dns-macos-openvpn-tunnelblick/</guid>
      <description>&#xA;  &#xA;&#xA;&#xA;&#xA;&lt;div&#xA;  &#xA;    class=&#34;flex px-4 py-3 rounded-md bg-primary-100 dark:bg-primary-900&#34;&#xA;  &gt;&#xA;&#xA;  &lt;span&#xA;    &#xA;      class=&#34;text-primary-400 ltr:pr-3 rtl:pl-3 flex items-center&#34;&#xA;    &gt;&#xA;&#xA;    &#xA;&#xA;  &lt;span class=&#34;relative block icon&#34;&gt;&#xA;    &lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; viewBox=&#34;0 0 512 512&#34;&gt;&lt;path fill=&#34;currentColor&#34; d=&#34;M506.3 417l-213.3-364c-16.33-28-57.54-28-73.98 0l-213.2 364C-10.59 444.9 9.849 480 42.74 480h426.6C502.1 480 522.6 445 506.3 417zM232 168c0-13.25 10.75-24 24-24S280 154.8 280 168v128c0 13.25-10.75 24-23.1 24S232 309.3 232 296V168zM256 416c-17.36 0-31.44-14.08-31.44-31.44c0-17.36 14.07-31.44 31.44-31.44s31.44 14.08 31.44 31.44C287.4 401.9 273.4 416 256 416z&#34;/&gt;&lt;/svg&gt;&#xA;&#xA;  &lt;/span&gt;&#xA;&#xA;&#xA;  &lt;/span&gt;&#xA;&#xA;  &lt;span&#xA;    &#xA;      class=&#34;dark:text-neutral-300&#34;&#xA;    &gt;В этой статье речь идет исключительно о настройке доступа к внутреннему рабочему защищенному сетевому контуру инфрастркутуры.&lt;/span&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&lt;p&gt;В этой статье я покажу как можно настроить &lt;strong&gt;Split DNS в MacOS&lt;/strong&gt;, и как изменить конфигурацию &lt;strong&gt;Tunnelblick&lt;/strong&gt;, чтобы &lt;strong&gt;автоматически включать Split DNS при подключении к OpenVPN серверу&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Split DNS&lt;/strong&gt; — это возможность отправлять DNS запросы для определенной зоны на определенные DNS серверы.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;👉 Пример использования, с которым столкнулся я &#xA;    &lt;div id=&#34;-%D0%BF%D1%80%D0%B8%D0%BC%D0%B5%D1%80-%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F-%D1%81-%D0%BA%D0%BE%D1%82%D0%BE%D1%80%D1%8B%D0%BC-%D1%81%D1%82%D0%BE%D0%BB%D0%BA%D0%BD%D1%83%D0%BB%D1%81%D1%8F-%D1%8F&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#-%D0%BF%D1%80%D0%B8%D0%BC%D0%B5%D1%80-%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F-%D1%81-%D0%BA%D0%BE%D1%82%D0%BE%D1%80%D1%8B%D0%BC-%D1%81%D1%82%D0%BE%D0%BB%D0%BA%D0%BD%D1%83%D0%BB%D1%81%D1%8F-%D1%8F&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Есть OpenVPN сервер и есть DNS серверы во внутреннем сетевом контуре, которые обслуживают две доменные зоны &lt;code&gt;foobar.com&lt;/code&gt; и &lt;code&gt;.bar&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Зона &lt;code&gt;foobar.com&lt;/code&gt; обслуживается как внутренними DNS-серверами, так и внешними (Cloudflare). Это сделано, чтобы иметь “внутри” приватные DNS записи, но с “реальными” доменными именами.&lt;/p&gt;&#xA;&lt;p&gt;Мы хотим при подключении VPN начинать использовать внутренние  DNS, а при отключении возвращаться обратно. Но мы не хотим при подключенном VPN отправлять все DNS запросы на внутренние серверы.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;❗ Изменение настроек OpenVPN сервера &#xA;    &lt;div id=&#34;-%D0%B8%D0%B7%D0%BC%D0%B5%D0%BD%D0%B5%D0%BD%D0%B8%D0%B5-%D0%BD%D0%B0%D1%81%D1%82%D1%80%D0%BE%D0%B5%D0%BA-openvpn-%D1%81%D0%B5%D1%80%D0%B2%D0%B5%D1%80%D0%B0&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#-%D0%B8%D0%B7%D0%BC%D0%B5%D0%BD%D0%B5%D0%BD%D0%B8%D0%B5-%D0%BD%D0%B0%D1%81%D1%82%D1%80%D0%BE%D0%B5%D0%BA-openvpn-%D1%81%D0%B5%D1%80%D0%B2%D0%B5%D1%80%D0%B0&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Так как мы будем управлять «раздачей» DNS серверов с помощью Tunnelblick, то нам нужно убрать (убедиться, что этого нет) push DNS серверов из конфигурации OpenVPN сервера.&lt;/p&gt;&#xA;&lt;p&gt;За это отвечают следующие инструкции в конфигурации, проверяем, что их нет:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;root@server01.bar /home/admin &lt;span class=&#34;c1&#34;&gt;# cat /etc/openvpn/openvpn.conf | grep dhcp&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;push &lt;span class=&#34;s2&#34;&gt;&amp;#34;dhcp-option DNS 35.36.37.0&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;push &lt;span class=&#34;s2&#34;&gt;&amp;#34;dhcp-option DNS 35.36.37.1&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;push &lt;span class=&#34;s2&#34;&gt;&amp;#34;dhcp-option DOMAIN bar&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;🌐 Настройка resolvers в MacOS &#xA;    &lt;div id=&#34;-%D0%BD%D0%B0%D1%81%D1%82%D1%80%D0%BE%D0%B9%D0%BA%D0%B0-resolvers-%D0%B2-macos&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#-%D0%BD%D0%B0%D1%81%D1%82%D1%80%D0%BE%D0%B9%D0%BA%D0%B0-resolvers-%D0%B2-macos&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Введя команду &lt;code&gt;scutil --dns&lt;/code&gt; мы можем увидеть список текущих резолверов:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;❯ scutil --dns&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;resolver &lt;span class=&#34;c1&#34;&gt;#1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  nameserver&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;0&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; : 192.168.31.1&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  if_index : &lt;span class=&#34;m&#34;&gt;8&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;en0&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  flags    : Request A records&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  reach    : 0x00020002 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;Reachable,Directly Reachable Address&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Это основной текущий резолвер, которой отправляет все DNS запросы на один DNS сервер (мой маршрутизатор).&lt;/p&gt;&#xA;&lt;p&gt;Мы можем насоздавать своих резолверов путем создания файла &lt;code&gt;/etc/resolver/foobar.com&lt;/code&gt;, с точным именем доменной зоны и адресами DNS серверов внутри:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;❯ cat /etc/resolver/foobar.com&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;nameserver 35.36.37.0&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;nameserver 35.36.37.1&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Снова проверяем резолверы. Теперь все DNS запросы для этой зоны пойдут на внутренние серверы, а остальные — как и было раньше через основной резолвер:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;❯ scutil --dns&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;resolver &lt;span class=&#34;c1&#34;&gt;#8&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  domain   : foobar.com&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  nameserver&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;0&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; : 35.36.37.0&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  nameserver&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;1&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; : 35.36.37.1&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  flags    : Request A records&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  reach    : 0x00000002 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;Reachable&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;🌀 Настройка Tunnelblick &#xA;    &lt;div id=&#34;-%D0%BD%D0%B0%D1%81%D1%82%D1%80%D0%BE%D0%B9%D0%BA%D0%B0-tunnelblick&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#-%D0%BD%D0%B0%D1%81%D1%82%D1%80%D0%BE%D0%B9%D0%BA%D0%B0-tunnelblick&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Чтобы добавлять резолверы автоматически при подключении VPN и удалять при отключении, нам нужно добавить скрипты в конфигурацию Tunnelblick.&lt;/p&gt;&#xA;&lt;p&gt;🔹 Создаем отдельную директорию &lt;code&gt;mkdir vpn&lt;/code&gt;, а в ней файл скрипта, который будет запускаться при подключении VPN&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;nano connected.sh&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#!/bin/bash -e&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mkdir -p /etc/resolver&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;cat &lt;span class=&#34;s&#34;&gt;&amp;lt;&amp;lt;EOF &amp;gt; /etc/resolver/foobar.com&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;nameserver 35.36.37.0&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;nameserver 35.36.37.1&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;EOF&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;cat &lt;span class=&#34;s&#34;&gt;&amp;lt;&amp;lt;EOF &amp;gt; /etc/resolver/bar&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;nameserver 35.36.37.0&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;nameserver 35.36.37.1&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;EOF&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;🔹 Создаем скрипт, который будет запускаться при отключении VPN &lt;code&gt;nano post-disconnect.sh&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#!/bin/bash -e&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rm /etc/resolver/foobar.com&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rm /etc/resolver/bar&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Эти скрипты будут запускаться от пользователя root, если не указано обратное в конфигурации. Документация по скриптам &lt;a href=&#34;https://tunnelblick.net/cUsingScripts.html&#34;   target=&#34;_blank&#34;&gt;&#xA;    есть на оф. сайте&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;🔹 Добавляем в директорию файл с &lt;code&gt;.ovpn&lt;/code&gt; конфигурацией. Предполагается, что мы ее уже имеем.&lt;/p&gt;&#xA;&lt;p&gt;🔹 Делаем скрипты исполняемыми:&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;chmod +x connected.sh post-disconnect.sh&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;🔹 Переименовываем, а точнее добавляем к названию директории расширение &lt;code&gt;.tblk&lt;/code&gt;. У нас получается файл &lt;code&gt;vpn.tblk&lt;/code&gt;, на который можно нажать правым кликом и посмотреть содержимое &lt;strong&gt;Show Package Contents&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;p&gt;🔹 Перетаскиваем файл &lt;code&gt;vpn.tblk&lt;/code&gt; мышкой на иконку Tunnelblick в Menu Bar (наверху). Таким образом мы добавляем новую конфигурацию в Tunnelblick. Разработчики обращают внимание именно на этот способ — им виднее.&lt;/p&gt;&#xA;&lt;p&gt;🔹 Подтверждаем несколько раз, что готовы к вероятности наличия &lt;a href=&#34;https://ru.wikipedia.org/wiki/Fork-%D0%B1%D0%BE%D0%BC%D0%B1%D0%B0&#34;   target=&#34;_blank&#34;&gt;&#xA;    fork-бомбы&lt;/a&gt; или &lt;code&gt;rm -rf&lt;/code&gt; в скриптах, и пробуем подключиться. После успешного подключения проверяем наличие новых резолверов, а так же успешные ping до внутренних DNS.&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Важно: проверка DNS записи через &lt;code&gt;dig&lt;/code&gt; выдаст ошибку NXDOMAIN, потому что &lt;code&gt;dig&lt;/code&gt; использует другой механизм резолва адресов.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;🙌 Дополнительно &#xA;    &lt;div id=&#34;-%D0%B4%D0%BE%D0%BF%D0%BE%D0%BB%D0%BD%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#-%D0%B4%D0%BE%D0%BF%D0%BE%D0%BB%D0%BD%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Обычно я в настройках Tunnelblick-а дополнительно включаю перезапуск сетевых интерфейсов, для большей стабильности работы интернета при засыпании ноутбука и отключениях VPN:&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/split-dns-macos-openvpn-tunnelblick/image-3.png&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;На этом всё 🤝 Если тебе понравилась статья, рекомендую подписаться на телеграм-канал, где анонсятся все новые статьи, а так же выходят интересные посты. Ссылка ниже:&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://t.me/etogeek&#34;   target=&#34;_blank&#34;&gt;&#xA;    @etogeek&lt;/a&gt;&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Как практиковаться начинающему DevOps-инженеру</title>
      <link>https://etogeek.dev/posts/devops-how-to-practice/</link>
      <pubDate>Sat, 25 Mar 2023 17:28:03 +0000</pubDate>
      <guid>https://etogeek.dev/posts/devops-how-to-practice/</guid>
      <description>&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/devops-how-to-practice/feature.jpg&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;«Я учусь на DevOps-а, но как мне практиковаться?»&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;Частая ситуация, когда ты хочешь быть DevOps-инженером, но не знаешь на чем тренироваться. В вакансиях требуют реальный опыт, ну или хотя бы демонстрацию знаний, а на текущей работе таких задач не дают.&lt;/p&gt;&#xA;&lt;p&gt;В этой статье я подготовил небольшой роад-мап или список задач, по которым ты можешь получить начальный опыт. А если ты уже работаешь с чем-то, то попрактиковаться с новыми инструментами. Он довольно универсальный, поэтому в каждый пункт можно сильно углубляться.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Напоминание один&lt;/strong&gt;: Старайся конспектировать каждый этап. Все действия и команды. Поверь, это пригодится. Я обращался к своим записям бесчисленное количество раз.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Напоминание два&lt;/strong&gt;: На каждом этапе пытайся разобраться, а что изменится, если ты выберешь другой инструмент. Какие плюсы и минусы будут? Например, &lt;strong&gt;Gitlab CI&lt;/strong&gt; или &lt;strong&gt;Jenkins&lt;/strong&gt;, &lt;strong&gt;Ansible&lt;/strong&gt; или &lt;strong&gt;Puppet&lt;/strong&gt;, &lt;strong&gt;Zabbix&lt;/strong&gt; или &lt;strong&gt;Prometheus&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;p&gt;У меня был микро-конспект видео с канала Techworld with Nana про изучение новых инструментов и технологий — 👉 &lt;a href=&#34;https://t.me/etogeek/91&#34;   target=&#34;_blank&#34;&gt;&#xA;    пост в телеграм-канале&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Вот пример задач, в каждой из которых можно найти множество подводных камней, решение которых и даст тебе заветный опыт. Расставлены в порядке увеличения сложности:&lt;/p&gt;&#xA;&lt;p&gt;🖥️ &lt;strong&gt;Создать виртуальную машину&lt;/strong&gt; — это можно сделать как на своем компьютере/ноутбуке, так и создать в каком-нибудь облаке. Некоторые облачные провайдеры дают бесплатный период — это удобно для обучения. В первом случае мы еще и научимся базово устанавливать Linux.&lt;/p&gt;&#xA;&lt;p&gt;🔑 &lt;strong&gt;Разобраться с настройкой ОС&lt;/strong&gt;, &lt;strong&gt;установкой пакетов&lt;/strong&gt;, научиться настраивать SSH и подключаться к серверу с помощью ключей.&lt;/p&gt;&#xA;&lt;p&gt;🗯️ &lt;strong&gt;Найти несложный проект на Github&lt;/strong&gt;, который ты будешь препарировать. Лучше, если это будет приложение, которое можно «повесить» на какой-нибудь порт, и оно будет что-то отдавать. Хотя бы &lt;code&gt;{”status”: “ok”}&lt;/code&gt; или html-страничку с Hello World. Например, мой — &lt;a href=&#34;https://t.me/etogeek/214&#34;   target=&#34;_blank&#34;&gt;&#xA;    писал о нем&lt;/a&gt; в телеграм-канале.&lt;/p&gt;&#xA;&lt;p&gt;Язык на котором написан сервис не особо важен — что тебе больше нравится. Во многих курсах используется Java. Вероятно потому, что в &lt;strong&gt;Java&lt;/strong&gt; у тебя будет выделенный этап сборки твоего кода в &lt;code&gt;.jar&lt;/code&gt; файл с помощью какого-нибудь Maven или Gradle. В Python, например, такого не будет. Я бы рекомендовал начать с Java, просто чтобы лучше ознакомиться с процессом.&lt;/p&gt;&#xA;&lt;p&gt;Искать можно по запросам типа «simple python web application». Например:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://gitlab.com/devops-bootcamp3&#34;   target=&#34;_blank&#34;&gt;&#xA;    https://gitlab.com/devops-bootcamp3&lt;/a&gt; — тут целая подборка на разных языках&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/noqcks/java-hello-world-api&#34;   target=&#34;_blank&#34;&gt;&#xA;    https://github.com/noqcks/java-hello-world-api&lt;/a&gt; — что-нибудь подобное&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://medium.com/@ismailvohra/how-to-create-a-simple-hello-world-rest-api-in-spring-boot-a89e75cbb48b&#34;   target=&#34;_blank&#34;&gt;&#xA;    https://medium.com/@ismailvohra/how-to-create-a-simple-hello-world-rest-api-in-spring-boot-a89e75cbb48b&lt;/a&gt; — можно и свой написать :)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;🖐️ &lt;strong&gt;Попробовать запустить этот сервис руками на виртуальной машине&lt;/strong&gt;. То есть нам нужно скачать репозиторий с кодом, собрать приложение и запустить его.&lt;/p&gt;&#xA;&lt;p&gt;🛠️ Когда у нас сформировался список действий, которые нужно сделать для разворачивания приложения — &lt;strong&gt;можно описать это все в Ansible&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;p&gt;🧑🏻‍💻 &lt;strong&gt;Добавить к приложению systemd-сервис&lt;/strong&gt;, чтобы его было проще запускать и контролировать его работу. Разобраться с автозапуском, переменными окружения. Узнать какие еще бывают системы инициализации.&lt;/p&gt;&#xA;&lt;p&gt;🤖 &lt;strong&gt;Автоматизировать деплой проекта&lt;/strong&gt; — сделать репозиторий в Gitlab или GitHub, установить &lt;a href=&#34;https://docs.gitlab.com/runner/&#34;   target=&#34;_blank&#34;&gt;&#xA;    self-hosted runner&lt;/a&gt; и по документации набросать пайплайн сборки и запуска приложения. Можно вообще развернуть Jenkins и поизучать его, заодно понять разницу между этими инструментами.&lt;/p&gt;&#xA;&lt;p&gt;📦 Наконец, &lt;strong&gt;написать Dockerfile&lt;/strong&gt; и &lt;strong&gt;собрать приложение в контейнер&lt;/strong&gt;. Разобраться как его запускать, пробрасывать порты, переменные, как подключать директории с виртуалки в контейнер. Изучить бест-практисы создания образов контейнеров, понять разницу между &lt;em&gt;CMD&lt;/em&gt; и &lt;em&gt;ENTRYPOINT&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;p&gt;🚚 &lt;strong&gt;Научиться загружать образы контейнеров в Docker Hub&lt;/strong&gt;. А затем начать использовать приватные Container Registry: поднять &lt;a href=&#34;https://docs.docker.com/registry/deploying/&#34;   target=&#34;_blank&#34;&gt;&#xA;    свой&lt;/a&gt;, или использовать &lt;a href=&#34;https://docs.gitlab.com/ee/user/packages/container_registry/&#34;   target=&#34;_blank&#34;&gt;&#xA;    тот, что есть в Gitlab&lt;/a&gt;. Есть еще и крупные «комбайны» типа &lt;a href=&#34;https://www.sonatype.com/products/nexus-repository&#34;   target=&#34;_blank&#34;&gt;&#xA;    Nexus&lt;/a&gt;, &lt;a href=&#34;https://jfrog.com/artifactory/&#34;   target=&#34;_blank&#34;&gt;&#xA;    Artifactory&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;⛴️ &lt;strong&gt;Посмотреть на Docker Compose&lt;/strong&gt;, разобраться зачем он нужен и как нам может помочь. Попробовать развернуть приложение через compose.&lt;/p&gt;&#xA;&lt;p&gt;🌎 &lt;strong&gt;Установить Nginx, настроить reverse proxy&lt;/strong&gt; для приложения. Понять как работает и &lt;strong&gt;получить SSL сертификат&lt;/strong&gt;. Автоматизировать конфигурацию можно с помощью Ansible, который, кстати, тоже можно запускать из Github/Jenkins.&lt;/p&gt;&#xA;&lt;p&gt;🔥 &lt;strong&gt;Установить Zabbix или Prometheus + Grafana&lt;/strong&gt; (вместо Prometheus можно поставить Victoria Metrics). &lt;strong&gt;Настроить мониторинг виртуальной машины&lt;/strong&gt; — процессора, оперативки, места на дисках, &lt;strong&gt;научиться отслеживать доступность приложения&lt;/strong&gt;, сделать удобный дашборд.&lt;/p&gt;&#xA;&lt;p&gt;📝 &lt;strong&gt;Написать документацию к приложению&lt;/strong&gt;. Подробно, но без воды опиши как его развернуть, какие зависимости установить, что нужно делать. Представь, что ты первый раз видишь это, и хочешь побыстрее разобраться. Упрости себе жизнь.&lt;/p&gt;&#xA;&lt;p&gt;Когда это уже давно пройдено можно переключиться на Advanced уровень:&lt;/p&gt;&#xA;&lt;p&gt;🪵 &lt;strong&gt;Поднять ELK-стэк&lt;/strong&gt; (а может быть &lt;strong&gt;Loki&lt;/strong&gt;?) и настроить сбор логов приложения для более удобного просмотра.&lt;/p&gt;&#xA;&lt;p&gt;☁️ &lt;strong&gt;Развернуть и настроить виртуальные машины в облаке с помощью Terraform&lt;/strong&gt;. В дополнение можно использовать Packer и Ansible.&lt;/p&gt;&#xA;&lt;p&gt;❤️‍🔥 &lt;strong&gt;Развернуть приложение в Kubernetes&lt;/strong&gt;. Начать можно с minikube — такой кубер на минималках для локальной машины. Изучить основы и структуру Kubernetes.&lt;/p&gt;&#xA;&lt;p&gt;В общем, вариантов много. Теоретические знания из статей и видео нужно обязательно закреплять на практике, и если в реальной работе у тебя таких задач нет, то можно максимально приблизиться к реальности вот на таких примерах.&lt;/p&gt;&#xA;&lt;p&gt;🤔 &lt;strong&gt;«И что мне с этим делать потом? Что говорить на собсеседованиях?»&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;во-первых&lt;/strong&gt;, хорошо оформленный репозиторий с плейбуками, пайплайнами и документацией можно действительно показывать работодателю и прикреплять к резюме&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;во-вторых&lt;/strong&gt;, на собеседовании намного лучше сказать «у меня нет продакшн опыта с этим инструментом, но я изучал его самостоятельно, знаю чем он отличается от X и Y, и что даст его внедрение. вот я делал A и B во время изучения»&lt;/p&gt;&#xA;&lt;p&gt;💡 Рекомендую подписаться на &lt;strong&gt;&lt;a href=&#34;https://t.me/etogeek&#34;   target=&#34;_blank&#34;&gt;&#xA;    телеграм-канал&lt;/a&gt;&lt;/strong&gt;, чтобы не пропускать новые посты и интересную информацию.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Миграция Grafana из SQLite в PostgreSQL</title>
      <link>https://etogeek.dev/posts/grafana-migration-to-postgresql/</link>
      <pubDate>Tue, 07 Mar 2023 19:00:17 +0000</pubDate>
      <guid>https://etogeek.dev/posts/grafana-migration-to-postgresql/</guid>
      <description>&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/grafana-migration-to-postgresql/feature.jpg&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Иногда может случиться так, что Grafana перестает показывать графики, показывает в веб-интерфейсе ошибки. Зайдя в логи сервиса ты можешь увидеть частые сообщения:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;error=&amp;#34;database is locked&amp;#34;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Что за ошибка? &#xA;    &lt;div id=&#34;%D1%87%D1%82%D0%BE-%D0%B7%D0%B0-%D0%BE%D1%88%D0%B8%D0%B1%D0%BA%D0%B0&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D1%87%D1%82%D0%BE-%D0%B7%D0%B0-%D0%BE%D1%88%D0%B8%D0%B1%D0%BA%D0%B0&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Опишу так, как это понял я.&lt;/p&gt;&#xA;&lt;p&gt;По умолчанию Grafana использует в качестве базы данных файл SQLite, и все было бы хорошо, если бы не версия 8.0.0, в которой разработчики перелопатили систему alert-ов. При увеличении количества настроенных алертов база данных может не справляться с нагрузкой и блокироваться.&lt;/p&gt;&#xA;&lt;p&gt;Даже разработчики &lt;a href=&#34;https://github.com/grafana/grafana/issues/46142#issuecomment-1406315976&#34;   target=&#34;_blank&#34;&gt;&#xA;    подтверждают&lt;/a&gt; такую проблему:&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/grafana-migration-to-postgresql/image.png&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Первое решение, временное &#xA;    &lt;div id=&#34;%D0%BF%D0%B5%D1%80%D0%B2%D0%BE%D0%B5-%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5-%D0%B2%D1%80%D0%B5%D0%BC%D0%B5%D0%BD%D0%BD%D0%BE%D0%B5&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%BF%D0%B5%D1%80%D0%B2%D0%BE%D0%B5-%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5-%D0%B2%D1%80%D0%B5%D0%BC%D0%B5%D0%BD%D0%BD%D0%BE%D0%B5&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Скорее всего первое, что тебе попадется в Google по этому запросу, это &lt;a href=&#34;https://community.grafana.com/t/database-is-locked-unable-to-use-grafana-anymore/16557&#34;   target=&#34;_blank&#34;&gt;&#xA;    статья&lt;/a&gt; в community Графаны с советом забекапить SQLite базу данных в новый файл, поменять их местами и перезапустить Графану.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sqlite3 grafana.db &lt;span class=&#34;s1&#34;&gt;&amp;#39;.clone grafana-new.db&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mv grafana.db grafana-old.db&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mv grafana-new.db grafana.db&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Такое приходилось делать, когда внезапно перезагружаешь контейнер или машину с графаной. &lt;strong&gt;И оно действительно работает&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Но спустя пару месяцев наши разработчики решили добавить десяток алертов, что привело к не выходящей из состояния блокировки базы данных.&lt;/p&gt;&#xA;&lt;p&gt;Пришлось прибегнуть к&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Второе решение, постоянное &#xA;    &lt;div id=&#34;%D0%B2%D1%82%D0%BE%D1%80%D0%BE%D0%B5-%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5-%D0%BF%D0%BE%D1%81%D1%82%D0%BE%D1%8F%D0%BD%D0%BD%D0%BE%D0%B5&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%B2%D1%82%D0%BE%D1%80%D0%BE%D0%B5-%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5-%D0%BF%D0%BE%D1%81%D1%82%D0%BE%D1%8F%D0%BD%D0%BD%D0%BE%D0%B5&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Это &lt;strong&gt;переехать на другую базу данных&lt;/strong&gt;. Выбираем PostgreSQL.&lt;/p&gt;&#xA;&lt;p&gt;В моем случае Grafana развернута в контейнере с помощью docker-compose, таким образом мы просто развернем рядом еще один контейнер с базой данных. Но есть проблема, мы же хотим сначала перенести SQLite БД в PostgreSQL.&lt;/p&gt;&#xA;&lt;p&gt;Поднимаем контейнер с PostgreSQL 13, подключаем к нему директорию, в которой он будет хранить данные. Я сделаю это с помощью docker-compose.&lt;/p&gt;&#xA;&lt;p&gt;Создаю директорию для хранения данных:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mkdir -p /var/lib/postgresql/data&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Можно также воспользоваться docker volume.&lt;/p&gt;&#xA;&lt;p&gt;Создаю &lt;em&gt;docker-compose.yml&lt;/em&gt;:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;version&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;3.9&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;services&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;postgres&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;postgres:13.10-alpine3.17&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;restart&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;always&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;container_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;postgres-grafana&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;environment&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;POSTGRES_USER=postgres&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;POSTGRES_PASSWORD=superstrongpassword&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ports&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;s1&#34;&gt;&amp;#39;127.0.0.1:5432:5432&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;volumes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;/var/lib/postgresql/data:/var/lib/postgresql/data&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Запускаю его:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker-compose up -d&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Зайдем в PostgreSQL и создадим новую БД:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker &lt;span class=&#34;nb&#34;&gt;exec&lt;/span&gt; -ti postgres-grafana /bin/sh&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;psql&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;CREATE DATABASE grafana&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Теперь нам нужно создать в базе данных таблицы с нужными схемами данных. Для этого мы запустим Grafana и подключим ее к свежей базе. Потом выключим. Добавляю в &lt;em&gt;docker-compose.yml&lt;/em&gt; новый сервис:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;grafana&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;grafana/grafana:version&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;container_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;grafana&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;user&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;2017&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;volumes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;/var/lib/grafana:/var/lib/grafana&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;environment&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;GF_DATABASE_TYPE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;postgres&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;GF_DATABASE_HOST&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;postgres:5432&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;GF_DATABASE_NAME&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;grafana&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;GF_DATABASE_USER&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;postgres&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;GF_DATABASE_PASSWORD&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;superstrongpassword&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;GF_DATABASE_SSL_MODE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;disable&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ports&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;m&#34;&gt;3000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;3000&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Запускаем и мониторим логи, дожидаясь завершения миграций:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker logs -f grafana&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;После того, как миграции закончатся, можно выключить Grafana и приступить к переносу БД SQLite в PostgreSQL.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker-compose stop grafana&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&lt;h1 class=&#34;relative group&#34;&gt;Перенос с помощью pgloader &#xA;    &lt;div id=&#34;%D0%BF%D0%B5%D1%80%D0%B5%D0%BD%D0%BE%D1%81-%D1%81-%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%D1%8C%D1%8E-pgloader&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%BF%D0%B5%D1%80%D0%B5%D0%BD%D0%BE%D1%81-%D1%81-%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%D1%8C%D1%8E-pgloader&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h1&gt;&#xA;&lt;p&gt;Ориентировался я на &lt;a href=&#34;https://polyglot.jamie.ly/programming/2019/07/01/grafana-sqlite-to-postgres.html&#34;   target=&#34;_blank&#34;&gt;&#xA;    статью&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; у меня не получилось&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;Первым делом я решил воспользоваться утилитой pgloader, которую можно установить из стандартных репозиториев:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;apt install pgloader&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Disclaimer&lt;/strong&gt;: именно из-за pgloader пришлось разворачивать PostgreSQL 13, а не 14. Дело в том, что в 14 версии начали использовать метод шифрования паролей по умолчанию &lt;code&gt;scram-sha-256&lt;/code&gt; вместо &lt;code&gt;md5&lt;/code&gt;. pgloader не умеет с ним работать. Можно изменить, но это лишние действия.&lt;/p&gt;&#xA;&lt;p&gt;Создаю файл-скрипт для переноса БД:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;l&#34;&gt;load database&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;from sqlite:///var/lib/grafana.db&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;into postgresql://postgres:superstrongpassword@127.0.0.1/grafana&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;with data only, reset sequences&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;set work_mem to &amp;#39;16MB&amp;#39;, maintenance_work_mem to &amp;#39;512 MB&amp;#39;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Запускаем утилиту:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;pgloader script.load&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;По идее, после этого должно все работать. Но нет. У меня перестала работать авторизация. После логина выбрасывало обратно на ввод логина/пароля.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Перенос с помощью grafana-migrate &#xA;    &lt;div id=&#34;%D0%BF%D0%B5%D1%80%D0%B5%D0%BD%D0%BE%D1%81-%D1%81-%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%D1%8C%D1%8E-grafana-migrate&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%BF%D0%B5%D1%80%D0%B5%D0%BD%D0%BE%D1%81-%D1%81-%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%D1%8C%D1%8E-grafana-migrate&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Как я смог понять проблему — Grafana записывает некоторые значения в БД в виде hex, что не очень хорошо переносится в PostgreSQL с помощью &lt;em&gt;pgloader&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;grafana-migrate&lt;/strong&gt; — это самописная утилита, которая лежит в github &lt;a href=&#34;https://github.com/wbh1/grafana-sqlite-to-postgres&#34;   target=&#34;_blank&#34;&gt;&#xA;    https://github.com/wbh1/grafana-sqlite-to-postgres&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Disclaimer&lt;/strong&gt;: если перед этим ты делал что-то с базой через pgloader, то лучше будет удалить БД, создать снова и провести миграции.&lt;/p&gt;&#xA;&lt;p&gt;Скачиваем бинарник с утилитой:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;wget https://github.com/wbh1/grafana-sqlite-to-postgres/releases/download/v2.2.3/grafana-migrate_linux_amd64-v2.2.3&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Формируем команду и запускаем утилиту:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;./grafana-migrate /var/lib/grafana/grafana.db &lt;span class=&#34;s1&#34;&gt;&amp;#39;postgres://postgres:superstrongpassword@127.0.0.1:5432/grafana?sslmode=disable&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Здесь возникает подводный камень:&lt;/p&gt;&#xA;&lt;p&gt;У меня &lt;code&gt;grafana.db&lt;/code&gt; весит 150 мегабайт.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;На сервере с HDD дисками процедура миграции БД &lt;strong&gt;не закончилась за 12 часов&lt;/strong&gt;.&lt;/li&gt;&#xA;&lt;li&gt;На сервере с SSD дисками это было быстрее, но я не стал ждать дольше часа.&lt;/li&gt;&#xA;&lt;li&gt;На сервере с nvme диском эта процедура заняла чуть меньше 30 минут.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;После этого я просто перенес директорию с данными &lt;code&gt;/var/lib/postgresql/data&lt;/code&gt; с &amp;ldquo;быстрого&amp;rdquo; сервера на другой, где должна была крутиться Grafana.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Полезности &#xA;    &lt;div id=&#34;%D0%BF%D0%BE%D0%BB%D0%B5%D0%B7%D0%BD%D0%BE%D1%81%D1%82%D0%B8&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%BF%D0%BE%D0%BB%D0%B5%D0%B7%D0%BD%D0%BE%D1%81%D1%82%D0%B8&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Давай доформируем окончательно наш &lt;em&gt;docker-compose.yml&lt;/em&gt; файл:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;version&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;3.9&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;services&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;postgres&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;postgres:13.10-alpine3.17&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;restart&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;always&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;container_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;postgres-grafana&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;environment&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;POSTGRES_USER=postgres&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;POSTGRES_PASSWORD=superstrongpassword&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ports&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;s1&#34;&gt;&amp;#39;127.0.0.1:5432:5432&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;volumes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;/var/lib/postgresql/data:/var/lib/postgresql/data&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;healthcheck&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;test&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;CMD-SHELL&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;pg_isready -U postgres&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;interval&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;5s&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;timeout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;5s&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;retries&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;grafana&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;grafana/grafana:9.0.4&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;container_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;grafana&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;user&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;2017&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;depends_on&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;postgres&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;condition&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;service_healthy&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;volumes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;/var/lib/grafana:/var/lib/grafana&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;environment&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;GF_DATABASE_TYPE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;postgres&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;GF_DATABASE_HOST&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;postgres:5432&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;GF_DATABASE_NAME&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;grafana&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;GF_DATABASE_USER&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;postgres&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;GF_DATABASE_PASSWORD&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;superstrongpassword&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;GF_DATABASE_SSL_MODE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;disable&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ports&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;m&#34;&gt;127.0.0.1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;3000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;3000&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Я добавил healthcheck для сервиса postgres, который раз в 5 секунд проверяет, доступна ли база данных. Это удобно на этапе запуска:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;если Grafana запустится до того, как запустится postgres - возникнет ошибка. Параметр depends_on будет ждать, пока сервис postgres не перейдет в состояние healthy.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Так же я закрепил контейнер с Grafana за интерфейсом 127.0.0.1. У меня уже настроен Nginx reverse proxy, который будет проксировать запросы извне, на 127.0.0.1:3000 - в Grafana.&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;🌈 Надеюсь, тебе понравилась статья. Рекомендую подписаться на канал в &lt;strong&gt;Телеграме&lt;/strong&gt;, чтобы не пропускать полезную информацию:&lt;/p&gt;&#xA;&lt;p&gt;👉 &lt;a href=&#34;https://t.me/etogeek&#34;   target=&#34;_blank&#34;&gt;&#xA;    Телеграм канал&lt;/a&gt;&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Не перебивай!</title>
      <link>https://etogeek.dev/posts/do-not-interrupt/</link>
      <pubDate>Tue, 28 Feb 2023 08:51:09 +0000</pubDate>
      <guid>https://etogeek.dev/posts/do-not-interrupt/</guid>
      <description>&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/do-not-interrupt/feature.jpg&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Небольшая история про один из важнейших софт-скиллов. Или как сказали некоторые в телеграм-канале &amp;ldquo;&lt;em&gt;ты бы еще сказал, что нужно штаны снимать в туалете&lt;/em&gt;&amp;rdquo;. Это база, но не все замечают за собой такие проблемы.&lt;/p&gt;&#xA;&lt;p&gt;Года три назад после одного из совещаний ко мне подошел наш рекрутер и сказал, что перебивать технического директора не стоит. Как, собственно, и других людей во время разговора в принципе.&lt;/p&gt;&#xA;&lt;p&gt;Рефлексия по произошедшему показала, что когда человек говорит мне что-то, то в голове я уже давно закончил его фразу и готов слушать следующую или рассказывать свою. &lt;strong&gt;Так происходит не только в работе, но и дома&lt;/strong&gt;. Конечно, всех это бесит.&lt;/p&gt;&#xA;&lt;p&gt;Не то что бы я постоянно увожу разговор в другую степь, хотя такое тоже бывает, чаще я стараюсь именно закончить фразу за собеседника, чтобы он поскорее перешел к продолжению.&lt;/p&gt;&#xA;&lt;p&gt;➡ &lt;strong&gt;Не перебивать — один из важнейших софт-скиллов любого человека&lt;/strong&gt;. Это базовое уважение к твоему собеседнику.&lt;/p&gt;&#xA;&lt;p&gt;Попробуй переложить ситуацию на себя — ты рассказываешь историю, а тебя перебивают или вставляют свои реплики в твою речь. Меня бы это выбесило очень быстро.&lt;/p&gt;&#xA;&lt;p&gt;Именно то замечание как будто открыло мне глаза на весь масштаб ситуации — я не осознавая того перебивал СТО в разговоре (вопрос субординации тут не поднимаю, у всех все по-разному).&lt;/p&gt;&#xA;&lt;p&gt;К сожалению, я не нашел какой-то серебряной пули от этой проблемы — только самоконтроль. Ты сам можешь этого не замечать — можно договориться с близкими или друзьями, чтобы они обращали твое внимание, когда ты перебиваешь кого-то. Я сделал именно так.&lt;/p&gt;&#xA;&lt;p&gt;Если ты думаешь с чего бы начать прокачивание софт-скиллов — &lt;strong&gt;начни с анализа своего поведения в диалогах&lt;/strong&gt;. Поставь себя на место собеседника.&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;Вот &lt;a href=&#34;https://t.me/etogeek&#34;   target=&#34;_blank&#34;&gt;&#xA;    здесь&lt;/a&gt; можно подписаться на новые посты, а так же комментировать их.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>История про принтеры</title>
      <link>https://etogeek.dev/posts/stories-printers01/</link>
      <pubDate>Wed, 15 Feb 2023 17:50:16 +0000</pubDate>
      <guid>https://etogeek.dev/posts/stories-printers01/</guid>
      <description>&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/stories-printers01/feature.jpg&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Году так в 2016 я работал в фармацевтической компании. Это был первый день, а точнее первые сутки, когда я перешел с должности оператора техподдержки, на позицию саппорта. Мы работали сутки-трое и мне предстояла вполне стандартная ночка — переустановить винду пару раз, следить чтобы ничего не упало, а если упадет — звонить кому нужно.&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/stories-printers01/image-2.png&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Специфика работы компании такова, что по ночам на складе кипит работа сильнее, чем днем — там собираются заказы, которые утром поедут в аптеки. К каждому заказу должен быть напечатан комплект документов. Для этого был даже специальный отдел “распечатка”, где стояли несколько огромных принтеров Riso.&lt;/p&gt;&#xA;&lt;p&gt;Это такие бандуры, больше двух метров в длину, которые умеют печатать порядка &lt;strong&gt;120 полноцветных страниц в минуту&lt;/strong&gt;, а если нужно, в конце может &lt;strong&gt;скреплять документы, как степлером&lt;/strong&gt;. Но в таком случае, простите, только 60 в минуту 😭&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/stories-printers01/RISO_ComColor_FT5230-ScannerASFMFF-1-scaled-1.jpg&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Перед ночной сменой меня заверили, что принтеры недавно обслужены, краска свежая стоит, всё будет хорошо. И конечно же в час ночи мне звонят и сообщают, что один из трех принтеров перестал степлировать документы. К моменту, когда я дошел до склада не работали уже два из трех.&lt;/p&gt;&#xA;&lt;p&gt;На меня, видящего эти принтеры первый раз в жизни, смотрят неодобрительно и шутят (или нет), что сейчас сам сяду скреплять бумажки степлером. Я пооткрывал крышки, попротирал датчики — стандартная процедура. Коллега, который хоть немного шарит в этих машинах, уже разбужен и советует попробовать поменять блоки степлирования местами и осмотреть их визуально. meh.&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/stories-printers01/1454577879655726147_30914564-1.jpg&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;В кабинет начинают стекаться местные работники со степлерами в руках, принтер, с которым я не ковыряюсь запускают в обычном режиме — степлировать будут вручную. Мой коллега уже вызвал такси и едет на работу, чтобы спасать мне жизнь.&lt;/p&gt;&#xA;&lt;p&gt;К утру удалось оживить только один из двух сломанных “степлеров”, а местные рабочие действительно сидели и вручную скрепляли листы бумаги. Инженер из сервисной компании уже вызван, и должен приехать в этот же день. Мы сидим в кабинете, упиваемся кофем, ждем начала рабочего дня. Слушаю истории из жизни в компании. До нового года оставалось 5 дней.&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/stories-printers01/image-1.png&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Хорошее время, вспоминаю его с теплотой.&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;👉 Телеграм-канал — @&lt;a href=&#34;https://t.me/etogeek&#34;   target=&#34;_blank&#34;&gt;&#xA;    etogeek&lt;/a&gt;&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Итоги 2022 года</title>
      <link>https://etogeek.dev/posts/2022-summary/</link>
      <pubDate>Mon, 09 Jan 2023 20:59:42 +0000</pubDate>
      <guid>https://etogeek.dev/posts/2022-summary/</guid>
      <description>&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/2022-summary/feature.jpg&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Подвожу итоги прошедшего 2022 года, пусть туда и катится.&lt;/p&gt;&#xA;&lt;p&gt;Горизонт планирования уменьшился до нескольких недель, а в худшее время — до нескольких часов.&lt;/p&gt;&#xA;&lt;p&gt;☕️ &lt;strong&gt;купил кофемашинку&lt;/strong&gt; — капсульная nespresso vertuo — крутая. А потом капсулы перестали продавать ¯_(ツ)_/¯&lt;/p&gt;&#xA;&lt;p&gt;🌍 окончательно &lt;strong&gt;решил&lt;/strong&gt; для себя, что буду &lt;strong&gt;эмигрировать&lt;/strong&gt; в другую страну — всё&lt;/p&gt;&#xA;&lt;p&gt;🤡 &lt;strong&gt;купил тысячу долларов по 120&lt;/strong&gt; — «а вдруг дороже будет»&lt;/p&gt;&#xA;&lt;p&gt;👷‍♂️ &lt;strong&gt;сменил работу&lt;/strong&gt; — теперь занимаюсь даже двумя проектами, значительно увеличил доход&lt;/p&gt;&#xA;&lt;p&gt;🚘 &lt;strong&gt;2000 км за рулем&lt;/strong&gt; — впервые проехал от Москвы до Орска с остановкой в Набережных Челнах&lt;/p&gt;&#xA;&lt;p&gt;🌊 &lt;strong&gt;покатался на сапе&lt;/strong&gt; — взял у друзей, интересно&lt;/p&gt;&#xA;&lt;p&gt;🏕️ &lt;strong&gt;пожил в глэмпинге в Карелии&lt;/strong&gt; — уехали в глушь на машине с собакой, Тусили в огромной сафари-палатке. Очень холодно. Жена чуть ногу не сломала. Но красиво.&lt;/p&gt;&#xA;&lt;p&gt;🇬🇧 &lt;strong&gt;занимался английским&lt;/strong&gt; — три месяца, регулярно. Очень пригодилось.&lt;/p&gt;&#xA;&lt;p&gt;🧵 &lt;strong&gt;написал тред в твиттере&lt;/strong&gt; — первый, который разлетелся на 700+ лайков. Это про ведение дневника. Приятно.&lt;/p&gt;&#xA;&lt;p&gt;🚜 &lt;strong&gt;улетел в Турцию&lt;/strong&gt; без обратного билета — &lt;strong&gt;а потом в Сербию&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;😢 &lt;strong&gt;продали машину&lt;/strong&gt; — вроде ничего особенного, но машина была в кредите и продавалась по доверенности. Я-то был в другой стране. А машину жалко.&lt;/p&gt;&#xA;&lt;p&gt;🚙 &lt;strong&gt;поездил на машине в другой стране&lt;/strong&gt; — взял в аренду в Анталии&lt;/p&gt;&#xA;&lt;p&gt;🎮 &lt;strong&gt;купил Nintendo Switch&lt;/strong&gt; — уж очень портативная и удобная штука. Игры дорогие только.&lt;/p&gt;&#xA;&lt;p&gt;🙋‍♀️ &lt;strong&gt;человек года&lt;/strong&gt; — моя дорогая жена, которая поддерживала меня во всем каждый день&lt;/p&gt;&#xA;&lt;p&gt;🎶 &lt;strong&gt;музыка года&lt;/strong&gt; — &lt;a href=&#34;https://open.spotify.com/track/5tzAAu5xYIZqCVQyAeX4YX?si=1959da6810b44fea&#34;   target=&#34;_blank&#34;&gt;&#xA;    Zola Blood - The Only Thing&lt;/a&gt;, &lt;a href=&#34;https://open.spotify.com/track/0tVp8zfnp5fbnKO9axKaqX?si=d9ade67c65684feb&#34;   target=&#34;_blank&#34;&gt;&#xA;    Amesoeurs - Amesoeurs&lt;/a&gt;, &lt;a href=&#34;https://open.spotify.com/track/5Z5nbOXhsSbySVC7WUc6y9?si=afc356aaac0644ec&#34;   target=&#34;_blank&#34;&gt;&#xA;    Arctic Monkeys - Four Out Of Five&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;🔥 &lt;strong&gt;самое яркое событие&lt;/strong&gt; — уехал жить в другую страну. Незапланированно.&lt;/p&gt;&#xA;&lt;p&gt;Итоги 2021 года — &lt;a href=&#34;https://etogeek.dev/posts/2021-short/&#34;   target=&#34;_blank&#34;&gt;&#xA;    тут&lt;/a&gt;.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Apache Airflow: деплой DAG файлов из git</title>
      <link>https://etogeek.dev/posts/airflow-dag-deploy-from-git/</link>
      <pubDate>Sat, 12 Nov 2022 13:11:39 +0000</pubDate>
      <guid>https://etogeek.dev/posts/airflow-dag-deploy-from-git/</guid>
      <description>&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/airflow-dag-deploy-from-git/feature.jpg&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;У меня в опыте три развертывания Apache Airflow в продакшн, и я ни разу не находил в Гугле способа хранить DAG-файлы в git и деплоить их в Airflow. Пришлось изобретать свой.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Цель &#xA;    &lt;div id=&#34;%D1%86%D0%B5%D0%BB%D1%8C&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D1%86%D0%B5%D0%BB%D1%8C&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Дать возможность разработчикам редактировать DAG-файлы, зависимости и т.д. локально на своих компьютерах, пушить код в git-репозиторий, и через несколько минут получать эти изменения в Airflow.&lt;/p&gt;&#xA;&lt;p&gt;Так же передо мной стояла задача хранить конфигурацию Apache Airflow в git.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Дисклаймер&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;В этой статье я не рассматриваю способы тестирования DAG-файлов перед выкаткой на прод, хотя это очень важно.&lt;br&gt;&#xA;У Airflow есть документация, в которой описаны различные best practices, включая способы тестирования.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Условия &#xA;    &lt;div id=&#34;%D1%83%D1%81%D0%BB%D0%BE%D0%B2%D0%B8%D1%8F&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D1%83%D1%81%D0%BB%D0%BE%D0%B2%D0%B8%D1%8F&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;У меня установлен Apache Airflow согласно прекрасной документации. В моем случае я не использую Airflow в Docker, а запускаю его на виртуальной машине через systemd.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Что нужно &#xA;    &lt;div id=&#34;%D1%87%D1%82%D0%BE-%D0%BD%D1%83%D0%B6%D0%BD%D0%BE&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D1%87%D1%82%D0%BE-%D0%BD%D1%83%D0%B6%D0%BD%D0%BE&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;git-репозиторий в котором мы будем хранить наш код и пайплайн&lt;/li&gt;&#xA;&lt;li&gt;CI/CD система (GitLab CI, GitHub Actions, Drone CI, тысячи их)&lt;/li&gt;&#xA;&lt;li&gt;Настроенный runner для вашей CI/CD системы&lt;/li&gt;&#xA;&lt;li&gt;Установленный Ansible, вместе с модулем ansible.posix.synchronize на машине с runner-ом, и установленный rsync на обоих машинах&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Готовим репозиторий &#xA;    &lt;div id=&#34;%D0%B3%D0%BE%D1%82%D0%BE%D0%B2%D0%B8%D0%BC-%D1%80%D0%B5%D0%BF%D0%BE%D0%B7%D0%B8%D1%82%D0%BE%D1%80%D0%B8%D0%B9&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%B3%D0%BE%D1%82%D0%BE%D0%B2%D0%B8%D0%BC-%D1%80%D0%B5%D0%BF%D0%BE%D0%B7%D0%B8%D1%82%D0%BE%D1%80%D0%B8%D0%B9&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Создал директорию &lt;code&gt;dags&lt;/code&gt;. В ней я храню все DAG-и, дополнительные файлы, например, скрипты. Вообще что угодно — это всё будет деплоиться в наш инстанс Airflow.&lt;/p&gt;&#xA;&lt;p&gt;Создал в корне репозитория файлы:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;connections.json — здесь мы храним необходимые подключения для Airflow в следующем виде:&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;test_connection&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;conn_type&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;postgres&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Тестовая БД&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;host&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;localhost&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;login&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;dev_ro&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;password&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;super_password&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;schema&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;mark&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;port&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5432&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;extra&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;{\&amp;#34;cursor\&amp;#34;: \&amp;#34;dictcursor\&amp;#34;}&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#xA;&lt;li&gt;requirements.txt — зависимости для Python, которые нужны для запуска DAG-ов. Записываю в стандартном для Python виде:&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;apache-airflow-providers-postgres==2.0.0&#xA;pytz==2021.1&#xA;pytzdata==2020.1&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;&#xA;&lt;li&gt;variables.json — переменные, которые Airflow будет использовать в работе:&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;#34;my_var&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;test&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;#34;your_var&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;hello&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Еще один дисклаймер:&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;я не рассматриваю сейчас способы хранения sensitive data в переменных и подключениях. Для этого нужно использовать немного другой способ. Например — шифровать файлы с помощью Ansible Vault.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;airflow.cfg — конфигурация Apache Airflow. По умолчанию можно записать данные из актуального файла &lt;code&gt;/etc/airflow/airflow.cfg&lt;/code&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&#xA;&lt;h3 class=&#34;relative group&#34;&gt;Плейбук &#xA;    &lt;div id=&#34;%D0%BF%D0%BB%D0%B5%D0%B9%D0%B1%D1%83%D0%BA&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%BF%D0%BB%D0%B5%D0%B9%D0%B1%D1%83%D0%BA&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h3&gt;&#xA;&lt;p&gt;Деплой я настроил с помощью любимого Ansible.&lt;/p&gt;&#xA;&lt;p&gt;В корневой директории репозитория создал директорию &lt;code&gt;ansible&lt;/code&gt;, а в ней файл &lt;code&gt;ansible.cfg&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;[defaults]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;host_key_checking&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;False&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;ansible_ssh_private_key_file&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;~/.ssh/id_rsa&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;ansible_python_interpreter&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;/usr/bin/python3&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;callbacks_enabled&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;profile_tasks&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;forks&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;25&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Там же создал файл &lt;code&gt;hosts.yml&lt;/code&gt; и в нем указал мой хост с Airflow:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;all&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;children&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;airflow&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;hosts&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;airflow.your.domain&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Теперь переходим к нашему плейбуку. У меня на сайте есть несколько статей по базовому Ansible, рекомендую посмотреть: первая статья, вторая статья.&lt;/p&gt;&#xA;&lt;p&gt;Создал файл &lt;code&gt;playbook.yml&lt;/code&gt; и начал заполнять:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;Airflow deploy playbook&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;hosts&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;airflow&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;gather_facts&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;become&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;vars&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;src_path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;..&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;dst_path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;/etc/airflow&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;airflow_home_env&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;AIRFLOW_HOME&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;/etc/airflow&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&lt;h3 class=&#34;relative group&#34;&gt;DAG &#xA;    &lt;div id=&#34;dag&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#dag&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h3&gt;&#xA;&lt;p&gt;Переходим к таскам. Их я разделил тегами, чтобы была возможность запускать разные команды из одного плейбука:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# SYNC&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;Sync DAGs directory&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ansible.posix.synchronize&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;src&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;{{ src_path }}/dags/&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;dest&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;{{ dst_path }}/dags/&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;delete&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;recursive&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;tags&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;sync&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;Change files permissions&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ansible.builtin.file&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;{{ dst_path }}&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;state&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;directory&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;recurse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;owner&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;airflow&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;group&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;airflow&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;tags&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;sync&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Опишу что тут происходит. В первом таске я использую модуль &lt;code&gt;ansible.posix.sychronize&lt;/code&gt;, который под капотом запускает rsync. Синхронизирую директорию &lt;code&gt;../dags&lt;/code&gt; в директории с репозиторием и директорию &lt;code&gt;/etc/airflow/dags&lt;/code&gt; на машине с Airflow.&lt;/p&gt;&#xA;&lt;p&gt;Я указываю директиву &lt;code&gt;delete: true&lt;/code&gt;, чтобы удалять те файлы, что были удалены из репозитория, и директиву &lt;code&gt;recursive: true&lt;/code&gt;, чтобы синхронизировать так же директории внутри &lt;code&gt;dags&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Во втором таске я проставляю корректные права на файлы.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h3 class=&#34;relative group&#34;&gt;Зависимости &#xA;    &lt;div id=&#34;%D0%B7%D0%B0%D0%B2%D0%B8%D1%81%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D0%B8&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%B7%D0%B0%D0%B2%D0%B8%D1%81%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D0%B8&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# Install requirements&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;Copy requirements.txt file&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ansible.builtin.copy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;src&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;{{ src_path }}/requirements.txt&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;dest&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;{{ dst_path }}/&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;owner&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;airflow&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;group&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;airflow&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;mode&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;0775&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;tags&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;requirements&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;Install requirements python modules&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ansible.builtin.pip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;requirements&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;{{ dst_path }}/requirements.txt&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;tags&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;requirements&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;register&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;pip_output&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;become_user&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;airflow&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;Print install output&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ansible.builtin.debug&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;var&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;pip_output.stdout_lines&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;tags&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;requirements&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;В этом блоке я копирую файл с зависимостями, запускаю его установку и вывожу результат.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h3 class=&#34;relative group&#34;&gt;Переменные &#xA;    &lt;div id=&#34;%D0%BF%D0%B5%D1%80%D0%B5%D0%BC%D0%B5%D0%BD%D0%BD%D1%8B%D0%B5&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%BF%D0%B5%D1%80%D0%B5%D0%BC%D0%B5%D0%BD%D0%BD%D1%8B%D0%B5&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# Import variables&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;Copy variables.json file&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ansible.builtin.copy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;src&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;{{ src_path }}/variables.json&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;dest&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;{{ dst_path }}/&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;owner&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;airflow&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;group&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;airflow&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;mode&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;0775&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;tags&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;variables&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;Import variables&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ansible.builtin.shell&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;/home/airflow/.local/bin/airflow variables import variables.json&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;chdir&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;{{ dst_path }}&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;become_user&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;airflow&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;tags&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;variables&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;environment&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;{{ airflow_home_env }}&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;register&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;variables_import_output&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;Print variables import output&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ansible.builtin.debug&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;var&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;variables_import_output.stdout_lines&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;tags&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;variables&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Первым делом я, уже стандартно, копирую файл с переменными. Затем совершенно некрасивый, но необходимый этап — bashsinble — запускаю команду импорта переменных, указывая chdir, пользователя &lt;code&gt;airflow&lt;/code&gt; и переменной &lt;code&gt;AIRFLOW_HOME&lt;/code&gt;.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h3 class=&#34;relative group&#34;&gt;Подключения &#xA;    &lt;div id=&#34;%D0%BF%D0%BE%D0%B4%D0%BA%D0%BB%D1%8E%D1%87%D0%B5%D0%BD%D0%B8%D1%8F&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%BF%D0%BE%D0%B4%D0%BA%D0%BB%D1%8E%D1%87%D0%B5%D0%BD%D0%B8%D1%8F&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h3&gt;&#xA;&lt;p&gt;Импорт подключений выглядит абсолютно так же как и импорт переменных. Меняется только тэг и команда.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Деплой в GitHub Actions &#xA;    &lt;div id=&#34;%D0%B4%D0%B5%D0%BF%D0%BB%D0%BE%D0%B9-%D0%B2-github-actions&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%B4%D0%B5%D0%BF%D0%BB%D0%BE%D0%B9-%D0%B2-github-actions&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;В GHA мы храним пайплайны(workflows) в директории &lt;code&gt;.github/workflows&lt;/code&gt;. У меня она выглядит так:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;gt; tree .github&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;.github&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;└── workflows&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    ├── airflow_cfg.yml&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    ├── connections.yml&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    ├── dags.yml&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    ├── requirements.yml&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    └── variables.yml&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Разделил все на отдельные файлы, чтобы была возможность запускать их по отдельности вручную, если понадобится. Что в &lt;code&gt;dags.yml&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;Deploy DAG files&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;on&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;workflow_dispatch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;push&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;branches&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;master&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;paths&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;s1&#34;&gt;&amp;#39;dags/**&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;jobs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;sync-dags&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;runs-on&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;self-hosted&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;steps&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;Checkout repo&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;uses&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;actions/checkout@v3&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;Run Common playbook&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;env&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;          &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;PY_COLORS&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;          &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ANSIBLE_FORCE_COLOR&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;          cd ansible &amp;amp;&amp;amp; \&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;          ansible-playbook -i hosts.yml airflow-deploy.yml \&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;          --tags sync&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;          &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Разберу по частям:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;workflow_dispatch:&lt;/code&gt;  позволяет запускать пайплайны вручную из меню Actions, а так же запускать их не из master ветки.&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;branch: master&lt;/code&gt; и &lt;code&gt;paths:&lt;/code&gt; запустит пайплайн только в случае коммита в master ветку и при изменении в указанных файлах&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;jobs:&lt;/code&gt; указываю, что запускаться мы будем на self-hosted раннерах. Запускаю модуль Checkout для скачивания репозитория и запускаю плейбук с определенным тегом.&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;env:&lt;/code&gt; передаю несколько переменных, например для включения цветного вывода в GitHub&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Остальные workflows выглядят абсолютно так же, меняется только название, директория в paths: и тэг в ansible.&lt;/p&gt;&#xA;&lt;p&gt;Так же у меня в репозиторий добавлен скрипт для уведомлений о деплоях в Slack, который я вызываю после выполнения плейбука в зависимости от результата:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;Send OK branch notify&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;${{ github.ref != &amp;#39;refs/heads/master&amp;#39; &amp;amp;&amp;amp; success() }}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;python3 notify.py deploy_branch&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;Send OK notify&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;${{ github.ref == &amp;#39;refs/heads/master&amp;#39; &amp;amp;&amp;amp; success() }}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;python3 notify.py deploy_ok&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;Send FAIL notify&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;failure()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;python3 notify.py deploy_failed&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Итог &#xA;    &lt;div id=&#34;%D0%B8%D1%82%D0%BE%D0%B3&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%B8%D1%82%D0%BE%D0%B3&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Airflow периодически проверяет наличие новых файлов и изменения в старых. Интервал проверки настраивается в конфигурационном файле.&lt;/p&gt;&#xA;&lt;p&gt;Теперь я могу запускать пайплайны вручную из вкладки Actions:&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/airflow-dag-deploy-from-git/1.png&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/airflow-dag-deploy-from-git/2.png&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/airflow-dag-deploy-from-git/3.png&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Разработчики коммитят код в ветки, делают Pull Requests, ревьювят свой код. Все довольны.&lt;/p&gt;&#xA;&lt;p&gt;📱 Оставить комментарий к посту или задать любой вопрос ты можешь в моем Telegram-канале:&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://t.me/etogeek&#34;   target=&#34;_blank&#34;&gt;&#xA;    @etogeek&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;🇬🇧English version on &lt;a href=&#34;https://medium.com/devops-dev/apache-airflow-deploy-dags-from-git-1f065893280a&#34;   target=&#34;_blank&#34;&gt;&#xA;    Medium&lt;/a&gt;.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Как ведение дневника помогает моей продуктивности</title>
      <link>https://etogeek.dev/posts/journaling/</link>
      <pubDate>Thu, 15 Sep 2022 10:33:11 +0000</pubDate>
      <guid>https://etogeek.dev/posts/journaling/</guid>
      <description>&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/journaling/feature.jpg&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Не очень нравится само название «&lt;em&gt;дневник&lt;/em&gt;», мне намного больше импонирует английское &lt;strong&gt;journaling&lt;/strong&gt;. Одна из лучших техник-находок этого года, хотя сама техника, конечно же, не нова.&lt;/p&gt;&#xA;&lt;p&gt;Главный совет — &lt;strong&gt;веди дневник&lt;/strong&gt;. Каждый день, хотя бы несколько осмысленных предложений. Пиши вечером о вчерашнем дне, или утром про вчерашний день.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Как делаю я? &#xA;    &lt;div id=&#34;%D0%BA%D0%B0%D0%BA-%D0%B4%D0%B5%D0%BB%D0%B0%D1%8E-%D1%8F&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%BA%D0%B0%D0%BA-%D0%B4%D0%B5%D0%BB%D0%B0%D1%8E-%D1%8F&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Пишу заметку и утром и вечером. Стараюсь писать менее сухо, как будто пишу пост в телеграм-канал.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Утром&lt;/strong&gt; вспоминаю и осмысливаю, что делал вчера и на холодную голову записываю размышления о прошедшем дне. Стараюсь оценить насколько продуктивный получился день.&lt;/p&gt;&#xA;&lt;p&gt;Затем пишу краткий план на день и &lt;strong&gt;хайлайт&lt;/strong&gt; — центральную задачу, которая доставит мне радость, приблизит меня к цели или просто будет очень полезной.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h3 class=&#34;relative group&#34;&gt;Как придумать хайлайт? &#xA;    &lt;div id=&#34;%D0%BA%D0%B0%D0%BA-%D0%BF%D1%80%D0%B8%D0%B4%D1%83%D0%BC%D0%B0%D1%82%D1%8C-%D1%85%D0%B0%D0%B9%D0%BB%D0%B0%D0%B9%D1%82&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%BA%D0%B0%D0%BA-%D0%BF%D1%80%D0%B8%D0%B4%D1%83%D0%BC%D0%B0%D1%82%D1%8C-%D1%85%D0%B0%D0%B9%D0%BB%D0%B0%D0%B9%D1%82&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h3&gt;&#xA;&lt;p&gt;Представь, что за день ты можешь делать только одну единственную задачу. Странно, конечно, но попробуй. Что бы это было?&lt;/p&gt;&#xA;&lt;p&gt;🎮 «&lt;em&gt;Час поиграть в Геншин&lt;/em&gt;». Почему нет, ведь это доставит мне удовольствие, и заодно отвлекусь от рабочих мыслей.&lt;/p&gt;&#xA;&lt;p&gt;📱 «&lt;em&gt;Позвонить маме&lt;/em&gt;». Отлично, мама будет рада.&lt;/p&gt;&#xA;&lt;p&gt;🤵🏻‍♂️ «&lt;em&gt;Сделать сборочный пайплайн для приложения&lt;/em&gt;». Всегда приятно, когда работает то, что ты сделал.&lt;/p&gt;&#xA;&lt;p&gt;Например, вчера у меня был записан хайлайт «&lt;em&gt;сделать ревью статьи про journaling&lt;/em&gt;».&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Вечером&lt;/strong&gt; подвожу итоги на горячую голову. Теперь можно дать волю эмоциям, высказаться о задачах, пожарах, мыслях, которые меня беспокоят. Оставляю небольшой план на следующий день, вроде «а завтра надо бы доделать эту задачу».&lt;/p&gt;&#xA;&lt;p&gt;Наконец, дополняю страничку несколькими полями, вроде сегодняшней погоды, настроения, отмечаю не болела ли сегодня голова. Думаю, в перспективе можно будет построить какую-нибудь аналитику по этим данным.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Зачем вести дневник? &#xA;    &lt;div id=&#34;%D0%B7%D0%B0%D1%87%D0%B5%D0%BC-%D0%B2%D0%B5%D1%81%D1%82%D0%B8-%D0%B4%D0%BD%D0%B5%D0%B2%D0%BD%D0%B8%D0%BA&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%B7%D0%B0%D1%87%D0%B5%D0%BC-%D0%B2%D0%B5%D1%81%D1%82%D0%B8-%D0%B4%D0%BD%D0%B5%D0%B2%D0%BD%D0%B8%D0%BA&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;🧘 Такое ежедневное написание заметок &lt;strong&gt;помогает&lt;/strong&gt;  &lt;strong&gt;рефлексировать&lt;/strong&gt; — в процессе создания короткой заметки фокусируешься на своих ощущениях и мыслях. Это тренировка осознанности. В первые пару дней я не мог выдавить из себя даже три предложения и писал что-то вроде “я поел, завтра тоже поем”. Теперь я начал записывать свои мысли более обширно, и замечаю это как раз благодаря регулярным заметкам.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;📈 Трекинг прогресса&lt;/strong&gt; — если чему-то учишься или двигаешься к цели, будет очень полезно в эти заметки включать какой-либо маркер прогресса продвижения к твоей цели. Например “сегодня разобрался как собирать java-приложение, это большое продвижение по моему ci/cd роад-мапу”.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;✍️ Ты учишься писать&lt;/strong&gt; — чтобы научиться что-то делать, нужно делать это каждый день. Вот и пиши, по чуть-чуть и каждый день. Представь, что в процессе написания простой ежедневной заметки у тебя появится огненная мысль, которая со временем превратится в крутую статью.&lt;/p&gt;&#xA;&lt;p&gt;Если хочешь улучшить навыки написания текстов, следует, когда ведение дневника войдет в привычку, стараться каждую заметку оформлять в виде законченного материала. Так, как будто это завтра выложишь в блог.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Где вести журнал? &#xA;    &lt;div id=&#34;%D0%B3%D0%B4%D0%B5-%D0%B2%D0%B5%D1%81%D1%82%D0%B8-%D0%B6%D1%83%D1%80%D0%BD%D0%B0%D0%BB&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%B3%D0%B4%D0%B5-%D0%B2%D0%B5%D1%81%D1%82%D0%B8-%D0%B6%D1%83%D1%80%D0%BD%D0%B0%D0%BB&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Основное различие будет - &lt;strong&gt;цифровой&lt;/strong&gt; или &lt;strong&gt;физический&lt;/strong&gt;. Или оба?&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;📓 Бумажный дневник&lt;/strong&gt; я вести не пробовал — мне слишком жалко время, и боюсь, что очень быстро забью на эту привычку. Очевидно, что на письмо от руки тратится больше времени и за это время будет много отвлечений.&lt;/p&gt;&#xA;&lt;p&gt;Но! Во многих статьях и книгах говорят о том, что записывание своих мыслей на бумагу помогает еще лучше в них разобраться. Если ты лучше воспринимаешь информацию визуализируя — стоит попробовать.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;⌨️ Цифровой&lt;/strong&gt; — тут вариантов много. Начиная от простых текстовых файлов, которые ты пишешь каждый день в блокноте и сохраняешь в отдельную директорию, заканчивая специализированными приложениями для ведения дневников, вроде &lt;strong&gt;DayOne&lt;/strong&gt;. Последний, кстати, бесплатный, но без синхронизации между устройствами.&lt;/p&gt;&#xA;&lt;p&gt;Я пишу ежедневные заметки уже около месяца прямо в &lt;strong&gt;Notion&lt;/strong&gt;. Сделал database-табличку, и добавил view в виде календаря, чтобы видеть, какие дни пропустил. Заметил, что по выходным обычно забываю сделать заметку, потому что меньше сижу за ноутбуком.&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/journaling/image-6.png&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Веду дневник уже месяц и могу заверить, что это помогает мне &lt;strong&gt;собрать мысли в кучу&lt;/strong&gt;, &lt;strong&gt;проанализировать день&lt;/strong&gt; и &lt;strong&gt;сделать выводы&lt;/strong&gt;. Начинают проскакивать мысли «&lt;em&gt;Хм, свободная минутка, почему бы не написать пару строк в дневник&lt;/em&gt;» &lt;strong&gt;Рекомендую&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;p&gt;👉 А ты пробовал вести дневник? Прокомментировать ты можешь в &lt;strong&gt;&lt;a href=&#34;https://t.me/etogeek&#34;   target=&#34;_blank&#34;&gt;&#xA;    Телеграм-канале&lt;/a&gt;&lt;/strong&gt;. Подпишись и не пропускай новые статьи.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Перенос git репозитория</title>
      <link>https://etogeek.dev/posts/transfer-git-repo/</link>
      <pubDate>Thu, 18 Aug 2022 06:55:30 +0000</pubDate>
      <guid>https://etogeek.dev/posts/transfer-git-repo/</guid>
      <description>&lt;p&gt;Небольшой набор команд для переноса одного git репозитория в другой. Я, например, пользовался при переносе из Github в Gitlab.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git clone --bare git@git.test.com:my-repo-a.git&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; my-repo-a.git&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git fetch origin&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git remote add new-origin git@git.test.com:my-repo-b.git&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git push --mirror new-origin&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git remote rm origin&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git remote rename new-origin origin&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Давай разберем команды:&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;git clone --bare&lt;/code&gt; — клонируем не сам репозиторий, а директорию .git&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;git fetch origin&lt;/code&gt; — убеждаемся, что у нас все изменения присутствуют&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;git remote add&lt;/code&gt; — добавляем новый remote репозиторий&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;git push --mirror new-origin&lt;/code&gt; — загружаем весь наш репозиторий в новый. mirror передает все refs — тэги, бранчи&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;git remote rm&lt;/code&gt; — удаляем старый origin локально&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;git remote rename&lt;/code&gt; — переименовываем new-origin в origin, чтобы было проще работать&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;Комментарии можно оставить в уютном чате 👉 &lt;a href=&#34;https://t.me/etogeekchat&#34;   target=&#34;_blank&#34;&gt;&#xA;    https://t.me/etogeekchat&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;А больше гайдов и новостей выходит в канале 👉 &lt;a href=&#34;https://t.me/etogeek&#34;   target=&#34;_blank&#34;&gt;&#xA;    https://t.me/etogeek&lt;/a&gt;&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Как посмотреть внешний IP в Linux</title>
      <link>https://etogeek.dev/posts/external-ip/</link>
      <pubDate>Tue, 16 Aug 2022 10:06:32 +0000</pubDate>
      <guid>https://etogeek.dev/posts/external-ip/</guid>
      <description>&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/external-ip/feature.jpg&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Мне часто нужно узнать &lt;strong&gt;с какого IP адреса я выхожу в интернет&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Конечно, мы можем зайти на какой-нибудь сайт, вроде myip.ru и посмотреть, но как быть, когда ты работаешь &lt;strong&gt;на сервере&lt;/strong&gt;?&lt;/p&gt;&#xA;&lt;p&gt;В терминале пользуюсь регулярно:&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;curl ifconfig.so&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;В ответ получим &lt;strong&gt;наш внешний ip-адрес&lt;/strong&gt;. И всё.&lt;/p&gt;&#xA;&lt;p&gt;Но, начав работать с некоторыми облачными провайдерами заметил, что такой запрос может возвращать &amp;ldquo;серый&amp;rdquo; адрес, вроде:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;rnd-1:~$ curl ifconfig.so&#xA;172.20.4.1&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Тогда можно использовать один из многих других сайтов, например:&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;curl ifconfig.me&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;В браузере можно воспользоваться теми же сайтами, они дадут более даже подробную информацию:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://ifconfig.me/&#34;   target=&#34;_blank&#34;&gt;&#xA;    https://ifconfig.me/&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://wtfismyip.com/&#34;   target=&#34;_blank&#34;&gt;&#xA;    https://wtfismyip.com/&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Кстати, можно так же воспользоваться утилитой &lt;strong&gt;dig&lt;/strong&gt;, например, когда на сервере нет установленного &lt;strong&gt;curl&lt;/strong&gt;:&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;dig ip @dns.toys&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;Но это скорее из разряда прикольных вещей — &lt;a href=&#34;https://www.dns.toys/&#34;   target=&#34;_blank&#34;&gt;&#xA;    https://www.dns.toys/&lt;/a&gt;&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;👉 Больше шортиков выходит у меня в Telegram-канале: &lt;a href=&#34;https://t.me/etogeek&#34;   target=&#34;_blank&#34;&gt;&#xA;    https://t.me/etogeek&lt;/a&gt;&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>caffeinate — macbook, не спи</title>
      <link>https://etogeek.dev/posts/caffeinate/</link>
      <pubDate>Sun, 14 Aug 2022 12:48:01 +0000</pubDate>
      <guid>https://etogeek.dev/posts/caffeinate/</guid>
      <description>&lt;p&gt;На MacOS есть встроенная программа для того, чтобы система не уходила в сон.&lt;/p&gt;&#xA;&lt;p&gt;Запускается из терминала простой командой:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;caffeinate -t 3600&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Параметр &lt;code&gt;-t&lt;/code&gt; и количество секунд устанавливает время бодрствования устройства. 3600 секунд = час.&lt;/p&gt;&#xA;&lt;p&gt;Но вот беда — терминал нельзя закрывать. Об этом в конце.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Параметры &#xA;    &lt;div id=&#34;%D0%BF%D0%B0%D1%80%D0%B0%D0%BC%D0%B5%D1%82%D1%80%D1%8B&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%BF%D0%B0%D1%80%D0%B0%D0%BC%D0%B5%D1%82%D1%80%D1%8B&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;&lt;code&gt;-t&lt;/code&gt; — количество секунд&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;-d&lt;/code&gt; — не выключать экран&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;-i&lt;/code&gt; — не переводить истему в idle режим&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;-s&lt;/code&gt; — не давать засыпать системе, когда комп подключен к питанию&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;-u&lt;/code&gt; — имитирует поведение пользователя&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;-w PID&lt;/code&gt; — можно указать process id, чтобы программа завершилась после завершения процесса. Например:&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;caffeinate -disu -t 13453&lt;/code&gt;&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h3 class=&#34;relative group&#34;&gt;Зачем использую? &#xA;    &lt;div id=&#34;%D0%B7%D0%B0%D1%87%D0%B5%D0%BC-%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D1%83%D1%8E&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%B7%D0%B0%D1%87%D0%B5%D0%BC-%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D1%83%D1%8E&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h3&gt;&#xA;&lt;p&gt;Пару раз я скачивал что-то очень большое, не хотел прерывать загрузку. Ну, и при засыпании прерываются ssh-сессии, помогает, чтобы не перезаходить потом. VPN не обрывает соединение.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h3 class=&#34;relative group&#34;&gt;Минусы &#xA;    &lt;div id=&#34;%D0%BC%D0%B8%D0%BD%D1%83%D1%81%D1%8B&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%BC%D0%B8%D0%BD%D1%83%D1%81%D1%8B&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h3&gt;&#xA;&lt;p&gt;Терминал нельзя закрывать. Да, мы можем добавить амперсанд &amp;amp; в конец команды, чтобы она запустился в фоне, тогда мы сможем продолжить использовать эту терминальную сессию. Но закрыть — нет.&lt;/p&gt;&#xA;&lt;p&gt;Еще я пользуюсь комбайном для MacOS — &lt;strong&gt;Alfred&lt;/strong&gt;. Там стоит workflow для включения &lt;strong&gt;caffeinate&lt;/strong&gt; режима. Тык-тык две клавиши, написал &lt;code&gt;caff 2h&lt;/code&gt; и пошел по своим делам.&lt;/p&gt;&#xA;&lt;p&gt;Так же можно использовать одно из многих приложений, которое крутится в трее и запускает caffeinate, например:&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://apps.apple.com/ru/app/owly-prevent-display-sleep&#34;   target=&#34;_blank&#34;&gt;&#xA;    https://apps.apple.com/ru/app/owly-prevent-display-sleep&lt;/a&gt;&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;👉 Больше шортиков выходит у меня в Telegram-канале: &lt;a href=&#34;https://t.me/etogeek&#34;   target=&#34;_blank&#34;&gt;&#xA;    https://t.me/etogeek&lt;/a&gt;&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Дейлики</title>
      <link>https://etogeek.dev/posts/daily-meetup/</link>
      <pubDate>Mon, 01 Aug 2022 15:32:38 +0000</pubDate>
      <guid>https://etogeek.dev/posts/daily-meetup/</guid>
      <description>&lt;p&gt;Они же &lt;em&gt;митапы&lt;/em&gt;, &lt;em&gt;синки&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Да, я про ежедневные встречи с командой. Во времена удаленки и распределенных команд они проводятся онлайн.&lt;/p&gt;&#xA;&lt;p&gt;Меня поражают люди, которые направо и налево кричат «на кой черт нам эти митапы, только отвлекают от работы»&lt;/p&gt;&#xA;&lt;p&gt;Ну да, ну да. Занят ты, конечно. Вообще, это реально нужно и полезно. Команда, если, конечно, у вас принята командная работа, а не «каждый сам за себя» должна знать, чем ты занимаешься.&lt;/p&gt;&#xA;&lt;p&gt;Это занимает от силы 15 минут в день в зависимости от размера команды, всегда в одно время. Говорить надо и того меньше — минуту-две: чем занимался, чем будешь заниматься, какие блокеры были.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Основная цель — прозрачность работы команды, обменяться статусами по задачам и проекту в целом.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;На моем предыдущем месте работы дейлики длились по часу-полтора. Это выглядело так: пять разработчиков, три девопса, один куа. Каждый рассказывает с чем столкнулся вчера, но в отличие от классического скрам-дейлика лид начинает досконально разбирать каждую проблему вплоть до непосредственного фикса в прямом эфире.&lt;/p&gt;&#xA;&lt;p&gt;По началу это было интересно — лид смешно шутил, но через пяток дней я понял, что это какая-то дичь и надо по-быстрому рассказывать, мьютиться и заниматься своими задачами.&lt;/p&gt;&#xA;&lt;p&gt;На текущей работе дейлик длится от 10 до 20 минут. В случае если кто-то начинает углубляться в проблему звучит фраза “давай это вынесем в отдельную встречу или в чат”. Всё. Идеально.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Синкапы нужны&lt;/strong&gt;, кто бы что не говорил. Они помогают поддерживать актуальное понимание того, чем занята команда. Минимум воды, максимум полезности. Удачи!&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;👉 Больше шортиков выходит у меня в Telegram-канале: &lt;a href=&#34;https://t.me/etogeek&#34;   target=&#34;_blank&#34;&gt;&#xA;    https://t.me/etogeek&lt;/a&gt;&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Краткие итоги 2021 года</title>
      <link>https://etogeek.dev/posts/2021-short/</link>
      <pubDate>Sat, 01 Jan 2022 08:30:00 +0000</pubDate>
      <guid>https://etogeek.dev/posts/2021-short/</guid>
      <description>&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/2021-short/feature.jpeg&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Все подводят &lt;strong&gt;итоги&lt;/strong&gt;  &lt;strong&gt;года&lt;/strong&gt; и я подведу. Полноценную статью не успеваю закончить.&lt;/p&gt;&#xA;&lt;p&gt;Сначала думаешь — да ничего и не случилось за год — а как начинаешь вспоминать, молишься, чтобы не превратилось в лонгрид.&lt;/p&gt;&#xA;&lt;p&gt;🔧 &lt;strong&gt;Сменил должность на текущей работе&lt;/strong&gt; — стало проще&lt;/p&gt;&#xA;&lt;p&gt;🐕 &lt;strong&gt;Купили собаку&lt;/strong&gt; — взрыв мозга&lt;/p&gt;&#xA;&lt;p&gt;🎉 &lt;strong&gt;Нашли топовых друзей&lt;/strong&gt; — еще и соседи&lt;/p&gt;&#xA;&lt;p&gt;✍️ &lt;strong&gt;Создал блог и не забросил его через месяц&lt;/strong&gt; — но подзабросил через три&lt;/p&gt;&#xA;&lt;p&gt;🤵‍♂️ &lt;strong&gt;Женил лучшего друга&lt;/strong&gt; — первый раз на чужой свадьбе&lt;/p&gt;&#xA;&lt;p&gt;🧗 &lt;strong&gt;Начал заниматься скалолазанием&lt;/strong&gt; — главное не падать&lt;/p&gt;&#xA;&lt;p&gt;🏞 &lt;strong&gt;Съездили в Сочи и Турцию&lt;/strong&gt; — нужно отдыхать&lt;/p&gt;&#xA;&lt;p&gt;🏜 &lt;strong&gt;Пережили жару и чуть не похоронили кота&lt;/strong&gt; — сраное лето&lt;/p&gt;&#xA;&lt;p&gt;💻 &lt;strong&gt;Купил макбук&lt;/strong&gt; — лучшее приобретение&lt;/p&gt;&#xA;&lt;p&gt;🏎 &lt;strong&gt;Купил новую машину&lt;/strong&gt; — привет, китаец&lt;/p&gt;&#xA;&lt;p&gt;👨‍🚀 &lt;strong&gt;Сменил работу спустя пять лет&lt;/strong&gt; — ну наконец-то&lt;/p&gt;&#xA;&lt;p&gt;🏗 &lt;strong&gt;Купил первый Лего&lt;/strong&gt; — Юра, почти тридцать лет, здравствуйте&lt;/p&gt;&#xA;&lt;p&gt;Поздравляю всех &lt;strong&gt;с наступающим новым годом&lt;/strong&gt;! Пусть все ваши желания сбудутся!🎄&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://t.me/etogeek&#34;   target=&#34;_blank&#34;&gt;&#xA;    Телеграм канал&lt;/a&gt; 📺&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://t.me/etogeekchat&#34;   target=&#34;_blank&#34;&gt;&#xA;    Чат&lt;/a&gt; 🤘🏼&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://twitter.com/etosamoe3&#34;   target=&#34;_blank&#34;&gt;&#xA;    Твиттор&lt;/a&gt;  &lt;strong&gt;🐤&lt;/strong&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</description>
    </item>
    <item>
      <title>Кто не роет, у того нет норки</title>
      <link>https://etogeek.dev/posts/zakon-homyachka/</link>
      <pubDate>Thu, 19 Aug 2021 10:18:12 +0000</pubDate>
      <guid>https://etogeek.dev/posts/zakon-homyachka/</guid>
      <description>&lt;p&gt;Когда я читал «&lt;strong&gt;Джедайские техники&lt;/strong&gt;» Дорофеева, почему-то пропустил целый блок про закон хомячка. Осознав то, что мне пытался донести автор, меня, можно сказать, осенило.&lt;/p&gt;&#xA;&lt;p&gt;Если вкратце, то автор рекомендует двигаться к целям &lt;strong&gt;маленькими кусочками&lt;/strong&gt;, по чуть-чуть, но постоянно.&lt;/p&gt;&#xA;&lt;p&gt;В моем понимании нужно разбить задачу на такие маленькие части, чтобы ты мог их сделать за один присест. Например, когда у тебя возникает 15-минутный перерыв между встречами. Обычно в такое время мы ожидаем следующей встречи и ничего не делаем, хотя можем взять задачу из плана на день и сделать небольшой кусочек от нее, а затем переформулировать её суть с учетом уже сделанного.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Небольшой пример:&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;У меня была запланирована задача «&lt;strong&gt;Составить список баз данных в инфраструктуре&lt;/strong&gt;». Зачем — не столь важно. Задача муторная, неинтересная, довольно долгая и поэтому браться за нее не хочется.&lt;/p&gt;&#xA;&lt;p&gt;Но вчера в течение дня я &lt;strong&gt;несколько раз&lt;/strong&gt; брался за неё &lt;strong&gt;небольшими отрывками&lt;/strong&gt; по 10-30 минут, и уже к вечеру задача была выполнена почти на 75%. Если бы я не заставлял себя «делать по чуть-чуть», то она так бы и переносилась в планах на следующий день.&lt;/p&gt;&#xA;&lt;p&gt;Ты, вероятно, часто встречал упоминание этого метода в различных формулировках: пиши статьи каждый день, хотя бы по паре предложений; отжимайся хотя бы пару раз в день. В общем:&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Делай по чуть-чуть, не откладывай на потом.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;Привыканием к этому я и собираюсь заняться в ближайшие несколько недель.&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://t.me/etogeek&#34;   target=&#34;_blank&#34;&gt;&#xA;    Телеграм канал&lt;/a&gt; 📺&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://t.me/etogeekchat&#34;   target=&#34;_blank&#34;&gt;&#xA;    Чат&lt;/a&gt; 🤘🏼&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://twitter.com/etosamoe3&#34;   target=&#34;_blank&#34;&gt;&#xA;    Твиттор&lt;/a&gt;  &lt;strong&gt;🐤&lt;/strong&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</description>
    </item>
    <item>
      <title>Увеличение разделов в Linux на горячую</title>
      <link>https://etogeek.dev/posts/linux-partition-resize/</link>
      <pubDate>Thu, 06 May 2021 06:33:17 +0000</pubDate>
      <guid>https://etogeek.dev/posts/linux-partition-resize/</guid>
      <description>&lt;p&gt;Частая задача в моей текущей работе — увеличить объем раздела на виртуальной машине &lt;strong&gt;без выключения машины&lt;/strong&gt; (на горячую). Под рукой постоянно держу небольшой cheatsheet (шпаргалку) по работе с разделами в Linux.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;План действий &#xA;    &lt;div id=&#34;%D0%BF%D0%BB%D0%B0%D0%BD-%D0%B4%D0%B5%D0%B9%D1%81%D1%82%D0%B2%D0%B8%D0%B9&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%BF%D0%BB%D0%B0%D0%BD-%D0%B4%D0%B5%D0%B9%D1%81%D1%82%D0%B2%D0%B8%D0%B9&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Перед изменением размеров разделов рекомендуется отмонтировать диск. Если диск системный, то нужно загружаться в recovery. Я делаю это на горячую на свой страх и риск.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&lt;strong&gt;Делаем бекап машины!&lt;/strong&gt; (не могу не написать этого)&lt;/li&gt;&#xA;&lt;li&gt;Увеличиваем объем диска в гипервизоре (этот пункт не затрагиваем)&lt;/li&gt;&#xA;&lt;li&gt;Сканируем диски в системе&lt;/li&gt;&#xA;&lt;li&gt;Меняем размер раздела&lt;/li&gt;&#xA;&lt;li&gt;Увеличиваем файловую систему&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Сканируем диски в системе &#xA;    &lt;div id=&#34;%D1%81%D0%BA%D0%B0%D0%BD%D0%B8%D1%80%D1%83%D0%B5%D0%BC-%D0%B4%D0%B8%D1%81%D0%BA%D0%B8-%D0%B2-%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D0%B5&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D1%81%D0%BA%D0%B0%D0%BD%D0%B8%D1%80%D1%83%D0%B5%D0%BC-%D0%B4%D0%B8%D1%81%D0%BA%D0%B8-%D0%B2-%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D0%B5&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;После увеличения объема диска есть вероятность того, что в системе мы новый объем сразу не увидим. Проверяем. Я использую команду &lt;code&gt;lsblk&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;root@hostname.infra.local:/home/user# lsblk&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;NAME   MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;fd0      2:0    &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;     4K  &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; disk&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;loop0    7:0    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  99,2M  &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; loop /snap/core/10908&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;loop2    7:2    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  99,2M  &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; loop /snap/core/10958&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sda      8:0    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;    32G  &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; disk&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├─sda1   8:1    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     1M  &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; part&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;└─sda2   8:2    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;    32G  &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; part /&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sdb      8:16   &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     4T  &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; disk            &lt;span class=&#34;o&#34;&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;--------&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;└─sdb1   8:17   &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     4T  &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; part /pgsqldb   &lt;span class=&#34;o&#34;&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;--------&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sdc      8:32   &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; 109,8G  &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; disk&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;└─sdc1   8:33   &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; 109,8G  &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; part /temp_dump&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sr0     11:0    &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;   829M  &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; rom&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Стрелкой я выделил диск, который мы увеличили до 5Т, но увеличенный объем не видим.&lt;/p&gt;&#xA;&lt;p&gt;Нужно заставить систему просканировать диск.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; &amp;gt; /sys/block/sdb/device/rescan&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;(вместо sdb - нужный диск)&lt;/p&gt;&#xA;&lt;p&gt;Сразу после этого снова выполняем &lt;code&gt;lsblk&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sdb      8:16   &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     5T  &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; disk&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;└─sdb1   8:17   &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     4T  &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; part /pgsqldb&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;То что нужно. Перейдем к увеличению раздела.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Меняем размер раздела &#xA;    &lt;div id=&#34;%D0%BC%D0%B5%D0%BD%D1%8F%D0%B5%D0%BC-%D1%80%D0%B0%D0%B7%D0%BC%D0%B5%D1%80-%D1%80%D0%B0%D0%B7%D0%B4%D0%B5%D0%BB%D0%B0&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%BC%D0%B5%D0%BD%D1%8F%D0%B5%D0%BC-%D1%80%D0%B0%D0%B7%D0%BC%D0%B5%D1%80-%D1%80%D0%B0%D0%B7%D0%B4%D0%B5%D0%BB%D0%B0&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Вообще, мы можем для любых вариантов использовать программу &lt;strong&gt;parted&lt;/strong&gt;, но я покажу еще способ с программой &lt;strong&gt;fdisk&lt;/strong&gt;. Он подходит для разделов объемом менее 4 терабайт.&lt;/p&gt;&#xA;&lt;p&gt;Здесь будет несколько вариантов:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;у нас обычная разметка диска или LVM&lt;/li&gt;&#xA;&lt;li&gt;у нас диск c таблицей разметки GPT более 4 терабайт объемом&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Рекомендую на этом этапе сначала выполнить команду &lt;code&gt;parted -l&lt;/code&gt;, которая может показать ошибку и предложить опции &lt;code&gt;Fix\\Ignore&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Warning: Not all of the space available to /dev/sdb appears to be used, you can fix the GPT to use all of the space &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;an extra &lt;span class=&#34;m&#34;&gt;41943040&lt;/span&gt; blocks&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; or &lt;span class=&#34;k&#34;&gt;continue&lt;/span&gt; with the&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;current setting?&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Fix/Ignore? Fix&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Пишем &lt;code&gt;Fix&lt;/code&gt; .&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h3 class=&#34;relative group&#34;&gt;Обычные разделы или LVM &#xA;    &lt;div id=&#34;%D0%BE%D0%B1%D1%8B%D1%87%D0%BD%D1%8B%D0%B5-%D1%80%D0%B0%D0%B7%D0%B4%D0%B5%D0%BB%D1%8B-%D0%B8%D0%BB%D0%B8-lvm&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%BE%D0%B1%D1%8B%D1%87%D0%BD%D1%8B%D0%B5-%D1%80%D0%B0%D0%B7%D0%B4%D0%B5%D0%BB%D1%8B-%D0%B8%D0%BB%D0%B8-lvm&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h3&gt;&#xA;&lt;p&gt;Чаще всего я пользуюсь программой fdisk. Можно посмотреть все наши диски\разделы: &lt;code&gt;fdisk -l&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Так как меняем размер раздела на диске &lt;code&gt;sdb&lt;/code&gt;, то и заходим в &lt;code&gt;fdisk /dev/sdb.&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;Вводим команду &lt;code&gt;p&lt;/code&gt; и получаем наш &lt;strong&gt;текущий список разделов&lt;/strong&gt;:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Device     Start        End    Sectors Size Type&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;/dev/sdb1   &lt;span class=&#34;m&#34;&gt;2048&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;8589843750&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;8589841703&lt;/span&gt;   4T Linux filesystem&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Удаляем раздел&lt;/strong&gt;  &lt;code&gt;/dev/sdb1&lt;/code&gt;: вводим команду &lt;code&gt;d&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Command &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;m &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;help&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;: d&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Selected partition &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Partition &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; has been deleted.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Если разделов на диске больше, программа предложит ввести цифру нужного раздела.&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Никаких изменений на диск мы еще не сделали! Не боимся.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;Создаем &lt;strong&gt;новый раздел&lt;/strong&gt;  &lt;code&gt;n&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Command &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;m &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;help&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;: n&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Partition number &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;1-128, default 1&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;First sector &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;34-8589934558, default 2048&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Last sector, +sectors or +size&lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;K,M,G,T,P&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;2048-8589934558, default 8589934558&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Created a new partition &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; of &lt;span class=&#34;nb&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Linux filesystem&amp;#39;&lt;/span&gt; and of size &lt;span class=&#34;m&#34;&gt;4&lt;/span&gt; TiB.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Обратите внимание, программа сообщает, что при создании раздела она нашла запись ext4 в разделе и предлагает удалить ее. &lt;strong&gt;Не удаляем&lt;/strong&gt;. Нет! Не надо!&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Partition &lt;span class=&#34;c1&#34;&gt;#1 contains a ext4 signature.&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Do you want to remove the signature? &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;Y&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;es/&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;N&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;o: N&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Опять вводим команду &lt;code&gt;p&lt;/code&gt;, чтобы посмотреть новую информацию:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Device     Start         End     Sectors Size Type&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;/dev/sdb1   &lt;span class=&#34;m&#34;&gt;2048&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;10751953125&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;10751951078&lt;/span&gt;   5T Linux filesystem&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ну вроде все хорошо. Теперь вводим команду &lt;code&gt;w&lt;/code&gt; (&lt;em&gt;write&lt;/em&gt;). После этого &lt;strong&gt;ваши изменения запишутся на диск&lt;/strong&gt;.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Большой диск GPT &#xA;    &lt;div id=&#34;%D0%B1%D0%BE%D0%BB%D1%8C%D1%88%D0%BE%D0%B9-%D0%B4%D0%B8%D1%81%D0%BA-gpt&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%B1%D0%BE%D0%BB%D1%8C%D1%88%D0%BE%D0%B9-%D0%B4%D0%B8%D1%81%D0%BA-gpt&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Если по предыдущему пункту попробовать отресайзить диск большого объема (больше 4 терабайт), то появится ошибка &lt;code&gt;Value out of range&lt;/code&gt;. Придется воспользоваться программой &lt;strong&gt;parted&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Делаем &lt;code&gt;parted /dev/sdb&lt;/code&gt; и &lt;strong&gt;смотрим текущие разделы&lt;/strong&gt; командой &lt;code&gt;print&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;parted&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; print&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Model: VMware Virtual disk &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;scsi&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Disk /dev/sdb: 4398GB&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Sector size &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;logical/physical&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;: 512B/512B&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Partition Table: gpt&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Disk Flags:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Number  Start   End     Size    File system  Name     Flags&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;      1049kB  3299GB  3299GB  ext4         primary&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Меняем размер раздела&lt;/strong&gt; командой &lt;code&gt;resizepart 1&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;parted&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; resizepart &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Warning: Partition /dev/sdb1 is being used. Are you sure you want to &lt;span class=&#34;k&#34;&gt;continue&lt;/span&gt;?&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Yes/No? Yes&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;End?  &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;3299GB&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;? 4398GB&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;На вопрос &lt;code&gt;End?&lt;/code&gt; пишем новый объем раздела. Если хотим расширить его до максимума, то цифры можно подсмотреть выше в выводе команды &lt;code&gt;print&lt;/code&gt;: &lt;code&gt;Disk /dev/sdb: 4398GB&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;Здесь &lt;strong&gt;изменения происходят сразу&lt;/strong&gt;, поэтому при отсутствии вывода после предыдущей команды можно делать &lt;strong&gt;quit&lt;/strong&gt;.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Увеличение файловой системы &#xA;    &lt;div id=&#34;%D1%83%D0%B2%D0%B5%D0%BB%D0%B8%D1%87%D0%B5%D0%BD%D0%B8%D0%B5-%D1%84%D0%B0%D0%B9%D0%BB%D0%BE%D0%B2%D0%BE%D0%B9-%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D1%8B&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D1%83%D0%B2%D0%B5%D0%BB%D0%B8%D1%87%D0%B5%D0%BD%D0%B8%D0%B5-%D1%84%D0%B0%D0%B9%D0%BB%D0%BE%D0%B2%D0%BE%D0%B9-%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D1%8B&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Тут должно быть все просто. Разница только между разметками: обычная или LVM.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h3 class=&#34;relative group&#34;&gt;Обычная разметка &#xA;    &lt;div id=&#34;%D0%BE%D0%B1%D1%8B%D1%87%D0%BD%D0%B0%D1%8F-%D1%80%D0%B0%D0%B7%D0%BC%D0%B5%D1%82%D0%BA%D0%B0&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%BE%D0%B1%D1%8B%D1%87%D0%BD%D0%B0%D1%8F-%D1%80%D0%B0%D0%B7%D0%BC%D0%B5%D1%82%D0%BA%D0%B0&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h3&gt;&#xA;&lt;p&gt;Просто выполняем команду &lt;code&gt;resize2fs /dev/sdb1&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;root@hostname.infra.local:/home/user# resize2fs /dev/sdb1&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;resize2fs 1.44.1 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;24-Mar-2018&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Filesystem at /dev/sdb1 is mounted on /pgsqldb&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; on-line resizing required&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;old_desc_blocks&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; 384, &lt;span class=&#34;nv&#34;&gt;new_desc_blocks&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;512&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;The filesystem on /dev/sdb1 is now &lt;span class=&#34;m&#34;&gt;1073730212&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;4k&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; blocks long.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&lt;h3 class=&#34;relative group&#34;&gt;LVM &#xA;    &lt;div id=&#34;lvm&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#lvm&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h3&gt;&#xA;&lt;p&gt;Вообще, &lt;strong&gt;LVM&lt;/strong&gt; позволяет добавлять физические диски (&lt;strong&gt;PV&lt;/strong&gt; - physical volume) в &lt;strong&gt;VG&lt;/strong&gt; - volume group, а в них создавать логические тома (&lt;strong&gt;LV&lt;/strong&gt; - logical volume) примерно по такой схеме:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sda1     sda2     sdb     sdc       &amp;lt;-- PV&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;       &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;       &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; +--------+- VG00 -+-------+        &amp;lt;-- VG&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;              &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; +-------+-------+---------+&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;       &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;       &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;         &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;root    usr     home      var       &amp;lt;-- LV&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;       &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;       &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;         &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ext3 reiserfs reiserfs    xfs       &amp;lt;-- Файловые системы&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Следовательно, можно не ресайзить диск в гипервизоре, а добавить еще один. Затем добавить новый PV в VG.&lt;/p&gt;&#xA;&lt;p&gt;Но мы идем путем джедая увеличения диска, поэтому в случае с LVM к ресайзингу файловой системы добавляется несколько команд.&lt;/p&gt;&#xA;&lt;p&gt;Для просмотра информации о PV, VG, LV существуют команды &lt;code&gt;pvs&lt;/code&gt;, &lt;code&gt;vgs&lt;/code&gt;, &lt;code&gt;lvs&lt;/code&gt;. И их более подробные варианты &lt;code&gt;pvscan&lt;/code&gt;, &lt;code&gt;vgscan&lt;/code&gt;, &lt;code&gt;lvscan&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;root@hostname:/home/user# pvs&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  PV         VG            Fmt  Attr PSize  PFree&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  /dev/sda5  t-ubuntu16-vg lvm2 a--  49.52g    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;root@hostname:/home/user# vgs&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  VG            &lt;span class=&#34;c1&#34;&gt;#PV #LV #SN Attr   VSize  VFree&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  t-ubuntu16-vg   &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;   &lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;   &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; wz--n- 49.52g    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;root@hostname:/home/user# lvs&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  LV     VG            Attr       LSize  Pool Origin Data%  Meta%  Move Log Cpy%Sync Convert&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  root   t-ubuntu16-vg -wi-ao---- 48.52g&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  swap_1 t-ubuntu16-vg -wi-ao----  1.00g&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#xA;&lt;li&gt;Меняем размер PV &lt;code&gt;pvresize /dev/sda1&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;Увеличиваем размер LV до максимального &lt;code&gt;lvextend -l +100%FREE /dev/mapper/vg1-dsads-lv-root&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;Увеличиваем файловую систему на нужном разделе &lt;code&gt;resize2fs /dev/mapper/vg1-dsads-lv-root&lt;/code&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;Итог &#xA;    &lt;div id=&#34;%D0%B8%D1%82%D0%BE%D0%B3&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%B8%D1%82%D0%BE%D0%B3&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Смотрим, что у нас получилось &lt;code&gt;df -h&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;root@hostname.infra.local:/home/user# df -h&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Filesystem                                Size  Used Avail Use% Mounted on&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;udev                                       38G     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;   38G   0% /dev&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;tmpfs                                     7,5G  4,4M  7,5G   1% /run&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;/dev/sda2                                  32G   24G  6,3G  79% /&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;/dev/sdb1                                 5,0T  3,3T  1,5T  71% /pgsqldb &lt;span class=&#34;o&#34;&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;-----&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Видим новый объем. Все счастливы и довольны.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;TL;DR &#xA;    &lt;div id=&#34;tldr&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#tldr&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;&lt;strong&gt;Кратко&lt;/strong&gt; cheatsheet у меня выглядит так:&lt;/p&gt;&#xA;&lt;p&gt;Смотрим, видит ли система новое место на дисках: &lt;code&gt;lsblk&lt;/code&gt;, &lt;code&gt;parted -l&lt;/code&gt;, &lt;code&gt;fdisk -l&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;Если не видит: &lt;code&gt;echo 1 &amp;gt; /sys/block/sda/device/rescan&lt;/code&gt; (вместо sda - нужный диск) &lt;code&gt;fdisk /dev/sda&lt;/code&gt; (if sda)&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;p&lt;/code&gt; - просмотр разделов&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;d&lt;/code&gt; - удалить раздел который расширяешь&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;n&lt;/code&gt; - создать новый раздел (создастся с тем индексом, который удалишь)&lt;/p&gt;&#xA;&lt;p&gt;В большинстве случаев везде ответы будут по умолчанию. &lt;strong&gt;Когда спросит про затирание метки - НЕ удалять&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;p&lt;/code&gt; - проверить что разделы имеют тот же вид что и в начале&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;w&lt;/code&gt; - записать изменения на диск&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;resize2fs /dev/sda1&lt;/code&gt; (номер - индекс раздела)&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Если LVM&lt;/strong&gt;:&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;pvresize /dev/sda1&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;lvextend -l +100%FREE /dev/mapper/vg1-dsads-lv-root&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;resize2fs /dev/mapper/vg1-dsads-lv-root&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Если ошибка с gpt&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;parted -l&lt;/code&gt; → &lt;code&gt;Fix&lt;/code&gt;&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;💡 Подписывайтесь на Телеграм-канал, чтобы не пропускать новые статьи&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://t.me/etogeek&#34;   target=&#34;_blank&#34;&gt;&#xA;    Телеграм канал&lt;/a&gt; 📺&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://t.me/etogeekchat&#34;   target=&#34;_blank&#34;&gt;&#xA;    Чат&lt;/a&gt; 🤘🏼&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</description>
    </item>
    <item>
      <title>Индивидуальные презентации в IT-отделе</title>
      <link>https://etogeek.dev/posts/public-presentation-short/</link>
      <pubDate>Tue, 20 Apr 2021 21:00:00 +0000</pubDate>
      <guid>https://etogeek.dev/posts/public-presentation-short/</guid>
      <description>&lt;p&gt;Некоторое время назад я закидывал свои мысли по поводу корпоративных презентаций в свой &lt;a href=&#34;https://t.me/etogeek&#34;   target=&#34;_blank&#34;&gt;&#xA;    Телеграм-канал&lt;/a&gt;, а сейчас хочу выложить на сайт.&lt;/p&gt;&#xA;&lt;p&gt;В марте этого года наш &lt;strong&gt;Head of IT&lt;/strong&gt; решил привнести в работу АйТи-департамента что-то новенькое — общие митапы по итогам квартала и планированию следующего.&lt;/p&gt;&#xA;&lt;p&gt;Часть таких митапов проходила в формате индивидуальных выступлений каждого сотрудника, где он рассказывал о своих достижениях и работе за первый квартал 2021 года. Подавляющему большинству сотрудников эта затея не понравилась и меня это расстраивает.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;&lt;strong&gt;Немного истории&lt;/strong&gt; &#xA;    &lt;div id=&#34;%D0%BD%D0%B5%D0%BC%D0%BD%D0%BE%D0%B3%D0%BE-%D0%B8%D1%81%D1%82%D0%BE%D1%80%D0%B8%D0%B8&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%BD%D0%B5%D0%BC%D0%BD%D0%BE%D0%B3%D0%BE-%D0%B8%D1%81%D1%82%D0%BE%D1%80%D0%B8%D0%B8&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Когда я пришел в эту компанию чуть больше пяти лет назад, весь IT-департамент состоял из 20 человек: три админа, три-четыре саппорта, пару руководителей, хэд-оф-айти (другой), и десяток-полтора 1С-программистов и пару бизнес-консультантов вместе с ними. Бизнес-консультант — это современный продакт (ПМ).&lt;/p&gt;&#xA;&lt;p&gt;Сейчас численность департамента &lt;strong&gt;&lt;strong&gt;больше 120 человек&lt;/strong&gt;&lt;/strong&gt;. Количество проектов выросло в разы, число групп и отделов возросло вместе с ними.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;&lt;strong&gt;Как было анонсировано&lt;/strong&gt; &#xA;    &lt;div id=&#34;%D0%BA%D0%B0%D0%BA-%D0%B1%D1%8B%D0%BB%D0%BE-%D0%B0%D0%BD%D0%BE%D0%BD%D1%81%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BE&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%BA%D0%B0%D0%BA-%D0%B1%D1%8B%D0%BB%D0%BE-%D0%B0%D0%BD%D0%BE%D0%BD%D1%81%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BE&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Вам необходимо подготовить презентацию Вашей индивидуальной работы в 1 кв. &lt;em&gt;(квартал — прим. автора)&lt;/em&gt;: выполненные задачи, достижения, анализ показателей. Каждое выступление в эти 2 дня будет длиться 10 минут: 7 минут - само выступление и 3 минуты - вопросы.При подготовке выступления просим воспользовался шаблоном презентации, который был разослан всем 19 марта.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;&lt;strong&gt;Что было после анонса&lt;/strong&gt; &#xA;    &lt;div id=&#34;%D1%87%D1%82%D0%BE-%D0%B1%D1%8B%D0%BB%D0%BE-%D0%BF%D0%BE%D1%81%D0%BB%D0%B5-%D0%B0%D0%BD%D0%BE%D0%BD%D1%81%D0%B0&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D1%87%D1%82%D0%BE-%D0%B1%D1%8B%D0%BB%D0%BE-%D0%BF%D0%BE%D1%81%D0%BB%D0%B5-%D0%B0%D0%BD%D0%BE%D0%BD%D1%81%D0%B0&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Когда всем разослали анонс митапа с индивидуальными выступлениями всех сотрудников, работа отдела встала минут на 40. Постоянно звучали фразы “&lt;strong&gt;&lt;strong&gt;да зачем это надо&lt;/strong&gt;&lt;/strong&gt;”, “&lt;strong&gt;&lt;strong&gt;а работать кто будет&lt;/strong&gt;&lt;/strong&gt;”, “&lt;strong&gt;&lt;strong&gt;че за бред&lt;/strong&gt;&lt;/strong&gt;”, “&lt;strong&gt;&lt;strong&gt;а можно не участвовать?&lt;/strong&gt;&lt;/strong&gt;”. Следующее подобное бурление началось за 1-2 дня до митапа.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;&lt;strong&gt;Что было на митапе&lt;/strong&gt; &#xA;    &lt;div id=&#34;%D1%87%D1%82%D0%BE-%D0%B1%D1%8B%D0%BB%D0%BE-%D0%BD%D0%B0-%D0%BC%D0%B8%D1%82%D0%B0%D0%BF%D0%B5&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D1%87%D1%82%D0%BE-%D0%B1%D1%8B%D0%BB%D0%BE-%D0%BD%D0%B0-%D0%BC%D0%B8%D1%82%D0%B0%D0%BF%D0%B5&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Конечно, все подготовили презентации, за исключением парочки залетных. Но 95% презентаций состояли из слайда со списком технических задач, который зачитывался с экрана и дополнялся импровизированным текстом.&lt;/p&gt;&#xA;&lt;p&gt;65% не уложились в 7 минут из-за того, что зацикливались на нескольких пунктах и долго рассказывали про них. Я слушал не все выступления, но те, что смог в большинстве своем были скучными. И богатыми на технические термины - не дай бог подключится кто-нибудь, кто не знает, что такое RAID5 или черри-пик.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;&lt;strong&gt;Как следовало анонсировать (ИМХО)&lt;/strong&gt; &#xA;    &lt;div id=&#34;%D0%BA%D0%B0%D0%BA-%D1%81%D0%BB%D0%B5%D0%B4%D0%BE%D0%B2%D0%B0%D0%BB%D0%BE-%D0%B0%D0%BD%D0%BE%D0%BD%D1%81%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D1%82%D1%8C-%D0%B8%D0%BC%D1%85%D0%BE&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%BA%D0%B0%D0%BA-%D1%81%D0%BB%D0%B5%D0%B4%D0%BE%D0%B2%D0%B0%D0%BB%D0%BE-%D0%B0%D0%BD%D0%BE%D0%BD%D1%81%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D1%82%D1%8C-%D0%B8%D0%BC%D1%85%D0%BE&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Основная задача — рассказать для чего проводятся подобные митинги. Для этого я привел выше небольшую историческую справку. У нас большой АйТи отдел, в котором много людей, включая удаленных сотрудников. Далеко не все знают, чем занимается соседний отдел или даже соседняя команда.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;&lt;strong&gt;Важно знать с чем и как работают твои коллеги.&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;&lt;strong&gt;Как следовало готовиться (ИМХО)&lt;/strong&gt; &#xA;    &lt;div id=&#34;%D0%BA%D0%B0%D0%BA-%D1%81%D0%BB%D0%B5%D0%B4%D0%BE%D0%B2%D0%B0%D0%BB%D0%BE-%D0%B3%D0%BE%D1%82%D0%BE%D0%B2%D0%B8%D1%82%D1%8C%D1%81%D1%8F-%D0%B8%D0%BC%D1%85%D0%BE&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%BA%D0%B0%D0%BA-%D1%81%D0%BB%D0%B5%D0%B4%D0%BE%D0%B2%D0%B0%D0%BB%D0%BE-%D0%B3%D0%BE%D1%82%D0%BE%D0%B2%D0%B8%D1%82%D1%8C%D1%81%D1%8F-%D0%B8%D0%BC%D1%85%D0%BE&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Рассказать коллегам, стараясь донести это понятным языком, чем ты занимался в этом квартале, над какими проектами работал.&lt;/li&gt;&#xA;&lt;li&gt;Почему бы не проявить творческий подход? Сделать красивую презентацию с анимацией.&lt;/li&gt;&#xA;&lt;li&gt;Подготовить заранее текст, прочитать его вслух, чтобы проверить, укладываешься ли ты в тайминги.&lt;/li&gt;&#xA;&lt;li&gt;Если не можешь “читать” из головы - читай с бумажки или второго экрана. В этом нет ничего зазорного, наоборот, со стороны твоя презентация будет выглядеть подготовленной и нескучной.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;А еще — это &lt;strong&gt;&lt;strong&gt;развитие софт-скиллов&lt;/strong&gt;&lt;/strong&gt; (публичные выступления).&lt;/p&gt;&#xA;&lt;p&gt;Очень надеюсь, что по итогам следующего квартала коллеги будут делать более интересные презентации. Постараюсь кому смогу донести важность этого мероприятия.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;&lt;strong&gt;Полезные ссылки&lt;/strong&gt; &#xA;    &lt;div id=&#34;%D0%BF%D0%BE%D0%BB%D0%B5%D0%B7%D0%BD%D1%8B%D0%B5-%D1%81%D1%81%D1%8B%D0%BB%D0%BA%D0%B8&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%BF%D0%BE%D0%BB%D0%B5%D0%B7%D0%BD%D1%8B%D0%B5-%D1%81%D1%81%D1%8B%D0%BB%D0%BA%D0%B8&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://youtu.be/xazyiJTwAiQ&#34;   target=&#34;_blank&#34;&gt;&#xA;    Выступление про мониторинг&lt;/a&gt; — выступление про мониторинг. Но здесь важен формат подачи: классная презентация, нескучный текст.&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://www.youtube.com/watch?v=slpmYbcx7vA&#34;   target=&#34;_blank&#34;&gt;&#xA;    Подкаст про публичные выступления&lt;/a&gt; - подкаст от Линкмиап про подготовку к публичным выступлениям.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;💡 Если вам нравятся мои посты, то подписывайтесь также:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://t.me/etogeek&#34;   target=&#34;_blank&#34;&gt;&#xA;    Новостной канал&lt;/a&gt; 📺&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://t.me/etogeekchat&#34;   target=&#34;_blank&#34;&gt;&#xA;    Чат&lt;/a&gt; 🤘🏼&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</description>
    </item>
    <item>
      <title>Перенос базы Postgresql на другой диск</title>
      <link>https://etogeek.dev/posts/move-psql/</link>
      <pubDate>Thu, 01 Apr 2021 21:00:00 +0000</pubDate>
      <guid>https://etogeek.dev/posts/move-psql/</guid>
      <description>&lt;p&gt;Жил был сервис, и была у него база, и жила она в &lt;code&gt;/var/lib/postgresql&lt;/code&gt; на основном диске. И стала база занимать почти весь диск.&lt;/p&gt;&#xA;&lt;p&gt;Ну и мы не лыком шиты, перенесем ее на отдельный диск.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;&lt;strong&gt;План такой&lt;/strong&gt;&lt;/strong&gt;:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Добавляем новый диск&lt;/li&gt;&#xA;&lt;li&gt;Монтируем его в какую-то папку&lt;/li&gt;&#xA;&lt;li&gt;Переносим туда БД&lt;/li&gt;&#xA;&lt;li&gt;Тут, возможно, мы захотим жить в новой директории - тогда поменяем конфиг.&lt;/li&gt;&#xA;&lt;li&gt;Прячем текущую папку &lt;code&gt;/var/lib/postgresql&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;Монтируем наш диск вместо старой папки&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Погнали 🚀&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;&lt;strong&gt;Готовим диск&lt;/strong&gt; &#xA;    &lt;div id=&#34;%D0%B3%D0%BE%D1%82%D0%BE%D0%B2%D0%B8%D0%BC-%D0%B4%D0%B8%D1%81%D0%BA&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%B3%D0%BE%D1%82%D0%BE%D0%B2%D0%B8%D0%BC-%D0%B4%D0%B8%D1%81%D0%BA&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Представим, что мы уже добавили диск в гипервизоре и видим его в выводе &lt;code&gt;lsblk&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;admin@host.infra.domain.local:~$ lsblk&#xA;NAME   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT&#xA;loop0    7:0    0 99.2M  1 loop /snap/core/10908&#xA;loop1    7:1    0 99.2M  1 loop /snap/core/10859&#xA;sda      8:0    0  550G  0 disk &#xA;├─sda1   8:1    0    1M  0 part &#xA;└─sda2   8:2    0  550G  0 part /&#xA;sdb      8:16   0 1000G  0 disk &amp;lt;&amp;lt;&amp;lt;&amp;lt;------- THIS&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Теперь разметим его с помощью &lt;code&gt;parted&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;parted /dev/sdb&#xA;mklabel gpt&#xA;unit s&#xA;mkpart primary ext4 0% 100%&#xA;quit&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Отформатируем раздел в ext4:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;mkfs.ext /dev/sdb1&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;&lt;strong&gt;Монтируем диск&lt;/strong&gt; &#xA;    &lt;div id=&#34;%D0%BC%D0%BE%D0%BD%D1%82%D0%B8%D1%80%D1%83%D0%B5%D0%BC-%D0%B4%D0%B8%D1%81%D0%BA&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%BC%D0%BE%D0%BD%D1%82%D0%B8%D1%80%D1%83%D0%B5%D0%BC-%D0%B4%D0%B8%D1%81%D0%BA&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Сначала создадим левую папку, куда мы примонтируем новый раздел и будем перекидывать базу &lt;code&gt;mkdir /tempdb&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;☝️ Хорошим тоном считается добавлять в /etc/fstab записи с UUID диска, а не /dev/sdb1.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;Дальше посмотрим UUID нашего раздела. Даем команду &lt;code&gt;blkid&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;/dev/sda2: UUID=&amp;#34;b651b0ae-ef1d-11e9-be07-005056926860&amp;#34; TYPE=&amp;#34;ext4&amp;#34; PARTUUID=&amp;#34;1cd39f87-c0cd-f24d-baff-a39ff152be09&amp;#34;&#xA;/dev/loop0: TYPE=&amp;#34;squashfs&amp;#34;&#xA;/dev/loop1: TYPE=&amp;#34;squashfs&amp;#34;&#xA;/dev/sdb1: UUID=&amp;#34;fe561baa-f4de-4c52-bdff-cc496353a0a9&amp;#34; TYPE=&amp;#34;ext4&amp;#34; PARTLABEL=&amp;#34;primary&amp;#34; PARTUUID=&amp;#34;4d0987b3-0010-434c-aac4-b42ba2a75582&amp;#34;&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Вот же он: &lt;code&gt;/dev/sdb1: UUID=&amp;quot;fe561baa-f4de-4c52-bdff-cc496353a0a9&amp;quot;&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;Теперь добавляем строку в файл &lt;code&gt;/etc/fstab&lt;/code&gt;, можно руками через vi/nano, можно echo-м:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;echo &amp;#39;UUID=fe561baa-f4de-4c52-bdff-cc496353a0a9 /tempdb ext4 defaults 0 0&amp;#39; &amp;gt;&amp;gt; /etc/fstab&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;После этого делаем &lt;code&gt;mount -a&lt;/code&gt;. Раздел примонтирован в новую папку.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;&lt;strong&gt;Перенос базы&lt;/strong&gt; &#xA;    &lt;div id=&#34;%D0%BF%D0%B5%D1%80%D0%B5%D0%BD%D0%BE%D1%81-%D0%B1%D0%B0%D0%B7%D1%8B&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%BF%D0%B5%D1%80%D0%B5%D0%BD%D0%BE%D1%81-%D0%B1%D0%B0%D0%B7%D1%8B&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Сначала остановим сервис &lt;code&gt;systemctl stop postgresql&lt;/code&gt;, чтобы во время копирования не записывались свежие данные.&lt;/p&gt;&#xA;&lt;p&gt;Начинаем копировать данные:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;rsync -arv /var/lib/postgres/ /tempdb&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;и ждем. У меня база на 500Гб копировалась два часа, но всё зависит от железа, самой базы, фазы луны и дня недели.&lt;/p&gt;&#xA;&lt;p&gt;На этом этапе мы можем изменить путь к базе в конфигурационном файле postgresql и запустить сервис, всё будет работать. Для этого редактируем файл &lt;code&gt;/etc/postgres/10/main/postgresql.conf&lt;/code&gt; и меняем пусть к директории в строке:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;data_directory = &amp;#39;/tempdb/10/main&amp;#39;&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Но если мы не хотим менять папку в конфиге Постгреса, то нужно спрятать старую базу, ведь мы не хотим ее сразу удалять, вдруг понадобится.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;mkdir /var/oldpostgres &amp;amp;&amp;amp; mv /var/lib/postgres/ /var/oldpostgres&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Теперь проверим, что мы не напутали с папками и /var/lib/postgresql на месте. Если нет - &lt;code&gt;mkdir /var/lib/postgresql&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Размонтируем диск &lt;code&gt;umount /tembdb&lt;/code&gt;, а затем заходим в &lt;code&gt;nano /etc/fstab&lt;/code&gt; и редактируем строку с подключением. Нам нужно изменить точку монтирования. Должно получиться так:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;UUID=fe561baa-f4de-4c52-bdff-cc496353a0a9 /var/lib/postgresql ext4 defaults 0 0&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;blockquote&gt;&#xA;&lt;p&gt;☝️ Не забудь, что UUID у тебя будет свой уникальный&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;Делаем &lt;code&gt;mount -a&lt;/code&gt; и пробуем запустить сервис &lt;code&gt;systemctl start postgresql&lt;/code&gt;.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;&lt;strong&gt;Проверяем&lt;/strong&gt; &#xA;    &lt;div id=&#34;%D0%BF%D1%80%D0%BE%D0%B2%D0%B5%D1%80%D1%8F%D0%B5%D0%BC&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%BF%D1%80%D0%BE%D0%B2%D0%B5%D1%80%D1%8F%D0%B5%D0%BC&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Как проверить? Ну например &lt;code&gt;netstat -tulpn | grep 5432&lt;/code&gt;, и если у вас постгрес на стандартном порту, должен быть вывод, вроде:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;tcp        0      0 0.0.0.0:5432            0.0.0.0:*               LISTEN      225199/postgres     &#xA;tcp6       0      0 :::5432                 :::*                    LISTEN      225199/postgres&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Можно попробовать зайди в базу &lt;code&gt;sudo -u postgres psql&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;psql (10.16 (Ubuntu 10.16-0ubuntu0.18.04.1))&#xA;Type &amp;#34;help&amp;#34; for help.&#xA;&#xA;postgres=# &#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Если всё ок, можно удалить старую базу, чтобы освободить место в корне.&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;💡 Если вам нравятся мои посты, то подписывайтесь также:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://t.me/etogeek&#34;   target=&#34;_blank&#34;&gt;&#xA;    Новостной канал&lt;/a&gt; 📺&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://t.me/etogeekchat&#34;   target=&#34;_blank&#34;&gt;&#xA;    Чат&lt;/a&gt; 🤘🏼&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</description>
    </item>
    <item>
      <title>Бекапы индексов в Elasticsearch</title>
      <link>https://etogeek.dev/posts/kibana-index-backup/</link>
      <pubDate>Tue, 02 Mar 2021 21:00:00 +0000</pubDate>
      <guid>https://etogeek.dev/posts/kibana-index-backup/</guid>
      <description>&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/kibana-index-backup/feature.jpg&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;👌 Ты заходишь в Кибану, а там ничего нет.&lt;/p&gt;&#xA;&lt;p&gt;Случилась у нас однажды &lt;strong&gt;&lt;strong&gt;беда&lt;/strong&gt;&lt;/strong&gt;: люди начали заходить в Кибану, и удивлялись, почему они не видят их любимых дашбордов. Не было ничего: ни индексов, ни дашбордов, ни визуализаций.&lt;/p&gt;&#xA;&lt;p&gt;Причиной этого является, скорее всего, скоропостижная кончина &lt;strong&gt;&lt;strong&gt;системного индекса .kibana&lt;/strong&gt;&lt;/strong&gt;, в котором хранятся все ее настройки.&lt;/p&gt;&#xA;&lt;p&gt;Люди делятся на два типа: те, кто еще не делает бекапы и те, кто уже делает бекапы&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;&lt;strong&gt;Бекапы индексов&lt;/strong&gt; &#xA;    &lt;div id=&#34;%D0%B1%D0%B5%D0%BA%D0%B0%D0%BF%D1%8B-%D0%B8%D0%BD%D0%B4%D0%B5%D0%BA%D1%81%D0%BE%D0%B2&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%B1%D0%B5%D0%BA%D0%B0%D0%BF%D1%8B-%D0%B8%D0%BD%D0%B4%D0%B5%D0%BA%D1%81%D0%BE%D0%B2&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Вот мы сейчас и научимся делать бекапы\снапшоты индексов&lt;/p&gt;&#xA;&lt;p&gt;Ссылка на официальную документацию вот: &lt;a href=&#34;https://www.elastic.co/guide/en/elasticsearch/reference/current/snapshot-restore.html&#34;   target=&#34;_blank&#34;&gt;&#xA;    https://www.elastic.co/guide/en/elasticsearch/reference/current/snapshot-restore.html&lt;/a&gt;, но я попробую сделать саммари.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h3 class=&#34;relative group&#34;&gt;&lt;strong&gt;Что нужно сделать&lt;/strong&gt; &#xA;    &lt;div id=&#34;%D1%87%D1%82%D0%BE-%D0%BD%D1%83%D0%B6%D0%BD%D0%BE-%D1%81%D0%B4%D0%B5%D0%BB%D0%B0%D1%82%D1%8C&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D1%87%D1%82%D0%BE-%D0%BD%D1%83%D0%B6%D0%BD%D0%BE-%D1%81%D0%B4%D0%B5%D0%BB%D0%B0%D1%82%D1%8C&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;добавить директорию как репозиторий для хранения снапшотов на машины с эластикой&lt;/li&gt;&#xA;&lt;li&gt;настроить бекапы в новый репозиторий&lt;/li&gt;&#xA;&lt;li&gt;????&lt;/li&gt;&#xA;&lt;li&gt;PROFIT&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&#xA;&lt;h3 class=&#34;relative group&#34;&gt;&lt;strong&gt;Добавить директорию&lt;/strong&gt; &#xA;    &lt;div id=&#34;%D0%B4%D0%BE%D0%B1%D0%B0%D0%B2%D0%B8%D1%82%D1%8C-%D0%B4%D0%B8%D1%80%D0%B5%D0%BA%D1%82%D0%BE%D1%80%D0%B8%D1%8E&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%B4%D0%BE%D0%B1%D0%B0%D0%B2%D0%B8%D1%82%D1%8C-%D0%B4%D0%B8%D1%80%D0%B5%D0%BA%D1%82%D0%BE%D1%80%D0%B8%D1%8E&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h3&gt;&#xA;&lt;p&gt;Предположим, что у нас уже смонтирована какая-нибудь сетевая шара как директория &lt;strong&gt;&lt;strong&gt;&lt;code&gt;/mnt/bck01&lt;/code&gt;&lt;/strong&gt;&lt;/strong&gt;. Подключение к шаре выполняется с помощью программы cifs-utils и запись в &lt;code&gt;/etc/fstab&lt;/code&gt; выглядит так:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;//&amp;lt;server_hostname&amp;gt;/BackupServers$/LinuxServers/kibana /mnt/bck01 cifs user,rw,credentials&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;/root/.smbclient,iocharset&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;utf8,uid&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;elasticsearch,gid&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;elasticsearch,file_mode&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;0775,dir_mode&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;0775&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;em&gt;Подробнее про подключение сетевых шар в другой статье.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;Нужно добавить эту директорию в настройки Elasticsearch. Идем в &lt;code&gt;/etc/elasticsearch/elasticsearch.yml&lt;/code&gt; и добавляем:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;repo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;/mnt/bck01&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Сохраняем файл и делаем:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;systemctl restart elasticsearch&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;💡 Добавлять в конфигурацию путь к директории нужно на каждой ноде в кластере эластики&lt;/strong&gt;&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;&lt;strong&gt;Настройка репозитория&lt;/strong&gt; &#xA;    &lt;div id=&#34;%D0%BD%D0%B0%D1%81%D1%82%D1%80%D0%BE%D0%B9%D0%BA%D0%B0-%D1%80%D0%B5%D0%BF%D0%BE%D0%B7%D0%B8%D1%82%D0%BE%D1%80%D0%B8%D1%8F&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%BD%D0%B0%D1%81%D1%82%D1%80%D0%BE%D0%B9%D0%BA%D0%B0-%D1%80%D0%B5%D0%BF%D0%BE%D0%B7%D0%B8%D1%82%D0%BE%D1%80%D0%B8%D1%8F&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Дальше есть два варианта настройки:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;В Кибане&lt;/li&gt;&#xA;&lt;li&gt;Через АПИ&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&#xA;&lt;h3 class=&#34;relative group&#34;&gt;&lt;strong&gt;В Кибане&lt;/strong&gt; &#xA;    &lt;div id=&#34;%D0%B2-%D0%BA%D0%B8%D0%B1%D0%B0%D0%BD%D0%B5&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%B2-%D0%BA%D0%B8%D0%B1%D0%B0%D0%BD%D0%B5&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h3&gt;&#xA;&lt;p&gt;Stack Management → Snaphot and restore → Repositories → Register repository&lt;/p&gt;&#xA;&lt;p&gt;Вводим имя и выбираем &lt;em&gt;Shared file system&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/kibana-index-backup/Untitled1.png&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;&lt;strong&gt;Location&lt;/strong&gt;&lt;/strong&gt; - путь к нашей директории. Нас предупреждают, что путь должен быть добавлен в конфигурацию на &lt;strong&gt;&lt;strong&gt;всех нодах кластера&lt;/strong&gt;&lt;/strong&gt;!&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;&lt;strong&gt;Read only&lt;/strong&gt;&lt;/strong&gt; - если мы подключаем эту шару к нескольким кластерам, то рекомендуют только одному кластеру работать на запись. Остальным следует установить этот переключатель.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;h3 class=&#34;relative group&#34;&gt;&lt;strong&gt;Через API&lt;/strong&gt; &#xA;    &lt;div id=&#34;%D1%87%D0%B5%D1%80%D0%B5%D0%B7-api&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D1%87%D0%B5%D1%80%D0%B5%D0%B7-api&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h3&gt;&#xA;&lt;p&gt;Делаем cURL запрос к кластеру:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;curl -X PUT &lt;span class=&#34;s2&#34;&gt;&amp;#34;localhost:9200/_snapshot/bck01?pretty&amp;#34;&lt;/span&gt; -H &lt;span class=&#34;s1&#34;&gt;&amp;#39;Content-Type: application/json&amp;#39;&lt;/span&gt; -d&lt;span class=&#34;s1&#34;&gt;&amp;#39;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s1&#34;&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s1&#34;&gt;  &amp;#34;type&amp;#34;: &amp;#34;fs&amp;#34;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s1&#34;&gt;  &amp;#34;settings&amp;#34;: {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s1&#34;&gt;    &amp;#34;location&amp;#34;: &amp;#34;/mnt/bck01/&amp;#34;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s1&#34;&gt;    &amp;#34;compress&amp;#34;: true&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s1&#34;&gt;  }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s1&#34;&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&lt;h2 class=&#34;relative group&#34;&gt;&lt;strong&gt;Настройка политик снапшотов&lt;/strong&gt; &#xA;    &lt;div id=&#34;%D0%BD%D0%B0%D1%81%D1%82%D1%80%D0%BE%D0%B9%D0%BA%D0%B0-%D0%BF%D0%BE%D0%BB%D0%B8%D1%82%D0%B8%D0%BA-%D1%81%D0%BD%D0%B0%D0%BF%D1%88%D0%BE%D1%82%D0%BE%D0%B2&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;&#xA;    &#xA;    &lt;span&#xA;        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;&#xA;        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700&#34;&#xA;            style=&#34;text-decoration-line: none !important;&#34; href=&#34;#%D0%BD%D0%B0%D1%81%D1%82%D1%80%D0%BE%D0%B9%D0%BA%D0%B0-%D0%BF%D0%BE%D0%BB%D0%B8%D1%82%D0%B8%D0%BA-%D1%81%D0%BD%D0%B0%D0%BF%D1%88%D0%BE%D1%82%D0%BE%D0%B2&#34; aria-label=&#34;Якорь&#34;&gt;#&lt;/a&gt;&#xA;    &lt;/span&gt;        &#xA;    &#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Укажем системе что и когда бекапить:&lt;/p&gt;&#xA;&lt;p&gt;Переходим в раздел &lt;strong&gt;&lt;strong&gt;Policies&lt;/strong&gt;&lt;/strong&gt; и нажимаем &lt;strong&gt;&lt;strong&gt;Create a policy&lt;/strong&gt;&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/kibana-index-backup/Untitled2-1.png&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;&lt;strong&gt;Name&lt;/strong&gt;&lt;/strong&gt; - имя политики ни на что не влияет&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;&lt;strong&gt;Snapshot name&lt;/strong&gt;&lt;/strong&gt; - это именно имя снапшота, желательно разделять по дням добавив {now/d}&lt;/p&gt;&#xA;&lt;p&gt;На следующем экране &lt;strong&gt;&lt;strong&gt;убираем переключатель&lt;/strong&gt;&lt;/strong&gt;, с которым будут бекапиться все индексы и &lt;strong&gt;&lt;strong&gt;указываем название индекса&lt;/strong&gt;&lt;/strong&gt;, который мы хотим резервировать:&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/kibana-index-backup/Untitled3-1.png&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;На следующем экране говорим системе как долго хранить снапшоты:&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/kibana-index-backup/Untitled4-1.png&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Всё.&lt;/p&gt;&#xA;&lt;p&gt;На экране политик можно руками запустить выполнение бекапа:&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;  &#xA;  &#xA;    &lt;figure&gt;&#xA;      &#xA;      &lt;img&#xA;        class=&#34;my-0 rounded-md&#34;&#xA;        loading=&#34;lazy&#34;&#xA;        src=&#34;https://etogeek.dev/posts/kibana-index-backup/Untitled5-1.png&#34;&#xA;        alt=&#34;&#34;&#xA;      /&gt;&#xA;      &#xA;      &#xA;    &lt;/figure&gt;&#xA;  &#xA;&#xA;&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;🔥 Не забудь забекапить бекап проверить восстановление из бекапа&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;💡 Если вам нравятся мои посты, то подписывайтесь также:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://t.me/etogeek&#34;   target=&#34;_blank&#34;&gt;&#xA;    Телеграм канал&lt;/a&gt; 📺&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://t.me/etogeekchat&#34;   target=&#34;_blank&#34;&gt;&#xA;    Чат&lt;/a&gt; 🤘🏼&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</description>
    </item>
  </channel>
</rss>