diff --git a/.github/workflows/Container.yml b/.github/workflows/Container.yml new file mode 100644 index 0000000000..7b83cefcbb --- /dev/null +++ b/.github/workflows/Container.yml @@ -0,0 +1,92 @@ +name: Publish Docker image + +on: + workflow_dispatch: + inputs: + tag: + description: 'Tag to build instead' + required: false + default: '' + mark_as_latest: + description: 'Mark as latest' + type: boolean + required: false + default: false + push: + tags: + - 'v*' + branches: + - master + +jobs: + push_to_registry: + name: Build container - 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 + + - name: Check out the package + uses: actions/checkout@v4 + with: + ref: ${{ inputs.tag || github.ref_name }} + path: package + + - name: Get package spec + id: pkg + run: | + if [[ -n "${{ inputs.tag }}" ]]; then + echo "ref=${{ inputs.tag }}" >> $GITHUB_OUTPUT + echo "name=${{ inputs.tag }}" >> $GITHUB_OUTPUT + elif [[ "${{ 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 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' || inputs.mark_as_latest) }} + type=raw,value=dev,enable=${{ matrix.default == true && github.ref_type == 'branch' && inputs.tag == '' }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build and push image + uses: docker/build-push-action@v6 + with: + context: package + 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..ccc7f82323 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,11 @@ 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) +# TODO: do this with a startup script so that you can mount `/depot` as a volume +# TODO: demote CUDA_Runtime_jll as an [extra] dep so that it doesn't show in the status +RUN cp -ar /usr/local/share/julia/environments /depot WORKDIR "/workspace" diff --git a/README.md b/README.md index 3676c61aa2..74669f0f7b 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,15 @@ 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](https://github.com/JuliaGPU/CUDA.jl/pkgs/container/cuda.jl/versions) 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 # other tags available too +``` + For more usage instructions and other information, please refer to [the documentation](https://juliagpu.github.io/CUDA.jl/stable/).