Kubernetes в домашней лаборатории обычно пугает тремя вещами: десятком компонентов, требованием к памяти и сложной установкой. K3s решает это одним бинарником и systemd-службой: control plane, containerd, CNI и базовые дополнения уже внутри. Ниже — установка на один узел, проверка рабочим приложением, постоянное хранилище, второй узел и обслуживание без магии.
Что такое k3s и чем он отличается
K3s — сертифицированный дистрибутив Kubernetes от Rancher, собранный в один исполняемый файл. Из стандартной поставки убрали тяжёлые и необязательные компоненты, заменили etcd на SQLite по умолчанию, а облачные контроллеры и CSI-плагины оставили подключаемыми. В комплекте идут CoreDNS, metrics-server, локальный провижионер хранилища local-path, балансировщик ServiceLB и ingress-контроллер Traefik. Для домашней лаборатории это готовый набор: поставил — и можно запускать поды.
Важно понимать границы: один сервер — это не отказоустойчивый кластер. SQLite-база и единственный control plane означают простой при перезагрузке. Для HA нужны три сервера и встроенный etcd, но дома чаще достаточно одного узла и резервной копии состояния.
Требования и установка
Минимально достаточно 2 ядер, 2 ГБ памяти и 20 ГБ диска; на практике control plane с несколькими подами комфортно живёт при 4 ГБ. Поддерживаются x86_64, arm64 и armhf, поэтому подойдёт и мини-ПК, и Raspberry Pi. Установка занимает минуту: скрипт скачивает бинарник, создаёт службу и запускает её.
curl -sfL https://get.k3s.io | sh -
sudo systemctl status k3s --no-pager
sudo k3s kubectl get nodes -o wide
sudo k3s kubectl get pods -A
Kubeconfig по умолчанию лежит в /etc/rancher/k3s/k3s.yaml и доступен только root. Чтобы работать обычным пользователем, скопируйте файл в домашний каталог и замените 127.0.0.1 на адрес сервера — иначе kubectl с другой машины будет искать API локально.
mkdir -p ~/.kube
sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
sudo chown $USER:$USER ~/.kube/config
sed -i 's/127.0.0.1/192.168.1.50/' ~/.kube/config
Проверьте, что kubectl видит кластер. Если команда ругается на сертификат, убедитесь, что в конфиге именно IP или DNS-имя сервера, а не localhost.
Настройка через config.yaml
Параметры установки удобно хранить в /etc/rancher/k3s/config.yaml: так их видно в git и не нужно вспоминать флаги. Для дома разумно сразу зафиксировать адрес узла и отключить лишнее, если вы планируете ставить свой ingress.
write-kubeconfig-mode: "0644"
node-ip: 192.168.1.50
disable:
- traefik
- servicelb
cluster-cidr: 10.42.0.0/16
service-cidr: 10.43.0.0/16
После правки перезапустите службу. Не меняйте cluster-cidr и service-cidr на работающем кластере: поды и сервисы получат несогласованные адреса, и понадобится переустановка. Если оставляете Traefik, порты 80 и 443 будут заняты им; если отключаете — ставьте ingress-nginx или Caddy отдельно.
Первое приложение и проверка
Начните с простого Deployment и NodePort: это проверит, что планировщик, сеть и kube-proxy работают. Образ nginx:alpine небольшой, поэтому стартует быстро даже на слабом железе.
kubectl create deployment web --image=nginx:alpine
kubectl scale deployment web --replicas=2
kubectl expose deployment web --port=80 --type=NodePort
kubectl get pods,svc -o wide
В выводе service найдите порт вида 30080 и откройте http://192.168.1.50:30080. Если страница не открывается, проверьте, что поды в статусе Running, а не CrashLoopBackOff, и что порт не блокирует локальный firewall. Дальше удобнее описывать ресурсы в YAML и применять через kubectl apply -f.
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 2
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: whoami
image: traefik/whoami
ports:
- containerPort: 80
Хранилище и ingress
Local-path создаёт каталог на узле и отдаёт его поду через PersistentVolumeClaim. Этого достаточно для домашних сервисов, но данные привязаны к конкретному узлу: для базы с репликацией лучше смотреть на Longhorn или внешний NFS.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: app-data
spec:
accessModes:
- ReadWriteOnce
storageClassName: local-path
resources:
requests:
storage: 1Gi
Для доступа по имени хоста установите ingress-контроллер и опишите Ingress. Не забудьте добавить запись в /etc/hosts или локальный DNS, иначе браузер не найдёт whoami.lab.
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/baremetal/deploy.yaml
kubectl get pods -n ingress-nginx
kubectl get ingress
Для маршрутизации по имени хоста опишите Ingress. Сохраните манифест в web-ingress.yaml и примените через kubectl apply.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web
spec:
ingressClassName: nginx
rules:
- host: whoami.lab
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web
port:
number: 80
Проверка через curl с заголовком Host помогает отделить проблему DNS от проблемы маршрутизации.
Второй узел и обслуживание
Агент подключается к серверу по токену из /var/lib/rancher/k3s/server/node-token. На сервере должны быть открыты 6443/tcp и 8472/udp, между узлами — 10250/tcp. Не публикуйте API в интернет: для домашней лаборатории достаточно локальной сети.
sudo cat /var/lib/rancher/k3s/server/node-token
curl -sfL https://get.k3s.io | K3S_URL=https://192.168.1.50:6443 K3S_TOKEN=ТОКЕН sh -
kubectl get nodes
kubectl top nodes
Обновление выполняйте повторным запуском установочного скрипта с нужным каналом. Перед этим сохраните состояние: для SQLite остановите службу и скопируйте /var/lib/rancher/k3s/server/db, для etcd сделайте снимок командой k3s etcd-snapshot save.
sudo systemctl stop k3s
sudo cp -a /var/lib/rancher/k3s/server/db /var/backups/k3s-db-$(date +%F)
sudo systemctl start k3s
curl -sfL https://get.k3s.io | INSTALL_K3S_CHANNEL=stable sh -
Диагностика
Когда под не запускается, идите по цепочке: служба, события, описание пода, логи контейнера. K3s прячет kubectl за подкомандой, но можно пользоваться и обычным kubectl с корректным kubeconfig.
- Pending — планировщику не хватает ресурсов или под не проходит по affinity.
- ImagePullBackOff — неверный образ или недоступный registry.
- CrashLoopBackOff — контейнер падает сразу после старта, смотрите kubectl logs.
- ContainerCreating — проблема с CNI, томом или секретом.
sudo journalctl -u k3s -n 100 --no-pager
kubectl get events -A --sort-by=.lastTimestamp
kubectl describe pod POD_NAME -n NAMESPACE
sudo k3s crictl ps
Типичные причины: нехватка памяти, неверный образ, отсутствующий StorageClass, занятый порт. События показывают причину быстрее логов. Если не стартует сам сервер, смотрите journalctl -u k3s: там будут ошибки базы, сертификатов или занятого порта 6443.
Итог
K3s даёт полноценный Kubernetes без многочасовой настройки: один скрипт, один конфиг, понятные дополнения. Для дома достаточно одного узла с local-path и ingress, а второй узел добавляется токеном. Держите config.yaml в git, делайте резервную копию базы перед обновлением, не выставляйте API наружу и проверяйте состояние через события и journalctl. Этого набора хватает, чтобы лаборатория оставалась воспроизводимой, а не собиралась заново после каждой перезагрузки.