mirror of
https://github.com/radixark/miles.git
synced 2026-11-04 08:25:29 +08:00
295 lines
15 KiB
Docker
295 lines
15 KiB
Docker
# doc-dev: docs/ci/02-docker-build.md
|
|
# Build via docker/build.py (single source of truth for variant defaults,
|
|
# platforms, tags, and build-arg ordering); callers may append explicit
|
|
# build-arg overrides. See docs/ci/02-docker-build.md. Examples:
|
|
# python docker/build.py --variant cu13 --image-tag dev --push # radixark/miles:dev (amd64+arm64)
|
|
# python docker/build.py --variant cu13-x86 --image-tag dev --push # radixark/miles:dev (amd64)
|
|
# python docker/build.py --variant cu13-aarch64 --image-tag dev --push # radixark/miles:dev (arm64)
|
|
# python docker/build.py --variant cu12-x86 --image-tag dev --push # radixark/miles:dev-cu12 (CUDA 12.9)
|
|
#
|
|
# A multi-arch build runs the recipe once per platform; it installs the wheels
|
|
# release for the current arch picked from TARGETARCH — WHEELS_TAG_X86 on amd64,
|
|
# WHEELS_TAG_ARM64 on arm64 (cu12-x86 overrides WHEELS_TAG_X86).
|
|
|
|
ARG SGLANG_IMAGE_TAG=v0.5.18
|
|
FROM lmsysorg/sglang:${SGLANG_IMAGE_TAG} AS sglang
|
|
|
|
# ======================================== Arguments =============================================
|
|
|
|
ARG SGLANG_BRANCH=sglang-miles
|
|
ARG SGLANG_COMMIT=""
|
|
|
|
ARG MEGATRON_REPO=radixark/Megatron-LM
|
|
ARG MEGATRON_BRANCH=miles-main
|
|
# Empty means the branch HEAD at build time; release builds set it to freeze one commit.
|
|
ARG MEGATRON_COMMIT=""
|
|
|
|
ARG ENABLE_CUDA_13=1
|
|
|
|
ARG WHEELS_REPO=yueming-yuan/miles-wheels
|
|
# Two complete wheels release tags (the wheels repo's own names). The build picks
|
|
# one by TARGETARCH and installs it verbatim — no on-the-fly tag assembly here.
|
|
ARG TARGETARCH
|
|
ARG WHEELS_TAG_X86=cu130-torch213-x86_64
|
|
ARG WHEELS_TAG_ARM64=cu130-torch213-aarch64
|
|
|
|
# ======================================== Setup =============================================
|
|
|
|
WORKDIR /root/
|
|
|
|
# ======================================== Apt dependencies =============================================
|
|
|
|
RUN apt update
|
|
# ethtool for network diagnostics
|
|
RUN apt install -y nvtop rsync dnsutils ethtool tmux
|
|
|
|
# nccl-tests for diagnostics
|
|
RUN git clone https://github.com/NVIDIA/nccl-tests.git /tmp/nccl-tests && \
|
|
cd /tmp/nccl-tests && \
|
|
git checkout ae98985f5599617be94042f4aa3637d10014ce89 && \
|
|
make -j$(nproc) CUDA_HOME=/usr/local/cuda && \
|
|
cp /tmp/nccl-tests/build/*_perf /usr/local/bin/ && \
|
|
rm -rf /tmp/nccl-tests
|
|
|
|
# ====================================== Collect pre-built wheels ============================================
|
|
# Optional: drop pre-built wheels into <miles>/wheels/ to skip the release download below.
|
|
# `wheel[s]/` is a glob — silently no-ops when wheels/ is absent (CI's path).
|
|
COPY wheel[s]/ /tmp/wheels/
|
|
|
|
RUN case "${TARGETARCH}" in \
|
|
amd64) WHEELS_TAG="${WHEELS_TAG_X86}" ;; \
|
|
arm64) WHEELS_TAG="${WHEELS_TAG_ARM64}" ;; \
|
|
*) echo "unsupported TARGETARCH: ${TARGETARCH}" >&2; exit 1 ;; \
|
|
esac && \
|
|
echo "Fetching wheels release ${WHEELS_TAG}" && \
|
|
mkdir -p /tmp/wheels && \
|
|
curl -sL "https://api.github.com/repos/${WHEELS_REPO}/releases/tags/${WHEELS_TAG}" \
|
|
| python3 -c "import sys,json,subprocess,os; w='/tmp/wheels'; \
|
|
[subprocess.run(['curl','-fSL','-o',os.path.join(w,a['name']),a['browser_download_url']],check=True) \
|
|
for a in json.load(sys.stdin).get('assets',[]) \
|
|
if a['name'].endswith(('.whl', '.tar.gz')) and not os.path.exists(os.path.join(w,a['name']))]" && \
|
|
ls -lh /tmp/wheels/
|
|
|
|
# ====================================== Python dependencies ============================================
|
|
|
|
# flash-attn
|
|
RUN pip install /tmp/wheels/flash_attn-*.whl
|
|
|
|
# flash-attn hopper (FA3): Hopper-only (sm_90a). Coexists with FA2.
|
|
# The wheel ships flash_attn_interface top-level (what transformer_engine imports)
|
|
# plus a flash_attn_3.flash_attn_interface re-export shim, so there is nothing to
|
|
# fetch separately. Do not overwrite the shim with a copy of the full module: it
|
|
# re-runs @torch.library.custom_op("flash_attn_3::...") and double-registers.
|
|
RUN pip install /tmp/wheels/flash_attn_3-*.whl
|
|
|
|
RUN pip install git+https://github.com/ISEEKYAN/mbridge.git@89eb10887887bc74853f89a4de258c0702932a1c --no-deps
|
|
|
|
RUN pip install flash-linear-attention==0.5.2
|
|
# required for DeepSeek V4
|
|
RUN pip install tilelang==0.1.8 -f https://tile-ai.github.io/whl/nightly/cu128/
|
|
RUN pip install --no-deps tile_kernels==1.0.0
|
|
# FlashQLA backend for Qwen GDN linear-attention layers (requires SM90+, CUDA 12.8+, PyTorch 2.8+; built on tilelang above)
|
|
RUN pip install -v --no-build-isolation "git+https://github.com/QwenLM/FlashQLA.git"
|
|
# Prefer the prebuilt wheel; fall back to a source build while the current
|
|
# arch's wheels release doesn't carry it yet (QEMU-emulated arm64 source
|
|
# builds are the expensive path this avoids).
|
|
RUN if ls /tmp/wheels/fast_hadamard_transform-*.whl 2>/dev/null | grep -q .; then \
|
|
pip install /tmp/wheels/fast_hadamard_transform-*.whl; \
|
|
else \
|
|
pip install "git+https://github.com/Dao-AILab/fast-hadamard-transform.git@e7706faf8d1c3b9f241e36860640ad1dac644ede" --no-build-isolation; \
|
|
fi
|
|
|
|
# Mamba kernels for nemotron_h hybrid (mamba+attention) models.
|
|
RUN if ls /tmp/wheels/causal_conv1d-*.whl 2>/dev/null | grep -q . && \
|
|
ls /tmp/wheels/mamba_ssm-*.whl 2>/dev/null | grep -q .; then \
|
|
pip install /tmp/wheels/causal_conv1d-*.whl /tmp/wheels/mamba_ssm-*.whl; \
|
|
else \
|
|
pip install causal-conv1d==1.6.1 mamba-ssm==2.3.1 --no-build-isolation; \
|
|
fi
|
|
|
|
# transformer_engine
|
|
# --no-deps pins the three TE dists to exactly the wheels above, so pip cannot
|
|
# pull a conflicting core dist. That also drops transformer_engine_torch's own
|
|
# runtime deps, which still have to be installed: transformer_engine.pytorch
|
|
# imports onnxscript unconditionally (module/_common -> export -> onnx_extensions).
|
|
RUN --mount=type=bind,source=docker/verify_transformer_engine.py,target=/tmp/verify_transformer_engine.py \
|
|
if [ "${ENABLE_CUDA_13}" = "1" ]; then \
|
|
TE_CORE_DIST=transformer_engine_cu13; \
|
|
else \
|
|
TE_CORE_DIST=transformer_engine_cu12; \
|
|
fi && \
|
|
set -- \
|
|
/tmp/wheels/transformer_engine-2.17.0-py3-none-any.whl \
|
|
/tmp/wheels/${TE_CORE_DIST}-2.17.0-py3-none-manylinux_2_28_*.whl \
|
|
/tmp/wheels/transformer_engine_torch-2.17.0-cp312-cp312-linux_*.whl; \
|
|
if [ "$#" -ne 3 ] || [ ! -f "$1" ] || [ ! -f "$2" ] || [ ! -f "$3" ]; then \
|
|
echo "expected exactly one TransformerEngine 2.17 wheel for each component (${TE_CORE_DIST})" >&2; exit 1; \
|
|
fi && \
|
|
pip uninstall -y transformer-engine transformer-engine-cu12 transformer-engine-cu13 transformer-engine-torch && \
|
|
pip install --force-reinstall --no-deps "$@" && \
|
|
pip install einops onnx onnxscript pydantic nvdlfw-inspect && \
|
|
python3 /tmp/verify_transformer_engine.py "${TE_CORE_DIST}"
|
|
|
|
# TE patches (cu13): B300/GB300 FA2 and backward override fixes
|
|
# te_dequantized_backward_override.patch is a hot fix from
|
|
# https://github.com/NVIDIA/TransformerEngine/pull/3141; drop it after TE v2.18.
|
|
COPY docker/patch/ /tmp/patches/
|
|
# A `for` loop exits with the status of its LAST iteration, so a patch that fails
|
|
# to apply here used to be swallowed whenever a later one succeeded -- the image
|
|
# shipped with TE silently unpatched. Fail the build instead.
|
|
RUN if [ "${ENABLE_CUDA_13}" = "1" ] && [ -d /tmp/patches/cu13 ]; then \
|
|
TE_DIR=$(python -c 'import importlib.util; print(importlib.util.find_spec("transformer_engine").submodule_search_locations[0])') && \
|
|
for p in /tmp/patches/cu13/*.patch; do \
|
|
echo "Applying $(basename $p) to $TE_DIR"; \
|
|
patch -d "$TE_DIR" -p1 < "$p" || { \
|
|
echo "TE patch $(basename $p) did not apply cleanly against the installed transformer_engine" >&2; \
|
|
exit 1; \
|
|
}; \
|
|
done; \
|
|
fi && rm -rf /tmp/patches
|
|
|
|
# apex
|
|
RUN pip install /tmp/wheels/apex-*.whl
|
|
|
|
RUN git clone https://github.com/${MEGATRON_REPO}.git --recursive -b ${MEGATRON_BRANCH} Megatron-LM && \
|
|
cd Megatron-LM && \
|
|
if [ -n "${MEGATRON_COMMIT}" ]; then git checkout -f ${MEGATRON_COMMIT} && git submodule update --init --recursive; fi && \
|
|
pip install -e .
|
|
|
|
# Muon optimizer support: megatron/core/optimizer/muon.py requires this for Newton-Schulz
|
|
# orthogonalization; not on PyPI (only a dependency-confusion stub), so install from the
|
|
# git source Megatron-LM itself pins in pyproject.toml.
|
|
RUN pip install "git+https://github.com/NVIDIA-NeMo/Emerging-Optimizers.git@v0.3.0"
|
|
|
|
# torch_memory_saver's source build needs TMS_CUDA_MAJOR; take it from torch.
|
|
RUN TMS_CUDA_MAJOR=$(python3 -c "import torch; print(torch.version.cuda.split('.')[0])") \
|
|
pip install git+https://github.com/fzyzcjy/torch_memory_saver.git@f05a8754daf68238d54e4cf31cb3ba866684bbaf --no-cache-dir --force-reinstall
|
|
RUN pip install "nvidia-modelopt[torch]>=0.37.0" --no-build-isolation
|
|
RUN pip install git+https://github.com/radixark/Megatron-Bridge.git@bridge --no-deps --no-build-isolation
|
|
RUN pip install megatron-energon --no-deps
|
|
RUN pip install multi-storage-client --no-deps
|
|
|
|
COPY requirements.txt /tmp/requirements.txt
|
|
RUN rm -rf /usr/lib/python3/dist-packages/jwt /usr/lib/python3/dist-packages/PyJWT* && pip install -r /tmp/requirements.txt
|
|
|
|
# The apt copy shadows the pip one in ldconfig and the loader interleaves the two,
|
|
# so transformer_engine gets a mixed set of libcudnn sub-libraries. The packages are
|
|
# held, hence --allow-change-held-packages.
|
|
RUN if [ "${ENABLE_CUDA_13}" = "1" ]; then CU=13; else CU=12; fi; \
|
|
apt-get remove -y --purge --allow-change-held-packages \
|
|
libcudnn9-cuda-${CU} libcudnn9-dev-cuda-${CU} libcudnn9-headers-cuda-${CU} \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
RUN if [ "${ENABLE_CUDA_13}" = "1" ]; then \
|
|
pip install nvidia-cudnn-cu13==9.22.0.52; \
|
|
else \
|
|
pip install nvidia-cudnn-cu12==9.22.0.52; \
|
|
fi
|
|
|
|
|
|
RUN rm -rf /root/.cache/pip /root/flash-attention
|
|
|
|
# ====================================== Install sglang-miles ============================================
|
|
|
|
# Install sglang from sglang-miles branch
|
|
# The CUDA 12 sglang images sed their dependency markers from cu13 to cu12 in
|
|
# python/pyproject.toml and never commit it, so a plain checkout aborts on the dirty
|
|
# file. Force it, then re-apply the same rewrite: the tree we just checked out carries
|
|
# the cu13 markers, and sglang's editable install makes them visible to every later pip
|
|
# resolve, which would drag cu13 packages onto a cu12 image.
|
|
RUN cd /sgl-workspace/sglang && \
|
|
git fetch origin ${SGLANG_BRANCH} && \
|
|
if [ -n "${SGLANG_COMMIT}" ]; then \
|
|
git checkout -f ${SGLANG_COMMIT}; \
|
|
else \
|
|
git checkout -f FETCH_HEAD; \
|
|
fi && \
|
|
if [ "${ENABLE_CUDA_13}" != "1" ]; then \
|
|
sed -i 's/cuda-python>=13\.0/cuda-python>=12,<13/' python/pyproject.toml && \
|
|
sed -i 's/flashinfer_python\[cu13\]/flashinfer_python[cu12]/' python/pyproject.toml && \
|
|
sed -i 's/nvidia-cutlass-dsl\[cu13\]/nvidia-cutlass-dsl/' python/pyproject.toml; \
|
|
fi && \
|
|
pip install -e "python[all]" --no-deps
|
|
|
|
# =========================== Mooncake structured object store wheel ===========================
|
|
|
|
# Miles' Mooncake backend requires the structured object store API. Install the selected
|
|
# CUDA 13 wheel as one coherent Python/native package; CUDA 12 keeps its base Mooncake.
|
|
RUN if [ "${ENABLE_CUDA_13}" = "1" ]; then \
|
|
set -- /tmp/wheels/mooncake_transfer_engine*-*.whl; \
|
|
if [ "$#" -ne 1 ] || [ ! -e "$1" ]; then \
|
|
echo "expected exactly one mooncake wheel for cu13/${TARGETARCH}" >&2; exit 1; \
|
|
fi && \
|
|
pip install --force-reinstall --no-deps "$1" && \
|
|
python3 -c "from mooncake.structured_object_store import FieldSchema, MooncakeBundleTransfer, export_ref, import_ref; MooncakeBundleTransfer.put; MooncakeBundleTransfer.get; MooncakeBundleTransfer.release_result; MooncakeBundleTransfer.cleanup_dataproto"; \
|
|
else \
|
|
echo "CUDA 12 variant: keeping the base image's mooncake"; \
|
|
fi
|
|
|
|
COPY docker/install-kube-tools.sh /tmp/install-kube-tools.sh
|
|
RUN bash /tmp/install-kube-tools.sh "${TARGETARCH}" && rm -f /tmp/install-kube-tools.sh
|
|
|
|
# ====================================== Install main package ============================================
|
|
|
|
ARG MILES_COMMIT=main
|
|
RUN git clone https://github.com/radixark/miles.git /root/miles && \
|
|
cd /root/miles && \
|
|
git checkout ${MILES_COMMIT} && \
|
|
pip install -e . --no-deps
|
|
|
|
# int4_qat
|
|
RUN pip install /tmp/wheels/fake_int4_quant_cuda-*.whl
|
|
|
|
# ====================================== Install sgl-model-gateway ============================================
|
|
# SGL_ROUTER_USE_WHEELS=0:
|
|
# Build from source https://github.com/radixark/sgl-router-for-miles
|
|
# SGL_ROUTER_USE_WHEELS=1 (default):
|
|
# Install the pre-built sgl-model-gateway wheel
|
|
|
|
ARG SGL_ROUTER_USE_WHEELS=1
|
|
ARG SGL_ROUTER_REPO=https://github.com/radixark/sgl-router-for-miles.git
|
|
ARG SGL_ROUTER_BRANCH=main
|
|
|
|
RUN --mount=type=cache,target=/root/.cache/pip \
|
|
set -eux; \
|
|
if [ "${SGL_ROUTER_USE_WHEELS}" = "1" ]; then \
|
|
pip install --force-reinstall /tmp/wheels/sglang_router-*.whl && \
|
|
tar xzf /tmp/wheels/sgl-model-gateway-linux-*.tar.gz -C /usr/local/bin/ && \
|
|
chmod +x /usr/local/bin/sgl-model-gateway; \
|
|
elif [ "${SGL_ROUTER_USE_WHEELS}" = "0" ]; then \
|
|
git clone --branch "${SGL_ROUTER_BRANCH}" --depth 1 "${SGL_ROUTER_REPO}" /build/sgl-model-gateway && \
|
|
curl --proto '=https' --tlsv1.2 --retry 3 --retry-delay 2 -sSf https://sh.rustup.rs | sh -s -- -y && \
|
|
export PATH="/root/.cargo/bin:${PATH}" && \
|
|
python3 -m pip install maturin && \
|
|
cd /build/sgl-model-gateway/bindings/python && \
|
|
ulimit -n 65536 && \
|
|
maturin build --release --features vendored-openssl --out /build/gateway_wheels && \
|
|
cd /build/sgl-model-gateway && \
|
|
cargo build --release --bin sgl-model-gateway --features vendored-openssl && \
|
|
cp target/release/sgl-model-gateway /usr/local/bin/sgl-model-gateway && \
|
|
chmod +x /usr/local/bin/sgl-model-gateway && \
|
|
pip install --force-reinstall /build/gateway_wheels/sglang_router-*.whl && \
|
|
rm -rf /root/.cargo /root/.rustup /build/sgl-model-gateway /build/gateway_wheels; \
|
|
fi
|
|
|
|
# ==================== Reconcile apache-tvm-ffi with sglang's pin ====================
|
|
# sglang v0.5.16 bumped nvidia-cutlass-dsl 4.5.2 -> 4.6.0 while leaving its
|
|
# apache-tvm-ffi pin at 0.1.11. cutlass-dsl 4.6.0's tvm_ffi provider calls
|
|
# tvm_ffi.utils.kwargs_wrapper.make_kwargs_wrapper(..., map_dataclass_to_tuple=...)
|
|
# and that parameter only exists in apache-tvm-ffi >= 0.1.10. An earlier install in
|
|
# this file drags in an older apache-tvm-ffi, so flashinfer's CuTe rmsnorm path dies
|
|
# during CUDA-graph capture with
|
|
# TypeError: make_kwargs_wrapper() got an unexpected keyword argument 'map_dataclass_to_tuple'
|
|
# Restore sglang's own pin after every other install, and fail the build loudly if it
|
|
# does not take effect rather than shipping an image that only breaks under CI.
|
|
RUN pip install --force-reinstall --no-deps "apache-tvm-ffi==0.1.11" && \
|
|
python3 -c "import inspect; from tvm_ffi.utils import kwargs_wrapper as k; \
|
|
p = inspect.signature(k.make_kwargs_wrapper).parameters; \
|
|
assert 'map_dataclass_to_tuple' in p, \
|
|
'apache-tvm-ffi resolves to a build too old for nvidia-cutlass-dsl 4.6.0: %s' % list(p)"
|
|
|
|
RUN rm -rf /tmp/wheels
|
|
|
|
# The cu130 sglang base ships its baked Rust toolchain outside the default PATH.
|
|
ENV PATH="/root/.cargo/bin:${PATH}"
|