mirror of
https://github.com/radixark/miles.git
synced 2026-10-02 07:14:53 +08:00
214 lines
6.8 KiB
Python
214 lines
6.8 KiB
Python
#!/usr/bin/env python3
|
|
# doc-dev: docs/developer/ci/02-docker-build.md
|
|
"""Build and push Miles Docker images.
|
|
|
|
Usage:
|
|
python docker/build.py --variant cu13 --image-tag dev --push # multi-arch (amd64+arm64)
|
|
python docker/build.py --variant cu13-x86 --image-tag dev --push # single arch
|
|
python docker/build.py --variant cu12-x86 --image-tag latest
|
|
python docker/build.py --variant cu13 --image-tag dev --dry-run
|
|
"""
|
|
|
|
import os
|
|
import subprocess
|
|
from datetime import datetime, timezone
|
|
from enum import Enum
|
|
from pathlib import Path
|
|
|
|
import image_inputs
|
|
import typer
|
|
|
|
CACHE_DIR = "/tmp/miles-docker-cache"
|
|
REPO_ROOT = Path(__file__).resolve().parent.parent
|
|
|
|
VARIANTS = {
|
|
"cu13": {
|
|
"image": "radixark/miles",
|
|
"platforms": ["linux/amd64", "linux/arm64"],
|
|
"tag_postfix": "",
|
|
"build_args": {},
|
|
},
|
|
"cu13-x86": {
|
|
"image": "radixark/miles",
|
|
"platforms": ["linux/amd64"],
|
|
"tag_postfix": "",
|
|
"build_args": {},
|
|
},
|
|
"cu13-aarch64": {
|
|
"image": "radixark/miles",
|
|
"platforms": ["linux/arm64"],
|
|
"tag_postfix": "",
|
|
"build_args": {},
|
|
},
|
|
"cu12-x86": {
|
|
"image": "radixark/miles",
|
|
"platforms": ["linux/amd64"],
|
|
"tag_postfix": "-cu12",
|
|
"build_args": {
|
|
"ENABLE_CUDA_13": "0",
|
|
"SGLANG_IMAGE_TAG": "v0.5.19-cu129",
|
|
# Frozen with the base image: sglang publishes no cu12 image from v0.5.20 on,
|
|
# so this variant cannot follow the branch the cu13 images track.
|
|
"SGLANG_BRANCH": "sglang-miles-v0.5.19-final",
|
|
"WHEELS_TAG_X86": "cu129-x86_64",
|
|
},
|
|
},
|
|
"rocm724-mi35x": {
|
|
"image": "rocm/sgl-dev",
|
|
"tag_postfix": "-rocm724-mi35x",
|
|
"tag_prefix": "miles",
|
|
"dockerfile": "docker/Dockerfile.rocm",
|
|
"build_args": {
|
|
"GPU_ARCH": "gfx950",
|
|
"SGLANG_IMAGE_REPO": "lmsysorg/sglang",
|
|
"SGLANG_IMAGE_TAG": "v0.5.20-rocm724-mi35x",
|
|
"WHEELS_TAG_ROCM": "rocm724-gfx950-v0.5.20",
|
|
"APPLY_ROCR_VMMFIX": "1",
|
|
"TE_USE_WHEEL": "1",
|
|
},
|
|
},
|
|
"rocm10-mi35x": {
|
|
"image": "rocm/sgl-dev",
|
|
"tag_postfix": "-rocm10-mi35x",
|
|
"tag_prefix": "miles",
|
|
"dockerfile": "docker/Dockerfile.rocm",
|
|
"build_args": {
|
|
"GPU_ARCH": "gfx950",
|
|
"SGLANG_IMAGE_REPO": "lmsysorg/sglang",
|
|
"SGLANG_IMAGE_TAG": "v0.5.20-rocm10-mi35x",
|
|
"WHEELS_TAG_ROCM": "rocm10-gfx950-v0.5.18",
|
|
"APEX_USE_PREBUILT": "1",
|
|
"TE_USE_WHEEL": "1",
|
|
},
|
|
},
|
|
}
|
|
|
|
|
|
def run(cmd: list[str], dry_run: bool) -> None:
|
|
print(f"+ {' '.join(cmd)}", flush=True)
|
|
if dry_run:
|
|
return
|
|
subprocess.run(cmd, check=True)
|
|
|
|
|
|
def build_and_push(
|
|
variant: str,
|
|
image_tag: str,
|
|
dry_run: bool,
|
|
dockerfile: str,
|
|
push: bool = False,
|
|
custom_tag: str = "",
|
|
extra_build_args: list[str] | None = None,
|
|
context: Path = REPO_ROOT,
|
|
output: str = "",
|
|
) -> None:
|
|
extra_build_args = extra_build_args or []
|
|
config = VARIANTS[variant]
|
|
# A variant may pin its own Dockerfile (e.g. ROCm); otherwise use the CLI default.
|
|
dockerfile = config.get("dockerfile", dockerfile)
|
|
image = config["image"]
|
|
postfix = config.get("tag_postfix", "")
|
|
platforms = config.get("platforms")
|
|
|
|
if image_tag == "latest":
|
|
tags = [f"{image}:latest{postfix}"]
|
|
elif image_tag == "dev":
|
|
prefix = config.get("tag_prefix", "dev")
|
|
timestamp = datetime.now(timezone.utc).strftime("%Y%m%d%H%M")
|
|
tags = [f"{image}:{prefix}{postfix}", f"{image}:{prefix}{postfix}-{timestamp}"]
|
|
elif image_tag == "custom":
|
|
if not custom_tag:
|
|
raise typer.BadParameter("--custom-tag is required when --image-tag is custom")
|
|
tags = [f"{image}:{custom_tag}{postfix}"]
|
|
else:
|
|
raise typer.BadParameter(f"Unknown image tag: {image_tag}")
|
|
|
|
cmd = [
|
|
"docker",
|
|
"buildx",
|
|
"build",
|
|
"-f",
|
|
str(context / dockerfile),
|
|
]
|
|
|
|
if platforms:
|
|
cmd += ["--platform", ",".join(platforms)]
|
|
|
|
if push:
|
|
cmd += ["--push"]
|
|
if output:
|
|
cmd += ["--output", output]
|
|
|
|
# Proxy args (pass through if set in environment, check both cases)
|
|
for arg_name in ["HTTP_PROXY", "HTTPS_PROXY"]:
|
|
value = os.environ.get(arg_name.lower()) or os.environ.get(arg_name)
|
|
if value:
|
|
cmd += ["--build-arg", f"{arg_name}={value}"]
|
|
|
|
cmd += ["--build-arg", "NO_PROXY=localhost,127.0.0.1"]
|
|
|
|
# Variant-specific build args
|
|
for key, value in config.get("build_args", {}).items():
|
|
cmd += ["--build-arg", f"{key}={value}"]
|
|
|
|
# Caller overrides (e.g. release builds pinning SGLANG_COMMIT / MILES_COMMIT
|
|
# from release-lock.json) come last so they win over variant defaults.
|
|
for spec in extra_build_args:
|
|
assert "=" in spec, f"--build-arg expects KEY=VALUE, got {spec!r}"
|
|
cmd += ["--build-arg", spec]
|
|
|
|
# CI reads this back off the published tag to skip rebuilds whose inputs are unchanged.
|
|
cmd += ["--label", f"{image_inputs.LABEL_KEY}={image_inputs.compute(root=context)}"]
|
|
|
|
for tag in tags:
|
|
cmd += ["-t", tag]
|
|
|
|
# Context is repo root
|
|
cmd += [str(context)]
|
|
|
|
print(f"\n=== Building {' '.join(tags)} ===", flush=True)
|
|
run(cmd, dry_run)
|
|
|
|
|
|
class Variant(str, Enum):
|
|
cu13 = "cu13"
|
|
cu13_x86 = "cu13-x86"
|
|
cu13_aarch64 = "cu13-aarch64"
|
|
cu12_x86 = "cu12-x86"
|
|
rocm724_mi35x = "rocm724-mi35x"
|
|
rocm10_mi35x = "rocm10-mi35x"
|
|
|
|
|
|
class ImageTag(str, Enum):
|
|
latest = "latest"
|
|
dev = "dev"
|
|
custom = "custom"
|
|
|
|
|
|
def main(
|
|
variant: Variant = typer.Option(..., help="Build variant to use."), # noqa: B008
|
|
image_tag: ImageTag = typer.Option(..., help="Tag mode: latest, dev, or custom."), # noqa: B008
|
|
dockerfile: str = typer.Option("docker/Dockerfile", help="Path to the Dockerfile."), # noqa: B008
|
|
dry_run: bool = typer.Option(False, help="Print commands without executing them."), # noqa: B008
|
|
push: bool = typer.Option(False, help="Push images to registry after building."), # noqa: B008
|
|
custom_tag: str = typer.Option("", help="Custom tag name (required when --image-tag is custom)."), # noqa: B008
|
|
build_arg: list[str] = typer.Option([], help="Extra KEY=VALUE build-arg (repeatable)."), # noqa: B008
|
|
context: Path = typer.Option(REPO_ROOT, help="Repository build context, separate from this driver."), # noqa: B008
|
|
output: str = typer.Option("", help="Buildx exporter, e.g. type=oci,dest=/tmp/image,tar=false."), # noqa: B008
|
|
) -> None:
|
|
build_and_push(
|
|
variant.value,
|
|
image_tag.value,
|
|
dry_run,
|
|
dockerfile,
|
|
push=push,
|
|
custom_tag=custom_tag,
|
|
extra_build_args=build_arg,
|
|
context=context.resolve(),
|
|
output=output,
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
typer.run(main)
|