Доработка документации

This commit is contained in:
Tot Maxim
2026-07-18 23:33:26 +03:00
parent 40ff73f647
commit 53032f1fd7
6 changed files with 479 additions and 8 deletions

7
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,7 @@
{
"cSpell.ignoreWords": [
"armhf",
"gitea",
"раннеров"
]
}

4
notes.txt Normal file
View File

@@ -0,0 +1,4 @@
«Почему в серьёзном предприятии Gitea, а не GitLab?» Честный и защищаемый ответ:
GitLab CE тяжёлый по ресурсам (Postgres + Redis + Sidekiq + Gitaly, легко 4+ ГБ RAM).
Для скромного парка в закрытом контуре Gitea — один лёгкий Go-бинарь, разворачивается и обслуживается минимальными силами.
Под задачу «внутреннее хранение конфигов/ролей + CI кросс-сборки» этого достаточно.

View File

@@ -638,6 +638,303 @@ gitea http: 302
---
### 24. Модуль 2 — проверка CI-стека + `kind-config.yaml` новой топологии
Начиная с этой записи, команды выполнял **ты сам** напрямую в интерактивной SSH-сессии на сервере (login shell, `~/.local/bin` уже в `PATH`), а мне присылал готовый лог поэтому команды ниже идут без SSH-обвязки из пункта 0.
**Команда:**
```bash
docker ps --filter name=gitea --format '{{.Names}}\t{{.Status}}'
cat > ~/k8s/kind-config.yaml << 'EOF'
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
extraPortMappings:
- containerPort: 80
hostPort: 8080
- containerPort: 443
hostPort: 8443
- role: worker
- role: worker
EOF
cat ~/k8s/kind-config.yaml
```
**Разбор аргументов:**
- `docker ps --filter name=gitea --format '{{.Names}}\t{{.Status}}'` сужает список контейнеров до тех, чьё имя содержит `gitea`, и печатает только имя и статус быстрая проверка перед началом работы, что боевой CI-стек жив (чек-лист из SERVER.md).
- `cat > файл << 'EOF' ... EOF` heredoc: всё, что между `<< 'EOF'` и завершающим `EOF`, построчно записывается в файл через `cat`; кавычки вокруг `'EOF'` отключают подстановку переменных/спецсимволов внутри блока (не нужна здесь, но привычка на будущее для блоков с `$`).
- `role: control-plane` / `role: worker` три записи в списке `nodes` = kind создаст три Docker-контейнера, один control-plane и два worker (топология модуля 2 из LEARNING.md).
- `extraPortMappings` внутри control-plane-ноды пробрасывает порты хоста на порты внутри этого контейнера-ноды: `hostPort: 8080` `containerPort: 80` и `hostPort: 8443` `containerPort: 443`. Сделано сразу в модуле 2 (адаптация из SERVER.md), а не только в модуле 6 (когда понадобится Ingress) иначе пришлось бы пересоздавать кластер повторно. Именно 8080/8443, а не 80/443, потому что порты 80/443 хоста уже заняты Gitea/nginx (см. `ss -tulpn` в сессии 80/443/3000/3030/222/4400/9090 заняты, 8080/8443 свободны).
**Получен ответ:**
```
gitea_runner Up 7 hours
gitea Up 7 hours (healthy)
gitea_db Up 7 hours (healthy)
gitea_runner_armhf Up 7 hours
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
extraPortMappings:
- containerPort: 80
hostPort: 8080
- containerPort: 443
hostPort: 8443
- role: worker
- role: worker
```
**Значит:** CI-стек в порядке, можно продолжать. Файл `kind-config.yaml` записан корректно готов к использованию в `kind create cluster --config`.
---
### 25. Модуль 2 — пересоздание кластера с топологией control-plane + 2 worker
**Команда:**
```bash
kind delete cluster --name lab
kind create cluster --name lab --config kind-config.yaml
kubectl cluster-info --context kind-lab
```
**Разбор аргументов:**
- `kind delete cluster --name lab` удаляет старый однонодовый кластер (созданный в модуле 0-1 без конфига) вместе с его Docker-контейнером, чтобы освободить имя `lab` под новую топологию.
- `kind create cluster --name lab --config kind-config.yaml` `--config` вместо голого `--name` заставляет kind читать топологию и extraPortMappings из файла, а не создавать кластер дефолтной однонодовой конфигурацией.
- `kubectl cluster-info --context kind-lab` печатает адрес API-сервера и CoreDNS для явно указанного контекста `kind-lab` (kind сам создаёт и переключает kubectl-контекст с таким именем при каждом `create cluster`).
**Получен ответ:**
```
Deleting cluster "lab" ...
Deleted nodes: ["lab-control-plane"]
Creating cluster "lab" ...
✓ Ensuring node image (kindest/node:v1.36.1) 🖼
✓ Preparing nodes 📦 📦 📦
✓ Writing configuration 📜
✓ Starting control-plane 🕹️
✓ Installing CNI 🔌
✓ Installing StorageClass 💾
✓ Joining worker nodes 🚜
Set kubectl context to "kind-lab"
Kubernetes control plane is running at https://127.0.0.1:36761
CoreDNS is running at https://127.0.0.1:36761/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
```
**Значит:** пересоздание прошло без ошибок и быстро (образ ноды `kindest/node:v1.36.1` уже был в кэше Docker с прошлого раза не пришлось скачивать заново). Обрати внимание на новый шаг в выводе, которого не было при однонодовом создании: `✓ Joining worker nodes` именно он добавляет `lab-worker`/`lab-worker2` к кластеру после того, как control-plane уже поднят. kubectl-контекст переключён на `kind-lab` автоматически.
---
### 26. Модуль 2 — проверка нод
**Команда:**
```bash
kubectl get nodes -o wide
```
**Получен ответ:**
```
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
lab-control-plane Ready control-plane 57s v1.36.1 172.22.0.4 <none> Debian GNU/Linux 13 (trixie) 6.8.0-136-generic (amd64) containerd://2.3.1
lab-worker Ready <none> 46s v1.36.1 172.22.0.2 <none> Debian GNU/Linux 13 (trixie) 6.8.0-136-generic (amd64) containerd://2.3.1
lab-worker2 Ready <none> 47s v1.36.1 172.22.0.3 <none> Debian GNU/Linux 13 (trixie) 6.8.0-136-generic (amd64) containerd://2.3.1
```
**Значит:** все 3 ноды поднялись и перешли в `Ready` за 46-57 секунд быстрее, чем однонодовый кластер с нуля (модуль 0, ~53с только на control-plane), потому что образ ноды уже был в кэше. Колонка `ROLES` подтверждает топологию: одна `control-plane`, две `<none>` (worker kind не проставляет им явную роль-метку по умолчанию, в отличие от control-plane). `KERNEL-VERSION` (6.8.0-136) новее, чем было зафиксировано в SERVER.md на момент установки (6.8.0-124) сервер обновлялся ядром между сессиями, это не связано с kind и не требует действий.
---
### 27. Модуль 2 — компоненты control-plane и DaemonSet'ы вживую
**Команда:**
```bash
kubectl get pods -n kube-system -o wide
```
**Получен ответ:**
```
NAME READY STATUS RESTARTS AGE NODE
coredns-589f44dc88-s8m6z 1/1 Running 0 86s lab-control-plane
coredns-589f44dc88-xlkk7 1/1 Running 0 86s lab-control-plane
etcd-lab-control-plane 1/1 Running 0 93s lab-control-plane
kindnet-62npg 1/1 Running 0 86s lab-control-plane
kindnet-9lsrg 1/1 Running 0 86s lab-worker2
kindnet-xwjqx 1/1 Running 0 85s lab-worker
kube-apiserver-lab-control-plane 1/1 Running 0 93s lab-control-plane
kube-controller-manager-lab-control-plane 1/1 Running 0 93s lab-control-plane
kube-proxy-6ltl9 1/1 Running 0 85s lab-worker
kube-proxy-9z5x9 1/1 Running 0 86s lab-control-plane
kube-proxy-h7xtp 1/1 Running 0 86s lab-worker2
kube-scheduler-lab-control-plane 1/1 Running 0 93s lab-control-plane
```
**Значит:** ровно та картина, которую описывает теория модуля 2. Четыре компонента control-plane (`kube-apiserver`, `etcd`, `kube-controller-manager`, `kube-scheduler`) все с суффиксом `-lab-control-plane` в имени и все в колонке `NODE` только на `lab-control-plane`, других экземпляров нет. Два DaemonSet'а `kindnet-*` (CNI, сетевая связность между подами на разных нодах) и `kube-proxy-*` (сетевые правила для Service, разбирается в модуле 5) у каждого ровно по одному поду на каждую из 3 нод (control-plane и оба worker), что и есть определение DaemonSet: «один под на каждую подходящую ноду». `coredns-*` не DaemonSet, а обычный Deployment на 2 реплики; обе оказались на `lab-control-plane`, потому что worker-ноды пока полностью пустые, и scheduler разместил их там, где выгоднее (это никак не привязано к ролям нод просто текущее решение scheduler'а по ресурсам).
Устная самопроверка модуля 2 (3 вопроса из LEARNING.md компоненты control-plane, компоненты worker-ноды, почему etcd в проде выносят отдельно с нечётным числом нод) пройдена развёрнуто и верно. Дополнительно самостоятельно разобрана разница iptables/nftables применительно к двум режимам работы `kube-proxy` не входит в буквальную программу модуля 2, но напрямую относится к роли `kube-proxy` и пригодится в модуле 5 (Service и сеть).
---
### 28. Модуль 3 — `pod.yaml` (Namespace + Pod) и создание
**Команда:**
```bash
cat > ~/k8s/pod.yaml << 'EOF'
apiVersion: v1
kind: Namespace
metadata:
name: demo
---
apiVersion: v1
kind: Pod
metadata:
name: demo-pod
namespace: demo
labels:
app: demo
spec:
containers:
- name: hello
image: nginxdemos/hello:latest
ports:
- containerPort: 80
EOF
kubectl apply -f ~/k8s/pod.yaml
```
**Разбор аргументов:**
- Один файл, два объекта через разделитель `---` YAML позволяет описать несколько документов в одном файле, `kubectl apply -f` применяет их по порядку сверху вниз. Namespace идёт первым не случайно: Pod ссылается на `namespace: demo` во втором документе, а этот namespace должен уже существовать (или быть создан в той же команде apply) к моменту создания Pod.
- Четыре ключа верхнего уровня из теории модуля 3: `apiVersion` (версия API `v1` для обоих базовых типов), `kind` (тип объекта), `metadata` (имя/namespace/labels «паспорт»), `spec` (только у Pod желаемое состояние: какой контейнер запускать).
- `labels: app: demo` метка на Pod, пока не используется явно, но именно так Service/Deployment в следующих модулях будут находить нужные поды через selector.
**Получен ответ:**
```
namespace/demo created
pod/demo-pod created
```
**Значит:** оба объекта созданы с первого раза, ошибок в манифесте нет.
---
### 29. Модуль 3 — базовый набор команд: get, describe, logs, exec
**Команда:**
```bash
kubectl -n demo get pods
kubectl -n demo get pods -o wide
kubectl -n demo describe pod demo-pod
kubectl -n demo logs demo-pod
kubectl -n demo exec -it demo-pod -- sh
```
**Разбор аргументов:**
- `-n demo` на каждой команде явно указывает namespace (без него kubectl смотрит в `default`, где ничего нет).
- `get pods -o wide` `-o wide` добавляет колонки `IP` и `NODE` к базовому выводу.
- `describe pod` в отличие от `get`, разворачивает объект полностью: условия (`Conditions`), примонтированные volume, и главное секцию `Events` (хронологический лог того, что control-plane и kubelet делали с этим подом: `Scheduled` `Pulling`/`Pulled` `Created` `Started`).
- `logs demo-pod` без указания контейнера (работает, потому что в поде он один); печатает stdout/stderr процесса внутри контейнера.
- `exec -it ... -- sh` `-i` (interactive) + `-t` (tty) дают полноценный интерактивный терминал внутри контейнера; `sh`, а не `bash`, потому что образ `nginxdemos/hello` собран на Alpine (slim-образ без bash).
**Получен ответ:**
```
NAME READY STATUS RESTARTS AGE
demo-pod 1/1 Running 0 25s
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
demo-pod 1/1 Running 0 31s 10.244.2.2 lab-worker <none> <none>
Node: lab-worker/172.22.0.2
Status: Running
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 89s default-scheduler Successfully assigned demo/demo-pod to lab-worker
Normal Pulling 88s kubelet Pulling image "nginxdemos/hello:latest"
Normal Pulled 82s kubelet Successfully pulled image ... in 6.311s
Normal Created 82s kubelet Container created
Normal Started 82s kubelet Container started
2026/07/18 19:54:20 [notice] 1#1: nginx/1.29.1
2026/07/18 19:54:20 [notice] 1#1: start worker process 27..30
/ # exit
```
**Значит:** под запланирован планировщиком на `lab-worker` (control-plane исключён taint'ом), образ скачался за 6.3с, контейнер стартовал и поднял 4 worker-процесса nginx. `logs` показал именно вывод приложения (nginx), `describe`/`Events` действия самого Kubernetes вокруг пода как объекта; это и есть разница между ними. `exec` подтвердил, что контейнер полноценно интерактивен.
---
### 30. Модуль 3 — полный манифест объекта (`-o yaml`)
**Команда:**
```bash
kubectl -n demo get pod demo-pod -o yaml
```
**Разбор аргументов:**
- `-o yaml` просит API-сервер отдать объект целиком в том виде, в каком он реально хранится в etcd, а не только колонки таблицы `get pods`.
**Получен ответ (сокращённо, полный лог — в сессии):**
```
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{...исходный JSON манифеста...}
resourceVersion: "2565"
uid: ea8061a9-eca7-4cc0-91d0-86b6e87af510
spec:
containers:
- imagePullPolicy: Always
...
nodeName: lab-worker
restartPolicy: Always
schedulerName: default-scheduler
serviceAccountName: default
terminationGracePeriodSeconds: 30
tolerations: [...]
volumes:
- name: kube-api-access-p5g68
projected: {...}
status:
conditions: [...]
containerStatuses: [...]
phase: Running
podIP: 10.244.2.2
hostIP: 172.22.0.2
```
**Значит:** написанные 12 строк манифеста развернулись примерно в 120. Сверх исходного `pod.yaml` появилось: служебные поля `metadata` (`uid`, `resourceVersion`, аннотация с сохранённым исходным манифестом на ней основан three-way merge при следующих `apply`); дефолты в `spec`, которые Kubernetes подставил сам (`imagePullPolicy`, `restartPolicy`, `schedulerName`, `serviceAccountName`, `tolerations`, автомонтированный volume с токеном сервис-аккаунта, `nodeName` результат работы планировщика); и целиком раздел `status`, которого в манифесте нет вообще это runtime-состояние, которое пишет kubelet, наблюдая за реальным контейнером. Итог: `spec` желаемое состояние (то, что попросили, плюс дефолты), `status` фактическое состояние (что control loop реально наблюдает) основа declarative-модели Kubernetes.
---
### 31. Модуль 3 — удаление голого Pod (без self-healing)
**Команда:**
```bash
kubectl -n demo delete pod demo-pod
kubectl -n demo get pods
```
**Разбор аргументов:**
- В отличие от модуля 1 (Deployment), здесь Pod создан напрямую, без контроллера сверху.
**Получен ответ:**
```
pod "demo-pod" deleted from demo namespace
No resources found in demo namespace.
```
**Значит:** под удалился и не пересоздался namespace `demo` пуст. Это и есть отличие голого Pod от Pod'а под управлением Deployment/ReplicaSet: `restartPolicy: Always` перезапускает контейнер *внутри* пода при его падении, но если исчезает сам Pod (удалён вручную или нода, на которой он жил, выходит из строя), пересоздать его некому за это отвечает контроллер (ReplicaSet), которого здесь нет. Отсюда правило курса: голые Pod'ы в проде почти не используют, кроме короткоживущих задач.
Устная самопроверка модуля 3 (роли ключей манифеста, `logs` vs `describe`, что добавляет `-o yaml`, плюс доп. вопрос про поведение при падении ноды) пройдена развёрнуто и верно.
---
## Соглашение на будущее
Начиная с этой сессии, каждая команда, которую я выполняю в рамках курса (не только сегодня), добавляется в этот файл новой датированной секцией в том же формате: задача команда разбор аргументов полученный ответ что он значит. `PROGRESS.md` при этом остаётся коротким журналом-указателем со ссылкой на соответствующий раздел здесь.

View File

@@ -8,7 +8,7 @@
**Окружение курса** (одно на все модули, то же, что в CASE.md): Windows + Docker Desktop, кластер `kind` с именем `lab`, 1 control-plane + 2 worker. Все команды курса — в bash-синтаксисе (heredoc, `base64 -d`, настоящий `curl`), поэтому выполнять их нужно в **Git Bash** (ставится вместе с Git for Windows), а не в PowerShell/cmd — там половина команд не сработает или сработает иначе (`curl` в PowerShell — алиас на `Invoke-WebRequest`). Docker Desktop стоит выделить не меньше 8 ГБ RAM — иначе стек мониторинга из модуля 12 повиснет в Pending. Не пересоздавай кластер между модулями без необходимости — большинство модулей продолжают работать в одном и том же кластере и в одних и тех же namespace (`demo`, `monitoring`), это ближе к тому, как выглядит реальная работа с уже существующим кластером.
Альтернативное окружение — прохождение курса на удалённом Ubuntu-сервере по SSH вместо Windows + Docker Desktop: спеки железа, бенчмарки и точечные адаптации команд по модулям — в [SERVER.md](SERVER.md). Текущий прогресс прохождения (какой модуль пройден, на чём остановился, состояние кластера) — в [PROGRESS.md](PROGRESS.md), построчный разбор каждой выполненной команды с объяснением аргументов — в [COMMANDS.md](COMMANDS.md).
Альтернативное окружение — прохождение курса на удалённом Ubuntu-сервере по SSH вместо Windows + Docker Desktop: спеки железа, бенчмарки и точечные адаптации команд по модулям — в [SERVER.md](SERVER.md). Текущий прогресс прохождения (какой модуль пройден, на чём остановился, состояние кластера) — в [PROGRESS.md](PROGRESS.md), построчный разбор каждой выполненной команды с объяснением аргументов — в [COMMANDS.md](COMMANDS.md), а как просто запустить/остановить/проверить стенд между сессиями без учебных объяснений — в [USAGE.md](USAGE.md).
---

View File

@@ -1,6 +1,6 @@
# Прогресс прохождения курса Kubernetes
Живой журнал, а не справочник — обновляется в конце каждой сессии по курсу. Цель: за 30 секунд понять, где остановился, и продолжить без перечитывания всего [LEARNING.md](LEARNING.md). Окружение и бенчмарки — в [SERVER.md](SERVER.md), построчный разбор каждой выполненной команды (что делает каждый аргумент, что значит ответ) — в [COMMANDS.md](COMMANDS.md); сюда их не дублировать.
Живой журнал, а не справочник — обновляется в конце каждой сессии по курсу. Цель: за 30 секунд понять, где остановился, и продолжить без перечитывания всего [LEARNING.md](LEARNING.md). Окружение и бенчмарки — в [SERVER.md](SERVER.md), построчный разбор каждой выполненной команды (что делает каждый аргумент, что значит ответ) — в [COMMANDS.md](COMMANDS.md), как запустить/остановить/проверить стенд между сессиями — в [USAGE.md](USAGE.md); сюда их не дублировать.
## Статус модулей
@@ -8,8 +8,8 @@
|---|---|---|---|
| 0 | Установка инструментов | ✅ Пройден | 2026-07-18 |
| 1 | Зачем вообще Kubernetes | ✅ Пройден (как смоук-тест наладки) | 2026-07-18 |
| 2 | Архитектура кластера | Не начат | |
| 3 | Pod и kubectl-база | Не начат | |
| 2 | Архитектура кластера | ✅ Пройден | 2026-07-18 |
| 3 | Pod и kubectl-база | ✅ Пройден | 2026-07-18 |
| 4 | Deployment и ReplicaSet | ⬜ Не начат | |
| 5 | Service и сеть | ⬜ Не начат | |
| 6 | Ingress | ⬜ Не начат | |
@@ -41,12 +41,42 @@
Затыки: нет (модуль 0 и смоук-тест прошли гладко).
### 2026-07-18 — модуль 2: архитектура кластера
Формат сессии новый: команды на сервере выполнял сам, мне присылались логи шаг за шагом — я вёл по шагам и разбирал вывод.
Сделано:
- Кластер `lab` пересоздан с топологией 1 control-plane + 2 worker через `~/k8s/kind-config.yaml`, сразу с `extraPortMappings` 8080→80/8443→443 (адаптация из SERVER.md) — все 3 ноды `Ready` за 46-57с. Разбор команд — [COMMANDS.md](COMMANDS.md) #24-26.
- Вживую найдены и разобраны все 4 компонента control-plane (`kube-apiserver`, `etcd`, `kube-controller-manager`, `kube-scheduler` — все на `lab-control-plane`) и оба DaemonSet'а (`kindnet`, `kube-proxy` — по одному поду на каждую из 3 нод); `coredns` опознан как обычный Deployment (2 реплики), а не DaemonSet. Разбор — [COMMANDS.md](COMMANDS.md) #27.
- Устная самопроверка модуля 2 (компоненты control-plane/worker, кворум etcd) пройдена развёрнуто и верно. Дополнительно самостоятельно разобрана разница iptables/nftables применительно к режимам `kube-proxy` (сверх программы модуля 2, пригодится в модуле 5).
- CI-стек Gitea проверен перед началом — все 4 контейнера Up/healthy, не задет.
Осталось на следующую сессию:
- **Модуль 3** — Pod и kubectl-база: создать `pod.yaml` (Namespace `demo` + Pod `demo-pod`), пройти базовый набор команд (`get`, `describe`, `logs`, `exec`, `-o yaml`, `delete`).
Затыки: нет, модуль 2 прошёл гладко.
### 2026-07-18 — модуль 3: Pod и kubectl-база
Формат сессии тот же: команды на сервере выполнял сам, логи присылались шаг за шагом.
Сделано:
- Создан `~/k8s/pod.yaml` (Namespace `demo` + Pod `demo-pod`, образ `nginxdemos/hello:latest`), применён — под ушёл в `Running` на `lab-worker` за ~7с (время скачивания образа). Разбор — [COMMANDS.md](COMMANDS.md) #28.
- Пройден базовый набор команд: `get pods [-o wide]`, `describe pod` (разобрана секция `Events` как таймлайн действий control-plane/kubelet), `logs` (вывод самого nginx), `exec -it ... -- sh` (интерактивный вход внутрь контейнера). Разбор — [COMMANDS.md](COMMANDS.md) #29.
- Разобран `kubectl get pod -o yaml` построчно: чем ~120 строк реального объекта отличаются от 12 строк исходного манифеста (служебные поля metadata, дефолты в spec, целиком раздел status как runtime-состояние от kubelet) — основа declarative-модели K8s (`spec` = желаемое, `status` = фактическое). Разбор — [COMMANDS.md](COMMANDS.md) #30.
- Под удалён (`kubectl delete pod`) — наглядно показано отсутствие self-healing у голого Pod без контроллера сверху (в отличие от Deployment в модуле 1). Разбор — [COMMANDS.md](COMMANDS.md) #31.
- Устная самопроверка модуля 3 (4 ключа манифеста, `logs` vs `describe`, что добавляет `-o yaml`, плюс доп. вопрос про поведение при падении ноды) пройдена развёрнуто и верно.
Осталось на следующую сессию:
- **Модуль 4** — Deployment и ReplicaSet: `deployment.yaml` (3 реплики), посмотреть, как Deployment управляет ReplicaSet, а ReplicaSet — подами.
Затыки: нет, модуль 3 прошёл гладко.
## Состояние кластера прямо сейчас
- Кластер `lab` **существует**, 1-нодовый (control-plane, без workers), создан дефолтной командой `kind create cluster --name lab` — это ещё не топология из модуля 2.
- Namespace `demo` и `monitoring`**не созданы**.
- В кластере ничего не развёрнуто (deployment `demo` из смоук-теста удалён).
- Перед модулем 2 кластер будет пересоздан командой из курса (`kind delete cluster --name lab` + `kind create cluster --name lab --config kind-config.yaml`) — это ожидаемо и совпадает с шагами модуля 2, ничего вручную чистить не нужно.
- Кластер `lab` **существует**, топология модуля 2: 1 control-plane (`lab-control-plane`) + 2 worker (`lab-worker`, `lab-worker2`), создан через `kind-config.yaml` с `extraPortMappings` 8080→80/8443→443.
- Namespace `demo` **существует** (создан в модуле 3), но пуст — `demo-pod` был удалён по завершении практики модуля 3. Namespace `monitoring` — не создан.
- В кластере, кроме namespace `demo`, ничего не развёрнуто.
## Открытые вопросы / затыки

133
stack/kubernetes/USAGE.md Normal file
View File

@@ -0,0 +1,133 @@
# USAGE: как пользоваться стендом (запуск / остановка / проверка)
Операционная шпаргалка, а не учебный материал — сюда смотришь, когда нужно просто поднять/погасить стенд между сессиями курса, без объяснений «зачем». Учебный курс — [LEARNING.md](LEARNING.md), окружение и характеристики сервера — [SERVER.md](SERVER.md), на чём остановился по модулям — [PROGRESS.md](PROGRESS.md), разбор каждой команды — [COMMANDS.md](COMMANDS.md).
## 1. Подключение
```bash
ssh totserver@192.168.31.163
```
Ключ уже настроен, пароль не спросит. Дальше два варианта:
- **Обычная интерактивная SSH-сессия** (просто зашёл и работаешь в терминале) — это login shell, `kind`/`helm`/`kubectl`/`docker` уже все видны в `PATH` без танцев.
- **Одна неинтерактивная команда с хоста** (`ssh totserver@192.168.31.163 'команда'`) — это **non-login shell**, `~/.local/bin` (где лежат `kind` и `helm`) в `PATH` не попадает. Либо вызывать по полному пути (`~/.local/bin/kind ...`), либо оборачивать в `bash -lc "..."`:
```bash
ssh totserver@192.168.31.163 'bash -lc "kubectl get nodes"'
```
`kubectl` (snap) и `docker` видны в обоих случаях без исключений.
## 2. Быстрая проверка состояния
Одним блоком — что вообще сейчас поднято:
```bash
bash -lc '
echo "--- kind-контейнеры ---"
docker ps -a --filter "name=^lab-" --format "{{.Names}}\t{{.Status}}"
echo "--- kubectl context ---"
kubectl config current-context 2>&1
echo "--- ноды ---"
kubectl get nodes -o wide 2>&1
echo "--- поды по всем namespace ---"
kubectl get pods -A 2>&1
echo "--- helm-релизы ---"
helm list -A 2>&1
echo "--- CI-стек Gitea (не трогать, но убедиться, что жив) ---"
docker ps --filter name=gitea --format "{{.Names}}\t{{.Status}}"
'
```
Если первый блок (`docker ps -a --filter "name=^lab-"`) пустой — кластера нет вообще, нужно создавать (раздел 3). Если контейнеры есть, но статус `Exited` — кластер существует, но остановлен, поднимать без пересоздания (раздел 3). Если `Up` — кластер уже работает, `kubectl get nodes` покажет `Ready`.
## 3. Запуск кластера
**Кластера нет вообще** (`docker ps -a` по `lab-*` пусто) — создать заново:
```bash
# одна нода, как сейчас (модули 0-1 пройдены на таком)
bash -lc "kind create cluster --name lab"
# либо с топологией модуля 2 (1 control-plane + 2 worker), если уже дошёл до неё —
# см. kind-config.yaml с extraPortMappings 8080/8443 в SERVER.md
bash -lc "kind create cluster --name lab --config kind-config.yaml"
```
Занимает ~50-60 секунд с нуля (в основном скачивание образа ноды при первом разе).
**Контейнеры есть, но `Exited`** (типичная ситуация после перезагрузки сервера — у kind-нод, в отличие от Gitea, нет `restart: unless-stopped`, сами не поднимаются) — поднять существующие без потери состояния:
```bash
ssh totserver@192.168.31.163 'docker start $(docker ps -aq --filter "name=^lab-")'
ssh totserver@192.168.31.163 'bash -lc "kubectl wait --for=condition=Ready nodes --all --timeout=120s"'
```
После любого из двух вариантов — проверить, что kubectl смотрит именно в этот кластер:
```bash
ssh totserver@192.168.31.163 'bash -lc "kubectl config current-context"'
# ожидается: kind-lab
```
## 4. Остановка кластера (пауза, не удаление)
Когда заканчиваешь сессию курса и не хочешь держать кластер прогретым (он ест ~500 МБ RAM и часть ядра в простое — не критично, но незачем без нужды):
```bash
ssh totserver@192.168.31.163 'docker stop $(docker ps -aq --filter "name=^lab-")'
```
Поды, PVC, всё состояние кластера остаётся на диске и возвращается как было после `docker start` (раздел 3).
## 5. Полный снос кластера
Когда нужно чистое состояние — например, начиная модуль 2 с новой топологией (см. [PROGRESS.md](PROGRESS.md) → «Осталось на следующую сессию»):
```bash
ssh totserver@192.168.31.163 'bash -lc "kind delete cluster --name lab"'
```
Безопасно для остального на сервере — kind-образы отдельные от всего, что использует Gitea.
## 6. Чего не трогать
Рядом на том же Docker крутится боевой CI-стек Gitea — не часть курса, но легко случайно задеть. Коротко (полный чек-лист — [SERVER.md → «CI-инфраструктура на сервере — что нельзя ломать»](SERVER.md#ci-инфраструктура-на-сервере--что-нельзя-ломать)):
- Не занимать порты **80, 443, 3000, 3030, 222, 4400, 9090** в `kind-config.yaml` (`extraPortMappings` — только 8080/8443).
- Не выполнять `docker system prune -a` / `docker image prune -a` — унесёт локально собранный образ `gitea-runner-armhf`, которого больше нигде нет.
- Не удалять `.runner`-файлы раннеров (`/opt/gitea/runner*/`) и `.env` рядом.
- Не запускать `docker-compose ... --remove-orphans` внутри `/opt/gitea`.
- `kind delete cluster` (раздел 5) — безопасен, гасит только кластер, Gitea не задевает.
## 7. Куда дальше
- На чём остановился по модулям курса — [PROGRESS.md](PROGRESS.md).
- Что проходить дальше и как — [LEARNING.md](LEARNING.md).
### Шпаргалка
```
# === СОСТОЯНИЕ КЛАСТЕРА ===
kubectl get nodes -o wide
kubectl get pods -A
kubectl get pods,svc,deploy # pods, services, deployments разом
# === ДЕТАЛИ ===
kubectl describe pod <имя>
kubectl logs <имя-пода> --tail=20
kubectl logs -f <имя-пода> # follow
# === ВНУТРЬ ПОДА ===
kubectl exec -it <имя-пода> -- /bin/sh
kubectl exec <имя-пода> -- ls /app
# === СОЗДАТЬ/УДАЛИТЬ ===
kubectl apply -f файл.yaml
kubectl delete -f файл.yaml
kubectl delete pod <имя>
# === ПОМОЩЬ ===
kubectl api-resources | grep -v "^NAME" | less # что бывает
kubectl explain deployment --recursive | less # все поля deployment
```