File size: 7,150 Bytes
b6a9d87
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
# syntax=docker/dockerfile:1
# GR00T Docker image (x86_64 + aarch64)
#
# Single Dockerfile for both architectures. flash-attn is installed from
# official cu12torch2.9 cp312 release wheels (x86_64 + aarch64); on aarch64
# (GB200, Grace Hopper) torchcodec is installed from the prebuilt wheel under
# scripts/deployment/dgpu/wheels/ via a pyproject.toml path source. If that
# wheel is missing, the Docker build bootstraps it from source first.
#
# Build:
#   docker build -f docker/Dockerfile -t gr00t .
#
# Run:
#   docker run -it --rm --gpus all --ipc=host gr00t

FROM nvidia/cuda:12.8.0-devel-ubuntu24.04

SHELL ["/bin/bash", "-c"]

ENV DEBIAN_FRONTEND=noninteractive \
    NVIDIA_DRIVER_CAPABILITIES=graphics,utility,compute \
    GR00T_GLOBAL_VENV=/opt/gr00t-venv \
    UV_PROJECT_ENVIRONMENT=/opt/gr00t-venv \
    PYTHON=/usr/bin/python \
    CUDA_HOME=/usr/local/cuda \
    PATH=/usr/local/cuda/bin:${PATH} \
    LD_LIBRARY_PATH=/usr/local/cuda/lib64:${LD_LIBRARY_PATH}

# System dependencies
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
    --mount=type=cache,target=/var/lib/apt,sharing=locked <<INNEREOF
set -euo pipefail
APT_RETRY_OPTS=(
    -o Acquire::Retries=5
    -o Acquire::http::Timeout=30
    -o Acquire::https::Timeout=30
)
# The CI build network can reach NVIDIA's HTTPS apt repo, but repeatedly times
# out on Ubuntu's default HTTP mirror URLs. Use HTTPS for Ubuntu indexes so apt
# does not depend on port 80 egress from the runner.
find /etc/apt -type f \( -name '*.list' -o -name '*.sources' \) -exec sed -i \
    -e 's|http://archive.ubuntu.com/ubuntu|https://archive.ubuntu.com/ubuntu|g' \
    -e 's|http://security.ubuntu.com/ubuntu|https://security.ubuntu.com/ubuntu|g' \
    {} +

apt_update() {
    for attempt in 1 2 3 4 5; do
        if apt-get "${APT_RETRY_OPTS[@]}" update \
            -o APT::Update::Error-Mode=any; then
            return 0
        fi
        rm -rf /var/lib/apt/lists/*
        sleep $((attempt * 10))
    done
    return 1
}

apt_update

apt_install_retry() {
    for attempt in 1 2 3 4 5; do
        if apt-get "${APT_RETRY_OPTS[@]}" install -y --no-install-recommends \
            build-essential \
            ca-certificates \
            git \
            git-lfs \
            curl \
            wget \
            python3.12 \
            python3.12-venv \
            python3.12-dev \
            python3-pip \
            python-is-python3 \
            software-properties-common \
            cmake \
            ninja-build \
            pkg-config \
            pybind11-dev \
            ffmpeg \
            libavdevice-dev \
            libavfilter-dev \
            libavformat-dev \
            libavcodec-dev \
            libavutil-dev \
            libswresample-dev \
            libswscale-dev \
            libegl1; then
            return 0
        fi
        if [ "$attempt" != "5" ]; then
            rm -rf /var/lib/apt/lists/*
            apt_update || true
            sleep $((attempt * 10))
        fi
    done
    return 1
}

apt_install_retry

# The sim-eval setup scripts (LIBERO, SimplerEnv, RoboCasa*) build isolated
# `uv venv --python 3.10` islands whose deps pin an older torch (e.g. 2.5.1),
# so the image must still ship a 3.10 interpreter even though the GR00T root env
# is now 3.12. ubuntu24.04 has no python3.10 in its default repos -> deadsnakes.
add_python310_retry() {
    for attempt in 1 2 3 4 5; do
        if add-apt-repository -y ppa:deadsnakes/ppa \
            && apt_update \
            && apt-get "${APT_RETRY_OPTS[@]}" install -y --no-install-recommends \
                python3.10 \
                python3.10-venv \
                python3.10-dev; then
            return 0
        fi
        if [ "$attempt" != "5" ]; then
            rm -rf /var/lib/apt/lists/*
            apt_update || true
            sleep $((attempt * 10))
        fi
    done
    return 1
}

add_python310_retry

pip_install_retry() {
    for attempt in 1 2 3 4 5; do
        # Ubuntu 24.04's system Python is PEP 668 "externally managed"; this only
        # bootstraps pip/setuptools/wheel before uv builds its own venv, so opting
        # out is safe inside the image. --ignore-installed avoids trying to
        # uninstall debian's pip 24.0 (no RECORD file -> "Cannot uninstall").
        if python -m pip install --upgrade --retries 5 --break-system-packages --ignore-installed pip setuptools wheel; then
            return 0
        fi
        sleep $((attempt * 10))
    done
    return 1
}

pip_install_retry

curl --retry 5 --retry-delay 10 --retry-max-time 300 -LsSf https://astral.sh/uv/0.8.14/install.sh | env UV_INSTALL_DIR=/usr/local/bin sh
INNEREOF

WORKDIR /tmp/gr00t-venv-build

# Install Python dependencies from lockfile
# The wheels/ dir must be present before `uv sync` because pyproject.toml
# references the aarch64 torchcodec wheel there as a path source.
COPY pyproject.toml uv.lock ./
COPY scripts/deployment/dgpu/bootstrap_wheels.sh ./scripts/deployment/dgpu/bootstrap_wheels.sh
COPY scripts/deployment/dgpu/wheels/ ./scripts/deployment/dgpu/wheels/

RUN <<INNEREOF
set -euo pipefail

uv_lock_retry() {
    for attempt in 1 2 3 4 5; do
        if UV_PREVIEW=1 UV_HTTP_TIMEOUT=300 UV_CONCURRENT_DOWNLOADS=4 uv lock; then
            return 0
        fi
        sleep $((attempt * 10))
    done
    return 1
}

uv_sync_retry() {
    for attempt in 1 2 3 4 5; do
        if UV_PREVIEW=1 UV_HTTP_TIMEOUT=300 UV_CONCURRENT_DOWNLOADS=4 uv sync --frozen --no-install-project --extra dev --no-cache; then
            return 0
        fi
        sleep $((attempt * 10))
    done
    return 1
}

if [ "$(uname -m)" = "aarch64" ]; then
    bash scripts/deployment/dgpu/bootstrap_wheels.sh
    uv_lock_retry
fi

uv_sync_retry
sha256sum uv.lock | awk '{print $1}' > "${GR00T_GLOBAL_VENV}/.gr00t-uv-lock.sha256"
mkdir -p /opt/gr00t-image-metadata
cp pyproject.toml uv.lock /opt/gr00t-image-metadata/
cd /
rm -rf /tmp/gr00t-venv-build
INNEREOF

WORKDIR /
RUN mkdir -p /workspace && ln -sfn "${GR00T_GLOBAL_VENV}" /workspace/.venv
WORKDIR /workspace

ENV PATH="${GR00T_GLOBAL_VENV}/bin:${PATH}" \
    VIRTUAL_ENV="${GR00T_GLOBAL_VENV}"

# EGL/Vulkan setup for headless rendering (MuJoCo, PyOpenGL)
RUN mkdir -p /usr/share/glvnd/egl_vendor.d && \
    cat >/usr/share/glvnd/egl_vendor.d/10_nvidia.json <<'JSONEOF'
{
    "file_format_version" : "1.0.0",
    "ICD" : {
        "library_path" : "libEGL_nvidia.so.0"
    }
}
JSONEOF

RUN mkdir -p /usr/share/vulkan/icd.d && \
    cat >/usr/share/vulkan/icd.d/nvidia_icd.json <<'JSONEOF'
{
    "file_format_version": "1.0.0",
    "ICD": {
        "library_path": "libGLX_nvidia.so.0",
        "api_version": "1.2.140"
    }
}
JSONEOF

ENV MUJOCO_GL="egl" \
    PYOPENGL_PLATFORM="egl" \
    __EGL_VENDOR_LIBRARY_FILENAMES="/usr/share/glvnd/egl_vendor.d/10_nvidia.json"

# aarch64 torchcodec is installed from the repo-local wheel by `uv sync` above
# via a pyproject.toml path source; the wheel is built from source by
# bootstrap_wheels.sh before sync (then committed back) if missing. flash-attn
# comes from official release URLs — no source build needed.

CMD ["/bin/bash"]