From ebb2cf1cdbbcf95b7f9d6369f7472d42103dd5ec Mon Sep 17 00:00:00 2001 From: Victor Skvortsov Date: Mon, 15 Jun 2026 13:54:37 +0500 Subject: [PATCH] Separate Docker and VM base image versions --- .github/workflows/build-artifacts.yml | 10 +- .github/workflows/docker.yml | 200 +---------------- .github/workflows/vm-images.yml | 203 ++++++++++++++++++ .../_internal/core/backends/aws/resources.py | 4 +- .../_internal/core/backends/azure/compute.py | 7 +- .../_internal/core/backends/gcp/compute.py | 6 +- .../_internal/core/backends/oci/resources.py | 6 +- .../server/services/backends/provisioning.py | 16 +- .../services/jobs/configurators/base.py | 2 +- src/dstack/_internal/settings.py | 11 +- src/dstack/version.py | 5 +- .../core/backends/aws/test_resources.py | 4 +- .../core/backends/azure/test_compute.py | 8 +- .../pipeline_tasks/test_running_jobs.py | 8 +- .../_internal/server/routers/test_runs.py | 2 +- .../services/backends/test_provisioning.py | 36 ++-- 16 files changed, 273 insertions(+), 255 deletions(-) create mode 100644 .github/workflows/vm-images.yml diff --git a/.github/workflows/build-artifacts.yml b/.github/workflows/build-artifacts.yml index 306e543fe8..d642a81117 100644 --- a/.github/workflows/build-artifacts.yml +++ b/.github/workflows/build-artifacts.yml @@ -216,12 +216,14 @@ jobs: VERSION=${{ inputs.version }} IS_RELEASE=True fi - BASE_IMAGE=$(cat src/dstack/version.py | grep "base_image = ") - BASE_IMAGE_UBUNTU_VERSION=$(cat src/dstack/version.py | grep "base_image_ubuntu_version = ") + DOCKER_BASE_IMAGE=$(cat src/dstack/version.py | grep "docker_base_image = ") + DOCKER_BASE_IMAGE_UBUNTU_VERSION=$(cat src/dstack/version.py | grep "docker_base_image_ubuntu_version = ") + VM_BASE_IMAGE=$(cat src/dstack/version.py | grep "vm_base_image = ") echo "__version__ = \"$VERSION\"" > src/dstack/version.py echo "__is_release__ = $IS_RELEASE" >> src/dstack/version.py - echo $BASE_IMAGE >> src/dstack/version.py - echo $BASE_IMAGE_UBUNTU_VERSION >> src/dstack/version.py + echo $DOCKER_BASE_IMAGE >> src/dstack/version.py + echo $DOCKER_BASE_IMAGE_UBUNTU_VERSION >> src/dstack/version.py + echo $VM_BASE_IMAGE >> src/dstack/version.py cp README.md src uv build - uses: actions/upload-artifact@v4 diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index bb9abe8f00..2b593144bd 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -1,5 +1,5 @@ -name: Build Docker & cloud images -run-name: Build Docker & cloud images ${{ inputs.image_version }}${{ inputs.staging && ' (staging)' || '' }} +name: Build Docker images +run-name: Build Docker images ${{ inputs.image_version }}${{ inputs.staging && ' (staging)' || '' }} on: workflow_dispatch: @@ -11,35 +11,12 @@ on: description: "Build staging images" type: boolean default: false - build_docker: - description: "Build Docker images" - type: boolean - default: true - build_aws: - description: "Build AWS images" - type: boolean - default: true - build_azure: - description: "Build Azure images" - type: boolean - default: true - build_gcp: - description: "Build GCP images" - type: boolean - default: true - build_oci: - description: "Build OCI images" - type: boolean - default: true env: - PACKER_VERSION: "1.9.2" BUILD_DOCKER_REPO: ${{ inputs.staging && 'base-stgn' || 'base' }} - VM_IMAGE_BUILD_PREFIX: ${{ inputs.staging && format('stgn-{0}-', github.run_number) || '' }} # staging ? prefix : '' jobs: build-docker: - if: inputs.build_docker defaults: run: working-directory: docker @@ -83,176 +60,3 @@ jobs: --provenance=false \ --push \ -f $FILE . - - build-aws-images: - needs: build-docker - if: always() && inputs.build_aws && (needs.build-docker.result == 'success' || needs.build-docker.result == 'skipped') - defaults: - run: - working-directory: scripts/packer - runs-on: ubuntu-latest - env: - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - strategy: - matrix: - variant: ["", "-cuda"] - steps: - - uses: actions/checkout@v4 - - name: Download packer - run: | - wget https://releases.hashicorp.com/packer/${{ env.PACKER_VERSION }}/packer_${{ env.PACKER_VERSION }}_linux_amd64.zip - unzip packer_${{ env.PACKER_VERSION }}_linux_amd64.zip - chmod +x packer - - name: Run packer - run: | - ./packer build -var-file=versions.json $PROD_VARS -var image_version=${{ inputs.image_version }} -var build_prefix=$VM_IMAGE_BUILD_PREFIX aws-image${{ matrix.variant }}.json - env: - PROD_VARS: ${{ !inputs.staging && '-var-file=aws-vars-prod.json' || '' }} # production ? var-file : '' - - build-azure-images: - needs: build-docker - if: always() && inputs.build_azure && (needs.build-docker.result == 'success' || needs.build-docker.result == 'skipped') - defaults: - run: - working-directory: scripts/packer - runs-on: ubuntu-latest - env: - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - VERSION: ${{ github.run_number }} - strategy: - matrix: - variant: ["", "-cuda", "-grid"] - steps: - - uses: actions/checkout@v4 - - uses: Azure/login@v2 - name: Log in to az - with: - creds: '{"clientId":"${{ secrets.AZURE_CLIENT_ID }}","clientSecret":"${{ secrets.AZURE_CLIENT_SECRET }}","subscriptionId":"${{ secrets.AZURE_SUBSCRIPTION_ID }}","tenantId":"${{ secrets.AZURE_TENANT_ID }}"}' - - name: Download packer - run: | - wget https://releases.hashicorp.com/packer/${{ env.PACKER_VERSION }}/packer_${{ env.PACKER_VERSION }}_linux_amd64.zip - unzip packer_${{ env.PACKER_VERSION }}_linux_amd64.zip - chmod +x packer - - name: Run packer - run: | - ./packer build -var-file=versions.json -var image_version=${{ inputs.image_version }} -var build_prefix=$VM_IMAGE_BUILD_PREFIX azure-image${{ matrix.variant }}.json - - name: Publish azure image - if: ${{ !inputs.staging }} - run: | - IMAGE_DEFINITION=${VM_IMAGE_BUILD_PREFIX}dstack${{ matrix.variant }}-${{ inputs.image_version }} - IMAGE_NAME=${VM_IMAGE_BUILD_PREFIX}dstack${{ matrix.variant }}-${{ inputs.image_version }} - ../publish_azure_image.sh $IMAGE_DEFINITION $IMAGE_NAME - - build-gcp-images: - needs: build-docker - if: always() && inputs.build_gcp && (needs.build-docker.result == 'success' || needs.build-docker.result == 'skipped') - defaults: - run: - working-directory: scripts/packer - runs-on: ubuntu-latest - strategy: - matrix: - variant: ["", "-cuda"] - permissions: - contents: "read" - id-token: "write" - steps: - - uses: actions/checkout@v4 - - name: Authenticate to Google Cloud - uses: google-github-actions/auth@v2 - with: - workload_identity_provider: "projects/531508670106/locations/global/workloadIdentityPools/github-identity-pool/providers/github-id-provider" - service_account: "github-actions@dstack.iam.gserviceaccount.com" - create_credentials_file: true - - name: Set up Cloud SDK - uses: google-github-actions/setup-gcloud@v2 - - name: Download packer - run: | - wget https://releases.hashicorp.com/packer/${{ env.PACKER_VERSION }}/packer_${{ env.PACKER_VERSION }}_linux_amd64.zip - unzip packer_${{ env.PACKER_VERSION }}_linux_amd64.zip - chmod +x packer - - name: Run packer - run: | - ./packer build -var-file=versions.json -var image_version=${{ inputs.image_version }} -var build_prefix=$VM_IMAGE_BUILD_PREFIX gcp-image${{ matrix.variant }}.json - - name: Publish images - run: | - IMAGE_VERSION=${IMAGE_VERSION//./-} - gcloud compute images add-iam-policy-binding ${VM_IMAGE_BUILD_PREFIX}dstack${{ matrix.variant }}-$IMAGE_VERSION --member='allAuthenticatedUsers' --role='roles/compute.imageUser' - env: - IMAGE_VERSION: ${{ inputs.image_version }} - - build-oci-images: - needs: build-docker - if: always() && inputs.build_oci && (needs.build-docker.result == 'success' || needs.build-docker.result == 'skipped') - runs-on: ubuntu-latest - env: - OCI_COMPARTMENT: ocid1.compartment.oc1..aaaaaaaaxu2uq64unfa2imwkp37icxqv6f7gwp2mczdt2mukuqbkauwqmbtq - OCI_SUBNET: ocid1.subnet.oc1.eu-frankfurt-1.aaaaaaaaewxkaqsmbi2tig5sfw4eexzo3mkb4zrpm4gwvfhqdddnxicxe4fa - OCI_AVAILABILITY_DOMAIN: kZql:EU-FRANKFURT-1-AD-3 - OCI_REGION: eu-frankfurt-1 - strategy: - matrix: - variant: ["", "-cuda"] - steps: - - uses: actions/checkout@v4 - - name: Setup OCI config - run: | - mkdir ~/.oci - echo "$OCI_KEY_CONTENT" > ~/.oci/key.pem - echo [DEFAULT] > ~/.oci/config - echo region=$OCI_REGION >> ~/.oci/config - echo tenancy=$OCI_TENANCY >> ~/.oci/config - echo user=$OCI_USER >> ~/.oci/config - echo fingerprint=$OCI_FINGERPRINT >> ~/.oci/config - echo key_file=~/.oci/key.pem >> ~/.oci/config - env: - OCI_TENANCY: ${{ secrets.OCI_CLI_TENANCY }} - OCI_USER: ${{ secrets.OCI_CLI_USER }} - OCI_FINGERPRINT: ${{ secrets.OCI_CLI_FINGERPRINT }} - OCI_KEY_CONTENT: ${{ secrets.OCI_CLI_KEY_CONTENT }} - - name: Install packer - working-directory: scripts/packer - run: | - wget https://releases.hashicorp.com/packer/${{ env.PACKER_VERSION }}/packer_${{ env.PACKER_VERSION }}_linux_amd64.zip - unzip packer_${{ env.PACKER_VERSION }}_linux_amd64.zip - chmod +x packer - ./packer plugins install github.com/hashicorp/oracle - - name: Run packer - working-directory: scripts/packer - run: | - ./packer build \ - -var-file=versions.json \ - -var image_version=${{ inputs.image_version }} \ - -var build_prefix=$VM_IMAGE_BUILD_PREFIX \ - -var oci_compartment_ocid=$OCI_COMPARTMENT \ - -var oci_subnet_ocid=$OCI_SUBNET \ - -var oci_availability_domain=$OCI_AVAILABILITY_DOMAIN \ - oci-image${{ matrix.variant }}.json - - uses: astral-sh/setup-uv@v5 - with: - python-version: "3.12" - - name: Install dependencies for publishing - run: | - uv sync --extra oci - - name: Copy image to target regions - if: ${{ !inputs.staging }} - run: | - uv run scripts/oci_image_tools.py copy \ - --image ${VM_IMAGE_BUILD_PREFIX}dstack${{ matrix.variant }}-${{ inputs.image_version }} \ - --from $OCI_REGION \ - --compartment $OCI_COMPARTMENT - - name: Publish image in OCI Marketplace - if: ${{ !inputs.staging }} - run: | - uv run scripts/oci_image_tools.py publish \ - --image ${VM_IMAGE_BUILD_PREFIX}dstack${{ matrix.variant }}-${{ inputs.image_version }} \ - --compartment $OCI_COMPARTMENT \ - --version ${{ inputs.image_version }} \ - --description "Image for running workloads with dstack - https://dstack.ai/" \ - --os "Ubuntu 22.04" \ - --contact-name dstack \ - --contact-email hello@dstack.ai diff --git a/.github/workflows/vm-images.yml b/.github/workflows/vm-images.yml new file mode 100644 index 0000000000..6251fa2027 --- /dev/null +++ b/.github/workflows/vm-images.yml @@ -0,0 +1,203 @@ +name: Build VM images +run-name: Build VM images ${{ inputs.image_version }}${{ inputs.staging && ' (staging)' || '' }} + +on: + workflow_dispatch: + inputs: + image_version: + description: "Image version" + required: true + staging: + description: "Build staging images" + type: boolean + default: false + build_aws: + description: "Build AWS images" + type: boolean + default: true + build_azure: + description: "Build Azure images" + type: boolean + default: true + build_gcp: + description: "Build GCP images" + type: boolean + default: true + build_oci: + description: "Build OCI images" + type: boolean + default: true + +env: + PACKER_VERSION: "1.9.2" + VM_IMAGE_BUILD_PREFIX: ${{ inputs.staging && format('stgn-{0}-', github.run_number) || '' }} # staging ? prefix : '' + +jobs: + build-aws-images: + if: inputs.build_aws + defaults: + run: + working-directory: scripts/packer + runs-on: ubuntu-latest + env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + strategy: + matrix: + variant: ["", "-cuda"] + steps: + - uses: actions/checkout@v4 + - name: Download packer + run: | + wget https://releases.hashicorp.com/packer/${{ env.PACKER_VERSION }}/packer_${{ env.PACKER_VERSION }}_linux_amd64.zip + unzip packer_${{ env.PACKER_VERSION }}_linux_amd64.zip + chmod +x packer + - name: Run packer + run: | + ./packer build -var-file=versions.json $PROD_VARS -var image_version=${{ inputs.image_version }} -var build_prefix=$VM_IMAGE_BUILD_PREFIX aws-image${{ matrix.variant }}.json + env: + PROD_VARS: ${{ !inputs.staging && '-var-file=aws-vars-prod.json' || '' }} # production ? var-file : '' + + build-azure-images: + if: inputs.build_azure + defaults: + run: + working-directory: scripts/packer + runs-on: ubuntu-latest + env: + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + VERSION: ${{ github.run_number }} + strategy: + matrix: + variant: ["", "-cuda", "-grid"] + steps: + - uses: actions/checkout@v4 + - uses: Azure/login@v2 + name: Log in to az + with: + creds: '{"clientId":"${{ secrets.AZURE_CLIENT_ID }}","clientSecret":"${{ secrets.AZURE_CLIENT_SECRET }}","subscriptionId":"${{ secrets.AZURE_SUBSCRIPTION_ID }}","tenantId":"${{ secrets.AZURE_TENANT_ID }}"}' + - name: Download packer + run: | + wget https://releases.hashicorp.com/packer/${{ env.PACKER_VERSION }}/packer_${{ env.PACKER_VERSION }}_linux_amd64.zip + unzip packer_${{ env.PACKER_VERSION }}_linux_amd64.zip + chmod +x packer + - name: Run packer + run: | + ./packer build -var-file=versions.json -var image_version=${{ inputs.image_version }} -var build_prefix=$VM_IMAGE_BUILD_PREFIX azure-image${{ matrix.variant }}.json + - name: Publish azure image + if: ${{ !inputs.staging }} + run: | + IMAGE_DEFINITION=${VM_IMAGE_BUILD_PREFIX}dstack${{ matrix.variant }}-${{ inputs.image_version }} + IMAGE_NAME=${VM_IMAGE_BUILD_PREFIX}dstack${{ matrix.variant }}-${{ inputs.image_version }} + ../publish_azure_image.sh $IMAGE_DEFINITION $IMAGE_NAME + + build-gcp-images: + if: inputs.build_gcp + defaults: + run: + working-directory: scripts/packer + runs-on: ubuntu-latest + strategy: + matrix: + variant: ["", "-cuda"] + permissions: + contents: "read" + id-token: "write" + steps: + - uses: actions/checkout@v4 + - name: Authenticate to Google Cloud + uses: google-github-actions/auth@v2 + with: + workload_identity_provider: "projects/531508670106/locations/global/workloadIdentityPools/github-identity-pool/providers/github-id-provider" + service_account: "github-actions@dstack.iam.gserviceaccount.com" + create_credentials_file: true + - name: Set up Cloud SDK + uses: google-github-actions/setup-gcloud@v2 + - name: Download packer + run: | + wget https://releases.hashicorp.com/packer/${{ env.PACKER_VERSION }}/packer_${{ env.PACKER_VERSION }}_linux_amd64.zip + unzip packer_${{ env.PACKER_VERSION }}_linux_amd64.zip + chmod +x packer + - name: Run packer + run: | + ./packer build -var-file=versions.json -var image_version=${{ inputs.image_version }} -var build_prefix=$VM_IMAGE_BUILD_PREFIX gcp-image${{ matrix.variant }}.json + - name: Publish images + run: | + IMAGE_VERSION=${IMAGE_VERSION//./-} + gcloud compute images add-iam-policy-binding ${VM_IMAGE_BUILD_PREFIX}dstack${{ matrix.variant }}-$IMAGE_VERSION --member='allAuthenticatedUsers' --role='roles/compute.imageUser' + env: + IMAGE_VERSION: ${{ inputs.image_version }} + + build-oci-images: + if: inputs.build_oci + runs-on: ubuntu-latest + env: + OCI_COMPARTMENT: ocid1.compartment.oc1..aaaaaaaaxu2uq64unfa2imwkp37icxqv6f7gwp2mczdt2mukuqbkauwqmbtq + OCI_SUBNET: ocid1.subnet.oc1.eu-frankfurt-1.aaaaaaaaewxkaqsmbi2tig5sfw4eexzo3mkb4zrpm4gwvfhqdddnxicxe4fa + OCI_AVAILABILITY_DOMAIN: kZql:EU-FRANKFURT-1-AD-3 + OCI_REGION: eu-frankfurt-1 + strategy: + matrix: + variant: ["", "-cuda"] + steps: + - uses: actions/checkout@v4 + - name: Setup OCI config + run: | + mkdir ~/.oci + echo "$OCI_KEY_CONTENT" > ~/.oci/key.pem + echo [DEFAULT] > ~/.oci/config + echo region=$OCI_REGION >> ~/.oci/config + echo tenancy=$OCI_TENANCY >> ~/.oci/config + echo user=$OCI_USER >> ~/.oci/config + echo fingerprint=$OCI_FINGERPRINT >> ~/.oci/config + echo key_file=~/.oci/key.pem >> ~/.oci/config + env: + OCI_TENANCY: ${{ secrets.OCI_CLI_TENANCY }} + OCI_USER: ${{ secrets.OCI_CLI_USER }} + OCI_FINGERPRINT: ${{ secrets.OCI_CLI_FINGERPRINT }} + OCI_KEY_CONTENT: ${{ secrets.OCI_CLI_KEY_CONTENT }} + - name: Install packer + working-directory: scripts/packer + run: | + wget https://releases.hashicorp.com/packer/${{ env.PACKER_VERSION }}/packer_${{ env.PACKER_VERSION }}_linux_amd64.zip + unzip packer_${{ env.PACKER_VERSION }}_linux_amd64.zip + chmod +x packer + ./packer plugins install github.com/hashicorp/oracle + - name: Run packer + working-directory: scripts/packer + run: | + ./packer build \ + -var-file=versions.json \ + -var image_version=${{ inputs.image_version }} \ + -var build_prefix=$VM_IMAGE_BUILD_PREFIX \ + -var oci_compartment_ocid=$OCI_COMPARTMENT \ + -var oci_subnet_ocid=$OCI_SUBNET \ + -var oci_availability_domain=$OCI_AVAILABILITY_DOMAIN \ + oci-image${{ matrix.variant }}.json + - uses: astral-sh/setup-uv@v5 + with: + python-version: "3.12" + - name: Install dependencies for publishing + run: | + uv sync --extra oci + - name: Copy image to target regions + if: ${{ !inputs.staging }} + run: | + uv run scripts/oci_image_tools.py copy \ + --image ${VM_IMAGE_BUILD_PREFIX}dstack${{ matrix.variant }}-${{ inputs.image_version }} \ + --from $OCI_REGION \ + --compartment $OCI_COMPARTMENT + - name: Publish image in OCI Marketplace + if: ${{ !inputs.staging }} + run: | + uv run scripts/oci_image_tools.py publish \ + --image ${VM_IMAGE_BUILD_PREFIX}dstack${{ matrix.variant }}-${{ inputs.image_version }} \ + --compartment $OCI_COMPARTMENT \ + --version ${{ inputs.image_version }} \ + --description "Image for running workloads with dstack - https://dstack.ai/" \ + --os "Ubuntu 22.04" \ + --contact-name dstack \ + --contact-email hello@dstack.ai diff --git a/src/dstack/_internal/core/backends/aws/resources.py b/src/dstack/_internal/core/backends/aws/resources.py index 4e92bd84b5..b658bf6372 100644 --- a/src/dstack/_internal/core/backends/aws/resources.py +++ b/src/dstack/_internal/core/backends/aws/resources.py @@ -4,7 +4,7 @@ import botocore.client import botocore.exceptions -import dstack.version as version +from dstack._internal import settings from dstack._internal.core.backends.aws.models import AWSOSImageConfig from dstack._internal.core.errors import BackendError, ComputeError, ComputeResourceNotFoundError from dstack._internal.utils.logging import get_logger @@ -37,7 +37,7 @@ def get_image_id_and_username( image_owner = DLAMI_OWNER_ACCOUNT_ID username = "ubuntu" else: - image_name = f"dstack-{version.base_image}" + image_name = f"dstack-{settings.DSTACK_VM_BASE_IMAGE_VERSION}" image_owner = DSTACK_ACCOUNT_ID username = "ubuntu" response = ec2_client.describe_images( diff --git a/src/dstack/_internal/core/backends/azure/compute.py b/src/dstack/_internal/core/backends/azure/compute.py index 5959f88149..e94d656934 100644 --- a/src/dstack/_internal/core/backends/azure/compute.py +++ b/src/dstack/_internal/core/backends/azure/compute.py @@ -33,7 +33,6 @@ VirtualMachinePublicIPAddressConfiguration, ) -from dstack import version from dstack._internal import settings from dstack._internal.core.backends.azure import resources as azure_resources from dstack._internal.core.backends.azure import utils as azure_utils @@ -444,13 +443,13 @@ def from_instance_type(cls, instance: InstanceType) -> "VMImageVariant": def get_image_name(self) -> str: if self is self.GRID: - return f"dstack-grid-{version.base_image}" + return f"dstack-grid-{settings.DSTACK_VM_BASE_IMAGE_VERSION}" elif self is self.CUDA: - return f"dstack-cuda-{version.base_image}" + return f"dstack-cuda-{settings.DSTACK_VM_BASE_IMAGE_VERSION}" elif self is self.CUDA_WITH_PROPRIETARY_KERNEL_MODULES: return f"dstack-cuda-{DSTACK_OS_IMAGE_WITH_PROPRIETARY_NVIDIA_KERNEL_MODULES}" elif self is self.STANDARD: - return f"dstack-{version.base_image}" + return f"dstack-{settings.DSTACK_VM_BASE_IMAGE_VERSION}" else: raise ValueError(f"Unexpected image variant {self!r}") diff --git a/src/dstack/_internal/core/backends/gcp/compute.py b/src/dstack/_internal/core/backends/gcp/compute.py index b7310e4a5b..d5a5cdce8f 100644 --- a/src/dstack/_internal/core/backends/gcp/compute.py +++ b/src/dstack/_internal/core/backends/gcp/compute.py @@ -16,7 +16,7 @@ import dstack._internal.core.backends.gcp.auth as auth import dstack._internal.core.backends.gcp.resources as gcp_resources -from dstack import version +from dstack._internal import settings from dstack._internal.core.backends.base.compute import ( Compute, ComputeTTLCache, @@ -1133,12 +1133,12 @@ def _get_image(instance_type_name: str, gpu_name: Optional[str]) -> GCPImage: ) elif gpu_name is not None: if not requires_nvidia_proprietary_kernel_modules(gpu_name): - image_name = f"dstack-cuda-{version.base_image}" + image_name = f"dstack-cuda-{settings.DSTACK_VM_BASE_IMAGE_VERSION}" else: image_name = f"dstack-cuda-{DSTACK_OS_IMAGE_WITH_PROPRIETARY_NVIDIA_KERNEL_MODULES}" is_ufw_installed = True else: - image_name = f"dstack-{version.base_image}" + image_name = f"dstack-{settings.DSTACK_VM_BASE_IMAGE_VERSION}" is_ufw_installed = True image_name = image_name.replace(".", "-") return GCPImage( diff --git a/src/dstack/_internal/core/backends/oci/resources.py b/src/dstack/_internal/core/backends/oci/resources.py index 4f32e299db..ea7a3922de 100644 --- a/src/dstack/_internal/core/backends/oci/resources.py +++ b/src/dstack/_internal/core/backends/oci/resources.py @@ -22,7 +22,7 @@ import oci from oci.object_storage.models import CreatePreauthenticatedRequestDetails -from dstack import version +from dstack._internal import settings from dstack._internal.core.backends.base.compute import requires_nvidia_proprietary_kernel_modules from dstack._internal.core.backends.oci.region import OCIRegionClient from dstack._internal.core.consts import DSTACK_OS_IMAGE_WITH_PROPRIETARY_NVIDIA_KERNEL_MODULES @@ -356,10 +356,10 @@ def terminate_instance_if_exists(client: oci.core.ComputeClient, instance_id: st def get_marketplace_listing_and_package( gpu_name: Optional[str], client: oci.marketplace.MarketplaceClient ) -> Tuple[oci.marketplace.models.Listing, oci.marketplace.models.ImageListingPackage]: - listing_name = f"dstack-{version.base_image}" + listing_name = f"dstack-{settings.DSTACK_VM_BASE_IMAGE_VERSION}" if gpu_name is not None: if not requires_nvidia_proprietary_kernel_modules(gpu_name): - listing_name = f"dstack-cuda-{version.base_image}" + listing_name = f"dstack-cuda-{settings.DSTACK_VM_BASE_IMAGE_VERSION}" else: listing_name = f"dstack-cuda-{DSTACK_OS_IMAGE_WITH_PROPRIETARY_NVIDIA_KERNEL_MODULES}" diff --git a/src/dstack/_internal/server/services/backends/provisioning.py b/src/dstack/_internal/server/services/backends/provisioning.py index f289a41dce..435cca53ac 100644 --- a/src/dstack/_internal/server/services/backends/provisioning.py +++ b/src/dstack/_internal/server/services/backends/provisioning.py @@ -118,12 +118,18 @@ def _patch_base_image_for_aws_efa( if not is_efa_enabled: return image_name - if parse_image_name(image_name).repo != settings.DSTACK_BASE_IMAGE: + if parse_image_name(image_name).repo != settings.DSTACK_DOCKER_BASE_IMAGE: return image_name - if image_name.endswith(f"-base-ubuntu{settings.DSTACK_BASE_IMAGE_UBUNTU_VERSION}"): - return image_name[:-17] + f"-devel-efa-ubuntu{settings.DSTACK_BASE_IMAGE_UBUNTU_VERSION}" - if image_name.endswith(f"-devel-ubuntu{settings.DSTACK_BASE_IMAGE_UBUNTU_VERSION}"): - return image_name[:-18] + f"-devel-efa-ubuntu{settings.DSTACK_BASE_IMAGE_UBUNTU_VERSION}" + if image_name.endswith(f"-base-ubuntu{settings.DSTACK_DOCKER_BASE_IMAGE_UBUNTU_VERSION}"): + return ( + image_name[:-17] + + f"-devel-efa-ubuntu{settings.DSTACK_DOCKER_BASE_IMAGE_UBUNTU_VERSION}" + ) + if image_name.endswith(f"-devel-ubuntu{settings.DSTACK_DOCKER_BASE_IMAGE_UBUNTU_VERSION}"): + return ( + image_name[:-18] + + f"-devel-efa-ubuntu{settings.DSTACK_DOCKER_BASE_IMAGE_UBUNTU_VERSION}" + ) return image_name diff --git a/src/dstack/_internal/server/services/jobs/configurators/base.py b/src/dstack/_internal/server/services/jobs/configurators/base.py index a1137b9ce2..761745247d 100644 --- a/src/dstack/_internal/server/services/jobs/configurators/base.py +++ b/src/dstack/_internal/server/services/jobs/configurators/base.py @@ -87,7 +87,7 @@ def get_default_image(nvcc: bool = False) -> str: Args: nvcc: If True, returns 'devel' variant, otherwise 'base'. """ - return f"{settings.DSTACK_BASE_IMAGE}:{settings.DSTACK_BASE_IMAGE_VERSION}-{'devel' if nvcc else 'base'}-ubuntu{settings.DSTACK_BASE_IMAGE_UBUNTU_VERSION}" + return f"{settings.DSTACK_DOCKER_BASE_IMAGE}:{settings.DSTACK_DOCKER_BASE_IMAGE_VERSION}-{'devel' if nvcc else 'base'}-ubuntu{settings.DSTACK_DOCKER_BASE_IMAGE_UBUNTU_VERSION}" class JobConfigurator(ABC): diff --git a/src/dstack/_internal/settings.py b/src/dstack/_internal/settings.py index 2847fb84e3..9d3517a80a 100644 --- a/src/dstack/_internal/settings.py +++ b/src/dstack/_internal/settings.py @@ -20,11 +20,14 @@ DSTACK_USE_LATEST_FROM_BRANCH = os.getenv("DSTACK_USE_LATEST_FROM_BRANCH") is not None -DSTACK_BASE_IMAGE = os.getenv("DSTACK_BASE_IMAGE", "dstackai/base") -DSTACK_BASE_IMAGE_VERSION = os.getenv("DSTACK_BASE_IMAGE_VERSION", version.base_image) -DSTACK_BASE_IMAGE_UBUNTU_VERSION = os.getenv( - "DSTACK_BASE_IMAGE_UBUNTU_VERSION", version.base_image_ubuntu_version +DSTACK_DOCKER_BASE_IMAGE = os.getenv("DSTACK_DOCKER_BASE_IMAGE", "dstackai/base") +DSTACK_DOCKER_BASE_IMAGE_VERSION = os.getenv( + "DSTACK_DOCKER_BASE_IMAGE_VERSION", version.docker_base_image ) +DSTACK_DOCKER_BASE_IMAGE_UBUNTU_VERSION = os.getenv( + "DSTACK_DOCKER_BASE_IMAGE_UBUNTU_VERSION", version.docker_base_image_ubuntu_version +) +DSTACK_VM_BASE_IMAGE_VERSION = os.getenv("DSTACK_VM_BASE_IMAGE_VERSION", version.vm_base_image) DSTACK_DIND_IMAGE = os.getenv("DSTACK_DIND_IMAGE", "dstackai/dind") CLI_LOG_LEVEL = os.getenv("DSTACK_CLI_LOG_LEVEL", "INFO").upper() diff --git a/src/dstack/version.py b/src/dstack/version.py index 273007fe6f..08924a235c 100644 --- a/src/dstack/version.py +++ b/src/dstack/version.py @@ -5,5 +5,6 @@ __version__ = "0.0.0" __is_release__ = False -base_image = "0.13" -base_image_ubuntu_version = "22.04" +docker_base_image = "0.13" +docker_base_image_ubuntu_version = "22.04" +vm_base_image = "0.13" diff --git a/src/tests/_internal/core/backends/aws/test_resources.py b/src/tests/_internal/core/backends/aws/test_resources.py index 4fa9a30fa6..efe19ad870 100644 --- a/src/tests/_internal/core/backends/aws/test_resources.py +++ b/src/tests/_internal/core/backends/aws/test_resources.py @@ -131,7 +131,7 @@ def test_raises_resource_not_found_if_none_available( caplog: pytest.LogCaptureFixture, ec2_client_mock: Mock, ): - monkeypatch.setattr("dstack.version.base_image", "0.0") + monkeypatch.setattr("dstack._internal.settings.DSTACK_VM_BASE_IMAGE_VERSION", "0.0") caplog.set_level(logging.WARNING) ec2_client_mock.describe_images.return_value = { "Images": [ @@ -169,7 +169,7 @@ def test_uses_default_image_name_and_account_id_if_image_config_not_provided( expected_name: str, expected_owner: str, ): - monkeypatch.setattr("dstack.version.base_image", "0.0") + monkeypatch.setattr("dstack._internal.settings.DSTACK_VM_BASE_IMAGE_VERSION", "0.0") _, username = get_image_id_and_username( ec2_client_mock, gpu_name="A10G" if cuda else None, diff --git a/src/tests/_internal/core/backends/azure/test_compute.py b/src/tests/_internal/core/backends/azure/test_compute.py index d83b04edae..50b5a342ac 100644 --- a/src/tests/_internal/core/backends/azure/test_compute.py +++ b/src/tests/_internal/core/backends/azure/test_compute.py @@ -1,6 +1,6 @@ import pytest -from dstack import version +from dstack._internal import settings from dstack._internal.core.backends.azure.compute import VMImageVariant from dstack._internal.core.models.instances import Gpu, InstanceType, Resources @@ -55,9 +55,9 @@ def test_from_instance_type( @pytest.mark.parametrize( ["variant", "expected_name"], [ - [VMImageVariant.GRID, f"dstack-grid-{version.base_image}"], - [VMImageVariant.CUDA, f"dstack-cuda-{version.base_image}"], - [VMImageVariant.STANDARD, f"dstack-{version.base_image}"], + [VMImageVariant.GRID, f"dstack-grid-{settings.DSTACK_VM_BASE_IMAGE_VERSION}"], + [VMImageVariant.CUDA, f"dstack-cuda-{settings.DSTACK_VM_BASE_IMAGE_VERSION}"], + [VMImageVariant.STANDARD, f"dstack-{settings.DSTACK_VM_BASE_IMAGE_VERSION}"], ], ) def test_get_image_name(self, variant: VMImageVariant, expected_name: str): diff --git a/src/tests/_internal/server/background/pipeline_tasks/test_running_jobs.py b/src/tests/_internal/server/background/pipeline_tasks/test_running_jobs.py index e308b89ce8..51c490b91e 100644 --- a/src/tests/_internal/server/background/pipeline_tasks/test_running_jobs.py +++ b/src/tests/_internal/server/background/pipeline_tasks/test_running_jobs.py @@ -651,8 +651,8 @@ async def test_provisioning_shim( registry_username="", registry_password="", image_name=( - f"dstackai/base:{settings.DSTACK_BASE_IMAGE_VERSION}-" - f"base-ubuntu{settings.DSTACK_BASE_IMAGE_UBUNTU_VERSION}" + f"dstackai/base:{settings.DSTACK_DOCKER_BASE_IMAGE_VERSION}-" + f"base-ubuntu{settings.DSTACK_DOCKER_BASE_IMAGE_UBUNTU_VERSION}" ), container_user="root", privileged=False, @@ -745,8 +745,8 @@ async def test_provisioning_shim_with_volumes( registry_username="", registry_password="", image_name=( - f"dstackai/base:{settings.DSTACK_BASE_IMAGE_VERSION}-" - f"base-ubuntu{settings.DSTACK_BASE_IMAGE_UBUNTU_VERSION}" + f"dstackai/base:{settings.DSTACK_DOCKER_BASE_IMAGE_VERSION}-" + f"base-ubuntu{settings.DSTACK_DOCKER_BASE_IMAGE_UBUNTU_VERSION}" ), container_user="root", privileged=privileged, diff --git a/src/tests/_internal/server/routers/test_runs.py b/src/tests/_internal/server/routers/test_runs.py index 93414c9f42..69bf1693bc 100644 --- a/src/tests/_internal/server/routers/test_runs.py +++ b/src/tests/_internal/server/routers/test_runs.py @@ -152,7 +152,7 @@ def get_dev_env_run_plan_dict( " && tail -f /dev/null" ), ] - image_name = f"dstackai/base:{settings.DSTACK_BASE_IMAGE_VERSION}-base-ubuntu{settings.DSTACK_BASE_IMAGE_UBUNTU_VERSION}" + image_name = f"dstackai/base:{settings.DSTACK_DOCKER_BASE_IMAGE_VERSION}-base-ubuntu{settings.DSTACK_DOCKER_BASE_IMAGE_UBUNTU_VERSION}" run_spec = { "configuration": { diff --git a/src/tests/_internal/server/services/backends/test_provisioning.py b/src/tests/_internal/server/services/backends/test_provisioning.py index 94e8c98be9..51637d2c3a 100644 --- a/src/tests/_internal/server/services/backends/test_provisioning.py +++ b/src/tests/_internal/server/services/backends/test_provisioning.py @@ -47,8 +47,8 @@ def _call_resolve_provisioning_image( ) def test_patch_aws_efa_instance_with_suffix(self, suffix: str, instance_type: str) -> None: image_name = ( - f"{settings.DSTACK_BASE_IMAGE}:{settings.DSTACK_BASE_IMAGE_VERSION}{suffix}" - f"-ubuntu{settings.DSTACK_BASE_IMAGE_UBUNTU_VERSION}" + f"{settings.DSTACK_DOCKER_BASE_IMAGE}:{settings.DSTACK_DOCKER_BASE_IMAGE_VERSION}{suffix}" + f"-ubuntu{settings.DSTACK_DOCKER_BASE_IMAGE_UBUNTU_VERSION}" ) result = self._call_resolve_provisioning_image( image_name, @@ -56,8 +56,8 @@ def test_patch_aws_efa_instance_with_suffix(self, suffix: str, instance_type: st instance_type, ) expected = ( - f"{settings.DSTACK_BASE_IMAGE}:{settings.DSTACK_BASE_IMAGE_VERSION}" - f"-devel-efa-ubuntu{settings.DSTACK_BASE_IMAGE_UBUNTU_VERSION}" + f"{settings.DSTACK_DOCKER_BASE_IMAGE}:{settings.DSTACK_DOCKER_BASE_IMAGE_VERSION}" + f"-devel-efa-ubuntu{settings.DSTACK_DOCKER_BASE_IMAGE_UBUNTU_VERSION}" ) assert result == expected @@ -76,8 +76,8 @@ def test_patch_aws_efa_instance_with_suffix(self, suffix: str, instance_type: st ) def test_patch_all_efa_instance_types(self, instance_type: str, suffix: str) -> None: image_name = ( - f"{settings.DSTACK_BASE_IMAGE}:{settings.DSTACK_BASE_IMAGE_VERSION}{suffix}" - f"-ubuntu{settings.DSTACK_BASE_IMAGE_UBUNTU_VERSION}" + f"{settings.DSTACK_DOCKER_BASE_IMAGE}:{settings.DSTACK_DOCKER_BASE_IMAGE_VERSION}{suffix}" + f"-ubuntu{settings.DSTACK_DOCKER_BASE_IMAGE_UBUNTU_VERSION}" ) result = self._call_resolve_provisioning_image( image_name, @@ -85,8 +85,8 @@ def test_patch_all_efa_instance_types(self, instance_type: str, suffix: str) -> instance_type, ) expected = ( - f"{settings.DSTACK_BASE_IMAGE}:{settings.DSTACK_BASE_IMAGE_VERSION}" - f"-devel-efa-ubuntu{settings.DSTACK_BASE_IMAGE_UBUNTU_VERSION}" + f"{settings.DSTACK_DOCKER_BASE_IMAGE}:{settings.DSTACK_DOCKER_BASE_IMAGE_VERSION}" + f"-devel-efa-ubuntu{settings.DSTACK_DOCKER_BASE_IMAGE_UBUNTU_VERSION}" ) assert result == expected @@ -106,8 +106,8 @@ def test_no_patch_non_aws_backends( instance_type: str, ) -> None: image_name = ( - f"{settings.DSTACK_BASE_IMAGE}:{settings.DSTACK_BASE_IMAGE_VERSION}{suffix}" - f"-ubuntu{settings.DSTACK_BASE_IMAGE_UBUNTU_VERSION}" + f"{settings.DSTACK_DOCKER_BASE_IMAGE}:{settings.DSTACK_DOCKER_BASE_IMAGE_VERSION}{suffix}" + f"-ubuntu{settings.DSTACK_DOCKER_BASE_IMAGE_UBUNTU_VERSION}" ) result = self._call_resolve_provisioning_image(image_name, backend, instance_type) assert result == image_name @@ -118,7 +118,7 @@ def test_no_patch_non_aws_backends( ["t3.micro", "m5.large", "c5.xlarge", "r5.2xlarge", "m6i.large", "g6.xlarge"], ) def test_no_patch_non_efa_aws_instances(self, instance_type: str, suffix: str) -> None: - image_name = f"{settings.DSTACK_BASE_IMAGE}:{settings.DSTACK_BASE_IMAGE_VERSION}{suffix}" + image_name = f"{settings.DSTACK_DOCKER_BASE_IMAGE}:{settings.DSTACK_DOCKER_BASE_IMAGE_VERSION}{suffix}" result = self._call_resolve_provisioning_image( image_name, BackendType.AWS, @@ -137,9 +137,9 @@ def test_no_patch_non_efa_aws_instances(self, instance_type: str, suffix: str) - "nvidia/cuda:11.8-runtime-ubuntu20.04", "python:3.9-slim", "custom/image:latest", - f"{settings.DSTACK_BASE_IMAGE}:{settings.DSTACK_BASE_IMAGE_VERSION}-custom", - f"{settings.DSTACK_BASE_IMAGE}:{settings.DSTACK_BASE_IMAGE_VERSION}-devel-efa", - f"{settings.DSTACK_BASE_IMAGE}:{settings.DSTACK_BASE_IMAGE_VERSION}", + f"{settings.DSTACK_DOCKER_BASE_IMAGE}:{settings.DSTACK_DOCKER_BASE_IMAGE_VERSION}-custom", + f"{settings.DSTACK_DOCKER_BASE_IMAGE}:{settings.DSTACK_DOCKER_BASE_IMAGE_VERSION}-devel-efa", + f"{settings.DSTACK_DOCKER_BASE_IMAGE}:{settings.DSTACK_DOCKER_BASE_IMAGE_VERSION}", ], ) def test_no_patch_other_images(self, instance_type: str, image_name: str) -> None: @@ -154,12 +154,12 @@ def test_no_patch_other_images(self, instance_type: str, image_name: str) -> Non def test_patch_aws_efa_image_with_registry_prefix(self, suffix: str) -> None: registry = "registry.example" image_name = ( - f"{registry}/{settings.DSTACK_BASE_IMAGE}:{settings.DSTACK_BASE_IMAGE_VERSION}{suffix}" - f"-ubuntu{settings.DSTACK_BASE_IMAGE_UBUNTU_VERSION}" + f"{registry}/{settings.DSTACK_DOCKER_BASE_IMAGE}:{settings.DSTACK_DOCKER_BASE_IMAGE_VERSION}{suffix}" + f"-ubuntu{settings.DSTACK_DOCKER_BASE_IMAGE_UBUNTU_VERSION}" ) result = self._call_resolve_provisioning_image(image_name, BackendType.AWS, "p5.48xlarge") expected = ( - f"{registry}/{settings.DSTACK_BASE_IMAGE}:{settings.DSTACK_BASE_IMAGE_VERSION}" - f"-devel-efa-ubuntu{settings.DSTACK_BASE_IMAGE_UBUNTU_VERSION}" + f"{registry}/{settings.DSTACK_DOCKER_BASE_IMAGE}:{settings.DSTACK_DOCKER_BASE_IMAGE_VERSION}" + f"-devel-efa-ubuntu{settings.DSTACK_DOCKER_BASE_IMAGE_UBUNTU_VERSION}" ) assert result == expected