# Решение проблем ## Таблица проблем | Проблема | Причина | Решение | |---|---|---| | `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 ` | 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 get svc ` — проверь имя и порт | | ingress-proxypass: 502 Bad Gateway | Кластер не достигает внешний IP | `curl -v http://:` с ноды кластера; проверь файрвол на внешнем хосте | | ingress-proxypass: 503 | Endpoints пустой | `kubectl -n ingress-proxypass get endpoints` — должны быть адреса | | ingress-proxypass: 404 | Имя хоста не совпадает | `kubectl -n ingress-proxypass describe ingress ` — хост должен совпасть точно | | 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 -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 -n kubectl logs -n --tail=100 -f kubectl logs -n --previous # логи предыдущего контейнера # PVC проблемы kubectl describe pvc -n kubectl get pv # Ingress проблемы kubectl describe ingress -n 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 -n 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 -n # Проверить что сервис существует kubectl -n get svc # Логи 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 не должен быть ) kubectl -n ingress-proxypass describe endpoints # Тест изнутри кластера kubectl run curl --rm -it --image=curlimages/curl -- \ curl -v http://.ingress-proxypass.svc.cluster.local: # Проверить что 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 443 # Тест подключения curl -v --proxy hysteria2://pass@: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- ``` ### 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, не функциональная ошибка сценария.