Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 12 additions & 5 deletions Dockerfile.QA
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ ARG IGPU_BUILD
# Ensure apt-get won't prompt for selecting options
ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update && \
RUN --mount=type=secret,id=apt_sources,target=/etc/apt/sources.list.d/nvidia-artifactory-ubuntu.list,required=false \
apt-get update && \
apt-get install -y --no-install-recommends \
build-essential \
libarchive-dev \
Expand Down Expand Up @@ -177,7 +178,9 @@ RUN mkdir -p qa/custom_models/custom_sequence_int32/1 && \

# L0_lifecycle needs No-GPU build of identity backend.
WORKDIR /workspace/tritonbuild/identity
RUN rm -rf install build && mkdir build && cd build && \
RUN --mount=type=secret,id=gitconfig,target=/run/secrets/gitconfig,required=false \
export GIT_CONFIG_GLOBAL=/run/secrets/gitconfig && \
rm -rf install build && mkdir build && cd build && \
cmake -DTRITON_ENABLE_GPU=OFF \
-DCMAKE_INSTALL_PREFIX:PATH=/workspace/tritonbuild/identity/install \
-DTRITON_REPO_ORGANIZATION:STRING=${TRITON_REPO_ORGANIZATION} \
Expand All @@ -190,7 +193,9 @@ RUN rm -rf install build && mkdir build && cd build && \
# L0_backend_python test require triton_shm_monitor
ARG TRITON_BOOST_URL="https://archives.boost.io/release/1.80.0/source/boost_1_80_0.tar.gz"
WORKDIR /workspace/tritonbuild/python
RUN rm -rf install build && mkdir build && cd build && \
RUN --mount=type=secret,id=gitconfig,target=/run/secrets/gitconfig,required=false \
export GIT_CONFIG_GLOBAL=/run/secrets/gitconfig && \
rm -rf install build && mkdir build && cd build && \
cmake -DCMAKE_INSTALL_PREFIX:PATH=/workspace/tritonbuild/python/install \
-DTRITON_REPO_ORGANIZATION:STRING=${TRITON_REPO_ORGANIZATION} \
-DTRITON_COMMON_REPO_TAG:STRING=${TRITON_COMMON_REPO_TAG} \
Expand Down Expand Up @@ -316,7 +321,8 @@ ARG TARGETPLATFORM
ENV DEBIAN_FRONTEND=noninteractive

# install platform specific packages
RUN if grep -qE '^VERSION_ID="(18\.04|20\.04|22\.04|24\.04)' /etc/os-release; then \
RUN --mount=type=secret,id=apt_sources,target=/etc/apt/sources.list.d/nvidia-artifactory-ubuntu.list,required=false \
if grep -qE '^VERSION_ID="(18\.04|20\.04|22\.04|24\.04)' /etc/os-release; then \
apt-get update && \
apt-get install -y --no-install-recommends \
libpng-dev && \
Expand All @@ -329,7 +335,8 @@ RUN if grep -qE '^VERSION_ID="(18\.04|20\.04|22\.04|24\.04)' /etc/os-release; th

# CI/QA for memcheck requires valgrind
# libarchive-dev is required by Python backend
RUN apt-get update && apt-get install -y --no-install-recommends \
RUN --mount=type=secret,id=apt_sources,target=/etc/apt/sources.list.d/nvidia-artifactory-ubuntu.list,required=false \
apt-get update && apt-get install -y --no-install-recommends \
curl \
gdb \
libarchive-dev \
Expand Down
15 changes: 12 additions & 3 deletions Dockerfile.sdk
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,9 @@ COPY TRITON_VERSION .
COPY ${TRITON_CLIENT_REPO_SUBDIR} client

WORKDIR /workspace/client_build
RUN cmake -DCMAKE_INSTALL_PREFIX=/workspace/install \
RUN --mount=type=secret,id=gitconfig,target=/run/secrets/gitconfig,required=false \
export GIT_CONFIG_GLOBAL=/run/secrets/gitconfig && \
cmake -DCMAKE_INSTALL_PREFIX=/workspace/install \
-DTRITON_VERSION=`cat /workspace/TRITON_VERSION` \
-DTRITON_REPO_ORGANIZATION=${TRITON_REPO_ORGANIZATION} \
-DTRITON_COMMON_REPO_TAG=${TRITON_COMMON_REPO_TAG} \
Expand All @@ -138,11 +140,15 @@ RUN cmake -DCMAKE_INSTALL_PREFIX=/workspace/install \
-DTRITON_ENABLE_EXAMPLES=ON -DTRITON_ENABLE_TESTS=ON \
-DTRITON_ENABLE_GPU=${TRITON_ENABLE_GPU} /workspace/client
RUN --mount=type=secret,id=maven_settings,target=/run/secrets/maven_settings,required=false \
--mount=type=secret,id=gitconfig,target=/run/secrets/gitconfig,required=false \
export GIT_CONFIG_GLOBAL=/run/secrets/gitconfig && \
if [ -f /run/secrets/maven_settings ]; then export MAVEN_ARGS="--settings /run/secrets/maven_settings"; fi && \
cmake --build . -v --parallel ${TRITON_CLIENT_BUILD_PARALLEL} --target cc-clients java-clients python-clients

# Install Java API Bindings
RUN --mount=type=secret,id=maven_settings,target=/run/secrets/maven_settings,required=false \
--mount=type=secret,id=gitconfig,target=/run/secrets/gitconfig,required=false \
export GIT_CONFIG_GLOBAL=/run/secrets/gitconfig && \
if [ -f /run/secrets/maven_settings ]; then export MAVEN_ARGS="--settings /run/secrets/maven_settings"; fi && \
if [ "$TARGETPLATFORM" = "linux/amd64" ]; then \
source /workspace/client/src/java-api-bindings/scripts/install_dependencies_and_build.sh \
Expand All @@ -167,7 +173,8 @@ ARG TRITON_CORE_REPO_TAG
ARG TARGETPLATFORM
ARG TRITON_ENABLE_GPU

RUN apt-get update && \
RUN --mount=type=secret,id=apt_sources,target=/etc/apt/sources.list.d/nvidia-artifactory-ubuntu.list,required=false \

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking for the SDK image — this is the only apt step that got the mount.

The sdk_build stage's apt step (line 64) and the DCGM install in this runtime stage (line 256) have no apt_sources mount. On a mirror-only network docker build -f Dockerfile.sdk fails on the first stage, before ever reaching this line.

Same fix as build.py's mount_apt_sources_secret: add --mount=type=secret,id=apt_sources,target=/etc/apt/sources.list.d/nvidia-artifactory-ubuntu.list,required=false to lines 64 and 256.

apt-get update && \
apt-get install -y --no-install-recommends \
curl \
default-jdk \
Expand Down Expand Up @@ -257,7 +264,9 @@ RUN rm -f /usr/bin/python && \
# Install Model Analyzer
ARG TRITON_MODEL_ANALYZER_REPO_TAG
ARG TRITON_MODEL_ANALYZER_REPO="${TRITON_REPO_ORGANIZATION}/model_analyzer@${TRITON_MODEL_ANALYZER_REPO_TAG}"
RUN pip3 install "git+${TRITON_MODEL_ANALYZER_REPO}"
RUN --mount=type=secret,id=gitconfig,target=/run/secrets/gitconfig,required=false \
export GIT_CONFIG_GLOBAL=/run/secrets/gitconfig && \
pip3 install "git+${TRITON_MODEL_ANALYZER_REPO}"

# Entrypoint Banner
ENV NVIDIA_PRODUCT_NAME="Triton Server SDK"
Expand Down
196 changes: 172 additions & 24 deletions build.py
Original file line number Diff line number Diff line change
Expand Up @@ -263,10 +263,10 @@

def cmd(self, clist, check_exitcode=False):
if isinstance(clist, str):
self._file.write(f"{clist}\n")

Check failure

Code scanning / CodeQL

Clear-text storage of sensitive information High

This expression stores
sensitive data (secret)
as clear text.
This expression stores
sensitive data (secret)
as clear text.
This expression stores
sensitive data (secret)
as clear text.
This expression stores
sensitive data (secret)
as clear text.
This expression stores
sensitive data (secret)
as clear text.
else:
for c in clist:
self._file.write(f"{c} ")

Check failure

Code scanning / CodeQL

Clear-text storage of sensitive information High

This expression stores
sensitive data (secret)
as clear text.
This expression stores
sensitive data (secret)
as clear text.
This expression stores
sensitive data (secret)
as clear text.
This expression stores
sensitive data (secret)
as clear text.
This expression stores
sensitive data (secret)
as clear text.
self.blankln()

def cwd(self, path):
Expand Down Expand Up @@ -878,6 +878,122 @@
)


# Keys that 'docker build --secret' accepts. A spec may also carry keys that
# only the Dockerfile side understands ('target', 'required', 'mode', ...);
# passing those to the build command is an error, so they are filtered out.
DOCKER_BUILD_SECRET_KEYS = ("id", "src", "source", "env", "type")

# Id whose secret is mounted over the apt source list, and where it lands when
# the spec does not say.
APT_SOURCES_SECRET_ID = "apt_sources"
APT_SOURCES_DEFAULT_TARGET = "/etc/apt/sources.list.d/nvidia-artifactory-ubuntu.list"

# Variable holding a git config, and where the build container writes it out.
# The container is handed the contents rather than the file because docker
# resolves a bind mount source on the daemon, which need not share a filesystem
# with this script, and silently mounts a directory when it finds nothing there.
GIT_CONFIG_CONTENT_ENV = "TRITON_GITCONFIG"
GIT_CONFIG_CONTAINER_PATH = "/tmp/gitconfig"


def parse_secret_spec(spec):
"""Split a Docker secret spec into ordered (key, value) pairs.

Docker does not require any particular field order, so callers look up
keys by name rather than by position.
"""
fields = []
for field in spec.split(","):
key, _, value = field.partition("=")
key = key.strip()
if key:
fields.append((key, value.strip()))
return fields


def secret_spec_value(spec, name):
"""Return the value of one field of a secret spec, or "" when unset."""
for key, value in parse_secret_spec(spec):
if key == name:
return value
return ""


def secret_spec_id(spec):
"""Return the 'id' of a Docker secret spec, or "" when it has none."""
return secret_spec_value(spec, "id")


def declared_secret_specs():
"""Every --docker-build-secret spec, in the order it was given."""
return [spec for spec in getattr(FLAGS, "docker_build_secret", None) or [] if spec]


def declared_secret_ids():
"""Ids of every secret supplied with --docker-build-secret."""
return {i for i in (secret_spec_id(s) for s in declared_secret_specs()) if i}


def secret_build_args():
"""Return the '--secret' arguments to forward to 'docker build'.

Only the keys the build command understands are forwarded. Keys that
belong to the Dockerfile mount are dropped here and applied where the
Dockerfile is generated instead.
"""
args = []
for spec in declared_secret_specs():
fields = [
"{}={}".format(key, value)
for key, value in parse_secret_spec(spec)
if key in DOCKER_BUILD_SECRET_KEYS
]
if fields:
args.append("--secret {}".format(",".join(fields)))
return args


def apt_sources_secret_mount():
"""Return the RUN mount flag that overlays the apt source list, or an
empty string when no such secret was requested.

The mount point comes from the spec's 'target' when it has one, so
'--docker-build-secret id=apt_sources,src=<path>,target=<path>' controls
where the list lands. Without a 'target' it defaults to the NVIDIA
Artifactory source list. When the secret is absent this returns "" and the
generated Dockerfiles are byte-identical to a build without this feature.
"""
for spec in declared_secret_specs():
if secret_spec_id(spec) != APT_SOURCES_SECRET_ID:
continue
target = secret_spec_value(spec, "target") or APT_SOURCES_DEFAULT_TARGET
return "--mount=type=secret,id={},target={},required=false ".format(
APT_SOURCES_SECRET_ID, target
)
return ""


def mount_apt_sources_secret(df):
"""Prefix every 'RUN apt-get update' in df with the apt source list mount.

A secret mount is scoped to a single RUN instruction, so every apt step
that should resolve through the mirror needs its own.

The first step of the build base is included even though it is the step
that installs ca-certificates. On a base image that already carries them
the step resolves through the mirror like any other. On one that does not,
apt reports the failed TLS handshake, still exits 0, and installs from the
distribution repositories, so the step behaves as it did before the mount
was added.
"""
mount = apt_sources_secret_mount()
if not mount:
return df

marker = "RUN apt-get update"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking — the DCGM step never receives the mount, so a GPU build fails on a mirror-only network.

This matches only the literal string RUN apt-get update. install_dcgm_libraries() emits apt install / apt update -qq, never RUN apt-get update, so it is silently skipped. In the generated build/Dockerfile.buildbase and build/Dockerfile the DCGM step runs apt update -qq && apt install ... datacenter-gpu-manager-4-core with no --mount=type=secret. On the exact network this flag exists for — only the authenticated mirror reachable — --enable-gpu dies there.

The flag help and docs/customization_guide/build.md both claim every apt step is covered; today it's only the ones spelled apt-get update.

Suggest matching both spellings, e.g. a regex over RUN apt(-get)? update, and asserting the substitution count is non-zero so a future rename fails loudly instead of silently.

return df.replace(marker, "RUN " + mount + "apt-get update")


def create_dockerfile_buildbase_rhel(ddir, dockerfile_name, argmap):
df = """
ARG TRITON_VERSION={}
Expand Down Expand Up @@ -1131,8 +1247,10 @@
ENTRYPOINT []
"""

df = mount_apt_sources_secret(df)

with open(os.path.join(ddir, dockerfile_name), "w") as dfile:
dfile.write(df)

Check failure

Code scanning / CodeQL

Clear-text storage of sensitive information High

This expression stores
sensitive data (secret)
as clear text.
This expression stores
sensitive data (secret)
as clear text.
This expression stores
sensitive data (secret)
as clear text.
This expression stores
sensitive data (secret)
as clear text.
This expression stores
sensitive data (secret)
as clear text.
This expression stores
sensitive data (secret)
as clear text.
This expression stores
sensitive data (secret)
as clear text.


def create_dockerfile_cibase(ddir, dockerfile_name, argmap):
Expand Down Expand Up @@ -1259,8 +1377,10 @@
ldconfig

"""
df = mount_apt_sources_secret(df)

with open(os.path.join(ddir, dockerfile_name), "w") as dfile:
dfile.write(df)

Check failure

Code scanning / CodeQL

Clear-text storage of sensitive information High

This expression stores
sensitive data (secret)
as clear text.
This expression stores
sensitive data (secret)
as clear text.
This expression stores
sensitive data (secret)
as clear text.
This expression stores
sensitive data (secret)
as clear text.
This expression stores
sensitive data (secret)
as clear text.
This expression stores
sensitive data (secret)
as clear text.
This expression stores
sensitive data (secret)
as clear text.
This expression stores
sensitive data (secret)
as clear text.
This expression stores
sensitive data (secret)
as clear text.
This expression stores
sensitive data (secret)
as clear text.
This expression stores
sensitive data (secret)
as clear text.


def dockerfile_prepare_container_linux(argmap, backends, enable_gpu, target_machine):
Expand Down Expand Up @@ -1683,6 +1803,8 @@

baseargs += ["--cache-from={}".format(k) for k in cachefrommap]

baseargs += secret_build_args()

baseargs += ["."]

docker_script.cwd(THIS_SCRIPT_DIR)
Expand Down Expand Up @@ -1729,6 +1851,19 @@
# or explicit --release-version). Dev / pre-release builds leave it
# unset so build_wheel.py reads the in-tree TRITON_VERSION file and
# takes the PEP 817 variant path.
# Authenticate the GitHub clones cmake_build performs in this container.
# It runs the build rather than an image build, so a build secret cannot
# reach it, and a bind mount cannot either: docker resolves the source
# path on the daemon, which need not share the filesystem this script
# runs on, and silently creates a directory when it does not find it.
# Hand the container the contents instead and let it write the file.
#
# Passed by name rather than as NAME=value, so the contents, which carry
# a credential, stay out of this script, which CI publishes as a build
# artifact.
if os.environ.get(GIT_CONFIG_CONTENT_ENV):
runargs += ["-e", GIT_CONFIG_CONTENT_ENV]

if "TRITON_RELEASE_VERSION" in os.environ:
runargs += [
"-e",
Expand All @@ -1745,7 +1880,23 @@

runargs += ["tritonserver_buildbase"]

runargs += ["./cmake_build"]
# Write the git config out inside the container, where the contents
# arrived as an environment variable, and point git at it for the build
# only. Single quoted so the outer shell leaves the expansion to the
# container, whose environment is the one holding the value.
if os.environ.get(GIT_CONFIG_CONTENT_ENV):
runargs += [
"bash",
"-c",
'\'printf "%s" "${}" > {} && export GIT_CONFIG_GLOBAL={} && '
"./cmake_build'".format(
GIT_CONFIG_CONTENT_ENV,
GIT_CONFIG_CONTAINER_PATH,
GIT_CONFIG_CONTAINER_PATH,
),
]
else:
runargs += ["./cmake_build"]

# Remove existing tritonserver_builder container...
docker_script._file.write(
Expand Down Expand Up @@ -1785,14 +1936,7 @@
"docker",
"build",
]
if secrets:
finalargs += [
f"--secret id=req,src={requirements}",
"--secret id=VLLM_INDEX_URL",
"--secret id=PYTORCH_TRITON_URL",
"--secret id=NVPL_SLIM_URL",
f"--build-arg BUILD_PUBLIC_VLLM={build_public_vllm}",
]
finalargs += secret_build_args()
finalargs += [
"-t",
"tritonserver",
Expand Down Expand Up @@ -2623,15 +2767,25 @@
help="This flag sets the Python version for RHEL platform of Triton Inference Server to be built. Default: the latest supported version.",
)
parser.add_argument(
"--build-secret",
"--docker-build-secret",
Comment thread
mc-nv marked this conversation as resolved.
action="append",
required=False,
nargs=2,
metavar=("key", "value"),
help="Add build secrets in the form of <key> <value>. These secrets are used during the build process for vllm. The secrets are passed to the Docker build step as `--secret id=<key>`. The following keys are expected and their purposes are described below:\n\n"
" - 'req': A file containing a list of dependencies for pip (e.g., requirements.txt).\n"
" - 'build_public_vllm': A flag (default is 'true') indicating whether to build the public VLLM version.\n\n"
"Ensure that the required environment variables for these secrets are set before running the build.",
metavar="spec",
help="Pass a build secret to 'docker build' using Docker's own syntax. The spec is forwarded "
"unchanged, so every form 'docker build --secret' accepts is supported:\n\n"
" - 'id=<id>,src=<path>' read the secret from a file (source= is an accepted alias)\n"
" - 'id=<id>,env=<var>' read the secret from an environment variable\n"
" - 'id=<id>' read the environment variable of the same name\n\n"
"May be repeated. The secret is available to a Dockerfile step that mounts it with "
"'RUN --mount=type=secret,id=<id>', and never becomes part of an image layer.\n\n"
"A spec may additionally carry 'target=<path>'. Docker rejects that key on the command line "
"because it belongs to the Dockerfile mount, so it is stripped from the build command and "
"applied where the Dockerfile is generated.\n\n"
"The id 'apt_sources' carries extra meaning: it is also mounted for the duration of each apt step "
"in the generated Dockerfile and Dockerfile.buildbase, so package installs resolve through a "
"package mirror. It lands on 'target' when the spec sets one, and on "
"/etc/apt/sources.list.d/nvidia-artifactory-ubuntu.list otherwise. When the secret is omitted the "
"generated Dockerfiles are unchanged and apt uses the distribution repositories.",
)
parser.add_argument(
"--triton-wheels-dependencies-group",
Expand Down Expand Up @@ -2666,8 +2820,8 @@
FLAGS.override_backend_cmake_arg = []
if FLAGS.extra_backend_cmake_arg is None:
FLAGS.extra_backend_cmake_arg = []
if FLAGS.build_secret is None:
FLAGS.build_secret = []
if FLAGS.docker_build_secret is None:
FLAGS.docker_build_secret = []

FLAGS.boost_url = os.getenv(
"TRITON_BOOST_URL",
Expand Down Expand Up @@ -2777,12 +2931,6 @@
)
backends["python"] = backends["vllm"]

secrets = dict(getattr(FLAGS, "build_secret", []))
if secrets:
requirements = secrets.get("req", "")
build_public_vllm = secrets.get("build_public_vllm", "true")
log('Build Arg for BUILD_PUBLIC_VLLM: "{}"'.format(build_public_vllm))

# Initialize map of repo agents to build and repo-tag for each.
repoagents = {}
for be in FLAGS.repoagent:
Expand Down
Loading
Loading