Модуль 17 / 19
GitLab CI/CD → Kubernetes
Зміст
1. Цілі модуля
Ви навчитеся проєктувати безпечний GitLab pipeline для Kubernetes: один раз збирати образ, перевіряти й підписувати immutable digest, розгортати через GitLab Agent і підтверджувати результат після deploy.
- pipeline graph та artifacts
- immutable image digest
- SBOM, vulnerability scan і Cosign
- GitLab Agent та KUBE_CONTEXT
- protected environments і rollback
2. Теорія: pipeline як ланцюг довіри
Pipeline має просувати той самий artifact між середовищами, а не перебудовувати його. Commit SHA зручний як tag, але deploy фіксує digest виду repository@sha256:…, який неможливо непомітно перепризначити.
Stages виражають порядок, needs — залежності даних, artifacts передають звіти, dotenv — обчислений IMAGE_REF. Cache прискорює повторні завантаження, але не є довіреним build artifact.
3. Теорія: supply-chain security
Мінімальний ланцюг: відтворювана збірка → SBOM → vulnerability policy → підпис digest → перевірка identity підписанта → admission policy у кластері. Scan не доводить відсутність уразливостей, а підпис не доводить безпечність вмісту; це різні твердження.
GitLab.com може видавати OIDC ID token з audience sigstore для keyless Cosign. Короткоживучий ключ і certificate пов’язують підпис із pipeline identity. Self-managed GitLab потребує власної довіреної Sigstore infrastructure або керованих ключів.
4. Теорія: GitLab Agent і межі доступу
GitLab Agent установлює outbound-з’єднання з KAS, тому runner не потребує прямого inbound-доступу до API Server. Авторизовані CI jobs отримують kubeconfig через KUBECONFIG і обирають context формату project/path:agent-name.
Окремий Agent потрібен для кожного кластера. Використовуйте environment-scoped KUBE_CONTEXT, окремі ServiceAccounts і namespaced RBAC; не зберігайте cluster-admin kubeconfig в CI variables. Дозвіл проєкту в agent config сам собою не замінює Kubernetes RBAC.
5. Теорія: push deploy або GitOps
Прямий CI deploy є push-based: job отримує право змінювати кластер. Він доречний для staging, міграції та спеціальних pipeline-driven процедур, але GitLab рекомендує GitOps для production через сильнішу модель безпеки.
За GitOps pipeline будує, перевіряє й публікує artifact, потім змінює декларативний desired state через merge request. In-cluster controller виконує pull і reconciliation; production credentials не видаються build runner.
6. Методичні вказівки
- Захистіть default branch, tags, CI configuration і production environment
- Використовуйте deployment approvals і manual promotion для production
- resource_group серіалізує deploy одного environment
- Розділяйте build і deployment projects за суворої моделі доступу
- Використовуйте masked/protected/file variables та зовнішнє secret storage
- Фіксуйте CI images за digest у production
- Не виводьте tokens, kubeconfig і registry password
- Зберігайте security reports та deployment evidence з розумним retention
- Rollback перевіряйте тим самим smoke test, що й deploy
7. Лабораторна робота: доступ через Agent
Після реєстрації Agent збережіть конфігурацію як .gitlab/agents/staging/config.yaml, замінивши project id на реальний шлях. У GitLab створіть environment-scoped protected variable KUBE_CONTEXT зі значенням шляху context вашого Agent.
ci_access:
projects:
- id: platform/course-webДля namespace course-staging надайте CI identity лише get/list/watch і необхідні зміни Deployment/Service. Перевірте дозволи через kubectl auth can-i до першого deploy.
8. Лабораторна робота: manifest з immutable image
Збережіть manifest як k8s/deployment.yaml. Pipeline замінює лише маркер IMAGE_REFERENCE на digest із build job; у Git зберігається шаблон без mutable tag.
apiVersion: apps/v1
kind: Deployment
metadata:
name: course-web
namespace: course-staging
spec:
replicas: 2
selector:
matchLabels: {app: course-web}
template:
metadata:
labels:
app: course-web
spec:
containers:
- name: web
image: IMAGE_REFERENCE
ports: [{name: http, containerPort: 8080}]
readinessProbe:
httpGet: {path: /healthz, port: http}
resources:
requests: {cpu: 50m, memory: 64Mi}
limits: {memory: 128Mi}9. Лабораторна робота: build, scan, sign і deploy
Збережіть приклад як .gitlab-ci.yml та адаптуйте health path до застосунку. Він розрахований на GitLab.com для public Sigstore і на Docker runner, якому дозволено dind. Для production зафіксуйте CI images за перевіреними digests.
stages: [validate, build, scan, sign, deploy, verify]
variables:
IMAGE_TAG: "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA"
DOCKER_TLS_CERTDIR: "/certs"
validate:
stage: validate
image: alpine/helm:4.2.4
script:
- helm lint ./chart -f ./chart/values-staging.yaml
- helm template course-web ./chart -f ./chart/values-staging.yaml > rendered.yaml
artifacts:
paths: [rendered.yaml]
build:
stage: build
image: docker:28.4.0-cli
services:
- name: docker:28.4.0-dind
before_script:
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY"
script:
- docker buildx build --pull --push --tag "$IMAGE_TAG" .
- IMAGE_DIGEST="$(docker buildx imagetools inspect "$IMAGE_TAG" --format '{{.Manifest.Digest}}')"
- test -n "$IMAGE_DIGEST"
- echo "IMAGE_REF=$CI_REGISTRY_IMAGE@$IMAGE_DIGEST" > build.env
artifacts:
reports:
dotenv: build.env
scan:
stage: scan
image:
name: aquasec/trivy:0.66.0
entrypoint: [""]
script:
- trivy image --username "$CI_REGISTRY_USER" --password "$CI_REGISTRY_PASSWORD" --format cyclonedx --output gl-sbom-report.cdx.json "$IMAGE_REF"
- trivy image --username "$CI_REGISTRY_USER" --password "$CI_REGISTRY_PASSWORD" --exit-code 1 --severity HIGH,CRITICAL --ignore-unfixed "$IMAGE_REF"
artifacts:
when: always
paths: [gl-sbom-report.cdx.json]
reports:
cyclonedx: gl-sbom-report.cdx.json
sign:
stage: sign
image:
name: ghcr.io/sigstore/cosign/cosign:v3.1.2
entrypoint: [""]
id_tokens:
SIGSTORE_ID_TOKEN:
aud: sigstore
variables:
COSIGN_YES: "true"
script:
- cosign login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY"
- cosign sign "$IMAGE_REF"
- cosign verify "$IMAGE_REF" --certificate-identity-regexp "^https://gitlab.com/$CI_PROJECT_PATH//.gitlab-ci.yml@refs/heads/$CI_DEFAULT_BRANCH$" --certificate-oidc-issuer "https://gitlab.com"
deploy_staging:
stage: deploy
image:
name: registry.k8s.io/kubectl:v1.37.0
entrypoint: [""]
resource_group: course-staging
environment:
name: staging
script:
- kubectl config use-context "$KUBE_CONTEXT"
- kubectl create namespace course-staging --dry-run=client -o yaml | kubectl apply -f -
- sed "s|IMAGE_REFERENCE|$IMAGE_REF|" k8s/deployment.yaml > deployment.rendered.yaml
- kubectl apply --dry-run=server -f deployment.rendered.yaml
- kubectl apply --server-side --field-manager=gitlab-ci -f deployment.rendered.yaml
- kubectl rollout status deployment/course-web -n course-staging --timeout=180s
rules:
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
verify_staging:
stage: verify
image:
name: registry.k8s.io/kubectl:v1.37.0
entrypoint: [""]
environment:
name: staging
script:
- kubectl config use-context "$KUBE_CONTEXT"
- kubectl get deployment,pod -n course-staging -o wide
- kubectl get deployment course-web -n course-staging -o jsonpath='{.spec.template.spec.containers[0].image}{"\n"}'
- kubectl wait deployment/course-web -n course-staging --for=condition=Available --timeout=60s
needs: [deploy_staging]У Settings → CI/CD захистіть environment production/staging за своєю моделлю, задайте allowed deployers та approvals. Критерій: scan створює CycloneDX SBOM, signature перевірено, у Deployment записано той самий IMAGE_REF, rollout і verify успішні.
10. Діагностика: deploy job зелений, rollout червоний
Спочатку розрізніть помилку доступу Agent, admission rejection, apply conflict і відмову workload. Зберіть контекст і стан до rollback.
kubectl config current-context
kubectl auth can-i patch deployments -n course-staging
kubectl get deployment course-web -n course-staging -o yaml
kubectl rollout status deployment/course-web -n course-staging --timeout=60s
kubectl get replicaset,pod -n course-staging -o wide
kubectl events -n course-staging --types=Warning
kubectl logs -n course-staging -l app=course-web --all-containers --tail=100 --prefix
kubectl rollout history deployment/course-web -n course-staging
kubectl rollout undo deployment/course-web -n course-staging
kubectl rollout status deployment/course-web -n course-staging --timeout=180sАвтоматичний rollback доречний лише за надійного health signal. За database migration старий image може бути несумісним зі схемою: використовуйте expand/contract та окремий tested rollback plan.
11. Контроль і шпаргалка
- Чому не можна перебудовувати image для production?
- Чим tag відрізняється від digest?
- Що доводять SBOM, scan і signature?
- Навіщо environment-scoped KUBE_CONTEXT?
- Що серіалізує resource_group?
- Чому GitOps безпечніший за push-based production deploy?
- Коли rollout undo може погіршити інцидент?
Модуль засвоєно, якщо pipeline переносить один digest через перевірки, використовує Agent без cluster-admin credentials, зберігає evidence і має перевірюваний promotion/rollback процес.
PDF для роботи офлайн
Завантажте оформлену версію модуля для читання без підключення до мережі.