Модуль 19 / 19
Финальный проект: production-like платформа
Содержание
1. Цели итогового модуля
Итоговый проект объединяет архитектуру, Kubernetes API, workloads, networking, storage/configuration, security, observability, CI/CD, GitOps и operations в один проверяемый результат.
- репозиторий как source of truth
- immutable delivery
- надёжный и ограниченный workload
- SLI/SLO и alerts
- failure drills и evidence
- эксплуатационная документация
2. Сценарий проекта
Вы отвечаете за сервис course-web в staging и production. Команда требует безопасный rollout без потери доступности, автоматическое восстановление экземпляров, ограниченный сетевой и API-доступ, наблюдаемость, supply-chain проверки и GitOps promotion одного image digest.
Локальный kind используется для функциональной приёмки. Ограничения single-host среды — отсутствие реальной zone failure, cloud load balancer и production storage — фиксируются явно, а не скрываются зелёными тестами.
3. Требуемая структура репозитория
- app/ — исходный код, unit tests, Dockerfile и health endpoints
- chart/ или deploy/base/ — базовые Kubernetes manifests
- deploy/overlays/staging и deploy/overlays/production — только различия окружений
- argocd/ — AppProject, Applications или ApplicationSet
- .gitlab-ci.yml — validate, build, scan, sign, publish и promotion
- docs/architecture.md — границы системы и зависимости
- docs/runbooks/ — deploy, rollback, degraded service и disaster recovery
- docs/evidence/ — отчёт лаборатории без credentials и secrets
Generated manifests, kubeconfig, private keys, tokens и local snapshots не коммитятся. README содержит prerequisites, bootstrap, verification, cleanup и известные ограничения.
4. Архитектурные требования
- Deployment минимум с тремя replicas, RollingUpdate, readiness/liveness и progress deadline
- requests/limits, non-root, dropped capabilities, read-only filesystem и RuntimeDefault seccomp
- Service и проверяемые EndpointSlices
- PDB и topology spread; объясните поведение при недостатке Nodes
- HPA с Metrics Server и безопасным scale-down
- ConfigMap/Secret separation; production secrets поступают из утверждённого secret system
- NetworkPolicy с описанным CNI enforcement
- отдельный ServiceAccount с automount=false, если Kubernetes API не нужен
5. Требования к delivery и GitOps
Pipeline выполняет tests, manifest validation, image build, SBOM, vulnerability policy и signature verification. Между staging и production продвигается один digest без rebuild. Production promotion выполняется merge request с approval.
Argo CD Application использует ограниченный AppProject, automated sync, selfHeal и осознанный prune. targetRevision и dependencies фиксируются. Rollback меняет desired state через Git; прямой kubectl write считается break-glass и документируется.
6. SLI, SLO и error budget
Для учебного проекта примите SLO: availability 99,9% за rolling 30 days, успешность HTTP не ниже 99%, p95 latency ниже 300 ms и успешный rollback до 10 минут. 99,9% availability соответствует примерно 43 минутам 12 секундам error budget за 30 дней.
Определите источник каждого SLI, PromQL, окно, исключения и владельца. Добавьте multi-window burn-rate alerts, dashboard и runbook. Эти числа — учебная гипотеза; production targets выводятся из пользовательских ожиданий и стоимости надёжности.
7. Методические указания
- Сначала напишите acceptance criteria, затем реализацию
- Каждое решение свяжите с конкретным риском или SLO
- Не добавляйте инструмент без владельца и runbook
- Используйте immutable versions и digests
- Проверяйте позитивные и негативные security cases
- Failure drill должен иметь hypothesis, steady state, action, evidence и recovery
- Не удаляйте evidence до postmortem
- Явно отделяйте проверенное от предположений и ограничений среды
8. Лабораторная работа: базовая платформа
Сохраните manifest как deploy/base/application.yaml. Он создаёт hardened учебный HTTP workload, Service, PDB, HPA и NetworkPolicy. NetworkPolicy реально применяется только CNI с поддержкой policy.
apiVersion: v1
kind: Namespace
metadata:
name: final-project
labels:
course.example.com/access: allowed
---
apiVersion: v1
kind: ConfigMap
metadata: {name: web-content, namespace: final-project}
data:
index.html: |
<!doctype html>
<html><body><h1>final-project: ok</h1></body></html>
---
apiVersion: apps/v1
kind: Deployment
metadata: {name: web, namespace: final-project}
spec:
replicas: 3
revisionHistoryLimit: 5
progressDeadlineSeconds: 120
strategy:
rollingUpdate: {maxUnavailable: 0, maxSurge: 1}
selector:
matchLabels: {app: web}
template:
metadata:
labels: {app: web}
spec:
automountServiceAccountToken: false
securityContext:
seccompProfile: {type: RuntimeDefault}
containers:
- name: web
image: busybox:1.37.0
command: ["httpd", "-f", "-p", "8080", "-h", "/www"]
ports: [{name: http, containerPort: 8080}]
readinessProbe:
httpGet: {path: /, port: http}
initialDelaySeconds: 2
periodSeconds: 5
livenessProbe:
httpGet: {path: /, port: http}
initialDelaySeconds: 10
periodSeconds: 10
resources:
requests: {cpu: 20m, memory: 16Mi}
limits: {memory: 32Mi}
securityContext:
runAsNonRoot: true
runAsUser: 65534
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities: {drop: ["ALL"]}
volumeMounts:
- {name: content, mountPath: /www, readOnly: true}
volumes:
- name: content
configMap: {name: web-content}
---
apiVersion: v1
kind: Service
metadata: {name: web, namespace: final-project}
spec:
selector: {app: web}
ports: [{name: http, port: 80, targetPort: http}]
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata: {name: web, namespace: final-project}
spec:
maxUnavailable: 1
selector:
matchLabels: {app: web}
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata: {name: web, namespace: final-project}
spec:
scaleTargetRef: {apiVersion: apps/v1, kind: Deployment, name: web}
minReplicas: 3
maxReplicas: 6
behavior:
scaleDown:
stabilizationWindowSeconds: 300
metrics:
- type: Resource
resource:
name: cpu
target: {type: Utilization, averageUtilization: 60}
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata: {name: web-ingress, namespace: final-project}
spec:
podSelector:
matchLabels: {app: web}
policyTypes: [Ingress]
ingress:
- from:
- namespaceSelector:
matchLabels: {course.example.com/access: allowed}
ports:
- {protocol: TCP, port: 8080}9. Лабораторная работа: развёртывание и приёмка
Metrics Server должен работать по модулю 9. Примените точный manifest через Server-Side Apply и проверьте workload, traffic, autoscaling signal и effective permissions.
kubectl config current-context
kubectl apply --dry-run=server -f deploy/base/application.yaml
kubectl apply --server-side --field-manager=final-project -f deploy/base/application.yaml
kubectl rollout status deployment/web -n final-project --timeout=180s
kubectl wait pod -n final-project -l app=web --for=condition=Ready --timeout=120s
kubectl get deployment,pod,service,endpointslice,poddisruptionbudget,horizontalpodautoscaler -n final-project -o wide
kubectl run smoke -n final-project --rm -i --restart=Never --image=busybox:1.37.0 -- wget -qO- http://web
kubectl top pod -n final-project --containers
kubectl auth can-i --list -n final-projectКритерий: три Ready Pod, три ready endpoint, smoke test возвращает final-project: ok, PDB допускает максимум один voluntary disruption, HPA получает CPU metrics.
10. Failure drill №1: потеря Pod
Гипотеза: удаление одного Pod не нарушает доступность и Deployment создаёт экземпляр с новым UID. Сохраните timeline и Events.
POD="$(kubectl get pod -n final-project -l app=web -o jsonpath='{.items[0].metadata.name}')"
UID_BEFORE="$(kubectl get pod "$POD" -n final-project -o jsonpath='{.metadata.uid}')"
kubectl delete pod "$POD" -n final-project --wait=false
kubectl rollout status deployment/web -n final-project --timeout=120s
kubectl get pod -n final-project -l app=web -o custom-columns='NAME:.metadata.name,UID:.metadata.uid,READY:.status.containerStatuses[0].ready'
kubectl get event -n final-project --sort-by=.metadata.creationTimestamp
test -n "$UID_BEFORE"Успех: desired replicas восстановлены за 120 секунд, новый UID отличается, smoke/SLI не нарушают выбранный threshold.
11. Failure drill №2: ошибочная readiness
Гипотеза: плохая revision не входит в ready endpoints, rollout останавливается, а rollback восстанавливает трафик. Timeout первой команды ожидаем.
kubectl patch deployment web -n final-project --type=json -p='[{"op":"replace","path":"/spec/template/spec/containers/0/readinessProbe/httpGet/path","value":"/missing"}]'
kubectl rollout status deployment/web -n final-project --timeout=45s
kubectl get pod,endpointslice -n final-project -o wide
kubectl events -n final-project --types=Warning
kubectl rollout undo deployment/web -n final-project
kubectl rollout status deployment/web -n final-project --timeout=180s
kubectl run smoke-after-rollback -n final-project --rm -i --restart=Never --image=busybox:1.37.0 -- wget -qO- http://webУспех: доказан NotReady нового ReplicaSet, старые endpoints сохраняли обслуживание, rollback завершён менее чем за 10 минут, финальный smoke успешен.
12. Failure drill №3: неверный Service selector
Гипотеза: Pods останутся Ready, но Service потеряет endpoints и пользовательский запрос завершится ошибкой. Диагностика должна найти несоответствие labels без restart Pods.
kubectl patch service web -n final-project --type=merge -p='{"spec":{"selector":{"app":"wrong"}}}'
kubectl get endpointslice -n final-project -l kubernetes.io/service-name=web -o yaml
kubectl run failed-smoke -n final-project --rm -i --restart=Never --image=busybox:1.37.0 -- wget -T 5 -qO- http://web
kubectl get service web -n final-project -o jsonpath='{.spec.selector}{"\n"}'
kubectl get pod -n final-project -l app=web --show-labels
kubectl patch service web -n final-project --type=merge -p='{"spec":{"selector":{"app":"web"}}}'
kubectl run recovered-smoke -n final-project --rm -i --restart=Never --image=busybox:1.37.0 -- wget -qO- http://webУспех: пустой EndpointSlice объясняет отказ, selector восстановлен минимальным patch, smoke снова успешен. После успешного smoke сохраните evidence и только затем удалите лабораторный namespace.
13. Пакет доказательств
До cleanup соберите evidence и сохраните очищенный от чувствительных данных вывод в docs/evidence. Добавьте UTC timestamps, commit SHA, image digest, версии инструментов, ожидаемый и фактический результат.
git rev-parse HEAD
git status --short
kubectl version
kubectl get deployment web -n final-project -o yaml
kubectl get pod -n final-project -l app=web -o yaml
kubectl get event -n final-project --sort-by=.metadata.creationTimestamp
kubectl get endpointslice -n final-project -l kubernetes.io/service-name=web -o yaml
kubectl top pod -n final-project --containers
kubectl auth can-i --list -n final-project
kubectl delete namespace final-project- pipeline URL и security reports
- Argo CD revision, sync/health и diff
- rollout history и smoke results
- SLI dashboard/alert screenshots или экспорт
- timeline трёх drills
- найденные ограничения и follow-up actions
14. Приёмка и оценивание
- 20 баллов — воспроизводимый repository и документация
- 20 — корректные Kubernetes manifests и security context
- 15 — tests, SBOM, scan, signature и immutable digest
- 15 — ограниченный Argo CD project и GitOps promotion
- 15 — metrics/logs, SLI/SLO, alerts и runbook
- 15 — три воспроизводимых failure drills с evidence
Обязательные стоп-факторы: committed secret/kubeconfig, cluster-admin для приложения или CI, mutable latest в deploy, отсутствующий rollback, неработающий smoke test. Проект принят при 80/100 и отсутствии стоп-факторов.
15. Защита проекта
- Покажите путь commit → digest → Git promotion → Argo revision → Pod imageID
- Объясните каждое permission и NetworkPolicy rule
- Докажите, что readiness защищает traffic, а liveness не маскирует dependency failure
- Рассчитайте error budget и реакцию на быстрый burn
- Проведите случайно выбранный failure drill без подсказки
- Назовите три ограничения kind и production mitigation
Курс завершён, если другой инженер по README воспроизводит систему, проверяет её безопасность и наблюдаемость, выполняет drill и восстанавливает steady state без устных инструкций автора.
PDF для работы офлайн
Скачайте оформленную версию модуля для чтения без подключения к сети.