diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 6f8b0485f..c0ac2cc45 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -207,23 +207,12 @@ jobs: date: ${{ inputs.date }} package-name: cuopt_sh_client package-type: python - service-container: - if: inputs.build_type == 'nightly' - needs: [wheel-build-cuopt, wheel-build-cuopt-server] - runs-on: ubuntu-latest - steps: - - name: Checkout code repo - uses: actions/checkout@v3 - with: - ref: ${{ inputs.sha }} - fetch-depth: 0 # unshallow fetch for setuptools-scm - persist-credentials: false - - name: build service - env: - GH_TOKEN: ${{ github.token }} - run: | - gh workflow run service_nightly.yaml \ - -f branch=${{ inputs.branch }} \ - -f sha=${{ inputs.sha }} \ - -f date=${{ inputs.date }} \ - -f build_type=${{ inputs.build_type }} + build-images: + needs: [wheel-publish-cuopt, wheel-publish-cuopt-server] + uses: ./.github/workflows/build_test_publish_images.yaml + secrets: inherit + with: + branch: ${{ inputs.branch }} + sha: ${{ inputs.sha }} + date: ${{ inputs.date }} + build_type: ${{ inputs.build_type || 'branch' }} diff --git a/.github/workflows/build_images.yaml b/.github/workflows/build_images.yaml new file mode 100644 index 000000000..796427264 --- /dev/null +++ b/.github/workflows/build_images.yaml @@ -0,0 +1,102 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: Build and push image variant + +on: + workflow_call: + inputs: + ARCHES: + required: true + type: string + CUDA_VER: + required: true + type: string + CUOPT_VER: + required: true + type: string + IMAGE_TAG_PREFIX: + required: true + type: string + LINUX_VER: + required: true + type: string + PYTHON_VER: + required: true + type: string + +jobs: + build: + strategy: + matrix: + ARCH: ["${{ inputs.ARCHES }}"] + CUDA_VER: ["${{ inputs.CUDA_VER }}"] + PYTHON_VER: ["${{ inputs.PYTHON_VER }}"] + LINUX_VER: ["${{ inputs.LINUX_VER }}"] + fail-fast: false + runs-on: "linux-${{ matrix.ARCH }}-cpu4" + steps: + - name: Checkout code repo + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Login to DockerHub + uses: docker/login-action@v3 + with: + username: ${{ secrets.CUOPT_DOCKERHUB_USERNAME }} + password: ${{ secrets.CUOPT_DOCKERHUB_TOKEN }} + - name: Copy License and Version files + run: | + cp ./LICENSE ./ci/docker/context/LICENSE + cp ./VERSION ./ci/docker/context/VERSION + cp ./thirdparty/THIRD_PARTY_LICENSES ./ci/docker/context/THIRD_PARTY_LICENSES + - name: Login to NGC + uses: docker/login-action@v3 + with: + registry: "nvcr.io" + username: "$oauthtoken" + password: ${{ secrets.CUOPT_NGC_DOCKER_KEY }} + - name: Set up Docker Context for Buildx + id: buildx-context + run: | + docker context create builders + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + driver: docker + endpoint: ./ci/docker/context + - name: Trim CUDA and Python versions + id: trim + run: | + echo "CUDA_SHORT=$(echo '${{ inputs.CUDA_VER }}' | sed -E 's/([0-9]+\.[0-9]+)\.[0-9]+/\1/')" >> $GITHUB_OUTPUT + echo "PYTHON_SHORT=$(echo '${{ inputs.PYTHON_VER }}' | sed -E 's/([0-9]+\.[0-9]+)\.[0-9]+/\1/')" >> $GITHUB_OUTPUT + - name: Build image and push to DockerHub and NGC + uses: docker/build-push-action@v6 + with: + context: ./ci/docker/context + file: ./ci/docker/Dockerfile + push: true + pull: true + build-args: | + CUDA_VER=${{ inputs.CUDA_VER }} + PYTHON_SHORT_VER=${{ steps.trim.outputs.PYTHON_SHORT }} + CUOPT_VER=${{ inputs.CUOPT_VER }} + LINUX_VER=${{ inputs.LINUX_VER }} + tags: nvidia/cuopt:${{ inputs.IMAGE_TAG_PREFIX }}-cuda${{ steps.trim.outputs.CUDA_SHORT }}-py${{ steps.trim.outputs.PYTHON_SHORT }}-${{ matrix.ARCH }} + + - name: Push image to NGC + run: | + docker tag nvidia/cuopt:${{ inputs.IMAGE_TAG_PREFIX }}-cuda${{ steps.trim.outputs.CUDA_SHORT }}-py${{ steps.trim.outputs.PYTHON_SHORT }}-${{ matrix.ARCH }} nvcr.io/nvstaging/nvaie/cuopt:${{ inputs.IMAGE_TAG_PREFIX }}-cuda${{ steps.trim.outputs.CUDA_SHORT }}-py${{ steps.trim.outputs.PYTHON_SHORT }}-${{ matrix.ARCH }} + docker push nvcr.io/nvstaging/nvaie/cuopt:${{ inputs.IMAGE_TAG_PREFIX }}-cuda${{ steps.trim.outputs.CUDA_SHORT }}-py${{ steps.trim.outputs.PYTHON_SHORT }}-${{ matrix.ARCH }} diff --git a/.github/workflows/build_test_publish_images.yaml b/.github/workflows/build_test_publish_images.yaml new file mode 100644 index 000000000..aaac5991e --- /dev/null +++ b/.github/workflows/build_test_publish_images.yaml @@ -0,0 +1,185 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: Build, Test and Publish cuopt images + +on: + workflow_call: + inputs: + branch: + type: string + date: + type: string + sha: + type: string + build_type: + type: string + arch: + type: string + default: '["amd64", "arm64"]' + description: 'JSON array of architectures to build for' + cuda_ver: + type: string + default: '["12.8.0"]' + description: 'JSON array of CUDA versions to build for' + python_ver: + type: string + default: '["3.12.11"]' + description: 'JSON array of Python versions to build for' + linux_ver: + type: string + default: '["22.04"]' + description: 'JSON array of Linux versions to build for' + + +defaults: + run: + shell: bash + +permissions: + actions: read + checks: none + contents: read + deployments: none + discussions: none + id-token: write + issues: none + packages: read + pages: none + pull-requests: read + repository-projects: none + security-events: none + statuses: none + +jobs: + compute-matrix: + runs-on: ubuntu-latest + container: + image: rapidsai/ci-conda:25.08-latest + outputs: + MATRIX: ${{ steps.compute-matrix.outputs.MATRIX }} + CUOPT_VER: ${{ steps.compute-cuopt-ver.outputs.CUOPT_VER }} + IMAGE_TAG_PREFIX: ${{ steps.compute-cuopt-ver.outputs.IMAGE_TAG_PREFIX }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 # unshallow fetch for setuptools-scm + persist-credentials: false + + - name: Compute matrix + id: compute-matrix + run: | + MATRIX=$(jq -c '.' <> $GITHUB_OUTPUT + + - name: Install gha-tools + run: | + mkdir -p /tmp/gha-tools + curl -s -L 'https://github.com/rapidsai/gha-tools/releases/latest/download/tools.tar.gz' | tar -xz -C /tmp/gha-tools + echo "/tmp/gha-tools" >> "${GITHUB_PATH}" + + - name: Compute cuopt version + id: compute-cuopt-ver + run: | + ver=$(rapids-generate-version) + # Remove starting 0s from version 25.08.0a18 -> 25.8.0a18 + CUOPT_VER=$(echo "$ver" | sed -E 's/\.0+([0-9])/\.\1/g') + echo "CUOPT_VER=$CUOPT_VER" >> $GITHUB_OUTPUT + if rapids-is-release-build; then + IMAGE_TAG_PREFIX="$CUOPT_VER" + else + IMAGE_TAG_PREFIX=$(echo "$CUOPT_VER" | sed -E 's/([0-9]+\.[0-9]+\.[0-9]+)a.*/\1a/') + fi + echo "IMAGE_TAG_PREFIX=$IMAGE_TAG_PREFIX" >> $GITHUB_OUTPUT + + build-images: + name: Build images + needs: compute-matrix + secrets: inherit + strategy: + matrix: ${{ fromJson(needs.compute-matrix.outputs.MATRIX) }} + uses: ./.github/workflows/build_images.yaml + with: + ARCHES: ${{ matrix.arch }} + CUDA_VER: ${{ matrix.cuda_ver }} + CUOPT_VER: ${{ needs.compute-matrix.outputs.CUOPT_VER }} + IMAGE_TAG_PREFIX: ${{ needs.compute-matrix.outputs.IMAGE_TAG_PREFIX }} + LINUX_VER: ${{ matrix.linux_ver }} + PYTHON_VER: ${{ matrix.python_ver }} + + build-cuopt-multiarch-manifest: + name: Build cuopt multiarch manifest + needs: [build-images, compute-matrix] + strategy: + matrix: + CUDA_VER: ${{ fromJson(needs.compute-matrix.outputs.MATRIX).cuda_ver }} + PYTHON_VER: ${{ fromJson(needs.compute-matrix.outputs.MATRIX).python_ver }} + runs-on: ubuntu-latest + steps: + - name: Checkout code repo + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Login to DockerHub + uses: docker/login-action@v3 + with: + username: ${{ secrets.CUOPT_DOCKERHUB_USERNAME }} + password: ${{ secrets.CUOPT_DOCKERHUB_TOKEN }} + - name: Login to NGC + uses: docker/login-action@v3 + with: + registry: "nvcr.io" + username: "$oauthtoken" + password: ${{ secrets.CUOPT_NGC_DOCKER_KEY }} + - name: Trim CUDA and Python versions + id: trim + run: | + echo "CUDA_SHORT=$(echo '${{ matrix.CUDA_VER }}' | sed -E 's/([0-9]+\.[0-9]+)\.[0-9]+/\1/')" >> $GITHUB_OUTPUT + echo "PYTHON_SHORT=$(echo '${{ matrix.PYTHON_VER }}' | sed -E 's/([0-9]+\.[0-9]+)\.[0-9]+/\1/')" >> $GITHUB_OUTPUT + - name: Create multiarch manifest + shell: bash + env: + CUOPT_VER: ${{ needs.compute-matrix.outputs.CUOPT_VER }} + CUDA_SHORT: ${{ steps.trim.outputs.CUDA_SHORT }} + PYTHON_SHORT: ${{ steps.trim.outputs.PYTHON_SHORT }} + IMAGE_TAG_PREFIX: ${{ needs.compute-matrix.outputs.IMAGE_TAG_PREFIX }} + BUILD_TYPE: ${{ inputs.build_type }} + run: bash ci/docker/create_multiarch_manifest.sh + + test-images: + name: Test images + needs: [build-cuopt-multiarch-manifest, compute-matrix] + secrets: inherit + strategy: + matrix: + CUDA_VER: ${{ fromJson(needs.compute-matrix.outputs.MATRIX).cuda_ver }} + PYTHON_VER: ${{ fromJson(needs.compute-matrix.outputs.MATRIX).python_ver }} + ARCH: ${{ fromJson(needs.compute-matrix.outputs.MATRIX).arch }} + uses: ./.github/workflows/test_images.yaml + with: + ARCH: ${{ matrix.ARCH }} + CUDA_VER: ${{ matrix.CUDA_VER }} + PYTHON_VER: ${{ matrix.PYTHON_VER }} + IMAGE_TAG_PREFIX: ${{ needs.compute-matrix.outputs.IMAGE_TAG_PREFIX }} diff --git a/.github/workflows/service_nightly.yaml b/.github/workflows/service_nightly.yaml deleted file mode 100644 index 94071d1bf..000000000 --- a/.github/workflows/service_nightly.yaml +++ /dev/null @@ -1,201 +0,0 @@ -name: Build Managed service docker, deploy and test - -on: - workflow_dispatch: - inputs: - branch: - type: string - date: - type: string - sha: - type: string - build_type: - type: string - -defaults: - run: - shell: bash - -permissions: - actions: read - checks: none - contents: read - deployments: none - discussions: none - id-token: write - issues: none - packages: read - pages: none - pull-requests: read - repository-projects: none - security-events: none - statuses: none - -jobs: - managed-service-nightly-amd: - name: Managed service nightly build for AMD64 architecture - env: - GH_TOKEN: ${{ github.token }} - RAPIDS_BUILD_TYPE: ${{ inputs.build_type }} - RAPIDS_CUDA_VERSION: "12.5.1" - RAPIDS_PY_VERSION: "3.12" - DOCKER_BUILDKIT: 1 - runs-on: "linux-amd64-cpu4" - steps: - - uses: aws-actions/configure-aws-credentials@v2 - with: - role-to-assume: ${{ vars.AWS_ROLE_ARN }} - aws-region: ${{ vars.AWS_REGION }} - role-duration-seconds: 43200 # 12h - - - name: Checkout code repo - uses: actions/checkout@v4 - with: - ref: ${{ inputs.sha }} - fetch-depth: 0 # unshallow fetch for setuptools-scm - persist-credentials: false - - - name: Standardize repository information - uses: rapidsai/shared-actions/rapids-github-info@main - with: - branch: ${{ inputs.branch }} - date: ${{ inputs.date }} - sha: ${{ inputs.sha }} - - - name: Docker login to nvcr.io - uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1 - with: - registry: "nvcr.io" - username: "$oauthtoken" - password: ${{ secrets.CUOPT_PRD_NGC_DOCKER_KEY }} - - - name: Install aws and python - run: | - set -x - sudo apt-get update -y && sudo apt-get install -y software-properties-common && sudo add-apt-repository -y ppa:deadsnakes/ppa - sudo apt-get install -y awscli python3.12 - - - name: Install GHA tools - run: | - git clone https://github.com/rapidsai/gha-tools.git -b main /tmp/gha-tools - echo "/tmp/gha-tools/tools" >> "${GITHUB_PATH}" - - - name: Download latest artifacts from S3 - run: | - # make rapids-download-wheels-from-github download everything to the same directory - export RAPIDS_UNZIP_DIR="$(pwd)/wheels" - mkdir "${RAPIDS_UNZIP_DIR}" - - # download latest wheels built from build.yaml - RAPIDS_PY_CUDA_SUFFIX="$(rapids-wheel-ctk-name-gen ${RAPIDS_CUDA_VERSION})" - RAPIDS_PY_WHEEL_NAME="cuopt_mps_parser" rapids-download-wheels-from-github python - RAPIDS_PY_WHEEL_NAME="cuopt_${RAPIDS_PY_CUDA_SUFFIX}" rapids-download-wheels-from-github python - RAPIDS_PY_WHEEL_NAME="cuopt_server_${RAPIDS_PY_CUDA_SUFFIX}" rapids-download-wheels-from-github python - RAPIDS_PY_WHEEL_NAME="libcuopt_${RAPIDS_PY_CUDA_SUFFIX}" rapids-download-wheels-from-github cpp - - - name: Generate git commit file for tracking the container - run: | - bash container-builder/bin/make_git_info.sh ./ - - - name: Build cuopt self hosted service docker image - uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671 - with: - context: . - push: true - tags: nvcr.io/j9mrpofbmtxd/test/cuopt:25.08 - file: ci/build-service.Dockerfile - build-args: | - nspect_id="NSPECT-LZ5P-VOVE" - arch=amd - - - name: Push image to prod env - run: | - docker tag nvcr.io/j9mrpofbmtxd/test/cuopt:25.08 nvcr.io/0616513341838337/cuopt:nightly - docker tag nvcr.io/j9mrpofbmtxd/test/cuopt:25.08 nvcr.io/0616513341838337/cuopt:25.08 - - docker push nvcr.io/0616513341838337/cuopt:nightly - docker push nvcr.io/0616513341838337/cuopt:25.08 - - managed-service-nightly-arm: - name: Managed service nightly build for ARM architecture - env: - GH_TOKEN: ${{ github.token }} - RAPIDS_BUILD_TYPE: ${{ inputs.build_type }} - RAPIDS_CUDA_VERSION: "12.5.1" - RAPIDS_PY_VERSION: "3.12" - DOCKER_BUILDKIT: 1 - runs-on: "linux-arm64-cpu4" - steps: - - uses: aws-actions/configure-aws-credentials@v2 - with: - role-to-assume: ${{ vars.AWS_ROLE_ARN }} - aws-region: ${{ vars.AWS_REGION }} - role-duration-seconds: 43200 # 12h - - - name: Checkout code repo - uses: actions/checkout@v4 - with: - ref: ${{ inputs.sha }} - fetch-depth: 0 # unshallow fetch for setuptools-scm - persist-credentials: false - - - name: Standardize repository information - uses: rapidsai/shared-actions/rapids-github-info@main - with: - branch: ${{ inputs.branch }} - date: ${{ inputs.date }} - sha: ${{ inputs.sha }} - - - name: Docker login to nvcr.io - uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1 - with: - registry: "nvcr.io" - username: "$oauthtoken" - password: ${{ secrets.CUOPT_PRD_NGC_DOCKER_KEY }} - - - name: Install aws and python - run: | - set -x - sudo apt-get update -y && sudo apt-get install -y software-properties-common && sudo add-apt-repository -y ppa:deadsnakes/ppa - sudo apt-get install -y awscli python3.12 - - - name: Install GHA tools - run: | - git clone https://github.com/rapidsai/gha-tools.git -b main /tmp/gha-tools - echo "/tmp/gha-tools/tools" >> "${GITHUB_PATH}" - - - name: Download latest artifacts from S3 - run: | - # make rapids-download-wheels-from-github download everything to the same directory - export RAPIDS_UNZIP_DIR="$(pwd)/wheels" - mkdir "${RAPIDS_UNZIP_DIR}" - - # download latest wheels built from build.yaml - RAPIDS_PY_CUDA_SUFFIX="$(rapids-wheel-ctk-name-gen ${RAPIDS_CUDA_VERSION})" - RAPIDS_PY_WHEEL_NAME="cuopt_mps_parser" rapids-download-wheels-from-github python - RAPIDS_PY_WHEEL_NAME="cuopt_${RAPIDS_PY_CUDA_SUFFIX}" rapids-download-wheels-from-github python - RAPIDS_PY_WHEEL_NAME="cuopt_server_${RAPIDS_PY_CUDA_SUFFIX}" rapids-download-wheels-from-github python - RAPIDS_PY_WHEEL_NAME="libcuopt_${RAPIDS_PY_CUDA_SUFFIX}" rapids-download-wheels-from-github cpp - - - name: Generate git commit file for tracking the container - run: | - bash container-builder/bin/make_git_info.sh ./ - - - name: Build cuopt self hosted service docker image - uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671 - with: - context: . - push: true - tags: nvcr.io/j9mrpofbmtxd/test/cuopt:25.08.arm - file: ci/build-service.Dockerfile - build-args: | - nspect_id="NSPECT-LZ5P-VOVE" - arch=arm - - - name: Push image to prod env - run: | - docker tag nvcr.io/j9mrpofbmtxd/test/cuopt:25.08.arm nvcr.io/0616513341838337/cuopt:nightly.arm - docker tag nvcr.io/j9mrpofbmtxd/test/cuopt:25.08.arm nvcr.io/0616513341838337/cuopt:25.08.arm - - docker push nvcr.io/0616513341838337/cuopt:nightly.arm - docker push nvcr.io/0616513341838337/cuopt:25.08.arm diff --git a/.github/workflows/test_images.yaml b/.github/workflows/test_images.yaml new file mode 100644 index 000000000..24e669bf7 --- /dev/null +++ b/.github/workflows/test_images.yaml @@ -0,0 +1,62 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, + +name: Test images + +on: + workflow_call: + inputs: + ARCH: + required: true + type: string + CUDA_VER: + required: true + type: string + PYTHON_VER: + required: true + type: string + IMAGE_TAG_PREFIX: + required: true + type: string + + +jobs: + + prepare: + runs-on: ubuntu-latest + outputs: + CUDA_SHORT: ${{ steps.trim.outputs.CUDA_SHORT }} + PYTHON_SHORT: ${{ steps.trim.outputs.PYTHON_SHORT }} + steps: + - name: Trim versions + id: trim + run: | + CUDA_SHORT=$(echo "${{ inputs.CUDA_VER }}" | sed -E 's/([0-9]+\.[0-9]+)\.[0-9]+/\1/') + PYTHON_SHORT=$(echo "${{ inputs.PYTHON_VER }}" | sed -E 's/([0-9]+\.[0-9]+)\.[0-9]+/\1/') + + echo "CUDA_SHORT=$CUDA_SHORT" >> $GITHUB_OUTPUT + echo "PYTHON_SHORT=$PYTHON_SHORT" >> $GITHUB_OUTPUT + + test: + runs-on: "linux-${{ inputs.ARCH }}-gpu-a100-latest-1" + needs: prepare + container: + image: "nvidia/cuopt:${{ inputs.IMAGE_TAG_PREFIX }}-cuda${{ needs.prepare.outputs.CUDA_SHORT }}-py${{ needs.prepare.outputs.PYTHON_SHORT }}" + options: --user root + steps: + - name: Checkout code repo + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Test cuopt + run: | + bash ./ci/docker/test_image.sh diff --git a/ci/docker/Dockerfile b/ci/docker/Dockerfile new file mode 100644 index 000000000..7810aa4de --- /dev/null +++ b/ci/docker/Dockerfile @@ -0,0 +1,103 @@ +# syntax=docker/dockerfile:1.2 +# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +ARG CUDA_VER=unset +ARG CUOPT_VER=unset +ARG PYTHON_SHORT_VER=unset +ARG LINUX_VER=unset + +FROM nvidia/cuda:${CUDA_VER}-runtime-ubuntu${LINUX_VER} AS cuda-libs + +# Install cuOpt +FROM nvidia/cuda:${CUDA_VER}-base-ubuntu${LINUX_VER} AS python-env + +ARG CUDA_VER +ARG CUOPT_VER +ARG PYTHON_SHORT_VER + +ENV DEBIAN_FRONTEND=noninteractive + +# gcc is required for building psutils +RUN apt-get update && apt-get install -y --no-install-recommends build-essential software-properties-common && \ + add-apt-repository ppa:deadsnakes/ppa && \ + apt-get install -y --no-install-recommends \ + wget \ + unzip \ + gcc \ + python${PYTHON_SHORT_VER} \ + python${PYTHON_SHORT_VER}-dev \ + python${PYTHON_SHORT_VER}-venv \ + && rm -rf /var/lib/apt/lists/* && \ + python${PYTHON_SHORT_VER} -m ensurepip --upgrade && \ + python${PYTHON_SHORT_VER} -m pip install --upgrade pip + +ENV DEBIAN_FRONTEND="" + +RUN ln -sf /usr/bin/python${PYTHON_SHORT_VER} /usr/bin/python && \ + groupadd -r cuopt && \ + useradd -r -g cuopt cuopt && \ + chown -R cuopt:cuopt /usr/local/lib/python${PYTHON_SHORT_VER}/dist-packages + +USER cuopt + +FROM python-env AS install-env + +WORKDIR /home/cuopt + +ARG CUOPT_VER +ARG PYTHON_SHORT_VER + +RUN cuda_suffix=cu$(echo ${CUDA_VER} | cut -d'.' -f1) && \ + cuda_major_minor=$(echo ${CUDA_VER} | cut -d'.' -f1-2) && \ + python -m pip install \ + --extra-index-url https://pypi.nvidia.com \ + --extra-index-url https://pypi.anaconda.org/rapidsai-wheels-nightly/simple \ + --user \ + --no-cache-dir \ + "cuopt-server-${cuda_suffix}==${CUOPT_VER}" \ + "cuopt-sh-client==${CUOPT_VER}" \ + "nvidia-cuda-runtime-${cuda_suffix}==${cuda_major_minor}.*" && \ + python -m pip list + +USER root + +# Remove gcc to save space, gcc was required for building psutils +RUN apt-get purge -y gcc && rm -rf /var/lib/apt/lists/* + +USER cuopt + +COPY ./LICENSE /home/cuopt/LICENSE +COPY ./VERSION /home/cuopt/VERSION +COPY ./THIRD_PARTY_LICENSES /home/cuopt/THIRD_PARTY_LICENSES + +FROM install-env AS cuopt-final + +ARG PYTHON_SHORT_VER + +# Set environment variables in .bashrc for all future shells +RUN echo 'export PATH="/usr/local/cuda/bin:/usr/bin:/usr/local/bin:/usr/local/nvidia/bin/:/home/cuopt/.local/lib/python${PYTHON_SHORT_VER}/dist-packages/libcuopt/bin/:/home/cuopt/.local/bin:$PATH"' >> /home/cuopt/.bashrc && \ + echo 'export LD_LIBRARY_PATH="/usr/lib/x86_64-linux-gnu:/usr/lib/aarch64-linux-gnu:/usr/local/cuda/lib64:/usr/local/nvidia/lib:/usr/local/nvidia/lib64:/usr/lib/wsl/lib:/usr/lib/wsl/lib/libnvidia-container:/usr/lib/nvidia:/usr/lib/nvidia-current:/home/cuopt/.local/lib/python${PYTHON_SHORT_VER}/dist-packages/libcuopt/lib/:/home/cuopt/.local/lib/python${PYTHON_SHORT_VER}/dist-packages/rapids_logger/lib64:${LD_LIBRARY_PATH}"' >> /home/cuopt/.bashrc + + +# Create a .bash_profile that sources .bashrc if it exists +RUN echo 'if [ -f ~/.bashrc ]; then . ~/.bashrc; fi' > /home/cuopt/.bash_profile + +COPY --from=cuda-libs /usr/local/cuda/lib64/libnvrtc* /usr/local/cuda/lib64/ +COPY --from=cuda-libs /usr/local/cuda/lib64/libnvJitLink* /usr/local/cuda/lib64/ + +# Use a shell as entrypoint to handle both service and interactive modes +ENTRYPOINT ["/bin/bash", "-c"] +CMD ["python -m cuopt_server.cuopt_service"] diff --git a/ci/docker/README.md b/ci/docker/README.md new file mode 100644 index 000000000..0645da072 --- /dev/null +++ b/ci/docker/README.md @@ -0,0 +1,13 @@ +# Container Image build and test suite + +## context + +Add all the files and data for the buildx context to ``context`` folder like entrypoint script, and others. + +## test + +To test the container image, run the [test_image.sh](test_image.sh) script as shown below from the latest github repo: + +```bash +docker run -it --rm --gpus all -u root --volume $PWD:/repo -w /repo --entrypoint "/bin/bash" nvidia/cuopt:[TAG] ./ci/docker/test_image.sh +``` \ No newline at end of file diff --git a/ci/docker/context/README.md b/ci/docker/context/README.md new file mode 100644 index 000000000..84191ae9e --- /dev/null +++ b/ci/docker/context/README.md @@ -0,0 +1 @@ +Place holder for docker context. \ No newline at end of file diff --git a/ci/docker/create_multiarch_manifest.sh b/ci/docker/create_multiarch_manifest.sh new file mode 100644 index 000000000..a579e9e32 --- /dev/null +++ b/ci/docker/create_multiarch_manifest.sh @@ -0,0 +1,104 @@ +#!/bin/bash + +# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -euo pipefail + +# Function to check if a Docker image exists in the registry +check_image_exists() { + local image=$1 + echo "Checking if image exists: $image" + + # Try to pull the image manifest to check if it exists + if docker manifest inspect "$image" >/dev/null 2>&1; then + echo "✓ Image exists: $image" + return 0 + else + echo "✗ Image does not exist: $image" + return 1 + fi +} + +# Function to create manifest with error checking +create_manifest() { + local manifest_name=$1 + local amd64_image=$2 + local arm64_image=$3 + + echo "Creating manifest: $manifest_name" + + # Check if both architecture images exist + if ! check_image_exists "$amd64_image"; then + echo "Error: AMD64 image not found: $amd64_image" + return 1 + fi + + if ! check_image_exists "$arm64_image"; then + echo "Error: ARM64 image not found: $arm64_image" + return 1 + fi + + # Create the manifest + echo "Creating multi-arch manifest..." + docker manifest create --amend "$manifest_name" "$amd64_image" "$arm64_image" + + # Annotate with architecture information + echo "Annotating ARM64 architecture..." + docker manifest annotate "$manifest_name" "$arm64_image" --arch arm64 + + echo "Annotating AMD64 architecture..." + docker manifest annotate "$manifest_name" "$amd64_image" --arch amd64 + + # Push the manifest + echo "Pushing manifest: $manifest_name" + docker manifest push "$manifest_name" + + echo "✓ Successfully created and pushed manifest: $manifest_name" +} + +# Create manifest for dockerhub and nvstaging +echo "=== Creating Docker Hub manifests ===" +create_manifest \ + "nvidia/cuopt:${IMAGE_TAG_PREFIX}-cuda${CUDA_SHORT}-py${PYTHON_SHORT}" \ + "nvidia/cuopt:${IMAGE_TAG_PREFIX}-cuda${CUDA_SHORT}-py${PYTHON_SHORT}-amd64" \ + "nvidia/cuopt:${IMAGE_TAG_PREFIX}-cuda${CUDA_SHORT}-py${PYTHON_SHORT}-arm64" + +echo "=== Creating NVCR staging manifests ===" +create_manifest \ + "nvcr.io/nvstaging/nvaie/cuopt:${IMAGE_TAG_PREFIX}-cuda${CUDA_SHORT}-py${PYTHON_SHORT}" \ + "nvcr.io/nvstaging/nvaie/cuopt:${IMAGE_TAG_PREFIX}-cuda${CUDA_SHORT}-py${PYTHON_SHORT}-amd64" \ + "nvcr.io/nvstaging/nvaie/cuopt:${IMAGE_TAG_PREFIX}-cuda${CUDA_SHORT}-py${PYTHON_SHORT}-arm64" + +# Only create latest manifests for release builds +if [[ "${BUILD_TYPE}" == "release" ]]; then + echo "=== Creating latest manifests for release build ===" + + echo "Creating Docker Hub latest manifest..." + create_manifest \ + "nvidia/cuopt:latest-cuda${CUDA_SHORT}-py${PYTHON_SHORT}" \ + "nvidia/cuopt:${IMAGE_TAG_PREFIX}-cuda${CUDA_SHORT}-py${PYTHON_SHORT}-amd64" \ + "nvidia/cuopt:${IMAGE_TAG_PREFIX}-cuda${CUDA_SHORT}-py${PYTHON_SHORT}-arm64" + + echo "Creating NVCR staging latest manifest..." + create_manifest \ + "nvcr.io/nvstaging/nvaie/cuopt:latest-cuda${CUDA_SHORT}-py${PYTHON_SHORT}" \ + "nvcr.io/nvstaging/nvaie/cuopt:${IMAGE_TAG_PREFIX}-cuda${CUDA_SHORT}-py${PYTHON_SHORT}-amd64" \ + "nvcr.io/nvstaging/nvaie/cuopt:${IMAGE_TAG_PREFIX}-cuda${CUDA_SHORT}-py${PYTHON_SHORT}-arm64" +else + echo "Skipping latest manifest creation (BUILD_TYPE=${BUILD_TYPE}, not 'release')" +fi + +echo "=== Multi-architecture manifest creation completed ===" \ No newline at end of file diff --git a/ci/docker/test_image.sh b/ci/docker/test_image.sh new file mode 100644 index 000000000..1a3272cdc --- /dev/null +++ b/ci/docker/test_image.sh @@ -0,0 +1,59 @@ +#!/bin/bash + +# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -euo pipefail + +chsh -s /bin/bash cuopt + +# Install dependencies +apt-get update +DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends file bzip2 + +# Download test data +bash datasets/linear_programming/download_pdlp_test_dataset.sh +bash datasets/mip/download_miplib_test_dataset.sh +pushd ./datasets +./get_test_data.sh --solomon +./get_test_data.sh --tsp +popd + +# Create symlink to cuopt +ln -sf "$(pwd)" /home/cuopt/cuopt + +# Set permissions since the repo is mounted on root +chmod -R a+w "$(pwd)" + +# If this script is being run as root, use 'su - cuopt -c ""' to run each command as cuopt. + +# Change to cuopt home directory and then to cuopt repo +cat > /home/cuopt/test.sh <