При создании файла образа нужно учитывать среду выполнения и требования для успешного деплоя образа в Container Apps.
Чтобы деплой завершился успешно, образ должен удовлетворять обязательным требованиям. Также, желательно, чтобы образ соответствовал и рекомендованным требованиям. Если рекомендованные требования не будут соблюдены, приложение будет развернуто, но его работа может быть нестабильна.
Язык программирования — любой.
Доступный формат образа — Docker Image Manifest V 2.
Docker-образ должен имплементировать любой тип веб-сервера и определять номер порта, на котором контейнер будет принимать запросы.
Docker-образ должен быть собран под плафторму linux/amd64.
Пока не поддерживается запуск Docker-oбразов, собранных под другие платформы. При использовании Apple Mac с процессором серии М образ по умолчанию собирается под платформу arm64.
При работе на Mac добавляйте в команду скачивания и сборки образа параметр --platform linux/amd64.
docker buildx build \--platform linux/amd64,linux/arm64 \--tag <registry_name>.cr.cloud.ru/<repository>:<tag> \--push .
где:
<registry_name> — название реестра;
<repository> — название репозитория;
<tag> — название тега.
В Docker-образе не должны использоваться настройки для подключения томов (volumes) к контейнеру.
Образ на основе Dockerfile, который содержит инструкцию VOLUME, не может использоваться для развертывания контейнера.
Образ должен быть загружен в Artifact Registry в том же проекте, что и Container Apps.
Если вы планируете делать образ доступным другим пользователям, он должен быть загружен в публичный репозиторий.
Если образ создается для использования в Container Services, то приложение должно слушать 0.0.0.0:$PORT. При этом переменная окружения PORT не может быть переопределена.
import osimport uvicornif __name__ == "__main__":port = int(os.getenv("PORT", "8080"))uvicorn.run("app:app", host="0.0.0.0", port=port)
Если образ создается для использования в Container Jobs, процесс должен завершаться с exit code 0 при успехе.
Нужно учитывать, что задание, которое развернется из образа в Container Jobs, укладывается в таймаут 3 600 секунд и учитывает лимит — не более 5 параллельных запусков.
Если контейнер запускается от имени root-пользователя, то при создании контейнера в Container Apps необходимо включить привилегированный режим, иначе контейнер не запустится.
Если контейнер запускается не от имени root-пользователя, то в Dockerfile укажите команду создания пользователя с идентификатором 1000 и назначьте ему права на пользовательскую директорию:
RUN addgroup -g 1000 appuser \&& adduser -u 1000 -G appuser -s /bin/sh -D appuserRUN chown -R 1000 /mydirectory
где /mydirectory — название директории в вашем приложении.
По умолчанию контейнеры в Container Apps запускаются от имени пользователя с идентификатором (UID) 1000, если не включен привилегированный режим.
Docker-образ не должен находиться в карантине из-за наличия уязвимостей в реестре Artifact Registry, иначе его будет невозможно использовать для развертывания контейнера.
В образе есть HTTP-эндпоинт для health-проверок (/health, /ready, /ping), который помогает отслеживать состояния приложения в контейнере. При успешной health-пробе возвращается ответ «200 OK».
В вашем образе есть обработка сигнала SIGTERM. Это поможет корректно завершить все задачи перед завершением задания или остановкой контейнера.
import signal, sysdef handle_sigterm(signum, frame):# дождаться завершения активных запросов, закрыть пулы соединений с БДgraceful_stop()sys.exit(0)signal.signal(signal.SIGTERM, handle_sigterm)
Используйте быстрый «холодный» старт (желательно — 1–3 секунды), чтобы минимизировать задержку при первом обращении.
При настройке логирования, записывайте их в стандартные потоки вывода — stdout, stderr, желательно в формате JSON и без буферизации. Это упростит парсинг и поиск в сервисе логирования, а также не будет задержки в появлении логов.
Не храните данные внутри контейнеров, используйте внешние хранилища.
Используйте минималистичный базовый образ без лишних утилит для более быстрого «холодного» старта.
Используйте явные, иммутабельные теги для разных версий образов, чтобы точно понимать в чем отличия.
Если вы планируете загружать образ на витрину «Маркетплейса», то он должен удовлетворять дополнительным требованиям:
В образе пропишите OCI-метки: title, description, vendor, version, source, licenses.
LABEL org.opencontainers.image.title="My Awesome App"LABEL org.opencontainers.image.description="Описание приложения для витрины маркетплейса"LABEL org.opencontainers.image.vendor="Имя разработчика / компании"LABEL org.opencontainers.image.version="1.0.0"LABEL org.opencontainers.image.licenses="Apache-2.0"LABEL org.opencontainers.image.url="https://docs.myapp.com"LABEL org.opencontainers.image.source="https://gitverse.ru/user/repo"LABEL org.opencontainers.image.revision="<git-sha>"LABEL org.opencontainers.image.created="2026-07-28T15:00:00Z"
Подготовьте манифест приложения и опишите в нем:
Переменные окружения — укажите всю конфигурацию приложения.
Минимально необходимые ресурсы — CPU и RAM.
Требуется ли подключения к другим сервисам — например «нужен S3-бакет» или «нужна БД PostgreSQL».
Тип рабочей нагрузки — Container Services или Container Jobs
Пример Docker-файла, который удовлетворяет всем обязательным требованиям:
dockerfile# ---------- build stage ----------FROM --platform=linux/amd64 python:3.12-slim AS builderWORKDIR /buildCOPY requirements.txt .RUN pip install --no-cache-dir --target=/build/deps -r requirements.txt# ---------- runtime stage ----------FROM --platform=linux/amd64 python:3.12-slim# метаданные для витрины маркетплейсаLABEL org.opencontainers.image.title="My Awesome App" \org.opencontainers.image.description="ETL-задание для синхронизации данных" \org.opencontainers.image.vendor="ACME Ltd." \org.opencontainers.image.version="1.0.0" \org.opencontainers.image.licenses="Apache-2.0" \org.opencontainers.image.source="https://gitverse.ru/acme/my-awesome-app"# логи без буферизации, сразу в stdoutENV PYTHONUNBUFFERED=1 \PYTHONDONTWRITEBYTECODE=1 \PYTHONPATH=/app/deps# пользователь с UID 1000 — требование среды выполненияRUN groupadd -g 1000 appuser \&& useradd -u 1000 -g appuser -m -s /bin/sh appuserWORKDIR /appCOPY --from=builder /build/deps /app/depsCOPY --chown=1000:1000 ./src /appRUN chown -R 1000:1000 /appUSER appuser# декларативно; фактический порт платформа передает в $PORTEXPOSE 8080# exec-форма, чтобы SIGTERM дошел до процессаCMD ["python", "main.py"]