Новости:

SMF - Just Installed!

Главное меню

Последние сообщения

#1
CloudLife Monitoring — работа с Git

Проект разделён на две части.

1. Репозиторий разработки
2. Репозиторий релиза

=========================================
РАЗРАБОТКА
=========================================

Рабочий каталог:

Код: bash
~/cloudlife-monitoring

Именно здесь изменяются:

Код: bash
assets/
compose/
docker/
install/
docs/
build/

После окончания изменений собирается релиз.

=========================================
СБОРКА РЕЛИЗА
=========================================

Запустить:

Код: bash
./build-release.sh

Будет создан каталог

Код: bash
dist/cloudlife-monitoring-1.0.0

и архив

Код: bash
dist/cloudlife-monitoring-1.0.0.tar.gz

=========================================
РЕЛИЗНЫЙ РЕПОЗИТОРИЙ
=========================================

Рабочий каталог:

Код: bash
~/cloudlife-monitoring/dist/cloudlife-monitoring-1.0.0

Посмотреть изменения:

Код: bash
git status

Добавить всё:

Код: bash
git add .

Коммит:

Код: bash
git commit -m "CloudLife Monitoring v1.0.0"

Отправить:

Код: bash
git push

=========================================
НОВАЯ ВЕРСИЯ
=========================================

Изменяем только проект разработки.

После изменений:

Код: bash
./build-release.sh

Получаем новый релиз.

Переходим:

Код: bash
cd dist/cloudlife-monitoring-1.0.1

Коммитим:

Код: bash
git add .
git commit -m "CloudLife Monitoring v1.0.1"
git tag v1.0.1
git push
git push --tags

=========================================
ПОЛУЧИТЬ ПОСЛЕДНИЕ ИЗМЕНЕНИЯ
=========================================

На другой машине:

Код: bash
git pull

или

Код: bash
git clone ssh://git@git.cloud-life.site:2222/cloud-life/monitoring.git

=========================================
ПРОВЕРКА
=========================================

Что изменилось:

Код: bash
git diff

История:

Код: bash
git log --oneline --decorate --graph

Последние коммиты:

Код: bash
git log -10

=========================================
ОТМЕНА
=========================================

Отменить изменения файла:

Код: bash
git restore имя_файла

Отменить всё:

Код: bash
git restore .

=========================================
ВАЖНО
=========================================

Никогда не редактировать релизный репозиторий вручную.

Правильный путь:

Разработка
        ↓
build-release.sh
        ↓
Релиз
        ↓
git commit
        ↓
git push
#2
📘 ДОПОЛНЕНИЕ: установка NVIDIA через .run (fallback вариант)
⚠️ Когда использовать .run вместо apt

Использовать NVIDIA .run installer имеет смысл только если:

apt install nvidia-driver-470 тянет неподходящую версию
или ломает систему зависимостями (DKMS / kernel mismatch)
или нужен строгий контроль версии драйвера
или система уже "разъехалась" после неудачных установок
📥 1. Скачивание драйвера

Пример:

NVIDIA 470.xx legacy driver

Файл:

Код: bash
NVIDIA-Linux-x86_64-470.xxx.run

🧹 2. Подготовка системы перед установкой
Удалить конфликтующие драйверы:

Код: bash
sudo apt purge 'nvidia*'
sudo rm -rf /var/lib/dkms/nvidia
sudo update-initramfs -u

(Рекомендуется) отключить nouveau:

Код: bash
sudo nano /etc/modprobe.d/blacklist-nouveau.conf

Добавить:

Код: bash
blacklist nouveau
options nouveau modeset=0

Обновить initramfs:

Код: bash
sudo update-initramfs -u

🔁 3. Перезагрузка в чистую систему

Код: bash
sudo reboot

🧭 4. Переход в TTY

Код: bash
Ctrl + Alt + F3

Остановить GUI:

Код: bash
sudo systemctl stop gdm

или lightdm / sddm


🚀 5. Установка .run драйвера

Код: bash
chmod +x NVIDIA-Linux-.run
sudo ./NVIDIA-Linux-.run

⚠️ 6. КРИТИЧЕСКИЕ ВОПРОСЫ В УСТАНОВЩИКЕ
DKMS:

Код: bash
Register the kernel module with DKMS?

👉 ❌ NO

Secure Boot:

Код: bash
Would you like to sign the module?

👉 ❌ NO (Install without signing)
(если Secure Boot отключён в BIOS)

32-bit libs:

👉 ✔ YES (если нужны игры / Steam)

🧠 Почему DKMS нужно отключать

На старых GPU (Kepler, например GT 740M):

NVIDIA GeForce GT 740M

DKMS часто ломается на обновлениях ядра
требует headers и совпадения версий
может зависать на "building module"
конфликтует с остатками старых установок

👉 поэтому фиксированное ядро + manual install стабильнее

🔧 7. После установки

Код: bash
sudo reboot

🧪 8. Проверка

Код: bash
nvidia-smi

🎮 9. PRIME режим (обязательно после .run)

Код: bash
sudo apt install nvidia-prime
sudo prime-select on-demand
sudo reboot

⚡ 10. Проверка offload

Код: bash
__NV_PRIME_RENDER_OFFLOAD=1 __GLX_VENDOR_LIBRARY_NAME=nvidia glxinfo | grep OpenGL

🧱 11. Важное правило
НЕ смешивать:

❌ apt nvidia-driver + .run одновременно
❌ DKMS + manual install без понимания
❌ nouveau + nvidia вместе

🏁 Итог

.run — это:

✔ ручной контроль
✔ фиксированная версия драйвера
✔ спасение при сломанном apt
❌ но требует аккуратной очистки системы
#3
🧭 Общая идея
Intel HD 4400 → основной рендер (энергосбережение)
NVIDIA GT 740M → только по запросу (PRIME offload)
драйвер → NVIDIA 470 (legacy)
режим → prime-select on-demand
🧰 1. Подготовка системы

Установить Ubuntu 22.04 / 24.04 LTS (рекомендуется 22.04 для старых GPU)

Обновить систему:

Код: bash
sudo apt update && sudo apt upgrade -y

🔒 2. Отключение Secure Boot (рекомендуется)

Проверка:

Код: bash
mokutil --sb-state

Если включён:

👉 BIOS → Secure Boot → Disabled

🧠 3. Установка драйвера NVIDIA 470

Для GPU:

NVIDIA GeForce GT 740M

Установка:

Код: bash
sudo apt install nvidia-driver-470

⚠️ Важно при установке .run (если используется)
DKMS → ❌ NO
Secure Boot signing → ❌ NO (если SB отключён)
🔧 4. Очистка сломанного DKMS (если была проблема)

Код: bash
sudo apt purge 'nvidia*'
sudo rm -rf /var/lib/dkms/nvidia
sudo update-initramfs -u

⚙️ 5. Установка PRIME (обязательно)

Код: bash
sudo apt install nvidia-prime

🔄 6. Переключение в правильный режим
✔ Рекомендуемый режим (правильный)

Код: bash
sudo prime-select on-demand
sudo reboot

❌ НЕ рекомендуется:

Код: bash
sudo prime-select nvidia

(это держит GPU всегда активным → нагрев + расход)

🧪 7. Проверка режима

Код: bash
prime-select query

Ожидаемо:

Код: bash
on-demand

🎮 8. Запуск приложений через NVIDIA
Вариант 1:

Код: bash
prime-run glxgears

Вариант 2 (ручной):

Код: bash
__NV_PRIME_RENDER_OFFLOAD=1 __GLX_VENDOR_LIBRARY_NAME=nvidia <command>

Пример:

Код: bash
__NV_PRIME_RENDER_OFFLOAD=1 __GLX_VENDOR_LIBRARY_NAME=nvidia steam

🧪 9. Проверка OpenGL
Intel (нормальный idle режим):

Код: bash
glxinfo | grep OpenGL

NVIDIA (offload тест):

Код: bash
__NV_PRIME_RENDER_OFFLOAD=1 __GLX_VENDOR_LIBRARY_NAME=nvidia glxinfo | grep OpenGL

🧠 10. Проверка NVIDIA

Код: bash
nvidia-smi

⚡ 11. Результат правильной конфигурации

✔ Intel рендерит рабочий стол
✔ NVIDIA включается только по требованию
✔ нет перегрева
✔ работает Horizon / игры / Steam

🧩 12. Частые проблемы
❌ prime-run not found

Код: bash
sudo apt install nvidia-prime

❌ NVIDIA не включается

Проверить:

Код: bash
prime-select query

Должно быть:

Код: bash
on-demand

❌ OpenGL всегда Intel

Это нормально — NVIDIA включается только через offload.

🧱 13. Архитектура системы
Intel HD 4400 → display server
NVIDIA GT 740M → compute / gaming
PRIME → переключение
🏁 Итог

Эта конфигурация:

✔ стабильная
✔ энергоэффективная
✔ не ломает систему при обновлениях
✔ подходит для старых Kepler GPU
#4
Настройка WireGuard клиента на Ubuntu Server как шлюза (NAT) с сохранением локального доступа

Часто при поднятии WireGuard на удаленном сервере Ubuntu с конфигом
Код: bash
AllowedIPs = 0.0.0.0/0
сервер намертво забивает на локальную сеть и отправляет весь трафик (включая ответы на ваш SSH) внутрь туннеля. Как результат — моментальная потеря связи с сервером.

Ниже пошаговая инструкция, как настроить клиента, завернуть весь внешний трафик в VPN, сохранить доступ к локальным подсетям за вашим основным роутером (например, MikroTik) и превратить саму Ubuntu в шлюз (NAT) для других устройств.

Исходные данные для примера:
* Интерфейс Ubuntu: ens192
* IP-адрес Ubuntu: 192.168.11.50
* Шлюз (локальный роутер): 192.168.11.1
* Локальные подсети, которые НЕ надо пускать в VPN: 192.168.10.0/24, 192.168.20.0/24, 172.16.0.0/24



Шаг 1. Установка WireGuard и resolvconf

Поскольку в клиентских конфигах часто прописана строка
Код: bash
DNS = ...
, WireGuard при старте будет искать утилиту для управления резолвом. Установим сам пакет и нужный обработчик:

Код: bash
sudo apt update && sudo apt install wireguard resolvconf -y

Шаг 2. Включение маршрутизации (IP Forwarding) в ядре

Чтобы Ubuntu могла работать как роутер и пересылать пакеты из локальной сети в VPN, нужно разрешить форвардинг.

Включаем прямо сейчас:
Код: bash
sudo sysctl -w net.ipv4.ip_forward=1

Чтобы настройка сохранялась после перезагрузки, открываем файл конфигурации:
Код: bash
sudo nano /etc/sysctl.conf
Находим и раскомментируем (или добавляем в конец файла) строчку:
Код: bash
net.ipv4.ip_forward=1

Шаг 3. Настройка конфигурационного файла WireGuard

Создаем или редактируем файл /etc/wireguard/wg0.conf:
Код: bash
sudo nano /etc/wireguard/wg0.conf

Вставляем ваш конфиг, используя символ обратного слэша
Код: bash
\
для переноса длинных строк. Это позволит сохранить читаемость файла без поломки скрипта:

Код: bash
[Interface]
PrivateKey = <ВАШ_ПРИВАТНЫЙ_КЛЮЧ>
Address = 10.0.0.2/32, fd00::2/128
DNS = 1.1.1.1

# --- Скрипты включения интерфейса (PostUp) ---
# 1. Исправляем асимметричную маршрутизацию (SSH не отвалится)
PostUp = ip rule add from 192.168.11.50 table 200; \
         ip route add default via 192.168.11.1 dev ens192 table 200;
# 2. Добавляем маршруты до локальных подсетей в обход VPN
PostUp = ip route add 192.168.10.0/24 via 192.168.11.1 dev ens192; \
         ip route add 192.168.20.0/24 via 192.168.11.1 dev ens192; \
         ip route add 172.16.0.0/24 via 192.168.11.1 dev ens192;
# 3. Настраиваем правила пересылки и NAT (Маскарадинг)
PostUp = iptables -A FORWARD -i ens192 -o wg0 -j ACCEPT; \
         iptables -A FORWARD -i wg0 -o ens192 -m state --state RELATED,ESTABLISHED -j ACCEPT; \
         iptables -t nat -A POSTROUTING -o wg0 -j MASQUERADE

# --- Скрипты выключения интерфейса (PostDown) ---
# 1. Удаляем правила асимметричной маршрутизации
PostDown = ip rule del from 192.168.11.50 table 200; \
           ip route del default via 192.168.11.1 dev ens192 table 200;
# 2. Удаляем локальные маршруты
PostDown = ip route del 192.168.10.0/24 via 192.168.11.1 dev ens192; \
           ip route del 192.168.20.0/24 via 192.168.11.1 dev ens192; \
           ip route del 172.16.0.0/24 via 192.168.11.1 dev ens192;
# 3. Удаляем правила пересылки и NAT
PostDown = iptables -D FORWARD -i ens192 -o wg0 -j ACCEPT; \
           iptables -D FORWARD -i wg0 -o ens192 -m state --state RELATED,ESTABLISHED -j ACCEPT; \
           iptables -t nat -D POSTROUTING -o wg0 -j MASQUERADE

[Peer]
PublicKey = <КЛЮЧ_СЕРВЕРА>
Endpoint = 198.51.100.5:51820
AllowedIPs = 0.0.0.0/0, ::/0

Шаг 4. Запуск и автозапуск

Запускаем туннель:
Код: bash
sudo wg-quick up wg0
Проверяем, что связь по SSH не прервалась. Если всё ок, добавляем интерфейс в автозапуск при старте системы:
Код: bash
sudo systemctl enable wg-quick@wg0

Для проверки статуса подключения можно использовать команду:
Код: bash
sudo wg

Финал: Теперь достаточно на вашем основном роутере (например, в IP -> Routes на MikroTik) завернуть трафик нужных локальных клиентов, указав шлюзом IP-адрес нашей Ubuntu (192.168.11.50), и они пойдут в интернет через WireGuard.
#5
Настройки *nix (почта, web, система etc) / Doom II web
Последний ответ от George - Май 29, 2026, 12:58
Инструкция по сборке чистой веб-версии DOOM 2 (WebAssembly) с Fullscreen

Эта пошаговая инструкция позволит вам собрать оригинальный DOOM 2 для работы на любом веб-сервере в браузере. Игра компилируется на базе движка Doomgeneric через Emscripten (WASM), не зависает в браузере и поддерживает разворачивание на весь экран по кнопке.



Шаг 1. Подготовка окружения и скачивание исходников
Создаем чистую изолированную папку и клонируем официальный репозиторий:
Код: bash
rm -rf ~/doom-fresh
mkdir ~/doom-fresh && cd ~/doom-fresh
git clone https://github.com/ozkl/doomgeneric.git
cd doomgeneric/doomgeneric

Шаг 2. Добавление файла игры (WAD)
Скопируйте ваш файл игровых ресурсов (например, от Doom 2) в текущую папку. Важно: имя файла внутри WebAssembly должно быть строго маленькими буквами!
Код: bash
cp /путь/к/вашему/файлу/doom2.wad ./doom2.wad

Шаг 3. Создание HTML-шаблона (shell.html)
Браузеры блокируют автоматический переход во весь экран, поэтому нам нужна кнопка, привязанная к действию пользователя. Создаем файл-шаблон:
Код: bash
nano shell.html
Вставьте в него следующий код:
Код: bash
<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="utf-8">
    <title>Чистый DOOM на WebAssembly</title>
    <style>
        body { background: #111; color: #fff; text-align: center; font-family: sans-serif; margin: 0; padding: 20px; }
        #canvas { width: 640px; height: 400px; border: 3px solid #444; background: #000; display: block; margin: 20px auto; image-rendering: pixelated; }
        .btn { padding: 12px 25px; font-size: 18px; font-weight: bold; background: #e50914; color: white; border: none; cursor: pointer; border-radius: 5px; }
        .btn:hover { background: #b80710; }
    </style>
</head>
<body>
    <h2>DOOM 2 (Чистая WebAssembly сборка)</h2>
    <button class="btn" id="fs-btn">ИГРАТЬ НА ВЕСЬ ЭКРАН</button>
    <canvas id="canvas" oncontextmenu="event.preventDefault()"></canvas>

    <script type='text/javascript'>
        var Module = {
            canvas: (function() { return document.getElementById('canvas'); })(),
            print: function(text) { console.log(text); },
            printErr: function(text) { console.error(text); },
            arguments: ['-iwad', 'doom2.wad']
        };

        // Запрос полноэкранного режима по клику (требование безопасности браузера)
        document.getElementById('fs-btn').addEventListener('click', () => {
            const canvas = document.getElementById('canvas');
            if (canvas.requestFullscreen) { canvas.requestFullscreen(); }
            else if (canvas.webkitRequestFullscreen) { canvas.webkitRequestFullscreen(); } 
        });
    </script>
    {{{ SCRIPT }}}
</body>
</html>
Сохраните и закройте ([kbd]Ctrl+O[/kbd], [kbd]Enter[/kbd], [kbd]Ctrl+X[/kbd]).

Шаг 4. Настройка Makefile под WebAssembly
Открываем родной файл сборки для Emscripten, чтобы заставить его использовать наш HTML-шаблон и вшить WAD-файл игры:
Код: bash
nano Makefile.emscripten
Найдите строчку, начинающуюся с LDFLAGS=, и замените её целиком на следующую:
Код: bash
LDFLAGS=-s USE_SDL=2 -s ALLOW_MEMORY_GROWTH=1 -s ASYNCIFY=1 --shell-file shell.html --preload-file doom2.wad@/doom2.wad
Сохраните и закройте файл.

Шаг 5. Компиляция проекта
Активируем компилятор Emscripten (укажите ваш путь к emsdk) и запускаем автоматическую сборку через Makefile:
Код: bash
source ~/doom-web/emsdk/emsdk_env.sh
emmake make -f Makefile.emscripten

Шаг 6. Запуск веб-сервера
После успешного завершения сборки запускаем локальный сервер прямо из этой папки:
Код: bash
python3 -m http.server 8080


Результат:
Откройте браузер по адресу http://ВАШ_IP_СЕРВЕРА:8080/doomgeneric.html. Нажмите на красную кнопку «ИГРАТЬ НА ВЕСЬ ЭКРАН», и игра развернется на полный монитор без зависаний браузера!
#6
Автообновление Docker-ноды с Galera (без потери кворума)

Описание
Скрипт выполняет:
  • безопасный вывод ноды из Galera
  • drain Docker Swarm
  • apt upgrade
  • reboot при необходимости
  • возврат в Galera и Docker

---

⚠️ ВАЖНО
  • минимум 3 ноды в Galera
  • wsrep_cluster_size > 1 перед началом

---

1. Скрипт

Код: bash
nano /usr/local/sbin/docker-galera-upgrade.sh

Код: bash
#!/bin/bash

set -euo pipefail
export DEBIAN_FRONTEND=noninteractive

LOG="/var/log/docker-galera-upgrade.log"
STATE_FILE="/var/run/docker-galera-upgrade.state"

NODE=$(hostname)

log() {
echo "[$(date '+%F %T')] $*" | tee -a "$LOG"
}

log "===== START NODE UPGRADE (DOCKER + GALERA) ====="

# --- функции Galera ---

galera_size() {
mysql -Nse "SHOW STATUS LIKE 'wsrep_cluster_size'" | awk '{print $2}'
}

galera_ready() {
mysql -Nse "SHOW STATUS LIKE 'wsrep_ready'" | grep -q ON
}

# --- swarm check ---

if docker info 2>/dev/null | grep -q "Swarm: active"; then
SWARM=1
else
SWARM=0
fi

# --- POST REBOOT ---

if [ -f "$STATE_FILE" ]; then
log "Post-reboot stage"

```
# ждём Galera
until galera_ready; do
    log "Waiting Galera..."
    sleep 5
done

# возвращаем Docker
if [ "$SWARM" -eq 1 ]; then
    docker node update --availability active "$NODE" || true
fi

rm -f "$STATE_FILE"
log "Node restored"
exit 0
```

fi

# --- PRECHECK Galera ---

SIZE=$(galera_size)

if [ "$SIZE" -le 1 ]; then
log "Cluster size too small → abort"
exit 1
fi

log "Galera size OK: $SIZE"

# --- drain docker ---

if [ "$SWARM" -eq 1 ]; then
docker node update --availability drain "$NODE"
fi

# --- graceful Galera leave ---

log "Stopping MariaDB (leave cluster)"
systemctl stop mariadb

touch "$STATE_FILE"

# --- upgrade ---

apt update >> "$LOG" 2>&1
apt upgrade -y >> "$LOG" 2>&1

# --- reboot ---

if [ -f /var/run/reboot-required ]; then
reboot
fi

# --- start MariaDB ---

systemctl start mariadb

# ждём Galera

until galera_ready; do
sleep 5
done

# --- restore docker ---

if [ "$SWARM" -eq 1 ]; then
docker node update --availability active "$NODE"
fi

rm -f "$STATE_FILE"

log "===== DONE ====="

---

2. systemd service

Код: bash
nano /etc/systemd/system/docker-galera-upgrade.service

Код: bash
[Unit]
Description=Docker + Galera node upgrade
After=network-online.target docker.service mariadb.service
Requires=docker.service mariadb.service

[Service]
Type=oneshot
ExecStart=/usr/local/sbin/docker-galera-upgrade.sh

[Install]
WantedBy=multi-user.target

---

3. systemd timer

Код: bash
nano /etc/systemd/system/docker-galera-upgrade.timer

Код: bash
[Unit]
Description=Node upgrade timer

[Timer]
OnCalendar=*-*-* 04:00:00
Persistent=true

[Install]
WantedBy=timers.target

---

4. Активация

Код: bash
systemctl daemon-reload
systemctl enable --now docker-galera-upgrade.timer
systemctl enable docker-galera-upgrade.service

---

Как это работает
  • проверяется размер Galera
  • нода выводится из Docker
  • MariaDB корректно останавливается
  • после reboot ждём wsrep_ready
  • нода возвращается в кластер

---

⚠️ Рекомендации
  • использовать IST (одинаковая версия MariaDB)
  • проверить wsrep_sst_method (xtrabackup-v2)
  • не обновлять все ноды одновременно

---

Проверка

Код: bash
mysql -e "SHOW STATUS LIKE 'wsrep_cluster_size';"
docker node ls

---
#7
Мониторинг (минимальный, но рабочий)

1. Лог-файл

Docker:
Код: bash
tail -f /var/log/docker-apt-upgrade.log

HAProxy:
Код: bash
tail -f /var/log/haproxy-upgrade.log

---

2. Проверка через systemd

Код: bash
systemctl status docker-apt-upgrade.service
systemctl status haproxy-upgrade.service

---

3. Уведомление в Telegram (простой вариант)

Добавить в конец скрипта:

Код: bash
curl -s -X POST https://api.telegram.org/botTOKEN/sendMessage 
-d chat_id=CHAT_ID 
-d text="Upgrade done on $(hostname)"

---

4. Проверка кластера после обновления

Docker:
Код: bash
docker node ls
docker service ls

---

5. Health endpoint (если нет)

Код: bash
echo OK | nc -l -p 8080

или через nginx.

---

Результат
  • видно статус обновлений
  • есть лог
  • есть уведомления
#8
Rolling обновление HAProxy (keepalived, 2 ноды)

Описание
Обновляет обе ноды по очереди:
  • проверяет health второй ноды
  • обновляет текущую
  • гарантирует, что одна нода всегда жива

---

1. Скрипт

Код: bash
nano /usr/local/sbin/haproxy-ha-upgrade.sh

Код: bash
#!/bin/bash

set -euo pipefail
export DEBIAN_FRONTEND=noninteractive

LOG="/var/log/haproxy-upgrade.log"
LOCK="/var/run/haproxy-upgrade.lock"

VIP="10.10.1.100"
PEER="haproxy-2"   # поменять на вторую ноду

log() {
echo "[$(date '+%F %T')] $*" | tee -a "$LOG"
}

if [ -f "$LOCK" ]; then
exit 0
fi

touch "$LOCK"
trap "rm -f $LOCK" EXIT

# self check

curl -fs http://127.0.0.1/health || exit 1

# peer check

ssh "$PEER" "curl -fs http://127.0.0.1/health" || exit 1

# upgrade

apt update >> "$LOG" 2>&1
apt upgrade -y >> "$LOG" 2>&1

if [ -f /var/run/reboot-required ]; then
reboot
fi

sleep 30

curl -fs http://127.0.0.1/health || exit 1

---

2. systemd service

Код: bash
nano /etc/systemd/system/haproxy-upgrade.service

Код: bash
[Unit]
Description=HAProxy upgrade

[Service]
Type=oneshot
ExecStart=/usr/local/sbin/haproxy-ha-upgrade.sh

---

3. systemd timer

Код: bash
nano /etc/systemd/system/haproxy-upgrade.timer

Код: bash
[Unit]
Description=HAProxy upgrade timer

[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true

[Install]
WantedBy=timers.target

---

4. Активация

Код: bash
systemctl daemon-reload
systemctl enable --now haproxy-upgrade.timer

---

Важно
  • на второй ноде указать обратный PEER
  • сделать разное время запуска (например 03:00 и 03:10)
#9
Автоматическое обновление Docker-ноды (APT + reboot + возврат из maintenance)

Описание
Скрипт выполняет:
  • apt update && apt upgrade -y
  • перевод ноды в drain (если Docker Swarm)
  • перезагрузку при необходимости
  • автоматический возврат ноды в active после reboot

---

1. Создание скрипта

Код: bash
nano /usr/local/sbin/docker-apt-upgrade.sh

Вставить:

Код: bash
#!/bin/bash

set -euo pipefail
export DEBIAN_FRONTEND=noninteractive

LOG="/var/log/docker-apt-upgrade.log"
STATE_FILE="/var/run/docker-upgrade.state"

NODE=$(hostname)

log() {
echo "[$(date '+%F %T')] $*" | tee -a "$LOG"
}

log "===== START DOCKER NODE UPGRADE ====="

if docker info 2>/dev/null | grep -q "Swarm: active"; then
SWARM=1
else
SWARM=0
fi

if [ -f "$STATE_FILE" ]; then
log "Post-reboot detected → restoring node"

```
if [ "$SWARM" -eq 1 ]; then
    docker node update --availability active "$NODE" || true
fi

rm -f "$STATE_FILE"
exit 0
```

fi

if [ "$SWARM" -eq 1 ]; then
docker node update --availability drain "$NODE"
fi

touch "$STATE_FILE"

apt update >> "$LOG" 2>&1
apt upgrade -y >> "$LOG" 2>&1

if [ -f /var/run/reboot-required ]; then
reboot
fi

if [ "$SWARM" -eq 1 ]; then
docker node update --availability active "$NODE"
fi

rm -f "$STATE_FILE"

Код: bash
chmod +x /usr/local/sbin/docker-apt-upgrade.sh

---

2. systemd service

Код: bash
nano /etc/systemd/system/docker-apt-upgrade.service

Код: bash
[Unit]
Description=Docker node upgrade
After=network-online.target docker.service
Requires=docker.service

[Service]
Type=oneshot
ExecStart=/usr/local/sbin/docker-apt-upgrade.sh

[Install]
WantedBy=multi-user.target

---

3. systemd timer

Код: bash
nano /etc/systemd/system/docker-apt-upgrade.timer

Код: bash
[Unit]
Description=Docker node upgrade timer

[Timer]
OnCalendar=*-*-* 03:30:00
Persistent=true

[Install]
WantedBy=timers.target

---

4. Активация

Код: bash
systemctl daemon-reload
systemctl enable --now docker-apt-upgrade.timer
systemctl enable docker-apt-upgrade.service

---

Результат
  • нода уходит в drain
  • обновляется
  • перезагружается
  • сама возвращается в кластер
#10
📌 Core Scripts (HAProxy Dynamic System)

1. MAIN CLI:
Код: bash
/usr/local/bin/haproxy-map-update.sh

---

2. CERT ENGINE:
Код: bash
/usr/local/bin/haproxy-cert.sh

---

⚙️ 1. haproxy-cert.sh

Код: bash
#!/bin/bash
set -euo pipefail

DOMAIN=${1:-}

if [ -z "$DOMAIN" ]; then
    echo "[CERT] no domain!"
    exit 1
fi

echo "[CERT] Issuing certificate for $DOMAIN"

certbot certonly --standalone \
  --http-01-port=8888 \
  -d "$DOMAIN" \
  --non-interactive \
  --agree-tos \
  --email george@cloud-life.site \
  --expand

echo "[CERT] Building PEM"

cat /etc/letsencrypt/live/$DOMAIN/fullchain.pem \
    /etc/letsencrypt/live/$DOMAIN/privkey.pem \
    > /etc/ssl/haproxy/$DOMAIN.pem

echo "[CERT] Done"

---

⚙️ 2. haproxy-map-update.sh

Код: bash
#!/bin/bash

GREEN="\033[0;32m"
NC="\033[0m"
RED="\033[0;31m"

CFG="/etc/haproxy/haproxy.cfg"

HOSTS_MAP="/etc/haproxy/maps/hosts.map"
PASSTH_MAP="/etc/haproxy/maps/passth.map"
ACME_MAP="/etc/haproxy/maps/acme_backends.map"

usage() {
    echo -e "${GREEN}"
    echo "Usage:"
    echo "  $0 set DOMAIN BACKEND MODE IP_LIST PORT [-y]"
    echo "  $0 del DOMAIN"
    echo -e "${NC}"
    exit 1
}

if [ $# -lt 2 ]; then
    usage
fi

ACTION=$1
DOMAIN=$2
BACKEND=${3:-}
MODE=${4:-}
IP_LIST=${5:-}
PORT=${6:-}
AUTO_YES=${7:-}

set -euo pipefail

backend_exists() {
    grep -q "^backend $BACKEND" "$CFG"
}

build_servers() {
    IFS=',' read -ra IPS <<< "$IP_LIST"
    i=1
    SERVERS=""

    for ip in "${IPS[@]}"; do
        name="${BACKEND}${i}"

        if [ "$PORT" == "443" ]; then
            SERVERS+="    server ${name} ${ip}:${PORT} ssl verify none check\n"
        else
            SERVERS+="    server ${name} ${ip}:${PORT} check\n"
        fi

        i=$((i+1))
    done
}

create_backend() {

    echo "[BACKEND] Creating: $BACKEND"

    if [ -z "${IP_LIST:-}" ]; then
        read -p "IP(s): " IP_LIST
    fi

    if [ -z "${PORT:-}" ]; then
        read -p "PORT: " PORT
    fi

    build_servers

    TMP=$(mktemp)

    awk -v block="$(cat <<EOF
backend $BACKEND
    mode http
    balance roundrobin
    option forwardfor header X-Real-IP
    http-request set-header X-Forwarded-For %[src]
    http-request set-header X-Forwarded-Port %[dst_port]

$SERVERS
EOF
)" '
    /# --- AUTO_BACKENDS_END ---/ {
        print block
    }
    { print }
    ' "$CFG" > "$TMP"

    mv "$TMP" "$CFG"
}

issue_cert() {
    /usr/local/bin/haproxy-cert.sh "$DOMAIN"
}

update_map() {
    local MAP=$1
    local KEY=$2
    local VALUE=$3

    TMP=$(mktemp)
    [ -f "$MAP" ] && cp "$MAP" "$TMP"

    grep -v "^$KEY " "$TMP" > "$TMP.new" || true
    echo "$KEY $VALUE" >> "$TMP.new"

    mv "$TMP.new" "$MAP"
    rm -f "$TMP"
}

delete_maps() {
    for m in "$HOSTS_MAP" "$PASSTH_MAP" "$ACME_MAP"; do
        [ -f "$m" ] && grep -v "^$DOMAIN " "$m" > "$m.tmp" && mv "$m.tmp" "$m"
    done
}

engine() {
    for MAP in "$HOSTS_MAP" "$PASSTH_MAP" "$ACME_MAP"; do
        [ -f "$MAP" ] || continue
        awk '{ print length($1), $0 }' "$MAP" | sort -nr | cut -d" " -f2- > "$MAP.tmp"
        mv "$MAP.tmp" "$MAP"
    done
}

echo "[MAP] $DOMAIN"

if [ "$ACTION" == "set" ]; then

    if ! backend_exists; then
        if [[ "$AUTO_YES" == "-y" ]]; then
            create_backend
        else
            read -p "Create backend $BACKEND? [Y/n]: " c
            [[ "$c" =~ ^[Nn]$ ]] && exit 1
            create_backend
        fi
    fi

    if [ "$MODE" == "http" ]; then
        issue_cert
        update_map "$HOSTS_MAP" "$DOMAIN" "$BACKEND"

    elif [ "$MODE" == "passth" ]; then
        update_map "$PASSTH_MAP" "$DOMAIN" "$BACKEND"
        update_map "$ACME_MAP" "$DOMAIN" "acme_${BACKEND}"
    fi

    delete_maps
    engine
    systemctl reload haproxy

elif [ "$ACTION" == "del" ]; then
    delete_maps
    engine
    systemctl reload haproxy
fi

echo -e "${GREEN}[DONE]${NC}"

---

📌 Требуемая структура HAProxy:

Код: bash
# --- AUTO_BACKENDS_END ---

---

🏁 Результат

  • Полностью автоматическое создание backend'ов
  • Multi-node поддержка
  • ACME + TLS split
  • Maps-driven routing
  • UI-ready CLI backend
🡱 🡳

Отметьте интересные вам фрагменты текста и они станут доступны по уникальной ссылке в адресной строке браузера.