Ctrl K
ComunidadGuías…

docker/install_nvidia_toolkit.sh

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
#!/usr/bin/env bash
# Installs the NVIDIA Container Toolkit into Docker. Host side only: Docker resolves --gpus in the daemon, before a container exists, so no image can do this itself.
#   curl -fsSL https://raw.githubusercontent.com/unslothai/unsloth/main/docker/install_nvidia_toolkit.sh -o install_nvidia_toolkit.sh && sudo -E bash install_nvidia_toolkit.sh
# UNSLOTH_TOOLKIT_VERIFY=0 skips the final `docker run --gpus all` check. UNSLOTH_DESTDIR, UNSLOTH_OS_RELEASE,
# UNSLOTH_PROC_VERSION, UNSLOTH_RUN_USER_DIR and UNSLOTH_WSL_LIB_DIR redirect the host paths they name (tests only).
set -euo pipefail

DESTDIR="${UNSLOTH_DESTDIR:-}"
OS_RELEASE="${UNSLOTH_OS_RELEASE:-/etc/os-release}"
PROC_VERSION="${UNSLOTH_PROC_VERSION:-/proc/version}"
RUN_USER_DIR="${UNSLOTH_RUN_USER_DIR:-/run/user}"
WSL_LIB_DIR="${UNSLOTH_WSL_LIB_DIR:-/usr/lib/wsl/lib}"
BASE="https://nvidia.github.io/libnvidia-container"

say()  { printf '%s\n' "$*"; }
fail() { printf 'ERROR: %s\n' "$1" >&2; exit "${2:-1}"; }

command -v docker >/dev/null 2>&1 \
    || fail "docker is not installed. Install Docker Engine first, on Linux with
       curl -fsSL https://get.docker.com -o get-docker.sh && sh get-docker.sh
       (https://docs.docker.com/engine/install/ covers Docker Desktop and the distro packages), then run this again." 2

# Grep variables, not pipes: under pipefail `grep -q` closing the pipe turns the producer's
# SIGPIPE into a false miss.

# Mac and Windows never need the toolkit; answer before the endpoint check.
host_os="$(uname -s)"
case "$host_os" in
    Darwin|MINGW*|MSYS*|CYGWIN*)
        if [[ -n "${DOCKER_CONTEXT:-}" || -z "${DOCKER_HOST:-}" ]]; then
            host_endpoint="$(docker context inspect --format '{{.Endpoints.docker.Host}}' 2>/dev/null)" \
                || fail "cannot inspect the Docker context '${DOCKER_CONTEXT:-current}' (it may exist only in the invoking user's Docker config); refusing to guess which daemon it drives." 2
        else
            host_endpoint="$DOCKER_HOST"
        fi
        # loopback tcp is this machine too (Docker Desktop's "expose daemon on tcp://localhost:2375")
        case "$host_endpoint" in
            ""|unix://*|npipe://*) ;;
            tcp://localhost|tcp://localhost:*|tcp://127.*|tcp://\[::1\]*|localhost:*|127.*) ;;
            *) fail "the Docker CLI on this machine talks to a remote daemon (${host_endpoint}); run this script on that host, it configures the local Docker only." 2 ;;
        esac
        if [[ "$host_os" == Darwin ]]; then
            say "macOS: no NVIDIA GPU can be attached on a Mac, so there is nothing to install."
            say "The image runs CPU-only there: drop --gpus and set UNSLOTH_ALLOW_CPU=1."
            exit 0
        fi
        # GPU support is the WSL 2 backend only (docs.docker.com/desktop/features/gpu/); Hyper-V's
        # LinuxKit VM has none. The kernel string tells them apart: *-microsoft-standard-WSL2 vs *-linuxkit.
        desktop_info="$(docker info --format '{{.OperatingSystem}}|{{.KernelVersion}}' 2>/dev/null)" \
            || fail "Docker Desktop is not running, or the Docker CLI cannot reach it. Start Docker Desktop,
       wait until it reports running, then run this again." 2
        desktop_os_name="${desktop_info%%|*}"
        desktop_os="$(printf '%s' "$desktop_os_name" | tr '[:upper:]' '[:lower:]')"
        desktop_kernel="${desktop_info#*|}"
        kernel_lc="$(printf '%s' "$desktop_kernel" | tr '[:upper:]' '[:lower:]')"
        case "$desktop_os" in
            *"docker desktop"*)
                case "$kernel_lc" in
                    *microsoft*|*wsl*)
                        say "Windows: Docker Desktop with the WSL 2 backend brings its own GPU support, nothing to install here."
                        say "Keep a current NVIDIA Windows driver installed (from nvidia.com), then: docker run --gpus all ..."
                        say "Only a WSL 2 distro running its own Docker Engine needs this script; run it inside that distro."
                        exit 0 ;;
                esac
                fail "Docker Desktop is running on the Hyper-V backend (kernel ${desktop_kernel}), which has no
       GPU support. Switch it to WSL 2 (Settings > General > Use the WSL 2 based engine), keep a
       current NVIDIA Windows driver installed (from nvidia.com), then: docker run --gpus all ..." 2 ;;
        esac
        say "Windows: this Docker CLI drives ${desktop_os_name:-a daemon}, not Docker Desktop, and a Windows shell cannot configure it."
        say "Only a WSL 2 distro running its own Docker Engine needs this script; run it inside that distro."
        exit 0 ;;
esac

# Docker Desktop ships its own GPU integration; installing here would configure a daemon it does not use. Checked before elevating.
docker_info="$(docker info 2>/dev/null || true)"
if grep -qi 'Operating System: Docker Desktop' <<<"$docker_info"; then
    if grep -qi microsoft "$PROC_VERSION" 2>/dev/null; then
        say "Docker Desktop with the WSL 2 backend: GPU support comes with it, nothing to install here."
        say "Keep a current NVIDIA Windows driver installed (from nvidia.com; wsl --update updates WSL itself, not the driver)."
        exit 0
    fi
    fail "Docker Desktop for Linux has no NVIDIA GPU support. Install Docker Engine instead
       (https://docs.docker.com/engine/install/) and run this again." 2
fi

# Everything below edits THIS machine; DOCKER_CONTEXT overrides DOCKER_HOST, which overrides the selected context.
if [[ -n "${DOCKER_CONTEXT:-}" || -z "${DOCKER_HOST:-}" ]]; then
    endpoint="$(docker context inspect --format '{{.Endpoints.docker.Host}}' 2>/dev/null)" \
        || fail "cannot inspect the Docker context '${DOCKER_CONTEXT:-current}' (it may exist only in the invoking user's Docker config); refusing to guess which daemon to configure." 2
else
    endpoint="$DOCKER_HOST"
fi
case "$endpoint" in
    ""|unix:///var/run/docker.sock|unix:///run/docker.sock|npipe://*) ;;
    unix://*) fail "the Docker CLI talks to a daemon on ${endpoint}, not the system daemon this script configures (/etc/docker/daemon.json, service docker). Point it at the default socket or configure that daemon by hand." 2 ;;
    *) fail "the Docker CLI talks to a remote daemon (${endpoint}); run this script on that host, it configures the local Docker only." 2 ;;
esac

# Rootless Docker keeps its own daemon, which the steps below would miss; root sees a different one, and `curl | sudo bash` arrives root with DOCKER_HOST stripped, so probe SUDO_UID's socket too.
rootless_daemon() {
    local opts
    opts="$(docker info --format '{{join .SecurityOptions ","}}' 2>/dev/null || true)"
    grep -q rootless <<<"$opts" && return 0
    [[ -n "${SUDO_UID:-}" && -S "${RUN_USER_DIR}/${SUDO_UID}/docker.sock" ]] || return 1
    opts="$(DOCKER_HOST="unix://${RUN_USER_DIR}/${SUDO_UID}/docker.sock" \
            docker info --format '{{join .SecurityOptions ","}}' 2>/dev/null || true)"
    grep -q rootless <<<"$opts"
}
if rootless_daemon; then
    fail "rootless Docker detected. Follow NVIDIA's rootless procedure instead:
       https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html#rootless-mode" 2
fi

if [[ "$(id -u)" != 0 ]]; then
    # piped through `curl | bash` there is no file to re-run: $0 is just "bash"
    if [[ -r "$0" ]] && command -v sudo >/dev/null 2>&1; then
        say "Re-running with sudo."
        exec sudo -E bash "$0" "$@"
    fi
    fail "run this as root: sudo -E bash install_nvidia_toolkit.sh" 2
fi

# On WSL 2 the Windows driver puts nvidia-smi under /usr/lib/wsl/lib, which sudo's secure_path drops from PATH.
NVSMI="$(command -v nvidia-smi 2>/dev/null || true)"
[[ -z "$NVSMI" && -x "${WSL_LIB_DIR}/nvidia-smi" ]] && NVSMI="${WSL_LIB_DIR}/nvidia-smi"

# sudo strips LD_* variables, so restore LD_LIBRARY_PATH for hosts whose NVIDIA libraries
# are outside ldconfig.
nvsmi() { LD_LIBRARY_PATH="${NV_LIB_DIR}${NV_LIB_DIR:+${LD_LIBRARY_PATH:+:}}${LD_LIBRARY_PATH:-}" "$NVSMI" "$@"; }
NV_LIB_DIR=""
gpu_list=""
# 2>&1: the loader's complaint separates a missing driver from an unreachable one.
[[ -n "$NVSMI" ]] && gpu_list="$(nvsmi -L 2>&1 || true)"
if [[ -n "$NVSMI" ]] && grep -qi 'libnvidia-ml' <<<"$gpu_list"; then
    for _dir in /usr/lib64-nvidia /usr/local/nvidia/lib64 "$WSL_LIB_DIR"; do
        [[ -e "${DESTDIR}${_dir}/libnvidia-ml.so.1" ]] || continue
        NV_LIB_DIR="${DESTDIR}${_dir}"
        gpu_list="$(nvsmi -L 2>&1 || true)"
        grep -q '^GPU' <<<"$gpu_list" && break
        NV_LIB_DIR=""
    done
fi
if [[ -z "$NVSMI" ]] || ! grep -q '^GPU' <<<"$gpu_list"; then
    # A library complaint means the driver exists but the loader cannot see it.
    if grep -qi 'libnvidia-ml' <<<"$gpu_list"; then
        fail "nvidia-smi cannot load libnvidia-ml.so, so this script cannot read the driver:
       ${gpu_list%%$'\n'*}
       sudo drops every LD_* variable, so a driver reached through LD_LIBRARY_PATH (Colab
       puts its libraries in /usr/lib64-nvidia) disappears when this script elevates.
       Find the directory holding libnvidia-ml.so.1 and name it on the command line:
       sudo LD_LIBRARY_PATH=/usr/lib64-nvidia bash install_nvidia_toolkit.sh" 2
    fi
    if grep -qi microsoft "$PROC_VERSION" 2>/dev/null; then
        fail "no NVIDIA GPU is visible in this WSL 2 distro. Install a current NVIDIA Windows driver
       from nvidia.com (never a Linux driver inside WSL), restart WSL (wsl --shutdown), then run
       this again. Unsloth images need driver 570.26 or newer." 2
    fi
    fail "no NVIDIA driver found (nvidia-smi lists no GPU). Install the driver first, with your
       distribution's packages (Ubuntu: 'sudo ubuntu-drivers install'; RHEL/Fedora: the
       nvidia-driver module from the CUDA repository), reboot, then run this again.
       Unsloth images need driver 570.26 or newer." 2
fi
MIN_DRIVER=570.26
# `sed -n 1p`, not `head -1`: head's SIGPIPE kills the script under set -e on multi-GPU hosts.
DRIVER="$(nvsmi --query-gpu=driver_version --format=csv,noheader 2>/dev/null | sed -n 1p | tr -d '[:space:]')"
driver_ok() {
    [[ -n "$DRIVER" ]] && [[ "$(printf '%s\n' "$MIN_DRIVER" "$DRIVER" | sort -V | head -1)" == "$MIN_DRIVER" ]]
}

# Explicit platform: a cached ubuntu image of the other arch would be picked and fail on "exec nvidia-smi: no such file".
case "$(uname -m)" in
    aarch64|arm64) PLATFORM=linux/arm64 ;;
    x86_64|amd64)  PLATFORM=linux/amd64 ;;
    *) fail "unsupported architecture $(uname -m): the Unsloth images are built for linux/amd64 and linux/arm64 only." 2 ;;
esac

configured() {
    local info; info="$(docker info 2>/dev/null || true)"
    grep -qi 'Runtimes:.*nvidia' <<<"$info"
}

if configured; then
    say "Docker already lists the nvidia runtime; nothing to install."
elif command -v nvidia-ctk >/dev/null 2>&1 && command -v nvidia-container-runtime >/dev/null 2>&1; then
    # nvidia-container-toolkit-base ships nvidia-ctk without the runtime, so check both before skipping the install.
    say "nvidia-container-toolkit is installed but Docker does not list the nvidia runtime."
else
    [[ -r "$OS_RELEASE" ]] || fail "cannot read $OS_RELEASE to pick a package manager." 2
    # shellcheck disable=SC1090
    ID_LIKE="$( . "$OS_RELEASE"; printf '%s %s' "${ID:-}" "${ID_LIKE:-}" )"
    case " $ID_LIKE " in
        *" ubuntu "*|*" debian "*)
            say "Adding NVIDIA's apt repository and installing nvidia-container-toolkit."
            apt-get update -qq
            apt-get install -y -qq --no-install-recommends ca-certificates curl gnupg2 >/dev/null
            mkdir -p "${DESTDIR}/usr/share/keyrings" "${DESTDIR}/etc/apt/sources.list.d"
            # Staged then renamed: a failed download must not leave a truncated keyring or list behind.
            keyring="${DESTDIR}/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg"
            list="${DESTDIR}/etc/apt/sources.list.d/nvidia-container-toolkit.list"
            curl -fsSL "${BASE}/gpgkey" | gpg --dearmor --yes -o "${keyring}.tmp"
            chmod 0644 "${keyring}.tmp"  # apt reads Signed-By keys as _apt, so not umask 077
            mv -f "${keyring}.tmp" "$keyring"
            curl -fsSL "${BASE}/stable/deb/nvidia-container-toolkit.list" \
                | sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' \
                > "${list}.tmp"
            mv -f "${list}.tmp" "$list"
            apt-get update -qq
            apt-get install -y -qq nvidia-container-toolkit
            ;;
        *" rhel "*|*" fedora "*|*" centos "*|*" amzn "*|*" rocky "*|*" almalinux "*)
            command -v curl >/dev/null 2>&1 || { command -v dnf >/dev/null 2>&1 && dnf install -y curl || yum install -y curl; }
            mkdir -p "${DESTDIR}/etc/yum.repos.d"
            repo="${DESTDIR}/etc/yum.repos.d/nvidia-container-toolkit.repo"
            curl -fsSL "${BASE}/stable/rpm/nvidia-container-toolkit.repo" > "${repo}.tmp"
            mv -f "${repo}.tmp" "$repo"
            if command -v dnf >/dev/null 2>&1; then
                say "Installing nvidia-container-toolkit with dnf."
                dnf install -y nvidia-container-toolkit
            else
                say "Installing nvidia-container-toolkit with yum."
                yum install -y nvidia-container-toolkit
            fi
            ;;
        *" suse "*|*" opensuse "*|*" sles "*|*" opensuse-leap "*|*" opensuse-tumbleweed "*)
            say "Installing nvidia-container-toolkit with zypper."
            zypper --non-interactive ar "${BASE}/stable/rpm/nvidia-container-toolkit.repo" || true
            zypper --non-interactive --gpg-auto-import-keys install nvidia-container-toolkit
            ;;
        *)
            fail "unrecognised distribution ($ID_LIKE). Install the toolkit by hand:
       https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html" 2
            ;;
    esac
fi

if ! configured; then
    say "Registering the nvidia runtime with Docker."
    nvidia-ctk runtime configure --runtime=docker
    if command -v systemctl >/dev/null 2>&1 && systemctl restart docker 2>/dev/null; then
        say "Docker restarted (systemctl)."
    elif command -v service >/dev/null 2>&1 && service docker restart 2>/dev/null; then
        say "Docker restarted (service)."
    else
        fail "could not restart Docker; restart it yourself, then run: docker run --rm --gpus all --platform ${PLATFORM} ubuntu:24.04 nvidia-smi -L"
    fi
    configured || fail "Docker still does not list the nvidia runtime after the install; see 'docker info'."
fi

if [[ "${UNSLOTH_TOOLKIT_VERIFY:-1}" != 0 ]]; then
    say "Verifying: docker run --rm --gpus all --platform ${PLATFORM} ubuntu:24.04 nvidia-smi -L"
    if ! docker run --rm --gpus all --platform "${PLATFORM}" ubuntu:24.04 nvidia-smi -L; then
        fail "a container could not see the GPU. On WSL 2 install a current NVIDIA Windows driver
       from nvidia.com (wsl --update updates WSL itself, not the driver); otherwise see
       https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/troubleshooting.html"
    fi
fi
if grep -qi microsoft "$PROC_VERSION" 2>/dev/null; then
    say "WSL 2 host: the GPU comes from the Windows NVIDIA driver; update it with NVIDIA's Windows installer."
fi
if ! driver_ok; then
    fail "the toolkit works, but NVIDIA driver ${DRIVER:-unknown} is below ${MIN_DRIVER}, the minimum for
       the Unsloth images. Update the driver, reboot, then run: docker run --rm --gpus all --platform ${PLATFORM} ubuntu:24.04 nvidia-smi -L" 3
fi
say "Driver ${DRIVER} meets the ${MIN_DRIVER} minimum."
say "Done. Start Unsloth with:"
cat <<'EOF'
docker run -d --name unsloth --gpus all --ipc=host \
  --ulimit memlock=-1 --ulimit stack=67108864 \
  -p 8000:8000 -p 8888:8888 \
  -v "$PWD":/workspace/host \
  -v unsloth-workspace:/workspace \
  -v "$HOME/.cache/huggingface":/workspace/.cache/huggingface \
  -v unsloth-studio:/opt/unsloth-studio \
  unsloth/unsloth && docker logs -f unsloth
EOF