<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:yandex="http://news.yandex.ru" xmlns:media="http://search.yahoo.com/mrss/" xmlns:turbo="http://turbo.yandex.ru" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
  <channel>
    <atom:link href="https://devops.org.ru/turbo.xml" rel="self" type="application/rss+xml"/>
    <title>Сергей Антропов</title>
    <link>https://devops.org.ru/</link>
    <description>Записки DevOps</description>
    <lastBuildDate>Fri, 28 Mar 2025 11:36:14 +0300</lastBuildDate>
    <item turbo="true">
      <title>Minio: пользователи и права доступа к бакетам и файлам</title>
      <link>https://devops.org.ru/minio-users-and-permissions</link>
      <description>&lt;p&gt;Небольшая памятка по установке и разграничению прав клиента в Minio S3</description>
      <turbo:content>
								<![CDATA[
									<p>Небольшая памятка по установке и разграничению прав клиента в Minio S3<!-- pagebreak --></p>
<h3>Установка клиента</h3>
<pre class="language-shell"><code># macOS
brew install minio/stable/mc

# Windows
Download https://dl.min.io/client/mc/release/windows-amd64/mc.exe

# Linux
wget https://dl.min.io/client/mc/release/linux-amd64/mc
chmod +x mc</code></pre>
<p></p>
<h3>Авторизация клиента</h3>
<p><strong>ALIAS</strong> - имя подключения<br><strong>S3-ENDPOINT</strong> - URL вашего minio сервера<br><strong>ACCESS-KEY</strong> - ключ доступа<br><strong>SECRET-KEY</strong> - пароль.</p>
<pre class="language-shell"><code>mc alias set &lt;ALIAS&gt; &lt;S3-ENDPOINT&gt; &lt;ACCESS-KEY&gt; &lt;SECRET-KEY&gt;</code></pre>
<p></p>
<h3>Создание пользователя</h3>
<pre class="language-shell"><code>mc admin user add &lt;ALIAS&gt;</code></pre>
<p></p>
<h3>Создание бакета</h3>
<pre class="language-shell"><code>mc mb &lt;ALIAS&gt;/&lt;BUCKET_NAME&gt;</code></pre>
<p></p>
<h3>Добавление пермишшенов</h3>
<p>Права доступа добавляются через концепцию policy. Для примера добавим права администрирования объектов для определенного бакета, а после - выдадим эти права нашему пользователю.</p>
<p>Создаем локальный файл <strong>bucket-policy.json</strong></p>
<pre class="language-shell"><code>{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": [
        "s3:*"
      ],
      "Effect": "Allow",
      "Resource": [
        "arn:aws:s3:::&lt;BACKET_NAME&gt;/*"
      ],
      "Sid": ""
    }
  ]
}</code></pre>
<p>Или еще пример, который дает право на запись определенному пользователю только в его бакет. Но, при этом, он может видеть все остальное:</p>
<pre class="language-json"><code>{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": [
        "s3:PutBucketPolicy",
        "s3:GetBucketPolicy",
        "s3:DeleteBucketPolicy",
        "s3:ListAllMyBuckets",
        "s3:ListBucket"
      ],
      "Effect": "Allow",
      "Resource": [
        "arn:aws:s3:::user2bucket"
      ],
      "Sid": ""
    },
    {
      "Action": [
        "s3:AbortMultipartUpload",
        "s3:DeleteObject",
        "s3:GetObject",
        "s3:ListMultipartUploadParts",
        "s3:PutObject"
      ],
      "Effect": "Allow",
      "Resource": [
        "arn:aws:s3:::user2bucket/*"
      ],
      "Sid": ""
    }
  ]
}</code></pre>
<p></p>
<p>В данном файле мы описываем права доступа для бакета <strong>&lt;BACKET_NAME&gt;</strong>.</p>
<p>После этого нам потребуется выгрузить пермиссии на сервер и сохранить под именем <strong>&lt;POLICY_NAME&gt;</strong>:</p>
<pre class="language-shell"><code>mc admin policy add &lt;ALIAS&gt; &lt;POLICY_NAME&gt; ./bucket-policy.json</code></pre>
<p></p>
<p>Далее нам нужно применить наш policy к ранее созданному пользователю:</p>
<pre class="language-shell"><code>mc admin policy set &lt;ALIAS&gt; &lt;POLICY_NAME&gt; user=&lt;USER_NAME&gt;</code></pre>
<p></p>
								]]>
							</turbo:content>
      <turbo:source>https://devops.org.ru/minio-users-and-permissions</turbo:source>
      <turbo:topic>Minio: пользователи и права доступа к бакетам и файлам</turbo:topic>
      <pubDate>Wed, 19 Jan 2022 10:04:16 +0300</pubDate>
      <guid isPermaLink="false">986a6d0f23d58be4a1aa87e6449e5b73</guid>
    </item>
    <item turbo="true">
      <title>Памятка по командам PostgreSQL (пополняемая)</title>
      <link>https://devops.org.ru/postgresql-command-memo</link>
      <description>&lt;p&gt;Так как я не ДБА, то частенько забываю, как выдавать ралзичные права, создавать пользователей и так далее. Так что тут буду собирать команды SQL для облегчения жизни...&lt;/p&gt;
&lt;p&gt;</description>
      <turbo:content>
								<![CDATA[
									<p>Так как я не ДБА, то частенько забываю, как выдавать ралзичные права, создавать пользователей и так далее. Так что тут буду собирать команды SQL для облегчения жизни...</p>
<p><!-- pagebreak --></p>
<h3>Выдать пользователю RO на базу</h3>
<pre class="language-plsql"><code>-- Создаем группу для RO
CREATE ROLE readonly;

-- Даём доступ к существующим таблицам
GRANT USAGE ON SCHEMA public TO readonly;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly;

-- Даём доступ к будущим таблицам
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO readonly;

-- Создаем пользователя с правами RO
CREATE USER пользователь WITH PASSWORD 'пароль';
GRANT readonly TO пользователь;</code></pre>
<pre class="language-plsql"><code>-- На конкретную базу
GRANT CONNECT ON DATABASE ИМЯ_БД TO пользователь;
GRANT USAGE ON SCHEMA public TO пользователь;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO пользователь;
GRANT SELECT ON ALL SEQUENCES IN SCHEMA public TO пользователь;</code></pre>
<p></p>
<h3>Дампим БД с исключением таблиц и восстанавливаем на другом Хосте</h3>
<p>Сначала склонируем:</p>
<pre class="language-shell"><code>pg_dump \
--clean \
--encoding=utf8 \
--format=directory \
--jobs=5 \
--verbose \
--compress=5 \
--exclude-table=table_num_1_to_exclude \
--exclude-table=table_num_1_to_exclude \
--file=DIR_NAME \
--dbname=DB_NAME \
--host=HOST \
--port=PORT \
--username=USERNAME \
--password</code></pre>
<p></p>
<p>Потом архивируем</p>
<pre class="language-shell"><code>tar -cvf ARCHIVE_NAME.tar.gz DIR_NAME/
rm -rf DIR_NAME/</code></pre>
<p></p>
<p>Потом копируем на удаленный хост и разархивируем</p>
<pre class="language-shell"><code>tar -xvf ARCHIVE_NAME.tar.gz
rm ARCHIVE_NAME.tar.gz</code></pre>
<p></p>
<p>И восстанавливаем</p>
<pre class="language-shell"><code>pg_restore \
--dbname=IMPORT_DAB_NAME \
--format=directory \
--jobs=5 \
--verbose \
--host=HOST \
--port=PORT \
--username=USERNAME \
--password \
DIR_NAME</code></pre>
<p></p>
<h3>Смотрим отставание реплик в кластере PostgreSQL + Patroni</h3>
<pre class="language-plsql"><code>--получить лаг в реплика
select 
    r.client_addr,
    r.usename,
    r.application_name,
    r.state,
    r.sync_state,
    --сколько сгенерировано, но не отправлено
    --если &gt; 0, то говорит о сетевой проблеме =&gt; запусти nicstat
    (pg_wal_lsn_diff(pg_current_wal_lsn(), r.sent_lsn) / 1024)::int as pending,

    --сколько отправлено, но не записано
    --если много, то проблема с диском -&gt; запусти io top
    (pg_wal_lsn_diff(r.sent_lsn,  r.write_lsn) / 1024)::int as write,

    --записано, но не было выпущено (еще  в ОЗУ) не закоммичено
    --если много =&gt; trouble at HDDs =&gt; запусти IO STAT || io top
    (pg_wal_lsn_diff(r.write_lsn, r.flush_lsn) / 1024)::int as flush,

    --данные засинканы, но не воспроизведены репликой
    --если &gt;0 =&gt; диски или цпу =&gt; запусти IO STAT || io top
    (pg_wal_lsn_diff(r.flush_lsn, r.replay_lsn) / 1024)::int as replay,
    --всего
    ((pg_wal_lsn_diff(pg_current_wal_lsn(), r.replay_lsn)) / 1024)::int as total_lag
from pg_catalog.pg_stat_replication as r</code></pre>
								]]>
							</turbo:content>
      <turbo:source>https://devops.org.ru/postgresql-command-memo</turbo:source>
      <turbo:topic>Памятка по командам PostgreSQL (пополняемая)</turbo:topic>
      <pubDate>Sat, 12 Jun 2021 16:22:15 +0300</pubDate>
      <guid isPermaLink="false">a4c36d0023f52d644363a536cd846b09</guid>
    </item>
    <item turbo="true">
      <title>Настраиваем потоковую репликацию в PostgreSQL 10</title>
      <link>https://devops.org.ru/replication-and-restore-in-postgresql-10</link>
      <description>&lt;p&gt;Быстрая памятка для себя. Содержит описание создания реплики и восстановления. &lt;/p&gt;
&lt;p&gt;</description>
      <turbo:content>
								<![CDATA[
									<p>Быстрая памятка для себя. Содержит описание создания реплики и восстановления. </p>
<p><!-- pagebreak --></p>
<h2>1. Подготовка</h2>
<h3>1.1 Сначала ставим 10 PostgreSQL</h3>
<p>Версии на серверах должны быть одинаковые.</p>
<pre class="language-shell"><code>sudo apt-get update
sudo apt-get -y upgrade

wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -

echo "deb http://apt.postgresql.org/pub/repos/apt/ $(lsb_release -cs)-pgdg main" \
| sudo tee /etc/apt/sources.list.d/postgresql-pgdg.list &gt; /dev/null

sudo apt-get update

sudo apt-get install postgresql-10

dpkg -l | grep postgresql</code></pre>
<p></p>
<h3>1.2 Настраиваем брандмауэр</h3>
<p>При использовании брандмауэра, необходимо открыть TCP-порт 5432 — он используется сервером postgre.</p>
<p>а) Firewalld:</p>
<pre class="language-shell" style="font-size: 16px; word-spacing: 0px;"><code>firewall-cmd --permanent --add-port=5432/tcp
firewall-cmd --reload</code></pre>
<p></p>
<p>б) Iptables:</p>
<pre class="language-shell" style="font-size: 16px; word-spacing: 0px;"><code>iptables -A INPUT -p tcp --dport 5432 -j ACCEPT</code></pre>
<p></p>
<p>в) UFW:</p>
<pre class="language-shell" style="font-size: 16px; word-spacing: 0px;"><code>ufw allow 5432/tcp</code></pre>
<p></p>
<h3>1.3 Отключаем SELinux</h3>
<p>Если активирована система безопасности SELinux (по умолчанию в системах Red Hat / CentOS / Fedora), отключаем ее:</p>
<pre class="language-shell" style="font-size: 16px; word-spacing: 0px;"><code>setenforce 0
sed -i 's/^SELINUX=.*/SELINUX=disabled/g' /etc/selinux/config</code></pre>
<p>Если необходимо, чтобы SELinux работал, <a href="https://www.dmosk.ru/miniinstruktions.php?mini=selinux-setting">настраиваем его</a>.</p>
<p style="line-height: 1.6; font-family: 'Segoe UI', sans-serif; font-size: medium; background-color: #ffffff;"></p>
<p style="line-height: 1.6; font-family: 'Segoe UI', sans-serif; font-size: medium; background-color: #ffffff;"></p>
<h2>2. Настройки на Master</h2>
<p>Будем настраивать серверы с IP-адресами <strong>10.14.0.143</strong> (первичный или master) и <strong>10.14.0.85</strong> (вторичный или slave).</p>
<p>Переходим на сервер, с которого будем реплицировать данные (мастер) и выполняем следующие действия.</p>
<p></p>
<h3>2.1 Создаем пользователя в PostgreSQL</h3>
<p>Входим в систему под пользователем postgres:</p>
<pre class="language-shell" style="font-size: 16px; word-spacing: 0px;"><code>sudo su
su postgres</code></pre>
<p></p>
<p>Создаем нового пользователя для репликации:</p>
<pre class="language-shell" style="font-size: 16px; word-spacing: 0px;"><code>createuser --replication -P repluser</code></pre>
<p>* система запросит пароль — его нужно придумать и ввести дважды. В данном примере мы создаем пользователя repluser.</p>
<p>Выходим из оболочки пользователя postgres:</p>
<pre class="language-shell" style="font-size: 16px; word-spacing: 0px;"><code>exit</code></pre>
<h3></h3>
<h3>2.2 Настраиваем postgresql</h3>
<p>Смотрим расположение конфигурационного файла postgresql.conf командой:</p>
<pre class="language-shell" style="font-size: 16px; word-spacing: 0px;"><code>su -u postgres -c "psql -c 'SHOW config_file;'"</code></pre>
<p></p>
<p>В моем случае система вернула строку:</p>
<pre class="language-shell" style="font-size: 16px; word-spacing: 0px;"><code>/etc/postgresql/10/main/postgresql.conf</code></pre>
<p>* конфигурационный файл находится по пути /etc/postgresql/10/main/postgresql.conf.</p>
<p></p>
<p>Открываем конфигурационный файл postgresql.conf.</p>
<pre class="language-shell" style="font-size: 16px; word-spacing: 0px;"><code>nano /etc/postgresql/10/main/postgresql.conf</code></pre>
<p></p>
<p>Редактируем следующие параметры:</p>
<pre class="language-shell" style="font-size: 16px; word-spacing: 0px;"><code>listen_addresses = 'localhost, 10.14.0.143'
wal_level = replica
max_wal_senders = 2
max_replication_slots = 2
hot_standby = on
hot_standby_feedback = on</code></pre>
<div>
<p></p>
<ul>
<li><strong>10.14.0.143</strong> — IP-адрес сервера, на котором он будем слушать запросы Postgre; </li>
<li><strong>wal_level</strong> указывает, сколько информации записывается в WAL (журнал операций, который используется для репликации); </li>
<li><strong>max_wal_senders</strong> — количество планируемых слейвов; </li>
<li><strong>max_replication_slots</strong> — максимальное число слотов репликации (данный параметр не нужен для postgresql 9.2 — с ним сервер не запустится); </li>
<li><strong>hot_standby </strong>— определяет, можно или нет подключаться к postgresql для выполнения запросов в процессе восстановления; </li>
<li><strong>hot_standby_feedback </strong>— определяет, будет или нет сервер slave сообщать мастеру о запросах, которые он выполняет.</li>
</ul>
</div>
<p>Открываем конфигурационный файл <strong>pg_hba.conf</strong> — он находитсяч в том же каталоге, что и файл postgresql.conf:</p>
<pre class="language-shell" style="font-size: 16px; word-spacing: 0px;"><code>nano /etc/postgresql/10/main/pg_hba.conf</code></pre>
<p></p>
<p>Добавляем следующие строки:</p>
<pre class="language-shell" style="font-size: 16px; word-spacing: 0px;"><code>host replication repluser 127.0.0.1/32 md5
host replication repluser 10.14.0.143/32 md5
host replication repluser 10.14.0.85/32 md5</code></pre>
<p>* данной настройкой мы разрешаем подключение к базе данных replication пользователю repluser с локального сервера (localhost и 10.14.0.143) и сервера 10.14.0.85.</p>
<p>Перезапускаем службу postgresql:</p>
<pre class="language-shell" style="font-size: 16px; word-spacing: 0px;"><code>systemctl restart postgresql</code></pre>
<ul>
<li>обратите внимание, что название для сервиса в системах Linux может различаться.<br><br></li>
</ul>
<h2></h2>
<h2>3. Настройки на Slave</h2>
<h3>3.1 Подготавливаемся к репликации</h3>
<p>Смотрим путь до конфигурационного файла postgresql:</p>
<pre class="language-shell" style="font-size: 16px; word-spacing: 0px;"><code>sudo su
su -u postgres -c "psql -c 'SHOW data_directory;'" </code></pre>
<p></p>
<p>В моем случае путь был:</p>
<pre class="language-shell" style="font-size: 16px; word-spacing: 0px;"><code>/var/lib/postgresql/10/main</code></pre>
<p></p>
<p>Также смотрим путь до конфигурационного файла postgresql.conf (нам это понадобиться ниже):</p>
<pre class="language-shell" style="font-size: 16px; word-spacing: 0px;"><code>su -u postgres -c "psql -c 'SHOW config_file;'"</code></pre>
<p></p>
<p>Останавливаем сервис postgresql:</p>
<pre class="language-shell" style="font-size: 16px; word-spacing: 0px;"><code>systemctl stop postgresql</code></pre>
<p></p>
<p>На всякий случай, создаем архив базы:</p>
<pre class="language-shell" style="font-size: 16px; word-spacing: 0px;"><code>tar -czvf /tmp/data_pgsql.tar.gz /var/lib/postgresql/10/main</code></pre>
<p>* в данном примере мы сохраним все содержимое каталога /var/lib/pgsql/9.6/data в виде архива /tmp/data_pgsql.tar.gz.</p>
<p></p>
<p>Удаляем содержимое каталога с данными:</p>
<pre class="language-shell" style="font-size: 16px; word-spacing: 0px;"><code>rm -rf /var/lib/postgresql/10/main/*</code></pre>
<p></p>
<h3>3.2 Реплицируем данные с Master сервера</h3>
<p>а) <strong>Если у нас postgresql 9</strong>:</p>
<pre class="language-shell" style="font-size: 16px; word-spacing: 0px;"><code>su -u postgres -с "pg_basebackup \
-h 10.14.0.143 \
-U repluser \
-D /var/lib/pgsql/9.6/data \
--xlog-method=stream \
--write-recovery-conf"</code></pre>
<ul>
<li>где <strong>10.14.0.143 — </strong>IP-адрес мастера</li>
<li><strong>/var/lib/pgsql/9.6/data</strong> — путь до каталога с данными.</li>
</ul>
<p>б) <strong>Если у нас postgresql 10:</strong></p>
<pre class="language-shell" style="font-size: 16px; word-spacing: 0px;"><code># Входим в screen
screen -S recovery
sudo su

pg_basebackup \
--host=10.14.0.143 \
--username=repluser \
--pgdata=/var/lib/postgresql/10/main \
--progress \
--checkpoint=fast \
--wal-method=stream \
--write-recovery-conf \
&amp;&amp; chown -R postgres:postgres /var/lib/postgresql/10/main \
&amp;&amp; service postgresql start

# Выход из Screen Ctrl+a и d
# Просмотр всех сеансов screen -ls
# Подключится обратно screen -r recovery</code></pre>
<ul>
<li>где <strong>10.14.0.143 — </strong>IP-адрес мастера</li>
<li><strong>/var/lib/postgresql/10/main</strong> — путь до каталога с данными.</li>
</ul>
<p>После ввода команды система запросит пароль для созданной ранее учетной записи <strong>repluser</strong> — вводим его. Начнется процесс клонирования данных.</p>
<p>А еще, желательно, почитать о том, <a href="https://postgrespro.ru/docs/postgresql/10/app-pgbasebackup" target="_blank" rel="noopener">что такое pg_basebackup</a></p>
<p></p>
<h3>3.3 Настраиваем конфиг Slave сервера после клонирования данных</h3>
<p>Ввиду того, что утилита <strong>pg_basebackup</strong> (которой мы пользовались выше) клонирует БД полностью, в том числе и конфиги, то не повторяйте мою ошибку. Только после окончания миграции данных из пункта 2.2 вы открываете конфигурационный файл postgresql.conf на <strong>Slave</strong>:</p>
<pre class="language-shell" style="font-size: 16px; word-spacing: 0px;"><code>nano /etc/postgresql/10/main/postgresql.conf</code></pre>
<p></p>
<p>Если это сделать во время миграции, старый конфиг, который вы изменили, затрется тем, что восстановится с Master и репликации у вас не будет. А будут только данные, сбэкапленные с Мастера. Идем дальше.</p>
<p>И редактируем следующие параметры:</p>
<pre class="language-shell" style="font-size: 16px; word-spacing: 0px;"><code>listen_addresses = 'localhost, 10.14.0.85'</code></pre>
<ul>
<li>где 10.14.0.85 — IP-адрес нашего вторичного сервера (Slave).</li>
</ul>
<p></p>
<p>Снова запускаем сервис postgresql:</p>
<pre class="language-shell" style="font-size: 16px; word-spacing: 0px;"><code>systemctl start postgresql</code></pre>
<h2></h2>
<h2></h2>
<h2>4. Проверка репликации</h2>
<h3>4.1 Посмотреть статус</h3>
<p>Статус работы репликации можно посмотреть следующими командами.</p>
<p>На Master:</p>
<pre class="language-shell" style="font-size: 16px; word-spacing: 0px;"><code>select * from pg_stat_replication;</code></pre>
<p></p>
<p>На Slave:</p>
<pre class="language-shell" style="font-size: 16px; word-spacing: 0px;"><code>select * from pg_stat_wal_receiver;</code></pre>
<p></p>
<h3>4.2 Создать тестовую базу</h3>
<p>На мастере заходим в командную оболочку PostgreSQL:</p>
<pre class="language-shell" style="font-size: 16px; word-spacing: 0px;"><code>su -u postgres -c "psql"</code></pre>
<p></p>
<p>Создаем новую базу данных:</p>
<pre class="language-shell" style="font-size: 16px; word-spacing: 0px;"><code>=# CREATE DATABASE repltest ENCODING='UTF8';</code></pre>
<p></p>
<p>Теперь на Slave сервере смотрим список баз:</p>
<pre class="language-shell" style="font-size: 16px; word-spacing: 0px;"><code>su -u postgres -c "psql"

=# \l</code></pre>
<p></p>
<p>Мы должны увидеть среди баз ту, которую создали на первичном сервере:</p>
<div>
<pre class="language-shell"><code>   Name    |  Owner   | Encoding |   Collate   |    Ctype    |   Access
 ...
 repltest  | postgres | UTF8     | ru_RU.UTF-8 | ru_RU.UTF-8 | 
 ...
</code></pre>
<h3><strong></strong></h3>
<h2><strong></strong></h2>
<h2><strong>5. Восстановление</strong></h2>
<h3>5.1 Превращаем Slave в Master</h3>
<p>Что делать, когда ваш мастер случайно и неожиданно умер? Правильно. Нужно повысить Slave до Master. Потом создать новый сервер и повторить все шаги с начала этой статьи.<strong></strong></p>
<pre class="language-shell"><code>sudo -u postgres /usr/lib/postgresql/10/bin/pg_ctl promote -D /var/lib/postgresql/10/main</code></pre>
<ul>
<li>где <strong>pg_ctl </strong>- утилита работы с сервером</li>
<li><strong>-D /var/lib/postgresql/10/main </strong>-- путь до наших данных в postgresql </li>
</ul>
<p>При этом в каталоге /var/lib/postgresql/10/main файл recovery.conf автоматически будет переименован в recovery.done.</p>
<p>Легко проверить, что в бывшую реплику теперь можно писать. Конечно, если только вы не использовали синхронную репликацию с одной-единственной репликой.</p>
<p>Интересно, что хотя реплику и можно промоутнуть до мастера без перезапуска PostgreSQL, на практике вы, вероятно, все же захотите его перезапустить по следующей причине. Дело в том, что приложение, которое ранее подключилось к этой реплике, так и будет использовать ее в качестве реплики даже после промоута, хотя операции чтения можно было бы размазать по остальным репликам в кластере. Перезапустив PostgreSQL, вы порвете все сетевые соединения, а значит приложению придется подключиться заново, проверить, подключился ли он к мастеру или реплике (запрос SELECT pg_is_in_recovery(); вернет false на мастере и true на репликах), и использовать сетевое соединение соответствующим образом.</p>
<p></p>
<h3>5.2 Переключение на новый Master</h3>
<p>Переключение остальных реплик на новый мастер, а также восстановление бывшего мастера в качестве реплики происходит одинаково.</p>
<p>Чтобы было чуть меньше путаницы с новым мастером, старым мастером, старой репликой и новой репликой, условимся, что сервера мы называем в соответствии с их текущими ролями. То есть, мастером мы называем новый мастер, бывший репликой до фейловера, а репликой — тот, второй сервер.</p>
<p>В простом и не совсем правильном варианте нужно отредактировать, или создать, если его еще нет, файл /var/lib/postgresql/10/main/recovery.conf, указав в нем правильный IP мастера, и сделать sudo service postgresql restart (простой reload не прокатит). Кто-то для того, чтобы не править конфиги и не останавливать СУБД, использует схему с балансировщикам и DNS, но я лично так никогда не делал. В любом случае, этот способ неправильный. Для того, чтобы все хорошо работало во всяких хитрых граничных случаях, реплику следует остановить, сделать pg_rewind, затем запустить реплику.</p>
<p>Утилита pg_rewind находит точку в WAL, начиная с которой WAL мастера и WAL реплики начинают расходиться. Затем она «перематывает» (отсюда и название) WAL реплики на эту точку и накатывает недостающую историю с мастера. Таким образом, реплика и местер всегда приходят к консистентному состоянию. Плюс к этому pg_rewind синхронизирует файлы мастера и реплики намного быстрее, чем pg_basebackup или rsync.</p>
<p>Если вы считаете, что pg_rewind не требуется при использовании синхронной репликации, вот пример маловероятной, но теоретически возможной ситуации. У вас много серверов с PostgreSQL. Сервера в кластере умирают сравнительно часто, поэтому вы решили автоматизировать фейловер. Умирает мастер, запускается фейловер. Среди реплик находится та, что имеет наиболее длинный WAL, на ней делается pg_ctl promote. В этот момент с очень большой задержкой (скажем, 5 секунд — были какие-то сетевые проблемы) на другую реплику прилетает пакет от уже мертвого мастера, и WAL этой реплики становится длиннее WAL нового мастера. Вы не сможете подключить эту реплику к новому мастеру, все сломалось. Если вы хотите, чтобы фейловер работал в том числе и при таких странных граничных случаях, используйте pg_rewind.</p>
<p>Итак, на реплике говорим:</p>
<pre class="language-shell"><code>sudo -u postgres /usr/lib/postgresql/9.5/bin/pg_rewind \
  -D /var/lib/postgresql/9.5/main/ \
  --source-server="host=10.0.3.223 port=5432 user=postgres password=??"</code></pre>
<p></p>
<p>Перемещаем и правим recovery.conf:</p>
<pre class="language-shell"><code>sudo mv /var/lib/postgresql/9.5/main/recovery.done \
  /var/lib/postgresql/9.5/main/recovery.conf
sudo vim /var/lib/postgresql/9.5/main/recovery.conf</code></pre>
<p></p>
<p>Проверяем IP мастера и наличие строчки:</p>
<pre class="language-shell"><code>recovery_target_timeline = 'latest'</code></pre>
<p></p>
<p>Запускаем реплику, смотрим в логи. Там обязательно должно быть:</p>
<pre class="language-shell"><code>LOG:  database system is ready to accept read only connections</code></pre>
<p></p>
<p>Значит PostgreSQL работает в качестве реплики.</p>
<p>Если вдруг видим что-то вроде:</p>
<pre class="language-shell"><code>ERROR: requested WAL segment 0000000200000005 has already been removed</code></pre>
<p></p>
<p>значит реплика слишком отстала от мастера, и нужно перенести файлы с мастера при помощи pg_basebackup, как было описано выше.</p>
<p></p>
<p><strong>PS: Ошибка локали.</strong><br>Если у вас стоит убунта, с EN_US UTF8, а базы созданы в локали RU_UTF8, то сделайте следующее:</p>
<pre class="language-shell" style="font-size: 16px; word-spacing: 0px;"><code>sudo locale-gen ru_RU.UTF-8</code></pre>
<p>И еще немного теории и практики <a href="https://eax.me/postgresql-replication/" target="_blank" rel="noopener">вот тут</a></p>
</div>
								]]>
							</turbo:content>
      <turbo:source>https://devops.org.ru/replication-and-restore-in-postgresql-10</turbo:source>
      <turbo:topic>Настраиваем потоковую репликацию в PostgreSQL 10</turbo:topic>
      <pubDate>Sat, 15 May 2021 17:00:23 +0300</pubDate>
      <guid isPermaLink="false">8cba036e3d6558ab7f737bb798d68acb</guid>
    </item>
    <item turbo="true">
      <title>Создаем пользователя для доступа по ключам. Подготовка среды к Ansible.</title>
      <link>https://devops.org.ru/user-create-with-ssh-keys-access-for-ansible</link>
      <description>&lt;p&gt;Появилась одна интересная задачка. Есть куча машин с разными root паролями. В общем одним Ansible не обойтись. Решил навести порядок и заодно создать везде пользователя с ключами, что бы можно было удобно подключаться и управлять машинами в том числе через Ansible.&lt;/p&gt;
&lt;p&gt;</description>
      <turbo:content>
								<![CDATA[
									<p>Появилась одна интересная задачка. Есть куча машин с разными root паролями. В общем одним Ansible не обойтись. Решил навести порядок и заодно создать везде пользователя с ключами, что бы можно было удобно подключаться и управлять машинами в том числе через Ansible.</p>
<p><!-- pagebreak --></p>
<p>Вот сам скрипт. Тут, думаю, все и так понятно.</p>
<pre class="language-shell"><code>#!/bin/bash

# Создание пользователя для подключения к хостам. Включая доступ по ssh ключам.
# Пароль для пользователя генерится рандомным образом и состоит из 20 символов
# На удаленной машине залогинится под рутом и запустить вот так:
# curl -s https://devops.org.ru/cu.sh | bash /dev/stdin

# Переменные
USERNAME="devops"
PASSWORD=`tr -cd '[:alnum:]' &lt; /dev/urandom | fold -w20 | head -n1`
HOSTNAME=`hostname -I | cut -d' ' -f1`
# Публичный ключ для файла authorized_keys сгенерированный с помощью linux машины управления
LINUXKEY="ssh-rsa key"
# Публичный ключ для файла authorized_keys сгенерированный с помощью putty
PUTTYKEY="ssh-rsa key"
# Проверяем, под рутом ли запущен скрипт.
if [ "$(whoami)" != 'root' ]; then
echo "Для запуска скрипта вы должны войти под пользователем ROOT! Выходим..."
exit 1;
fi

# Проверяем пользователя, есть ли такой в системе.
if id -u $USERNAME &gt;/dev/null 2&gt;&amp;1;
then
    echo "Пользователь $USERNAME уже есть в системе. Выходим"
    exit 1;
fi

# Создаем пользователя
useradd -m -s /bin/bash "$USERNAME"
echo -e "$PASSWORD\n$PASSWORD" | passwd "$USERNAME"

# Прописываем пользователю SUDO без запроса пароля
echo -e "$USERNAME     ALL=(ALL)    NOPASSWD: ALL" &gt; /etc/sudoers.d/$USERNAME

# Создаем директорию .ssh и файл авторизации по ключам
mkdir -p /home/$USERNAME/.ssh
echo -e $LINUXKEY &gt;&gt; /home/$USERNAME/.ssh/authorized_keys
echo -e $PUTTYKEY &gt;&gt; /home/$USERNAME/.ssh/authorized_keys

# Исправляем прова доступа
chown -R $USERNAME:$USERNAME /home/$USERNAME/.ssh/authorized_keys
chmod -R 0600 /home/$USERNAME/.ssh/authorized_keys

echo ""
echo "Пользователь $USERNAME c паролем: $PASSWORD создан на хосте $HOSTNAME"
</code></pre>
								]]>
							</turbo:content>
      <turbo:source>https://devops.org.ru/user-create-with-ssh-keys-access-for-ansible</turbo:source>
      <turbo:topic>Создаем пользователя для доступа по ключам. Подготовка среды к Ansible.</turbo:topic>
      <pubDate>Fri, 23 Apr 2021 20:15:14 +0300</pubDate>
      <guid isPermaLink="false">22b4cf0db37f9e8bf77d55ad86f20c57</guid>
    </item>
    <item turbo="true">
      <title>Увеличиваем размер LVM диска на Proxmox</title>
      <link>https://devops.org.ru/resize-hdd-lvm-on-proxmox</link>
      <description>&lt;p&gt;Оставлю шпаргалку, что бы не забыть. Рутинная операция, но помнить надо.</description>
      <turbo:content>
								<![CDATA[
									<p>Оставлю шпаргалку, что бы не забыть. Рутинная операция, но помнить надо.<!-- pagebreak --></p>
<p></p>
<ol>
<li>Добавляем место для виртуалки, через веб-интерфейс ProxMox (Resize disk), либо через консоль гипервизора:<br>
<pre class="language-shell"><code>qm resize &lt;vmid&gt; &lt;disk&gt; &lt;size&gt;

Пример:
qm resize 100 virtio0 +5G ### добавили 5Гб для vm 100 к диску virtio0</code></pre>
</li>
<li>Заходим на vm</li>
<li><strong>fdisk -l</strong> должен показать полный размер диска и созданные партиции.</li>
<li>Расширяем нужную партицию через удобный вам инструмент, например cfdisk:<br>
<pre class="language-shell"><code>cfdisk /dev/sda</code></pre>
<p>Выбираем партицию -&gt; жмем Resize -&gt; пишем размер диска (по умолчанию все доступное место) -&gt; Write</p>
</li>
<li>Расширяем Physical volume:
<pre class="language-shell"><code>pvresize /dev/sda ### расширяем
pvdisplay ### проверяем что размер изменился</code></pre>
</li>
<li>Расширяем Logical volume:
<pre class="language-shell"><code>vgdisplay ### проверяем что в Volume group есть место "Free PE / Size"
lvextend -r -l +100%FREE /dev/VolumeGroup/lv ### Расширяем Logical volume на все доступное пространство
resize2fs /dev/VolumeGroup/lv ### фиксируем изменения
xfs_growfs /dev/VolumeGroup/lv ### Данную команду используем на CentOS вместо resize2fs</code></pre>
<p><br>Profit...<br><br>Не забываем про </p>
<pre class="language-shell"><code>lsblk
fdisk -l
df -h</code></pre>
</li>
</ol>
<p>Увеличение диска без LVM</p>
<pre class="language-shell"><code>sudo fdisk /dev/sda
p
d
выбрать диск
n
выбрать раздел
enter
enter
удалить сигнатуру y
p - проверяем диск
w
sudo resize2fs /dev/sda1</code></pre>
<p></p>
<p>Расширяем LVM другим диском</p>
<pre class="language-shell"><code>1. Добавляем диск в ВМ
2. fdisk -l - находим добавленный диск (например /dev/sdf)
3. Делаем fdisk /dev/sdf
p
n
enter
enter
enter
enter
p
t
8e
p
w
pvcreate /dev/sdf1
vgs и выбираем имя vgs
vgextend VGS_NAME /dev/sdN
df -h и выбираем LVM который нужно расширить
lvextend -l +100%FREE /dev/mapper/db--vkok--vg-pgdata
resize2fs /dev/mapper/db--vkok--vg-pgdata
Ждем. Проверячем через df -h</code></pre>
								]]>
							</turbo:content>
      <turbo:source>https://devops.org.ru/resize-hdd-lvm-on-proxmox</turbo:source>
      <turbo:topic>Увеличиваем размер LVM диска на Proxmox</turbo:topic>
      <pubDate>Wed, 21 Apr 2021 10:24:46 +0300</pubDate>
      <guid isPermaLink="false">5ada378bf4f86914c38276c78790ccb9</guid>
    </item>
  </channel>
</rss>
