diff --git a/docs/backend-react-dev-autodeploy.md b/docs/backend-react-dev-autodeploy.md index af367485..beda18de 100644 --- a/docs/backend-react-dev-autodeploy.md +++ b/docs/backend-react-dev-autodeploy.md @@ -70,24 +70,27 @@ Script находит уже работающий `web` container по labels: - `com.docker.compose.project=api-react`; - `com.docker.compose.service=web`. +Фактические Compose services `web`, `celerys` и `redis` подтверждены по labels +React-dev сервера, включая `com.docker.compose.service=celerys`. + Из labels читаются Compose project, working directory и config files. Все пути проверяются через `realpath`: working directory должен совпадать с `/root/api-react-dev`, каждый config file должен находиться внутри этого каталога, а legacy `docker-compose.yml` отклоняется. Compose-команда собирается как Bash array без `eval`. До любых изменений -containers проверяется наличие точных сервисов `web`, `celery` и `redis`. Иное -имя celery не угадывается: deploy завершается с явной ошибкой. +containers проверяется наличие точных сервисов `web`, `celerys` и `redis`. Иное +имя Celery service не угадывается: deploy завершается с явной ошибкой. ## Порядок deploy 1. Получение deployment lock через `flock`. 2. Проверка repository, origin, git state и stale deploy. 3. Сохранение предыдущих SHA, container IDs, image IDs и image references. -4. Сборка новых `web` и `celery` images без остановки текущего backend. +4. Сборка новых `web` и `celerys` images без остановки текущего backend. 5. `python manage.py check` во временном container нового `web` image. 6. `python manage.py migrate --noinput` с существующим React-dev `.env`. -7. Пересоздание только `web` и `celery` через `up -d --no-deps --force-recreate`. +7. Пересоздание только `web` и `celerys` через `up -d --no-deps --force-recreate`. 8. Ожидание running state с ограниченным timeout. 9. Публичный HTTPS health-check. @@ -109,7 +112,7 @@ https://api-react-dev.procollab.ru/programs/?limit=1 - непустой body; - отсутствие HTML/SPA fallback; - корректный JSON; -- running state `web` и `celery`. +- running state `web` и `celerys`. Используются ограниченные connect/total timeout и восемь попыток с паузой. Полный API response в лог не выводится. @@ -125,7 +128,7 @@ script: 1. возвращает предыдущий code SHA; 2. возвращает предыдущие image IDs на сохраненные image references; -3. пересоздает только `web` и `celery`; +3. пересоздает только `web` и `celerys`; 4. повторяет ограниченный React-dev health-check; 5. сообщает результат rollback; 6. завершает deploy с ошибкой даже при успешном rollback. @@ -152,7 +155,7 @@ GitHub Actions использует одну общую concurrency group с - repository имеет tracked изменения или неверный origin; - workflow устарел относительно текущего `origin/master`; - Compose labels отсутствуют или указывают вне React-dev; -- сервисы называются не `web`, `celery`, `redis`; +- сервисы называются не `web`, `celerys`, `redis`; - build, Django check или migration завершились ошибкой; - containers не перешли в running state; - HTTPS endpoint вернул redirect, HTML, не-JSON или статус не `200`; diff --git a/scripts/deploy_react_dev.sh b/scripts/deploy_react_dev.sh index eb221bd8..64bb8048 100644 --- a/scripts/deploy_react_dev.sh +++ b/scripts/deploy_react_dev.sh @@ -6,6 +6,7 @@ readonly DEPLOY_DIR="/root/api-react-dev" readonly FORBIDDEN_DIR="/root/api" readonly EXPECTED_REPOSITORY="PROCOLLAB-github/api" readonly EXPECTED_COMPOSE_PROJECT="api-react" +readonly CELERY_SERVICE="celerys" readonly HEALTH_URL="https://api-react-dev.procollab.ru/programs/?limit=1" readonly LOCK_FILE="${DEPLOY_DIR}/.react-dev-deploy.lock" readonly LOCK_TIMEOUT_SECONDS=10 @@ -193,12 +194,12 @@ rollback_deployment() { --detach \ --no-deps \ --force-recreate \ - web celery || + web "$CELERY_SERVICE" || rollback_failed=1 if ((rollback_failed == 0)); then wait_for_service_running web || rollback_failed=1 - wait_for_service_running celery || rollback_failed=1 + wait_for_service_running "$CELERY_SERVICE" || rollback_failed=1 fi if ((rollback_failed == 0)); then @@ -397,7 +398,7 @@ validate_compose_services() { services_output="$("${COMPOSE_CMD[@]}" config --services)" mapfile -t services <<< "$services_output" - for expected_service in web celery redis; do + for expected_service in web "$CELERY_SERVICE" redis; do found=false for service in "${services[@]}"; do if [[ "$service" == "$expected_service" ]]; then @@ -419,7 +420,7 @@ mapfile -t current_celery_containers < <( docker ps \ --quiet \ --filter "label=com.docker.compose.project=${COMPOSE_PROJECT}" \ - --filter "label=com.docker.compose.service=celery" + --filter "label=com.docker.compose.service=${CELERY_SERVICE}" ) if ((${#current_celery_containers[@]} != 1)); then fail "Не найден ровно один running celery container проекта api-react." @@ -468,8 +469,8 @@ fi validate_compose_services BUILD_ATTEMPTED=true -log "Сборка новых web и celery images без остановки текущих containers." -"${COMPOSE_CMD[@]}" build web celery +log "Сборка новых web и ${CELERY_SERVICE} images без остановки текущих containers." +"${COMPOSE_CMD[@]}" build web "$CELERY_SERVICE" new_web_image_id="$( docker image inspect --format '{{.Id}}' "$PREVIOUS_WEB_IMAGE_REF" @@ -494,15 +495,15 @@ log "Применение миграций React-dev на новом web image." MIGRATION_COMPLETED=true CONTAINERS_MAY_HAVE_CHANGED=true -log "Пересоздание только React-dev web и celery." +log "Пересоздание только React-dev web и ${CELERY_SERVICE}." "${COMPOSE_CMD[@]}" up \ --detach \ --no-deps \ --force-recreate \ - web celery + web "$CELERY_SERVICE" wait_for_service_running web -wait_for_service_running celery +wait_for_service_running "$CELERY_SERVICE" if ! health_check "Deploy"; then fail "Публичный React-dev HTTPS health-check не пройден." fi @@ -510,7 +511,7 @@ fi web_container_id="" celery_container_id="" compose_service_container_id web web_container_id -compose_service_container_id celery celery_container_id +compose_service_container_id "$CELERY_SERVICE" celery_container_id web_status="$(docker inspect --format '{{.State.Status}}' "$web_container_id")" celery_status="$(docker inspect --format '{{.State.Status}}' "$celery_container_id")"