From 6f9bcaa12b0047ac8295caa2599da2ba680e788b Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Thu, 19 Dec 2024 11:46:41 +0100 Subject: [PATCH] Update Dockerfile and add workflow to register. [ci skip] --- .github/workflows/Container.yml | 77 +++++++++++++++++++++++++++++++++ Dockerfile | 51 +++++++++++++++++----- README.md | 10 +++++ 3 files changed, 127 insertions(+), 11 deletions(-) create mode 100644 .github/workflows/Container.yml diff --git a/.github/workflows/Container.yml b/.github/workflows/Container.yml new file mode 100644 index 0000000000..d84a4ee18f --- /dev/null +++ b/.github/workflows/Container.yml @@ -0,0 +1,77 @@ +name: Publish Docker image + +on: + workflow_dispatch: + inputs: + ref: + description: 'Git ref to build instead' + required: false + default: '' + push: + tags: + - 'v*' + branches: + - master + +jobs: + push_to_registry: + name: Build Docker image - Julia ${{ matrix.julia }} - CUDA ${{ matrix.cuda }} + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + strategy: + matrix: + julia: ["1.10", "1.11"] + cuda: ["11.8", "12.6"] + include: + - julia: "1.11" + cuda: "12.6" + default: true + + steps: + - name: Check out the repo + uses: actions/checkout@v4 + with: + ref: ${{ inputs.ref || github.ref }} + + - name: Get package spec + id: pkg + run: | + if [[ "${{ github.ref_type }}" == "tag" ]]; then + echo "ref=${{ github.ref_name }}" >> $GITHUB_OUTPUT + echo "name=${{ github.ref_name }}" >> $GITHUB_OUTPUT + else + echo "ref=${{ github.sha }}" >> $GITHUB_OUTPUT + echo "name=dev" >> $GITHUB_OUTPUT + fi + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata (tags, labels) + id: meta + uses: docker/metadata-action@v5 + with: + images: ghcr.io/${{ github.repository }} + tags: | + type=raw,value=${{ steps.pkg.outputs.name }}-julia${{ matrix.julia }}-cuda${{ matrix.cuda }} + type=raw,value=latest,enable=${{ matrix.default == true && github.ref_type == 'tag' }} + type=raw,value=dev,enable=${{ matrix.default == true && github.ref_type == 'branch' }} + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + build-args: | + JULIA_VERSION=${{ matrix.julia }} + CUDA_VERSION=${{ matrix.cuda }} + PACKAGE_SPEC=CUDA#${{ steps.pkg.outputs.ref }} diff --git a/Dockerfile b/Dockerfile index 2a943e3e7c..26052f5264 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,21 +1,47 @@ # example of a Docker container for CUDA.jl with a specific toolkit embedded at run time. -FROM julia:1.8-bullseye +ARG JULIA_VERSION=1 +FROM julia:${JULIA_VERSION} +ARG CUDA_VERSION=12.6 -# system-wide packages +ARG PACKAGE_SPEC=CUDA + +LABEL maintainer="Tim Besard " +LABEL description="CUDA.jl container with CUDA ${CUDA_VERSION} installed for Julia ${JULIA_VERSION}" +LABEL version="1.0" -ENV JULIA_DEPOT_PATH=/usr/local/share/julia -RUN julia -e 'using Pkg; Pkg.add("CUDA")' +# system-wide packages -# hard-code a CUDA toolkit version -RUN julia -e 'using CUDA; CUDA.set_runtime_version!(v"12.2")' -# re-importing CUDA.jl below will trigger a download of the relevant artifacts +# no trailing ':' as to ensure we don't touch anything outside this directory. without it, +# Julia touches the compilecache timestamps in its shipped depot (for some reason; a bug?) +ENV JULIA_DEPOT_PATH=/usr/local/share/julia -# generate the device runtime library for all known and supported devices. -# this is to avoid having to do this over and over at run time. -RUN julia -e 'using CUDA; CUDA.precompile_runtime()' +# pre-install the CUDA toolkit from an artifact. we do this separately from CUDA.jl so that +# this layer can be cached independently. it also avoids double precompilation of CUDA.jl in +# order to call `CUDA.set_runtime_version!`. +RUN julia -e '#= configure the preference =# \ + env = "/usr/local/share/julia/environments/v$(VERSION.major).$(VERSION.minor)"; \ + mkpath(env); \ + write("$env/LocalPreferences.toml", \ + "[CUDA_Runtime_jll]\nversion = \"'${CUDA_VERSION}'\""); \ + \ + #= install the JLL =# \ + using Pkg; \ + Pkg.add("CUDA_Runtime_jll")' && \ + #= remove nondeterminisms =# \ + cd /usr/local/share/julia && \ + rm -rf compiled registries scratchspaces logs && \ + find -exec touch -h -d "@0" {} + && \ + touch -h -d "@0" /usr/local/share + +# install CUDA.jl itself +RUN julia -e 'using Pkg; pkg"add '${PACKAGE_SPEC}'"; \ + using CUDA; CUDA.precompile_runtime()' && \ + #= remove useless stuff =# \ + cd /usr/local/share/julia && \ + rm -rf registries scratchspaces logs # user environment @@ -25,6 +51,9 @@ RUN julia -e 'using CUDA; CUDA.precompile_runtime()' # case there might not be a (writable) home directory. RUN mkdir -m 0777 /depot -ENV JULIA_DEPOT_PATH=/depot:/usr/local/share/julia +ENV JULIA_DEPOT_PATH=/depot:/usr/local/share/julia: + +# make sure the user depot is the one used by default (which requires a valid Project.toml) +RUN cp -ar /usr/local/share/julia/environments /depot WORKDIR "/workspace" diff --git a/README.md b/README.md index 3676c61aa2..64815e03dd 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,16 @@ This may take a while, as it will precompile the package and download a suitable the CUDA toolkit. If your GPU is not fully supported, the above command (or any other command that initializes the toolkit) will issue a warning. +For quick testing, you can also use the `juliagpu/cuda.jl` container image from the GitHub +Container Registry, which provides Julia, a precompiled version of CUDA.jl, and a matching +CUDA toolkit: + +```sh +docker run -it --rm --gpus=all ghcr.io/juliagpu/cuda.jl:latest +# `latest` refers to the latest released version, `nightly` is the current master branch. +# version-qualified names are available too, e.g., `v5.5.2-julia1.11-cuda12.6` +``` + For more usage instructions and other information, please refer to [the documentation](https://juliagpu.github.io/CUDA.jl/stable/).