From 7031744a199f0bf8b8e35043afa959977e5ecdbd Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Wed, 3 Jun 2026 10:47:19 +0200 Subject: [PATCH] fix(nsjail): precompile python stdlib + raise download rlimit_as (#9429) Co-authored-by: Claude Opus 4.8 (1M context) --- .github/DockerfileBackendTests | 2 +- .github/workflows/backend-test-windows.yml | 2 +- .github/workflows/backend-test.yml | 2 +- Dockerfile | 11 +++++++---- .../windmill-worker/nsjail/download.py.config.proto | 9 ++++++++- backend/windmill-worker/src/python_versions.rs | 5 +++++ docker/DockerfileSlim | 9 ++++++--- docker/DockerfileSlimEe | 9 ++++++--- 8 files changed, 35 insertions(+), 14 deletions(-) diff --git a/.github/DockerfileBackendTests b/.github/DockerfileBackendTests index 4473c00f0a..88275f204b 100644 --- a/.github/DockerfileBackendTests +++ b/.github/DockerfileBackendTests @@ -28,7 +28,7 @@ ENV PATH="${PATH}:/usr/local/go/bin" ENV GO_PATH=/usr/local/go/bin/go # UV -RUN curl --proto '=https' --tlsv1.2 -LsSf https://github.com/astral-sh/uv/releases/download/0.9.24/uv-installer.sh | sh && mv /usr/local/cargo/bin/uv /usr/local/bin/uv +RUN curl --proto '=https' --tlsv1.2 -LsSf https://github.com/astral-sh/uv/releases/download/0.9.25/uv-installer.sh | sh && mv /usr/local/cargo/bin/uv /usr/local/bin/uv ENV TZ=Etc/UTC diff --git a/.github/workflows/backend-test-windows.yml b/.github/workflows/backend-test-windows.yml index f7c49654d1..1c73e5d429 100644 --- a/.github/workflows/backend-test-windows.yml +++ b/.github/workflows/backend-test-windows.yml @@ -74,7 +74,7 @@ jobs: - uses: astral-sh/setup-uv@v6.2.1 with: - version: "0.9.24" + version: "0.9.25" - uses: shivammathur/setup-php@v2 with: diff --git a/.github/workflows/backend-test.yml b/.github/workflows/backend-test.yml index 9009b47e9d..8f1f15447c 100644 --- a/.github/workflows/backend-test.yml +++ b/.github/workflows/backend-test.yml @@ -62,7 +62,7 @@ jobs: node-version: "20" - uses: astral-sh/setup-uv@v6.2.1 with: - version: "0.9.24" + version: "0.9.25" - uses: shivammathur/setup-php@v2 with: php-version: "8.3" diff --git a/Dockerfile b/Dockerfile index 14aa5363ef..2c55cf36f2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -233,11 +233,14 @@ ENV PATH="${PATH}:/usr/local/go/bin" ENV GO_PATH=/usr/local/go/bin/go # Install UV -RUN curl --proto '=https' --tlsv1.2 -LsSf https://github.com/astral-sh/uv/releases/download/0.9.24/uv-installer.sh | sh && mv /root/.local/bin/uv /usr/local/bin/uv +RUN curl --proto '=https' --tlsv1.2 -LsSf https://github.com/astral-sh/uv/releases/download/0.9.25/uv-installer.sh | sh && mv /root/.local/bin/uv /usr/local/bin/uv # Preinstall python runtimes to temp build location (will copy with world-writable perms later) -RUN UV_CACHE_DIR=/tmp/build_cache/uv UV_PYTHON_INSTALL_DIR=/tmp/build_cache/py_runtime uv python install 3.11 -RUN UV_CACHE_DIR=/tmp/build_cache/uv UV_PYTHON_INSTALL_DIR=/tmp/build_cache/py_runtime uv python install $LATEST_STABLE_PY +# --compile-bytecode precompiles the stdlib to .pyc so jobs don't recompile it on every run +# under the read-only nsjail runtime mount (uv >= 0.9.25). The copy below MUST preserve +# timestamps or Python's mtime-based .pyc invalidation discards these compiled files. +RUN UV_CACHE_DIR=/tmp/build_cache/uv UV_PYTHON_INSTALL_DIR=/tmp/build_cache/py_runtime uv python install 3.11 --compile-bytecode +RUN UV_CACHE_DIR=/tmp/build_cache/uv UV_PYTHON_INSTALL_DIR=/tmp/build_cache/py_runtime uv python install $LATEST_STABLE_PY --compile-bytecode RUN curl -sL https://deb.nodesource.com/setup_20.x | bash - @@ -259,7 +262,7 @@ RUN export GOCACHE=/tmp/build_cache/go && \ # chmod a+rw adds read+write WITHOUT removing execute bits (755->777, 644->666) # Note: uv python install only creates py_runtime, not uv cache - we create uv/go dirs for runtime RUN mkdir -p /tmp/windmill/cache && \ - cp -r /tmp/build_cache/* /tmp/windmill/cache/ && \ + cp -r --preserve=timestamps /tmp/build_cache/* /tmp/windmill/cache/ && \ chmod -R a+rw /tmp/windmill/cache && \ rm -rf /tmp/build_cache && \ mkdir -p -m 777 /tmp/windmill/cache/uv /tmp/windmill/cache/go /tmp/windmill/cache/rustup /tmp/windmill/cache/cargo diff --git a/backend/windmill-worker/nsjail/download.py.config.proto b/backend/windmill-worker/nsjail/download.py.config.proto index 217fe5763a..18957bb4a5 100644 --- a/backend/windmill-worker/nsjail/download.py.config.proto +++ b/backend/windmill-worker/nsjail/download.py.config.proto @@ -5,7 +5,14 @@ hostname: "python" log_level: ERROR time_limit: 900 -rlimit_as: 2048 +# uv's --compile-bytecode spawns a bytecode-compile thread pool sized to the +# host's CPU count. Each thread reserves virtual address space for its stack, so +# on high-core machines the aggregate overruns a low rlimit_as and installs fail +# intermittently with "OS can't spawn worker thread: Resource temporarily +# unavailable (os error 11)" / "memory allocation failed". A low cap (was 2048) +# is the address-space companion to the fd exhaustion fixed below; raised well +# above the run sandbox's 4096 to give the compile pool headroom on large nodes. +rlimit_as: 8192 rlimit_cpu: 1000 rlimit_fsize: 1024 # uv's --compile-bytecode spawns a Python interpreter that compiles .py files diff --git a/backend/windmill-worker/src/python_versions.rs b/backend/windmill-worker/src/python_versions.rs index 5d06d3e673..0ae6b3462a 100644 --- a/backend/windmill-worker/src/python_versions.rs +++ b/backend/windmill-worker/src/python_versions.rs @@ -537,6 +537,11 @@ impl PyV { &v, "--python-preference=only-managed", "--no-bin", + // Compile the runtime's stdlib to bytecode at install time. The + // runtime is mounted read-only into the job nsjail, so without + // precompiled .pyc Python would recompile ~stdlib from source on + // every job (and can never persist it). Requires uv >= 0.9.25. + "--compile-bytecode", ]) // TODO: Do we need these? .envs([ diff --git a/docker/DockerfileSlim b/docker/DockerfileSlim index b192a46c34..91e64d9fb4 100644 --- a/docker/DockerfileSlim +++ b/docker/DockerfileSlim @@ -54,14 +54,17 @@ RUN curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmo ENV TZ=Etc/UTC # Install UV -RUN curl --proto '=https' --tlsv1.2 -LsSf https://github.com/astral-sh/uv/releases/download/0.9.24/uv-installer.sh | sh && mv /root/.local/bin/uv /usr/local/bin/uv +RUN curl --proto '=https' --tlsv1.2 -LsSf https://github.com/astral-sh/uv/releases/download/0.9.25/uv-installer.sh | sh && mv /root/.local/bin/uv /usr/local/bin/uv # Preinstall python runtime to temp location (will copy with world-writable perms later) -RUN UV_PYTHON_INSTALL_DIR=/tmp/build_cache/py_runtime uv python install $LATEST_STABLE_PY +# --compile-bytecode precompiles the stdlib to .pyc so jobs don't recompile it on every run +# under the read-only nsjail runtime mount (uv >= 0.9.25). The copy below MUST preserve +# timestamps or Python's mtime-based .pyc invalidation discards these compiled files. +RUN UV_PYTHON_INSTALL_DIR=/tmp/build_cache/py_runtime uv python install $LATEST_STABLE_PY --compile-bytecode # Copy to final location with world-writable permissions for arbitrary UID support RUN mkdir -p /tmp/windmill/cache && \ - cp -r /tmp/build_cache/* /tmp/windmill/cache/ && \ + cp -r --preserve=timestamps /tmp/build_cache/* /tmp/windmill/cache/ && \ chmod -R a+rw /tmp/windmill/cache && \ rm -rf /tmp/build_cache && \ mkdir -p -m 777 /tmp/windmill/cache/uv diff --git a/docker/DockerfileSlimEe b/docker/DockerfileSlimEe index 24d93586f8..15366bfe06 100644 --- a/docker/DockerfileSlimEe +++ b/docker/DockerfileSlimEe @@ -54,14 +54,17 @@ RUN curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmo ENV TZ=Etc/UTC # Install UV -RUN curl --proto '=https' --tlsv1.2 -LsSf https://github.com/astral-sh/uv/releases/download/0.9.24/uv-installer.sh | sh && mv /root/.local/bin/uv /usr/local/bin/uv +RUN curl --proto '=https' --tlsv1.2 -LsSf https://github.com/astral-sh/uv/releases/download/0.9.25/uv-installer.sh | sh && mv /root/.local/bin/uv /usr/local/bin/uv # Preinstall python runtime to temp location (will copy with world-writable perms later) -RUN UV_PYTHON_INSTALL_DIR=/tmp/build_cache/py_runtime uv python install $LATEST_STABLE_PY +# --compile-bytecode precompiles the stdlib to .pyc so jobs don't recompile it on every run +# under the read-only nsjail runtime mount (uv >= 0.9.25). The copy below MUST preserve +# timestamps or Python's mtime-based .pyc invalidation discards these compiled files. +RUN UV_PYTHON_INSTALL_DIR=/tmp/build_cache/py_runtime uv python install $LATEST_STABLE_PY --compile-bytecode # Copy to final location with world-writable permissions for arbitrary UID support RUN mkdir -p /tmp/windmill/cache && \ - cp -r /tmp/build_cache/* /tmp/windmill/cache/ && \ + cp -r --preserve=timestamps /tmp/build_cache/* /tmp/windmill/cache/ && \ chmod -R a+rw /tmp/windmill/cache && \ rm -rf /tmp/build_cache && \ mkdir -p -m 777 /tmp/windmill/cache/uv