vault backup: 2026-10-09 21:27:26
This commit is contained in:
@@ -1,21 +0,0 @@
|
||||
|
||||
### DNS table:
|
||||
[V2Fly](https://github.com/v2fly/domain-list-community) - список доменных имен для маршрутизации
|
||||
[IP list](https://github.com/ryderams/routes) - список маршрутов
|
||||
|
||||
| | | | | | | |
|
||||
| ------- | ------- | ------- | ------- | ------- | -------------- | -------------- |
|
||||
| 8.8.4.4 | 1.1.1.1 | 8.8.8.8 | 1.0.0.1 | 9.9.9.9 | 213.177.96.201 | 213.177.97.201 |
|
||||
Очистка DNS таблицы Win
|
||||
```bash
|
||||
ipconfig /flushdns
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
Описание на Habr:
|
||||
|
||||
AI провайдер: polza.ai
|
||||
API: `pza_emOW7E0WsoXli4c85T5t_j2q1_Nk2dIT`
|
||||
|
||||
|
||||
@@ -1,184 +0,0 @@
|
||||
# Настройка Amnezia VPN на роутере Keenetic Giga KN-1011
|
||||
|
||||
Роутер Keenetic Giga (KN-1011) под управлением KeeneticOS поддерживает подключение к VPN через протоколы **OpenVPN** и **WireGuard**. Amnezia VPN умеет экспортировать конфигурации в оба формата, что позволяет подключить роутер к вашему собственному VPN-серверу, развёрнутому через Amnezia.
|
||||
|
||||
---
|
||||
|
||||
## 1. Экспорт конфигурации из Amnezia VPN
|
||||
|
||||
### 1.1. На устройстве, где установлен Amnezia VPN
|
||||
|
||||
1. Откройте приложение **Amnezia VPN**.
|
||||
2. Перейдите на вкладку **Сервер** (Server) / **Подключения** (Connections).
|
||||
3. Нажмите кнопку **Поделиться** (Share) / **Экспорт** (Export) рядом с вашим подключением.
|
||||
4. Выберите формат экспорта:
|
||||
|
||||
| Если роутер использует | Выберите формат |
|
||||
|------------------------|----------------|
|
||||
| OpenVPN | **For OpenVPN connection** (`.ovpn`) |
|
||||
| WireGuard (родной) | **For WireGuard connection** (`.conf`) |
|
||||
|
||||
> **Протокол AmneziaWG (awg) НЕ поддерживается роутером Keenetic.**
|
||||
> Если у вас включён AmneziaWG, временно переключитесь в приложении на обычный WireGuard или OpenVPN, экспортируйте конфиг, затем можно вернуть обратно.
|
||||
|
||||
5. Сохраните полученный файл на компьютер (`.ovpn` или `.conf`).
|
||||
|
||||
### 1.2. Если доступна только ссылка `vpn://`
|
||||
|
||||
На серверах Amnezia Self-hosted и Premium иногда конфиг выдаётся в виде URI `vpn://...`. Его нужно преобразовать: откройте этот URI в приложении Amnezia, выберите нужный протокол (OpenVPN или WireGuard), после чего экспортируйте как описано выше.
|
||||
|
||||
---
|
||||
|
||||
## 2. Установка необходимого компонента на роутере
|
||||
|
||||
Зайдите в веб-интерфейс роутера:
|
||||
- **Адрес:** `http://my.keenetic.net` или `http://192.168.1.1`
|
||||
- **Логин/пароль:** по умолчанию `admin` / `1234` (если не меняли)
|
||||
|
||||
### 2.1. Для OpenVPN
|
||||
|
||||
1. Перейдите: **Управление** → **Общие настройки системы** (или **System Settings**).
|
||||
2. В блоке **Обновление KeeneticOS и компонентов** нажмите **Выбор компонентов** (Component options).
|
||||
3. Найдите компонент **OpenVPN-клиент и сервер** (OpenVPN client and server).
|
||||
4. Установите флажок и нажмите **Сохранить**.
|
||||
5. Роутер скачает и установит компонент (может потребоваться перезагрузка).
|
||||
|
||||
### 2.2. Для WireGuard
|
||||
|
||||
1. Перейдите: **Управление** → **Общие настройки системы**.
|
||||
2. Нажмите **Выбор компонентов**.
|
||||
3. Найдите компонент **WireGuard VPN**.
|
||||
4. Установите флажок → **Сохранить**.
|
||||
|
||||
---
|
||||
|
||||
## 3. Создание VPN-подключения
|
||||
|
||||
### 3.1. Через OpenVPN (.ovpn)
|
||||
|
||||
1. Откройте экспортированный файл `.ovpn` в любом текстовом редакторе (Блокнот, Notepad++).
|
||||
2. Выделите ВЕСЬ текст файла (Ctrl+A) и скопируйте (Ctrl+C).
|
||||
|
||||
3. В веб-интерфейсе роутера перейдите:
|
||||
**Интернет** → **Другие подключения** (Other connections).
|
||||
|
||||
4. В блоке **VPN-подключения** нажмите **Создать подключение** (Create connection).
|
||||
|
||||
5. Заполните:
|
||||
|
||||
| Поле | Значение |
|
||||
|-------------------------|----------|
|
||||
| **Использовать для выхода в интернет** | ☑ (включить) |
|
||||
| **Имя подключения** | Например `Amnezia-OVPN` |
|
||||
| **Тип (протокол)** | **OpenVPN** |
|
||||
| **Конфигурация OpenVPN** | Вставьте скопированный текст (Ctrl+V) |
|
||||
| **Получать маршруты с удалённой стороны** | ☑ (включить) |
|
||||
|
||||
6. При необходимости нажмите **Показать дополнительные настройки** и укажите:
|
||||
- **Подключаться через** → «Любое интернет-подключение»
|
||||
- **Расписание работы** → выключить (или настроить по желанию)
|
||||
|
||||
7. Нажмите **Сохранить**, затем переведите переключатель в положение **Вкл**.
|
||||
|
||||
#### Типичные проблемы с OpenVPN на Keenetic
|
||||
|
||||
| Ошибка в логе | Решение |
|
||||
|--------------|---------|
|
||||
| `auth-user-pass without inline credentials` | Найдите и удалите строку `auth-user-pass` — она должна быть заменена на блок `<auth-user-pass>...</auth-user-pass>` с логином и паролем внутри |
|
||||
| `block-outside-dns` | Добавьте в конфиг строку: `pull-filter ignore "block-outside-dns"` |
|
||||
| `cipher BF-CBC` / `keysize` | Замените `cipher BF-CBC` на: `data-ciphers AES-256-GCM:AES-128-GCM:CHACHA20-POLY1305` и `data-ciphers-fallback AES-256-CBC` |
|
||||
| `Unrecognized option: client-ip` | Добавьте: `ignore-unknown-option client-ip block-ipv6` |
|
||||
|
||||
> Лог OpenVPN доступен в веб-интерфейсе: **Диагностика** → **Системный журнал**, фильтр по `OpenVPN0`.
|
||||
|
||||
### 3.2. Через WireGuard (.conf)
|
||||
|
||||
Keenetic умеет импортировать WireGuard-конфигурацию напрямую из файла:
|
||||
|
||||
1. В веб-интерфейсе перейдите: **Интернет** → **Другие подключения**.
|
||||
|
||||
2. В блоке **WireGuard** нажмите **Импорт из файла** (Import from a file).
|
||||
|
||||
3. Выберите файл `.conf`, экспортированный из Amnezia.
|
||||
|
||||
4. После импорта нажмите **Сохранить**.
|
||||
|
||||
5. Включите подключение переключателем в положение **Вкл**.
|
||||
|
||||
Если импорт из файла недоступен (старая версия KeeneticOS), настройте вручную:
|
||||
|
||||
1. Нажмите **Создать подключение** → тип **WireGuard**.
|
||||
2. Заполните поля из файла `.conf`:
|
||||
|
||||
| Поле в веб-интерфейсе | Значение из `.conf` |
|
||||
|-----------------------|---------------------|
|
||||
| Приватный ключ (PrivateKey) | `[Interface] PrivateKey = ...` |
|
||||
| Адрес (Address) | `[Interface] Address = ...` |
|
||||
| DNS-сервер (DNS) | `[Interface] DNS = ...` |
|
||||
| Открытый ключ пира (Peer PublicKey) | `[Peer] PublicKey = ...` |
|
||||
| Endpoint (адрес сервера:порт) | `[Peer] Endpoint = ...` |
|
||||
| Разрешённые IP (AllowedIPs) | `[Peer] AllowedIPs = ...` |
|
||||
| PersistentKeepalive | `[Peer] PersistentKeepalive = ...` |
|
||||
|
||||
3. Сохраните и включите.
|
||||
|
||||
> ⚠️ **Важно:** родной WireGuard на Keenetic **не поддерживает** параметры обфускации AmneziaWG (`Jc`, `Jmin`, `Jmax`, `S1`, `S2`, `H1`–`H4`). Если в вашем конфиге есть эти параметры — используйте OpenVPN, либо переключитесь в Amnezia на обычный WireGuard перед экспортом.
|
||||
|
||||
---
|
||||
|
||||
## 4. Настройка приоритетов и политик трафика
|
||||
|
||||
Чтобы весь трафик роутера шёл через VPN:
|
||||
|
||||
1. Перейдите: **Интернет** → **Приоритеты подключений** (Connection priorities).
|
||||
2. На вкладке **Правила по умолчанию**:
|
||||
- Найдите созданное VPN-подключение
|
||||
- Перетащите его мышью на самый верх списка
|
||||
- Нажмите **Сохранить**
|
||||
|
||||
3. Если хотите направлять через VPN только определённые устройства:
|
||||
- Перейдите на вкладку **Привязка политик** (Policy bindings)
|
||||
- Создайте правило, выбрав устройство и нужное VPN-подключение
|
||||
|
||||
---
|
||||
|
||||
## 5. Проверка подключения
|
||||
|
||||
1. Перейдите: **Диагностика** → **Ping**.
|
||||
2. Введите `8.8.8.8` и нажмите **Проверить**.
|
||||
3. Убедитесь, что пинг проходит.
|
||||
4. Дополнительно проверьте внешний IP-адрес роутера на главной странице (должен совпадать с IP вашего VPN-сервера).
|
||||
|
||||
Просмотр статуса VPN:
|
||||
- **Интернет** → **Другие подключения** — переключатель должен быть зелёным (Вкл).
|
||||
- **Диагностика** → **Системный журнал** — поиск по `OpenVPN0` или `Wireguard0`.
|
||||
|
||||
---
|
||||
|
||||
## 6. Если что-то не работает
|
||||
|
||||
| Симптом | Вероятная причина |
|
||||
|---------|-------------------|
|
||||
| Подключение не включается | Не установлен компонент OpenVPN/WireGuard в системе |
|
||||
| OpenVPN: ошибка в логе | Несовместимая директива — см. таблицу в п. 3.1 |
|
||||
| WireGuard: нет соединения | Конфиг содержит параметры AmneziaWG — используйте OpenVPN |
|
||||
| VPN есть, но интернет не работает | Неправильные приоритеты подключений (п. 4) |
|
||||
| Нет доступа к локальным ресурсам | В OpenVPN-конфиге `redirect-gateway` или в `AllowedIPs = 0.0.0.0/0` — роутер не видит локальную сеть. Используйте AllowedIPs по умолчанию от провайдера или отключите VPN для локальных адресов |
|
||||
|
||||
---
|
||||
|
||||
## 7. Сводка — что поддерживается на KN-1011
|
||||
|
||||
| Протокол | Поддержка | Способ подключения |
|
||||
|----------|-----------|-------------------|
|
||||
| **OpenVPN** (TUN, UDP/TCP) | ✅ Полная | Вставка текста `.ovpn` в поле конфигурации |
|
||||
| **WireGuard** (обычный) | ✅ Полная | Импорт `.conf` файла или ручной ввод |
|
||||
| **AmneziaWG** (awg) | ❌ **НЕ поддерживается** | Требуется OpenWRT с кастомным ядром |
|
||||
| **OpenVPN over Cloak** | ❌ Не поддерживается | Только через сторонний клиент |
|
||||
| **XRay (VLESS Reality)** | ❌ Не поддерживается | Только через сторонний клиент |
|
||||
|
||||
**Вывод:** для Keenetic используйте **OpenVPN** (рекомендуется) или обычный **WireGuard** без обфускации.
|
||||
|
||||
---
|
||||
|
||||
> **Источники:** официальная документация Keenetic (support.keenetic.com), документация Amnezia (docs.amnezia.org), GitHub amnezia-vpn/amnezia-client.
|
||||
@@ -1,76 +0,0 @@
|
||||
# Подключение Hermes Desktop (Windows) к серверу
|
||||
|
||||
Общая схема:
|
||||
|
||||
[Windows] --LAN--> [Сервер 192.168.1.44:9119]
|
||||
Hermes Desktop Hermes Dashboard
|
||||
(тот же агент, что в этом сеансе)
|
||||
|
||||
---
|
||||
|
||||
## 1. Что уже сделано на сервере
|
||||
|
||||
- Dashboard запущен на `0.0.0.0:9119` (слушает все интерфейсы)
|
||||
- Basic auth включена: логин `admin`, пароль `agent`
|
||||
- Dashboard **автоматически стартует при загрузке сервера** (systemd-сервис `hermes-dashboard.service`)
|
||||
- При падении перезапускается автоматически через 10 секунд
|
||||
|
||||
Проверить статус можно с сервера:
|
||||
|
||||
curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:9119/api/health
|
||||
# → 200
|
||||
|
||||
systemctl status hermes-dashboard.service
|
||||
# → active (running)
|
||||
|
||||
---
|
||||
|
||||
## 2. Подключение из Hermes Desktop (Windows)
|
||||
|
||||
1. Открой Hermes Desktop на Windows
|
||||
2. Нажми `Ctrl+K` (Cmd+K на Mac) — откроется палитра команд
|
||||
3. Набери **Remote Gateway** и выбери `Connect to Remote Gateway...` (или найди в Settings → Gateway → Remote)
|
||||
4. Введи данные:
|
||||
- **URL сервера:** `http://192.168.1.44:9119`
|
||||
- **Логин:** `admin`
|
||||
- **Пароль:** `agent`
|
||||
5. После подключения в левой панели появится серверный профиль — можно выбирать его и общаться как обычно.
|
||||
|
||||
> Если Windows подключён через VPN или из другой подсети — IP может отличаться.
|
||||
> Узнать текущий IP сервера: `hostname -I`
|
||||
|
||||
---
|
||||
|
||||
## 3. Управление сервисом (на сервере)
|
||||
|
||||
Сервис уже установлен и активен. Команды управления:
|
||||
|
||||
| Действие | Команда |
|
||||
|----------|---------|
|
||||
| Статус | `systemctl status hermes-dashboard.service` |
|
||||
| Стоп | `systemctl stop hermes-dashboard.service` |
|
||||
| Старт | `systemctl start hermes-dashboard.service` |
|
||||
| Рестарт | `systemctl restart hermes-dashboard.service` |
|
||||
| Отключить автозапуск | `systemctl disable hermes-dashboard.service` |
|
||||
| Логи (свежие) | `journalctl -u hermes-dashboard.service -n 50` |
|
||||
| Логи (хвост) | `journalctl -u hermes-dashboard.service -f` |
|
||||
|
||||
---
|
||||
|
||||
## 4. Безопасность
|
||||
|
||||
- Доступ к dashboard возможен из **любого устройства в той же LAN** (подсеть 192.168.1.0/24), кто знает логин/пароль
|
||||
- За пределами LAN dashboard **недоступен** — сервер не торчит в интернет
|
||||
- Пароль хранится в `/root/.hermes/config.yaml` — не свети этот файл
|
||||
- Для доступа из интернета — облачный relay (Nous Portal) или VPN/SSH-туннель
|
||||
|
||||
---
|
||||
|
||||
## 5. Что если не работает
|
||||
|
||||
| Симптом | Решение |
|
||||
|---------|---------|
|
||||
| Desktop пишет "Connection refused" | Сервис не запущен → `systemctl start hermes-dashboard.service` |
|
||||
| 401 Unauthorized | Неправильный логин или пароль — проверь в `config.yaml` секцию `basic_auth` |
|
||||
| Desktop видит сервер, но не отвечает | `hermes doctor` на сервере — проверить модель и соединение |
|
||||
| Нет соединения после ребута сервера | Сервис включён в автозапуск, проверь статус `systemctl status hermes-dashboard.service` |
|
||||
@@ -1,74 +0,0 @@
|
||||
import time, os
|
||||
from luma.core.interface.serial import i2c
|
||||
from luma.oled.device import ssd1306
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
|
||||
serial = i2c(port=1, address=0x3C)
|
||||
device = ssd1306(serial)
|
||||
font = ImageFont.truetype('/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf', 10)
|
||||
font_big = ImageFont.truetype('/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf', 16)
|
||||
device.cleanup = lambda: None
|
||||
|
||||
|
||||
def get_cpu():
|
||||
with open('/proc/loadavg') as f:
|
||||
parts = f.read().split()
|
||||
load = float(parts[0])
|
||||
cores = os.cpu_count() or 4
|
||||
percent = (load / cores) * 100
|
||||
return min(percent, 100), load
|
||||
|
||||
|
||||
def get_ram():
|
||||
with open('/proc/meminfo') as f:
|
||||
lines = f.readlines()
|
||||
mem_total = int([l for l in lines if 'MemTotal' in l][0].split()[1])
|
||||
mem_avail = int([l for l in lines if 'MemAvailable' in l][0].split()[1])
|
||||
used = mem_total - mem_avail
|
||||
return used / mem_total * 100, used // 1024, mem_total // 1024
|
||||
|
||||
|
||||
def get_temp():
|
||||
try:
|
||||
with open('/sys/class/thermal/thermal_zone0/temp') as f:
|
||||
return int(f.read()) / 1000
|
||||
except:
|
||||
return 0
|
||||
|
||||
|
||||
while True:
|
||||
cpu_pct, load = get_cpu()
|
||||
ram_pct, used_mb, total_mb = get_ram()
|
||||
temp = get_temp()
|
||||
|
||||
img = Image.new('1', device.size, 'black')
|
||||
draw = ImageDraw.Draw(img)
|
||||
|
||||
draw.rectangle((0, 0, 127, 63), outline='white', fill='black')
|
||||
|
||||
bar_w = 90
|
||||
bar_h = 12
|
||||
|
||||
# CPU bar
|
||||
draw.rectangle((32, 4, 32 + bar_w, 4 + bar_h), outline='white', fill='black')
|
||||
fill_w = int(bar_w * cpu_pct / 100)
|
||||
if fill_w > 0:
|
||||
draw.rectangle((32 + 1, 4 + 1, 32 + fill_w, 4 + bar_h - 1), fill='white')
|
||||
draw.text((2, 5), 'CPU', font=font, fill='white')
|
||||
draw.text((32 + bar_w + 4, 3), f'{cpu_pct:.0f}%', font=font_big, fill='white')
|
||||
draw.text((2, 20), f'Load: {load:.2f}', font=font, fill='white')
|
||||
|
||||
# RAM bar
|
||||
draw.rectangle((32, 36, 32 + bar_w, 36 + bar_h), outline='white', fill='black')
|
||||
fill_w2 = int(bar_w * ram_pct / 100)
|
||||
if fill_w2 > 0:
|
||||
draw.rectangle((32 + 1, 36 + 1, 32 + fill_w2, 36 + bar_h - 1), fill='white')
|
||||
draw.text((2, 38), 'RAM', font=font, fill='white')
|
||||
draw.text((32 + bar_w + 4, 36), f'{ram_pct:.0f}%', font=font_big, fill='white')
|
||||
|
||||
# Temp and PID at bottom
|
||||
draw.text((2, 52), f'Temp: {temp:.1f}C', font=font, fill='white')
|
||||
draw.text((72, 52), f'PID: {os.getpid()}', font=font, fill='white')
|
||||
|
||||
device.display(img)
|
||||
time.sleep(1)
|
||||
@@ -1,443 +0,0 @@
|
||||
# Подключение OLED дисплея SSD1306 128x64 к Raspberry Pi 5
|
||||
|
||||
## Содержание
|
||||
|
||||
1. [Подключение по I2C](#1-подключение-по-i2c)
|
||||
2. [Настройка I2C на RPi5](#2-настройка-i2c-на-rpi5)
|
||||
3. [Установка ПО](#3-установка-по)
|
||||
4. [Проверка дисплея](#4-проверка-дисплея)
|
||||
5. [Статическое изображение](#5-статическое-изображение)
|
||||
6. [CPU/Монитор в реальном времени](#6-cpumонитор-в-реальном-времени)
|
||||
7. [Systemd-сервис для автостарта](#7-systemd-сервис-для-автостарта)
|
||||
8. [Решение проблем](#8-решение-проблем)
|
||||
9. [Приложение: полный код скриптов](#9-приложение-полный-код-скриптов)
|
||||
|
||||
---
|
||||
|
||||
## 1. Подключение по I2C
|
||||
|
||||
SSD1306 подключается по I2C-шине. Пины I2C на Raspberry Pi 5:
|
||||
|
||||
| Пин | Название | GPIO | Назначение |
|
||||
|-----|----------|------|------------|
|
||||
| 3 | SDA | GPIO2 | I2C Data |
|
||||
| 5 | SCL | GPIO3 | I2C Clock |
|
||||
| 1 | 3.3V | — | Питание |
|
||||
| 6 | GND | — | Земля |
|
||||
|
||||
**Схема подключения:**
|
||||
```
|
||||
RPi5 OLED SSD1306
|
||||
GPIO2 (Pin 3) ─────── SDA
|
||||
GPIO3 (Pin 5) ─────── SCL
|
||||
3.3V (Pin 1) ─────── VCC
|
||||
GND (Pin 6) ─────── GND
|
||||
```
|
||||
|
||||
**Важно:** Для I2C нужны подтягивающие резисторы (pull-up) 4.7 кОм на линиях SDA и SCL к 3.3V. На большинстве готовых OLED-модулей они уже впаяны — дополнительно ставить не нужно.
|
||||
|
||||
---
|
||||
|
||||
## 2. Настройка I2C на RPi5
|
||||
|
||||
На Raspberry Pi 5 I2C по умолчанию **отключён** в `/boot/firmware/config.txt`.
|
||||
|
||||
### 2.1. Включение I2C
|
||||
|
||||
```bash
|
||||
sudo sed -i 's/#dtparam=i2c_arm=on/dtparam=i2c_arm=on/' /boot/firmware/config.txt
|
||||
sudo reboot
|
||||
```
|
||||
|
||||
**Проверка после перезагрузки:**
|
||||
|
||||
```bash
|
||||
sudo /usr/sbin/i2cdetect -y 1
|
||||
```
|
||||
|
||||
Вывод (пример — если дисплей подключён):
|
||||
```
|
||||
0 1 2 3 4 5 6 7 8 9 a b c d e f
|
||||
00: -- -- -- -- -- -- -- --
|
||||
...
|
||||
30: -- -- -- -- -- -- -- -- -- -- -- -- 3c -- -- --
|
||||
...
|
||||
```
|
||||
|
||||
Адрес **0x3C** — стандартный для SSD1306 (может быть 0x3D, проверяйте по даташиту).
|
||||
|
||||
**Заметка:** `i2cdetect` лежит в `/usr/sbin/` — этого каталога нет в PATH обычного пользователя. Используйте полный путь или `sudo`.
|
||||
|
||||
### 2.2. Альтернатива: raspi-config
|
||||
|
||||
```bash
|
||||
sudo raspi-config
|
||||
# → Interface Options → I2C → Enable
|
||||
sudo reboot
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Установка ПО
|
||||
|
||||
### 3.1. Системные пакеты
|
||||
|
||||
```bash
|
||||
sudo apt-get install -y i2c-tools python3-pip python3-smbus python3-pil
|
||||
```
|
||||
|
||||
### 3.2. Виртуальное окружение Python
|
||||
|
||||
На Raspberry Pi OS Bookworm используется `external-managed-environment` — `pip install` напрямую не работает. Обязательно создаём venv:
|
||||
|
||||
```bash
|
||||
python3 -m venv ~/oled-env
|
||||
source ~/oled-env/bin/activate
|
||||
```
|
||||
|
||||
### 3.3. Установка luma.oled
|
||||
|
||||
```bash
|
||||
pip install luma.oled Pillow
|
||||
```
|
||||
|
||||
Проверка установки:
|
||||
```bash
|
||||
python3 -c "from luma.core.interface.serial import i2c; from luma.oled.device import ssd1306; print('OK')"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Проверка дисплея
|
||||
|
||||
После включения I2C и установки ПО — быстрый тест сканирует шину и зажигает пиксели:
|
||||
|
||||
```bash
|
||||
source ~/oled-env/bin/activate
|
||||
python3 -c "
|
||||
from luma.core.interface.serial import i2c
|
||||
from luma.oled.device import ssd1306
|
||||
from PIL import Image, ImageDraw
|
||||
|
||||
serial = i2c(port=1, address=0x3C)
|
||||
device = ssd1306(serial)
|
||||
|
||||
img = Image.new('1', device.size, 'black')
|
||||
draw = ImageDraw.Draw(img)
|
||||
draw.rectangle((0, 0, 127, 63), outline='white', fill='black')
|
||||
draw.text((20, 25), 'Hello!', fill='white')
|
||||
|
||||
device.display(img)
|
||||
device.cleanup = lambda: None
|
||||
"
|
||||
```
|
||||
|
||||
Если дисплей показывает рамку и текст — всё работает.
|
||||
|
||||
---
|
||||
|
||||
## 5. Статическое изображение
|
||||
|
||||
Скрипт `/home/emb/oled_show.py` — выводит статический текст и завершается, оставляя изображение на дисплее.
|
||||
|
||||
```python
|
||||
#!/home/emb/oled-env/bin/python3
|
||||
import sys, os, signal
|
||||
from luma.core.interface.serial import i2c
|
||||
from luma.oled.device import ssd1306
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
|
||||
serial = i2c(port=1, address=0x3C)
|
||||
device = ssd1306(serial)
|
||||
|
||||
img = Image.new('1', device.size, 'black')
|
||||
draw = ImageDraw.Draw(img)
|
||||
font = ImageFont.truetype('/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf', 11)
|
||||
|
||||
draw.rectangle((0, 0, 127, 63), outline='white', fill='black')
|
||||
draw.text((2, 2), 'RPi5 Ready!', font=font, fill='white')
|
||||
draw.text((2, 16), 'I2C 128x64', font=font, fill='white')
|
||||
draw.text((2, 30), '192.168.1.46', font=font, fill='white')
|
||||
draw.text((2, 50), 'by Hermes', font=font, fill='white')
|
||||
|
||||
device.display(img)
|
||||
device.cleanup = lambda: None # <-- КЛЮЧЕВОЙ МОМЕНТ
|
||||
print('OLED ready')
|
||||
```
|
||||
|
||||
### Почему `device.cleanup = lambda: None`?
|
||||
|
||||
Библиотека `luma.oled` в деструкторе объекта `device` вызывает команду 0xAE (DISPLAY OFF), что гасит дисплей при завершении скрипта. Замена `cleanup` на пустую функцию предотвращает это.
|
||||
|
||||
Если вам нужно, чтобы скрипт работал постоянно (и дисплей не гас _никогда_), добавьте `signal.pause()` в конец — процесс останется висеть.
|
||||
|
||||
---
|
||||
|
||||
## 6. CPU/Монитор в реальном времени
|
||||
|
||||
Скрипт `/home/emb/oled_cpu_mon.py` — отображает загрузку CPU, RAM и температуру с обновлением раз в секунду.
|
||||
|
||||
```python
|
||||
import time, os
|
||||
from luma.core.interface.serial import i2c
|
||||
from luma.oled.device import ssd1306
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
|
||||
serial = i2c(port=1, address=0x3C)
|
||||
device = ssd1306(serial)
|
||||
font = ImageFont.truetype('/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf', 10)
|
||||
font_big = ImageFont.truetype('/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf', 16)
|
||||
device.cleanup = lambda: None
|
||||
|
||||
|
||||
def get_cpu():
|
||||
with open('/proc/loadavg') as f:
|
||||
parts = f.read().split()
|
||||
load = float(parts[0])
|
||||
cores = os.cpu_count() or 4
|
||||
percent = (load / cores) * 100
|
||||
return min(percent, 100), load
|
||||
|
||||
|
||||
def get_ram():
|
||||
with open('/proc/meminfo') as f:
|
||||
lines = f.readlines()
|
||||
mem_total = int([l for l in lines if 'MemTotal' in l][0].split()[1])
|
||||
mem_avail = int([l for l in lines if 'MemAvailable' in l][0].split()[1])
|
||||
used = mem_total - mem_avail
|
||||
return used / mem_total * 100, used // 1024, mem_total // 1024
|
||||
|
||||
|
||||
def get_temp():
|
||||
try:
|
||||
with open('/sys/class/thermal/thermal_zone0/temp') as f:
|
||||
return int(f.read()) / 1000
|
||||
except:
|
||||
return 0
|
||||
|
||||
|
||||
while True:
|
||||
cpu_pct, load = get_cpu()
|
||||
ram_pct, used_mb, total_mb = get_ram()
|
||||
temp = get_temp()
|
||||
|
||||
img = Image.new('1', device.size, 'black')
|
||||
draw = ImageDraw.Draw(img)
|
||||
|
||||
draw.rectangle((0, 0, 127, 63), outline='white', fill='black')
|
||||
|
||||
# CPU bar
|
||||
bar_w = 90
|
||||
bar_h = 12
|
||||
draw.rectangle((32, 4, 32 + bar_w, 4 + bar_h), outline='white', fill='black')
|
||||
fill_w = int(bar_w * cpu_pct / 100)
|
||||
if fill_w > 0:
|
||||
draw.rectangle((32 + 1, 4 + 1, 32 + fill_w, 4 + bar_h - 1), fill='white')
|
||||
draw.text((2, 5), 'CPU', font=font, fill='white')
|
||||
draw.text((32 + bar_w + 4, 3), f'{cpu_pct:.0f}%', font=font_big, fill='white')
|
||||
draw.text((2, 20), f'Load: {load:.2f}', font=font, fill='white')
|
||||
|
||||
# RAM bar
|
||||
draw.rectangle((32, 36, 32 + bar_w, 36 + bar_h), outline='white', fill='black')
|
||||
fill_w2 = int(bar_w * ram_pct / 100)
|
||||
if fill_w2 > 0:
|
||||
draw.rectangle((32 + 1, 36 + 1, 32 + fill_w2, 36 + bar_h - 1), fill='white')
|
||||
draw.text((2, 38), 'RAM', font=font, fill='white')
|
||||
draw.text((32 + bar_w + 4, 36), f'{ram_pct:.0f}%', font=font_big, fill='white')
|
||||
|
||||
# Temp and PID at bottom
|
||||
draw.text((2, 52), f'Temp: {temp:.1f}C', font=font, fill='white')
|
||||
draw.text((72, 52), f'PID: {os.getpid()}', font=font, fill='white')
|
||||
|
||||
device.display(img)
|
||||
time.sleep(1)
|
||||
```
|
||||
|
||||
### Визуальная схема расположения на дисплее:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ CPU ████████████████░░░░ 45% │
|
||||
│ Load: 1.80 │
|
||||
│ RAM ██████████░░░░░░░░ 32% │
|
||||
│ Temp: 52.3C PID: 2535 │
|
||||
└─────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Systemd-сервис для автостарта
|
||||
|
||||
Чтобы скрипт запускался автоматически при включении RPi5 и перезапускался при падении — используем systemd.
|
||||
|
||||
### 7.1. Создание сервиса
|
||||
|
||||
**Предварительно:** убедитесь, что модуль `i2c-dev` загружается при старте:
|
||||
|
||||
```bash
|
||||
echo i2c-dev | sudo tee /etc/modules-load.d/i2c-dev.conf
|
||||
```
|
||||
|
||||
Создать сервис:
|
||||
|
||||
```bash
|
||||
sudo tee /etc/systemd/system/oled-display.service > /dev/null << 'EOF'
|
||||
[Unit]
|
||||
Description=OLED Display Service
|
||||
After=multi-user.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=emb
|
||||
ExecStart=/home/emb/oled-env/bin/python3 /home/emb/oled_cpu_mon.py
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
```
|
||||
|
||||
| Параметр | Значение | Пояснение |
|
||||
|----------|----------|-----------|
|
||||
| `User=emb` | emb | Запуск от вашего пользователя |
|
||||
| `ExecStart` | venv/bin/python3 + скрипт | Полный путь к интерпретатору |
|
||||
| `Restart=always` | — | Перезапуск при любом завершении |
|
||||
| `RestartSec=5` | 5 | Пауза между перезапусками |
|
||||
|
||||
### 7.2. Активация и управление
|
||||
|
||||
```bash
|
||||
sudo systemctl daemon-reload # перечитать конфиги
|
||||
sudo systemctl enable oled-display.service # автостарт при boot
|
||||
sudo systemctl restart oled-display.service # запустить сейчас
|
||||
```
|
||||
|
||||
### 7.3. Базовые команды
|
||||
|
||||
```bash
|
||||
# Статус
|
||||
sudo systemctl status oled-display.service
|
||||
|
||||
# Остановка
|
||||
sudo systemctl stop oled-display.service
|
||||
|
||||
# Запуск
|
||||
sudo systemctl start oled-display.service
|
||||
|
||||
# Отключение автостарта
|
||||
sudo systemctl disable oled-display.service
|
||||
|
||||
# Логи
|
||||
sudo journalctl -u oled-display.service -n 20
|
||||
```
|
||||
|
||||
### 7.4. Смена скрипта
|
||||
|
||||
Чтобы переключить сервис на другой скрипт (например, со статического на монитор):
|
||||
|
||||
```bash
|
||||
sudo sed -i 's|oled_show.py|oled_cpu_mon.py|' /etc/systemd/system/oled-display.service
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl restart oled-display.service
|
||||
```
|
||||
|
||||
### 7.5. Обновление скрипта без простоя
|
||||
|
||||
Если сервис уже работает:
|
||||
|
||||
```bash
|
||||
sudo systemctl stop oled-display.service
|
||||
# редактируем /home/emb/oled_cpu_mon.py
|
||||
sudo systemctl restart oled-display.service
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Решение проблем
|
||||
|
||||
### 8.1. Дисплей пустой после запуска
|
||||
|
||||
**Причина:** деструктор luma.oled отключает дисплей.
|
||||
|
||||
**Решение:** `device.cleanup = lambda: None` перед выходом из скрипта.
|
||||
|
||||
### 8.2. I2C device not found после перезагрузки (стартовая гонка)
|
||||
|
||||
**Симптом:** сервис падает в логах с `I2C device not found: /dev/i2c-1`, хотя после ручного запуска всё работает.
|
||||
|
||||
**Причина:** systemd запускает сервис до загрузки модуля `i2c-dev`.
|
||||
|
||||
**Решение:** добавить ожидание устройства в скрипте:
|
||||
|
||||
```python
|
||||
import os, time
|
||||
for attempt in range(30):
|
||||
if os.path.exists('/dev/i2c-1'):
|
||||
break
|
||||
time.sleep(1)
|
||||
else:
|
||||
raise RuntimeError('I2C device /dev/i2c-1 not found after 30s')
|
||||
```
|
||||
|
||||
Также можно загружать модуль принудительно: `echo i2c-dev | sudo tee /etc/modules-load.d/i2c-dev.conf`
|
||||
|
||||
```bash
|
||||
sudo /usr/sbin/i2cdetect -y 1
|
||||
```
|
||||
|
||||
Если адрес не отображается:
|
||||
- Проверьте физическое подключение пинов SDA/SCL/3.3V/GND
|
||||
- Включите I2C в `/boot/firmware/config.txt` и перезагрузите
|
||||
- Проверьте `ls /dev/i2c*` — если шины нет, загрузите модуль: `sudo modprobe i2c-dev`
|
||||
|
||||
### 8.3. pip install не работает
|
||||
|
||||
Ошибка: `error: externally-managed-environment`
|
||||
|
||||
**Причина:** Raspberry Pi OS Bookworm включает PEP 668.
|
||||
|
||||
**Решение:** всегда используйте venv.
|
||||
|
||||
```bash
|
||||
python3 -m venv ~/oled-env
|
||||
source ~/oled-env/bin/activate
|
||||
pip install luma.oled Pillow
|
||||
```
|
||||
|
||||
### 8.4. i2cdetect: command not found
|
||||
|
||||
**Причина:** `/usr/sbin/` не в PATH обычного пользователя.
|
||||
|
||||
**Решение:** `sudo /usr/sbin/i2cdetect -y 1`
|
||||
|
||||
### 8.5. systemd-сервис падает
|
||||
|
||||
```bash
|
||||
sudo journalctl -u oled-display.service -n 20
|
||||
```
|
||||
|
||||
Типичные ошибки:
|
||||
- `SyntaxError` — проверьте скрипт на синтаксис
|
||||
- `ModuleNotFoundError` — проверьте путь в `ExecStart` (должен вести к python из venv)
|
||||
- `PermissionError` — файл скрипта должен быть читаемым для пользователя
|
||||
|
||||
### 8.6. Дисплей показывает мусор/рябь
|
||||
|
||||
- Сделайте полную инициализацию контроллера (последовательность 0xAE ... 0xAF)
|
||||
- Проверьте питание — 3.3V стабильно?
|
||||
- Убедитесь что нет конфликта адресов (другие I2C устройства на той же шине)
|
||||
|
||||
---
|
||||
|
||||
## 9. Приложение: полный код скриптов
|
||||
|
||||
### 9.1. static_text.py — статический текст
|
||||
|
||||
Полный код доступен в файле `/home/emb/oled_show.py` на целевой системе.
|
||||
|
||||
### 9.2. cpu_mon.py — монитор CPU/RAM/Температура
|
||||
|
||||
Полный код доступен в файле `/home/emb/oled_cpu_mon.py` на целевой системе.
|
||||
|
||||
Создано: Hermes Agent · Raspberry Pi 5 · Debian Bookworm · SSD1306 128×64 I2C
|
||||
@@ -1,91 +0,0 @@
|
||||
## Description
|
||||
|
||||
![[Pasted image 20260517222623.png]]
|
||||
|
||||
Radxa E25 https://docs.radxa.com/en/rock3/e25
|
||||
Radxa Wiki: https://wiki.radxa.com/Rock3/installusb-install-radxa-e25
|
||||
https://wikidevi.wi-cat.ru/Radxa_E25_(D2E8)
|
||||
Forum Radxa Community: https://forum.radxa.com/
|
||||
Документация и файлы: https://dl.radxa.com/cm3i/e25/
|
||||
|
||||
|
||||
## Debian microSD Installation Guide
|
||||
1. Скачать образ системы (Debian or OpenWRT)
|
||||
2. С помощью Balena Etcher записать образ на microSD
|
||||
3. Подключить питание к TypeC и Ethernet
|
||||
1. Debian: установить SSH соединение:
|
||||
1. user: `radxa`
|
||||
2. psw: `radxa`
|
||||
2. OpenWRT
|
||||
1. usr: `root`
|
||||
2. psw: `password`
|
||||
3. ip: 192.168.1.1
|
||||
4. Обновить систему:
|
||||
```bash
|
||||
sudo apt update && sudo apt upgrade -y
|
||||
```
|
||||
При возникновении проблем с репозиториями
|
||||
```bash
|
||||
sudo sed -i '/bullseye-backports/d' /etc/apt/sources.list /etc/apt/sources.list.d/* 2>/dev/null
|
||||
```
|
||||
При возникновении необходимости **downgrade** выполнить принудительное обновлениt:
|
||||
```bash
|
||||
sudo apt dist-upgrade --allow-downgrades
|
||||
```
|
||||
Перезагрузить систему
|
||||
```bash
|
||||
sudo systemctl -r reboot
|
||||
```
|
||||
Удалить неиспользуемые пакеты:
|
||||
```bash
|
||||
sudo apt autoremove -y
|
||||
```
|
||||
|
||||
### Setting with rsetup
|
||||
Настройка SBC с помощью стандартной утилиты `rsetup`
|
||||
|
||||
|
||||
## OpenWRT microSD Installation Guide
|
||||
IP-адрес по умолчанию:
|
||||
192.168.1.1
|
||||
Пароль root по умолчанию:
|
||||
password
|
||||
|
||||
|
||||
## EMMC Installation Guide
|
||||
1. Скачайте и распакуйте на свой компьютер следующие компоненты:
|
||||
1. **Утилиту RKDevTool** (версии 2.84 или новее) и **Драйвер DriverAssitant** от Rockchip.
|
||||
2. **Файл загрузчика (MiniLoader)**: обычно это файл вида `RK356x_SPL_Loader_v1.XX.bin`
|
||||
3. **Образ системы**: скачайте нужный дистрибутив с [официального сайта Radxa](https://docs.radxa.com/en/rock3/e25). Распакуйте архив, чтобы получить чистый файл
|
||||
4.
|
||||
```bash
|
||||
apt update && apt upgrade -y
|
||||
apt install -y docker.io
|
||||
```
|
||||
|
||||
2. Настройка автозапуска
|
||||
```bash
|
||||
systemctl enable docker
|
||||
systemctl start docker
|
||||
```
|
||||
|
||||
3. Установка Docker Compose
|
||||
```bash
|
||||
apt install -y docker-compose
|
||||
```
|
||||
|
||||
## Manual
|
||||
1. Просмотр списка образов
|
||||
```bash
|
||||
docker image ls
|
||||
```
|
||||
|
||||
2. Загрузка образа из Docker Hub
|
||||
```bash
|
||||
docker pull redis:7-alpine
|
||||
```
|
||||
|
||||
3. Запуск докер контейнера
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
@@ -1,98 +0,0 @@
|
||||
## Pinout
|
||||
[Raspberry PI 5 Documentation](https://www.raspberrypi.com/documentation/computers/raspberry-pi.html#flagship-series)
|
||||
![[Pasted image 20260901214038.png]]
|
||||
|
||||
|
||||
|
||||
[SPI Display Module Documentation](https://www.lcdwiki.com/3.2inch_SPI_Module_ILI9341_SKU:MSP3218)
|
||||
|
||||
### 3.2 TFT SPI Module Display ILI9341
|
||||
[SPI Display Module Documentation](https://www.lcdwiki.com/3.2inch_SPI_Module_ILI9341_SKU:MSP3218)
|
||||
|
||||
| Pin Number RP5 | Pin Number LCD | Pin Label | Description |
|
||||
| -------------------------------- | -------------- | --------- | -------------------------------------------------------------------------------------------- |
|
||||
| 2,4 - 5V / 1 - 3V3 | 1 | VCC | 5V/3.3V power input |
|
||||
| 6,9,14 | 2 | GND | Ground |
|
||||
| Pin 24 (**GPIO 8 / SPI0_CE0**) | 3 | CS | LCD chip select signal, low level enable |
|
||||
| Pin 11 (**GPIO 17**) | 4 | RESET | LCD reset signal, low level reset |
|
||||
| Pin 13 (**GPIO 27**) | 5 | DC/RS | LCD register / data selection signal,<br><br>high level: register, low level: data |
|
||||
| Pin 19 (**GPIO 10 / SPI0_MOSI**) | 6 | SDI(MOSI) | SPI bus write data signal |
|
||||
| Pin 23 (**GPIO 11 / SPI0_SCLK**) | 7 | SCK | SPI bus clock signal |
|
||||
| Pin 1 (**3.3V**) | 8 | LED | Backlight control, high level lighting,<br><br>if not controlled, connect 3.3V always bright |
|
||||
| Pin 21 (**GPIO 9 / SPI0_MISO**) | 9 | SDO(MISO) | SPI bus read data signal, if you do not need to the read function, you can not connect it |
|
||||
| | 10 | T_CLK | Touch SPI bus clock signal |
|
||||
| | 11 | T_CS | Touch screen chip select signal, low level enable |
|
||||
| | 12 | T_DIN | Touch SPI bus input |
|
||||
| | 13 | T_DO | Touch SPI bus output |
|
||||
| | 14 | T_IRQ | Touch screen interrupt signal, low level when touch is detected |
|
||||
|
||||
### RP5 Display module connection
|
||||
|
||||
```bash
|
||||
sudo apt update && sudo apt upgrade -y
|
||||
```
|
||||
При возникновении проблем с репозиториями
|
||||
```bash
|
||||
sudo sed -i '/bullseye-backports/d' /etc/apt/sources.list /etc/apt/sources.list.d/* 2>/dev/null
|
||||
```
|
||||
При возникновении необходимости **downgrade** выполнить принудительное обновлениt:
|
||||
```bash
|
||||
sudo apt dist-upgrade --allow-downgrades
|
||||
```
|
||||
Перезагрузить систему
|
||||
```bash
|
||||
sudo systemctl -r reboot
|
||||
```
|
||||
Удалить неиспользуемые пакеты:
|
||||
```bash
|
||||
sudo apt autoremove -y
|
||||
```
|
||||
|
||||
### Setting with rsetup
|
||||
Настройка SBC с помощью стандартной утилиты `rsetup`
|
||||
|
||||
|
||||
## OpenWRT microSD Installation Guide
|
||||
IP-адрес по умолчанию:
|
||||
192.168.1.1
|
||||
Пароль root по умолчанию:
|
||||
password
|
||||
|
||||
|
||||
## EMMC Installation Guide
|
||||
1. Скачайте и распакуйте на свой компьютер следующие компоненты:
|
||||
1. **Утилиту RKDevTool** (версии 2.84 или новее) и **Драйвер DriverAssitant** от Rockchip.
|
||||
2. **Файл загрузчика (MiniLoader)**: обычно это файл вида `RK356x_SPL_Loader_v1.XX.bin`
|
||||
3. **Образ системы**: скачайте нужный дистрибутив с [официального сайта Radxa](https://docs.radxa.com/en/rock3/e25). Распакуйте архив, чтобы получить чистый файл
|
||||
4.
|
||||
```bash
|
||||
apt update && apt upgrade -y
|
||||
apt install -y docker.io
|
||||
```
|
||||
|
||||
2. Настройка автозапуска
|
||||
```bash
|
||||
systemctl enable docker
|
||||
systemctl start docker
|
||||
```
|
||||
|
||||
3. Установка Docker Compose
|
||||
```bash
|
||||
apt install -y docker-compose
|
||||
```
|
||||
|
||||
## Manual
|
||||
1. Просмотр списка образов
|
||||
```bash
|
||||
docker image ls
|
||||
```
|
||||
|
||||
2. Загрузка образа из Docker Hub
|
||||
```bash
|
||||
docker pull redis:7-alpine
|
||||
```
|
||||
|
||||
3. Запуск докер контейнера
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
@@ -1,7 +1,5 @@
|
||||
#homelab #proxmox #Gitea #homeassistant #haos #adguard #wireguard
|
||||
|
||||
|
||||
|
||||
### Useful resources
|
||||
Про домашнюю лабораторию: [Prohomelab](https://prohomelab.com/)
|
||||
Proxmox Community Scripts [Proxmox Community-scripts](https://community-scripts.org/)
|
||||
@@ -0,0 +1,325 @@
|
||||
# Гайд: Разшаривание NFS на OMV и подключение к PVE + PBS
|
||||
|
||||
> Инфраструктура: OMV 8 (192.168.1.73), Proxmox VE, Proxmox Backup Server (отдельная VM/CT)
|
||||
|
||||
---
|
||||
|
||||
## 1. Настройка NFS-шары на OMV
|
||||
|
||||
### 1.1. Через веб-интерфейс
|
||||
|
||||
**Services → NFS → Settings**
|
||||
- [x] Enable NFS v4
|
||||
- [x] Enable async (повышает производительность)
|
||||
- Порт: 2049 (по умолчанию)
|
||||
- Apply
|
||||
|
||||
**Services → NFS → Shares → Add**
|
||||
|
||||
| Поле | Значение |
|
||||
|------|---------|
|
||||
| Shared folder | `/путь/к/шаре` (создать заранее через Storage → Shared Folders) |
|
||||
| Client | `192.168.1.0/24` — вся локальная сеть |
|
||||
| Options | `rw,subtree_check,no_root_squash` |
|
||||
|
||||
> **`no_root_squash` критически важен для LXC**: без него клиентский root внутри контейнера будет преобразован в nobody и не сможет писать в шару.
|
||||
|
||||
**Применить**: кнопка `Apply` (галочка) вверху.
|
||||
|
||||
### 1.2. Через CLI — проверка
|
||||
|
||||
```bash
|
||||
# Убедиться, что NFS запущен
|
||||
systemctl status nfs-server
|
||||
|
||||
# Просмотреть экспорты
|
||||
showmount -e localhost
|
||||
|
||||
# Или файл конфигурации
|
||||
cat /etc/exports
|
||||
|
||||
# Пример содержимого:
|
||||
# /srv/mergerfs/pool/backup 192.168.1.0/24(rw,subtree_check,no_root_squash,sync)
|
||||
```
|
||||
|
||||
> **Важно**: после каждого Apply в веб-UI OMV переписывает `/etc/exports`. Ручные правки там не переживают следующего Apply.
|
||||
|
||||
---
|
||||
|
||||
## 2. Подключение NFS к PVE и проброс в LXC
|
||||
|
||||
### 2.1. Создание PVE Storage для NFS
|
||||
|
||||
**Datacenter → Storage → Add → NFS**
|
||||
|
||||
| Поле | Значение |
|
||||
|------|---------|
|
||||
| ID | `nfs-omv` (произвольное имя) |
|
||||
| Server | `192.168.1.73` |
|
||||
| Export | Путь из `/etc/exports` на OMV (например `/export/backup`) |
|
||||
| Content | Выбрать, для чего это хранилище: `Container template`, `ISO image`, `VZDump backup file`, `Container`, `Snippets` |
|
||||
|
||||
**Через CLI** (альтернатива):
|
||||
|
||||
```bash
|
||||
pvesm add nfs nfs-omv --server 192.168.1.73 --path /mnt/pve/nfs-omv --export /export/backup
|
||||
```
|
||||
|
||||
**Проверка**:
|
||||
```bash
|
||||
pvesm status | grep nfs-omv
|
||||
# Должно показать storage nfs-omv online
|
||||
```
|
||||
|
||||
> **Ограничение**: PVE NFS storage не позволяет пробросить NFS-шару напрямую в LXC как bind mount. Для LXC нужен ручной mount на **хосте** PVE, потом `mp0` проброс.
|
||||
|
||||
---
|
||||
|
||||
### 2.2. Монтирование NFS на хосте PVE (для LXC)
|
||||
|
||||
Создаём точку монтирования на **хосте Proxmox**:
|
||||
|
||||
```bash
|
||||
# Создать директорию
|
||||
mkdir -p /mnt/nfs-omv-backup
|
||||
|
||||
# Монтировать (проверка)
|
||||
mount -t nfs 192.168.1.73:/export/backup /mnt/nfs-omv-backup -o rw,vers=4,hard,timeo=600,rsize=1048576,wsize=1048576,no_root_squash
|
||||
|
||||
# Проверить
|
||||
ls -la /mnt/nfs-omv-backup
|
||||
df -h /mnt/nfs-omv-backup
|
||||
```
|
||||
|
||||
> **Варианты опций**:
|
||||
> - `vers=4` — NFSv4 (безопаснее, быстрее)
|
||||
> - `vers=3` — NFSv3 (если v4 не работает)
|
||||
> - `hard` — процессы ждут восстановления соединения (vs `soft` — таймаут с ошибкой)
|
||||
> - `no_root_squash` — root на LXC может писать как root
|
||||
> - `rsize,wsize=1048576` — максимальный размер блока (1 МБ) для производительности
|
||||
|
||||
Если шару надо использовать как хранилище **CT templates** или **VZDump backup**, лучше сначала смонтировать, потом добавить через `pvesm add dir`:
|
||||
|
||||
```bash
|
||||
pvesm add dir nfs-omv-lxc --path /mnt/nfs-omv-backup --content vztmpl,backup,snippets
|
||||
```
|
||||
|
||||
#### 2.2.1. Автомонтирование (fstab)
|
||||
|
||||
Добавить в `/etc/fstab` на **хосте PVE**:
|
||||
|
||||
```bash
|
||||
# OMV NFS share — для проброса в LXC
|
||||
192.168.1.73:/export/backup /mnt/nfs-omv-backup nfs4 rw,hard,timeo=600,rsize=1048576,wsize=1048576,_netdev 0 0
|
||||
```
|
||||
|
||||
Проверка автомонтирования:
|
||||
```bash
|
||||
umount /mnt/nfs-omv-backup
|
||||
mount -a
|
||||
df -h | grep nfs
|
||||
```
|
||||
|
||||
> **`_netdev`** — система не пытается монтировать NFS до поднятия сети.
|
||||
|
||||
---
|
||||
|
||||
### 2.3. Проброс NFS-шары в LXC контейнер
|
||||
|
||||
Редактировать конфиг контейнера `/etc/pve/lxc/<CT_ID>.conf`:
|
||||
|
||||
```bash
|
||||
# Синтаксис: mp0: /путь/на/хосте,mp=/путь/в/LXC
|
||||
mp0: /mnt/nfs-omv-backup,mp=/mnt/backup
|
||||
```
|
||||
|
||||
**Через CLI**:
|
||||
```bash
|
||||
pct set <CT_ID> -mp0 /mnt/nfs-omv-backup,mp=/mnt/backup
|
||||
```
|
||||
|
||||
**В веб-интерфейсе PVE**: выбрать контейнер → Resources → Add → Mount Point.
|
||||
|
||||
**Проверка внутри LXC**:
|
||||
```bash
|
||||
pct enter <CT_ID>
|
||||
ls -la /mnt/backup
|
||||
touch /mnt/backup/test-root && rm /mnt/backup/test-root
|
||||
```
|
||||
|
||||
> Если `touch` выдаёт `Permission denied` — на OMV включено `root_squash` (сменить на `no_root_squash`).
|
||||
|
||||
---
|
||||
|
||||
### 2.4. Полный пример: LXC с backup-шара
|
||||
|
||||
```bash
|
||||
# Хост PVE: монтируем NFS
|
||||
mkdir -p /mnt/nfs-omv-backup
|
||||
echo '192.168.1.73:/export/backup /mnt/nfs-omv-backup nfs4 rw,hard,timeo=600,_netdev 0 0' >> /etc/fstab
|
||||
mount -a
|
||||
|
||||
# Пробрасываем в LXC (ID = 200)
|
||||
pct set 200 -mp0 /mnt/nfs-omv-backup,mp=/mnt/backup
|
||||
|
||||
# Внутри LXC: проверяем
|
||||
pct enter 200
|
||||
df -h | grep /mnt/backup
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Подключение NFS к PBS (Proxmox Backup Server)
|
||||
|
||||
### 3.1. Вариант A: Через веб-интерфейс PBS
|
||||
|
||||
**Server → Storage → Add → NFS**
|
||||
|
||||
| Поле | Значение |
|
||||
|------|---------|
|
||||
| Server | `192.168.1.73` (OMV) |
|
||||
| Export | `/export/backup` или путь из OMV |
|
||||
| Content | `backup` (основное) |
|
||||
| Node | выбрать текущую ноду PBS |
|
||||
| Enable | [x] |
|
||||
|
||||
**Важно для NFS на PBS:**
|
||||
- PBS хранит данные в собственной структуре (`.chunk` +datastore). **Не надо** указывать уже существующую директорию с файлами — PBS создаст свою иерархию.
|
||||
- NFS-шара НЕ может одновременно быть datastore PBS **и** использоваться для чего-то другого (в PBS своя структура хранения).
|
||||
|
||||
### 3.2. Вариант B: Через CLI на PBS
|
||||
|
||||
```bash
|
||||
pvesm add nfs pbs-nfs-omv \
|
||||
--server 192.168.1.73 \
|
||||
--export /export/pbs-datastore \
|
||||
--content backup
|
||||
```
|
||||
|
||||
**Проверка:**
|
||||
```bash
|
||||
pvesm status | grep pbs-nfs-omv
|
||||
# Должно показать status: available
|
||||
```
|
||||
|
||||
### 3.3. Настройка бэкапа на NFS datastore
|
||||
|
||||
После добавления storage в PBS:
|
||||
|
||||
1. **В PVE**: Datacenter → Backup → Add
|
||||
- Storage: выбрать `pbs-nfs-omv` (имя storage в PBS)
|
||||
- Schedule: по необходимости
|
||||
- Selection Mode: Include/Exclude
|
||||
- Compression: ZSTD (рекомендуется)
|
||||
- Encryption: по желанию
|
||||
- Enable: [x]
|
||||
|
||||
2. **Или ручной бэкап через CLI на PVE**:
|
||||
```bash
|
||||
# Полный бэкап VM 100 в PBS-storage
|
||||
vzdump 100 --mode snapshot --compress zstd --storage pbs-nfs-omv
|
||||
|
||||
# Или с указанием PBS напрямую
|
||||
proxmox-backup-client backup \
|
||||
--backup-id "vm-100" \
|
||||
--backup-type "qemu" \
|
||||
--repository 192.168.1.73:datastore:pbs-nfs-omv
|
||||
```
|
||||
|
||||
### 3.4. Особенности NFS c PBS
|
||||
|
||||
| Аспект | Комментарий |
|
||||
|--------|------------|
|
||||
| **Производительность** | NFS добавляет latency — для PBS лучше локальный диск (ZFS/SATA SSD) |
|
||||
| **Надёжность** | `soft,timeo=600` на PBS клиенте — при временной потере NFS бэкап упадёт с ошибкой, а не зависнет (`hard`) |
|
||||
| **GC (Garbage Collection)** | PBS периодически чистит expired backup snapshots. Убедиться, что NFS шара смонтирована **с опцией `rw`** |
|
||||
| **Verification** | PBS verifying jobs будут читать данные бэкапов — NFS latency замедлит. Нормально для периодических verify |
|
||||
| **Multi-node access** | PBS поддерживает кластер — NFS datastore доступен всем нодам |
|
||||
|
||||
**Рекомендуемый fstab на PBS** (с `soft` для бэкапов):
|
||||
|
||||
```bash
|
||||
192.168.1.73:/export/pbs-datastore /mnt/datastore nfs4 soft,timeo=600,retrans=2,rsize=1048576,wsize=1048576,_netdev 0 0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Проверка работоспособности
|
||||
|
||||
### 4.1. Доступность NFS
|
||||
|
||||
```bash
|
||||
# С любого узла (PVE, PBS, LXC)
|
||||
showmount -e 192.168.1.73
|
||||
|
||||
# Должен показать:
|
||||
# Export list for 192.168.1.73:
|
||||
# /export/backup 192.168.1.0/24
|
||||
```
|
||||
|
||||
### 4.2. Монтирование
|
||||
|
||||
```bash
|
||||
# На PVE хосте
|
||||
mount | grep nfs
|
||||
df -h | grep nfs
|
||||
```
|
||||
|
||||
### 4.3. Проброс в LXC
|
||||
|
||||
```bash
|
||||
pct enter <CT_ID>
|
||||
df -h | grep /mnt/backup # Должна быть видна шара
|
||||
touch /mnt/backup/test && rm /mnt/backup/test # root должен писать
|
||||
```
|
||||
|
||||
### 4.4. Бэкап через PBS
|
||||
|
||||
```bash
|
||||
# В PVE сделать пробный бэкап на PBS storage
|
||||
vzdump <VM_ID> --mode snapshot --compress zstd --storage pbs-nfs-omv
|
||||
# Убедиться, что exit code = 0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Типовые ошибки и решения
|
||||
|
||||
| Проблема | Причина | Решение |
|
||||
|----------|---------|---------|
|
||||
| `mount.nfs: access denied by server` | IP клиента не в списке Client NFS шары | Проверить OMV NFS Share → Client |
|
||||
| `Permission denied` при записи из LXC | `root_squash` на OMV | Включить `no_root_squash` в Options NFS шары |
|
||||
| LXC не стартует с mp0 | Путь на хосте не смонтирован | `mount -a`, проверить `fstab` |
|
||||
| PBS: datastore недоступен | NFS шара упала | `systemctl restart proxmox-backup` |
|
||||
| NFS mount зависает (`hung` | Потеря сети, опция `hard` | `umount -f /mnt/nfs-...` или `soft` опция |
|
||||
| LXC: mount inside container readonly | LXC не имеет `privileged: 1` | Использовать привилегированный контейнер или настроить device cgroup |
|
||||
|
||||
---
|
||||
|
||||
## 6. Сводная схема
|
||||
|
||||
```
|
||||
OMV (192.168.1.73)
|
||||
├── NFS export: /export/backup (no_root_squash, rw)
|
||||
│
|
||||
├── PVE host
|
||||
│ ├── mount /mnt/nfs-omv-backup
|
||||
│ ├── pvesm dir → templates/snippets
|
||||
│ └── mp0 проброс → LXC контейнер /mnt/backup
|
||||
│
|
||||
├── PBS node
|
||||
│ ├── mount /mnt/datastore (export: /export/pbs-datastore)
|
||||
│ ├── pvesm add nfs pbs-nfs-omv
|
||||
│ └── datastore для backup/verify/GC
|
||||
│
|
||||
└── Другие клиенты (RPi5, VM)
|
||||
└── mount -t nfs 192.168.1.73:/export/backup /mnt/shared
|
||||
```
|
||||
|
||||
## 7. Ограничения и рекомендации
|
||||
|
||||
- **NFS → PBS**: для продакшена используйте локальные SSD/NVMe, NFS добавляет latency и риск потери связи во время бэкапа
|
||||
- **no_root_squash**: безопасно только в доверенной подсети (192.168.1.0/24)
|
||||
- **Автомонтирование**: `_netdev` обязателен, иначе система может зависнуть при загрузке до поднятия сети
|
||||
- **PBS datastore**: выделите отдельную NFS-шару для PBS — не смешивайте с общими файлами
|
||||
- **Бэкап конфига OMV**: `omv-confbackup` сохраняет настройки NFS-шар
|
||||
Reference in New Issue
Block a user