Как мы ускорили деплой в 40 раз
Кэш сборки, триплеты и честные замеры на монорепозитории
Обычный деплой занимал 14 минут 12 секунд. Из них 11 уходит на сборку и прогон тестов, и всё это - время, когда продакшен живёт старой версией.
Замеры до#
Обычный пайплайн выглядел так:
stages:
- build # 6 мин 12 с
- test # 4 мин 30 с
- publish # 3 мин
| Этап | Время | Доля |
|---|---|---|
| Установка зависимостей | 5 мин 48 с | 42% |
| Компиляция | 22 с | 3% |
| Тесты | 4 мин 30 с | 32% |
| Сборка образа | 38 с | 5% |
| Выкладка | 3 мин | 18% |
Шаг 1: кэш зависимостей#
Монорепозиторий на Node.js. Ключ кэша - хеш файлов блокировки:
LOCK_HASH=$(sha256sum package-lock.json | cut -d' ' -f1)
echo "cache key: $LOCK_HASH"
TypeScript тоже кэшируется инкрементально:
export default defineConfig({
cacheDir: './node_modules/.cache/astro',
vite: {
build: { rollupOptions: { cache: true } },
},
});
Результат: 5 мин 48 с → 6 с.
Шаг 2: параллельные тесты#
Тесты шли последовательно, хотя 90% из них независимы. Разбили на три группы:
jobs:
test:
strategy:
matrix:
shard: [1, 2, 3]
steps:
- run: npm ci
- run: npm test -- --shard=${SHARD}/3
4 мин 30 с → 1 мин 05 с.
Шаг 3: кэш артефактов#
Сборка образа повторялась с нуля. Перенесли build в отдельную стадию, а на publish отдаём готовый артефакт:
# было
docker build -t app . && docker push app && ssh prod 'docker pull app'
# стало
docker buildx build --cache-from=registry/app:build -t app --push .
ssh prod 'docker pull app && docker restart app'
3 мин → 22 с.
Итог#
| Этап | Было | Стало | Экономия |
|---|---|---|---|
| Сборка | 6 мин 12 с | 48 с | -92% |
| Тесты | 4 мин 30 с | 1 мин 05 с | -76% |
| Деплой | 3 мин | 22 с | -88% |
| Всего | 14 мин 12 с | 2 мин 15 с | -84% |
Что дало в деньгах#
При 40 деплоях в неделю экономия - 11 часов в месяц. Плюс главный эффект: частота выкладки выросла с 8 до 35 раз в неделю, потому что стало не страшно катить часто.
Что бы я сделал иначе#
- Поставить замеры до оптимизации, а не после. Это заняло бы час и сэкономило недели.
- Начать с кэша зависимостей - самый большой выигрыш при минимальном риске.
- Не трогать последовательность тестов на этапе, когда можно было добавить кэш.