← Kubernetes локально
8 мин · просто · Урок 4 из 8

k3d: k3s в контейнерах

Самый быстрый локальный кластер, что приезжает вместе с k3s из коробки и почему стоит посмотреть на темп релизов проекта.

k3d - обёртка, которая запускает k3s в контейнерах Docker. Чтобы понять инструмент, нужно сначала понять, что такое k3s.

k3s - это облегчённый дистрибутив Kubernetes. Тот же API, те же объекты, те же манифесты, но собранный в один бинарь и с урезанным набором внутренних компонентов. Из коробки к нему приложены ingress-контроллер, балансировщик для Service типа LoadBalancer и провижнер хранилища. Сам k3s рассчитан на Linux и ставится на машину напрямую.

k3d берёт этот бинарь и упаковывает ноды в контейнеры. Получается то же, что делает kind, только с другим дистрибутивом внутри. Отсюда и скорость: в официальном бенчмарке minikube time-to-k8s (v1.36.0, linux/amd64) k3d поднимает кластер за 19,9 секунды против 33,0 у kind и 50,8 у minikube.

Версия и темп проекта

Актуальная версия - v5.9.0 от 2 июня 2026. По умолчанию она даёт Kubernetes v1.35.5, образ k3s v1.35.5-k3s1. Требуется Docker версии 20.10.5 и выше.

Тут стоит сказать прямо. Активность у проекта самая слабая среди популярных инструментов для локального кластера. Между релизом v5.8.3 (февраль 2025) и v5.9.0 (июнь 2026) прошло почти 16 месяцев. Последний коммит в репозитории датирован 12 июня 2026.

Это не значит, что проект мёртв: релиз вышел, коммиты есть, инструмент работает. Но темп заметно ниже, чем у kind, где релизы выходят каждые несколько месяцев. Что с этим делать - решать по своей ситуации. Для локального обучения полуторагодовая пауза почти ничего не меняет. Для чего-то, что живёт годами и требует быстрых обновлений под новые версии Kubernetes, разница между проектами уже ощутима.

Установка

curl -s https://raw.githubusercontent.com/k3d-io/k3d/main/install.sh | bash
brew install k3d
choco install k3d

Жизненный цикл кластера

k3d cluster create lab --servers 1 --agents 2 -p "8081:80@loadbalancer" --api-port 6550
k3d cluster create lab --image rancher/k3s:v1.36.4-k3s1
k3d cluster delete lab
k3d cluster list

Терминология у k3s своя. Server - это то, что в обычном Kubernetes зовётся control plane. Agent - рабочая нода. Так что --servers 1 --agents 2 даёт кластер из трёх нод.

Версия Kubernetes пинится не отдельным флагом, а образом k3s: --image rancher/k3s:v1.36.4-k3s1. Формат тега - версия Kubernetes плюс суффикс ревизии k3s.

Порты наружу

У k3d два разных способа отдать порт хосту, и путать их не стоит.

Первый - через встроенный балансировщик:

-p "8081:80@loadbalancer"

Порт 8081 на хосте уходит в порт 80 на loadbalancer-ноде. Это то, что нужно для Ingress и для Service типа LoadBalancer.

Второй - напрямую в конкретную ноду, для NodePort:

-p "8082:30080@agent:0"

Здесь agent:0 - это первая рабочая нода.

ФлагКуда уходит портДля чего
-p "8081:80@loadbalancer"на loadbalancer-ноду, порт 80Ingress и Service типа LoadBalancer
-p "8082:30080@agent:0"прямо в первую рабочую нодуNodePort

Соблазн пробросить сразу весь диапазон NodePort документация допускает, но предупреждает о последствиях: Docker поднимает отдельный процесс-прокси на каждый проброшенный порт. На диапазоне 30000-32767 это тридцать с лишним тысяч процессов, и машину может подвесить.

Что приезжает из коробки

Вместе с k3s в кластере сразу появляются:

Последний пункт заметно облегчает жизнь. В kind и minikube metrics-server ставится отдельно, в k3d он уже на месте.

Лишнее отключается флагами k3s, которые k3d пробрасывает через --k3s-arg:

k3d cluster create --k3s-arg "--disable=traefik@server:*" --k3s-arg "--disable=servicelb@server:*"

Формат значения - ARG@NODEFILTER. Без нод-фильтра команда не отработает, поэтому суффикс @server:* обязателен. Отключать Traefik приходится, когда на его место ставят другой ingress-контроллер, а servicelb - когда нужен MetalLB.

Что обычно ломается

ZFS. На файловой системе ZFS кластер с несколькими server-нодами не поднимается.

BTRFS. Требует прокинуть в ноду устройство маппера: -v /dev/mapper:/dev/mapper.

NodeHasDiskPressure. Ноды - контейнеры, и диск у них общий с хостом. Когда на хосте кончается место, kubelet видит нехватку и начинает выселять поды. Причина при этом снаружи кластера, и внутри её не найти.

Доступ к хосту из подов. Обращаться к сервисам, которые крутятся на хост-машине, нужно по имени host.k3d.internal. Адрес localhost внутри пода указывает на сам под.

Коротко

Прошли материал до конца? Отметьте урок пройденным.