This commit is contained in:
Hisumi 2022-10-08 12:32:59 +03:00
commit 42bb3bed62
8 changed files with 978 additions and 0 deletions

48
Makefile Normal file
View file

@ -0,0 +1,48 @@
all: install
install:
install -Dm644 settings.sh /etc/restic_backup/settings.sh
install -Dm755 restic_backup /usr/local/bin/restic_backup
install -Dm644 restic_backup.timer /etc/systemd/system/restic_backup.timer
install -Dm644 restic_backup.service /etc/systemd/system/restic_backup.service
systemctl daemon-reload
systemctl enable restic_backup.timer
uninstall:
systemctl disable restic_backup.timer
rm -v /etc/restic_backup/settings.sh
rm -v /usr/local/bin/restic_backup
rm -v /etc/systemd/system/restic_backup.timer
rm -v /etc/systemd/system/restic_backup.service
systemctl daemon-reload
html:
pandoc \
--from=markdown \
--to=html \
README.md \
--standalone \
--output=README.html \
--variable document-css=true \
--variable linkcolor='[HTML]{0000ff}' \
--metadata lang=ru \
--metadata title=''
pdf:
pandoc \
--from=markdown-implicit_figures \
--to=pdf \
README.md \
--output=README.pdf \
--pdf-engine=xelatex \
--variable mainfont='FreeSans' \
--variable monofont='FreeMono' \
--variable fontsize='12pt' \
--variable monofontoptions='Scale=0.9' \
--variable urlcolor='[HTML]{0000ff}' \
--variable pagestyle=empty \
--variable margin-left='20mm' \
--variable margin-right='20mm' \
--variable margin-top='20mm' \
--variable margin-bottom='20mm' \
--highlight-style haddock

451
README.html Normal file
View file

@ -0,0 +1,451 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru">
<head>
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
<title>README</title>
<style>
html {
line-height: 1.5;
font-family: Georgia, serif;
font-size: 20px;
color: #1a1a1a;
background-color: #fdfdfd;
}
body {
margin: 0 auto;
max-width: 36em;
padding-left: 50px;
padding-right: 50px;
padding-top: 50px;
padding-bottom: 50px;
hyphens: auto;
overflow-wrap: break-word;
text-rendering: optimizeLegibility;
font-kerning: normal;
}
@media (max-width: 600px) {
body {
font-size: 0.9em;
padding: 1em;
}
h1 {
font-size: 1.8em;
}
}
@media print {
body {
background-color: transparent;
color: black;
font-size: 12pt;
}
p, h2, h3 {
orphans: 3;
widows: 3;
}
h2, h3, h4 {
page-break-after: avoid;
}
}
p {
margin: 1em 0;
}
a {
color: [HTML]{0000ff};
}
a:visited {
color: [HTML]{0000ff};
}
img {
max-width: 100%;
}
h1, h2, h3, h4, h5, h6 {
margin-top: 1.4em;
}
h5, h6 {
font-size: 1em;
font-style: italic;
}
h6 {
font-weight: normal;
}
ol, ul {
padding-left: 1.7em;
margin-top: 1em;
}
li > ol, li > ul {
margin-top: 0;
}
blockquote {
margin: 1em 0 1em 1.7em;
padding-left: 1em;
border-left: 2px solid #e6e6e6;
color: #606060;
}
code {
font-family: Menlo, Monaco, 'Lucida Console', Consolas, monospace;
font-size: 85%;
margin: 0;
}
pre {
margin: 1em 0;
overflow: auto;
}
pre code {
padding: 0;
overflow: visible;
overflow-wrap: normal;
}
.sourceCode {
background-color: transparent;
overflow: visible;
}
hr {
background-color: #1a1a1a;
border: none;
height: 1px;
margin: 1em 0;
}
table {
margin: 1em 0;
border-collapse: collapse;
width: 100%;
overflow-x: auto;
display: block;
font-variant-numeric: lining-nums tabular-nums;
}
table caption {
margin-bottom: 0.75em;
}
tbody {
margin-top: 0.5em;
border-top: 1px solid #1a1a1a;
border-bottom: 1px solid #1a1a1a;
}
th {
border-top: 1px solid #1a1a1a;
padding: 0.25em 0.5em 0.25em 0.5em;
}
td {
padding: 0.125em 0.5em 0.25em 0.5em;
}
header {
margin-bottom: 4em;
text-align: center;
}
#TOC li {
list-style: none;
}
#TOC ul {
padding-left: 1.3em;
}
#TOC > ul {
padding-left: 0;
}
#TOC a:not(:hover) {
text-decoration: none;
}
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
span.underline{text-decoration: underline;}
div.column{display: inline-block; vertical-align: top; width: 50%;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
ul.task-list{list-style: none;}
pre > code.sourceCode { white-space: pre; position: relative; }
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
pre > code.sourceCode > span:empty { height: 1.2em; }
.sourceCode { overflow: visible; }
code.sourceCode > span { color: inherit; text-decoration: inherit; }
div.sourceCode { margin: 1em 0; }
pre.sourceCode { margin: 0; }
@media screen {
div.sourceCode { overflow: auto; }
}
@media print {
pre > code.sourceCode { white-space: pre-wrap; }
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
}
pre.numberSource code
{ counter-reset: source-line 0; }
pre.numberSource code > span
{ position: relative; left: -4em; counter-increment: source-line; }
pre.numberSource code > span > a:first-child::before
{ content: counter(source-line);
position: relative; left: -1em; text-align: right; vertical-align: baseline;
border: none; display: inline-block;
-webkit-touch-callout: none; -webkit-user-select: none;
-khtml-user-select: none; -moz-user-select: none;
-ms-user-select: none; user-select: none;
padding: 0 4px; width: 4em;
color: #aaaaaa;
}
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
div.sourceCode
{ }
@media screen {
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
}
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
code span.at { color: #7d9029; } /* Attribute */
code span.bn { color: #40a070; } /* BaseN */
code span.bu { color: #008000; } /* BuiltIn */
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
code span.ch { color: #4070a0; } /* Char */
code span.cn { color: #880000; } /* Constant */
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
code span.dt { color: #902000; } /* DataType */
code span.dv { color: #40a070; } /* DecVal */
code span.er { color: #ff0000; font-weight: bold; } /* Error */
code span.ex { } /* Extension */
code span.fl { color: #40a070; } /* Float */
code span.fu { color: #06287e; } /* Function */
code span.im { color: #008000; font-weight: bold; } /* Import */
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
code span.op { color: #666666; } /* Operator */
code span.ot { color: #007020; } /* Other */
code span.pp { color: #bc7a00; } /* Preprocessor */
code span.sc { color: #4070a0; } /* SpecialChar */
code span.ss { color: #bb6688; } /* SpecialString */
code span.st { color: #4070a0; } /* String */
code span.va { color: #19177c; } /* Variable */
code span.vs { color: #4070a0; } /* VerbatimString */
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
.display.math{display: block; text-align: center; margin: 0.5rem auto;}
</style>
<!--[if lt IE 9]>
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
<![endif]-->
</head>
<body>
<h1 id="restic_backup">restic_backup</h1>
<p><strong>restic_backup</strong> это скрипт для бэкапа файлов и баз
данных с помощью утилиты <strong>restic</strong>.</p>
<p>Ссылки:</p>
<ul>
<li><a href="https://restic.net/">Сайт проекта restic</a></li>
<li><a href="https://restic.readthedocs.io/">Документация restic</a>
(англ.)</li>
</ul>
<h1 id="описание-работы-скрипта">Описание работы скрипта</h1>
<p>Скрипт выполняет инкрементальные резервные копии в репозиторий для
бэкапов. В качестве репозитория может использоваться S3-севместимое
хранилище или другой диск. Все варианты описаны <a
href="https://restic.readthedocs.io/en/stable/030_preparing_a_new_repo.html">здесь</a>.</p>
<p>Инкрементальный бэкап означает, что первый бэкап содержит полную
копию всех данных, а все последующие копии содержат только изменённые
файлы. Поэтому первая резервная копия будет выполняться значительно
дольше последующих.</p>
<p>Бэкап запускается ежедневно по таймеру systemd
<strong>restic_backup.timer</strong>, таймер запускает юнит systemd
<strong>restic_backup.service</strong> из которого происходит
непосредственнй запуск скрипта <strong>restic_backup</strong>.</p>
<p>Просмотреть список таймеров можно командой:</p>
<pre><code>systemctl list-timers</code></pre>
<p>Уведомления на электронную почту при падении бэкапа могут быть
настроены по инструкции <a
href="https://wiki.archlinux.org/title/Systemd_(%D0%A0%D1%83%D1%81%D1%81%D0%BA%D0%B8%D0%B9)/Timers_(%D0%A0%D1%83%D1%81%D1%81%D0%BA%D0%B8%D0%B9)#%D0%92_%D0%BA%D0%B0%D1%87%D0%B5%D1%81%D1%82%D0%B2%D0%B5_%D0%B7%D0%B0%D0%BC%D0%B5%D0%BD%D1%8B_cron">systemd
Timers</a>.</p>
<p>Как альтернатива вместо таймеров systemd можно использовать
<strong>cron</strong>. Пример cron-задачи:</p>
<pre><code>0 1 * * * /usr/bin/local/restic_backup </code></pre>
<p>Общий алгоритм работы скрипта такой:</p>
<ol type="1">
<li>Скрипт вначале выполняет проверку инициализации репозитория и
инициализирует его если это не так командой
<code>restic init</code>.</li>
<li>Запускается удаление старых резервных копий командой
<code>restic forget</code>. В скрипте используется опция
<code>--keep-last</code>, однако можно переписать
<strong>/usr/bin/local/restic_backup</strong> на использование другой
опции. Сохраняется то количество резервных копий, которое указано в
переменной <code>RESTIC_KEEP</code> в файле
<strong>/etc/restic_backup/settings.sh</strong>.</li>
<li>Запускается дамп баз данных. SQL-дампы сохраняются во временную
директорию <strong>/tmp/restic_dbs_temp</strong>.</li>
<li>Выполняется запуск <code>retic backup</code>. В репозиторий
загружаются файлы и дампы баз данных.</li>
<li>В конце процесса удаляется временная директория с SQL-дампами.</li>
</ol>
<p>Подробный отладочный лог (xtrace) работы скрипта сохраняется в файл
<strong>/var/log/restic_backup.log</strong>. Если запуск бэкапа
производится по таймеру systemd, то логи также будут доступны через
утилиту <strong>journalctl</strong>:</p>
<pre><code>journalctl -u restic_backup.service</code></pre>
<p>Список файлов входящих в скрипт <strong>restic_backup</strong>:</p>
<ul>
<li><strong>/usr/local/bin/restic_backup</strong> — непосредственно
скрипт резервного копирования.</li>
<li><strong>/etc/restic_backup/settings.sh</strong> — файл с настройками
резервного копирования.</li>
<li><strong>/etc/systemd/system/restic_backup.timer</strong> — таймер
systemd.</li>
<li><strong>/etc/systemd/system/restic_backup.service</strong> — юнит
systemd запускающий бэкап, запускается через таймер.</li>
</ul>
<h1 id="подготовка-к-резервному-копированию">Подготовка к резервному
копированию</h1>
<h2 id="использование-makefile">Использование Makefile</h2>
<p><strong>Makefile</strong> это файл для утилиты <strong>GNU
make</strong>, помогающий автоматически выполнять некоторые действия по
коротким командам.</p>
<p>В процессе эксплуатации <strong>restic_backup</strong> Makefile не
используется, однако он помогает в установке скрипта. Доступные
команды:</p>
<ul>
<li><strong>make install</strong> — установка скрипта</li>
<li><strong>make uninstall</strong> — удаление скрипта вместе со всеми
настройками</li>
<li><strong>make html</strong> — конвертировать README.md в форат
HTML.</li>
<li><strong>make pdf</strong> — конвертировать README.md в форат
PDF.</li>
</ul>
<p>Последние две команды требуют установки утилиты
<strong>pandoc</strong>.</p>
<h2 id="настройка-хранилища">Настройка хранилища</h2>
<p>Будет рассмотрена настройка S3-бакета в качестве репозитория для
бэкапов.</p>
<ol type="1">
<li>Создать бакет</li>
<li>Указать реквизиты подключения в файле
<strong>/etc/restic_backup/settings.sh</strong>. Файл имеет подробные
комментарии с описанием всех параметров.</li>
</ol>
<h1 id="конфигурирование">Конфигурирование</h1>
<h2 id="настройка-параметров-бэкапа">Настройка параметров бэкапа</h2>
<p>Фрагмент файла <strong>/etc/restic_backup/settings.sh</strong>:</p>
<div class="sourceCode" id="cb4"><pre
class="sourceCode bash"><code class="sourceCode bash"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="co"># --------------------------- НАСТРОЙКИ RESTIC ---------------------------- #</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a><span class="co"># Ключ доступа к бакету S3. Совпадает с логином аккаунта</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a><span class="bu">export</span> <span class="va">AWS_ACCESS_KEY_ID</span><span class="op">=&lt;</span>MY_ACCESS_KEY<span class="op">&gt;</span></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a><span class="co"># Секретный ключ доступа к бакету. Можно скопировать из ПУА.</span></span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a><span class="bu">export</span> <span class="va">AWS_SECRET_ACCESS_KEY</span><span class="op">=&lt;</span>MY_SECRET_ACCESS_KEY<span class="op">&gt;</span></span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a><span class="co"># Адрес репозитория, куда будут сохраняться резервные копии</span></span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a><span class="bu">export</span> <span class="va">RESTIC_REPOSITORY</span><span class="op">=</span><span class="st">&quot;s3:https://s3.timeweb.com/restic-demo&quot;</span></span>
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-12"><a href="#cb4-12" aria-hidden="true" tabindex="-1"></a><span class="co"># Пароль для доупа к бэкапам. Все бэкапы шифруются. В случае утраты этого</span></span>
<span id="cb4-13"><a href="#cb4-13" aria-hidden="true" tabindex="-1"></a><span class="co"># пароля получить доступ к резрвным копиям будет невозможно!</span></span>
<span id="cb4-14"><a href="#cb4-14" aria-hidden="true" tabindex="-1"></a><span class="bu">export</span> <span class="va">RESTIC_PASSWORD</span><span class="op">=</span><span class="st">&quot;I9n7G7G0ZpDWA3GOcJbIuwQCGvGUBkU5&quot;</span></span>
<span id="cb4-15"><a href="#cb4-15" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-16"><a href="#cb4-16" aria-hidden="true" tabindex="-1"></a><span class="co"># Количество бэкпов, которые нужно хранить в репозитории</span></span>
<span id="cb4-17"><a href="#cb4-17" aria-hidden="true" tabindex="-1"></a><span class="va">RESTIC_KEEP</span><span class="op">=</span>2</span></code></pre></div>
<h2 id="добавление-файлов-в-бэкап">Добавление файлов в бэкап</h2>
<div class="sourceCode" id="cb5"><pre
class="sourceCode bash"><code class="sourceCode bash"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="co"># ------------------ СПИСОК ФАЙЛОВ И ДИРЕКТОРИЙ ДЛЯ БЭКАПА ---------------- #</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a><span class="co"># Пути к файлам и директориям обязательно должны быть абсолютными.</span></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a><span class="co"># Абсолютный путь содержит полный путь до файла/директории начиная от</span></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a><span class="co"># корневого каталога системы.</span></span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a><span class="co">#</span></span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a><span class="co"># Переменная `files` ниже содержит перечень путей. Обратите внимание, что</span></span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a><span class="co"># пути, которые содержат пробелы должны быть обязательно включены в кавычки.</span></span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a><span class="co"># Пример: &#39;/home/user/some dir&#39;</span></span>
<span id="cb5-10"><a href="#cb5-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-11"><a href="#cb5-11" aria-hidden="true" tabindex="-1"></a><span class="va">files</span><span class="op">=</span><span class="va">(</span></span>
<span id="cb5-12"><a href="#cb5-12" aria-hidden="true" tabindex="-1"></a> /etc</span>
<span id="cb5-13"><a href="#cb5-13" aria-hidden="true" tabindex="-1"></a> /home</span>
<span id="cb5-14"><a href="#cb5-14" aria-hidden="true" tabindex="-1"></a><span class="va">)</span></span></code></pre></div>
<h2 id="добавление-баз-данных-в-бэкап">Добавление баз данных в
бэкап</h2>
<div class="sourceCode" id="cb6"><pre
class="sourceCode bash"><code class="sourceCode bash"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="co"># ---------------------- СПИСОК БАЗ ДАННЫХ ДЛЯ БЭКАПА --------------------- #</span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a><span class="co"># Ниже необходимо указать реквизиты подключения к базам данных в формате URI</span></span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a><span class="co"># (DSN). Запись должна иметь следующий вид:</span></span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a><span class="co">#</span></span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a><span class="co"># mysql://пользователь:пароль@хост:порт/имя_базы_данных</span></span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a><span class="co">#</span></span>
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a><span class="co"># Обратите внимание на разделители между частями URI: &quot;:&quot;, &quot;@&quot;, &quot;/&quot;. Порт</span></span>
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a><span class="co"># можно опустить, если используется стандартный 3306.</span></span>
<span id="cb6-10"><a href="#cb6-10" aria-hidden="true" tabindex="-1"></a><span class="co">#</span></span>
<span id="cb6-11"><a href="#cb6-11" aria-hidden="true" tabindex="-1"></a><span class="co"># Ниже дан записи для БД bitrix_db и именем пользователя bitrix_usr и</span></span>
<span id="cb6-12"><a href="#cb6-12" aria-hidden="true" tabindex="-1"></a><span class="co"># паролем boRpBnhGn7ue.</span></span>
<span id="cb6-13"><a href="#cb6-13" aria-hidden="true" tabindex="-1"></a><span class="co">#</span></span>
<span id="cb6-14"><a href="#cb6-14" aria-hidden="true" tabindex="-1"></a><span class="co"># mysql://bitrix_usr:boRpBnhGn7ue@localhost/bitrix_db</span></span>
<span id="cb6-15"><a href="#cb6-15" aria-hidden="true" tabindex="-1"></a><span class="co">#</span></span>
<span id="cb6-16"><a href="#cb6-16" aria-hidden="true" tabindex="-1"></a><span class="co"># Если пароль пользователя базы данных содержит специальные символы, то такой</span></span>
<span id="cb6-17"><a href="#cb6-17" aria-hidden="true" tabindex="-1"></a><span class="co"># пароль обязательно надо закодировать в percent code. Это можно сделать</span></span>
<span id="cb6-18"><a href="#cb6-18" aria-hidden="true" tabindex="-1"></a><span class="co"># следующей командой в терминале:</span></span>
<span id="cb6-19"><a href="#cb6-19" aria-hidden="true" tabindex="-1"></a><span class="co">#</span></span>
<span id="cb6-20"><a href="#cb6-20" aria-hidden="true" tabindex="-1"></a><span class="co"># echo &#39;ваш_пароль&#39; | perl -MURI::Escape -wlne &#39;print uri_escape $_&#39;</span></span>
<span id="cb6-21"><a href="#cb6-21" aria-hidden="true" tabindex="-1"></a><span class="co">#</span></span>
<span id="cb6-22"><a href="#cb6-22" aria-hidden="true" tabindex="-1"></a><span class="co"># или через онлайн-сервис https://meyerweb.com/eric/tools/dencoder/</span></span>
<span id="cb6-23"><a href="#cb6-23" aria-hidden="true" tabindex="-1"></a><span class="co">#</span></span>
<span id="cb6-24"><a href="#cb6-24" aria-hidden="true" tabindex="-1"></a><span class="co"># Ниже введите URI с реквизитами баз данных так как это показано. Обратите</span></span>
<span id="cb6-25"><a href="#cb6-25" aria-hidden="true" tabindex="-1"></a><span class="co"># внимание, что знак равно не должен юыть отделён пробелами. Формат записи</span></span>
<span id="cb6-26"><a href="#cb6-26" aria-hidden="true" tabindex="-1"></a><span class="co"># строго такой как есть.</span></span>
<span id="cb6-27"><a href="#cb6-27" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-28"><a href="#cb6-28" aria-hidden="true" tabindex="-1"></a><span class="va">databases</span><span class="op">=</span><span class="va">(</span></span>
<span id="cb6-29"><a href="#cb6-29" aria-hidden="true" tabindex="-1"></a> mysql://пользователь:пароль@хост/базаанных</span>
<span id="cb6-30"><a href="#cb6-30" aria-hidden="true" tabindex="-1"></a> mysql://пользователь:пароль@хост/другая_базаанных</span>
<span id="cb6-31"><a href="#cb6-31" aria-hidden="true" tabindex="-1"></a><span class="va">)</span></span></code></pre></div>
<h1 id="запуск-резервного-копирования">Запуск резервного
копирования</h1>
<p>Запустить резервное копирование прямо сейчас можно командой:</p>
<pre><code>/usr/bin/local/restic_backup</code></pre>
<p>Выполнение бэкапа заблокирует ввод в темнал. Желательно запускать
команду в сессии tmux или screen. Пример запуска сессии tmux:</p>
<pre><code>tmux new -s restic_backup</code></pre>
<p>Весь вывод скрипта отправляется в отладочный лог
<strong>/var/log/restic_backup.log</strong>.</p>
<p>Отслеживать проесс можно командой:</p>
<pre><code>tail -f /var/log/restic_backup.log</code></pre>
<h1 id="восстановление-из-резервной-копии">Восстановление из резервной
копии</h1>
<p>Для удобства работы перед вызовом команд <strong>restic</strong>
следует импортировать файл с настройками.</p>
<p>Это делается командой:</p>
<pre><code>source /etc/restic_backup/settings.sh</code></pre>
<p>После чего можно выполнять команды restic без надобности вручную
указывать адрес репозитория и пароль.</p>
<h2 id="восстановление-файлов">Восстановление файлов</h2>
<p>Воспользуйтесь инструкцией из официальной документации утилиты:
https://restic.readthedocs.io/en/stable/050_restore.html</p>
<p>Сперва вам потребуется узнать ID снапшота (так в restic называются
бэкапы). Для этого запустите команду:</p>
<pre><code>restic snapshots</code></pre>
<p>В терминал будет напечатана таблица со списком всех имеющихся
снапшотов.</p>
<p>Вы можете восстанавливать снапшоты целиком командой вида:</p>
<pre><code>restic restore 79766175 --target /tmp/restore-work</code></pre>
<p>где 79766175 это ID снапшота, а /tmp/restore-work директория, куда
будут восстанавливаться файлы.</p>
<p>Другой вариант восстановления — через монтирование репозитория в
качестве сетевого диска. Документация:
https://restic.readthedocs.io/en/stable/050_restore.html#restore-using-mount</p>
<p>С примонтированного диска вы можете скопировать как все файлы, так и
отдельные.</p>
<p>Команды для монтирования:</p>
<pre><code>mkdir -p /mnt/restic
restic mount /mnt/restic</code></pre>
<p>restic при помощи FUSE создаст виртуальную файловую систему. При этом
терминал будет заблокирован. Можно запустить команду на монтирвоание в
сессии tmux или screen и работать с примонтирвоанным диском в другом
окне. Либо открыть новую сессию SSH и работать в ней. После можно
прервать restic сочетанием клавиш <code>Ctrl+C</code> или выполнить
команду:</p>
<pre><code>fusermount -u /mnt/restic</code></pre>
<p>чтобы отмонтировать бэкапы.</p>
<h2 id="восстановление-баз-данных">Восстановление баз данных</h2>
<p>Восстановление баз данных почти ничем не отличается от восстановления
обычных файлов.</p>
<ol type="1">
<li>Восстановите дамп в формате <strong>.sql.gz</strong> из репозитория
restic</li>
<li>Запустите команду для импорта дампа.</li>
</ol>
<pre><code>zcat имя_дампа.sql.gz | mysql -u&#39;пользователь&#39; имя_базы_данных -p&#39;пароль&#39;</code></pre>
<h1 id="удаление-старых-бэкапов">Удаление старых бэкапов</h1>
<p>Удаление производится командой <code>restic forget</code>. Смотрите
подробное описание в документации
https://restic.readthedocs.io/en/stable/060_forget.html</p>
</body>
</html>

275
README.md Normal file
View file

@ -0,0 +1,275 @@
# restic\_backup
**restic_backup** это скрипт для бэкапа файлов и баз данных с помощью утилиты
**restic**.
Ссылки:
- [Сайт проекта restic](https://restic.net/)
- [Документация restic](https://restic.readthedocs.io/) (англ.)
# Описание работы скрипта
Скрипт выполняет инкрементальные резервные копии в репозиторий для бэкапов. В
качестве репозитория может использоваться S3-севместимое хранилище или другой
диск. Все варианты описаны [здесь](https://restic.readthedocs.io/en/stable/030_preparing_a_new_repo.html).
Инкрементальный бэкап означает, что первый бэкап содержит полную копию всех
данных, а все последующие копии содержат только изменённые файлы. Поэтому первая
резервная копия будет выполняться значительно дольше последующих.
Бэкап запускается ежедневно по таймеру systemd **restic_backup.timer**, таймер
запускает юнит systemd **restic_backup.service** из которого происходит
непосредственнй запуск скрипта **restic_backup**.
Просмотреть список таймеров можно командой:
```
systemctl list-timers
```
Уведомления на электронную почту при падении бэкапа могут быть настроены по
инструкции [systemd Timers](https://wiki.archlinux.org/title/Systemd_(%D0%A0%D1%83%D1%81%D1%81%D0%BA%D0%B8%D0%B9)/Timers_(%D0%A0%D1%83%D1%81%D1%81%D0%BA%D0%B8%D0%B9)#%D0%92_%D0%BA%D0%B0%D1%87%D0%B5%D1%81%D1%82%D0%B2%D0%B5_%D0%B7%D0%B0%D0%BC%D0%B5%D0%BD%D1%8B_cron).
Как альтернатива вместо таймеров systemd можно использовать **cron**. Пример
cron-задачи:
```
0 1 * * * /usr/bin/local/restic_backup
```
Общий алгоритм работы скрипта такой:
1. Скрипт вначале выполняет проверку инициализации репозитория и инициализирует
его если это не так командой `restic init`.
2. Запускается удаление старых резервных копий командой `restic forget`. В
скрипте используется опция `--keep-last`, однако можно переписать
**/usr/bin/local/restic_backup** на использование другой опции. Сохраняется то
количество резервных копий, которое указано в переменной `RESTIC_KEEP` в файле
**/etc/restic_backup/settings.sh**.
3. Запускается дамп баз данных. SQL-дампы сохраняются во временную директорию
**/tmp/restic_dbs_temp**.
4. Выполняется запуск `retic backup`. В репозиторий загружаются файлы и дампы
баз данных.
5. В конце процесса удаляется временная директория с SQL-дампами.
Подробный отладочный лог (xtrace) работы скрипта сохраняется в файл
**/var/log/restic_backup.log**. Если запуск бэкапа производится по таймеру
systemd, то логи также будут доступны через утилиту **journalctl**:
```
journalctl -u restic_backup.service
```
Список файлов входящих в скрипт **restic_backup**:
- **/usr/local/bin/restic_backup** — непосредственно скрипт резервного копирования.
- **/etc/restic_backup/settings.sh** — файл с настройками резервного копирования.
- **/etc/systemd/system/restic_backup.timer** — таймер systemd.
- **/etc/systemd/system/restic_backup.service** — юнит systemd запускающий бэкап,
запускается через таймер.
# Подготовка к резервному копированию
## Использование Makefile
**Makefile** это файл для утилиты **GNU make**, помогающий автоматически выполнять
некоторые действия по коротким командам.
В процессе эксплуатации **restic_backup** Makefile не используется, однако он
помогает в установке скрипта. Доступные команды:
- **make install** — установка скрипта
- **make uninstall** — удаление скрипта вместе со всеми настройками
- **make html** — конвертировать README.md в форат HTML.
- **make pdf** — конвертировать README.md в форат PDF.
Последние две команды требуют установки утилиты **pandoc**.
## Настройка хранилища
Будет рассмотрена настройка S3-бакета в качестве репозитория для бэкапов.
1. Создать бакет
2. Указать реквизиты подключения в файле **/etc/restic_backup/settings.sh**. Файл
имеет подробные комментарии с описанием всех параметров.
# Конфигурирование
## Настройка параметров бэкапа
Фрагмент файла **/etc/restic_backup/settings.sh**:
```bash
# --------------------------- НАСТРОЙКИ RESTIC ---------------------------- #
# Ключ доступа к бакету S3. Совпадает с логином аккаунта
export AWS_ACCESS_KEY_ID=<MY_ACCESS_KEY>
# Секретный ключ доступа к бакету. Можно скопировать из ПУА.
export AWS_SECRET_ACCESS_KEY=<MY_SECRET_ACCESS_KEY>
# Адрес репозитория, куда будут сохраняться резервные копии
export RESTIC_REPOSITORY="s3:https://s3.timeweb.com/restic-demo"
# Пароль для доупа к бэкапам. Все бэкапы шифруются. В случае утраты этого
# пароля получить доступ к резрвным копиям будет невозможно!
export RESTIC_PASSWORD="tiwfBM4i3jDvMA6ah1AIs2HdoJ9w66OS"
# Количество бэкпов, которые нужно хранить в репозитории
RESTIC_KEEP=2
```
## Добавление файлов в бэкап
```bash
# ------------------ СПИСОК ФАЙЛОВ И ДИРЕКТОРИЙ ДЛЯ БЭКАПА ---------------- #
# Пути к файлам и директориям обязательно должны быть абсолютными.
# Абсолютный путь содержит полный путь до файла/директории начиная от
# корневого каталога системы.
#
# Переменная `files` ниже содержит перечень путей. Обратите внимание, что
# пути, которые содержат пробелы должны быть обязательно включены в кавычки.
# Пример: '/home/user/some dir'
files=(
/etc
/home
)
```
## Добавление баз данных в бэкап
```bash
# ---------------------- СПИСОК БАЗ ДАННЫХ ДЛЯ БЭКАПА --------------------- #
# Ниже необходимо указать реквизиты подключения к базам данных в формате URI
# (DSN). Запись должна иметь следующий вид:
#
# mysql://пользователь:пароль@хост:порт/имя_базы_данных
#
# Обратите внимание на разделители между частями URI: ":", "@", "/". Порт
# можно опустить, если используется стандартный 3306.
#
# Ниже дан записи для БД bitrix_db и именем пользователя bitrix_usr и
# паролем boRpBnhGn7ue.
#
# mysql://bitrix_usr:boRpBnhGn7ue@localhost/bitrix_db
#
# Если пароль пользователя базы данных содержит специальные символы, то такой
# пароль обязательно надо закодировать в percent code. Это можно сделать
# следующей командой в терминале:
#
# echo 'ваш_пароль' | perl -MURI::Escape -wlne 'print uri_escape $_'
#
# или через онлайн-сервис https://meyerweb.com/eric/tools/dencoder/
#
# Ниже введите URI с реквизитами баз данных так как это показано. Обратите
# внимание, что знак равно не должен юыть отделён пробелами. Формат записи
# строго такой как есть.
databases=(
mysql://пользователь:пароль@хост/базаанных
mysql://пользователь:пароль@хост/другая_базаанных
)
```
# Запуск резервного копирования
Запустить резервное копирование прямо сейчас можно командой:
```
/usr/bin/local/restic_backup
```
Выполнение бэкапа заблокирует ввод в темнал. Желательно запускать команду в
сессии tmux или screen. Пример запуска сессии tmux:
```
tmux new -s restic_backup
```
Весь вывод скрипта отправляется в отладочный лог **/var/log/restic_backup.log**.
Отслеживать проесс можно командой:
```
tail -f /var/log/restic_backup.log
```
# Восстановление из резервной копии
Для удобства работы перед вызовом команд **restic** следует импортировать файл
с настройками.
Это делается командой:
```
source /etc/restic_backup/settings.sh
```
После чего можно выполнять команды restic без надобности вручную указывать адрес
репозитория и пароль.
## Восстановление файлов
Воспользуйтесь инструкцией из официальной документации утилиты: https://restic.readthedocs.io/en/stable/050_restore.html
Сперва вам потребуется узнать ID снапшота (так в restic называются бэкапы). Для
этого запустите команду:
```
restic snapshots
```
В терминал будет напечатана таблица со списком всех имеющихся снапшотов.
Вы можете восстанавливать снапшоты целиком командой вида:
```
restic restore 79766175 --target /tmp/restore-work
```
где 79766175 это ID снапшота, а /tmp/restore-work директория, куда будут
восстанавливаться файлы.
Другой вариант восстановления — через монтирование репозитория в качестве сетевого
диска. Документация: https://restic.readthedocs.io/en/stable/050_restore.html#restore-using-mount
С примонтированного диска вы можете скопировать как все файлы, так и отдельные.
Команды для монтирования:
```
mkdir -p /mnt/restic
restic mount /mnt/restic
```
restic при помощи FUSE создаст виртуальную файловую систему. При этом терминал
будет заблокирован. Можно запустить команду на монтирвоание в сессии tmux или
screen и работать с примонтирвоанным диском в другом окне. Либо открыть новую
сессию SSH и работать в ней. После можно прервать restic сочетанием клавиш
`Ctrl+C` или выполнить команду:
```
fusermount -u /mnt/restic
```
чтобы отмонтировать бэкапы.
## Восстановление баз данных
Восстановление баз данных почти ничем не отличается от восстановления обычных
файлов.
1. Восстановите дамп в формате **.sql.gz** из репозитория restic
2. Запустите команду для импорта дампа.
```
zcat имя_дампа.sql.gz | mysql -u'пользователь' имя_базы_данных -p'пароль'
```
# Удаление старых бэкапов
Удаление производится командой `restic forget`. Смотрите подробное описание в
документации https://restic.readthedocs.io/en/stable/060_forget.html

BIN
README.pdf Normal file

Binary file not shown.

115
restic_backup Executable file
View file

@ -0,0 +1,115 @@
#!/usr/bin/env bash
set -o errexit # завершать скрипт при возникновении ошибок
source /etc/restic_backup/settings.sh # импортировать настройки
# Создать временную директорию для SQL-дампов
dbs_save_dir=/tmp/databases_backup
mkdir -p "$dbs_save_dir"
# Включить подробное логирование скрипта
log_file=/var/log/restic_backup.log
echo "# --------- BACKUP STARTED AT $(date -R) --------- #" >> "$log_file"
exec >> "$log_file"
exec 2>&1
export PS4='+$0:$LINENO '
set -o xtrace
# ------------------------------------------------------------------------- #
# Вспомогательные функции #
# ------------------------------------------------------------------------- #
parse_uri() {
# Парсер URI
uri="$1"
schema="$(cut -d ':' -f 1 <<<"$uri")"
user="$(grep -Po '(?<=://)(.*)(?=@)' <<<"$uri" | cut -d ':' -f 1)"
password="$(passw=$(grep -Po '(?<=://)(.*)(?=@)' <<<"$uri"); \
[[ $passw =~ .+:.+ ]] && echo ${passw##*:})" || true
host="$(grep -Po '(?<=@)(.*)' <<<"$uri" |
cut -d '/' -f 1 | cut -d ':' -f 1)"
port="$(prt=$(grep -Po '(?<=@)(.*)' <<<"$uri" |
cut -d '/' -f 1); [[ $prt =~ .+:[0-9]{1,} ]] &&
echo ${prt##*:})" || true
path="$(pth=$(grep -Po '(?<=@)(.*)' <<<"$uri"); [[ $pth =~ :~ ]] &&
cut -d ':' -f 2 <<<"$pth" ||
grep -Po '(?<=(:[~/])|/)(.*)' <<<"$pth" |
xargs -I {} echo /{} | sed 's%//%/%g')" || true
# Декодинг пароля
__urldecode() {
: "${*//+/ }"; echo -e "${_//%/\\x}";
}
password="$(__urldecode "$password")" || true
}
get_name() {
# Фунция печатает на экран имя файла с временной меткой
local name="${1##*/}"
local ext="$2"
local ff="${name_prefix}${name}_%Y%m%d-%H%M"
date +"${ff}${ext}"
}
dump_mysql() {
# Выполнить дамп базы данных
parse_uri "$1"
echo "Выполняется дамп БД '${path##*/}' от ${user}@${host} ..."
dump_name="$dbs_save_dir"/"$(get_name "$path" '.sql.gz')"
[ "$port" ] || port=3306 # Устанавливаем порт MySQL по умолчанию
mysqldump \
--no-tablespaces \
--host="$host" \
--port="$port" \
"${path##*/}" \
--user="$user" \
--password="$password" \
| gzip -9c > "$dump_name"
if [ -s "$dump_name" ]; then
echo "Дамп сохранён как: $dump_name"
else
rm "$dump_name"
echo "Что-то пошло не так. Размер дампа 0 байт. Файл $dump_name удалён" >&2
exit 1
fi
}
# ------------------------------------------------------------------------- #
# Запуск резерввного копирования #
# ------------------------------------------------------------------------- #
# Если репозиторий для бэкапов не инициализирован, инициализировать
# https://restic.readthedocs.io/en/stable/075_scripting.html
echo "Проверка репозитория ..."
if ! restic snapshots; then
restic init
fi
# Удаление старых бэкапов
echo "Удаление старых бэкапов ..."
restic forget --keep-last "$RESTIC_KEEP"
restic prune
# Дамп баз данных
# Запускается если массив databases непустой
if [ -n "$databases" ]; then
echo "Запуск дампа баз данных ..."
for database in "${databases[@]}"; do
dump_mysql "$database"
done
fi
# Выполнить резервное копирование в репозиторий
echo "Резервное копирование запущено ..."
restic backup "${files[@]}" "$dbs_save_dir"
# Осторожно удаляем временную директорию для SQL-дампов
echo "Удаление директории с временными файлами"
if [[ "$dbs_save_dir" == "/" ]]; then
echo "ПОПЫТКА УДАЛИТЬ КОРНЕВУЮ ФС!" >&2; exit 1
fi
rm -rf "${dbs_save_dir:-/nonexistent}"
echo -e "\e[32mРезервное копирование завершено\e[0m"

11
restic_backup.service Normal file
View file

@ -0,0 +1,11 @@
[Unit]
Description=Service for run restic backup
After=network.target
[Service]
ExecStart=/usr/bin/local/restic_backup
SyslogIdentifier=restic_backup
Restart=no
TimeoutStopSec=30
KillMode=process
Type=oneshot

10
restic_backup.timer Normal file
View file

@ -0,0 +1,10 @@
[Unit]
Description=Dayly restic backup timer
[Timer]
Unit=restic_backup.service
OnCalendar=*-*-* 1:00:00
Persistent=true
[Install]
WantedBy=timers.target

68
settings.sh Normal file
View file

@ -0,0 +1,68 @@
# ------------------------------------------------------------------------- #
# ВНИМАНИЕ! Этот файл должен содержать валидный код на Bash #
# ------------------------------------------------------------------------- #
# --------------------------- НАСТРОЙКИ RESTIC ---------------------------- #
# Ключ доступа к бакету S3. Совпадает с логином аккаунта
export AWS_ACCESS_KEY_ID=<MY_ACCESS_KEY>
# Секретный ключ доступа к бакету. Можно скопировать из ПУА.
export AWS_SECRET_ACCESS_KEY=<MY_SECRET_ACCESS_KEY>
# Адрес репозитория, куда будут сохраняться резервные копии
export RESTIC_REPOSITORY="s3:https://s3.timeweb.com/restic-demo"
# Пароль для доупа к бэкапам. Все бэкапы шифруются. В случае утраты этого
# пароля получить доступ к резрвным копиям будет невозможно!
export RESTIC_PASSWORD="tiwfBM4i3jDvMA6ah1AIs2HdoJ9w66OS"
# Количество бэкпов, которые нужно хранить в репозитории
RESTIC_KEEP=2
# ------------------ СПИСОК ФАЙЛОВ И ДИРЕКТОРИЙ ДЛЯ БЭКАПА ---------------- #
# Пути к файлам и директориям обязательно должны быть абсолютными.
# Абсолютный путь содержит полный путь до файла/директории начиная от
# корневого каталога системы.
#
# Переменная `files` ниже содержит перечень путей. Обратите внимание, что
# пути, которые содержат пробелы должны быть обязательно включены в кавычки.
# Пример: '/home/user/some dir'
files=(
/etc
/home
)
# ---------------------- СПИСОК БАЗ ДАННЫХ ДЛЯ БЭКАПА --------------------- #
# Ниже необходимо указать реквизиты подключения к базам данных в формате URI
# (DSN). Запись должна иметь следующий вид:
#
# mysql://пользователь:пароль@хост:порт/имя_базы_данных
#
# Обратите внимание на разделители между частями URI: ":", "@", "/". Порт
# можно опустить, если используется стандартный 3306.
#
# Ниже дан записи для БД bitrix_db и именем пользователя bitrix_usr и
# паролем boRpBnhGn7ue.
#
# mysql://bitrix_usr:boRpBnhGn7ue@localhost/bitrix_db
#
# Если пароль пользователя базы данных содержит специальные символы, то такой
# пароль обязательно надо закодировать в percent code. Это можно сделать
# следующей командой в терминале:
#
# echo 'ваш_пароль' | perl -MURI::Escape -wlne 'print uri_escape $_'
#
# или через онлайн-сервис https://meyerweb.com/eric/tools/dencoder/
#
# Ниже введите URI с реквизитами баз данных так как это показано. Обратите
# внимание, что знак равно не должен юыть отделён пробелами. Формат записи
# строго такой как есть.
databases=(
mysql://пользователь:пароль@хост/базаанных
mysql://пользователь:пароль@хост/другая_базаанных
)