diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..cf88154 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "cSpell.ignoreWords": [ + "armhf", + "gitea", + "раннеров" + ] +} \ No newline at end of file diff --git a/notes.txt b/notes.txt new file mode 100644 index 0000000..e0b292a --- /dev/null +++ b/notes.txt @@ -0,0 +1,4 @@ +«Почему в серьёзном предприятии Gitea, а не GitLab?» Честный и защищаемый ответ: +GitLab CE тяжёлый по ресурсам (Postgres + Redis + Sidekiq + Gitaly, легко 4+ ГБ RAM). +Для скромного парка в закрытом контуре Gitea — один лёгкий Go-бинарь, разворачивается и обслуживается минимальными силами. +Под задачу «внутреннее хранение конфигов/ролей + CI кросс-сборки» этого достаточно. \ No newline at end of file diff --git a/stack/kubernetes/COMMANDS.md b/stack/kubernetes/COMMANDS.md index bba3b56..7f3c5e7 100644 --- a/stack/kubernetes/COMMANDS.md +++ b/stack/kubernetes/COMMANDS.md @@ -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 Debian GNU/Linux 13 (trixie) 6.8.0-136-generic (amd64) containerd://2.3.1 +lab-worker Ready 46s v1.36.1 172.22.0.2 Debian GNU/Linux 13 (trixie) 6.8.0-136-generic (amd64) containerd://2.3.1 +lab-worker2 Ready 47s v1.36.1 172.22.0.3 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`, две `` (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 + +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` при этом остаётся коротким журналом-указателем — со ссылкой на соответствующий раздел здесь. diff --git a/stack/kubernetes/LEARNING.md b/stack/kubernetes/LEARNING.md index b5c7dcb..9619197 100644 --- a/stack/kubernetes/LEARNING.md +++ b/stack/kubernetes/LEARNING.md @@ -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). --- diff --git a/stack/kubernetes/PROGRESS.md b/stack/kubernetes/PROGRESS.md index dc66daa..53878c4 100644 --- a/stack/kubernetes/PROGRESS.md +++ b/stack/kubernetes/PROGRESS.md @@ -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`, ничего не развёрнуто. ## Открытые вопросы / затыки diff --git a/stack/kubernetes/USAGE.md b/stack/kubernetes/USAGE.md new file mode 100644 index 0000000..bb87262 --- /dev/null +++ b/stack/kubernetes/USAGE.md @@ -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 +``` \ No newline at end of file