From 18dab6d48d9867ba9f967423d8684f78f8ee080b Mon Sep 17 00:00:00 2001 From: qananasikq Date: Fri, 10 Apr 2026 15:14:04 +0300 Subject: [PATCH] docker non-root and healthcheck fix --- Dockerfile | 8 ++++++++ docker-compose.yml | 17 +++++++++++------ entrypoint.sh | 24 ++++++++++++++++++++++-- 3 files changed, 41 insertions(+), 8 deletions(-) diff --git a/Dockerfile b/Dockerfile index 90c1a7e..234ae74 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,12 +3,17 @@ FROM mcr.microsoft.com/playwright/python:v1.58.0-noble ENV PYTHONDONTWRITEBYTECODE=1 \ PYTHONUNBUFFERED=1 +RUN groupadd --system app && useradd --system --gid app --create-home app + WORKDIR /app # Xvfb for headed Chromium in container (IAAI blocks headless) RUN apt-get update -qq && apt-get install -y --no-install-recommends xvfb \ && rm -rf /var/lib/apt/lists/* +# Required for non-root Xvfb runtime in containers +RUN mkdir -p /tmp/.X11-unix && chmod 1777 /tmp/.X11-unix + COPY pyproject.toml uv.lock ./ RUN pip install --no-cache-dir uv \ && uv export --format requirements-txt --no-dev --no-hashes --no-emit-project --frozen -o /tmp/requirements.txt \ @@ -17,9 +22,12 @@ RUN pip install --no-cache-dir uv \ COPY . . RUN python -m compileall -q iaai_scraper RUN chmod +x entrypoint.sh +RUN chown -R app:app /app STOPSIGNAL SIGINT +USER app + # По умолчанию API, но worker/beat переопределяют CMD в docker-compose ENTRYPOINT ["./entrypoint.sh"] CMD ["uvicorn", "iaai_scraper.api.app:app", "--host", "0.0.0.0", "--port", "8000"] diff --git a/docker-compose.yml b/docker-compose.yml index a62605b..6a6b04c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -121,6 +121,7 @@ services: <<: *worker-service container_name: iaai-worker restart: unless-stopped + init: true depends_on: migrate: condition: service_completed_successfully @@ -130,13 +131,14 @@ services: command: > celery -A iaai_scraper.worker.celery_app worker --loglevel=info --concurrency=1 --pool=prefork + --pidfile=/tmp/celery-worker.pid -Q scraping --max-tasks-per-child=20 healthcheck: - test: ["CMD", "celery", "-A", "iaai_scraper.worker.celery_app", "inspect", "ping", "-d", "celery@$$HOSTNAME"] + test: ["CMD-SHELL", "test -f /tmp/celery-worker.pid && kill -0 $(cat /tmp/celery-worker.pid)"] interval: 60s - timeout: 20s + timeout: 10s retries: 3 - start_period: 40s + start_period: 90s logging: driver: json-file options: @@ -148,6 +150,7 @@ services: <<: *worker-service container_name: iaai-beat restart: unless-stopped + init: true depends_on: migrate: condition: service_completed_successfully @@ -156,12 +159,14 @@ services: command: > celery -A iaai_scraper.worker.celery_app beat --loglevel=info + --pidfile=/tmp/celery-beat.pid + --schedule=/tmp/celerybeat-schedule healthcheck: - test: ["CMD", "python", "-c", "import pathlib,sys; p=pathlib.Path('/tmp/celerybeat-schedule'); sys.exit(0 if p.exists() else 1)"] + test: ["CMD-SHELL", "test -f /tmp/celery-beat.pid && kill -0 $(cat /tmp/celery-beat.pid)"] interval: 60s - timeout: 20s + timeout: 10s retries: 3 - start_period: 60s + start_period: 90s logging: driver: json-file options: diff --git a/entrypoint.sh b/entrypoint.sh index 92e1676..d438c4f 100644 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -63,15 +63,35 @@ start_xvfb_if_needed() { # IAAI blocks headless Chromium on Linux; run headed via Xvfb virtual display export DISPLAY=:99 export IAAI_HEADLESS=false + local xvfb_pid_file="/tmp/xvfb-99.pid" + + if [ -f "${xvfb_pid_file}" ]; then + local existing_pid + existing_pid="$(cat "${xvfb_pid_file}")" + if [ -n "${existing_pid}" ] && kill -0 "${existing_pid}" 2>/dev/null; then + echo "[entrypoint] Reusing existing Xvfb on DISPLAY=${DISPLAY} (PID ${existing_pid})" + return 0 + fi + echo "[entrypoint] Removing stale Xvfb pid file" + rm -f "${xvfb_pid_file}" + fi if [ -S /tmp/.X11-unix/X99 ] || [ -f /tmp/.X99-lock ]; then - echo "[entrypoint] Reusing existing Xvfb on DISPLAY=${DISPLAY}" - return 0 + echo "[entrypoint] Removing stale Xvfb socket/lock files for DISPLAY=${DISPLAY}" + rm -f /tmp/.X11-unix/X99 /tmp/.X99-lock fi Xvfb :99 -screen 0 1920x1080x24 -nolisten tcp & XVFB_PID=$! + echo "${XVFB_PID}" > "${xvfb_pid_file}" sleep 0.5 + + if ! kill -0 "${XVFB_PID}" 2>/dev/null; then + echo "[entrypoint] ERROR: Xvfb failed to start on DISPLAY=${DISPLAY}" + rm -f "${xvfb_pid_file}" + exit 1 + fi + echo "[entrypoint] Xvfb started (PID ${XVFB_PID}, DISPLAY=${DISPLAY})" }