281 lines
13 KiB
Markdown
281 lines
13 KiB
Markdown
# Решение проблем
|
||
|
||
## Таблица проблем
|
||
|
||
| Проблема | Причина | Решение |
|
||
|---|---|---|
|
||
| `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, не функциональная ошибка сценария.
|