refactor(docker): rebase benchmark images on nightly lerobot-gpu

Use huggingface/lerobot-gpu:latest as base for both libero and metaworld
benchmark Dockerfiles instead of building from nvidia/cuda scratch. The
nightly image already has all extras installed via uv sync --extra all,
so we only need to overlay the PR source code (and libero asset setup).

This eliminates duplicated system dep installation, Python setup, uv
venv creation, and the Triton ptxas workaround from both files.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Pepijn
2026-04-13 18:31:28 +02:00
parent f31e85d5dc
commit ae8ca0719e
2 changed files with 12 additions and 124 deletions
+7 -64
View File
@@ -12,88 +12,31 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
# Isolated benchmark image for LIBERO integration tests. # Benchmark image for LIBERO integration tests.
# Installs only lerobot[libero] so its dep tree (hf-libero, dm-control, mujoco) # Extends the nightly GPU image (which already has all extras installed)
# cannot conflict with other benchmarks. # with the PR's source code and LIBERO-specific asset setup.
# #
# Build: docker build -f docker/Dockerfile.benchmark.libero -t lerobot-benchmark-libero . # Build: docker build -f docker/Dockerfile.benchmark.libero -t lerobot-benchmark-libero .
# Run: docker run --gpus all --rm lerobot-benchmark-libero lerobot-eval ... # Run: docker run --gpus all --rm lerobot-benchmark-libero lerobot-eval ...
ARG CUDA_VERSION=12.4.1 FROM huggingface/lerobot-gpu:latest
ARG OS_VERSION=22.04
FROM nvidia/cuda:${CUDA_VERSION}-base-ubuntu${OS_VERSION}
ARG PYTHON_VERSION=3.12
ENV DEBIAN_FRONTEND=noninteractive \
MUJOCO_GL=egl \
PATH=/lerobot/.venv/bin:$PATH \
CUDA_VISIBLE_DEVICES=0 \
DEVICE=cuda
# System deps — same set as Dockerfile.internal
RUN apt-get update && apt-get install -y --no-install-recommends \
software-properties-common build-essential git curl \
libglib2.0-0 libgl1-mesa-glx libegl1-mesa ffmpeg \
libusb-1.0-0-dev speech-dispatcher libgeos-dev portaudio19-dev \
cmake pkg-config ninja-build \
&& add-apt-repository -y ppa:deadsnakes/ppa \
&& apt-get update \
&& apt-get install -y --no-install-recommends \
python${PYTHON_VERSION} \
python${PYTHON_VERSION}-venv \
python${PYTHON_VERSION}-dev \
&& curl -LsSf https://astral.sh/uv/0.8.0/install.sh | sh \
&& mv /root/.local/bin/uv /usr/local/bin/uv \
&& useradd --create-home --shell /bin/bash user_lerobot \
&& usermod -aG sudo user_lerobot \
&& apt-get clean && rm -rf /var/lib/apt/lists/*
WORKDIR /lerobot
RUN chown -R user_lerobot:user_lerobot /lerobot
USER user_lerobot
ENV HOME=/home/user_lerobot \
HF_HOME=/home/user_lerobot/.cache/huggingface \
HF_LEROBOT_HOME=/home/user_lerobot/.cache/huggingface/lerobot \
TORCH_HOME=/home/user_lerobot/.cache/torch \
TRITON_CACHE_DIR=/home/user_lerobot/.cache/triton
RUN uv venv --python python${PYTHON_VERSION}
# ── Dependency layer (cached unless pyproject.toml / uv.lock change) ────────
# Copy only the files uv needs to resolve deps, plus a minimal package stub
# so the editable install can succeed without the full source tree.
# Uses `uv pip install` instead of `uv sync` because uv sync validates the
# entire lockfile across all extras — robomme's numpy<2.0 conflicts with the
# base numpy>=2.0, making the full lockfile unsatisfiable. pip-style install
# only resolves the requested extras for the current platform.
COPY --chown=user_lerobot:user_lerobot setup.py pyproject.toml uv.lock README.md MANIFEST.in ./
RUN mkdir -p src/lerobot && touch src/lerobot/__init__.py src/lerobot/py.typed
RUN uv pip install --no-cache -e ".[libero,smolvla]"
# Pre-download lerobot/libero-assets from HF Hub so nothing is fetched at # Pre-download lerobot/libero-assets from HF Hub so nothing is fetched at
# runtime (which times out on CI). Point the libero config at the cached path. # runtime (which times out on CI). Point the libero config at the cached path.
# libero/libero/__init__.py calls input() when ~/.libero/config.yaml is missing, # libero/libero/__init__.py calls input() when ~/.libero/config.yaml is missing,
# so we write the config before any libero import can happen. # so we write the config before any libero import can happen.
RUN LIBERO_DIR=$(python${PYTHON_VERSION} -c \ RUN LIBERO_DIR=$(python -c \
"import importlib.util, os; s=importlib.util.find_spec('libero'); \ "import importlib.util, os; s=importlib.util.find_spec('libero'); \
print(os.path.join(os.path.dirname(s.origin), 'libero'))") && \ print(os.path.join(os.path.dirname(s.origin), 'libero'))") && \
mkdir -p /home/user_lerobot/.libero && \ mkdir -p /home/user_lerobot/.libero && \
python${PYTHON_VERSION} -c "\ python -c "\
from huggingface_hub import snapshot_download; \ from huggingface_hub import snapshot_download; \
snapshot_download(repo_id='lerobot/libero-assets', repo_type='dataset', \ snapshot_download(repo_id='lerobot/libero-assets', repo_type='dataset', \
local_dir='/home/user_lerobot/.libero/assets')" && \ local_dir='/home/user_lerobot/.libero/assets')" && \
printf "assets: /home/user_lerobot/.libero/assets\nbddl_files: ${LIBERO_DIR}/bddl_files\ndatasets: ${LIBERO_DIR}/../datasets\ninit_states: ${LIBERO_DIR}/init_files\n" \ printf "assets: /home/user_lerobot/.libero/assets\nbddl_files: ${LIBERO_DIR}/bddl_files\ndatasets: ${LIBERO_DIR}/../datasets\ninit_states: ${LIBERO_DIR}/init_files\n" \
> /home/user_lerobot/.libero/config.yaml > /home/user_lerobot/.libero/config.yaml
# Workaround: Triton ships ptxas without the execute bit set. # Overlay the PR's source code on top of the nightly image.
# Without this chmod, any JIT compilation (e.g. torch.compile) fails
# with "Permission denied".
RUN chmod +x /lerobot/.venv/lib/python${PYTHON_VERSION}/site-packages/triton/backends/nvidia/bin/ptxas
# ── Source layer (rebuilds in seconds on code-only changes) ─────────────────
COPY --chown=user_lerobot:user_lerobot . . COPY --chown=user_lerobot:user_lerobot . .
CMD ["/bin/bash"] CMD ["/bin/bash"]
+5 -60
View File
@@ -12,71 +12,16 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
# Isolated benchmark image for MetaWorld integration tests. # Benchmark image for MetaWorld integration tests.
# Installs only lerobot[metaworld] so its dep tree (metaworld==3.0.0, mujoco>=3) # Extends the nightly GPU image (which already has all extras installed)
# cannot conflict with other benchmarks. # with the PR's source code.
# #
# Build: docker build -f docker/Dockerfile.benchmark.metaworld -t lerobot-benchmark-metaworld . # Build: docker build -f docker/Dockerfile.benchmark.metaworld -t lerobot-benchmark-metaworld .
# Run: docker run --gpus all --rm lerobot-benchmark-metaworld lerobot-eval ... # Run: docker run --gpus all --rm lerobot-benchmark-metaworld lerobot-eval ...
ARG CUDA_VERSION=12.4.1 FROM huggingface/lerobot-gpu:latest
ARG OS_VERSION=22.04
FROM nvidia/cuda:${CUDA_VERSION}-base-ubuntu${OS_VERSION}
ARG PYTHON_VERSION=3.12 # Overlay the PR's source code on top of the nightly image.
ENV DEBIAN_FRONTEND=noninteractive \
MUJOCO_GL=egl \
PATH=/lerobot/.venv/bin:$PATH \
CUDA_VISIBLE_DEVICES=0 \
DEVICE=cuda
# System deps — same set as Dockerfile.internal
RUN apt-get update && apt-get install -y --no-install-recommends \
software-properties-common build-essential git curl \
libglib2.0-0 libgl1-mesa-glx libegl1-mesa ffmpeg \
libusb-1.0-0-dev speech-dispatcher libgeos-dev portaudio19-dev \
cmake pkg-config ninja-build \
&& add-apt-repository -y ppa:deadsnakes/ppa \
&& apt-get update \
&& apt-get install -y --no-install-recommends \
python${PYTHON_VERSION} \
python${PYTHON_VERSION}-venv \
python${PYTHON_VERSION}-dev \
&& curl -LsSf https://astral.sh/uv/0.8.0/install.sh | sh \
&& mv /root/.local/bin/uv /usr/local/bin/uv \
&& useradd --create-home --shell /bin/bash user_lerobot \
&& usermod -aG sudo user_lerobot \
&& apt-get clean && rm -rf /var/lib/apt/lists/*
WORKDIR /lerobot
RUN chown -R user_lerobot:user_lerobot /lerobot
USER user_lerobot
ENV HOME=/home/user_lerobot \
HF_HOME=/home/user_lerobot/.cache/huggingface \
HF_LEROBOT_HOME=/home/user_lerobot/.cache/huggingface/lerobot \
TORCH_HOME=/home/user_lerobot/.cache/torch \
TRITON_CACHE_DIR=/home/user_lerobot/.cache/triton
RUN uv venv --python python${PYTHON_VERSION}
# ── Dependency layer (cached unless pyproject.toml / uv.lock change) ────────
# Copy only the files uv needs to resolve deps, plus a minimal package stub
# so the editable install can succeed without the full source tree.
# Uses `uv pip install` instead of `uv sync` — see Dockerfile.benchmark.libero
# for rationale (cross-extra numpy conflict with robomme).
COPY --chown=user_lerobot:user_lerobot setup.py pyproject.toml uv.lock README.md MANIFEST.in ./
RUN mkdir -p src/lerobot && touch src/lerobot/__init__.py src/lerobot/py.typed
RUN uv pip install --no-cache -e ".[metaworld,smolvla]"
# Workaround: Triton ships ptxas without the execute bit set.
# Without this chmod, any JIT compilation (e.g. torch.compile) fails
# with "Permission denied". See: https://github.com/triton-lang/triton/issues/2due
RUN chmod +x /lerobot/.venv/lib/python${PYTHON_VERSION}/site-packages/triton/backends/nvidia/bin/ptxas
# ── Source layer (rebuilds in seconds on code-only changes) ─────────────────
COPY --chown=user_lerobot:user_lerobot . . COPY --chown=user_lerobot:user_lerobot . .
CMD ["/bin/bash"] CMD ["/bin/bash"]