Добавил серию Blog 1-5 части. Подправил настройки и внешний вид сайта
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 1.3 MiB |
@@ -0,0 +1,634 @@
|
||||
---
|
||||
title: "Блог на Hugo в K3s: часть 5 - что делать когда всё внезапно сломалось"
|
||||
date: 2026-02-17
|
||||
draft: false
|
||||
description: "Сайт работал вчера, а сегодня 503. Алгоритм диагностики Kubernetes проблем за 5 минут - от DNS до пода, без паники и танцев с бубном."
|
||||
tags: ["kubernetes", "k3s", "traefik", "debugging", "nginx", "troubleshooting"]
|
||||
categories: ["infrastructure"]
|
||||
series: ["Блог на Hugo в K3s"]
|
||||
series_order: 5
|
||||
---
|
||||
|
||||
В части 4 мы разобрались с Git workflow. Всё работает: пушишь в `dev` - видишь на тестовом окружении, мержишь в `main` - публикуется на production.
|
||||
|
||||
А потом в один прекрасный день открываешь свой сайт и видишь `503 Service Temporarily Unavailable`.
|
||||
|
||||
Вчера же все работало! Ты ничего не менял. Что произошло?
|
||||
|
||||
Добро пожаловать в мир эксплуатации Kubernetes, где проблемы тоже возникают и требуют системного подхода без паники.
|
||||
|
||||
Эта статья - алгоритм диагностики от DNS до пода. Проходишь по шагам сверху вниз, находишь проблему за 5 минут. Не гадаешь, не тыкаешь наугад - работаешь по системе.
|
||||
|
||||
---
|
||||
|
||||
## Анатомия HTTP запроса в K3s
|
||||
|
||||
Прежде чем искать проблему, нужно понять путь запроса от браузера до nginx:
|
||||
|
||||
```
|
||||
Браузер
|
||||
↓ DNS запрос
|
||||
DNS сервер (провайдер или Cloudflare)
|
||||
↓ Возвращает IP адрес
|
||||
Роутер/Файрвол (OPNsense, MikroTik)
|
||||
↓ Port Forward 443 → K3s node
|
||||
MetalLB LoadBalancer
|
||||
↓ External IP
|
||||
Traefik Ingress Controller
|
||||
↓ IngressRoute matching
|
||||
Kubernetes Service
|
||||
↓ Endpoint selection
|
||||
Pod (Nginx контейнер)
|
||||
↓ Volume mount
|
||||
NFS хранилище
|
||||
```
|
||||
|
||||
Проблема может быть на любом из этих уровней. Секрет эффективной диагностики - проверять снаружи внутрь, последовательно исключая рабочие компоненты.
|
||||
|
||||
Когда тыкаешь наугад, проверяя сначала поды, потом DNS, потом снова поды - тратишь время. Когда идёшь по алгоритму - находишь проблему за минуты.
|
||||
|
||||
---
|
||||
|
||||
## Шаг 1: DNS - доходит ли домен до твоего IP
|
||||
|
||||
Первым делом проверяем что домен резолвится в правильный IP. Без этого дальше проверять бессмысленно - браузер просто не знает куда направлять запрос.
|
||||
|
||||
### Проверка
|
||||
|
||||
```bash
|
||||
# Проверяем DNS резолв (используй свой домен)
|
||||
dig blog.example.com +short
|
||||
|
||||
# Альтернатива если dig не установлен
|
||||
nslookup blog.example.com
|
||||
```
|
||||
|
||||
### Ожидаемый результат
|
||||
|
||||
```
|
||||
77.37.XXX.XXX
|
||||
```
|
||||
|
||||
Должен вернуться твой **публичный IP адрес** (тот который прописан в A-записи у DNS провайдера).
|
||||
|
||||
### Что может пойти не так
|
||||
|
||||
| Симптом | Причина | Как проверить | Решение |
|
||||
|---------|---------|---------------|---------|
|
||||
| Возвращается старый IP | DNS кеш не обновился | `dig blog.example.com @8.8.8.8` | Подожди TTL (обычно 300-3600 сек) |
|
||||
| `NXDOMAIN` ошибка | Домен не делегирован | Проверь NS записи у регистратора | Настрой DNS правильно |
|
||||
| Возвращается `127.0.0.1` | Локальный override | `cat /etc/hosts \| grep blog` | Удали строку из /etc/hosts |
|
||||
| Возвращается несколько IP | Round-robin DNS | Проверь все ли IP твои | Удали лишние A-записи |
|
||||
|
||||
Если DNS правильный - идём дальше.
|
||||
|
||||
---
|
||||
|
||||
## Шаг 2: Внешний доступ - доходит ли запрос до сервера
|
||||
|
||||
Теперь проверяем что запрос физически доходит до сервера. DNS может быть правильным, но файрвол может блокировать трафик.
|
||||
|
||||
### Проверка
|
||||
|
||||
```bash
|
||||
# Пробуем подключиться извне (важно - НЕ из локальной сети!)
|
||||
curl -v https://blog.example.com 2>&1 | head -30
|
||||
```
|
||||
|
||||
**Важно:** Запускай эту команду с **внешнего** сервера или используй мобильный интернет. Тест из локальной сети ничего не докажет - можешь обходить файрвол.
|
||||
|
||||
### Ситуация А: Connection refused или timeout
|
||||
|
||||
```
|
||||
curl: (7) Failed to connect to blog.example.com port 443: Connection refused
|
||||
```
|
||||
|
||||
Запрос вообще не дошёл до сервера. Проблема на сетевом уровне.
|
||||
|
||||
**Возможные причины:**
|
||||
|
||||
**1. Порт 443 закрыт на файрволе/роутере**
|
||||
|
||||
Проверь Port Forward правила на OPNsense/MikroTik. Должно быть:
|
||||
```
|
||||
WAN:443 → 192.168.X.X:443 (IP любой K3s ноды)
|
||||
```
|
||||
|
||||
**2. MetalLB не назначил External IP для Traefik**
|
||||
|
||||
```bash
|
||||
# Проверяем MetalLB
|
||||
kubectl get svc -n traefik traefik
|
||||
|
||||
# Ожидаемый результат
|
||||
NAME TYPE EXTERNAL-IP PORT(S)
|
||||
traefik LoadBalancer 192.168.X.X 80:30080/TCP,443:30443/TCP
|
||||
```
|
||||
|
||||
Если `EXTERNAL-IP` показывает `<pending>` - MetalLB не работает или пул IP адресов не настроен.
|
||||
|
||||
**3. Traefik под не запущен**
|
||||
|
||||
```bash
|
||||
# Проверяем что Traefik работает
|
||||
kubectl get pods -n traefik
|
||||
|
||||
# Должны быть все Running
|
||||
NAME READY STATUS
|
||||
traefik-xxxxxxxxxx-xxxxx 1/1 Running
|
||||
```
|
||||
|
||||
### Ситуация Б: TLS handshake прошёл, но 503
|
||||
|
||||
```
|
||||
< HTTP/2 503
|
||||
< content-type: text/plain; charset=utf-8
|
||||
< content-length: 20
|
||||
no available server
|
||||
```
|
||||
|
||||
Отлично - наша ситуация! Traefik работает, SSL сертификат отдаёт, но дальше запрос упирается в стену.
|
||||
|
||||
Сообщение `no available server` означает что Traefik **нашёл роутер**, но **не нашёл живой бэкенд** за ним.
|
||||
|
||||
Проблема внутри кластера. Идём глубже.
|
||||
|
||||
### Ситуация В: SSL certificate problem
|
||||
|
||||
```
|
||||
curl: (60) SSL certificate problem: unable to get local issuer certificate
|
||||
```
|
||||
|
||||
Сертификат невалидный или не выпущен.
|
||||
|
||||
```bash
|
||||
# Проверяем Certificate объект (используй свой namespace)
|
||||
kubectl get certificate -n blog
|
||||
|
||||
# Должно быть READY=True
|
||||
NAME READY SECRET AGE
|
||||
blog-tls True blog-tls 2d
|
||||
```
|
||||
|
||||
Если `READY=False` - cert-manager не смог выпустить сертификат.
|
||||
|
||||
```bash
|
||||
# Смотрим что пошло не так
|
||||
kubectl describe certificate blog-tls -n blog
|
||||
|
||||
# Ищем секцию Events внизу вывода - там описание проблемы
|
||||
```
|
||||
|
||||
### Главное: Запрос доходит до Traefik
|
||||
|
||||
```bash
|
||||
# Проверка (с ВНЕШНЕГО сервера!)
|
||||
curl -I https://blog.example.com
|
||||
|
||||
# Ожидаемый результат (любой из двух)
|
||||
HTTP/2 200 # Всё работает
|
||||
HTTP/2 503 # Traefik работает, но бэкенд недоступен
|
||||
|
||||
# Если connection refused/timeout - проблема в сети (см. выше)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Шаг 3: Traefik - правильно ли маршрутизируется трафик
|
||||
|
||||
Traefik получил запрос на твой домен. Что он с ним делает? Смотрим логи.
|
||||
|
||||
### Проверка логов Traefik
|
||||
|
||||
```bash
|
||||
# Смотрим последние 50 строк логов Traefik
|
||||
kubectl logs -n traefik deployment/traefik --tail=50
|
||||
|
||||
# Фильтруем только свой домен (убираем шум от других сервисов)
|
||||
kubectl logs -n traefik deployment/traefik --tail=100 | grep blog.example
|
||||
```
|
||||
|
||||
### Что искать в логах
|
||||
|
||||
**Нормальный запрос:**
|
||||
|
||||
```json
|
||||
{
|
||||
"request": "GET / HTTP/2.0",
|
||||
"status": 200,
|
||||
"size": 8994,
|
||||
"router": "blog-blog-https-xxxxx@kubernetescrd",
|
||||
"service": "blog-nginx-blog@kubernetescrd",
|
||||
"backend": "http://10.42.2.40:80",
|
||||
"duration": 12
|
||||
}
|
||||
```
|
||||
|
||||
Ключевые поля:
|
||||
- **router:** Traefik нашёл нужный IngressRoute (`blog-blog-https`)
|
||||
- **backend:** IP пода nginx куда проксируется запрос (`10.42.2.40:80`)
|
||||
- **status:** HTTP код ответа от nginx (`200` = всё хорошо)
|
||||
|
||||
**Проблемный запрос:**
|
||||
|
||||
```json
|
||||
{
|
||||
"request": "GET / HTTP/2.0",
|
||||
"status": 503,
|
||||
"router": "blog-blog-https-xxxxx@kubernetescrd",
|
||||
"error": "no available server"
|
||||
}
|
||||
```
|
||||
|
||||
Traefik нашёл роутер, но поле `backend` отсутствует - под недоступен или не существует.
|
||||
|
||||
### Проверяем список IngressRoute
|
||||
|
||||
```bash
|
||||
# Смотрим все IngressRoute в кластере
|
||||
kubectl get ingressroute -A
|
||||
|
||||
# Фильтруем только свой домен
|
||||
kubectl get ingressroute -A | grep blog.example
|
||||
```
|
||||
|
||||
**Важный момент:** Если один и тот же домен прописан в **двух разных IngressRoute** из разных namespace - Traefik будет балансировать между ними.
|
||||
|
||||
Например:
|
||||
|
||||
```bash
|
||||
NAMESPACE NAME AGE
|
||||
blog blog-https 10d # СТАРЫЙ namespace
|
||||
blog-new blog-https 2d # НОВЫЙ namespace
|
||||
```
|
||||
|
||||
Оба IngressRoute имеют `match: Host('blog.example.com')`. Traefik видит оба, честно балансирует трафик 50/50.
|
||||
|
||||
Если один из бэкендов мёртв - половина запросов уходит в пустоту. 503 через раз.
|
||||
|
||||
**Решение:** Удалить старый IngressRoute:
|
||||
|
||||
```bash
|
||||
# Удаляем дубль из старого namespace
|
||||
kubectl delete ingressroute blog-https blog-http -n blog
|
||||
```
|
||||
|
||||
### Проверяем синтаксис match
|
||||
|
||||
Traefik очень требователен к синтаксису. Частая ошибка - забыть backticks или скобки.
|
||||
|
||||
**Неправильно:**
|
||||
```yaml
|
||||
match: Host(blog.example.com) # Нет backticks
|
||||
match: Host `blog.example.com` # Нет скобок вокруг Host
|
||||
match: Host("blog.example.com") # Двойные кавычки вместо backticks
|
||||
```
|
||||
|
||||
**Правильно:**
|
||||
```yaml
|
||||
match: Host(`blog.example.com`)
|
||||
```
|
||||
|
||||
Проверяем:
|
||||
|
||||
```bash
|
||||
# Смотрим манифест IngressRoute
|
||||
kubectl get ingressroute blog-https -n blog -o yaml | grep match:
|
||||
|
||||
# Должно быть со скобками и backticks
|
||||
match: Host(`blog.example.com`)
|
||||
```
|
||||
|
||||
### Главное: Traefik нашёл роутер
|
||||
|
||||
```bash
|
||||
# Проверка
|
||||
kubectl logs -n traefik deployment/traefik --tail=50 | grep blog.example
|
||||
|
||||
# Ожидаемый результат - есть строки с "router": "blog-blog-https"
|
||||
# Если router не найден - проблема в IngressRoute match синтаксисе
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Шаг 4: Service - видит ли он поды
|
||||
|
||||
Traefik нашёл роутер, проксирует трафик на Service. Но Service может не видеть поды если selector неправильный.
|
||||
|
||||
### Проверка endpoints
|
||||
|
||||
```bash
|
||||
# Смотрим endpoints для Service (используй своё имя Service)
|
||||
kubectl get endpoints nginx -n blog
|
||||
|
||||
# Ожидаемый результат - НЕ пустой список IP
|
||||
NAME ENDPOINTS
|
||||
nginx 10.42.0.44:80,10.42.2.40:80
|
||||
```
|
||||
|
||||
Если видишь `<none>` - Service не нашёл ни одного пода. Две возможные причины.
|
||||
|
||||
### Причина 1: Selector не совпадает с labels
|
||||
|
||||
```bash
|
||||
# Смотрим selector у Service
|
||||
kubectl get svc nginx -n blog -o yaml | grep -A3 "selector:"
|
||||
|
||||
# Вывод
|
||||
selector:
|
||||
app: nginx
|
||||
|
||||
# Смотрим labels у подов
|
||||
kubectl get pods -n blog --show-labels | grep nginx
|
||||
|
||||
# Вывод
|
||||
nginx-xxxxxxxxxx-xxxxx 1/1 Running app=nginx-old
|
||||
```
|
||||
|
||||
Видишь проблему? Service ищет `app: nginx`, а под помечен `app: nginx-old`. Не совпадает.
|
||||
|
||||
**Решение:** Исправить Deployment или Service чтобы labels совпадали.
|
||||
|
||||
### Причина 2: Поды не Running
|
||||
|
||||
```bash
|
||||
# Смотрим статус подов
|
||||
kubectl get pods -n blog
|
||||
|
||||
# Видим
|
||||
NAME READY STATUS
|
||||
nginx-xxxxxxxxxx-xxxxx 0/1 CreateContainerError
|
||||
```
|
||||
|
||||
Под существует, но не работает. Service правильно не включает его в endpoints. Идём в следующий шаг - разбираемся почему под не запускается.
|
||||
|
||||
### Главное: Service видит поды
|
||||
|
||||
```bash
|
||||
# Проверка
|
||||
kubectl get endpoints nginx -n blog
|
||||
|
||||
# Ожидаемый результат - НЕ пустой
|
||||
nginx 10.42.0.44:80,10.42.2.40:80
|
||||
|
||||
# Если <none> - проблема в селекторах или поды не Running
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Шаг 5: Pod - что происходит внутри контейнера
|
||||
|
||||
Самый глубокий уровень. Под не запускается или падает в цикле перезапусков.
|
||||
|
||||
### Проверка статуса подов
|
||||
|
||||
```bash
|
||||
# Смотрим все поды в namespace
|
||||
kubectl get pods -n blog
|
||||
|
||||
# Фильтруем только nginx
|
||||
kubectl get pods -n blog | grep nginx
|
||||
```
|
||||
|
||||
**Возможные статусы проблем:**
|
||||
|
||||
### CreateContainerError
|
||||
|
||||
Контейнер вообще не может стартануть. Обычно проблема с volumes или образом.
|
||||
|
||||
```bash
|
||||
# Смотрим детали пода (используй своё имя пода)
|
||||
kubectl describe pod nginx-xxxxxxxxxx-xxxxx -n blog | tail -30
|
||||
```
|
||||
|
||||
Ищем секцию `Events` внизу вывода. Там будет описание проблемы:
|
||||
|
||||
**Пример 1: PVC не примонтировался**
|
||||
|
||||
```
|
||||
Events:
|
||||
Warning FailedMount MountVolume.SetUp failed for volume "blog-public-pvc":
|
||||
mount failed: mount.nfs: Connection timed out
|
||||
```
|
||||
|
||||
NFS хранилище недоступно. Возможные причины:
|
||||
- NFS сервер выключен или перезагружается
|
||||
- Неправильный IP или путь в PersistentVolume
|
||||
- Файрвол блокирует NFS трафик (порт 2049)
|
||||
|
||||
**Пример 2: Образ не скачался**
|
||||
|
||||
```
|
||||
Events:
|
||||
Warning Failed Failed to pull image "nginx:latest": rpc error: code = Unknown
|
||||
```
|
||||
|
||||
Контейнер не может скачать образ. Обычно это означает что `imagePullPolicy: Never`, а образ не импортирован на ноду.
|
||||
|
||||
```bash
|
||||
# Проверяем что образ есть на ноде (используй IP своей worker ноды)
|
||||
ssh user@192.168.X.X "sudo k3s crictl images | grep nginx"
|
||||
```
|
||||
|
||||
Если образа нет - импортируй его через `k3s ctr images import`.
|
||||
|
||||
**Пример 3: ConfigMap не найден**
|
||||
|
||||
```
|
||||
Events:
|
||||
Warning FailedMount ConfigMap "nginx-config" not found
|
||||
```
|
||||
|
||||
Deployment ссылается на несуществующий ConfigMap.
|
||||
|
||||
```bash
|
||||
# Проверяем что ConfigMap существует
|
||||
kubectl get configmap -n blog | grep nginx-config
|
||||
```
|
||||
|
||||
Если нет - создай или исправь имя в Deployment.
|
||||
|
||||
### CrashLoopBackOff
|
||||
|
||||
Контейнер запускается, но сразу падает. Смотрим логи **предыдущего** запуска:
|
||||
|
||||
```bash
|
||||
# Логи последнего упавшего контейнера
|
||||
kubectl logs nginx-xxxxxxxxxx-xxxxx -n blog --previous
|
||||
```
|
||||
|
||||
**Пример: Nginx падает из-за неправильного конфига**
|
||||
|
||||
```
|
||||
nginx: [emerg] unexpected "}" in /etc/nginx/nginx.conf:15
|
||||
nginx: configuration file /etc/nginx/nginx.conf test failed
|
||||
```
|
||||
|
||||
Синтаксическая ошибка в `nginx.conf`. Проверяем ConfigMap:
|
||||
|
||||
```bash
|
||||
# Смотрим содержимое конфига
|
||||
kubectl get configmap nginx-config -n blog -o yaml
|
||||
```
|
||||
|
||||
Находим ошибку, исправляем, применяем. Под перезапустится автоматически.
|
||||
|
||||
### Главное: Под работает
|
||||
|
||||
```bash
|
||||
# Проверка
|
||||
kubectl get pods -n blog | grep nginx
|
||||
|
||||
# Ожидаемый результат - все Running
|
||||
nginx-xxxxxxxxxx-xxxxx 1/1 Running 0 2d
|
||||
|
||||
# Если не Running - смотри troubleshooting выше
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Шаг 6: Контент - есть ли файлы для отдачи
|
||||
|
||||
Под работает, Service видит его, Traefik проксирует трафик. Но сайт отдаёт `404 Not Found` или пустую страницу.
|
||||
|
||||
Проблема: Hugo Builder не записал файлы на NFS или записал не туда.
|
||||
|
||||
### Проверка
|
||||
|
||||
```bash
|
||||
# Заходим в под nginx (используй своё имя пода)
|
||||
kubectl exec -it nginx-xxxxxxxxxx-xxxxx -n blog -- sh
|
||||
|
||||
# Внутри пода смотрим что примонтировалось
|
||||
ls -la /usr/share/nginx/html/
|
||||
|
||||
# Должен быть index.html и папки posts, tags, etc
|
||||
```
|
||||
|
||||
**Если директория пустая** - Hugo Builder не сработал. Проверяем его логи:
|
||||
|
||||
```bash
|
||||
# Логи Hugo Builder
|
||||
kubectl logs -n blog deployment/hugo-builder-prod --tail=50
|
||||
```
|
||||
|
||||
Ищем строку `Build successful!` и список созданных файлов. Если её нет:
|
||||
|
||||
1. **Webhook не сработал** - проверь настройки webhook в Gitea
|
||||
2. **Hugo упал с ошибкой** - читай логи выше, смотри на что ругается
|
||||
3. **Собрал в другую директорию** - проверь переменную `OUTPUT_DIR` в build.sh
|
||||
|
||||
### Главное: Контент на месте
|
||||
|
||||
```bash
|
||||
# Проверка (используй своё имя пода)
|
||||
kubectl exec -it nginx-xxxxxxxxxx-xxxxx -n blog -- ls /usr/share/nginx/html/ | head -5
|
||||
|
||||
# Ожидаемый результат
|
||||
index.html
|
||||
posts/
|
||||
tags/
|
||||
categories/
|
||||
|
||||
# Если пусто - Hugo Builder не отработал (см. выше)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Быстрый чеклист для любой проблемы
|
||||
|
||||
Сохрани эту последовательность - она работает для 95% проблем:
|
||||
|
||||
```
|
||||
[ ] DNS: dig домен → правильный IP?
|
||||
[ ] Сеть: curl -v https://домен → доходит до Traefik?
|
||||
[ ] MetalLB: kubectl get svc -n traefik → External IP назначен?
|
||||
[ ] Traefik: kubectl get pods -n traefik → Running?
|
||||
[ ] IngressRoute: kubectl get ingressroute -A | grep домен → нет дублей?
|
||||
[ ] Match синтаксис: Host(`домен`) со скобками и backticks?
|
||||
[ ] Endpoints: kubectl get endpoints -n namespace → не пустые?
|
||||
[ ] Selector: labels подов совпадают с selector Service?
|
||||
[ ] Pods: kubectl get pods -n namespace → все Running?
|
||||
[ ] PVC: kubectl get pvc -n namespace → все Bound?
|
||||
[ ] Контент: kubectl exec ls /usr/share/nginx/html → файлы есть?
|
||||
```
|
||||
|
||||
Проходишь по списку сверху вниз. Останавливаешься на первом `[ ]` где что-то не так. Чинишь. Проверяешь снова.
|
||||
|
||||
Не прыгай хаотично между уровнями. Алгоритм экономит время.
|
||||
|
||||
---
|
||||
|
||||
## Реальный пример: 503 через раз
|
||||
|
||||
Мой сайт отдавал `503` примерно в 50% запросов. Половина запросов работала, половина нет.
|
||||
|
||||
Прошёл по алгоритму:
|
||||
|
||||
1. ✅ DNS - правильный IP
|
||||
2. ✅ Сеть - Traefik отвечает
|
||||
3. ✅ MetalLB - External IP назначен
|
||||
4. ✅ Traefik - поды Running
|
||||
5. ❌ IngressRoute - **два роутера на один домен**
|
||||
|
||||
```bash
|
||||
kubectl get ingressroute -A | grep oakazanin
|
||||
|
||||
NAMESPACE NAME
|
||||
blog blog-https # СТАРЫЙ, бэкенд в CreateContainerError
|
||||
oakazanin blog-https # НОВЫЙ, работает
|
||||
```
|
||||
|
||||
Traefik видел два роутера, честно балансировал трафик 50/50. Каждый второй запрос улетал в мёртвый `blog/nginx`.
|
||||
|
||||
Диагноз поставлен за 3 минуты. Лечение - одна команда:
|
||||
|
||||
```bash
|
||||
# Удаляем дубль из старого namespace
|
||||
kubectl delete ingressroute blog-https blog-http -n blog
|
||||
```
|
||||
|
||||
Мораль: **всегда чисти за собой**. Старые namespace с нерабочими сервисами - источник неочевидных проблем.
|
||||
|
||||
---
|
||||
|
||||
## Откат и cleanup
|
||||
|
||||
Если в процессе диагностики что-то сломал ещё больше - откатываемся:
|
||||
|
||||
```bash
|
||||
# Восстанавливаем предыдущую версию манифеста
|
||||
kubectl apply -f nginx-deployment.yaml
|
||||
|
||||
# Перезапускаем поды принудительно
|
||||
kubectl rollout restart deployment/nginx -n blog
|
||||
|
||||
# Смотрим что изменения применились
|
||||
kubectl rollout status deployment/nginx -n blog
|
||||
```
|
||||
|
||||
**Золотое правило:** Перед экспериментами делай бэкапы манифестов:
|
||||
|
||||
```bash
|
||||
# Экспортируем текущее состояние с датой
|
||||
kubectl get deployment,service,ingressroute -n blog -o yaml > backup-$(date +%Y%m%d).yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Что дальше
|
||||
|
||||
Ты умеешь диагностировать проблемы. Но лучше их вообще не создавать.
|
||||
|
||||
В следующей части покажу как правильно мигрировать сервисы между namespace - без даунтайма, дублей IngressRoute и других сюрпризов которые приводят к 503.
|
||||
|
||||
Разберём реальный пример: переносим Gitea между namespace с NFS данными, получаем новый SSL за 32 секунды, и удаляем старый namespace навсегда.
|
||||
|
||||
---
|
||||
|
||||
**Стек этой части:**
|
||||
- Traefik 2.11 IngressRoute
|
||||
- Kubernetes 1.30 (K3s)
|
||||
- kubectl CLI
|
||||
- curl для внешних проверок
|
||||
- dig для DNS диагностики
|
||||
Reference in New Issue
Block a user