Внедрение DevOps-культуры: от «это не наша проблема» до 50 деплоев в день

Начальное состояние: диагноз

Когда CTO «ИнфоСистем» обратился к нам, он описал проблему одной фразой: «Разработчики кидают код через стену, а ops-команда ловит и ругается». Мы провели двухнедельный аудит и зафиксировали следующую картину:

  • Разрозненные команды: 5 команд разработки, отдельная QA-команда, отдельный ops-отдел. Коммуникация через тикеты в Jira, среднее время ответа — 3 дня.
  • Ручные процессы: деплой — 47-шаговая инструкция в Confluence, выполняет один человек (Андрей). Если Андрей в отпуске — деплоя нет.
  • Культура обвинений: после каждого инцидента — поиск виноватого. Разработчики боялись деплоить, ops-команда боялась менять инфраструктуру.
  • Нет автоматических тестов: QA-команда тестировала вручную 2-3 недели перед каждым релизом.
  • Нет мониторинга: об инцидентах узнавали от пользователей через поддержку.

DORA-метрики на старте были катастрофические:

МетрикаЗначениеУровень (DORA)
Deployment Frequency1 раз в кварталLow
Lead Time for Changes3 месяцаLow
Change Failure Rate45%Low
Mean Time to Recovery48 часовLow

Дорожная карта трансформации

Мы составили план на 2 года, разбитый на 4 фазы. Ключевой принцип — начинать с малого, показывать результаты быстро, масштабировать постепенно.

  • Фаза 1 (месяцы 1-3): Фундамент — CI/CD для одной пилотной команды, базовый мониторинг.
  • Фаза 2 (месяцы 4-8): Масштабирование — IaC, автотесты, cross-functional команды.
  • Фаза 3 (месяцы 9-16): Культура — blameless postmortems, shared on-call, внутренняя платформа.
  • Фаза 4 (месяцы 17-24): Оптимизация — метрики, self-service, полная автономия команд.

Фаза 1: пилотная команда и первый пайплайн

Мы выбрали одну команду из 8 человек, которая работала над API-платформой. Выбор был неслучайный — их тимлид был открыт к изменениям, а продукт позволял деплоить независимо от остальных.

Первый CI/CD пайплайн собрали за неделю:

# .gitlab-ci.yml — первая версия пайплайна
stages:
  - build
  - test
  - deploy

build:
  stage: build
  script:
    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA

test:
  stage: test
  script:
    - docker run --rm $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA npm test

deploy-staging:
  stage: deploy
  script:
    - ssh deploy@staging "docker pull $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA &&
        docker-compose up -d"
  environment:
    name: staging
  only:
    - main

Простой? Да. Но он работал. Команда начала деплоить на staging каждый день вместо раза в квартал. Через месяц мы добавили автоматический деплой в production с ручным подтверждением.

Параллельно поставили Prometheus + Grafana для базового мониторинга. Впервые команда видела, что происходит с их сервисом в продакшене, в реальном времени.

Ломаем стены: кросс-функциональные команды

Это был самый болезненный этап. Мы предложили реструктуризацию: вместо отдельных команд разработки, QA и ops создать кросс-функциональные продуктовые команды. Каждая команда включает:

  • 4-5 разработчиков (backend + frontend)
  • 1 QA-инженер (в команде, не в отдельном отделе)
  • 1 SRE/DevOps-инженер (по одному на 2-3 команды на первых порах)

Сопротивление было серьёзным. QA-руководитель считал, что качество упадёт. Ops-лид боялся потерять контроль. Мы провели серию воркшопов:

  • «День в чужой шкуре»: разработчики дежурили с ops-командой, ops смотрели на процесс разработки.
  • Совместная ретроспектива: впервые все сидели в одной комнате и обсуждали проблемы, а не кидали тикеты.
  • Shared on-call: разработчики начали дежурить по своим сервисам. Это радикально изменило отношение к качеству кода — когда тебя будят в 3 ночи из-за бага, ты начинаешь писать код иначе.

Infrastructure as Code: Terraform

Инфраструктура «ИнфоСистем» существовала в виде «серверов-снежинок» — каждый настроен вручную, никто не знает полную конфигурацию. Мы начали переводить всё в Terraform:

# infrastructure/main.tf
terraform {
  required_providers {
    yandex = { source = "yandex-cloud/yandex" }
  }
  backend "s3" {
    bucket   = "infosystems-terraform-state"
    key      = "production/main.tfstate"
    endpoint = "storage.yandexcloud.net"
    region   = "ru-central1"
  }
}

resource "yandex_compute_instance" "api" {
  count       = var.api_instance_count
  name        = "api-${count.index}"
  platform_id = "standard-v3"

  resources {
    cores  = 4
    memory = 8
  }

  boot_disk {
    initialize_params {
      image_id = data.yandex_compute_image.ubuntu.id
      size     = 50
    }
  }

  network_interface {
    subnet_id = yandex_vpc_subnet.default.id
    nat       = true
  }

  metadata = {
    ssh-keys = "deploy:${file("~/.ssh/deploy.pub")}"
  }
}

Процесс перевода занял 4 месяца. Каждую неделю мы импортировали в Terraform очередной кусок инфраструктуры, пока вся среда не оказалась под управлением кода. Теперь любое изменение проходит через PR, code review и terraform plan в CI:

# CI pipeline для Terraform
plan:
  script:
    - terraform init
    - terraform plan -out=tfplan
    - terraform show -json tfplan > plan.json
  artifacts:
    paths: [tfplan, plan.json]

apply:
  script:
    - terraform apply -auto-approve tfplan
  when: manual    # Ручное подтверждение для production
  only:
    - main

Культура автоматического тестирования

Ручное QA-тестирование длилось 2-3 недели. Мы поставили цель: 80% покрытие автотестами за 6 месяцев. Звучит амбициозно, но мы шли итеративно.

Первый шаг — контрактные тесты. Если сервис A зависит от API сервиса B, оба тестируют контракт:

# tests/contracts/test_user_api_contract.py
import pytest
from pact import Consumer, Provider

pact = Consumer('BookingService').has_pact_with(Provider('UserService'))

def test_get_user():
    expected = {
        'id': '123',
        'email': Like('user@example.com'),
        'name': Like('Иван Петров'),
    }

    (pact
        .given('user 123 exists')
        .upon_receiving('a request for user 123')
        .with_request('GET', '/api/users/123')
        .will_respond_with(200, body=expected))

    with pact:
        result = user_client.get_user('123')
        assert result['id'] == '123'

Параллельно ввели правило: каждый новый PR содержит тесты. Старый код покрывали тестами при каждом изменении. За 6 месяцев покрытие выросло с 4% до 72%.

Blameless Postmortems

Это изменение, которое оказало наибольшее влияние на культуру. После каждого инцидента мы проводим разбор без обвинений. Формат:

# Шаблон postmortem

## Инцидент: [Название]
**Дата:** YYYY-MM-DD
**Длительность:** X часов
**Severity:** SEV-1/SEV-2/SEV-3
**Ответственный за разбор:** [Имя]

## Хронология
- HH:MM — Обнаружено (как?)
- HH:MM — Эскалация
- HH:MM — Причина найдена
- HH:MM — Fix задеплоен
- HH:MM — Мониторинг подтвердил восстановление

## Корневая причина
[Технический анализ, не «Вася забыл»]

## Что сработало хорошо
- [Что помогло быстрее решить]

## Что можно улучшить
- [Без имён, только процессы и системы]

## Action Items
- [ ] [Конкретное действие] — владелец: [команда] — срок: [дата]
- [ ] [Конкретное действие] — владелец: [команда] — срок: [дата]

Первые три postmortem прошли тяжело. Люди по привычке искали виноватого. Мы ввели правило: фасилитатор прерывает любую попытку обвинения фразой «Мы ищем системные причины, не людей». К пятому postmortem команды привыкли. К десятому — начали сами предлагать системные улучшения.

Internal Developer Platform

К месяцу 12 у нас было 8 кросс-функциональных команд, каждая со своим пайплайном. Проблема: каждая команда настраивала CI/CD по-своему, конфигурации расходились, ops-инженеры тратили время на поддержку 8 разных пайплайнов.

Мы построили Internal Developer Platform (IDP) — набор абстракций поверх инфраструктуры:

# service.yaml — декларативное описание сервиса
apiVersion: platform.infosystems.ru/v1
kind: Service
metadata:
  name: booking-api
  team: booking
spec:
  language: python
  version: "3.11"

  build:
    dockerfile: Dockerfile
    cache: true

  deploy:
    replicas:
      staging: 1
      production: 3
    resources:
      cpu: "500m"
      memory: "512Mi"
    healthCheck:
      path: /health
      interval: 30s

  dependencies:
    - postgres:15
    - redis:7

  monitoring:
    alerts:
      - type: error_rate
        threshold: 5%
        channel: "#booking-alerts"
      - type: latency_p99
        threshold: 500ms
        channel: "#booking-alerts"

Разработчик описывает сервис декларативно, а платформа генерирует Dockerfile, CI/CD пайплайн, Kubernetes-манифесты, мониторинг и алерты. Команды перестали думать об инфраструктуре и сфокусировались на продукте.

Метрики, которые имеют значение: DORA

С самого начала мы измеряли четыре ключевых метрики DORA и вывели их на дашборд, видимый всем:

  • Deployment Frequency — как часто деплоим в production.
  • Lead Time for Changes — от коммита до production.
  • Change Failure Rate — процент деплоев, вызвавших инцидент.
  • Mean Time to Recovery — среднее время восстановления после инцидента.

Мы собирали эти метрики автоматически из GitLab, Kubernetes и PagerDuty:

# scripts/dora_metrics.py
from datetime import datetime, timedelta
import gitlab
import json

gl = gitlab.Gitlab('https://gitlab.infosystems.ru', private_token=TOKEN)

def deployment_frequency(project_id, days=30):
    """Частота деплоев за последние N дней"""
    project = gl.projects.get(project_id)
    deployments = project.deployments.list(
        environment='production',
        updated_after=(datetime.now() - timedelta(days=days)).isoformat(),
        per_page=100
    )
    return len(deployments) / days  # деплоев в день

def lead_time(project_id, days=30):
    """Среднее время от merge в main до деплоя"""
    project = gl.projects.get(project_id)
    mrs = project.mergerequests.list(
        state='merged',
        updated_after=(datetime.now() - timedelta(days=days)).isoformat(),
        per_page=100
    )
    lead_times = []
    for mr in mrs:
        merged_at = datetime.fromisoformat(mr.merged_at)
        # Находим деплой, содержащий этот коммит
        deploy_time = find_deployment_time(project, mr.merge_commit_sha)
        if deploy_time:
            lead_times.append((deploy_time - merged_at).total_seconds() / 3600)
    return sum(lead_times) / len(lead_times) if lead_times else None

Работа с сопротивлением

Любая трансформация встречает сопротивление. Вот конкретные ситуации, с которыми мы столкнулись, и как их решали:

  • «У нас нет времени на тесты». Мы показали статистику: 45% деплоев вызывают инцидент, каждый инцидент стоит в среднем 16 человеко-часов. Инвестиция в тесты окупилась за 2 месяца.
  • «Ops потеряет работу». Мы переквалифицировали ops-инженеров в SRE. Вместо ручной настройки серверов они проектируют платформу. Зарплата выросла, работа стала интереснее.
  • «Мой менеджер не поддержит». Мы провели серию презентаций для менеджмента с конкретными цифрами: время выхода на рынок, стоимость инцидентов, скорость найма (инженеры хотят работать с современными практиками).
  • «Это работало 10 лет, зачем менять». Самые скептичные инженеры стали адвокатами перемен после того, как увидели результаты пилотной команды. Показывайте, а не рассказывайте.

Buy-in от руководства

Без поддержки CTO и CEO трансформация невозможна. Мы подготовили бизнес-кейс на языке, понятном руководству:

  • Сокращение Time-to-Market с 3 месяцев до 1 дня = конкурентное преимущество.
  • Снижение Change Failure Rate = меньше потерянных клиентов.
  • Автоматизация = тот же объём работы с меньшим количеством ручного труда (но не увольнения — переквалификация).
  • Привлечение талантов: сильные инженеры не хотят работать с устаревшим стеком.

CTO стал спонсором трансформации. Он выделил 20% времени каждой команды на инфраструктурные улучшения и защитил это решение перед CEO.

Хронология: 24 месяца трансформации

МесяцMilestone
1Аудит, дорожная карта, выбор пилотной команды
2Первый CI/CD пайплайн, базовый мониторинг
3Пилотная команда деплоит ежедневно на staging
5Terraform для 30% инфраструктуры
6Реструктуризация: 3 кросс-функциональные команды
8Terraform для 100% инфраструктуры, 5 команд с CI/CD
10Первый blameless postmortem, shared on-call
12IDP v1, все 8 команд деплоят самостоятельно
15Автотесты: покрытие 72%, контрактные тесты
18DORA-метрики на общем дашборде
20Self-service инфраструктура, feature flags
2450 деплоев в день, DORA Elite уровень

Результаты через 2 года

МетрикаДоПослеУровень DORA
Deployment Frequency1/квартал50/деньElite
Lead Time for Changes3 месяца2 часаElite
Change Failure Rate45%3%Elite
MTTR48 часов25 минутElite

DevOps — это не Jenkins, не Kubernetes и не Terraform. Это культура, в которой люди работают вместе над общей целью: доставлять ценность пользователям быстро и надёжно. Инструменты помогают, но без изменения культуры они бесполезны. Если вы хотите начать трансформацию в своей компании — мы готовы помочь.

Нужна помощь с проектом?

Специалисты АйТи Фреш помогут с архитектурой, DevOps, безопасностью и разработкой — 15+ лет опыта

📞 Связаться с нами
#blameless#buyin#code#developer#devops#dora#infrastructure#internal
Комментарии 0

Оставить комментарий

загрузка...