Files
K3S/docs/troubleshooting.md
2026-04-27 08:40:08 +03:00

281 lines
13 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Решение проблем
## Таблица проблем
| Проблема | Причина | Решение |
|---|---|---|
| `Permission denied (publickey)` | Ключ не скопирован | `ssh-copy-id user@server` |
| `Vault decryption failed` | Неверный пароль vault | Проверь `VAULT_PASSWORD` в `.env` |
| kube-vip VIP не пингуется | Неверный интерфейс | Проверь `kube_vip_interface` — должен совпадать с `ip a` на master01 |
| RPi K3S не стартует | Не включены cgroups | `ssh pi@rpi "cat /proc/cgroups | grep memory"` — должно быть `1` |
| PVC в Pending | NFS не монтируется | `kubectl get events -A` — проверь монтирование NFS на нодах |
| ingress `EXTERNAL-IP <pending>` | kube-vip не работает | `kubectl -n kube-system get pods | grep kube-vip` — поды должны быть Running |
| etcd нет кворума | Нода упала | Нужно минимум 2 из 3 нод. `kubectl get nodes` |
| worker01/rpi01 не присоединились | master01 не готов | Убедись что master01 полностью стартовал. `serial: 1` гарантирует порядок |
| Kiali: "Could not get token" | Нет Secret | `kubectl -n istio-system get secret kiali-admin-token` |
| Grafana PVC Pending | NFS StorageClass не default | `kubectl get sc``nfs-master01` должен быть `(default)` |
| Molecule: image pull failed | Нет интернета | `docker pull geerlingguy/docker-ubuntu2204-ansible:latest` вручную |
| Molecule: idempotency failed | Таск не идемпотентен | Добавь `changed_when: false` |
| `ansible-lint` ошибки | Синтаксис | `make molecule-lint` и исправь файлы |
| etcd restore: снимок не найден | Неверный путь | `make etcd-list-snapshots` — снимок должен быть на первом мастере |
| add-node: нода не присоединяется | kube-vip не работает | `kubectl -n kube-system get pods | grep kube-vip` |
| remove-node: drain завис | PodDisruptionBudget | `kubectl get pdb -A` — проверь PDB |
| cert-manager: сертификат Pending | Нет публичного домена | При letsencrypt нужен публичный домен + ingress-nginx |
| Harbor: registry push fails | Неверный externalURL | `harbor_ingress_host` должен совпадать с реальным hostname |
| Jenkins: агент не стартует | RBAC | `kubectl get events -n jenkins` — проверь ServiceAccount |
| Vault: sealed после рестарта | Нет auto-unseal | Настрой `vault_auto_unseal_type` или используй k8s unsealer |
| NetBird: клиент не подключается | DNS не настроен | Настрой A-записи для Management, Signal, Coturn LB IPs |
| MinIO: distributed не поднимается | Мало нод | Distributed требует минимум 4 реплики/ноды |
| Nextcloud: redirect loop | HTTP→HTTPS конфликт | Добавь `TRUSTED_PROXIES` в конфиг Nextcloud |
| yandex-dns-controller: 401 Unauthorized | Неверный токен | Проверь `yandex_dns.token` в vault.yml; токен получается на oauth.yandex.ru |
| yandex-dns-controller: запись не создаётся | managed: false | Убедись что `managed: true` в ConfigMap |
| yandex-dns-controller: старая запись не удалена | Нет в state | Запись была создана вручную, не контроллером — удали вручную если нужно |
| ingress-add-domains: 404 | Ingress не найден в namespace сервиса | `kubectl get ingress -A -l app.kubernetes.io/instance=ingress-add-domains` |
| ingress-add-domains: 503 | Сервис/порт неверный | `kubectl -n <namespace> get svc <name>` — проверь имя и порт |
| ingress-proxypass: 502 Bad Gateway | Кластер не достигает внешний IP | `curl -v http://<external-ip>:<port>` с ноды кластера; проверь файрвол на внешнем хосте |
| ingress-proxypass: 503 | Endpoints пустой | `kubectl -n ingress-proxypass get endpoints` — должны быть адреса |
| ingress-proxypass: 404 | Имя хоста не совпадает | `kubectl -n ingress-proxypass describe ingress <name>` — хост должен совпасть точно |
| splitgw: трафик не перехватывается | TPROXY mark не применён | `iptables -t mangle -L SPLITGW -v -n` — должны быть правила; `journalctl -u splitgw-rules -f` |
| splitgw: sing-box не стартует | Ошибка конфига | `journalctl -u singbox -f`; `sing-box check --config /etc/sing-box/config.json` |
| splitgw: YouTube всё равно без VPN | DNS утечка | DNS-запросы должны идти через TPROXY; `nslookup youtube.com 8.8.8.8` с устройства |
| hysteria2-server: порт закрыт | Файрвол VPS | `ufw allow 443/udp` или `firewall-cmd --add-port=443/udp --permanent` |
| hysteria2-server: клиент не подключается | Самоподписанный сертификат | Добавь `insecure=1` в URL или установи `tls_mode: acme` |
| mediaserver: Prowlarr не видит индексеры | Hysteria2 sidecar упал | `kubectl -n mediaserver logs <prowlarr-pod> -c hysteria2` |
## Подробный вывод
```bash
ANSIBLE_VERBOSITY=2 make install # основной вывод
ANSIBLE_VERBOSITY=4 make install # максимальный вывод
ANSIBLE_VERBOSITY=2 ANSIBLE_TAGS=k3s make install # подробно для k3s
```
## Диагностика нод
```bash
make shell # открыть bash в ansible-контейнере
# Внутри контейнера:
ansible all -m ping
ansible all -m shell -a "systemctl status k3s"
ansible k3s_master -m shell -a "k3s kubectl get nodes"
ansible k3s_master -m shell -a "journalctl -u k3s -n 50 --no-pager"
```
## Диагностика кластера
```bash
export KUBECONFIG=$(pwd)/kubeconfig
# Общий статус
kubectl get nodes -o wide
kubectl get pods -A | grep -v Running
kubectl get events -A --sort-by='.lastTimestamp' | tail -30
# Ресурсы
kubectl top nodes
kubectl top pods -A --sort-by=cpu
# Конкретный под
kubectl describe pod <pod> -n <namespace>
kubectl logs <pod> -n <namespace> --tail=100 -f
kubectl logs <pod> -n <namespace> --previous # логи предыдущего контейнера
# PVC проблемы
kubectl describe pvc <pvc> -n <namespace>
kubectl get pv
# Ingress проблемы
kubectl describe ingress <name> -n <namespace>
kubectl logs -n ingress-nginx deployment/ingress-nginx-controller -f
```
## Проверка etcd
```bash
kubectl -n kube-system exec -it \
$(kubectl -n kube-system get pods -l component=etcd -o name | head -1) \
-- etcdctl --endpoints=https://127.0.0.1:2379 \
--cacert=/var/lib/kubernetes/k3s/server/tls/etcd/server-ca.crt \
--cert=/var/lib/kubernetes/k3s/server/tls/etcd/server-client.crt \
--key=/var/lib/kubernetes/k3s/server/tls/etcd/server-client.key \
member list
```
## Диагностика NFS
```bash
# На NFS сервере:
make shell
ansible nfs_server -m shell -a "exportfs -v"
ansible nfs_server -m shell -a "showmount -e localhost"
# На нодах кластера:
ansible k3s_cluster -m shell -a "mount | grep nfs"
ansible k3s_cluster -m shell -a "showmount -e 192.168.1.10"
```
## Диагностика kube-vip
```bash
# Статус подов
kubectl -n kube-system get pods -l app.kubernetes.io/name=kube-vip
# Логи
kubectl -n kube-system logs -l app.kubernetes.io/name=kube-vip
# Проверить VIP
ping 192.168.1.100 # kube_vip_address
curl -k https://192.168.1.100:6443/healthz
```
## Диагностика cert-manager
```bash
kubectl get certificate -A
kubectl get certificaterequest -A
kubectl get challenges -A
kubectl describe certificate <name> -n <namespace>
kubectl logs -n cert-manager deployment/cert-manager -f
```
## Диагностика ingress-add-domains
```bash
# Все созданные Ingresses
kubectl get ingress -A -l app.kubernetes.io/instance=ingress-add-domains
# Детали конкретного Ingress
kubectl describe ingress <name> -n <namespace>
# Проверить что сервис существует
kubectl -n <namespace> get svc <service-name>
# Логи nginx-контроллера
kubectl -n ingress-nginx logs -l app.kubernetes.io/name=ingress-nginx --tail=50
```
## Диагностика ingress-proxypass
```bash
# Ресурсы namespace
kubectl -n ingress-proxypass get all,ingress,endpoints
# Проверить Endpoints заполнены (Addresses не должен быть <none>)
kubectl -n ingress-proxypass describe endpoints <name>
# Тест изнутри кластера
kubectl run curl --rm -it --image=curlimages/curl -- \
curl -v http://<service>.ingress-proxypass.svc.cluster.local:<port>
# Проверить что nginx подхватил правила
kubectl -n ingress-nginx logs -l app.kubernetes.io/name=ingress-nginx --tail=50
```
## Диагностика splitgw
```bash
# Статус сервисов на хосте gateway
systemctl status singbox splitgw-rules
journalctl -u singbox -f
journalctl -u splitgw-rules -f
# Проверить TPROXY-правила iptables
iptables -t mangle -L SPLITGW -v -n
iptables -t mangle -L PREROUTING -v -n
# Проверить sing-box конфиг
sing-box check --config /etc/sing-box/config.json
# Диагностика DNS (с устройства-источника)
nslookup youtube.com
nslookup vk.com
```
## Диагностика hysteria2-server
```bash
# Статус на VPS
systemctl status hysteria2
journalctl -u hysteria2 -f
# Проверить порт открыт (UDP)
ss -ulnp | grep 443
# С клиентской машины:
nc -u <vps-ip> 443
# Тест подключения
curl -v --proxy hysteria2://pass@<vps-ip>:443?insecure=1 https://example.com
```
## Типичные Molecule ошибки
| Ошибка | Решение |
|---|---|
| `Unable to pull image` | `docker pull geerlingguy/docker-ubuntu2204-ansible:latest` |
| `FAILED: assert ... is defined` | Добавь переменную в `converge.yml` секцию `vars:` |
| `Idempotency: CHANGED` | Добавь `changed_when: false` к таску |
| `yamllint: wrong indentation` | Исправь отступы, запусти `make molecule-lint` |
| `ansible-lint: no-changed-when` | Добавь `changed_when: <условие>` |
| `sysctl: Operation not permitted` | Добавь `privileged: true` в `molecule.yml` |
## Детальная отладка Molecule (addons и cluster)
### 1) Сначала локализуй место падения
```bash
make molecule-addon-all
```
`molecule-addon-all` падает на первом сломанном аддоне.
После этого запускай конкретный сценарий:
```bash
make molecule-addon-<addon-name>
```
### 2) Запуск с продолжением после ошибок
Полезно для обзора нескольких проблем за один прогон:
```bash
make -k molecule-addon-all
```
`-k` (keep going) позволит пройти дальше даже после падения отдельных аддонов.
### 3) Типовые причины падений verify
- **Bool как строка**
- Симптом: `assert v.flag == true` падает, но в сообщении видно `True`.
- Безопасный паттерн:
- `(v.flag | string | lower) == 'true'`
- **Multi-document YAML**
- Симптом: `from_yaml` не может распарсить файл, где несколько `---`.
- Используй:
- `from_yaml_all | list`
- **Файлы /tmp и delegate_to**
- Симптом: `no such file or directory` в `helm lint/template`.
- Причина: файл рендерится на target-host, а читается на localhost.
- Решение: рендерить/копировать и читать в одном контексте (чаще всего `delegate_to: localhost`, `run_once: true`).
- **Undefined variables в шаблонах**
- Симптом: `AnsibleUndefinedVariable`.
- Решение: добавить недостающие vars в `molecule/default/converge.yml` (даже если они есть в role defaults).
### 4) Проверка cluster-сценария (3 master + 2 worker)
```bash
make molecule-cluster
```
Если видишь warning:
```text
'molecule/default/molecule.yml' glob failed
```
и при этом команда заканчивается `✓ cluster topology: OK`, это служебный warning shared-state Molecule, не функциональная ошибка сценария.