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