|
| 1 | +# Dynamo Container Build Process Improvements |
| 2 | + |
| 3 | +**Status**: Ready for Review |
| 4 | + |
| 5 | +**Authors**: [nv-tusharma] |
| 6 | + |
| 7 | +**Category**: Architecture |
| 8 | + |
| 9 | +**Replaces**: N/A |
| 10 | + |
| 11 | +**Replaced By**: N/A |
| 12 | + |
| 13 | +**Sponsor**: saturley-hall, nv-anants |
| 14 | + |
| 15 | +**Required Reviewers**: nnshah1, saturley-hall, nv-anants, nvda-mesharma, mc-nv, dmitry-tokarev-nv, pvijayakrish |
| 16 | + |
| 17 | +**Review Date**: 2025-07-18 |
| 18 | + |
| 19 | +**Pull Request**: TBD |
| 20 | + |
| 21 | +**Implementation PR / Tracking Issue**: TBD |
| 22 | + |
| 23 | +# Summary |
| 24 | + |
| 25 | +This document outlines a container build process optimization strategy for Dynamo to enhance the developer experience by re-organizing Dockerfiles along with defining a clear and maintainable structure for our Dockerfiles. |
| 26 | + |
| 27 | +One of the goals for this document is to define a clear and maintainable structure for our Dockerfiles. Specifically, to determine how many Dockerfiles we need and clarify the relationships between base, runtime, and development images. The aim is to ensure each environment's Dockerfile builds upon the previous (as supersets), maximizing environment consistency and coverage during daily development and testing. |
| 28 | + |
| 29 | +To achieve this goal, this document proposes certain optimizations to improve the current build process: |
| 30 | +- Restructuring the build process to provide a base container with a pre-built version of Dynamo + NIXL available on all distributions, enabling splitting of specific backends from the dynamo build process. |
| 31 | +- Defining a structure/template for all Dockerfiles to follow to ensure consistent and reproducible builds across backends along with specific roles/use cases targeted for each stage. |
| 32 | +- Implementing remote compiler caching strategies tools like sccache to significantly reduce rust compilation times across builds and CI/CD pipelines. |
| 33 | + |
| 34 | +# Motivation |
| 35 | + |
| 36 | +Dynamo's current container architecture consists of multiple Dockerfiles in the `/containers` directory of the [Dynamo repository](https://github.com/NVIDIA/Dynamo). These Dockerfiles are organized by backend (vLLM, sglang, TRT-LLM) and contain multiple stages (base, devel, ci, runtime) for different use cases. Each stage includes a Dynamo build, the specific backend, and NIXL - our high-throughput, low-latency point-to-point communication library for accelerating inference. |
| 37 | + |
| 38 | +The current approach faces several challenges: |
| 39 | + |
| 40 | +1. **Inefficient Build Process**: Components like Dynamo, NIXL, and backends are installed multiple times across stages instead of using a layered approach. Stages do not build upon each other which leads to repeated steps and inefficient build times. |
| 41 | + |
| 42 | +2. **Developer Experience Issues**: The unclear organization of Dockerfiles makes it difficult for developers to choose the right build for their needs, often defaulting to the devel build regardless of use case. |
| 43 | + |
| 44 | +3. **Build Reliability**: The complex layering and repeated steps across stages can lead to intermittent build failures. |
| 45 | + |
| 46 | +4. **Inconsistent Standards**: Without a unified Dockerfile for building Dynamo, NIXL, and dependencies, code is duplicated or missing across backend-specific Dockerfiles, and optimizations aren't shared effectively. |
| 47 | + |
| 48 | +This document proposes solutions to the build process challenges, aiming to improve overall container build efficiency and developer experience. |
| 49 | + |
| 50 | +## Goals |
| 51 | + |
| 52 | +* Remove duplicate code in current dockerfile implementations and define a single build base image which provides a pre-built container with Dynamo + NIXL. |
| 53 | + |
| 54 | +This base image should operate as a single base container which can then be used as base containers for backend-specific images. By leveraging a base container, We can reduce the redundant code across Dockerfiles and establish a single-source of truth for all Dynamo-builds. This would also enable us to replace the current devel container with the base container for local development/public CI for faster validation of changes. |
| 55 | + |
| 56 | +* Define the relationships between base, runtime, and development images for each Dockerfile and provide a structure/template to follow for Dockerfiles. |
| 57 | + |
| 58 | +* Reduce build flakiness by pinning/fixing dependencies in the base image from package managers and squashing/reducing layers as necessary |
| 59 | + |
| 60 | +Pinning/Fixing dependencies will ensure a unified build environment reducing "it works on my machine" problems or "this worked yesterday" |
| 61 | + |
| 62 | +* Minimize effort for providing multi-arch support across various backends for Dynamo by leveraging manylinux to build for multiple distributions |
| 63 | + |
| 64 | +* Implement remote compiler caching strategies to dramatically reduce build times across development and CI/CD environments |
| 65 | + |
| 66 | +By integrating tools like sccache for remote compilation caching, we can avoid redundant compilation work across builds, significantly speeding up the container build process for both developers and CI pipelines. |
| 67 | + |
| 68 | +### Non Goals |
| 69 | + |
| 70 | +- Container release strategy and processes (covered in separate DEP) |
| 71 | +- Unified build environment |
| 72 | + |
| 73 | +## Requirements |
| 74 | + |
| 75 | +### REQ \<\#1\> \<Backend Integration with Base Container\> |
| 76 | +The build-base container must be designed such that backend-specific Dockerfiles can integrate with it with minimal changes to their existing build process. This includes: |
| 77 | +- Multi-arch support is a P0. The Base container should be able to support both x84_64 and arm64 builds. |
| 78 | +- Clear documentation on how to use the base container |
| 79 | +- Standardized environment variables and paths |
| 80 | +- Replace the current devel container with the base container for local development/public CI for faster validation of changes. |
| 81 | + |
| 82 | +### REQ \<\#2\> \<Layered Container Structure\> |
| 83 | +Dockerfiles must follow a layered, super-set structure to optimize build efficiency: |
| 84 | +- Each stage should build upon the previous stage or use artifacts from the previous stage |
| 85 | +- Artifacts should be built only once and reused across stages |
| 86 | +- Clear separation between build-time and runtime dependencies |
| 87 | +- Minimal layer count to reduce build complexity |
| 88 | + |
| 89 | +### REQ \<\#3\> \<Stage Purpose Definition\> |
| 90 | +Each build stage must have a clearly defined purpose and scope: |
| 91 | +- Base: NIXL + Dynamo build from a manylinux container (Enables support on multiple platforms) |
| 92 | +- Backend Build: Builds the specified backend along with any dependencies required for the backend |
| 93 | +- Runtime: Minimal production deployment requirements |
| 94 | +- CI: Testing tools and validation requirements built on runtime |
| 95 | + |
| 96 | +# Proposal |
| 97 | + |
| 98 | +In order to address the requirements, we propose the following changes to the Dynamo build process: |
| 99 | + |
| 100 | +## Build-Base Container |
| 101 | + |
| 102 | +The base container will be a pre-built container that will be used by the backends to build the final container image. This build base container will contain a Dynamo build for all backends to use for their framework-specific build. The base image will leverage a manylinux base image to enable support for multiple distributions (U22, U24, etc). The container will also include a NIXL build since this is common across all backends. This will be a new Dockerfile in the /containers directory. Multi-arch support is also a P0 Also, the base container can be used as a drop-in replacement for the current devel container used in public CI. This would significantly reduce public CI build times and enable faster validation of changes. |
| 103 | + |
| 104 | +## Use-case of build stages along with relationship between stages (base, runtime, dev) |
| 105 | + |
| 106 | +Each backend-specific Dockerfile should follow a specific format. The backend-specific Dockerfiles should be divided up into multiple stages, with each stage inheriting artifacts/leveraging the previous stage as the base container. The following stages should be defined in the backend-specific Dockerfile: |
| 107 | + |
| 108 | +**Backend Build Stage:** |
| 109 | +- Targeted User: Developers |
| 110 | +- Base Image: Cuda base devel image |
| 111 | +- Functionality: Builds targeted backend along with backend-specific dependencies in editable mode. |
| 112 | + |
| 113 | +**Runtime Stage:** |
| 114 | +- Targeted User: Customers/Production |
| 115 | +- Base Image: Cuda base runtime image |
| 116 | +- Functionality: Minimal image with only the dependencies required to deploy and run Dynamo w/backend from the backend build stage; intended for production deployments. Copies dynamo artifacts from base image and backend artifacts from backend build image. |
| 117 | + |
| 118 | +**Development Stage:** |
| 119 | +- Targeted User: Developers/Internal CI Pipelines/Local Debugging |
| 120 | +- Base Image: Runtime image |
| 121 | +- Functionality: Adds development-specific tools, QA test scripts, internal models, and other dependencies needed for developers. We also want to integrate dev container features into this stage. |
| 122 | + |
| 123 | +The CUDA base images will be used from the [NVIDIA CUDA Container Registry](https://catalog.ngc.nvidia.com/orgs/nvidia/containers/cuda). Please refer to the Pros and Cons section for more details on why we chose to use the cuda runtime image instead of the Deep Learning CUDA image. |
| 124 | + |
| 125 | +# Implementation Details |
| 126 | + |
| 127 | +## Container Build Flow |
| 128 | + |
| 129 | +```mermaid |
| 130 | +flowchart TD |
| 131 | + A[Manylinux build base image] |
| 132 | + B[NIXL Setup/Build NIXL Wheel] |
| 133 | + C[Build Dependencies] |
| 134 | + D[Build Dynamo] |
| 135 | + E[Build Base Container] |
| 136 | + F[Build Backend from source] |
| 137 | + G[Backend Build Image] |
| 138 | + J[Cuda Runtime<br/>nvcr.io/nvidia/cuda.XX.YY-runtime] |
| 139 | + K[Install NATS + ETCD] |
| 140 | + L[Runtime-specific dependencies] |
| 141 | + M[pip install dynamo + Backend && NIXL] |
| 142 | + N[Backend Runtime Image] |
| 143 | + O[Install CI/Test/Dev dependencies] |
| 144 | + P[Development image] |
| 145 | +
|
| 146 | + %% Main build flow (left) |
| 147 | + A --> B |
| 148 | + B --> C |
| 149 | + C --> D |
| 150 | + D --> E |
| 151 | + E --> F |
| 152 | + F --> G |
| 153 | +
|
| 154 | + %% Runtime flow (right) |
| 155 | + J --> K |
| 156 | + K --> L |
| 157 | + L --> M |
| 158 | + M --> N |
| 159 | + N --> O |
| 160 | + O --> P |
| 161 | +
|
| 162 | + %% Cross-links with text |
| 163 | + E -.->|Copy Dynamo & NIXL Build Wheels| M |
| 164 | + G -.->|Copy Backend build| M |
| 165 | +
|
| 166 | + %% Styling |
| 167 | + classDef gray fill:#e5e7eb,stroke:#333,stroke-width:1px; |
| 168 | +``` |
| 169 | + |
| 170 | +The diagram above illustrates the proposed container build strategy showing the relationships between: |
| 171 | +- Build Base Container with common dependencies |
| 172 | +- Backend-specific development containers |
| 173 | +- Runtime containers |
| 174 | +- Development containers |
| 175 | + |
| 176 | +This layered approach ensures consistent builds, reduces duplication, and improves maintainability across all backend implementations. |
| 177 | + |
| 178 | +## Dockerfile Structure Template |
| 179 | + |
| 180 | +Each backend-specific Dockerfile will follow this standardized structure: |
| 181 | + |
| 182 | +```dockerfile |
| 183 | +# Backend Build Stage |
| 184 | +FROM nvcr.io/nvidia/cuda:XX.YY-devel-ubuntuXX.XX as backend-build |
| 185 | +# Install backend-specific dependencies and build backend |
| 186 | + |
| 187 | +# Runtime Stage |
| 188 | +FROM nvcr.io/nvidia/cuda:XX.YY-runtime-ubuntuXX.XX as runtime |
| 189 | +# Copy dynamo and NIXL wheels from base container |
| 190 | +# Copy backend artifacts from backend-build stage |
| 191 | +# Install runtime dependencies only |
| 192 | + |
| 193 | +# Development Stage |
| 194 | +FROM runtime as dev |
| 195 | +# Add development-specific tools and test dependencies |
| 196 | +``` |
| 197 | + |
| 198 | +## Dependency Management |
| 199 | + |
| 200 | +- **Pinned Dependencies**: All dependencies will be pinned to specific versions in the base container |
| 201 | +- **Multi-arch Support**: Base container will support both x86_64 and arm64 architectures |
| 202 | +- **Minimal Runtime**: Runtime images will only include necessary dependencies for production deployment |
| 203 | +- **Layered Caching**: Build layers will be optimized for Docker build cache efficiency |
| 204 | + |
| 205 | +## Build Caching Strategy (Phase 3) |
| 206 | + |
| 207 | +To further optimize build times after the initial Dockerfile restructuring, We will explore remote compiler caching strategies (further optimizations to be added in future): |
| 208 | + |
| 209 | +### Remote Compiler Caching Strategies |
| 210 | +- **Compiler cache Integration**: Leverage compiler cache service like [sccache](https://github.com/mozilla/sccache) in the build-base container to cache compilation results for Dynamo, NIXL, and backend dependencies. |
| 211 | +- **Remote Cache Storage**: Use a remote cache storage service to store the cached compilation results in CI/CD pipelines. |
| 212 | +- **Cache Size Management**: Configure appropriate cache size limits and cleanup policies to balance storage usage with build performance. |
| 213 | + |
| 214 | + |
| 215 | +### Implementation Considerations |
| 216 | +- **Cache Invalidation**: Implement smart cache invalidation based on dependency changes and version updates |
| 217 | +- **Monitoring**: Add build time metrics to measure cache effectiveness and identify optimization opportunities |
| 218 | +- **CI Integration**: Configure CI/CD pipelines to properly utilize remote caching storage. |
| 219 | + |
| 220 | + |
| 221 | +# Implementation Phases |
| 222 | + |
| 223 | +## Phase \<\#1\> \<Build Base Container Development\> |
| 224 | + |
| 225 | +**Release Target**: v0.5.0 |
| 226 | + |
| 227 | +**Effort Estimate**: 1 engineer, 1 week |
| 228 | + |
| 229 | +**Work Item(s):** N/A |
| 230 | + |
| 231 | +**Supported API / Behavior:** |
| 232 | + |
| 233 | +* Pre-built Build base container with multi-arch support containing Dynamo and NIXL |
| 234 | +* NIXL integration in base container |
| 235 | +* Manylinux base for broad distribution compatibility |
| 236 | +* Standardized environment variables and paths |
| 237 | +* Pinned dependencies for base container |
| 238 | + |
| 239 | +**Not Supported:** |
| 240 | + |
| 241 | + |
| 242 | +## Phase \<\#2\> \<Restructure backend Dockerfiles to follow proposed structure\> |
| 243 | + |
| 244 | +**Release Target**: v0.5.1 |
| 245 | + |
| 246 | +**Effort Estimate**: 1 engineer, 1 week |
| 247 | + |
| 248 | +**Work Item(s):** \<one or more links to github issues\> |
| 249 | + |
| 250 | +**Supported API / Behavior:** |
| 251 | + |
| 252 | +* Updated vLLM, sglang, and TRT-LLM Dockerfiles following new structure |
| 253 | +* Clear separation between backend build, runtime, and CI stages |
| 254 | +* Integration with base container for Dynamo and NIXL dependencies |
| 255 | +* Reduced build times through improved layering |
| 256 | +* Backward compatibility with existing Dockerfiles |
| 257 | +* Pinned dependencies for backend builds |
| 258 | + |
| 259 | +**Not Supported:** |
| 260 | + |
| 261 | +## Phase \<\#3\> \<Build Caching Optimization\> |
| 262 | + |
| 263 | +**Release Target**: v0.5.1 |
| 264 | + |
| 265 | +**Effort Estimate**: 1 engineer, 1 week |
| 266 | + |
| 267 | +**Work Item(s):** N/A |
| 268 | + |
| 269 | +**Supported API / Behavior:** |
| 270 | + |
| 271 | +* Integration of sccache for rust compilation caching across container builds |
| 272 | +* Remote cache storage for CI/CD pipelines to reduce cold build times |
| 273 | +* Cache invalidation strategies for dependency updates and smart cache layer management |
| 274 | + |
| 275 | +**Not Supported:** |
| 276 | + |
| 277 | +* Advanced distributed caching mechanisms |
| 278 | + |
| 279 | +# Related Proposals |
| 280 | + |
| 281 | +**\[Optional \- if not applicable omit\]** |
| 282 | + |
| 283 | +# Alternate Solutions |
| 284 | + |
| 285 | +**\[Required, if not applicable write N/A\]** |
| 286 | + |
| 287 | +List out solutions that were considered but ultimately rejected. Consider free form \- but a possible format shown below. |
| 288 | + |
| 289 | +## Alt \<\# 2\> \<Provide a single container with multi-backend support instead of multiple backend-specific containers\> |
| 290 | + |
| 291 | +**Pros:** |
| 292 | + |
| 293 | +- Reduce overall complexity (less containers, less Dockerfiles) |
| 294 | +- No need foradditonal security scans and QA validation. |
| 295 | +- Simpler Open Source approval process. |
| 296 | + |
| 297 | +**Cons:** |
| 298 | + |
| 299 | +- Container size, if a user is only interested in one particular backend, we should remove the dependencies associated with other backends. We would need to provide tools for users to create backend-specific containers for their deployment. |
| 300 | +- It is expected that Backends can differ on performance, which could be a result of backend-specific dependencies. |
| 301 | +- Build times are expected to be longer for a single container with multi-backend support. |
| 302 | +- Is it feasible for a user to want deploy multiple inference engines at once with dynamo? |
| 303 | + |
| 304 | +**Reason Rejected:** |
| 305 | + |
| 306 | +- There are more cons than pros for this approach. Along with the cons, the Dynamo base container is a good drop-in replacement for the multi-backend container. |
| 307 | + |
| 308 | +## Alt #3 Published Base Container with Pinned Dependencies |
| 309 | + |
| 310 | +**Description:** |
| 311 | + |
| 312 | +Instead of building NIXL, UCX, and other stable dependencies from source in each build, publish a pre-built base container with these pinned components. This would create a three-tier container hierarchy: |
| 313 | + |
| 314 | +1. **Base Image (Published):** CUDA + NIXL + UCX + uv + cargo + other stable dependencies |
| 315 | +2. **Dynamo Image:** Base Image + Dynamo Rust/Python builds |
| 316 | +3. **Framework Image:** Dynamo Image + Framework builds (vLLM, sglang, TRT-LLM) |
| 317 | + |
| 318 | +The base image would be published to a public registry (GitHub Container Registry or NGC) and updated infrequently when NIXL or other core dependencies change. |
| 319 | + |
| 320 | +**Pros:** |
| 321 | + |
| 322 | +- **Dramatically reduced build times:** Skip compilation of NIXL, UCX, and other stable components that rarely change |
| 323 | +- **Consistent environment:** All builds use the same pinned versions of core dependencies |
| 324 | +- **Simplified maintenance:** Base image updates are centralized and infrequent |
| 325 | +- **Better caching:** Base image can be cached across all builds and CI pipelines |
| 326 | +- **Reduced CI resource usage:** Less compilation work in CI/CD pipelines |
| 327 | +- **Public availability:** Base image could be made available to external users/partners |
| 328 | + |
| 329 | +**Cons:** |
| 330 | + |
| 331 | +- **Additional publishing workflow:** Need separate CI/CD pipeline to build and publish base images |
| 332 | +- **Registry management:** Need to manage storage and access for published base images |
| 333 | +- **Storage costs:** Published images consume registry storage space |
| 334 | +- **Security scanning:** Published base images need regular security scanning and updates |
| 335 | +- **Dependency on external registry:** Builds depend on availability of published base image |
| 336 | + |
| 337 | +**Implementation Considerations:** |
| 338 | + |
| 339 | +- **Publishing cadence:** Base image updates triggered by NIXL/UCX version changes or monthly schedule |
| 340 | +- **Versioning strategy:** Semantic versioning for base images (e.g., `nvidia/dynamo-base:v1.2.0`) |
| 341 | +- **Multi-arch support:** Publish both x86_64 and arm64 variants |
| 342 | +- **Registry choice:** GitHub Container Registry (ghcr.io) for open source, NGC for enterprise |
| 343 | +- **Fallback strategy:** Ability to build from source if published image unavailable |
| 344 | + |
| 345 | +**Reason Rejected:** |
| 346 | + |
| 347 | +N/A |
| 348 | + |
| 349 | +# Background |
| 350 | + |
| 351 | +**\[Optional \- if not applicable omit\]** |
| 352 | + |
| 353 | +Add additional context and references as needed to help reviewers and authors understand the context of the problem and solution being proposed. |
| 354 | + |
| 355 | +## References |
| 356 | + |
| 357 | +**\[Optional \- if not applicable omit\]** |
| 358 | + |
| 359 | +Add additional references as needed to help reviewers and authors understand the context of the problem and solution being proposed. |
| 360 | + |
| 361 | +* \<hyper-linked title of an external reference resource\> |
| 362 | + |
| 363 | +## Terminology & Definitions |
| 364 | + |
| 365 | +**Base Container:** Pre-built container with Dynamo and NIXL that serves as foundation for backend-specific builds |
| 366 | + |
| 367 | +**Backend Build:** Container stage that builds backend-specific code and dependencies |
| 368 | + |
| 369 | +**sccache:** Compiler cache tool that speeds up recompilation by caching previous compilation results |
| 370 | + |
| 371 | +**CI Stage:** Container stage with testing tools and validation requirements |
| 372 | + |
| 373 | +**Manylinux:** PEP 513 standard for Linux wheel distribution compatibility |
| 374 | + |
| 375 | +**NIXL:** High-throughput, low-latency point-to-point communication library for accelerating inference |
| 376 | + |
| 377 | +**Runtime Stage:** Minimal container stage with only production deployment requirements |
| 378 | + |
| 379 | +## Acronyms & Abbreviations |
| 380 | + |
| 381 | +**CI:** Continuous Integration |
| 382 | + |
| 383 | +**DEP:** Design Enhancement Proposal |
| 384 | + |
| 385 | +**NIXL:** NVIDIA Inference Exchange Library |
| 386 | + |
| 387 | + |
0 commit comments