Кэширование, секреты и развёртывания
Что это такое
Когда рабочий процесс выполняется надёжно, жить с ним приятно или нет решают три заботы: сколько он длится, в безопасности ли его учётные данные и означает ли зелёный прогон, что изменение протестировано, или что оно выпущено. Кэширование отвечает на первое, секреты — на второе, а разделение проверок и развёртываний — на третье. Все три — детали конфигурации, последствия которых несоразмерны их размеру.
Почему это важно
Конвейер, который заново ставит все зависимости на каждом прогоне, отнимает минуты из дня каждого участника и поощряет копить изменения. Конвейер, небрежно обращающийся с секретами, выливает их в логи или передаёт недоверенному коду. Конвейер, развёртывающий автоматически при прохождении тестов, тихо отменил решение о релизе. Ни один из этих отказов не объявляет о себе; они накапливаются как медленный и слегка небезопасный процесс, который никто не помнит, что выбирал.
Ключевые идеи
Кэшируйте то, что меняется редко, с ключом по тому, что меняется
Обычные кандидаты — зависимости и результаты сборки. Ключ кэша должен включать файл блокировки версий, чтобы изменение зависимости промахивалось мимо кэша, а всё остальное в него попадало. Кэш с ключом из ничего или из константы вечно отдаёт устаревшие артефакты, что даёт загадочные отказы, исчезающие при очистке кэша.
key: deps-${{ runner.os }}-${{ hashFiles('package-lock.json') }}
Секреты имеют область, маскировку и короткий срок жизни
Секрет подставляется как значение окружения, маскируется в логах там, где платформа это поддерживает, и ограничивается окружениями или задачами, которым он нужен. Предпочитайте короткоживущие учётные данные, полученные через федерацию идентичностей, долгоживущим токенам, хранимым как секреты: первые истекают сами, вторые — нет. Никогда не выводите секрет в лог и помните, что команда, печатающая свои аргументы, может его выдать.
Проверка — не развёртывание, и окружения их разделяют
Проверка pull request’а сообщает, хорошее ли изменение; развёртывание меняет то, что работает у пользователей. Держа их разными задачами, привязанными к разным событиям и защищёнными правилами окружения, вы получаете, что прошедший тест сам по себе не является релизом. Там, где платформа поддерживает обязательных рецензентов для окружения, это естественное место для решения человека.
Задачам развёртывания нужны собственные идентичность и права
У токена рабочего процесса есть права, и по умолчанию они могут быть шире, чем нужно задаче. Выдача только тех прав, которые задача использует, ограничивает то, что может сделать скомпрометированный шаг. Задача развёртывания в особенности должна быть единственной, держащей учётные данные, меняющие продакшен.
Артефакты связывают задачи и служат записью
Выгрузка артефакта сборки делает его доступным для поздней задачи и оставляет доказательство того, что именно было собрано. Ссылка на артефакт по имени и версии вместо повторной сборки — то, что делает протестированное поставленным. Настройки хранения решают, сколько эта запись живёт.
Прогоны недолговечны, поэтому о машине нельзя ничего предполагать
Постоянного рабочего каталога нет, пакет не установлен с прошлого раза, процесс не остался работать. Всё, что должно сохраниться, принадлежит кэшу, артефакту или внешнему сервису — и его нужно достать по ключу, который рабочий процесс может восстановить.
Практика
Закэшируйте установку зависимостей
Добавьте шаг кэша с ключом по файлу блокировки в рабочий процесс, устанавливающий зависимости, и убедитесь, что второй прогон быстрее, а изменение файла блокировки сбрасывает кэш.
Готово, когда: второй прогон сообщает о попадании в кэш и завершается заметно быстрее, а изменение файла блокировки даёт промах.
Отделите релиз от проверки
Дайте задаче развёртывания окружение, требующее одобрения, запускаемое только на push в основную ветку, и убедитесь, что pull request до неё не дотягивается.
Готово, когда: pull request выполняет проверки, но развернуть не может, а задача развёртывания перечисляет одобрение перед продолжением.
Частые ошибки
- Кэш с ключом, который никогда не меняется — устаревшие зависимости восстанавливаются бесконечно; исправить: стройте ключ кэша по хешу файла блокировки.
- Хранить долгоживущий токен как секрет — он не истекает, и его приходится менять вручную; исправить: предпочитайте короткоживущие федеративные учётные данные.
- Выводить секреты или отладочный вывод с ними — значение попадает в лог; исправить: маскируйте секреты и не печатайте конфигурацию.
- Развёртывать автоматически при прошедшем тесте — решение о релизе тихо отменяется; исправить: привяжите развёртывание к событию и правилу окружения.
- Давать рабочему процессу широкий токен — у скомпрометированного шага больше доступа, чем нужно; исправить: задайте минимальные права на каждую задачу.
Что читать дальше
- Рабочие процессы, триггеры и задачи (06-cloud-devops/15-github-actions/01-workflows-triggers-and-jobs) — структура, к которой применяются эти настройки.
- GitHub Actions (06-cloud-devops/15-github-actions) — сам сервис автоматизации.
- Режимы отказа конвейера (06-cloud-devops/08-ci-cd/02-pipeline-failure-modes) — более широкие способы, которыми конвейер теряет доверие, и секреты с медленными прогонами — два из них.