Доброго времени суток. Меня зовут Андросов Михаил, по профессии я MLOps-инженер.
Я всегда использую Windows. Уже много лет я работаю через связку Visual Studio Code и WSL.
Мне регулярно приходится эксплуатировать GPU-зависимые приложения в кластере Kubernetes. Иногда так получается, что тестовые ресурсы заняты, а что-то проверить надо.
В моём домашнем ПК установлена NVIDIA RTX 4090 на 24 GB, что является довольно мощной видеокартой с большим объёмом памяти (для домашнего устройства).
Встал вопрос, как мне быстро развернуть домашний Kubernetes кластер с возможностью использовать мои GPU ресурсы? Решение есть!
В данной статье я покажу, как на Windows развернуть кластер Kubernetes из одного узла с поддержкой GPU и time-slicing, используя WSL.
Альтернативы WSL
Изучение просторов сети подсказало, что есть решение проброса GPU на Hyper-V (наверное, WSL под капотом его и использует).
Я его не проверял, мне это решение сходу кажется долгой дорогой - нужно создать ВМ, сконфигурировать ВМ, установить дистрибутив, скачать и установить драйверы.
Можно установить какой-нибудь дистрибутив Linux на свою домашнюю станцию. Но это тоже как-то неудобно.
Подготовка
Версия Windows
Вам понадобится любая современная версия Windows 10 или Windows 11. Если вы периодически устанавливаете обновления, то проблем не возникнет.
Установка WSL
Установить компоненты WSL и стандартный дистрибутив Ubuntu 24.04 можно одной командой.
wsl --install⚠️ Для работы WSL должна быть включена виртуализация в BIOS/UEFI (Intel Virtualization Technology или AMD-V).
Подробности можно почитать у Microsoft
В статье буду производить развертывание кластера на дистрибутиве, запускаемом по умолчанию - Ubuntu 24.04.
Отключение swap в WSL
После установки WSL остановите его через PowerShell.
wsl --shutdownСоздайте файл C:\Users\%UserProfile%\.wslconfig (если его нет) и укажите в нём следующее значение.
[wsl2]
swap=0Запуск дистрибутива в WSL
Нужный дистрибутив в WSL можно запустить через команду wsl в PowerShell или, как я сам его запускаю, через Visual Studio Code.

Установка свежих драйверов NVIDIA
Можете установить их вручную или через Nvidia App (пользуюсь, удобно).
Нужны обычные пользовательские драйверы - Game Ready.
https://www.nvidia.com/en-eu/geforce/drivers/
Статический IP адрес для WSL
Сеть WSL по умолчанию работает в режиме NAT. WSL при рестарте может получить новый IP-адрес, что помешает нормальной работе кластера. Необходимо зафиксировать IP адрес для интерфейса в системе внутри WSL.
Если у вас Windows 11
Есть множество способов задать статический IP адрес.
Например, включите networkingMode=mirrored в C:\Users\%UserProfile%\.wslconfig и задайте IP адрес из вашей домашней сети через сетевой адаптер в «Настройках параметров адаптера».
Если у вас Windows 10
Это мой сценарий, пока не переехал на Windows 11. Здесь уже сложнее.
Можно попробовать установить сетевой адаптер WSL в режим работы «Внешняя сеть» через Диспетчер Hyper-V или PowerShell. В таком случае, он, как и домашний ПК будет получать IP адрес от вашего роутера, который можно будет зафиксировать. Но это работает ненадежно и может не получиться. На разных ПК было по-разному, где-то получилось, где-то нет.
Есть разные способы решения проблемы, но я для себя нашёл самый быстрый и простой - это поднять интерфейс WireGuard внутри системы в WSL.
В любом случае, на этом подробно останавливаться не буду. Делайте так, как считаете правильным.
(Опционально) Установка и настройка WireGuard в WSL
Установите WireGuard и сгенерируйте ключ.
sudo apt update
sudo apt install -y wireguard wireguard-tools
sudo mkdir -p /etc/wireguard
wg genkey | sudo tee /etc/wireguard/wg0.key
sudo chmod 600 /etc/wireguard/wg0.keyСоздайте конфигурационный файл wg0.conf.
sudo tee /etc/wireguard/wg0.conf >/dev/null <<EOF
[Interface]
Address = 10.8.0.1/32
ListenPort = 51820
PrivateKey = $(sudo cat /etc/wireguard/wg0.key)
EOFИ поднимите интерфейс wg0.
sudo systemctl enable --now wg-quick@wg0Выполните проверки и убедитесь, что всё работает.
systemctl status wg-quick@wg0
ping 10.8.0.1

Далее будут производиться привычные многим любителям Kubernetes настройки.
Подготовка системы для Kubernetes
Обновите вашу систему в WSL и установите перечисленные пакеты.
sudo apt update && sudo apt upgrade -y
sudo apt install -y apt-transport-https ca-certificates curl gnupgВключите нужные модули ядра.
sudo tee /etc/modules-load.d/k8s.conf <<EOF
overlay
br_netfilter
EOF
sudo modprobe overlay
sudo modprobe br_netfilterИ сетевые параметры.
sudo tee /etc/sysctl.d/k8s.conf <<EOF
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
sudo sysctl --systemПодключение репозиториев, установка и настройка containerd
Нужно подключить репозитории Docker и Kubernetes.
На момент чтения статьи, проверьте, какая версия Kubernetes самая свежая и подставьте её версию.
Kubernetes репозиторий.
ver="v1.34"
curl -fsSL https://pkgs.k8s.io/core:/stable:/$ver/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
sudo chmod 644 /etc/apt/keyrings/kubernetes-apt-keyring.gpg # allow unprivileged APT programs to read this keyring
echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/$ver/deb/ /" | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo chmod 644 /etc/apt/sources.list.d/kubernetes.list # helps tools such as command-not-found to work correctlyDocker репозиторий.
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
sudo tee /etc/apt/sources.list.d/docker.sources <<EOF
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
Components: stable
Signed-By: /etc/apt/keyrings/docker.asc
EOFУстановите containerd.
sudo apt update
sudo apt install containerd.io -yПо умолчанию, конфигурационный файл containerd почти пустой.
Его необходимо сгенерировать и включить использование cgroup.
sudo containerd config default | sudo tee /etc/containerd/config.toml > /dev/null
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/g' /etc/containerd/config.tomlТеперь нужно перезапустить службу containerd и добавить её в автозагрузку.
sudo systemctl restart containerd
sudo systemctl enable containerdОсталось установить компоненты Kubernetes.
sudo apt install kubelet kubeadm kubectl -yИнициализация Kubernetes кластера
Если у вас Windows 11 и/или вы как то иначе сделали себе статичный IP адрес в WSL, подставьте его.
myip="10.8.0.1"
sudo kubeadm init --apiserver-advertise-address=$myip --pod-network-cidr=10.244.0.0/16Результатом должен стать поднятый control-plane узел.

Подключение к Kubernetes кластеру
Скопируйте конфигурационный файл с контекстом себе в домашний каталог.
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/configТеперь можно проверить, что kubectl работает.
kubectl get noСостояние узла точно будет NotReady, потому что не установлен сетевой плагин.


Установка сетевого плагина
Можете взять другой сетевой плагин. Для простоты и удобства беру Flannel.
Разверните самую свежую версию.
kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.ymlПодождите какое-то время и проверьте узел еще раз, он должен быть в состоянии Ready.

Снятие taint с узла
Рекомендую сразу снять taint control-plane с узла, чтобы не приходилось дальше везде указывать tolerations.
kubectl taint no --all node-role.kubernetes.io/control-plane-Кластер запущен и работает. Теперь подготовим его для работы с GPU.
Подготовка GPU к работе в кластере Kubernetes
Проверка доступности GPU в WSL
На самом деле, при самом первом старте системы в WSL можно сразу проверить что ваша GPU доступна.
Для этого нужно выполнить команду nvidia-smi и получить вывод по вашей GPU (если у вас нет проблем с драйверами).

Что остается сделать:
Установить NVIDIA Container Toolkit.
Настроить containerd.
Установить и сконфигурировать NVIDIA Device Plugin.
Установка NVIDIA Container Toolkit и настройка containerd
Добавьте репозиторий от Nvidia.
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg \
&& curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \
sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.listУстановите NVIDIA Container Toolkit.
Проверьте актуальную версию в инструкции.
sudo apt update
export NVIDIA_CONTAINER_TOOLKIT_VERSION=1.18.1-1
sudo apt-get install -y \
nvidia-container-toolkit=${NVIDIA_CONTAINER_TOOLKIT_VERSION} \
nvidia-container-toolkit-base=${NVIDIA_CONTAINER_TOOLKIT_VERSION} \
libnvidia-container-tools=${NVIDIA_CONTAINER_TOOLKIT_VERSION} \
libnvidia-container1=${NVIDIA_CONTAINER_TOOLKIT_VERSION}Теперь нужно сконфигурировать containerd новым runtime Nvidia с помощью nvidia-ctk.
После конфигурации, службу containerd нужно обязательно перезапустить.
sudo nvidia-ctk runtime configure --runtime=containerd --set-as-default --config=/etc/containerd/config.toml
sudo systemctl restart containerd
sudo systemctl status containerd
На этом подготовка GPU в системе для Kubernetes завершена.
Установка и настройка NVIDIA Device Plugin
Можно установить NVIDIA Device Plugin сразу из этого манифеста и больше ничего не делать.
Но для локальных экспериментов, одной GPU мне будет мало и я хочу дополнительно настроить Time-Slicing, что бы можно было запросить у кластера хотя бы 2 GPU ресурса.
Для этого нужно создать ConfigMap , указанный ниже.
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: ConfigMap
metadata:
name: nvidia-device-plugin-config
namespace: kube-system
data:
config.yaml: |-
version: v1
sharing:
timeSlicing:
resources:
- name: nvidia.com/gpu
replicas: 2 # сколько "кусков" будет из одной GPU
EOFТеперь нужно создать DaemonSet, где подключим созданную конфигурацию для NVIDIA Device Plugin через ConfigMap.
Проверьте актуальную версию образа k8s-device-plugin и подставьте свежую, если уже вышла.
kubectl apply -f - <<'EOF'
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: nvidia-device-plugin-daemonset
namespace: kube-system
spec:
selector:
matchLabels:
name: nvidia-device-plugin-ds
updateStrategy:
type: RollingUpdate
template:
metadata:
labels:
name: nvidia-device-plugin-ds
spec:
tolerations:
- key: nvidia.com/gpu
operator: Exists
effect: NoSchedule
priorityClassName: "system-node-critical"
containers:
- image: nvcr.io/nvidia/k8s-device-plugin:v0.18.0
name: nvidia-device-plugin-ctr
env:
- name: CONFIG_FILE
value: /config/config.yaml
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
volumeMounts:
- name: kubelet-device-plugins-dir
mountPath: /var/lib/kubelet/device-plugins
- name: nvidia-device-plugin-config
mountPath: /config
readOnly: true
volumes:
- name: kubelet-device-plugins-dir
hostPath:
path: /var/lib/kubelet/device-plugins
type: Directory
- name: nvidia-device-plugin-config
configMap:
name: nvidia-device-plugin-config
EOFПосле запуска pod'a, проверьте его логи. Не должно быть ошибок по инициализации вашей GPU.
kubectl -n kube-system logs ds/nvidia-device-plugin-daemonset
Также при выполнении команды kubectl describe no , теперь вы должны увидеть 2 доступных GPU.

Финальная проверка
Запустите pod, который выполнит в контейнере команду nvidia-smi.
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: Pod
metadata:
name: nvidia-smi
spec:
restartPolicy: OnFailure
containers:
- name: nvidia-smi
image: nvidia/cuda:13.0.2-runtime-ubuntu24.04
command: ["nvidia-smi"]
resources:
limits:
nvidia.com/gpu: 1
EOFСкачивание образа займет какое-то время. Как контейнер отработает, проверьте его логи.
kubectl logs nvidia-smi
Если вывод есть - значит GPU доступно в кластере Kubernetes!
Заключение
Теперь можно использовать GPU как во взрослом кластере. Запускайте инференсы, ноутбуки, тестируйте Helm-чарты и делайте всё что хотели делать, но не могли делать без GPU.
Если убрать из статьи пояснения по командам и всё положить в скрипт, поднятие кластера с GPU на WSL можно сократить до каких-нибудь 10-15 минут времени.
Попробуйте, это очень удобно :)
Надеюсь, статья окажется для вас полезной.
