add: minor.

This commit is contained in:
Sergei Erjemin 2025-03-25 16:04:39 +03:00
parent 0a90d4a444
commit c7a5241d33
5 changed files with 114 additions and 86 deletions

View File

@ -0,0 +1,28 @@
# Установка 3X-UI как под в K3s (Kubernetes)
3X-UI — это симпатичный веб-интерфейс для управления VPN-серверами, такими как WireGuard, Shadowsocks, Xray, V2Ray
и тому подобное. Он позволяет настраивать и мониторить VPN-соединения и клиентов через браузер. Мы будем запускать
его как контейнер (под) внутри K3s кластера на Orange Pi 5.
### Создание namespace (не обязательно)
Для удобства организации рекомендую создать отдельное пространство имён (`namespace`) для 3X-UI. Это как папка для
ресурсов, чтобы они не смешивались с другими приложениями.
Выполним в терминале:
```bash
sudo kubectl create namespace x-ui
```
Проверим, что пространство имён создано:
```bash
kubectl get namespaces
```
Увидим x-ui в списке:
```text
NAME STATUS AGE
... ... ...
... ... ...
x-ui Active 6s
```

View File

@ -23,8 +23,8 @@
│ └── cclient-shadowsocks--izmir/ # Локация Измир │ └── cclient-shadowsocks--izmir/ # Локация Измир
│ ├── config.yaml │ ├── config.yaml
│ └── deployment.yaml │ └── deployment.yaml
├── ... ├──
└── ... └──
``` ```
Создаем файл `config.yaml` для первого Shadowsocks-клиента (Москва): Создаем файл `config.yaml` для первого Shadowsocks-клиента (Москва):
@ -61,7 +61,7 @@ data:
- `data:` — данные конфигурации. - `data:` — данные конфигурации.
- `config.json:` — имя файла, в который будет записан конфиг. - `config.json:` — имя файла, в который будет записан конфиг.
- `|` — говорит, что дальше будет многострочный текст. - `|` — говорит, что дальше будет многострочный текст.
- `{...}` — Собственно JSON-конфигурация нашего Shadowsocks-клиента. - `{}` — Собственно JSON-конфигурация нашего Shadowsocks-клиента.
- `server` и `server_port` — адрес и порт нашего VPS. - `server` и `server_port` — адрес и порт нашего VPS.
- `local_address` и `local_port` — где будет SOCKS5 внутри кластера. - `local_address` и `local_port` — где будет SOCKS5 внутри кластера.
- `password` и `method` — пароль и метод шифрования. Метод шифрования `chacha20-ietf-poly1305` -- используется, - `password` и `method` — пароль и метод шифрования. Метод шифрования `chacha20-ietf-poly1305` -- используется,
@ -150,10 +150,10 @@ sudo k3s kubectl get pods -n kube-system
Увидим что-то типа: Увидим что-то типа:
```text ```text
NAME READY STATUS RESTARTS AGE NAME READY STATUS RESTARTS AGE
...
...
shadowsocks-client-moscow-54d64bf5f4-trb6p 1/1 Running 0 24m shadowsocks-client-moscow-54d64bf5f4-trb6p 1/1 Running 0 24m
...
``` ```
Можно проверь логи: Можно проверь логи:
@ -232,10 +232,10 @@ sudo k3s ctr images ls | grep shadowsocks
Увидим что-то типа: Увидим что-то типа:
```text ```text
...
docker.io/library/shadowsocks-with-tools:latest application/vnd.oci.image.manifest.v1+json sha256:... 22.5 MiB linux/arm64 io.cri-containerd.image=managed docker.io/library/shadowsocks-with-tools:latest application/vnd.oci.image.manifest.v1+json sha256: 22.5 MiB linux/arm64 io.cri-containerd.image=managed
...
...
``` ```
Теперь нам нужно передать образ контейнера на другие ноды кластера. Как это сделать есть заметка "[Развертывание Теперь нам нужно передать образ контейнера на другие ноды кластера. Как это сделать есть заметка "[Развертывание
@ -246,15 +246,15 @@ docker.io/library/shadowsocks-with-tools:latest application/vnd.oci.image.ma
новый образ. Закомментируем строку `image: shadowsocks/shadowsocks-libev:latest` и вставим две строки после неё новый образ. Закомментируем строку `image: shadowsocks/shadowsocks-libev:latest` и вставим две строки после неё
(обратите внимание на заметки): (обратите внимание на заметки):
```yaml ```yaml
...
spec: spec:
containers: containers:
- name: shadowsocks-client - name: shadowsocks-client
# image: shadowsocks/shadowsocks-libev:latest # image: shadowsocks/shadowsocks-libev:latest
image: shadowsocks-with-tools # Без :latest, чтобы k3s не "ходил" за контейнером в реестр (например, DockerHub) image: shadowsocks-with-tools # Без :latest, чтобы k3s не "ходил" за контейнером в реестр (например, DockerHub)
imagePullPolicy: Never # Только локальный образ, не тянуть из реестра imagePullPolicy: Never # Только локальный образ, не тянуть из реестра
...
...
``` ```
Уберём старый под из deployment и удалим сам под из k3s: Уберём старый под из deployment и удалим сам под из k3s:
@ -276,9 +276,9 @@ sudo k3s kubectl get pods -n kube-system
Увидим что-то типа: Увидим что-то типа:
```text ```text
NAME READY STATUS RESTARTS AGE NAME READY STATUS RESTARTS AGE
...
shadowsocks-client-moscow-6cf7b956b8-mtsg4 1/1 Running 0 9s shadowsocks-client-moscow-6cf7b956b8-mtsg4 1/1 Running 0 9s
...
``` ```
#### Проверка работы Shadowsocks #### Проверка работы Shadowsocks
@ -344,12 +344,12 @@ sudo k3s kubectl logs -n kube-system shadowsocks-client-moscow-<hash>
Для этого нужно изменить _local_address_ в конфиге shadowsocks-клиента `config.yaml`: Для этого нужно изменить _local_address_ в конфиге shadowsocks-клиента `config.yaml`:
```yaml ```yaml
...
"server_port": <ПОРТ>, "server_port": <ПОРТ>,
# "local_address": "127.0.0.1", # "local_address": "127.0.0.1",
"local_address": "0.0.0.0", "local_address": "0.0.0.0",
"local_port": 1081, "local_port": 1081,
...
``` ```
Применим конфиг: Применим конфиг:
@ -437,9 +437,9 @@ sudo k3s kubectl get service -n kube-system
Увидим что-то типа: Увидим что-то типа:
```text ```text
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
...
ss-moscow-service ClusterIP 10.43.236.81 <none> 1081/TCP,1081/UDP 5m5s ss-moscow-service ClusterIP 10.43.236.81 <none> 1081/TCP,1081/UDP 5m5s
...
``` ```
Теперь другие поды могут обращаться к `ss-moscow-service.kube-system.svc.cluster.local:1081` как к SOCKS5-прокси. Теперь другие поды могут обращаться к `ss-moscow-service.kube-system.svc.cluster.local:1081` как к SOCKS5-прокси.

View File

@ -20,7 +20,7 @@ IoT-устройства, edge-серверы и т.п.). Для кластер
контейнеры всё равно запускаются runtimeом (это containerd в k3s). И Docker все равно еще нужен для создания контейнеры всё равно запускаются runtimeом (это containerd в k3s). И Docker все равно еще нужен для создания
образов, и если при установке k3s не указать `--docker` то k3s будет использовать его как runtime._ образов, и если при установке k3s не указать `--docker` то k3s будет использовать его как runtime._
Но, есть у k3s и минус для конкретно моего случая -- распределенная база **etcd**, в которой хранится состояния Но, есть у k3s и минус для конкретно моего случая распределенная база **etcd**, в которой хранится состояния
кластера, нод и подов, в нем заменена SQLite. Это круто для маленьких компьютеров: экономно по памяти и другим ресурсам, кластера, нод и подов, в нем заменена SQLite. Это круто для маленьких компьютеров: экономно по памяти и другим ресурсам,
и, что главное, никак не сказывается на производительности (пока узлов меньше 50-80), но означает, что в кластере k3s и, что главное, никак не сказывается на производительности (пока узлов меньше 50-80), но означает, что в кластере k3s
может быть только одна мастер-нода. Если мастер-нода упадет, её некому будет заменить и весь кластер умрет. может быть только одна мастер-нода. Если мастер-нода упадет, её некому будет заменить и весь кластер умрет.
@ -29,7 +29,7 @@ IoT-устройства, edge-серверы и т.п.). Для кластер
### Важное предупреждение ### Важное предупреждение
k3s -- это не упрощенная мини-версия Kubernetes, здесь все компоненты упакованы в один бинарник, а значит намного k3s это не упрощенная мини-версия Kubernetes, здесь все компоненты упакованы в один бинарник, а значит намного
проще не только добавлять узлы, но и удалять их. Так что если что-то пойдет не так с настройкой узла, просто удалите проще не только добавлять узлы, но и удалять их. Так что если что-то пойдет не так с настройкой узла, просто удалите
и начните заново. Удаление k3s с узла: и начните заново. Удаление k3s с узла:
```bash ```bash
@ -51,14 +51,14 @@ curl -sfL https://get.k3s.io | sh -s - server --cluster-init --tls-san=192.168.1
``` ```
Здесь: Здесь:
* `server` -- значение по умолчанию, устанавливает узел k3s в режиме *мастер* (control-plane). В этом режиме узел * `server` значение по умолчанию, устанавливает узел k3s в режиме *мастер* (control-plane). В этом режиме узел
будет запускать все компоненты управления Kubernetes: API-сервер, контроллер-менеджер, планировщик (scheduler). будет запускать все компоненты управления Kubernetes: API-сервер, контроллер-менеджер, планировщик (scheduler).
Такой узел отвечает за управление кластером и может также выполнять рабочие нагрузки (workloads), если Такой узел отвечает за управление кластером и может также выполнять рабочие нагрузки (workloads), если
не настроены ограничения (taints). Если бы мы указали `agent` -- был бы установлен узел k3s в режиме *воркер*-узла. не настроены ограничения (taints). Если бы мы указали `agent` был бы установлен узел k3s в режиме *воркер*-узла.
* `--cluster-init` -- добавляет поддержку высокой доступности (HA -- High Availability) через встроенный `etcd`. Это * `--cluster-init` — добавляет поддержку высокой доступности (HA — High Availability) через встроенный `etcd`. Это
значит, что узел инициализирует новый кластер и готов к тому, чтобы другие мастер-узлы могли к нему подключиться значит, что узел инициализирует новый кластер и готов к тому, чтобы другие мастер-узлы могли к нему подключиться
(для создания HA-конфигурации). (для создания HA-конфигурации).
* `--tls-san=192.168.1.27` -- добавляет IP 192.168.1.27 в сертификаты API-сервера, чтобы другие узлы и клиенты * `--tls-san=192.168.1.27` добавляет IP 192.168.1.27 в сертификаты API-сервера, чтобы другие узлы и клиенты
могли обращаться к нему по этому адресу. могли обращаться к нему по этому адресу.
Проверим, что все k3s запущен: Проверим, что все k3s запущен:
@ -70,9 +70,9 @@ sudo service k3s status
```text ```text
● k3s.service - Lightweight Kubernetes ● k3s.service - Lightweight Kubernetes
Loaded: loaded (/etc/systemd/system/k3s.service; enabled; vendor preset: enabled) Loaded: loaded (/etc/systemd/system/k3s.service; enabled; vendor preset: enabled)
Active: active (running) since ... Active: active (running) since
...
...
``` ```
Посмотрим сколько нод в кластере: Посмотрим сколько нод в кластере:
@ -113,16 +113,16 @@ kube-system traefik-5d45fc8cc9-t5d58 1/1 Running 0
Представлены следующие поды: Представлены следующие поды:
1. `coredns` — это DNS-сервер для кластера. Он отвечает за разрешение имен внутри Kubernetes (например, чтобы поды 1. `coredns` — это DNS-сервер для кластера. Он отвечает за разрешение имен внутри Kubernetes (например, чтобы поды
могли обращаться друг к другу по именам сервисов вроде my-service.default.svc.cluster.local). могли обращаться друг к другу по именам сервисов вроде my-service.default.svc.cluster.local).
2. `helm-install-traefik-crd` -- это временный под (Job), который устанавливает Custom Resource Definitions (CRD) 2. `helm-install-traefik-crd` это временный под (Job), который устанавливает Custom Resource Definitions (CRD)
для *Traefik* — ingress-контроллера, встроенного в k3s. CRD нужны для управления ingress-ресурсами для *Traefik* — ingress-контроллера, встроенного в k3s. CRD нужны для управления ingress-ресурсами
(маршрутизацией HTTP/HTTPS). Этот под — одноразовая задача (Job), а не постоянный сервис. Он запустился, выполнил (маршрутизацией HTTP/HTTPS). Этот под — одноразовая задача (Job), а не постоянный сервис. Он запустился, выполнил
работу (установил CRD) и завершился. Статус "*Completed*" значит, что он больше не работает. работу (установил CRD) и завершился. Статус "*Completed*" значит, что он больше не работает.
3. `helm-install-traefik` -- ещё один Job, который устанавливает сам Traefik через Helm-чарт. Этот под развернул 3. `helm-install-traefik` ещё один Job, который устанавливает сам Traefik через Helm-чарт. Этот под развернул
основной Traefik-под и завершился. основной Traefik-под и завершился.
4. `local-path-provisioner` -- компонент для автоматического создания локальных Persistent Volumes (PV) на узлах. Он 4. `local-path-provisioner` компонент для автоматического создания локальных Persistent Volumes (PV) на узлах. Он
позволяет подам запрашивать хранилище (например, через PersistentVolumeClaim) без сложной настройки NFS или внешних позволяет подам запрашивать хранилище (например, через PersistentVolumeClaim) без сложной настройки NFS или внешних
хранилищ. В k3s это встроено для простоты. хранилищ. В k3s это встроено для простоты.
5. `metrics-server` -- собирает данные об использовании ресурсов (CPU, память) подов и узлов. Это нужно для команд 5. `metrics-server` собирает данные об использовании ресурсов (CPU, память) подов и узлов. Это нужно для команд
вроде `kubectl top` или для Horizontal Pod Autoscaler (HPA). Установку метрик можно отключить при запуске k3s вроде `kubectl top` или для Horizontal Pod Autoscaler (HPA). Установку метрик можно отключить при запуске k3s
флагом `--disable=metrics-server`. флагом `--disable=metrics-server`.
6. `svclb-traefik` - это под для балансировки нагрузки (Service Load Balancer) для Traefik. В k3s нет встроенного 6. `svclb-traefik` - это под для балансировки нагрузки (Service Load Balancer) для Traefik. В k3s нет встроенного
@ -131,14 +131,14 @@ kube-system traefik-5d45fc8cc9-t5d58 1/1 Running 0
* один для самой логики балансировки; * один для самой логики балансировки;
* другой для мониторинга или дополнительной функциональности (например, *keepalived* или аналога) и это зависит * другой для мониторинга или дополнительной функциональности (например, *keepalived* или аналога) и это зависит
от реализации в k3s. от реализации в k3s.
7. `traefik` -- сам Traefik, ingress-контроллер, который обрабатывает HTTP/HTTPS трафик кластера и маршрутизирует 7. `traefik` сам Traefik, ingress-контроллер, который обрабатывает HTTP/HTTPS трафик кластера и маршрутизирует
его к соответствующим подам (с динамической конфигурацией нашим) и сервисам по правилам Ingress. Traefik в k3s его к соответствующим подам (с динамической конфигурацией нашим) и сервисам по правилам Ingress. Traefik в k3s
установлен по умолчанию, но его можно отключить при запуске k3s флагом `--disable=traefik` (не будет ни *traefik*, установлен по умолчанию, но его можно отключить при запуске k3s флагом `--disable=traefik` (не будет ни *traefik*,
ни *svclb*, ни связанных *Helm Jobs*). ни *svclb*, ни связанных *Helm Jobs*).
Обратите внимание, что, например, под `coredns` получил имя `coredns-ccb96694c-tfjwj`. Имена подов (Pod Names) Обратите внимание, что, например, под `coredns` получил имя `coredns-ccb96694c-tfjwj`. Имена подов (Pod Names)
в Kubernetes генерируются автоматически на основе правил, чтобы каждый под в кластере имел уникальное имя. в Kubernetes генерируются автоматически на основе правил, чтобы каждый под в кластере имел уникальное имя.
Структура имени -- `<имя-приложения>-<хеш-ревизии>-<случайный-суффикс>`. Впрочем, `<хеш-ревизии>` может отсутствовать, Структура имени `<имя-приложения>-<хеш-ревизии>-<случайный-суффикс>`. Впрочем, `<хеш-ревизии>` может отсутствовать,
если под не имеет контроллера репликации (например, Job или CronJob). если под не имеет контроллера репликации (например, Job или CronJob).
Можно проверить, что API нашего узла (кластера) отвечает: Можно проверить, что API нашего узла (кластера) отвечает:
@ -168,7 +168,7 @@ Unauthorized JSON-ответ от API. Что-то вроде:
sudo cat /var/lib/rancher/k3s/server/node-token sudo cat /var/lib/rancher/k3s/server/node-token
``` ```
Вывод будет что-то вроде `K10...::server:longrandomstring`. Это и есть токен, который нужно будет использовать. Вывод будет что-то вроде `K10::server:longrandomstring`. Это и есть токен, который нужно будет использовать.
Теперь на втором Orange Pi (например, с IP 192.168.1.28) можно запустить второй мастер-узел (вставим токен Теперь на втором Orange Pi (например, с IP 192.168.1.28) можно запустить второй мастер-узел (вставим токен
из предыдущего шага): из предыдущего шага):
@ -176,9 +176,9 @@ sudo cat /var/lib/rancher/k3s/server/node-token
curl -sfL https://get.k3s.io | sh -s - server --server https://192.168.1.27:6443 --token <ТОКЕН> --tls-san=192.168.1.28 curl -sfL https://get.k3s.io | sh -s - server --server https://192.168.1.27:6443 --token <ТОКЕН> --tls-san=192.168.1.28
``` ```
Здесь ключи: Здесь ключи:
* `--server https://192.168.1.27:6443` -- указывает на API мастер-узла, чтобы наш новый узел мог подключиться к кластеру. * `--server https://192.168.1.27:6443` указывает на API мастер-узла, чтобы наш новый узел мог подключиться к кластеру.
* `--token` — токен аутентификации из предыдущего шага. * `--token` — токен аутентификации из предыдущего шага.
* `--tls-san=192.168.1.28` -- добавляет IP нашего второго мастера в сертификаты (для будущих подключений). * `--tls-san=192.168.1.28` добавляет IP нашего второго мастера в сертификаты (для будущих подключений).
Проверим какие теперь ноды в кластере: Проверим какие теперь ноды в кластере:
```bash ```bash
@ -210,7 +210,7 @@ kube-system svclb-traefik-4f8c2580-xzt5d 2/2 Running 0
kube-system traefik-5d45fc8cc9-t5d58 1/1 Running 0 2h 10.42.0.8 opi5plus-2 <none> <none> kube-system traefik-5d45fc8cc9-t5d58 1/1 Running 0 2h 10.42.0.8 opi5plus-2 <none> <none>
``` ```
Как видим, у нас появился еще один `svclb-traefik` на второй ноде. Это под -- Service Load Balancer (SLB) для Traefik. Как видим, у нас появился еще один `svclb-traefik` на второй ноде. Это под Service Load Balancer (SLB) для Traefik.
Он эмулирует облачный балансировщик нагрузки (типа AWS ELB), которого нет в локальном окружении вроде Orange Pi. Он эмулирует облачный балансировщик нагрузки (типа AWS ELB), которого нет в локальном окружении вроде Orange Pi.
SLB перенаправляет внешний трафик (например, на порты 80/443) к сервисам типа LoadBalancer внутри кластера. SLB перенаправляет внешний трафик (например, на порты 80/443) к сервисам типа LoadBalancer внутри кластера.
@ -226,7 +226,7 @@ curl -sfL https://get.k3s.io | sh -s - agent --server https://192.168.1.10:6443
``` ```
Здесь ключ: Здесь ключ:
* `agent` -- устанавливает узел в режиме воркера (worker). Это значит, что узел будет выполнять рабочие нагрузки * `agent` устанавливает узел в режиме воркера (worker). Это значит, что узел будет выполнять рабочие нагрузки
(поды), но не будет управлять кластером (без *control-plane*, *master* и на нем нет реплики *etcd*). (поды), но не будет управлять кластером (без *control-plane*, *master* и на нем нет реплики *etcd*).
Посмотрим на ноды (команда выполняется на одном из мастер-узлов): Посмотрим на ноды (команда выполняется на одном из мастер-узлов):
@ -301,7 +301,7 @@ traefik LoadBalancer 10.43.164.48 192.168.1.26,192.168.1.27,192.16
им не нужен доступ к etcd в реальном времени. им не нужен доступ к etcd в реальном времени.
В чем может быть смысл иметь два мастера? Это обеспечивает репликацию данных (второй хранит копию etcd), но не В чем может быть смысл иметь два мастера? Это обеспечивает репликацию данных (второй хранит копию etcd), но не
даёт отказоустойчивости -- когда один мастер упал, кластер становится неуправляемым (нет управления через kubectl), даёт отказоустойчивости когда один мастер упал, кластер становится неуправляемым (нет управления через kubectl),
рабочие нагрузки (поды) могут продолжать работать, пока жив хотя бы один узел, но новые изменения (развертывание рабочие нагрузки (поды) могут продолжать работать, пока жив хотя бы один узел, но новые изменения (развертывание
подов и обновления) невозможны. подов и обновления) невозможны.
@ -399,7 +399,7 @@ traefik-6c979cd89d-z6wwm 1/1 Running 0 2
``` ```
Хотя, в целом, кластер остается рабочим, и сам чинится при отключении и восстановлении узлов, но если отключается нода Хотя, в целом, кластер остается рабочим, и сам чинится при отключении и восстановлении узлов, но если отключается нода
на которой исполняется под с `coredns` -- то временно будет затруднен перезапуска и создание новых подов, а значит на которой исполняется под с `coredns` то временно будет затруднен перезапуска и создание новых подов, а значит
и "переезд" подов с погашенного узла, до восстановления `coredns` тоже будет замедлен. Кроме того, если сценарий и "переезд" подов с погашенного узла, до восстановления `coredns` тоже будет замедлен. Кроме того, если сценарий
приложения(ий) развернутых внутри k3s предполагает переподключение с использованием имен подов или обнаружение подов, приложения(ий) развернутых внутри k3s предполагает переподключение с использованием имен подов или обнаружение подов,
то это тоже перестанет работать. то это тоже перестанет работать.
@ -410,12 +410,12 @@ sudo k3s kubectl edit deployment coredns -n kube-system
``` ```
Здесь: Здесь:
* `kubectl edit` -- Открывает редактор (по умолчанию *vim*) для изменения ресурса Kubernetes напрямую в кластере. * `kubectl edit` Открывает редактор (по умолчанию *vim*) для изменения ресурса Kubernetes напрямую в кластере.
Вместо создания локального YAML-файла и применения его через `kubectl apply`, мы сразу редактируем "живой" конфиг. Вместо создания локального YAML-файла и применения его через `kubectl apply`, мы сразу редактируем "живой" конфиг.
* `deployment coredns` -- Указывает, что редактируем объект типа *deployment* с именем `coredns`. Deployment — это * `deployment coredns` Указывает, что редактируем объект типа *deployment* с именем `coredns`. Deployment — это
контроллер, который управляет набором подов (в данном случае coredns), обеспечивая их количество (реплики), контроллер, который управляет набором подов (в данном случае coredns), обеспечивая их количество (реплики),
перезапуск и обновления. перезапуск и обновления.
* `-n kube-system` -- Указывает пространство имён (namespace), где находится *coredns8. В k3s системные компоненты, * `-n kube-system` Указывает пространство имён (namespace), где находится *coredns8. В k3s системные компоненты,
к которым относится *coredns(, обычно живут в kube-system. к которым относится *coredns(, обычно живут в kube-system.
В открывшемся окне найдем строку `replicas: 1` и заменим её на `replicas: 2`. В открывшемся окне найдем строку `replicas: 1` и заменим её на `replicas: 2`.
@ -431,7 +431,7 @@ spec:
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
coredns-ccb96694c-n4qsp 0/1 ContainerCreating 0 5s <none> opi5plus-1 <none> <none> coredns-ccb96694c-n4qsp 0/1 ContainerCreating 0 5s <none> opi5plus-1 <none> <none>
coredns-ccb96694c-wzh96 1/1 Running 0 3h10m 10.42.1.8 opi5plus-3 <none> <none> coredns-ccb96694c-wzh96 1/1 Running 0 3h10m 10.42.1.8 opi5plus-3 <none> <none>
...
``` ```
А затем: А затем:
@ -439,7 +439,7 @@ coredns-ccb96694c-wzh96 1/1 Running 0
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
coredns-ccb96694c-n4qsp 1/1 Running 0 15s 10.42.2.6 opi5plus-1 <none> <none> coredns-ccb96694c-n4qsp 1/1 Running 0 15s 10.42.2.6 opi5plus-1 <none> <none>
coredns-ccb96694c-wzh96 1/1 Running 0 3h10m 10.42.1.8 opi5plus-3 <none> <none> coredns-ccb96694c-wzh96 1/1 Running 0 3h10m 10.42.1.8 opi5plus-3 <none> <none>
...
``` ```
**Как это будет работать?** Обе реплики `coredns` привязаны к сервису `kube-dns` в пространстве имён `kube-system`. **Как это будет работать?** Обе реплики `coredns` привязаны к сервису `kube-dns` в пространстве имён `kube-system`.
@ -455,10 +455,10 @@ sudo k3s kubectl get endpoints kube-dns -n kube-system
гасили при экспериментах с устойчивостью кластера: гасили при экспериментах с устойчивостью кластера:
```text ```text
NAME ENDPOINTS AGE NAME ENDPOINTS AGE
kube-dns 10.42.1.8:53,10.42.2.6:53,10.42.1.8:53 + 3 more... 5d23h kube-dns 10.42.1.8:53,10.42.2.6:53,10.42.1.8:53 + 3 more 5d23h
``` ```
Каждый под `coredns` -- самостоятельный DNS-сервер. Они не взаимодействуют друг с другом и не обмениваются данными. Это Каждый под `coredns` самостоятельный DNS-сервер. Они не взаимодействуют друг с другом и не обмениваются данными. Это
просто экземпляры одного и того же сервиса, работающие параллельно. Они независимы, получают данные из API Kubernetes просто экземпляры одного и того же сервиса, работающие параллельно. Они независимы, получают данные из API Kubernetes
и отвечают на запросы параллельно. В каждом поде кластера в качестве DNS настроен `kube-dns` (задаётся в файле и отвечают на запросы параллельно. В каждом поде кластера в качестве DNS настроен `kube-dns` (задаётся в файле
`/etc/resolv.conf` внутри пода). Когда под отправляет DNS-запрос, его получит `kube-dns` и перенаправит запрос `/etc/resolv.conf` внутри пода). Когда под отправляет DNS-запрос, его получит `kube-dns` и перенаправит запрос
@ -518,7 +518,7 @@ sudo apt install keepalived
sudo nano /etc/keepalived/keepalived.conf sudo nano /etc/keepalived/keepalived.conf
``` ```
На первом мастер-узле (хост -- `opi5plus-1`, IP -- `192.168.1.26`): На первом мастер-узле (хост `opi5plus-1`, IP — `192.168.1.26`):
```text ```text
vrrp_instance VI_1 { vrrp_instance VI_1 {
state MASTER # ЭТО ГЛАВНЫЙ ХОСТ. ПО УМОЛЧАНИЮ ТРАФИК С VIP БУДЕТ ПЕРЕНАПРАВЛЯТЬСЯ НА ЭТОТ ХОСТ state MASTER # ЭТО ГЛАВНЫЙ ХОСТ. ПО УМОЛЧАНИЮ ТРАФИК С VIP БУДЕТ ПЕРЕНАПРАВЛЯТЬСЯ НА ЭТОТ ХОСТ
@ -537,7 +537,7 @@ vrrp_instance VI_1 {
} }
``` ```
На втором мастер-узле (хост -- `opi5plus-2`, IP -- `192.168.1.27`): На втором мастер-узле (хост `opi5plus-2`, IP — `192.168.1.27`):
```text ```text
vrrp_instance VI_1 { vrrp_instance VI_1 {
state BACKUP # ЭТО ВТОРОЙ ХОСТ. ОН БУДЕТ ПОЛУЧАТЬ ТРАФИК С VIP, ЕСЛИ ГЛАВНЫЙ ХОСТ УПАДЕТ state BACKUP # ЭТО ВТОРОЙ ХОСТ. ОН БУДЕТ ПОЛУЧАТЬ ТРАФИК С VIP, ЕСЛИ ГЛАВНЫЙ ХОСТ УПАДЕТ
@ -556,7 +556,7 @@ vrrp_instance VI_1 {
} }
``` ```
И, наконец, на третьем мастер-узле (хост -- `opi5plus-3`, IP -- `192.168.1.28`): И, наконец, на третьем мастер-узле (хост `opi5plus-3`, IP — `192.168.1.28`):
```text ```text
vrrp_instance VI_1 { vrrp_instance VI_1 {
state BACKUP # ЭТО ТРЕТИЙ ХОСТ. ОН БУДЕТ ПОЛУЧАТЬ ТРАФИК С VIP, ЕСЛИ ГЛАВНЫЙ- И БЭКАП-ХОСТ УПАДЕТ state BACKUP # ЭТО ТРЕТИЙ ХОСТ. ОН БУДЕТ ПОЛУЧАТЬ ТРАФИК С VIP, ЕСЛИ ГЛАВНЫЙ- И БЭКАП-ХОСТ УПАДЕТ
@ -588,15 +588,15 @@ ip addr show
то увидим: то увидим:
```text ```text
...
...
2: enP4p65s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000 2: enP4p65s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether c0:74:2b:fd:42:3c brd ff:ff:ff:ff:ff:ff link/ether c0:74:2b:fd:42:3c brd ff:ff:ff:ff:ff:ff
inet 192.168.1.26/24 brd 192.168.1.255 scope global dynamic noprefixroute enP4p65s0 inet 192.168.1.26/24 brd 192.168.1.255 scope global dynamic noprefixroute enP4p65s0
valid_lft 68779sec preferred_lft 68779sec valid_lft 68779sec preferred_lft 68779sec
inet 192.168.1.200/32 scope global enP4p65s0 inet 192.168.1.200/32 scope global enP4p65s0
valid_lft forever preferred_lft forever valid_lft forever preferred_lft forever
...
``` ```
Обратите внимание на виртуальный IP-адрес `192.168.1.200` находится в другой подсети (CIDR) и имеет маску `/32` (то Обратите внимание на виртуальный IP-адрес `192.168.1.200` находится в другой подсети (CIDR) и имеет маску `/32` (то
есть с маской подсети `255.255.255.255`). Это "точечная" подсеть, содержащая только один адрес, не привязан к основной есть с маской подсети `255.255.255.255`). Это "точечная" подсеть, содержащая только один адрес, не привязан к основной
@ -652,9 +652,9 @@ vps-sw-eye Ready <none> 35m v1.31.6+k3s1
Таким образом, для управления удаленным узлом нужно чтобы он имел локальный IP-адрес в домашней сети, а не внешний. Таким образом, для управления удаленным узлом нужно чтобы он имел локальный IP-адрес в домашней сети, а не внешний.
SSH-тоннель с помощью `autossh` и упаковкой UDP-трафика в TCP через `socat` не сработает (а я надеялся). Таким образом SSH-тоннель с помощью `autossh` и упаковкой UDP-трафика в TCP через `socat` не сработает (а я надеялся). Таким образом
"пробросить" Flannel для полноценного подключения удаленного k3s-узла -- VPN-туннель между каждой мастер-нодой на "пробросить" Flannel для полноценного подключения удаленного k3s-узла VPN-туннель между каждой мастер-нодой на
удаленный узел. Это вполне рабочия вариант, если удаленные узлы -- полноценные и произвольные хосты. Но в моём удаленный узел. Это вполне рабочия вариант, если удаленные узлы полноценные и произвольные хосты. Но в моём
случае удаленный узел -- хост на 1 ядро и 1 ГБ ОЗУ. К тому же он на платформе x86_64, а не ARM, а значит ради одного случае удаленный узел хост на 1 ядро и 1 ГБ ОЗУ. К тому же он на платформе x86_64, а не ARM, а значит ради одного
узла не стоит заморачиваться с VPN. узла не стоит заморачиваться с VPN.
Другим вариантом является подключение внутри самих подов на удаленном узле к необходимым сервисам напрямую. Но таким Другим вариантом является подключение внутри самих подов на удаленном узле к необходимым сервисам напрямую. Но таким

View File

@ -9,14 +9,14 @@ SSD NVMe, и для eMMC. Но, как и всякая магия, она мож
## Установка накопителей ## Установка накопителей
Выключим Orange Pi 5 Plus и установим в него eMMC-носитель... Выключим Orange Pi 5 Plus и установим в него eMMC-носитель
| Фото до и после установки eMMC. Внимание, устанавливайте до щелчка с обоих сторон! | | Фото до и после установки eMMC. Внимание, устанавливайте до щелчка с обоих сторон! |
|:-------------------------------------------------------------------------------------| |:-------------------------------------------------------------------------------------|
| ![Orange Pi 5 Plus без eMMC](../images/orange-pi--photo--without-emmc.webp) | | ![Orange Pi 5 Plus без eMMC](../images/orange-pi--photo--without-emmc.webp) |
| ![Orange Pi 5 Plus c установленным eMMC](../images/orange-pi--photo--with-emmc.webp) | | ![Orange Pi 5 Plus c установленным eMMC](../images/orange-pi--photo--with-emmc.webp) |
...и/или SSD-накопитель NVMe и/или SSD-накопитель NVMe
| Фото до и после установки NVMe. Более просто в установке. Для надёжности закрепить винтиком. | | Фото до и после установки NVMe. Более просто в установке. Для надёжности закрепить винтиком. |
|:---------------------------------------------------------------------------------------------------| |:---------------------------------------------------------------------------------------------------|

View File

@ -61,8 +61,8 @@ ls -al /tmp/boot-backup.tar.gz
Теперь нам нужно скопировать его на какой-нибудь внешний хост (или носитель, но я буду копировать на хост другого Теперь нам нужно скопировать его на какой-нибудь внешний хост (или носитель, но я буду копировать на хост другого
компьютера). Если что-то пойдёт не так (например, после обновления ядра система не загрузится) можно будет восстановить компьютера). Если что-то пойдёт не так (например, после обновления ядра система не загрузится) можно будет восстановить
файлы из этого архива. Мой Orange Pi 5 Plus, на котором я буду компилировать ядро имеет хост -- `opi5plus-3` (замени файлы из этого архива. Мой Orange Pi 5 Plus, на котором я буду компилировать ядро имеет хост `opi5plus-3` (замени
на свой хост), а имя пользователя от которого я работаю -- `opi` (замени на свой). На рабочем компьютере, с которого на свой хост), а имя пользователя от которого я работаю `opi` (замени на свой). На рабочем компьютере, с которого
я захожу по SSH на Orange, у меня есть папка `~/backup/` (`mkdir -p ~/backup`). Скачиваю в неё архив: я захожу по SSH на Orange, у меня есть папка `~/backup/` (`mkdir -p ~/backup`). Скачиваю в неё архив:
```bash ```bash
scp opi@opi5plus-3.local:/tmp/boot-backup.tar.gz ~/backup/opi5plus-3-boot-backup.tar.gz scp opi@opi5plus-3.local:/tmp/boot-backup.tar.gz ~/backup/opi5plus-3-boot-backup.tar.gz
@ -93,38 +93,38 @@ drwxr-xr-x 3 root root 4096 ноя 21 09:55 dtb-6.1.43-rockchip-rk3588/
``` ```
Ключевые файлы: Ключевые файлы:
* `Image` -- собственно ядро Linux (в данном случае версия 6.1.43 для Rockchip RK3588). При старте компьютера загрузчик * `Image` собственно ядро Linux (в данном случае версия 6.1.43 для Rockchip RK3588). При старте компьютера загрузчик
U-Boot загрузит его в память и передаст ему управление. Без этого файла система не запустится. U-Boot загрузит его в память и передаст ему управление. Без этого файла система не запустится.
* `vmlinuz-6.1.43-rockchip-rk3588` -- копия ядра (в системе уже есть резервная копия). * `vmlinuz-6.1.43-rockchip-rk3588` копия ядра (в системе уже есть резервная копия).
* `dtb/` -- Каталог файлами **Device Tree Blob** (`DTB`). Это бинарные файлы, описывающие аппаратное обеспечение устройства. * `dtb/` Каталог файлами **Device Tree Blob** (`DTB`). Это бинарные файлы, описывающие аппаратное обеспечение устройства.
Для Orange Pi 5 Plus используется файл вроде `rk3588-orangepi-5-plus.dtb` (находится в подкаталоге `dtb/rockchip/`). Для Orange Pi 5 Plus используется файл вроде `rk3588-orangepi-5-plus.dtb` (находится в подкаталоге `dtb/rockchip/`).
DTB передаётся ядру, чтобы оно знало, как работать с процессором (количество ядер, частоты), памятью (RAM, её объём DTB передаётся ядру, чтобы оно знало, как работать с процессором (количество ядер, частоты), памятью (RAM, её объём
и адреса), периферией (UART, I2C, SPI, Ethernet, USB, GPIO, прерывания и тому подобное). На ARM-устройствах и адреса), периферией (UART, I2C, SPI, Ethernet, USB, GPIO, прерывания и тому подобное). На ARM-устройствах
(в отличие от x86, где есть ACPI) нет стандартного способа обнаружения оборудования. DTB решает эту проблему, (в отличие от x86, где есть ACPI) нет стандартного способа обнаружения оборудования. DTB решает эту проблему,
предоставляя ядру "карту" железа. U-Boot загружает DTB из `/boot/dtb/ `и передаёт его ядру при старте. Кстати, предоставляя ядру "карту" железа. U-Boot загружает DTB из `/boot/dtb/ `и передаёт его ядру при старте. Кстати,
в подкаталоге dtb/rockchip/ есть `overlay/` -- это дополнительные файлы, которые могут использоваться для добавления в подкаталоге dtb/rockchip/ есть `overlay/` это дополнительные файлы, которые могут использоваться для добавления
и/или изменения функциональности устройства. Например, можно добавить поддержку новых периферийных устройств (камеру, и/или изменения функциональности устройства. Например, можно добавить поддержку новых периферийных устройств (камеру,
дисплей и т.п.). дисплей и т.п.).
* `uInitrd` -- Начальный RAM-диск (initrd), адаптированный для U-Boot. Содержит модули и скрипты, необходимые для * `uInitrd` Начальный RAM-диск (initrd), адаптированный для U-Boot. Содержит модули и скрипты, необходимые для
пред-загрузки (выбор накопителя, монтирование корневой файловой системы и т.п.). Если он повреждён или несовместим пред-загрузки (выбор накопителя, монтирование корневой файловой системы и т.п.). Если он повреждён или несовместим
с ядром, загрузка может упасть на этапе инициализации. с ядром, загрузка может упасть на этапе инициализации.
* `orangepiEnv.txt` -- Конфигурационный файл для U-Boot. Здесь задаются параметры загрузки, такие как путь расположение * `orangepiEnv.txt` Конфигурационный файл для U-Boot. Здесь задаются параметры загрузки, такие как путь расположение
дерева DTB, UUID корневой файловой системы, тип файловой системы... Без правильных настроек в этом файле U-Boot не дерева DTB, UUID корневой файловой системы, тип файловой системы Без правильных настроек в этом файле U-Boot не
найдёт нужные для загрузки файлы. найдёт нужные для загрузки файлы.
* `boot.scr` -- Скрипт загрузки для U-Boot. Содержит команды для загрузки ядра, initrd и DTB. U-Boot выполняет его * `boot.scr` Скрипт загрузки для U-Boot. Содержит команды для загрузки ядра, initrd и DTB. U-Boot выполняет его
при старте системы. Этот файл создаётся из `boot.cmd` с помощью утилиты `mkimage`. Если он повреждён или отсутствует, при старте системы. Этот файл создаётся из `boot.cmd` с помощью утилиты `mkimage`. Если он повреждён или отсутствует,
U-Boot не сможет загрузить систему. U-Boot не сможет загрузить систему.
* `dtb-6.1.43-rockchip-rk3588/` -- Копия каталога `dtb/`, обычно появляется, когда ядро устанавливается или обновляется * `dtb-6.1.43-rockchip-rk3588/` Копия каталога `dtb/`, обычно появляется, когда ядро устанавливается или обновляется
через пакетный менеджер (например, `apt`). Она привязана к конкретной версии ядра — в данном случае через пакетный менеджер (например, `apt`). Она привязана к конкретной версии ядра — в данном случае
`6.1.43-rockchip-rk3588`, для того, чтобы: Хранить DTB-файлы, соответствующие этой версии ядра и избегать конфликты `6.1.43-rockchip-rk3588`, для того, чтобы: Хранить DTB-файлы, соответствующие этой версии ядра и избегать конфликты
между DTB от разных версий ядра (если используется несколько ядер на одной системе). между DTB от разных версий ядра (если используется несколько ядер на одной системе).
Менее критичные, но полезные файлы: Менее критичные, но полезные файлы:
* `config-6.1.43-rockchip-rk3588` -- Конфигурация ядра, использованная при его сборке (он нам понадобится, чтобы * `config-6.1.43-rockchip-rk3588` Конфигурация ядра, использованная при его сборке (он нам понадобится, чтобы
пересобрать ядро с iSCSI). пересобрать ядро с iSCSI).
* `System.map-6.1.43-rockchip-rk3588` -- Карта меток (символов) ядра, полезна для отладки. * `System.map-6.1.43-rockchip-rk3588` Карта меток (символов) ядра, полезна для отладки.
* `initrd.img-6.1.43-rockchip-rk3588` -- Обычный initrd, из которого генерируется uInitrd. * `initrd.img-6.1.43-rockchip-rk3588` Обычный initrd, из которого генерируется uInitrd.
* `boot.bmp` и `logo.bmp` -- Изображения для экрана загрузки (не влияют на работу системы). * `boot.bmp` и `logo.bmp` Изображения для экрана загрузки (не влияют на работу системы).
## Устанавливать инструменты для сборки ядра ## Устанавливать инструменты для сборки ядра
@ -136,13 +136,13 @@ sudo apt install -y build-essential bc flex bison libssl-dev libncurses-dev git
Нам понадобятся следующие пакеты: Нам понадобятся следующие пакеты:
* `build-essential` -- Включает `gcc` (для ARM64, make и другие базовые инструменты компиляции. * `build-essential` Включает `gcc` (для ARM64, make и другие базовые инструменты компиляции.
* `bc` -- Утилита для точных математических вычислений в командной строке. Используется для вычислений в скриптах сборки ядра. * `bc` Утилита для точных математических вычислений в командной строке. Используется для вычислений в скриптах сборки ядра.
* `flex` и `bison` -- генератор лексических анализаторов и парсер. Нужны для обработки конфигурационных файлов ядра. * `flex` и `bison` генератор лексических анализаторов и парсер. Нужны для обработки конфигурационных файлов ядра.
* `libssl-dev` -- Для поддержки криптографии в ядре и `OpenSSL`. * `libssl-dev` Для поддержки криптографии в ядре и `OpenSSL`.
* `libncurses-dev` -- Библиотека для создания текстовых интерфейсов в терминале. Необходимо для работы интерфейса * `libncurses-dev` Библиотека для создания текстовых интерфейсов в терминале. Необходимо для работы интерфейса
`menuconfig` при настройке параметров ядра. `menuconfig` при настройке параметров ядра.
* `git` -- Для клонирования исходного кода ядра из репозитория Xunlong. * `git` Для клонирования исходного кода ядра из репозитория Xunlong.
> **Примечание:** Если вы хотите собрать ядро на x86-системе, установите кросс-компилятор `gcc-aarch64-linux-gnu` (`sudo apt install gcc-aarch64-linux-gnu`) и используйте его вместо обычного gcc в командах сборки. Он позволит собрать ядро для ARM64-архитектуры на x86-системе. > **Примечание:** Если вы хотите собрать ядро на x86-системе, установите кросс-компилятор `gcc-aarch64-linux-gnu` (`sudo apt install gcc-aarch64-linux-gnu`) и используйте его вместо обычного gcc в командах сборки. Он позволит собрать ядро для ARM64-архитектуры на x86-системе.
@ -314,10 +314,10 @@ sudo reboot
Если вы все делали правильно, то такого быть не должно. Но, тем не менее, если загрузка не произошла, то это может Если вы все делали правильно, то такого быть не должно. Но, тем не менее, если загрузка не произошла, то это может
выглядеть двумя способами: выглядеть двумя способами:
* Система зависает на этапе загрузки: синенький огонек на Orange Pi не загорается и Ethernet тоже не мигает -- * Система зависает на этапе загрузки: синенький огонек на Orange Pi не загорается и Ethernet тоже не мигает
_вы неправильно собрали ядро или испортили загрузчик_. _вы неправильно собрали ядро или испортили загрузчик.
* Система, вроде как, грузится, но все никак... огоньки весело мигают, но не получается подключиться ни по SSH, ни * Система, вроде как, грузится, но все никак огоньки весело мигают, но не получается подключиться ни по SSH, ни
другим способом, пинги на IP-адрес не проходят -- _вы забыли подключить модули, накосячили с конфигом или с `.dtb`_. другим способом, пинги на IP-адрес не проходят вы забыли подключить модули, накосячили с конфигом или с `.dtb`_.
Чтобы починить, загружайтесь с MicroSD-карты (не забудьте отключить питание перед тем как вставить MicroSD-карту). Чтобы починить, загружайтесь с MicroSD-карты (не забудьте отключить питание перед тем как вставить MicroSD-карту).
Затем смонтируйте, в зависимости где у вас система, eMMC: Затем смонтируйте, в зависимости где у вас система, eMMC:
@ -327,7 +327,7 @@ mount /dev/mmcblk2p1 /mnt/emmc
cd /mnt/emmc cd /mnt/emmc
``` ```
...или NVMe: или NVMe:
```bash ```bash
mkdir -p /mnt/nvme mkdir -p /mnt/nvme
mount /dev/nvme0n1p1 /mnt/nvme mount /dev/nvme0n1p1 /mnt/nvme