<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Systemd on DevOps Way - Практические гайды</title>
    <link>https://devopsway.ru/tags/systemd/</link>
    <description>Recent content in Systemd on DevOps Way - Практические гайды</description>
    <image>
      <title>DevOps Way - Практические гайды</title>
      <url>https://devopsway.ru/images/devopsway-og.png</url>
      <link>https://devopsway.ru/images/devopsway-og.png</link>
    </image>
    <generator>Hugo -- 0.161.1</generator>
    <language>ru</language>
    <lastBuildDate>Thu, 07 May 2026 09:04:53 -0400</lastBuildDate>
    <atom:link href="https://devopsway.ru/tags/systemd/feed.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Linux: 10 принципов — 1. Автоматизация</title>
      <link>https://devopsway.ru/posts/linux-01-automation/</link>
      <pubDate>Thu, 07 May 2026 14:00:00 +0300</pubDate>
      <guid>https://devopsway.ru/posts/linux-01-automation/</guid>
      <description>Первый принцип из 10: автоматизация. Bash-скрипты, cron, systemd timers. Пишем health-check.sh, который следит за сервером пока вы спите.</description>
      <content:encoded><![CDATA[<table>
  <thead>
      <tr>
          <th>Параметр</th>
          <th>Значение</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Bloom</td>
          <td>L2–L3 (Понимание → Применение)</td>
      </tr>
      <tr>
          <td>SFIA</td>
          <td>Уровень 1–2</td>
      </tr>
      <tr>
          <td>Артефакт</td>
          <td>health-check.sh + systemd timer</td>
      </tr>
      <tr>
          <td>Проверка</td>
          <td><code>systemctl status health-check.timer</code> → active</td>
      </tr>
  </tbody>
</table>
<hr>
<h2 id="сервер-упал-в-3-ночи">Сервер упал в 3 ночи</h2>
<p>Вы проснулись от звонка. &ldquo;Сайт не открывается&rdquo;. Полусонный, подключаетесь по SSH, смотрите логи. Диск заполнен на 100% &ndash; контейнерные логи съели всё за ночь.</p>
<p>А теперь альтернативная реальность: скрипт каждые 5 минут проверял диск. Когда заполнение перевалило за 85%, в journalctl появилась строка WARN. Утром вы открыли логи, увидели проблему и спокойно почистили &ndash; без звонков и паники.</p>
<p>Разница &ndash; один bash-скрипт и systemd timer.</p>
<p><strong>Автоматизация</strong> &ndash; это не &ldquo;напиши скрипт когда-нибудь&rdquo;. Это принцип: всё, что делаешь руками второй раз, должна делать машина.</p>
<hr>
<h2 id="bash-основы-за-10-минут">Bash: основы за 10 минут</h2>
<p>Если вы знаете командную строку Linux, вы уже знаете 80% bash. Скрипт &ndash; это файл с командами, которые выполняются по порядку.</p>
<h3 id="первый-скрипт">Первый скрипт</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="cp">#!/bin/bash
</span></span></span><span class="line"><span class="cl"><span class="c1"># hello.sh — первый скрипт</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;Hostname: </span><span class="k">$(</span>hostname<span class="k">)</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;Uptime:   </span><span class="k">$(</span>uptime -p<span class="k">)</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;Date:     </span><span class="k">$(</span>date <span class="s1">&#39;+%Y-%m-%d %H:%M&#39;</span><span class="k">)</span><span class="s2">&#34;</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">chmod +x hello.sh
</span></span><span class="line"><span class="cl">./hello.sh
</span></span><span class="line"><span class="cl"><span class="c1"># Hostname: web-server-01</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Uptime:   up 14 days, 3 hours</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Date:     2026-05-07 14:30</span>
</span></span></code></pre></div><p>Ключевые моменты:</p>
<ul>
<li><code>#!/bin/bash</code> &ndash; shebang, указывает какой интерпретатор использовать</li>
<li><code>chmod +x</code> &ndash; сделать файл исполняемым</li>
<li><code>$(команда)</code> &ndash; подставить результат команды</li>
</ul>
<h3 id="переменные-и-условия">Переменные и условия</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="cp">#!/bin/bash
</span></span></span><span class="line"><span class="cl"><span class="c1"># check-disk.sh — проверка диска</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">THRESHOLD</span><span class="o">=</span><span class="m">80</span>
</span></span><span class="line"><span class="cl"><span class="nv">USAGE</span><span class="o">=</span><span class="k">$(</span>df / <span class="p">|</span> tail -1 <span class="p">|</span> awk <span class="s1">&#39;{print $5}&#39;</span> <span class="p">|</span> tr -d <span class="s1">&#39;%&#39;</span><span class="k">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="o">[</span> <span class="s2">&#34;</span><span class="nv">$USAGE</span><span class="s2">&#34;</span> -gt <span class="s2">&#34;</span><span class="nv">$THRESHOLD</span><span class="s2">&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;WARN: Disk usage </span><span class="si">${</span><span class="nv">USAGE</span><span class="si">}</span><span class="s2">% (threshold: </span><span class="si">${</span><span class="nv">THRESHOLD</span><span class="si">}</span><span class="s2">%)&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl"><span class="k">else</span>
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;OK: Disk usage </span><span class="si">${</span><span class="nv">USAGE</span><span class="si">}</span><span class="s2">%&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">exit</span> <span class="m">0</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span></code></pre></div><p>Обратите внимание на <code>exit 1</code> и <code>exit 0</code>. Код возврата &ndash; это язык, на котором скрипты общаются с системой: 0 = всё хорошо, не-0 = проблема.</p>
<h3 id="функции">Функции</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="cp">#!/bin/bash
</span></span></span><span class="line"><span class="cl"><span class="c1"># health-check.sh — проверка здоровья сервера</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">check_disk<span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="nb">local</span> <span class="nv">threshold</span><span class="o">=</span><span class="s2">&#34;</span><span class="si">${</span><span class="nv">1</span><span class="k">:-</span><span class="nv">80</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">local</span> usage
</span></span><span class="line"><span class="cl">    <span class="nv">usage</span><span class="o">=</span><span class="k">$(</span>df / <span class="p">|</span> tail -1 <span class="p">|</span> awk <span class="s1">&#39;{print $5}&#39;</span> <span class="p">|</span> tr -d <span class="s1">&#39;%&#39;</span><span class="k">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="o">[</span> <span class="s2">&#34;</span><span class="nv">$usage</span><span class="s2">&#34;</span> -gt <span class="s2">&#34;</span><span class="nv">$threshold</span><span class="s2">&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">        <span class="nb">echo</span> <span class="s2">&#34;WARN disk=</span><span class="si">${</span><span class="nv">usage</span><span class="si">}</span><span class="s2">%&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl">    <span class="k">fi</span>
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;OK disk=</span><span class="si">${</span><span class="nv">usage</span><span class="si">}</span><span class="s2">%&#34;</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">check_memory<span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="nb">local</span> <span class="nv">threshold</span><span class="o">=</span><span class="s2">&#34;</span><span class="si">${</span><span class="nv">1</span><span class="k">:-</span><span class="nv">500</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">local</span> available
</span></span><span class="line"><span class="cl">    <span class="nv">available</span><span class="o">=</span><span class="k">$(</span>free -m <span class="p">|</span> awk <span class="s1">&#39;/Mem:/{print $7}&#39;</span><span class="k">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="o">[</span> <span class="s2">&#34;</span><span class="nv">$available</span><span class="s2">&#34;</span> -lt <span class="s2">&#34;</span><span class="nv">$threshold</span><span class="s2">&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">        <span class="nb">echo</span> <span class="s2">&#34;WARN memory=</span><span class="si">${</span><span class="nv">available</span><span class="si">}</span><span class="s2">MB free&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl">    <span class="k">fi</span>
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;OK memory=</span><span class="si">${</span><span class="nv">available</span><span class="si">}</span><span class="s2">MB free&#34;</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">check_load<span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="nb">local</span> <span class="nv">threshold</span><span class="o">=</span><span class="s2">&#34;</span><span class="si">${</span><span class="nv">1</span><span class="k">:-</span><span class="nv">4</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">local</span> load
</span></span><span class="line"><span class="cl">    <span class="nv">load</span><span class="o">=</span><span class="k">$(</span>awk <span class="s1">&#39;{printf &#34;%.0f&#34;, $1}&#39;</span> /proc/loadavg<span class="k">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="o">[</span> <span class="s2">&#34;</span><span class="nv">$load</span><span class="s2">&#34;</span> -gt <span class="s2">&#34;</span><span class="nv">$threshold</span><span class="s2">&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">        <span class="nb">echo</span> <span class="s2">&#34;WARN load=</span><span class="si">${</span><span class="nv">load</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl">    <span class="k">fi</span>
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;OK load=</span><span class="si">${</span><span class="nv">load</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Запускаем все проверки</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;=== Health Check </span><span class="k">$(</span>date <span class="s1">&#39;+%H:%M:%S&#39;</span><span class="k">)</span><span class="s2"> ===&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">FAILED</span><span class="o">=</span><span class="m">0</span>
</span></span><span class="line"><span class="cl">check_disk  <span class="m">80</span>  <span class="o">||</span> <span class="nv">FAILED</span><span class="o">=</span><span class="k">$((</span>FAILED <span class="o">+</span> <span class="m">1</span><span class="k">))</span>
</span></span><span class="line"><span class="cl">check_memory <span class="m">500</span> <span class="o">||</span> <span class="nv">FAILED</span><span class="o">=</span><span class="k">$((</span>FAILED <span class="o">+</span> <span class="m">1</span><span class="k">))</span>
</span></span><span class="line"><span class="cl">check_load  <span class="m">4</span>   <span class="o">||</span> <span class="nv">FAILED</span><span class="o">=</span><span class="k">$((</span>FAILED <span class="o">+</span> <span class="m">1</span><span class="k">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="o">[</span> <span class="s2">&#34;</span><span class="nv">$FAILED</span><span class="s2">&#34;</span> -gt <span class="m">0</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;=== </span><span class="nv">$FAILED</span><span class="s2"> check(s) failed ===&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl"><span class="k">else</span>
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;=== All checks passed ===&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">exit</span> <span class="m">0</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">chmod +x health-check.sh
</span></span><span class="line"><span class="cl">./health-check.sh
</span></span><span class="line"><span class="cl"><span class="c1"># === Health Check 14:35:12 ===</span>
</span></span><span class="line"><span class="cl"><span class="c1"># OK disk=45%</span>
</span></span><span class="line"><span class="cl"><span class="c1"># OK memory=2048MB free</span>
</span></span><span class="line"><span class="cl"><span class="c1"># OK load=1</span>
</span></span><span class="line"><span class="cl"><span class="c1"># === All checks passed ===</span>
</span></span></code></pre></div><hr>
<h2 id="cron-запуск-по-расписанию">Cron: запуск по расписанию</h2>
<p>Cron &ndash; классический планировщик в Linux. Формат расписания:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">┌───────── минута (0-59)
</span></span><span class="line"><span class="cl">│ ┌─────── час (0-23)
</span></span><span class="line"><span class="cl">│ │ ┌───── день месяца (1-31)
</span></span><span class="line"><span class="cl">│ │ │ ┌─── месяц (1-12)
</span></span><span class="line"><span class="cl">│ │ │ │ ┌─ день недели (0-7, 0 и 7 = воскресенье)
</span></span><span class="line"><span class="cl">│ │ │ │ │
</span></span><span class="line"><span class="cl">* * * * *  команда
</span></span></code></pre></div><p>Добавляем проверку каждые 5 минут:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">crontab -e
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-gdscript3" data-lang="gdscript3"><span class="line"><span class="cl"><span class="o">*/</span><span class="mi">5</span> <span class="o">*</span> <span class="o">*</span> <span class="o">*</span> <span class="o">*</span> <span class="o">/</span><span class="n">home</span><span class="o">/</span><span class="n">ubuntu</span><span class="o">/</span><span class="n">health</span><span class="o">-</span><span class="n">check</span><span class="o">.</span><span class="n">sh</span> <span class="o">&gt;&gt;</span> <span class="o">/</span><span class="k">var</span><span class="o">/</span><span class="nb">log</span><span class="o">/</span><span class="n">health</span><span class="o">-</span><span class="n">check</span><span class="o">.</span><span class="n">log</span> <span class="mi">2</span><span class="o">&gt;&amp;</span><span class="mi">1</span>
</span></span></code></pre></div><p>Проверяем:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">crontab -l
</span></span><span class="line"><span class="cl"><span class="c1"># */5 * * * * /home/ubuntu/health-check.sh &gt;&gt; /var/log/health-check.log 2&gt;&amp;1</span>
</span></span></code></pre></div><h3 id="типичные-ошибки-с-cron">Типичные ошибки с cron</h3>
<table>
  <thead>
      <tr>
          <th>Ошибка</th>
          <th>Причина</th>
          <th>Решение</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Скрипт не запускается</td>
          <td>Нет <code>chmod +x</code></td>
          <td><code>chmod +x script.sh</code></td>
      </tr>
      <tr>
          <td>Команда не найдена</td>
          <td>cron не знает PATH</td>
          <td>Указывайте полный путь: <code>/usr/bin/curl</code></td>
      </tr>
      <tr>
          <td>Нет вывода</td>
          <td>stdout/stderr не перенаправлены</td>
          <td><code>&gt;&gt; /var/log/file.log 2&gt;&amp;1</code></td>
      </tr>
      <tr>
          <td>Запускается не вовремя</td>
          <td>Timezone сервера</td>
          <td><code>timedatectl</code> для проверки</td>
      </tr>
  </tbody>
</table>
<hr>
<h2 id="systemd-timers-современная-альтернатива-cron">Systemd timers: современная альтернатива cron</h2>
<p>Systemd timers &ndash; это cron на стероидах. Преимущества:</p>
<ul>
<li>Логи через <code>journalctl</code> (не разбросаны по файлам)</li>
<li>Зависимости (запустить после сети, после диска)</li>
<li>Можно запустить вручную: <code>systemctl start unit.service</code></li>
<li>Видно статус: когда запускался, когда следующий</li>
</ul>
<h3 id="создаём-service-unit">Создаём service unit</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="c1"># /etc/systemd/system/health-check.service</span>
</span></span><span class="line"><span class="cl"><span class="k">[Unit]</span>
</span></span><span class="line"><span class="cl"><span class="na">Description</span><span class="o">=</span><span class="s">Server health check</span>
</span></span><span class="line"><span class="cl"><span class="na">After</span><span class="o">=</span><span class="s">network.target</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">[Service]</span>
</span></span><span class="line"><span class="cl"><span class="na">Type</span><span class="o">=</span><span class="s">oneshot</span>
</span></span><span class="line"><span class="cl"><span class="na">ExecStart</span><span class="o">=</span><span class="s">/home/ubuntu/health-check.sh</span>
</span></span><span class="line"><span class="cl"><span class="na">User</span><span class="o">=</span><span class="s">ubuntu</span>
</span></span></code></pre></div><h3 id="создаём-timer-unit">Создаём timer unit</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="c1"># /etc/systemd/system/health-check.timer</span>
</span></span><span class="line"><span class="cl"><span class="k">[Unit]</span>
</span></span><span class="line"><span class="cl"><span class="na">Description</span><span class="o">=</span><span class="s">Run health check every 5 minutes</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">[Timer]</span>
</span></span><span class="line"><span class="cl"><span class="na">OnBootSec</span><span class="o">=</span><span class="s">1min</span>
</span></span><span class="line"><span class="cl"><span class="na">OnUnitActiveSec</span><span class="o">=</span><span class="s">5min</span>
</span></span><span class="line"><span class="cl"><span class="na">AccuracySec</span><span class="o">=</span><span class="s">10s</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">[Install]</span>
</span></span><span class="line"><span class="cl"><span class="na">WantedBy</span><span class="o">=</span><span class="s">timers.target</span>
</span></span></code></pre></div><h3 id="активируем">Активируем</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo systemctl daemon-reload
</span></span><span class="line"><span class="cl">sudo systemctl <span class="nb">enable</span> --now health-check.timer
</span></span></code></pre></div><h3 id="проверяем">Проверяем</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Статус таймера</span>
</span></span><span class="line"><span class="cl">systemctl status health-check.timer
</span></span><span class="line"><span class="cl"><span class="c1"># ● health-check.timer - Run health check every 5 minutes</span>
</span></span><span class="line"><span class="cl"><span class="c1">#   Active: active (waiting)</span>
</span></span><span class="line"><span class="cl"><span class="c1">#   Trigger: Wed 2026-05-07 14:40:00 MSK; 2min left</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Список всех таймеров</span>
</span></span><span class="line"><span class="cl">systemctl list-timers
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Логи последнего запуска</span>
</span></span><span class="line"><span class="cl">journalctl -u health-check.service -n <span class="m">20</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Запустить вручную (для отладки)</span>
</span></span><span class="line"><span class="cl">sudo systemctl start health-check.service
</span></span><span class="line"><span class="cl">journalctl -u health-check.service --no-pager
</span></span></code></pre></div><h3 id="cron-vs-systemd-timer">Cron vs Systemd Timer</h3>
<table>
  <thead>
      <tr>
          <th>Параметр</th>
          <th>Cron</th>
          <th>Systemd Timer</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Логи</td>
          <td>Файл (сами настраиваете)</td>
          <td>journalctl (из коробки)</td>
      </tr>
      <tr>
          <td>Зависимости</td>
          <td>Нет</td>
          <td>After=network.target и т.д.</td>
      </tr>
      <tr>
          <td>Ручной запуск</td>
          <td>Нет (только ждать)</td>
          <td><code>systemctl start unit</code></td>
      </tr>
      <tr>
          <td>Статус</td>
          <td>Нет</td>
          <td><code>systemctl status</code></td>
      </tr>
      <tr>
          <td>Рандомизация</td>
          <td>Нет</td>
          <td>RandomizedDelaySec</td>
      </tr>
      <tr>
          <td>Удобство</td>
          <td>Одна строка</td>
          <td>Два файла (.service + .timer)</td>
      </tr>
  </tbody>
</table>
<p>Для простых задач cron проще. Для production &ndash; systemd timer надёжнее.</p>
<hr>
<h2 id="мини-тест">Мини-тест</h2>
<p><strong>1. Чем <code>exit 0</code> отличается от <code>exit 1</code> и зачем это нужно?</strong></p>
<details>
<summary>Ответ</summary>
<p><code>exit 0</code> &ndash; скрипт завершился успешно. <code>exit 1</code> &ndash; с ошибкой. Код возврата используется другими инструментами: cron, systemd, CI/CD pipeline, другие скрипты через <code>&amp;&amp;</code> и <code>||</code>. Это способ сообщить &ldquo;всё хорошо&rdquo; или &ldquo;что-то не так&rdquo; без парсинга текста.</p>
</details>
<p><strong>2. Почему в cron нужно писать полный путь <code>/usr/bin/curl</code> вместо просто <code>curl</code>?</strong></p>
<details>
<summary>Ответ</summary>
<p>Cron запускает команды с минимальным окружением, PATH обычно содержит только <code>/usr/bin:/bin</code>. Если <code>curl</code> установлен в <code>/usr/local/bin/</code>, cron его не найдёт. Полный путь решает проблему. Альтернатива &ndash; задать PATH в начале crontab.</p>
</details>
<p><strong>3. В чём преимущество <code>OnUnitActiveSec=5min</code> перед <code>*/5 * * * *</code>?</strong></p>
<details>
<summary>Ответ</summary>
<p><code>OnUnitActiveSec</code> отсчитывает время от завершения предыдущего запуска. Если скрипт выполняется 2 минуты, следующий запуск будет через 5 минут после окончания, а не по часам. Это гарантирует, что два экземпляра не перекроют друг друга. Cron всегда запускает по расписанию, даже если предыдущий ещё работает.</p>
</details>
<hr>
<h2 id="артефакт">Артефакт</h2>
<p>Три файла, которые после этого поста должны быть на вашем сервере:</p>
<ol>
<li><code>/home/ubuntu/health-check.sh</code> &ndash; скрипт проверки (из секции &ldquo;Функции&rdquo;)</li>
<li><code>/etc/systemd/system/health-check.service</code> &ndash; service unit</li>
<li><code>/etc/systemd/system/health-check.timer</code> &ndash; timer unit</li>
</ol>
<p>Проверка что всё работает:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Таймер активен?</span>
</span></span><span class="line"><span class="cl">systemctl is-active health-check.timer
</span></span><span class="line"><span class="cl"><span class="c1"># active</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Когда следующий запуск?</span>
</span></span><span class="line"><span class="cl">systemctl list-timers health-check.timer
</span></span><span class="line"><span class="cl"><span class="c1"># NEXT                         LEFT</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Wed 2026-05-07 14:45:00 MSK  3min left</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Логи работают?</span>
</span></span><span class="line"><span class="cl">journalctl -u health-check.service -n <span class="m">5</span> --no-pager
</span></span><span class="line"><span class="cl"><span class="c1"># May 07 14:40:01 web-server health-check.sh: === Health Check 14:40:01 ===</span>
</span></span><span class="line"><span class="cl"><span class="c1"># May 07 14:40:01 web-server health-check.sh: OK disk=45%</span>
</span></span><span class="line"><span class="cl"><span class="c1"># ...</span>
</span></span></code></pre></div><hr>
<h2 id="принцип">Принцип</h2>
<p>Автоматизация &ndash; не навык, а привычка мышления:</p>
<ul>
<li>Делаешь что-то <strong>второй раз</strong> → напиши скрипт</li>
<li>Скрипт запускаешь <strong>руками</strong> → добавь в cron/systemd</li>
<li>Результат скрипта <strong>читаешь глазами</strong> → добавь алерт</li>
</ul>
<p>Каждый уровень убирает человека из цепочки. Цель &ndash; чтобы проблема фиксировалась автоматически, а не когда вы вручную зашли и посмотрели.</p>
<hr>
<p>Это принцип 1 из 10. Следующий &ndash; <strong>Идемпотентность</strong>: почему <code>mkdir -p</code> лучше <code>mkdir</code>, и как писать скрипты, которые безопасно запускать повторно.</p>
<p>Все 10 принципов &ndash; в полном курсе <strong>КМБ (курс молодого бойца)</strong>. <a href="https://devopsway.ru/kmb/">Подробнее о курсе</a></p>
<hr>
<p>Telegram: <a href="https://t.me/DevITWay">@DevITWay</a>
Сайт: <a href="https://devopsway.ru/">devopsway.ru</a></p>
]]></content:encoded>
    </item>
  </channel>
</rss>
