CI/CD в Kubernetes: сборка, деплой, прогрессивные релизы

Рейтинг: 45.3% · 9 голосов
Kubernetes для разработчиков: поды, деплойменты, сервисы, ingress, конфиги и отладка. Уроки по главам с обсуждением.
Ответить
Аватара пользователя
anton_k8s
Сообщения: 55
Зарегистрирован: 12 май 2026, 03:23

CI/CD в Kubernetes: сборка, деплой, прогрессивные релизы

Сообщение anton_k8s »

Оглавление курса (48)
  1. Зачем нужен Kubernetes и из чего состоит кластер
  2. Поднимаем локальный кластер: minikube и kind
  3. Поды: базовая единица запуска
  4. Deployment и ReplicaSet: управляем репликами
  5. Service: сетевой доступ к подам
  6. ConfigMap и Secret: выносим конфигурацию
  7. Ingress: пускаем трафик снаружи
  8. Хранилище: Volumes и PersistentVolumeClaim
  9. Namespaces, requests и limits
  10. Health checks: liveness и readiness пробы
  11. Отладка: почему под не стартует
  12. Helm: пакетный менеджер для Kubernetes
  13. Базовая безопасность: RBAC и доступы
  14. Job и CronJob: разовые и периодические задачи
  15. StatefulSet и DaemonSet: stateful-нагрузки и системные агенты
  16. Стратегии обновления и планирование: rollout и rollback, graceful shutdown, nodeSelector, affinity, taints
  17. Автомасштабирование: HPA по метрикам, обзор VPA и Cluster Autoscaler
  18. Наблюдаемость: логи, метрики, events, обзор Prometheus и Grafana
  19. Безопасность глубже: securityContext, Pod Security Standards, NetworkPolicy, шифрование секретов
  20. Архитектура control plane: api-server, etcd, scheduler
  21. Узел кластера: kubelet, kube-proxy и container runtime
  22. Декларативная модель: reconciliation, контроллеры, CRD
  23. kubectl профессионально: get, describe, explain, jsonpath
  24. Под глубже: init, sidecar, lifecycle hooks и QoS
  25. Метки, селекторы и организация ресурсов
  26. Сервисы и kube-proxy глубже: типы, IPVS, EndpointSlices
  27. Ingress, ingress-контроллеры и Gateway API
  28. Секреты в кластере: шифрование, External Secrets, Vault
  29. Хранилище глубже: PV, StorageClass, CSI и StatefulSet
  30. Сеть кластера: CNI, NetworkPolicy и CoreDNS
  31. Планировщик: affinity, taints, topology spread
  32. Ресурсы, QoS и вытеснение: requests, limits, eviction
  33. Helm глубже: шаблоны, хуки, зависимости, OCI
  34. Kustomize и управление конфигурацией без шаблонов
  35. RBAC и аутентификация глубже
  36. Admission и Pod Security: контроль на входе
  37. Policy as code: Kyverno и OPA Gatekeeper
  38. GitOps: Argo CD и Flux
  39. Операторы и CRD: расширяем Kubernetes
  40. Сервис-меш: Istio, Linkerd и когда он нужен
  41. Эксплуатация кластера: апгрейд, узлы, бэкап etcd
  42. Где запускать кластер: managed, self-hosted, k3s, Deckhouse
  43. Траблшутинг кластера: Pending, CrashLoop, узлы NotReady
  44. Стоимость и эффективность кластера: FinOps
  45. CI/CD в Kubernetes: сборка, деплой, прогрессивные релизы (вы здесь)
  46. Лучшие практики и антипаттерны Kubernetes
  47. Сквозной проект и путь дальше: от манифеста до прода
  48. Деплой приложений через Argo CD: Application, sync и App-of-Apps
Боль, которую закрывает CI/CD в Kubernetes

Ты собрал образ руками, запушил в реестр, зашёл на ноду по ssh, сделал kubectl set image - и в пятницу вечером половина пользователей получила 500-е. Откатываться не на что, потому что ты не помнишь, какой тег крутился вчера. Знакомо. Ручной деплой в кластер - это не процесс, а лотерея, где приз - инцидент.

Грамотный kubernetes ci cd превращает выкатку в скучную, предсказуемую и повторяемую операцию. Любое изменение проходит один и тот же конвейер: собрали образ, проверили на дыры, запушили в реестр, описали желаемое состояние в Git, а контроллер в кластере сам привёл реальность к этому описанию. Если что-то пошло не так - автоматический откат по метрикам, а не нервный человек в три часа ночи. В этом уроке разберём весь путь от исходника до прогрессивного релиза с canary, и почему gitops ci cd вытеснил классический push из CI.

Изображение

Сборка образа в кластере без демона: kaniko, buildah, BuildKit

Первая ловушка новичка - запустить docker build прямо в раннере, который сам крутится подом в кластере. Для этого нужен доступ к Docker-демону, а его прокидывают через монтирование /var/run/docker.sock с ноды. Это дыра размером с ворота: под получает фактический root на хосте, может собрать любой контейнер и стартовать его в обход всех политик. В современном кластере на containerd демона Docker вообще нет (вспомни урок про отказ от dockershim), монтировать нечего.

Поэтому образы в CI собирают без демона, в userspace. Исторически тут правил kaniko от Google: один pod-процесс читает Dockerfile, выполняет инструкции в своей файловой системе, пушит результат в реестр по HTTPS и завершается. Важная новость 2026 года - проект kaniko заархивирован и больше не развивается. Он ещё работает, но новых фич и заплаток безопасности не будет, так что для новых пайплайнов его брать не стоит.

Что выбрать сегодня:
  • BuildKit (buildkitd) - современный дефолт. Умеет кэш слоёв в реестре, параллельную сборку стадий, mount-секреты на этапе build. Требует демон buildkitd, который запускают отдельным подом (rootless-режим есть). Это та же машинерия, что стоит за docker buildx из курса по Docker.
  • Buildah - daemonless-инструмент из мира Podman/CRI-O. Хорош, если ты уже в этой экосистеме, умеет собирать образы скриптами вообще без Dockerfile. Минус - чаще требует привилегий или возни с rootless-настройками.
Вот типовой шаг сборки на BuildKit внутри GitLab CI, запущенного в кластере:

Код: Выделить всё

build:
  image:
    name: moby/buildkit:rootless
    entrypoint: [""]
  variables:
    BUILDKITD_FLAGS: --oci-worker-no-process-sandbox
  script:
    - mkdir -p ~/.docker
    - echo "$DOCKER_AUTH_CONFIG" > ~/.docker/config.json
    - |
      buildctl-daemonless.sh build \
        --frontend dockerfile.v0 \
        --local context=. \
        --local dockerfile=. \
        --output type=image,name=registry.example.ru/app:$CI_COMMIT_SHA,push=true \
        --export-cache type=registry,ref=registry.example.ru/app:buildcache \
        --import-cache type=registry,ref=registry.example.ru/app:buildcache
Обрати внимание на тег: $CI_COMMIT_SHA, а не latest. Тег latest - корень зла в деплое: Kubernetes не понимает, что образ изменился, если имя то же, и при imagePullPolicy: IfNotPresent утянет старый слой из кэша ноды. Иммутабельный тег по коммиту даёт однозначное соответствие "этот под = этот код".

Пайплайн build -> scan -> push -> deploy

Конвейер не заканчивается на сборке. Минимальный честный пайплайн - четыре стадии:
  • build - собрали иммутабельный образ.
  • scan - прогнали сканер уязвимостей (Trivy, Grype) по образу и манифестам. Нашли критическую CVE в базовом слое - пайплайн красный, дальше не едем. Сюда же кладут проверку манифестов на политики: kube-linter, conftest, kubeconform для валидации схемы.
  • push - запушили в реестр (Yandex Container Registry, GitLab Registry, Harbor). Хорошая практика - подписать образ через cosign, чтобы кластер потом мог проверить происхождение.
  • deploy - обновили желаемое состояние.
Сканер ставь блокирующим, но с порогом. Если валить сборку на любой LOW-уязвимости в системных пакетах - команда быстро научится игнорировать красные пайплайны. Блокируй HIGH/CRITICAL с фиксом, остальное - в отчёт.

Деплой из CI или GitOps: почему Argo и Flux победили push

Теперь главный архитектурный выбор - как именно происходит деплой в kubernetes.

Push-модель (деплой из CI). Раннер держит kubeconfig с правами на кластер и сам делает kubectl apply, helm upgrade или kustomize build | kubectl apply. Просто, понятно, работает с первого дня. Проблемы вылезают на масштабе:
  • Раннер имеет полный доступ к кластеру. Скомпрометировали CI - скомпрометировали прод. Credentials живут вне кластера.
  • Дрейф состояния. Кто-то поправил реплики руками kubectl scale, и реальное состояние разошлось с тем, что в Git. CI об этом не знает.
  • Нет единого источника правды о том, что реально крутится в проде прямо сейчас.
Pull-модель (GitOps). Здесь идея gitops ci cd переворачивает поток: CI не ходит в кластер вообще. CI только собирает образ и коммитит новый тег в Git-репозиторий с манифестами. А внутри кластера живёт агент - Argo CD или Flux - который постоянно сравнивает (reconcile) желаемое состояние из Git с фактическим в кластере и подтягивает разницу. Git становится единственным источником правды.

Что это даёт на практике:
  • Кластер сам тянет изменения - наружу торчащих кредов с правами админа нет.
  • Дрейф лечится автоматически: поправил кто-то руками - Argo вернёт как в Git (self-heal). В UI Argo CD ты видишь статус OutOfSync по каждому ресурсу.
  • Откат - это git revert. История деплоев = история коммитов.
  • Code review на инфраструктуру: меняешь прод через Merge Request, а не через доступ к kubectl.
Типичная Argo CD Application, которая следит за каталогом манифестов:

Код: Выделить всё

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: payments
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://git.example.ru/infra/payments.git
    targetRevision: main
    path: deploy/overlays/prod
  destination:
    server: https://kubernetes.default.svc
    namespace: payments
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
Поле path тут указывает на overlay Kustomize - связка GitOps + Kustomize очень частая. Kustomize патчит базовые манифесты под окружение (другой namespace, другие реплики, другой образ) без шаблонов. Helm тоже дружит с Argo - source может ссылаться на Helm-чарт. Выбор между Kustomize и Helm - тема отдельного разговора, но для GitOps оба работают штатно.

Прогрессивные релизы: canary kubernetes и blue-green через Argo Rollouts

Обычный Deployment умеет только RollingUpdate - тупо заменяет старые поды новыми пачками. Если новая версия багованная, она поедет на 100% трафика, и ты заметишь это по графику ошибок постфактум. Прогрессивная доставка решает это: новую версию выкатывают на маленькую долю трафика, смотрят на метрики и только потом промоутят на всех.

Argo Rollouts - контроллер, который заменяет стандартный Deployment ресурсом Rollout и добавляет стратегии canary и blue-green. Canary kubernetes: подняли новую версию рядом, пустили на неё 10% трафика, подождали, проверили, повысили до 30%, и так до 100%. Blue-green: подняли полную копию (green) рядом с боевой (blue), прогнали тесты на green, потом мгновенно переключили сервис - удобно, когда нужен моментальный cutover и быстрый откат.

Самое ценное - argo rollouts умеет автоматический анализ по метрикам и rollback. Между шагами canary вставляется AnalysisTemplate, который дёргает Prometheus (или Datadog, или webhook) и проверяет, не вырос ли процент ошибок. Не прошло порог - rollout сам откатывается на стабильную версию, не дожидаясь человека.

Манифест canary с анализом (apiVersion argoproj.io/v1alpha1):

Код: Выделить всё

apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: api
spec:
  replicas: 6
  strategy:
    canary:
      steps:
        - setWeight: 10
        - pause: { duration: 2m }
        - analysis:
            templates:
              - templateName: success-rate
        - setWeight: 30
        - pause: { duration: 5m }
        - setWeight: 60
        - pause: { duration: 5m }
  selector:
    matchLabels: { app: api }
  template:
    metadata:
      labels: { app: api }
    spec:
      containers:
        - name: api
          image: registry.example.ru/api:abc123
---
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
  name: success-rate
spec:
  metrics:
    - name: success-rate
      interval: 30s
      successCondition: result[0] >= 0.99
      failureLimit: 3
      provider:
        prometheus:
          address: http://prometheus.monitoring:9090
          query: |
            sum(rate(http_requests_total{app="api",code!~"5.."}[2m]))
            /
            sum(rate(http_requests_total{app="api"}[2m]))
Разберём, что произойдёт. На шаге setWeight: 10 контроллер заведёт canary-поды и направит на них 10% запросов. Через analysis он раз в 30 секунд считает долю не-5xx ответов. successCondition требует >= 99% успеха; failureLimit: 3 значит "три провала подряд - аборт". Если доля просела, rollout получает статус Degraded и автоматически откатывается. Посмотреть статус живьём:

Код: Выделить всё

$ kubectl argo rollouts get rollout api --watch

Name:            api
Status:          Paused
Strategy:        Canary
  Step:          2/7
  SetWeight:     10
  ActualWeight:  10
Images:          registry.example.ru/api:abc123 (canary)
                 registry.example.ru/api:9f0c1d (stable)
Replicas:
  Desired:       6
  Updated:       1
  Ready:         6
  Available:     6
Видно: 1 под обновлён (canary), остальные на stable, реальный вес 10%. Status: Paused - контроллер ждёт окончания pause или результата analysis. Если бы анализ упал, ты бы увидел Status: Degraded и образ canary пропал бы. Альтернатива Argo Rollouts - Flagger, он работает поверх обычного Deployment и тесно интегрирован с service mesh (Istio, Linkerd) и Gateway API для управления трафиком. Выбор зависит от того, есть ли у тебя mesh.

Preview-окружения на каждый Pull Request

Мощная практика - поднимать эфемерное окружение на каждый PR. Открыл MR - CI собрал образ с тегом ветки, GitOps создал отдельный namespace preview-mr-128, развернул туда стек, и ревьюер получает живую ссылку потрогать фичу руками. Смержили или закрыли MR - namespace сносится, ресурсы освобождаются.

В Argo CD это делается через ApplicationSet с PR-генератором: он сам плодит Application на каждый открытый Pull Request и удаляет их при закрытии. Так QA тестирует не "на словах", а на работающем стенде, изолированном по namespace, с собственными лимитами через ResourceQuota.

Секреты в пайплайне и раннеры в кластере

Секреты - больное место. Категорически нельзя коммитить креды в Git в открытом виде, даже в приватный репозиторий, даже в base64 (это не шифрование). В GitOps секреты решают через Sealed Secrets (шифруешь публичным ключом контроллера, в Git лежит зашифрованный SealedSecret, расшифровать может только кластер) или External Secrets Operator (в Git лежит только ссылка, а сам секрет тянется из Vault, Yandex Lockbox, AWS Secrets Manager). В CI-переменные клади только то, что нужно самому пайплайну - токен реестра, ключ подписи - и помечай их masked/protected.

Раннеры в кластере. GitLab Runner и GitHub Actions Runner (ARC, Actions Runner Controller) можно запускать подами прямо в Kubernetes. Каждая CI-задача поднимает временный pod, отрабатывает и умирает - чисто и масштабируемо. Но помни про принцип наименьших привилегий: раннеру для GitOps не нужен доступ к проду. Его задача - собрать образ и сделать git push. Деплоит уже Argo изнутри. Если связываешь это с миром Jenkins из соседнего курса - там тот же принцип: Jenkins-агент в кластере собирает и пушит, а выкатку отдаёт GitOps-контроллеру, а не дёргает kubectl сам.

Типичные грабли и антипаттерны
  • Тег latest в продакшене - теряешь воспроизводимость и контроль версий. Всегда иммутабельный тег по SHA коммита.
  • docker.sock в раннере - root на ноде. Собирай через BuildKit/Buildah без демона.
  • kubeconfig админа в CI-переменных - один украденный токен = весь кластер. Переходи на pull-модель GitOps.
  • Деплой руками поверх GitOps - self-heal откатит твои ручные правки, и ты будешь долго думать, почему изменения "не применяются". Меняй через Git.
  • Canary без анализа метрик - это просто медленный rollout. Без AnalysisTemplate ты всё равно ловишь баги глазами. Подключай Prometheus.
  • Слепой analysis-запрос - если в начале канарейки трафика мало, метрика на пустом окне даёт деление на ноль и ложный аборт. Ставь разумный inconclusive-порог и достаточный interval.
Мини-лаба: повтори руками
  • Установи Argo Rollouts в kind/minikube: kubectl create namespace argo-rollouts и kubectl apply -n argo-rollouts -f install.yaml с офиц. релиза.
  • Поставь плагин kubectl argo rollouts (через krew).
  • Возьми любой образ с двумя тегами (например, разный текст на странице) и опиши Rollout с canary-стратегией из трёх шагов: setWeight 20, pause 1m, setWeight 100.
  • Примени, потом обнови image на второй тег: kubectl argo rollouts set image api api=твой/образ:v2.
  • Смотри kubectl argo rollouts get rollout api --watch - увидишь, как поднимается canary.
  • Откати вручную: kubectl argo rollouts abort api - и убедись, что трафик вернулся на stable.
Контрольные вопросы
  • Почему монтировать docker.sock в CI-раннер опасно и чем собирать образ вместо этого в 2026 году?
  • В чём принципиальная разница push-модели деплоя из CI и pull-модели GitOps по потоку доступа к кластеру?
  • Что именно делает AnalysisTemplate в Argo Rollouts и как он приводит к автоматическому откату?
  • Зачем preview-namespace на каждый PR и как его жизненный цикл связан с состоянием Merge Request?
Итог

Хороший CI/CD в Kubernetes - это конвейер build -> scan -> push -> deploy, где сборка идёт без демона на BuildKit, выкатка - через GitOps (Argo CD/Flux) с Git как единственным источником правды, а релизы прогрессивные: canary с анализом метрик и автоматическим откатом через Argo Rollouts. Ты убираешь человека из критического пути, а вместе с ним - и пятничные инциденты. Дальше будем углубляться в наблюдаемость, чтобы те самые метрики для analysis у тебя были честными.
👍4 ❤️ 🔥 😄 🤔1
Аватара пользователя
fan_sasha
Сообщения: 1
Зарегистрирован: 04 июн 2026, 07:11

Re: CI/CD в Kubernetes: сборка, деплой, прогрессивные релизы

Сообщение fan_sasha »

Раньше деплоил helm upgrade прямо из джобы гитлаба, и реально пару раз ловил дрейф когда коллега ручками реплики правил. Теперь понял почему self-heal в арго это не магия а необходимость. Спасибо, переезжаю на pull-модель.
👍3 ❤️1 🔥 😄 🤔
Аватара пользователя
soliditywhale
Сообщения: 1
Зарегистрирован: 30 май 2026, 03:17

Re: CI/CD в Kubernetes: сборка, деплой, прогрессивные релизы

Сообщение soliditywhale »

Вопрос по analysis: а если у меня на canary в начале вообще почти нет трафика, запрос success-rate же будет считаться по пустому окну и даст ноль? Как правильно ставить interval и failureLimit чтобы не словить ложный rollback на старте?
👍1 ❤️2 🔥 😄 🤔1
Ответить
← Предыдущая глава
Стоимость и эффективность кластера: FinOps
Следующая глава →
Лучшие практики и антипаттерны Kubernetes

Все главы курса «Kubernetes: оркестрация контейнеров от основ до продакшена»

Поделиться темой: ✈ Telegram VK
Похожие запросы: minikube или kind что выбрать для локального кластера kuberneteskubectl get describe logs основные команды для работы с кластеромчем отличается deployment от pod в kubernetes простыми словамичто такое service в kubernetes и как поды находят друг другаrequests и limits в kubernetes как правильно задать ресурсы подуnamespace в kubernetes зачем нужен и как разделить ресурсы

Вернуться в «Kubernetes на практике»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 1 гость