mod: Перенос контейнера Docker в k3s..

This commit is contained in:
Sergei Erjemin 2025-04-24 17:55:15 +03:00
parent dc04658e70
commit 138bb728fc

View File

@ -8,17 +8,73 @@ Let's Encrypt. Мне показалось, что зависимость от
[перекомпилировать ядро](../raspberry-and-orange-pi/opi5plus-rebuilding-linux-kernel-for-iscsi.md)
).
Я решил задокументировать процесс переноса контейнера из Docker в k3s, тем более Gitea был не единственный контейнер,
который нужно было перенести. Возможно мой опыт вам тоже пригодится.
Я решил задокументировать процесс переноса контейнера из Docker в k3s, тем более Gitea не единственный контейнер,
который мне нужно было перенести. Возможно мой опыт вам тоже пригодится.
## Перенос данных Docker-контейнера Gitea на узел k3s
Останавливаем докер
На старом хосте Gitea работал в контейнере через docker-compose. Вот её `docker-compose.yaml`:
```yaml
version: '3'
services:
gitea:
image: gitea/gitea:latest
container_name: gitea
restart: always
environment:
- USER_UID=1000
- USER_GID=1000
ports:
- "3000:3000"
- "2222:22"
volumes:
- /home/orangepi/docker-data/gitea-new:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
```
Архивируем данные gitea
Как видим, в контейнере Gitea (с именем `gitea-new`) в качестве базы используется SQLite. Файл базы, конфиги
и остальные данные хранятся в каталоге `/data` контейнера, смонтированного во внешний том (volume)
`/home/orangepi/docker-data/gitea-new`. Нам нужно будет перенести все эти данные в кластер k3s, в блочное
хранилище Longhorn. Для этого создадим архив с данными gitea и перенесем его на один из узлов k3s.
Переносим данные на узел k3s (в моем случае это opi5plus-1) по SCP:
Чтобы забрать данные из контейнера, нужно остановить его (на хосте с Docker):
```bash
docker stop docker stop gitea-new
```
### Архивируем данные gitea
Находясь на хосте в Docker, cоздадим архив с данными gitea:
```bash
tar -czf /home/orangepi/gitea-data.tar.gz -C /home/orangepi/docker-data/gitea-new .
```
Проверим, что архив создался:
```bash
ls -alh /home/orangepi/gitea-data.tar.gz
```
Увидим что-то вроде:
```text
-rw-r--r-- 1 orangepi orangepi 147M Apr 16 18:10 /home/orangepi/gitea-data.tar.gz
```
### Перенесём данные на узел k3s по SCP:
Находясь на хосте с Docker, перенесем архив с данными gitea на узел k3s (в моем случае это `opi5plus-1`):
```bash
scp /home/orangepi/gitea-data.tar.gz opi@opi5plus-1:/home/opi/
```
Перейдем на узел `opi5plus-1` k3s и проверим, что архив с данными gitea там есть:
```bash
ls -alh ~/gitea-data.tar.gz
```
Увидим что-то вроде:
```text
-rw-r--r-- 1 opi opi 147M Apr 16 18:12 /home/opi/gitea-data.tar.gz
```
## Подготовка узла k3s
@ -243,11 +299,11 @@ spec:
containers: # определяем контейнеры, которые будут запущены в поде
- name: gitea # имя контейнера `gitea`
image: gitea/gitea:latest # образ
env: # переменные окружения
- name: USER_UID # идентификатор пользователя
value: "1000"
- name: USER_GID # идентификатор группы
value: "1000"
# env: # переменные окружения
# - name: USER_UID # идентификатор пользователя
# value: "1000"
# - name: USER_GID # идентификатор группы
# value: "1000"
ports: # определяем порты, которые будут открыты в контейнере
- containerPort: 3000 # порт 3000
name: http # будет именоваться 'http' (используется в Gitea для веб-интерфейса)
@ -408,9 +464,11 @@ spec:
- name: http
port: 80
targetPort: 3000
protocol: TCP
#- name: ssh
# port: 22
# targetPort: 222
# targetPort: 22
# protocol: TCP
```
Объяснение:
@ -453,7 +511,7 @@ permanent: true — возвращает 301 (постоянный редире
sudo kubectl apply -f ~/k3s/gitea/https-redirect-middleware.yaml
```
### Подключаем сертификат Let's Encrypt
У нас уже настроена выдача сертификатов Lets Encrypt в подах cert-managerб cert-manager-cainjector и
cert-manager-webhook, в пронстве имен cert-manager. Это нестандартный способ (см. [заметку о cert-manager](k3s-cert-manager.md)).
@ -595,7 +653,7 @@ Events:
up to date and has not expired` в `Message`.
## Создание IngressRoute для доступа к Gitea
Создать IngressRoute для HTTPS
Настроим IngressRoute для маршрутизации git.cube2.ru через HTTPS с Lets Encrypt и редиректом HTTP.
@ -613,7 +671,7 @@ spec:
entryPoints:
- web
routes:
- match: Host(`git.cube2.ru`)
- match: Host("git.cube2.ru")
kind: Rule
services:
- name: gitea
@ -630,7 +688,7 @@ spec:
entryPoints:
- websecure
routes:
- match: Host(`git.cube2.ru`)
- match: Host("git.cube2.ru")
kind: Rule
services:
- name: gitea
@ -641,29 +699,270 @@ spec:
# certResolver: letsencrypt
```
Объяснение:
Первая IngressRoute (gitea-http):
entryPoints: web — слушает порт 80 (HTTP).
match: Host(git.cube2.ru) — обрабатывает запросы к git.cube2.ru.
services: gitea — направляет трафик в Service gitea (порт 80 → 3000).
middlewares: https-redirect — редирект на HTTPS.
Вторая IngressRoute (gitea-https):
entryPoints: websecure — слушает порт 443 (HTTPS).
tls: certResolver: letsencrypt — включает Lets Encrypt для автоматического получения сертификата.
Трафик идёт в тот же Service gitea.
* Первая IngressRoute (`gitea-http`):
* `entryPoints: web` — слушает порт 80 (HTTP).
* `match: Host("git.cube2.ru")` — обрабатывает запросы к git.cube2.ru.
* `services: gitea` — направляет трафик в Service gitea (порт 80 → 3000).
* `middlewares: https-redirect` — редирект на HTTPS.
* Вторая IngressRoute (gitea-https):
* `entryPoints: websecure` — слушает порт 443 (HTTPS).
* `tls: certResolver: gitea-tls` — включает Lets Encrypt для автоматического получения сертификата через
cert-manager в секрет gitea-tls. Трафик идёт в тот же Service gitea.
Применим манифест:
```bash
sudo kubectl apply -f ~/k3s/gitea/gitea-ingressroute.yaml
```
Все долно работать. Проверим, что IngressRoute создан:
## Подключение SSH к Gitea (опционально)
Кроме http-доступа и идентификации по логину, паролю и токенами, к к Gitea можно подключаться по SSH ([git://]). Выше
мы не настраивали SSH, так как Gitea я использовал как демонстрацию переноса Docker-контейнера в под K3s.
Порт 22 (SSH) на узлах K3s уже занят, поэтому нам нужно перенастроить его на другой порт. Например, 2222.
Для настройки SSH нужно:
- Обновить Service для Gitea, чтобы он включал порт 22 (SSH).
- Настроить Traefik для порта 2222
- Добавить IngressRouteTCP для Gitea (маршрутизация порта 22 изнутри пода а 2222 порт снаружи).
- Изменить Traefik Service для доступа по порту 2222
### Обновим Service для Gitea
Можно удалить временный под:
Вносим изменения в `~/k3s/gitea/gitea-service.yaml`, добавив в конце (этот блок был закомментирован):
```yaml
- port: 22 # SSH, добавить
targetPort: 22
protocol: TCP
name: ssh
```
Применим изменения:
```bash
kubectl apply -f ~/k3s/gitea/gitea-service.yaml
````
Теперь Traefik сможет маршрутизировать SSH-трафик. Проверим, что сервис обновился:
```bash
sudo kubectl get svc -n gitea -o wide
```
Увидим что теперь в сервисе gitea есть порт 22:
```text
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
gitea ClusterIP 10.43.211.8 <none> 80/TCP,22/TCP 5h app=gitea
```
### Настроим Traefik для порта 2222
Traefik должен слушать порт 2222. У меня Traefik настроен через `Helm values` в `HelmChartConfig`, через дополнительные
параметры `additionalArguments` . Чтобы Traefik начал слушать порт 2222, добавим новый entryPoint с именем `ssh` в
`~/k3s/traefik/traefik-config.yaml`. Добавим в конец файла: `- --entrypoints.ssh.address=:2222`. Полностью манифест
у меня выглядит так:
```yaml
aapiVersion: helm.cattle.io/v1
kind: HelmChartConfig
metadata:
name: traefik
namespace: kube-system
spec:
valuesContent: |-
additionalArguments:
- --entrypoints.web-custom.address=:2055 # Слушаем HTTP на 2055 (для web-панели 3x-ui)
- --entrypoints.ssh.address=:2222 # Слушаем TCP на 2222 (для SSH)
- --log.level=DEBUG
```
Применим изменения и перезапустим Traefik чтобы изменения вступили в силу:
```bash
sudo kubectl apply -f ~/k3s/traefik/traefik-config.yaml
sudo kubectl rollout restart deployment -n kube-system traefik
```
Проверим, что Traefik перезапустился:
```bash
sudo kubectl get pod -n kube-system -o wide
```
Увидим что-то вроде (время жизни пода Traefik небольшое, так как он недавно перезапустился):
```text
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
...
...
traefik-6b96fd9d85-8txb9 1/1 Running 0 119s 10.42.0.93 opi5plus-2 <none> <none>
...
```
### Добавим IngressRouteTCP для Gitea
Чтобы маршрутизировать трафик с порта 2222 на Service Gitea (порт 22), создадим манифест IngressRouteTCP:
```bash
nano ~/k3s/gitea/gitea-ingressroute-tcp.yaml
```
И вставим в него следующее содержимое:
```yaml
# IngressRouteTCP для маршрутизации SSH через Traefik
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRouteTCP
metadata:
name: gitea-ssh
namespace: gitea
spec:
entryPoints:
- ssh # Соответствует entryPoint в Traefik
routes:
- match: HostSNI(`*`) # Для TCP без SNI
services:
- name: gitea
port: 22 # Порт Service
```
Применим манифест:
```bash
sudo kubectl apply -f ~/k3s/gitea/gitea-ingressroute-tcp.yaml
```
Проверим, что IngressRouteTCP создан:
```bash
sudo kubectl get ingressroutetcp -n gitea
```
Увидим что-то вроде:
```text
NAME AGE
gitea-ssh 16s
```
### Изменим Traefik Service для доступа к SSH по порту 2222 через балансировщик
Если у вас уже есть манифест Traefik Service (например `~/k3s/traefik/traefik-service.yaml`), то нужно добавить в него
обработку порта:
```yaml
apiVersion: v1
kind: Service
metadata:
name: traefik
namespace: kube-system
...
...
spec:
ports:
...
...
...
- name: ssh # Имя порта (должно соответствовать имени в IngressRouteTCP и entryPoint)
port: 2222 # Внешний порт
targetPort: 2222 # Порт Traefik
protocol: TCP # Протокол
```
И, само собой, применить манифест:
```bash
sudo kubectl apply -f ~/k3s/traefik/traefik-service.yaml
```
Если у вас нет манифеста Traefik Service, то можно отредактировать его непосредственно в кластере через `kubectl edit`
(будьте готовы редактировать в **vim**):
```bash
sudo kubectl edit svc -n kube-system traefik -o yaml
```
Важно: значение `nodePort` для SSH не нужно указывать, так как оно будет сгенерировано автоматически сразу после
сохранения и выхода из редактора (из vim выходите через `<esc:>wq`).
Проверим, что Service обновился:
```bash
sudo kubectl get svc -n kube-system traefik
```
Увидим что-то вроде (орт 2222 добавился в Service Traefik):
```text
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
traefik LoadBalancer 10.43.164.48 192.168.1.26,192.168.1.27,192.168.1.28 80:32171/TCP,443:30329/TCP,9000:31317/TCP,2055:31675/TCP,2222:31963/TCP 6d
```
Проверим, что порт 2222 доступен на VIP-адресе keepalived (не забудьте заменить `<VIP>` на ваш Virtual IP):
```bash
nc -zv <VIP> 2222
```
Увидим, что порт 2222 доступен:
```text
Connection to <VIP> port 2222 [tcp/rockwell-csp2] succeeded!
```
SSH в Gitea по порту 2222 должен работать. Проверим, что SSH доступен. Создадим SSH-ключ:
```bash
ssh-keygen -t ed25519 -C "ваше-имя-в-gitea-или-что-угодно"
```
Возьмём публичный SSH-ключ `cat ~/.ssh/id_ed25519.pub` и добавим через web-интерфейс Gitea (_Settings → SSH / GPG Keys_
или _Настройки → SSH / GPG ключи_). После добавления ключа, можно подключиться к Gitea по SSH:
```bash
ssh -p 2222 git@<VIP>
```
Увидим что-то вроде:
```text
PTY allocation request failed on channel 0
Hi there, ваше-имя-в-gitea-или-что-угодно! You've successfully authenticated with the key named Apple Mac Mini M4, but Gitea does not provide shell access.
If this is unexpected, please log in with password and setup Gitea under another user.
Connection to <VIP> closed.
```
**ВСЕ!!** Можно настроить переадресацию внешнего 22 порта на домашнем роутере на 2222 порт VIP-адреса, и работать
со всеми репозиториями Gitea по SSH как обычно. Для большинства роутеров при обращении по доменому имени gitea тоже
будет работать по 22 порту.
## Удаление временного пода и архива
Можно удалить временный под, который мы использовали для переноса данных gitea в блочное хранилище Longhorn PVC:
```bash
sudo kubectl delete pod gitea-init-data -n gitea
```
И удалить временный архив с данными gitea которые мы перенесли из Docker-контейнера и положили в корень домашнего
каталога. Теперь данные gitea уже в Longhorn PVC и арихив не нужен:
```bash
sudo rm ~/gitea-data.tar.gz
```
## Выводы
### Плюсы
Размещение Gitea в K3s с Longhorn и Traefik повысит отказоустойчивость и доступность. Выход из строя любого узла
не приведёт к потере данных, так как Longhorn хранит данные в репликах на других узлах, а Traefik обеспечивает
автоматическую маршрутизацию трафика.
### Минусы
Сложность и число абстракций при доступе клиентов в Gitea возросло, и Gitea стала работать на 50-100 миллисекунд
медленнее (в зависимости от нагрузки на узлы). Если посмотреть метрики:
```bash
time curl -s -o /dev/null https://git.cube2.ru
```
Увидим что-то вроде:
```text
real 0m0.233s
user 0m0.124s
sys 0m0.015s
```
Где **233 мс** — суммарное время запроса (включая DNS, соединение, TLS и ответ Gitea). Для моей локальной
сети (1 Гбит/с) стало медленнее, чем ожидалось (~50-100 мс для Docker с Nginx). Но для цепочки K3s с Traefik, Longhorn
и keepalived — это разумно.
#### Основные компоненты задержки:
| Причина | Docker + Nginx | K3s + Traefik + Longhorn + Keepalived |
|-----------------------------|-------------------|-------------------------------------------------|
| DNS | 20-50 мс. | 20-50 мс. |
| Web-сервер + TLS | Nginx — 20-40 мс. | Traefik — 30-50 мс. |
| Маршрутизация | 10 мс. | IngressRoute + Service + Keepalived — 20-60 мс. |
| Доступ к базе данных SQLite | 10 мс. | Longhorn PVC — 20-50 мс. |
| | | |
| **Итого:** | **60-110 мс** | **90-210 мс** |