Модуль 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 або export
- 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 для роботи офлайн
Завантажте оформлену версію модуля для читання без підключення до мережі.