Dockerfile 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. # DeepSeek-V4.1-Flash — DGX Spark (arm64, sm_121a), clone-at-pin.
  2. #
  3. # One Dockerfile for tonyd2wild's overlay1/3/5 chain (MIT; see versions.env
  4. # header + patches/README note). No fallbacks: every gate fails loud.
  5. #
  6. # base pinned vllm/vllm-openai nightly (arm64 digest)
  7. # vLLM vllm-project/vllm @ dsv41-feat commit+tree (day-0 DSV4.1 support)
  8. # ext _C_stable_libtorch rebuilt for sm_121a (stable-only cmake build)
  9. # FI FlashInfer 0.7.0rc1 from pinned source (0.6.18 lacks the SM120
  10. # sparse-MLA decode kernel for page_block_size=32)
  11. # warm sparse-MLA + mxfp8 JIT kernels prebuilt in the image runtime env
  12. # patches the 7 boot-10 file replacements, baked over dist-packages,
  13. # md5-gated against the published manifest
  14. #
  15. # Model: deepseek-ai/DeepSeek-V4.1-Flash (510 GB; Engram tables stay on disk
  16. # via the baked engram.py patch — DSV41_ENGRAM_DISK=1 at serve time).
  17. ARG VLLM_BASE_IMAGE
  18. FROM ${VLLM_BASE_IMAGE}
  19. ARG VLLM_REPO
  20. ARG VLLM_REF
  21. ARG VLLM_REF_TREE
  22. ARG VLLM_PRETEND_VERSION
  23. ARG CUTLASS_TAG
  24. ARG CUTLASS_URL
  25. ARG FI_SHA
  26. ARG FI_CUTLASS_SHA
  27. ARG FI_CCCL_SHA
  28. ARG FI_SPDLOG_SHA
  29. ENV DEBIAN_FRONTEND=noninteractive \
  30. PIP_DISABLE_PIP_VERSION_CHECK=1 \
  31. MAX_JOBS=4
  32. # ---- vLLM python tree @ pin (clone-at-pin: commit + tree hash gates) ----
  33. RUN set -eux; \
  34. apt-get update -qq && apt-get install -y --no-install-recommends git ca-certificates >/dev/null && rm -rf /var/lib/apt/lists/*; \
  35. git config --global protocol.version 0; \
  36. git init -q /src/vllm; \
  37. git -C /src/vllm fetch --depth 1 ${VLLM_REPO} ${VLLM_REF}; \
  38. git -C /src/vllm checkout -q ${VLLM_REF}; \
  39. test "$(git -C /src/vllm rev-parse HEAD)" = "${VLLM_REF}"; \
  40. test "$(git -C /src/vllm rev-parse 'HEAD^{tree}')" = "${VLLM_REF_TREE}"; \
  41. echo "vLLM pinned at ${VLLM_REF} / tree ${VLLM_REF_TREE}"
  42. # ---- _C_stable_libtorch rebuilt for sm_121a (their build_stable_ext.sh) ----
  43. # stable-only build: external_projects includes commented out, cutlass fetched
  44. # at the pinned tag, Release build of the single C extension target.
  45. RUN set -eux; \
  46. cd /src/vllm; \
  47. mkdir -p build/_deps; \
  48. curl -sL -m 600 ${CUTLASS_URL} | tar xz -C build/_deps --strip-components=1 && \
  49. mv build/_deps/cutlass build/_deps/cutlass-src 2>/dev/null || true; \
  50. ls build/_deps/cutlass-src/include/cutlass/cutlass.h >/dev/null; \
  51. cp CMakeLists.txt CMakeLists.txt.orig; \
  52. sed -i -E "s|^(\s*)include\(cmake/external_projects/|\1# STABLE-ONLY: include(cmake/external_projects/|" CMakeLists.txt; \
  53. PYPATH=$(python3 -c "import sys;print(':'.join(p for p in sys.path if p))"); \
  54. TORCH_PREFIX=$(python3 -c "import torch;print(torch.utils.cmake_prefix_path)"); \
  55. cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DVLLM_TARGET_DEVICE=cuda \
  56. -DVLLM_PYTHON_EXECUTABLE=$(which python3) -DVLLM_PYTHON_PATH="$PYPATH" \
  57. -DFETCHCONTENT_BASE_DIR=build/_deps -DFETCHCONTENT_SOURCE_DIR_CUTLASS=build/_deps/cutlass-src \
  58. -DCMAKE_PREFIX_PATH="$TORCH_PREFIX" -DNVCC_THREADS=2 -DTORCH_CUDA_ARCH_LIST=12.1a; \
  59. cmake --build build --target _C_stable_libtorch -j 20; \
  60. find build vllm -maxdepth 2 -name "_C_stable_libtorch*.so" | tee /tmp/stable_ext.so; \
  61. # place the built extension in the package dir (vLLM imports vllm._C_stable_libtorch)
  62. cp -v $(find build vllm -maxdepth 2 -name "_C_stable_libtorch*.so" | head -1) vllm/
  63. # ---- merge the branch python tree (+ the sm121 .so) over the base install ----
  64. RUN set -eux; \
  65. SITE=/usr/local/lib/python3.12/dist-packages/vllm; \
  66. cp -a /src/vllm/vllm/. ${SITE}/; \
  67. find ${SITE} -name "__pycache__" -type d -prune -exec rm -rf {} +; \
  68. python3 -c "import vllm; print('vllm', vllm.__version__)"; \
  69. python3 -c "import vllm._C_stable_libtorch as m; print('stable ext ok:', m.__file__)"
  70. # ---- FlashInfer 0.7.0rc1 from pinned source (0.6.18 has no SM120 sparse-MLA) ----
  71. RUN set -eux; \
  72. pip uninstall -y -q flashinfer-jit-cache flashinfer-cubin flashinfer-python || true; \
  73. rm -rf /root/.cache/flashinfer; \
  74. mkdir -p /opt/fi-src; \
  75. curl -sL -m 900 "https://codeload.github.com/flashinfer-ai/flashinfer/tar.gz/${FI_SHA}" | tar xz -C /opt/fi-src --strip-components=1; \
  76. mkdir -p /opt/fi-src/3rdparty/cutlass /opt/fi-src/3rdparty/cccl /opt/fi-src/3rdparty/spdlog; \
  77. curl -sL -m 900 "https://codeload.github.com/NVIDIA/cutlass/tar.gz/${FI_CUTLASS_SHA}" | tar xz -C /opt/fi-src/3rdparty/cutlass --strip-components=1; \
  78. curl -sL -m 900 "https://codeload.github.com/NVIDIA/cccl/tar.gz/${FI_CCCL_SHA}" | tar xz -C /opt/fi-src/3rdparty/cccl --strip-components=1; \
  79. curl -sL -m 900 "https://codeload.github.com/gabime/spdlog/tar.gz/${FI_SPDLOG_SHA}" | tar xz -C /opt/fi-src/3rdparty/spdlog --strip-components=1; \
  80. ls /opt/fi-src/3rdparty/cutlass/include/cutlass/cutlass.h /opt/fi-src/3rdparty/cccl/README.md /opt/fi-src/3rdparty/spdlog/include/spdlog/spdlog.h; \
  81. cd /opt/fi-src && BUILD_NVEP=0 FLASHINFER_BUILD_NO_PIP=1 pip install --no-deps --no-build-isolation -q .; \
  82. rm -rf /opt/fi-src/build; \
  83. python3 -c "import flashinfer; assert '0.7.0rc1' in flashinfer.__version__, flashinfer.__version__; print('flashinfer', flashinfer.__version__)"
  84. ENV VLLM_HAS_FLASHINFER_CUBIN=1
  85. # ---- prebuild the SM120 sparse-MLA + mxfp8 GEMM JIT kernels ----
  86. # Exact runtime env (no verbose/debug nvcc flags — those change the cache key).
  87. # The compiled modules persist in /root/.cache/flashinfer inside the image.
  88. COPY patches/prewarm5.py /tmp/prewarm5.py
  89. RUN set -eux; \
  90. FLASHINFER_CUDA_ARCH_LIST=12.1a TORCH_CUDA_ARCH_LIST=12.1a \
  91. FLASHINFER_DISABLE_VERSION_CHECK=1 VLLM_HAS_FLASHINFER_CUBIN=1 \
  92. FLASHINFER_NVCC_THREADS=1 MAX_JOBS=4 \
  93. python3 /tmp/prewarm5.py | tee /tmp/prewarm.log; \
  94. grep -q "SPARSE-BUILT+LOADED" /tmp/prewarm.log || { echo "sparse MLA prewarm FAILED"; exit 1; }
  95. # ---- bake the 7 boot-10 patch files (md5-gated against their manifest) ----
  96. COPY patches/ /tmp/patches/
  97. RUN set -eux; \
  98. python3 - <<'PY'
  99. import hashlib, os, shutil, sys
  100. SITE = "/usr/local/lib/python3.12/dist-packages/vllm"
  101. # md5 prefixes from tonyd2wild patch/README.md (boot-10 manifest)
  102. MD5 = {
  103. "engram.py": "c0329107", "model_state.py": "0a14bee6",
  104. "weight_utils.py": "7e1027f1", "attention.py": "da9ef196",
  105. "flashinfer_sparse.py": "af0f8447", "sparse_swa.py": "cc419353",
  106. "sparse_attn_indexer.py": "a9b73756",
  107. }
  108. bad = []
  109. for line in open("/tmp/patches/mounts.txt"):
  110. line = line.strip()
  111. if not line or line.startswith("#"):
  112. continue
  113. f, rel = line.split()
  114. src = f"/tmp/patches/{f}"
  115. m = hashlib.md5(open(src, "rb").read()).hexdigest()[:8]
  116. if m != MD5.get(f):
  117. bad.append(f"{f}: {m} != {MD5.get(f)}")
  118. continue
  119. dst = os.path.join(SITE, rel)
  120. os.makedirs(os.path.dirname(dst), exist_ok=True)
  121. shutil.copyfile(src, dst)
  122. print(f"baked {f} -> {rel} (md5 {m})")
  123. sys.exit("\n".join(bad) if bad else None)
  124. PY
  125. # ---- final gates ----
  126. RUN set -eux; \
  127. python3 -c "import vllm; print('vllm', vllm.__version__)"; \
  128. python3 -c "import vllm._C_stable_libtorch; print('stable ext import ok')"; \
  129. python3 -c "import flashinfer; print('flashinfer', flashinfer.__version__)"; \
  130. grep -q "DSV41_ENGRAM_DISK" /usr/local/lib/python3.12/dist-packages/vllm/models/deepseek_v4_1/common/engram.py; \
  131. echo "=== image gates passed ==="