Из 2024 в 2025: вспоминаем лучшие практики CI/CD

5dcf99646dc5921c849f64d0bac1c0d4.png

Привет всем читающим. Меня зовут Данил, я DevOps-инженер, работаю в Nixys. Добро пожаловать в 2025 год.

В современном мире разработки ПО скорость выхода новых версий и обновлений имеет решающее значение. Чтобы оставаться конкурентоспособными, компании стремятся сократить время на разработку и внедрение новых функций. Этого можно достичь благодаря таким методологиям, как CI (Continuous Integration, непрерывная интеграция) и CD (Continuous Delivery/Deployment, непрерывная доставка/развертывание).

Если вы вдруг забыли или не знаете, что такое CI/CD.

CI/CD — это ключевые процессы, направленные на автоматизацию и ускорение разработки, тестирования и доставки программного обеспечения. Они позволяют разработчикам объединять свои изменения в единую кодовую базу, что снижает вероятность возникновения ошибок на поздних стадиях. Этот подход также улучшает управление версиями и обеспечивает более частые релизы, что, в свою очередь, увеличивает гибкость и скорость реакции на изменения в требованиях. CI/CD не только ускоряет разработку, но и делает ее более предсказуемой и контролируемой.

Когда компания внедряет CI/CD, она:

  • Сокращает время на тестирование. Автоматизация позволяет быстро и эффективно запускать тесты, избегая ручных проверок, что ускоряет цикл обратной связи.

  • Минимизирует ошибки на этапе интеграции. Частые небольшие изменения проще тестировать и обнаруживать ошибки, чем большие обновления, которые могут быть более сложными для тестирования.

  • Ускоряет вывод продукта на рынок. Благодаря автоматизированным процессам внедрения новых функций или исправлений программного обеспечения, разработчики могут выпускать обновления гораздо быстрее, снижая время вывода на рынок.

  • Упрощает управление версиями ПО. Каждое изменение кода может быть упаковано в отдельный релиз, что упрощает отслеживание и управление версиями программного обеспечения.

В 2024 году эти процессы продолжили свою эволюцию: появились новые инструменты и подходы. В этой статье вспомним лучшие практики CI/CD, с которыми наша команда встретила новый 2025 год.

Best practices CI/CD

1. Multi-stage сборка Docker-образа

Одной из ключевых рекомендаций является использование multi-stage сборки Docker-образа. Этот подход позволяет значительно уменьшить размер итогового образа, так как в конечный образ попадает только необходимый код и библиотеки, а временные и вспомогательные файлы остаются на стадии сборки. 

Преимущества multi-stage сборки:

  • Минимизация размера финального Docker-образа.

  • Упрощение управления зависимостями.

  • Снижение рисков уязвимостей благодаря уменьшению ненужных библиотек.

2. Билд через Kaniko

Kaniko — это инструмент для безопасной сборки Docker-образов внутри Kubernetes-кластера без необходимости привилегированного доступа. Это делает его отличным выбором для использования в CI/CD пайплайнах, где безопасность имеет первостепенное значение.

Основные бенефиты Kaniko:

  • Безопасность. Kaniko не требует выполнения привилегированных операций, в отличие от Docker-in-Docker (DinD), который может представлять угрозу для безопасности кластера.

  • Легкость интеграции с Kubernetes. Kaniko легко интегрируется с Kubernetes-платформами, что упрощает процесс автоматизации.

  • Ускорение повторных сборок. Когда части образа остаются неизменными между сборками, Kaniko использует закэшированные слои, что значительно сокращает время сборки. Это особенно важно при частых запусках пайплайнов.

  • Простота интеграции с Kubernetes. Kaniko был разработан специально для Kubernetes, поэтому он легко интегрируется с различными системами CI/CD, работающими в Kubernetes-кластерах. Он упрощает автоматизацию процессов сборки образов и их деплоя, минимизируя сложность настройки.

Почему не стоит использовать Docker-in-Docker:

  • DinD требует привилегированных контейнеров, что делает его менее безопасным в сравнении с Kaniko.

  • При использовании DinD могут возникать проблемы с производительностью из-за сложностей с запуском Docker внутри Docker.

3. Тегирование и использование protected веток в Gitlab

Для улучшения процесса релизов и управления версиями важно использовать стратегии тегирования и работы с защищёнными ветками (protected branches) в Gitlab:

  • Тегирование. Все релизы должны сопровождаться уникальными тегами, что позволяет легко отслеживать версионность и фиксировать конкретные состояния приложения.

  • Protected branches. Защищенные ветки позволяют ограничить доступ к изменению важнейших веток, например, master или production. Это помогает избежать случайных изменений и ошибок в ключевых ветках.

4. Создание репозитория с шаблонами CI

В организациях, где множество проектов, рекомендуется создавать репозитории с шаблонами CI/CD пайплайнов для унификации процессов. Это значительно облегчает масштабирование и управление проектами. Шаблоны позволяют:

  • Быстро разворачивать новые проекты на базе готовых пайплайнов.

  • Обеспечить консистентность процессов сборки и деплоя во всех проектах.

  • Уменьшить время на настройку CI/CD для новых команд.

Однако, если у вас только один проект, лучше использовать один пайплайн без child или downstream пайплайнов, так как это упростит поддержку и отладку процессов, а так же позволит более гибко управлять его логикой.

5. Использование гибких правил для триггера пайплайнов

Часто нет необходимости запускать полный пайплайн на каждое изменение в кодовой базе. Поэтому важно использовать гибкие правила для триггеров:

  • Триггерить пайплайн только при изменениях в определённых директориях или файлах (например, для деплоя на production запускать пайплайн только при изменениях в папке с конфигурациями).

  • Ограничение запуска пайплайнов по событиям (например, запускать пайплайн только при слиянии в защищённые ветки).

В рамках данной статьи мы будем использовать следующие инструменты:

  • Gitlab CI для организации процессов сборки, включая сборку Docker-образа;

  • ArgoCD для деплоя приложений;

  • Vault для безопасного хранения и использования секретов, интегрированного на уровне Gitlab CI.

А теперь посмотрим все эти практики в действии.

Лучшие практики CI/CD: билд приложение через GitLab CI

1. Пример multi-stage Dockerfile

Для начала создадим multi-stage Dockerfile, который позволяет собрать минимальный и оптимизированный образ для нашего приложения. В качестве примера используем PHP-приложение:

## common_artifact
FROM registry.examle.ru/infra/ubuntu-base:common AS common_artifact

SHELL ["/bin/bash", "-o", "pipefail", "-c"]

RUN su app -c 'wget --quiet https://repo1.maven.org/maven2/com/google/javascript/closure-compiler/v.../closure-compiler-v....jar -O  /opt/closure-tools/compiler.jar --no-check-certificate'

COPY --chown=app:app . /app

ARG db_string
ARG db_user_name
ARG db_password
ARG db_host
ARG db_port
ARG db_name

RUN echo "${db_host}  pgsql.db" >> /etc/hosts && \
    /usr/bin/php some commads

COPY --chown=app:app ./.infra/files/js_min.sh /app/js_min.sh

RUN su app -c '/bin/bash /app/js_min.sh'

WORKDIR /app/somedir/protected

RUN su app -c '/bin/bash gulpc'

### frontend
FROM registry.examle.ru/infra/nginx-upstream-limit:nginx-gc AS frontend

COPY ./.infra/files/frontend/nginx.conf /etc/nginx/nginx.conf
COPY ./.infra/files/frontend/content /app/somedir/content
COPY . /app

RUN rm -rf /app/somedir/protected/modules \
    /app/somedir/protected/resource \
    /app/somedir/js \
    /app/_sql/sqlLocalizationUpdate*

COPY --from=common_artifact /app/somedir/protected/modules /app/somedir/protected/modules
COPY --from=common_artifact /app/somedir/protected/resource /app/somedir/protected/resource
COPY --from=common_artifact /app/somedir/js /app/somedir/js
COPY --from=common_artifact /app/_sql/sqlLocalizationUpdate* /app/_sql/

## backend
FROM registry.examle.ru/infra/ubuntu-base:backend AS backend

SHELL ["/bin/bash", "-o", "pipefail", "-c"]

ENV LC_ALL=ru_RU.UTF-8
ENV LANG=ru_RU.UTF-8

COPY --chown=app:app ./.infra/files/backend/fonts /usr/share/fonts/TTF
RUN chown -R app:app /usr/share/fonts/TTF && chmod -R 0755 /usr/share/fonts/TTF

COPY ./.infra/files/frontend/wp_content /app/somedir/wp-content
COPY --chown=app:app ./.infra/files/somedir/cron.d /etc/cron.d
COPY --chown=app:app ./.infra/files/somedir/somedir /etc/somedir
COPY --chown=app:app ./.infra/files/somedir/d /etc/service/d
COPY --chown=app:app ./.infra/files/somedir/supercronic_run /etc/service/supercronic/run
COPY --chown=app:app ./.infra/files/backend/php-fpm.conf /etc/php/7.4/fpm/php-fpm.conf
COPY --chown=app:app ./.infra/files/backend/php.ini /etc/php/7.4/cli/php.ini
COPY --chown=app:app ./.infra/files/backend/php-fpm.ini /etc/php/7.4/fpm/php.ini
COPY --chown=app:app . /app

WORKDIR /app

RUN rm -rf /app/somedir/protected/modules \
    /app/somedir/protected/resource \
    /app/somedir/js \
    /app/_sql/sqlLocalizationUpdate* && \
    mkdir -p /app/somedir/protected/runtime && \
    chown -R app:app /app/somedir/protected/runtime

COPY --from=common_artifact --chown=app:app /app/somedir/protected/modules /app/somedir/protected/modules
COPY --from=common_artifact --chown=app:app /app/somedir/protected/resource /app/somedir/protected/resource
COPY --from=common_artifact --chown=app:app /app/somedir/js /app/somedir/js
COPY --from=common_artifact --chown=app:app /app/_sql/sqlLocalizationUpdate* /app/_sql/

Этот Dockerfile разбит на 3 этапа:

  1. common_artifact stage: установка зависимостей, вписывание аргументов для сборки приложения и его подготовка.

  2. frontend stage: копирование финального приложения в образ, замена и оптимизация кода, минимизируя размер Docker-образа.

  3. backend stage: копирование финального приложения в образ, замена и оптимизация кода, минимизируя размер Docker-образа.

2. Пример шаблона для GitLab CI с Kaniko

Для более гибкого и масштабируемого процесса, мы можем создать отдельный репозиторий с шаблонами для CI/CD. Этот подход особенно полезен для организаций с большим количеством проектов, где требуется унифицированный подход к сборке, тестированию и деплою. В данном случае мы создадим репозиторий, который будет содержать директории для различных этапов CI/CD — сборки, тестирования, деплоя и других шагов.

Структура репозитория с шаблонами CI/CD

ci-templates/

├── build/

│   └── kaniko-build-template.yml

├── test/

│   └── test-template.yml

├── deploy/

│   └── argo-deploy-template.yml

└── .gitlab-ci.yml

Описание директорий:

  • build/ — содержит шаблоны для сборки Docker-образов, в том числе Kaniko для различных окружений.

  • test/ — включает шаблоны для автоматизированного тестирования приложений (юнит-тесты, интеграционные тесты).

  • deploy/ — содержит шаблоны для деплоя, например, с использованием ArgoCD для различных сред (production, staging, development).

  • .gitlab-ci.yml — основной файл, который инклюдит шаблоны из других директорий и определяет общие правила.

Шаблон для Kaniko-сборки

В директории build/ создадим файл kaniko-build-template.yml, который будет отвечать за этапы сборки для разных окружений. В этом файле определим задачи для prod и dev окружений, используя правила и параметры, специфичные для каждого окружения:

stages:
  - build

include:
  - local: stage-ci.yml

.build:
  stage: build
  image:
    name: registry-url/infra/kaniko-executor:v1.16.0-debug
    entrypoint: [""]
  script:
    - mkdir -p /kaniko/.docker
    - cat ${CR_CONFIG} > /kaniko/.docker/config.json
    - touch new-test
    - echo "test" >> new-test
    - >
      /kaniko/executor
      --context "${CI_PROJECT_DIR}"
      --dockerfile "${DOCKERFILE}"
      --destination "registry-url/${REGISTRY}/${IMAGE}"
      --cache=true
      --cache-repo "registry-url/${REGISTRY}/${CI_PROJECT_NAME}-cache"


build_dev:
  extends: 
    - .build
  variables:
    REGISTRY: $CR_AGENCY_DEV_ID
    CR_CONFIG: $CR_AGENCY_DEV_CONFIG
    IMAGE: ${CI_PROJECT_NAME}:${CI_COMMIT_SHORT_SHA}
  rules:
    - if: $CI_COMMIT_BRANCH =~ /^feature\/.*/
    - if: $CI_COMMIT_BRANCH == "develop"
  cache:
    policy: push

build_prod:
  extends: 
    - .build
  variables:
    REGISTRY: $CR_AGENCY_PROD_ID
    CR_CONFIG: $CR_AGENCY_PROD_CONFIG
    IMAGE: ${CI_PROJECT_NAME}:${CI_COMMIT_TAG}
  rules:
    - if: $CI_COMMIT_TAG != null

build_stage:
  extends: 
    - .build
  variables:
    REGISTRY: $CR_AGENCY_PROD_ID
    CR_CONFIG: $CR_AGENCY_PROD_CONFIG
    IMAGE: ${CI_PROJECT_NAME}:${CI_COMMIT_TAG}
  rules:
    - if: '$CI_COMMIT_REF_NAME == "staging"'

Описание задачи для сборки:

  • build_template — шаблон, который содержит основные настройки для всех задач сборки, таких как образ Kaniko, кэширование, и авторизация в Docker Registry.

  • Отдельные задачи для окружений (build.prod, build.dev) — каждая задача предназначена для определённого окружения. Например, для продакшена будет собираться образ с тегом prod, для staging — с тегом stage, и так далее.

Каждое окружение имеет свои правила для триггеров пайплайна:

  • Сборка для продакшена запускается если коммит происходит с тегом.

  • Сборка для dev окружения — при коммите в ветку содержащую в название feature, при коммите в ветку develop.

Основной .gitlab-ci.yml файл

Основной файл .gitlab-ci.yml в репозитории проекта должен включать шаблон из директории с шаблонами. Это позволяет повторно использовать шаблоны для разных проектов, что значительно упрощает управление пайплайнами.

include:
  - project: 'ci-templates'
    file: 'build/kaniko-build-template.yml'

# Определение триггеров для сборки

workflow:
  rules:
    - if: '$CI_COMMIT_REF_NAME == "main"'
    - if: '$CI_COMMIT_REF_NAME == "staging"'
    - if: '$CI_COMMIT_REF_NAME == "develop"'

Здесь:

  • include — подключает шаблон kaniko-build-template.yml из репозитория ci-templates, который содержит всю логику для сборки Docker-образов.

  • workflow — задает условия для запуска пайплайнов: сборка запускается только при работе с ветками main, staging или develop.

Преимущества данного подхода:

  1. Унификация процессов. Использование шаблонов позволяет обеспечить единообразие процессов CI/CD для всех проектов. Это особенно важно для крупных организаций с большим количеством проектов.

  2. Повторное использование. Шаблоны могут быть использованы в разных проектах, что сокращает время на настройку CI/CD и минимизирует вероятность ошибок.

  3. Гибкость. При необходимости шаблоны можно модифицировать для определённых проектов, сохраняя при этом общую структуру.

Облегчение управления изменениями. Все изменения в пайплайнах можно вносить централизованно, в репозитории с шаблонами. Это упрощает поддержку и обновление CI/CD процессов для всех проектов одновременно.

ArgoCD: перейдём к деплою

Преимущества ArgoCD и почему его стоит использовать

ArgoCD — это мощный инструмент для автоматизации процессов деплоя в Kubernetes с использованием методологии GitOps, что делает его важным элементом CI/CD пайплайнов.

Преимущества ArgoCD:

  • GitOps-ориентированный деплой. ArgoCD автоматически синхронизирует состояние приложения в кластере с его состоянием в репозитории Git. Это делает деплой полностью управляемым через Git, улучшая контроль версий и прозрачность процессов.

  • Автоматическая синхронизация. ArgoCD непрерывно следит за изменениями в репозитории и автоматически обновляет Kubernetes-кластер, когда изменения происходят.

  • Гибкость и поддержка различных инструментов. ArgoCD поддерживает деплой из Helm-чартов, Kustomize, Jsonnet и других методов описания инфраструктуры.

  • Простота визуализации и управления. Встроенная панель управления (Web UI) позволяет легко следить за состоянием всех приложений, их версиями и статусами синхронизации.

Сравнение ArgoCD с FluxCD

FluxCD и ArgoCD оба являются GitOps-решениями для автоматизации деплоя в Kubernetes, но между ними есть ключевые различия:

Параметр

ArgoCD

FluxCD

UI

Имеет удобный веб-интерфейс для управления приложениями и отслеживания состояния синхронизации.

Веб-интерфейс отсутствует, управление осуществляется через CLI.

Поддержка Helm

Полноценная поддержка Helm-чартов и управление релизами через UI.

Поддержка Helm через дополнительный компонент helm-controller.

Установка и настройка

Более сложная установка и настройка, но более удобное управление после внедрения.

Простая установка, но управление требует использования сторонних инструментов.

Синхронизация

Поддерживает как ручную, так и автоматическую синхронизацию с возможностью отката изменений.

Основной упор на автоматическую синхронизацию без встроенной поддержки ручных операций.

В целом, ArgoCD более удобен для команд, которые хотят гибкости в управлении деплоем и удобный интерфейс, в то время как FluxCD больше подходит для сценариев, требующих минимальной инфраструктуры и полной автоматизации.

Установка ArgoCD через Helm

Для того чтобы установить ArgoCD через Helm и настроить его под свои нужды, включая настройку Ingress с хостом argo.example.com, необходимо отредактировать файл values.yaml. Этот файл позволяет кастомизировать установку ArgoCD, включая конфигурацию Ingress, ресурсы, безопасность и другие параметры.

Пример файла values.yaml для ArgoCD с включением Ingress:

# Общие настройки для ArgoCD

global:
  image:
    repository: argoproj/argocd
    tag: v2.9.0 # Укажите актуальную версию ArgoCD
  installCRDs: true

# Настройки для сервера ArgoCD

server:
  service:
    type: ClusterIP
    port: 80

  # Настройка Ingress для доступа к UI ArgoCD

  ingress:
    enabled: true
    annotations:
      kubernetes.io/ingress.class: nginx
      cert-manager.io/cluster-issuer: "letsencrypt-prod" # Опционально, если вы используете cert-manager для TLS
    hosts:
      - host: argo.example.com
        paths:
          - /
    tls:
      - secretName: argocd-tls
        hosts:
          - argo.example.com

# Настройки безопасности

# Определение ресурсов для ArgoCD серверов

controller:
  resources:
    limits:
      cpu: 500m
      memory: 512Mi
    requests:
      cpu: 250m
      memory: 256Mi

repoServer:
  resources:
    limits:
      cpu: 500m
      memory: 512Mi
    requests:
      cpu: 250m
      memory: 256Mi

# Настройка синхронизации проектов

applicationSet:
  enabled: true

# Опции RBAC для пользователей

rbacConfig:
  policy.default: role:readonly
  scopes: "[groups]"
# Автоматическое создание пользователя admin с паролем

initialAdminPassword: "supersecretpassword"

Описание ключевых параметров:

  1. global.image — определяет репозиторий и тег (версию) для образа ArgoCD. Важно указывать актуальную версию.

  2. server.service — указывает тип сервиса для ArgoCD. Мы используем тип ClusterIP, чтобы сервис был доступен только внутри кластера. Если требуется внешний доступ, можно заменить на LoadBalancer.

  3. server.ingress — настройка Ingress контроллера для доступа к веб-интерфейсу ArgoCD:

    • enabled — включает Ingress.

    • annotations — опционально добавляем аннотации для интеграции с cert-manager, если требуется автоматическая настройка TLS сертификатов.

    • hosts — задаёт доменное имя (в данном случае argo.example.com), по которому будет доступен ArgoCD.

    • tls — настройка для автоматического получения TLS-сертификатов, указав secretName для хранения сертификатов.

  4. controller.resources и repoServer.resources — задают ресурсы для компонентов ArgoCD (ограничения по CPU и памяти).

  5. rbacConfig — позволяет настроить права доступа по умолчанию для пользователей, задавая роль readonly для базовых пользователей.

  6. initialAdminPassword — задаёт начальный пароль для пользователя admin, который будет создан при установке ArgoCD.

Установка ArgoCD с использованием редактированного values.yaml

После редактирования файла values.yaml можно установить ArgoCD с помощью Helm:

  1. Сначала добавим репозиторий Helm для ArgoCD:

helm repo add argo https://argoproj.github.io/argo-helm
helm repo update
  1. Выполним установку ArgoCD с использованием нашего файла values.yaml:

helm install argo-cd argo/argo-cd --namespace argocd --create-namespace -f values.yaml

После завершения установки ArgoCD будет доступен по адресу https://argo.example.com, если настроен правильный DNS и сертификаты для Ingress.

Написание Helm-чарта с использованием nxs-universal-chart

Helm-чарт для приложения будет основан на nxs-universal-chart, который предоставляет универсальные шаблоны для управления различными типами приложений. Пример значений для web-приложения можно взять из образца.

Пример файла values.yml для вашего приложения:

app:
  name: "my-app"
  version: "1.0.0"

image:
  repository: "my-registry/my-app"
  tag: "latest"
  pullPolicy: "IfNotPresent"

replicaCount: 2
service:
  type: "ClusterIP"
  port: 80

ingress:
  enabled: true
  annotations: {}
  hosts:
    - host: "my-app.example.com"
      paths:
        - "/"
  tls: []

resources:
  limits:
    cpu: 100m
    memory: 128Mi

  requests:
    cpu: 50m
    memory: 64Mi

Интеграция ArgoCD с GitLab CI

Теперь мы создадим GitLab CI файл для деплоя через ArgoCD в три разных окружения: prod, stage и dev.

  1. Шаблон деплоя с ArgoCD — создадим файл argo-deploy-template.yml:

stages:
  - deploy

.deploy_template: &deploy_template
  image: bitnami/kubectl:latest
  script:
    - kubectl config set-cluster $K8S_CLUSTER --server=$K8S_SERVER --insecure-skip-tls-verify=true
    - kubectl config set-credentials $K8S_USER --token=$K8S_TOKEN
    - kubectl config set-context $K8S_CONTEXT --cluster=$K8S_CLUSTER --user=$K8S_USER
    - kubectl config use-context $K8S_CONTEXT
    - argocd app sync my-app --prune --timeout 300

# Деплой на production

deploy_prod:
  <<: *deploy_template
  stage: deploy
  environment:
    name: production
  script:
    - argocd app sync my-app-prod --prune --timeout 300
  rules:
    - if: '$CI_COMMIT_REF_NAME == "main"'

# Деплой на staging

deploy_stage:
  <<: *deploy_template
  stage: deploy
  environment:
    name: staging
  script:
    - argocd app sync my-app-stage --prune --timeout 300
  rules:
    - if: '$CI_COMMIT_REF_NAME == "staging"'

# Деплой на development

deploy_dev:
  <<: *deploy_template
  stage: deploy
  environment:
    name: development
  script:
    - argocd app sync my-app-dev --prune --timeout 300
  rules:
    - if: '$CI_COMMIT_REF_NAME == "develop"'

Этот шаблон:

  • Настраивает kubectl для работы с Kubernetes-кластером через предоставленные переменные (сервер, токен, контекст).

  • Использует команду argocd app sync для синхронизации приложений в разных окружениях (prod, stage, dev).

  1. Основной .gitlab-ci.yml — инклюдит шаблон:

include:
  - local: 'argo-deploy-template.yml'

workflow:
  rules:
    - if: '$CI_COMMIT_REF_NAME == "main"'
    - if: '$CI_COMMIT_REF_NAME == "staging"'
    - if: '$CI_COMMIT_REF_NAME == "develop"'

ArgoCD предлагает мощные инструменты для GitOps-ориентированного деплоя приложений, с поддержкой автоматической синхронизации и удобного управления через UI. Мы рассмотрели его установку через Helm, создание Helm-чарта на базе nxs-universal-chart, а также интеграцию с GitLab CI для деплоя в разные окружения.

HashiCorp Vault: Преимущества, Установка и Интеграция с GitLab CI

Преимущества использования Vault

HashiCorp Vault — это инструмент для безопасного управления секретами и доступом к ним в приложениях и инфраструктуре. Он поддерживает шифрование данных, динамическое управление доступом и аудит использования секретов, что делает его важным элементом в CI/CD пайплайнах.

Преимущества Vault:

  1. Централизованное управление секретами. Vault предоставляет единое место для безопасного хранения секретов (пароли, токены, API-ключи и другие конфиденциальные данные), что упрощает управление ими.

  2. Динамическая генерация секретов. Vault может динамически генерировать временные доступы, такие как креды для баз данных или облачных сервисов, которые автоматически истекают через определенное время.

  3. Гибкие политики доступа. Политики позволяют настроить гибкий контроль над доступом к секретам на основе ролей и сервисов.

  4. Аудит и безопасность. Vault ведёт полный аудит использования секретов, что помогает отслеживать и контролировать доступ к конфиденциальным данным.

  5. Шифрование данных в движении и покое. Все секреты в Vault хранятся в зашифрованном виде, что обеспечивает высокий уровень безопасности.

Сравнение Vault с FluxCD Secret Management

Параметр

HashiCorp Vault

FluxCD Secret Management

Управление секретами

Централизованное, с поддержкой динамических секретов.

Ограничено Kubernetes Secret и интеграцией с SOPS.

Шифрование

Полноценное шифрование секретов в хранилище и при передаче.

Шифрование секретов через внешние инструменты.

Политики и доступ

Гибкое управление доступом через политики и роли.

Доступ на основе ролей Kubernetes и RBAC.

Поддержка облаков и баз данных

Поддерживает динамическое создание временных учетных данных для облаков и баз данных.

Отсутствует поддержка динамических секретов.

Vault обеспечивает более мощное и гибкое управление секретами, в то время как FluxCD ограничен базовой работой с Kubernetes Secret и интеграцией с инструментами шифрования, такими как SOPS.

Установка Vault через Helm

Чтобы развернуть HashiCorp Vault в Kubernetes-кластере, можно использовать Helm для установки. Установка включает редактирование файла values.yaml для кастомизации конфигурации под ваши нужды.

Добавим репозиторий Helm для Vault:

helm repo add hashicorp https://helm.releases.hashicorp.com
helm repo update

Создадим файл values.yaml, чтобы кастомизировать установку Vault, включая настройку Ingress.

Пример файла values.yaml для Vault с поддержкой Ingress:

server:
  # Включение режима HA (High Availability) для Vault
  ha:
    enabled: true
    replicas: 3
  
# Настройка сервиса для Vault
  service:
    type: ClusterIP

  # Включение и настройка Ingress для доступа к Vault UI

  ingress:
    enabled: true
    annotations:
      kubernetes.io/ingress.class: nginx
      cert-manager.io/cluster-issuer: "letsencrypt-prod"  # Опционально, если вы используете cert-manager
    hosts:
      - host: vault.example.com
        paths:
          - /
    tls:
      - hosts:
          - vault.example.com
        secretName: vault-tls

  # Политики и начальные настройки

  extraConfig: |
    listener "tcp" {
      address = "0.0.0.0:8200"
      tls_disable = 1
    }
    storage "file" {
      path = "/vault/data"
    }
    ui = true

  # Задание ресурсов для Vault
  resources:
    limits:
      cpu: 500m
      memory: 512Mi
    requests:
      cpu: 250m
      memory: 256Mi

Описание параметров:

  • ha.enabled — включает режим High Availability (HA) для повышения отказоустойчивости, с тремя репликами Vault.

  • ingress — настраивает Ingress для доступа к Vault через домен vault.example.com. TLS поддерживается через cert-manager.

  • extraConfig — конфигурация для работы Vault. Включаем интерфейс UI и определяем хранилище данных.

  • resources — определение лимитов и запросов по ресурсам для оптимального использования кластера.

Установка Vault с нашим values.yaml:

helm install vault hashicorp/vault --namespace vault --create-namespace -f values.yaml

После установки Vault будет доступен через Ingress по адресу https://vault.example.com.

Интеграция Vault с GitLab CI

Теперь рассмотрим, как интегрировать Vault с GitLab CI для безопасного доступа к секретам во время сборки и деплоя приложения.

1. Шаблон для использования Vault в GitLab CI

Создадим файл vault-secrets-template.yml, который будет использоваться для извлечения секретов из Vault в процессе сборки.

stages:
  - build
# Шаблон для использования Vault

.vault_template: &vault_template
  image: hashicorp/vault:latest
  script:
    - vault login $VAULT_TOKEN
    - export DB_PASSWORD=$(vault kv get -field=password secret/db-creds)
    - echo "Database password retrieved from Vault: $DB_PASSWORD"

# Билд для production

build_prod:
  <<: *vault_template
  stage: build
  environment:
    name: production
  script:
    - vault login $VAULT_TOKEN
    - export DB_PASSWORD=$(vault kv get -field=password secret/prod/db-creds)
    - ./build.sh
  rules:
    - if: '$CI_COMMIT_REF_NAME == "main"'

# Билд для staging

build_stage:
  <<: *vault_template
  stage: build
  environment:
    name: staging
  script:
    - vault login $VAULT_TOKEN
    - export DB_PASSWORD=$(vault kv get -field=password secret/staging/db-creds)
    - ./build.sh

  rules:
    - if: '$CI_COMMIT_REF_NAME == "staging"'

# Билд для development

build_dev:
  <<: *vault_template
  stage: build
  environment:
    name: development
  script:
    - vault login $VAULT_TOKEN
    - export DB_PASSWORD=$(vault kv get -field=password secret/dev/db-creds)
    - ./build.sh

  rules:
    - if: '$CI_COMMIT_REF_NAME == "develop"'

Описание пайплайна:

  • vault login — логин в Vault с использованием токена, который передаётся в переменной окружения $VAULT_TOKEN.

  • vault kv get — команда для извлечения секретов из Vault. В этом примере мы извлекаем пароль базы данных.

  • build_prod, build_stage, build_dev — задачи для каждого окружения (production, staging, development), где мы получаем секреты, специфичные для окружения, и запускаем процесс сборки.

2. Основной .gitlab-ci.yml файл

Основной .gitlab-ci.yml файл должен инклюдить шаблон для использования Vault и настроить пайплайн для трёх окружений:

include:
  - local: 'vault-secrets-template.yml'

workflow:
  rules:
    - if: '$CI_COMMIT_REF_NAME == "main"'
    - if: '$CI_COMMIT_REF_NAME == "staging"'
    - if: '$CI_COMMIT_REF_NAME == "develop"'

Интеграция HashiCorp Vault с GitLab CI позволяет безопасно управлять секретами в процессе сборки и деплоя приложений. Мы рассмотрели процесс установки Vault через Helm, настройку Ingress, использование Vault для управления секретами через GitLab CI и создание пайплайнов для разных окружений. Этот подход улучшает безопасность и удобство работы с секретами в CI/CD процессах.

После рассмотрения всех шагов, включая интеграцию HashiCorp Vault и использование ArgoCD, мы можем объединить все настройки в один итоговый .gitlab-ci.yml файл. Этот файл будет включать три основных этапа: сборку образов с использованием Kaniko, деплой с помощью ArgoCD и использование Vault для управления секретами. Пайплайн будет разделён на три окружения: production, staging, и development.

Итоговый .gitlab-ci.yml файл

# Включение шаблонов для сборки, деплоя и работы с секретами Vault

include:
  - local: 'kaniko-build-template.yml'
  - local: 'argo-deploy-template.yml'
  - local: 'vault-secrets-template.yml'

# Определение этапов пайплайна

stages:
  - build
  - deploy

# Определение триггеров для пайплайнов на основе веток

workflow:
  rules:
    - if: '$CI_COMMIT_REF_NAME == "main"'
    - if: '$CI_COMMIT_REF_NAME == "staging"'
    - if: '$CI_COMMIT_REF_NAME == "develop"'

# Билд для production с Kaniko и Vault

build_prod:
  stage: build
  image: gcr.io/kaniko-project/executor:latest
  environment:
    name: production
  script:
    # Вход в Vault и получение секретов для продакшена
    - vault login $VAULT_TOKEN
    - export DB_PASSWORD=$(vault kv get -field=password secret/prod/db-creds)
    # Билд Docker-образа через Kaniko
    - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination $CI_REGISTRY_IMAGE:prod --cache=true
  rules:
    - if: '$CI_COMMIT_REF_NAME == "main"'

# Билд для staging с Kaniko и Vault

build_stage:
  stage: build
  image: gcr.io/kaniko-project/executor:latest
  environment:
    name: staging
  script:
    - vault login $VAULT_TOKEN
    - export DB_PASSWORD=$(vault kv get -field=password secret/staging/db-creds)
    - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination $CI_REGISTRY_IMAGE:stage --cache=true
  rules:
    - if: '$CI_COMMIT_REF_NAME == "staging"'

# Билд для development с Kaniko и Vault

build_dev:
  stage: build
  image: gcr.io/kaniko-project/executor:latest
  environment:
    name: development
  script:
    - vault login $VAULT_TOKEN
    - export DB_PASSWORD=$(vault kv get -field=password secret/dev/db-creds)
    - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination $CI_REGISTRY_IMAGE:dev --cache=true
  rules:
    - if: '$CI_COMMIT_REF_NAME == "develop"'

# Деплой на production через ArgoCD
deploy_prod:
  stage: deploy
  image: bitnami/kubectl:latest
  environment:
    name: production
  script:
    - kubectl config set-cluster $K8S_CLUSTER --server=$K8S_SERVER --insecure-skip-tls-verify=true
    - kubectl config set-credentials $K8S_USER --token=$K8S_TOKEN
    - kubectl config set-context $K8S_CONTEXT --cluster=$K8S_CLUSTER --user=$K8S_USER
    - kubectl config use-context $K8S_CONTEXT
    - argocd app sync my-app-prod --prune --timeout 300

  rules:

    - if: '$CI_COMMIT_REF_NAME == "main"'

# Деплой на staging через ArgoCD

deploy_stage:
  stage: deploy
  image: bitnami/kubectl:latest
  environment:
    name: staging
  script:
    - kubectl config set-cluster $K8S_CLUSTER --server=$K8S_SERVER --insecure-skip-tls-verify=true
    - kubectl config set-credentials $K8S_USER --token=$K8S_TOKEN
    - kubectl config set-context $K8S_CONTEXT --cluster=$K8S_CLUSTER --user=$K8S_USER
    - kubectl config use-context $K8S_CONTEXT
    # Синхронизация приложения через ArgoCD
    - argocd app sync my-app-stage --prune --timeout 300
  rules:
    - if: '$CI_COMMIT_REF_NAME == "staging"'

# Деплой на development через ArgoCD
deploy_dev:
  stage: deploy
  image: bitnami/kubectl:latest
  environment:
    name: development
  script:
    - kubectl config set-cluster $K8S_CLUSTER --server=$K8S_SERVER --insecure-skip-tls-verify=true
    - kubectl config set-credentials $K8S_USER --token=$K8S_TOKEN
    - kubectl config set-context $K8S_CONTEXT --cluster=$K8S_CLUSTER --user=$K8S_USER
    - kubectl config use-context $K8S_CONTEXT
    - argocd app sync my-app-dev --prune --timeout 300
  rules:
    - if: '$CI_COMMIT_REF_NAME == "develop"'

Обзор итогового пайплайна:

  1. Сборка (build):

    • Используется Kaniko для сборки Docker-образов для каждого окружения (prod, staging, dev).

    • В процессе сборки, перед запуском Kaniko, из Vault извлекаются секреты для каждого окружения (например, пароли для баз данных).

  2. Деплой (deploy):

    • После успешной сборки происходит деплой через ArgoCD.

    • Для каждого окружения используется отдельное приложение в ArgoCD (my-app-prod, my-app-stage, my-app-dev).

  3. Workflow:

    • Пайплайн запускается только при работе с ветками main (production), staging, и develop.

Заключение

Эффективный CI/CD-процесс сегодня — это не просто автоматизация сборки и деплоя, а комплексная система, которая включает управление секретами, безопасное окружение для сборки, гибкость конфигураций и контроль на каждом этапе. Интеграция инструментов, таких как GitLab CI, Kaniko, ArgoCD и HashiCorp Vault, позволяет создавать мощные и безопасные пайплайны, которые обеспечивают стабильность и предсказуемость процесса разработки.

А какие практики CI/CD будут актуальными ещё долгое время? Делитесь своим мнением в комментах.

Кстати, наша команда ещё публикует полезный контент и на других платформах: в телеграм-канале DevOps FM, на vc.ru и YouTube. Будем ждать вас и там :)

© Habrahabr.ru