Начну с признания: однажды я создал конвейер CI/CD, настройку которого заняло больше времени, чем работа над самим проектом, для которого он предназначался. Конвейер имел 47 этапов, три различные среды тестирования и достаточно YAML, чтобы взрослый разработчик прослезился. Самое интересное? Он был предназначен для статического сайта документации, который обновлялся раз в пару месяцев.
Если вы когда-либо объясняли, почему для вашего «простого» развёртывания требуется 15 различных инструментов, три уровня оркестрации и степень PhD в DevOps, чтобы это понять, то эта статья для вас. Сегодня мы поговорим о слонах в серверной: ваш конвейер CI/CD, вероятно, делает гораздо больше, чем нужно, и усложняет вам жизнь, а не упрощает её.
Гонка вооружений CI/CD
В какой-то момент сообщество DevOps охватила коллективная зависть к функциональности. Каждая статья в блоге, выступление на конференции и учебник кричали: «Вам нужно БОЛЬШЕ автоматизации, БОЛЬШЕ этапов, БОЛЬШЕ инструментов!» Мы начали относиться к конвейерам CI/CD как к рождественским ёлкам, украшая их всеми новыми блестящими инструментами, которые могли найти.
Результат? Конвейеры, которые сложнее, чем приложения, которые они развёртывают. Я видел, как команды тратили три недели на отладку конвейера развёртывания для сервиса, который можно было бы развернуть вручную за пять минут. Когда ваша автоматизация занимает больше времени на исправление, чем выполнение задачи вручную, что-то пошло ужасно неправильно.
Признаки того, что ваш конвейер вышел из-под контроля
Вот несколько красных флагов, указывающих на то, что ваш конвейер CI/CD, возможно, страдает от хронического переосмысления:
20-минутный цикл обратной связи рока
Ничто так не убивает производительность разработчиков, как ожидание 20 минут, пока ваш конвейер заработает, только чтобы увидеть, как он терпит неудачу на последнем шаге. Если ваши разработчики идут пить кофе, проверяют социальные сети или пишут мемуары в ожидании сборок, ваш конвейер перешёл черту от полезного к вредному.
Рай для коллекционеров инструментов
Ваша схема конвейера выглядит как карта метро крупного мегаполиса. Вы используете Jenkins для запуска GitHub Actions для развёртывания с помощью Spinnaker, отслеживая всё это с помощью Datadog и храня артефакты в трёх разных местах. Каждый инструмент решает проблему, но вместе они создают кошмар обслуживания, который заставил бы Рубе Голдберга гордиться.
Писатель YAML
Ваши файлы конфигурации конвейера длиннее, чем большинство новелл. Если новому члену команды требуется больше дня, чтобы разобраться в вашем процессе развёртывания, вы, вероятно, всё усложнили. Помните, сложность — враг надёжности.
История двух конвейеров
Позвольте мне показать вам, что я имею в виду на реальном примере. Вот типичный «корпоративный» конвейер, с которым я недавно столкнулся:
Этот конвейер занимал 45 минут в хороший день и требовал обслуживания от трёх разных команд. Приложение, которое он развёртывал? Простой REST API с четырьмя конечными точками, обрабатывающими около 100 запросов в день.
Сравните это с тем, что на самом деле нужно команде:
name: Простое развёртывание
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Запуск тестов
run: npm test
- name: Сборка и развёртывание
run: |
docker build -t myapp .
docker push myregistry/myapp:latest
ssh user@server 'docker pull myregistry/myapp:latest && docker-compose up -d'
Три шага. Пять минут. Тот же результат.
Когда сложность действительно имеет смысл
Прежде чем начать удалять половину конфигурации вашего конвейера, давайте проясним: сложность не всегда плоха. Иногда вам действительно нужны те 47 этапов. Вот когда сложность конвейера оправдана:
Приложения с высокими ставками
Если вы развёртываете банковское программное обеспечение, медицинские устройства или что-либо, где сбой означает реальные последствия, тогда да, вам нужны комплексные тесты, несколько сред и строгие процессы развёртывания. Стоимость ошибки намного превышает стоимость сложного конвейера.
Большие распределённые команды
Когда у вас десятки разработчиков работают над микросервисами, которые взаимодействуют сложным образом, вам нужна оркестрация. Альтернатива — хаос. Но даже в этом случае индивидуальный конвейер каждого сервиса должен быть максимально простым.
Требования регуляторов
Иногда правительство или ваша отрасль требует определённых процессов. Соответствие нормам обязательно, даже если это делает ваш конвейер похожим на блок-схему, разработанную кем-то, кто никогда не писал код.
Стратегия упрощения
Готовы очистить свой конвейер? Вот пошаговый подход к сокращению избыточности:
Шаг 1: Аудит текущего конвейера
Опишите каждый этап вашего конвейера и задайте три вопроса:
- Что делает этот этап?
- Что произойдёт, если мы его удалим?
- Как часто он обнаруживает реальные проблемы?
Вы будете удивлены, сколько этапов существует «просто так» или были добавлены для решения проблем, которые больше не существуют.
Шаг 2: Измерение всего
Начните собирать метрики производительности вашего конвейера:
# Пример скрипта времени конвейера
#!/bin/bash
START_TIME=$(date +%s)
# Ваши шаги конвейера здесь
echo "Запуск тестов..."
npm test
echo "Сборка приложения..."
npm run build
echo "Развёртывание..."
npm run deploy
END_TIME=$(date +%s)
DURATION=$((END_TIME - START_TIME))
echo "Конвейер завершен за ${DURATION} секунд"
# Лог в вашу систему мониторинга
curl -X POST "https://ваш-конечный-пункт-метрик.com/pipeline-metrics" \
-d "duration=${DURATION}&status=success×tamp=$(date -u +%Y-%m-%dT%H:%M:%S.%3NZ)"
Отслеживайте время сборки, частоту сбоев и, самое главное, время восстановления при возникновении проблем.
Шаг 3: Начните с минимально жизнеспособного конвейера
Что минимально необходимо вашему конвейеру? Обычно это что-то вроде:
- Запуск тестов
- Сборка приложения
- Развёртывание в производство
Начните с этого. Увеличивайте сложность только тогда, когда у вас есть доказательства, что это необходимо.
# Минимально жизнеспособный CI/CD
name: MVP Конвейер
on: [push]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Тест
run: make test
- name: Сборка
run: make build
- name: Развёртывание
run: make deploy
if: github.ref == 'refs/heads/main'
Шаг 4: Добавляйте сложность постепенно
Добавляйте новые этапы только тогда, когда у вас есть конкретная проблема, которую нужно решить. Каждое добавление должно:
- Решать реальную проблему, с которой вы сталкиваетесь
- Иметь чёткие критерии успеха/неудачи
- Быть простым в отладке при сбоях
Скрытые затраты на сложность конвейера
Сложные конвейеры не только замедляют развёртывания; они создают скрытые затраты, которые со временем растут:
Переключение контекста разработчиков
Каждую минуту, которую разработчики тратят на размышления о конфигурации конвейера, они не тратят на функции. Я видел команды, где старшие разработчики тратили 20% своего времени на обслуживание инфраструктуры развёртывания. Это не масштабирование; это трата.
Трение при адаптации новых сотрудников
Новые члены команды должны разобраться в вашем процессе развёртывания, прежде чем они смогут стать продуктивными. Сложный конвейер может добавить недели к процессу адаптации. Я однажды работал в компании, где у нового разработчика ушло три недели, чтобы добиться первого успешного развёртывания. Три недели!
Кошмары отладки
Когда ваш конвейер сбоит (а он будет сбоить), вам нужно отладить не только код приложения, но и инфраструктуру развёртывания, несколько сред тестирования и взаимодействие между десятками инструментов. Это как пытаться починить двигатель автомобиля вслепую и в перчатках.
Психология раздувания конвейера
Почему мы продолжаем добавлять сложность в наши конвейеры? Отчасти это заблуждение о понесённых затратах — мы уже столько вложили в создание этой сложной системы, что упрощение кажется отказом. Отчасти это разработка, ориентированная на резюме — блестящий новый инструмент хорошо смотрится в резюме, даже если он не решает реальной проблемы.