Доработка документации
This commit is contained in:
@@ -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` при этом остаётся коротким журналом-указателем — со ссылкой на соответствующий раздел здесь.
|
||||
|
||||
Reference in New Issue
Block a user