Как организовать полный цикл CI/CD для трех команд?

«Как организовать полный цикл CI/CD для трех команд?» — вопрос из категории CI/CD, который задают на 24% собеседований Devops Инженер. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

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

Архитектура:

Git Repositories (GitLab/GitHub)
├── Team A Repo
├── Team B Repo
└── Team C Repo
    └── .gitlab-ci.yml

CI/CD Pipeline (GitLab CI/GitHub Actions/Jenkins)
├── Shared Templates Library
├── Team-Specific Configs
└── Artifact Registry

GitOps (ArgoCD/Flux)
├── Git Repo: k8s-configs
│   ├── teams/team-a/
│   ├── teams/team-b/
│   └── teams/team-c/
└── Production Clusters

Monitoring (Prometheus/Grafana)
└── Team Dashboards

1. Git стратегия и репозитории

  • Каждая команда работает в своем репозитории или в монорепозитории с четкими границами
  • Использую GitFlow или Trunk-Based Development в зависимости от зрелости команд
  • Protected branches + mandatory code review

2. CI Pipeline (на примере GitLab CI)

Общие шаблоны в .gitlab-ci-templates.yml:

# Общий template для всех команд
.base_template: &base_template
  image: docker:latest
  services:
    - docker:dind
  before_script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY

.build_template: &build_template
  <<: *base_template
  stage: build
  script:
    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
  artifacts:
    paths:
      - docker-image.txt

Конфигурация команды A в .gitlab-ci.yml:

include:
  - project: 'infrastructure/ci-templates'
    file: '/templates/.gitlab-ci-templates.yml'

variables:
  TEAM: team-a
  K8S_NAMESPACE: team-a-${CI_ENVIRONMENT_NAME}

stages:
  - test
  - build
  - security-scan
  - deploy-dev
  - deploy-staging
  - deploy-prod

unit-tests:
  stage: test
  image: node:18
  script:
    - npm ci
    - npm test
  coverage: '/Statementss*:s*([^%]+)/'

build-image:
  <<: *build_template
  variables:
    DOCKER_BUILD_ARGS: --build-arg TEAM=$TEAM

security-scan:
  stage: security-scan
  image: aquasec/trivy:latest
  script:
    - trivy image --exit-code 1 --severity HIGH,CRITICAL $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA

deploy-to-dev:
  stage: deploy-dev
  image: bitnami/kubectl:latest
  script:
    - kubectl apply -f k8s/overlays/dev/
    - kubectl rollout status deployment/${TEAM}-app -n ${K8S_NAMESPACE}
  environment:
    name: dev
    url: https://dev.team-a.company.com

deploy-to-prod:
  stage: deploy-prod
  image: bitnami/kubectl:latest
  script:
    - kubectl apply -f k8s/overlays/prod/
    - kubectl rollout status deployment/${TEAM}-app -n ${K8S_NAMESPACE}
  environment:
    name: production
    url: https://team-a.company.com
  when: manual
  only:
    - main

3. CD с GitOps (ArgoCD)

Структура GitOps репозитория:

kubernetes-configs/
├── base/                    # Базовые манифесты
│   ├── team-a/
│   │   ├── deployment.yaml
│   │   └── service.yaml
│   └── kustomization.yaml
├── overlays/
│   ├── dev/                # Конфигурация для dev
│   │   ├── team-a/
│   │   │   ├── kustomization.yaml
│   │   │   └── patch-replicas.yaml
│   │   └── team-b/
│   ├── staging/
│   └── production/
└── applicationsets/        # ArgoCD ApplicationSets
    ├── team-a.yaml
    ├── team-b.yaml
    └── team-c.yaml

ArgoCD ApplicationSet для команды A:

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: team-a-apps
spec:
  generators:
  - git:
      repoURL: https://github.com/company/kubernetes-configs.git
      revision: HEAD
      directories:
      - path: overlays/dev/team-a
      - path: overlays/staging/team-a
      - path: overlays/production/team-a
  template:
    metadata:
      name: '{{path.basename}}-{{path[-2]}}'
    spec:
      project: team-a
      source:
        repoURL: https://github.com/company/kubernetes-configs.git
        targetRevision: HEAD
        path: '{{path}}'
      destination:
        server: https://kubernetes.default.svc
        namespace: 'team-a-{{path[-2]}}'
      syncPolicy:
        automated:
          selfHeal: true
          prune: true
        syncOptions:
        - CreateNamespace=true

4. Инфраструктура как код (Terraform)

# Модуль для команды
module "team_a_infra" {
  source = "../modules/team-infra"

  team_name = "team-a"
  environment = var.environment

  # Ресурсы команды
  vpc_id = data.aws_vpc.main.id
  subnet_ids = data.aws_subnets.private.ids

  # CI/CD интеграция
  github_repo = "company/team-a-app"
  container_registry = aws_ecr_repository.team_a.repository_url

  # Мониторинг
  prometheus_endpoint = data.aws_prometheus_workspace.main.prometheus_endpoint
}

# RBAC для доступа команды к своим ресурсам
resource "kubernetes_role" "team_a_dev" {
  metadata {
    name = "team-a-developer"
    namespace = "team-a-${var.environment}"
  }
  rule {
    api_groups = ["", "apps"]
    resources = ["pods", "deployments", "services"]
    verbs = ["get", "list", "watch", "create", "update", "patch"]
  }
}

5. Общие сервисы и стандарты

  • Общий Container Registry с пространствами имен по командам
  • Единая система мониторинга (Prometheus + Grafana) с дашбордами по командам
  • Централизованное логирование (Loki + Elasticsearch)
  • Security Scanning (Trivy, Snyk) на всех этапах
  • Cost Management тегирование всех ресурсов по командам

6. Процессы и governance

  • Еженедельные sync-митинги по улучшению CI/CD процессов
  • Общие метрики: Deployment Frequency, Lead Time, Change Failure Rate, MTTR
  • Автоматические approval-воркфлоу для production-деплоев
  • Ротация on-call duty между командами

Такая система позволяет командам работать автономно, но в рамках единых стандартов безопасности и качества.