Сценарий и ограничения
Есть одно stateless HTTP-приложение, Dockerfile, готовые тесты и две Linux-машины с Docker Compose v2. На каждой — отдельное окружение. Приложение слушает порт 8080 и содержит исполняемый файл /app/healthcheck, который возвращает код 0 только при готовности обслуживать запросы. Это условие примера: замените проверку на подходящую вашему приложению.
База данных и миграции схемы здесь не разворачиваются. Одна реплика при пересоздании может быть недоступна; этот сценарий не обеспечивает выпуск без простоя. Сначала отработайте его на stage. Это учебный разбор, не отчёт о внедрении у клиента.
1. Соберите один артефакт
Pipeline запускает имеющиеся тесты и прекращает работу при ошибке. После этого собирает образ один раз, публикует в вашем registry и сохраняет полный адрес с digest, например registry.example.org/team/app@sha256:…. Многоточие — обозначение, а не рабочий digest.
На stage и production передаётся этот же адрес. Повторная сборка для production может дать другой результат даже из того же коммита, если изменились зависимости. Не используйте latest как единственный идентификатор выпуска.
В артефакте CI сохраните commit SHA, digest, результат тестов и время сборки. Пароли registry и SSH-ключи не включайте. Registry должен сохранять предыдущий рабочий образ, иначе возврат на него может оказаться невозможен.
2. Зафиксируйте конфигурацию окружения
Пример compose.yaml для тестовой машины. Внешний HTTPS-прокси уже настроен и обращается к loopback-порту приложения. Ограничение 127.0.0.1 не открывает порт напрямую в интернет.
services:
app:
image: ${APP_IMAGE:?APP_IMAGE is required}
ports:
- "127.0.0.1:8080:8080"
healthcheck:
test: ["CMD", "/app/healthcheck"]
interval: 10s
timeout: 3s
retries: 6
start_period: 20s
restart: unless-stoppedДля APP_IMAGE требуется непустое значение. Compose подставляет переменные из окружения и env-файлов; переменная shell имеет приоритет. Поэтому в командах ниже образ задаётся явно для каждой операции. Подробнее: подстановка переменных в Docker Compose.
Расположите конфигурацию в отдельном каталоге приложения и всегда указывайте один project name. Секреты приложения должны поступать отдельным согласованным способом. Не печатайте полную разрешённую конфигурацию в CI, если в ней есть секреты.
3. Проверьте выпуск на stage
Следующие команды выполняет уполномоченный оператор в каталоге с compose.yaml. Введите реальный адрес образа из артефакта CI; файл current-image.txt должен содержать последний успешно проверенный digest. Для первого развёртывания предыдущей версии нет — не имитируйте возможность отката.
# Bash, тестовое окружение. Вставьте реальный digest из CI.
read -r -p 'Новый образ с digest: ' next_image
case "$next_image" in
*@sha256:*) ;;
*) echo 'Нужен адрес образа с digest'; exit 1 ;;
esac
# До изменения сохранить предыдущий успешно принятый образ.
cp current-image.txt previous-image.txt || exit 1
APP_IMAGE="$next_image" docker compose -p product -f compose.yaml pull app || exit 1
APP_IMAGE="$next_image" docker compose -p product -f compose.yaml up \
-d --no-build --wait --wait-timeout 120 app || exit 1
# Только после проверки здоровья — проверка маршрута приложения.
curl --fail --silent --show-error http://127.0.0.1:8080/health || exit 1Путь /health здесь тоже часть контракта примера. Замените его на существующий endpoint. Затем отдельно проверьте реальный HTTPS-адрес и ключевой пользовательский сценарий: внутренний healthcheck не обнаружит, например, ошибку маршрутизации на прокси.
Опция --wait ожидает running/healthy; без healthcheck одного running недостаточно для подтверждения готовности приложения. При изменении образа Compose пересоздаёт контейнер. См. справочник docker compose up.
Если любая команда завершилась ошибкой, не помечайте релиз успешным. После ошибки up новая версия уже могла частично запуститься: проверьте состояние, сохраните диагностику и выполните согласованный возврат. Скрипт с exit 1 только останавливает процедуру, а не делает автоматический rollback.
После всех проверок запишите новый digest атомарно, в том же каталоге:
printf '%s\n' "$next_image" > current-image.txt.tmp &&
mv current-image.txt.tmp current-image.txt4. Отрепетируйте возврат
Возврат — отдельное действие с проверкой результата. На stage возьмите предыдущий рабочий digest и выполните:
previous_image=$(cat previous-image.txt) || exit 1
APP_IMAGE="$previous_image" docker compose -p product -f compose.yaml pull app || exit 1
APP_IMAGE="$previous_image" docker compose -p product -f compose.yaml up \
-d --no-build --wait --wait-timeout 120 app || exit 1
curl --fail --silent --show-error http://127.0.0.1:8080/health || exit 1
# Сначала также проверить внешний HTTPS и пользовательский сценарий.
# Затем обновить запись текущей версии:
printf '%s\n' "$previous_image" > current-image.txt.tmp &&
mv current-image.txt.tmp current-image.txtСохраните результат возврата в журнале CI. Не удаляйте volumes командой down -v ради отката приложения. Если новая версия меняла данные несовместимым способом, возвращение старого образа само по себе не восстанавливает работоспособность: нужен заранее проверенный план для данных.
5. Перенесите процедуру в CI
Разделите jobs: тесты → сборка → stage → проверка → разрешение на production → production → проверка. Сохраните digest как артефакт между jobs. На сервере используйте ограниченный аккаунт и проверенный SSH host key; не отключайте проверку ключа хоста. Docker-доступ даёт широкие полномочия — runner для недоверенных merge requests не должен получать production-секреты.
Запретите одновременные выпуски в одно окружение, включая ручной возврат: иначе два задания перезапишут previous-image.txt. Например, в GitLab для jobs выпуска и отката задают один resource_group: production. Такая блокировка сериализует jobs, но сама по себе не гарантирует актуальность версии: перед разрешением на выпуск проверьте, что выбранный commit не вытеснен новым. Документация GitLab о resource groups.
Для production повторите проверенную процедуру с тем же digest и собственным журналом текущей версии. Успех stage — условие допуска, но не замена проверке production. Автоматизацию отката добавляйте только после проверки её критериев и сценария отказа.
Чек-лист приёмки pipeline
- Ошибка тестов останавливает выпуск.
- Stage и production используют один digest; из журнала можно найти исходный commit.
- Неуспешный healthcheck не превращается в успешный статус релиза.
- Проверен реальный HTTPS-маршрут и критичный пользовательский сценарий.
- Предыдущий образ доступен в registry; возврат выполнен на stage и проверен.
- Два выпуска в одно окружение не выполняются одновременно.
- Секреты отсутствуют в Git, артефактах и журнале; права выпуска ограничены.
- Инструкция описывает первый запуск, обычный релиз, ошибку и возврат.
Если нужно адаптировать процесс под ваш стек, настройка CI/CD включает одно приложение, два готовых окружения, существующие тесты, деплой, откат и передачу инструкции. Более широкий состав согласуется отдельно.
Разобраться в основах DevOps
Если перед практикой нужно выстроить порядок изучения Linux, контейнеров и доставки приложений, используйте дорожную карту DevOps на devroadmap. Карту можно проходить как учебный маршрут, а это руководство — использовать для разбора конкретного сценария релиза.
Обсудим вашу задачу
Команда под задачу. Закреплённый техлид. Состав работ, доступность и условия согласуем до старта.
Бриф остаётся только в памяти браузера. Скопируйте текст и вставьте его в бота вручную. Копирование и открытие бота не означают отправку или получение заявки.