---
title: Развертывание K3S в моей домашней лаборатории
slug: k3s-homelab-setup
canonical: https://shady2k.ru/posts/k3s-homelab-setup/
date: 2023-03-31
kind: article
author: human
lang: ru
summary: "Первый Kubernetes дома: ставлю K3s, в том числе в LXC-контейнер
  Proxmox, и разворачиваю NetBox с PostgreSQL и Redis через
  HelmChart-манифесты."
tags:
  - homelab
  - kubernetes
---

Как и у большинства технических энтузиастов, у меня дома есть сервера, и я всегда нахожусь в поиске новых технологий, которые могут меня заинтересовать. На работе у нас есть полноценный управляемый Kubernetes, но хотелось проверить его в деле не боясь что-то сломать. Я уже знал, что K8S довольно тяжелый и его сложно конфигурировать. В поисках легковесной альтернативы я наткнулся на [K3S](https://k3s.io/), легкий и простой в использовании дистрибутив Kubernetes, и решил попробовать его в своей домашней лаборатории. В этой статье я поделюсь своим опытом установки и использования k3s, а также приведу несколько практических примеров того, как я использовал его для развертывания таких приложений, как NetBox, популярного инструмента управления IP-адресами и инфраструктурой центров обработки данных.

## Установка k3s

Установка k3s была простой и быстрой. Просто выполнил команду из официальной документации по процессу установки. Сразу рекомендую отключить traefik и встроенный балансер.

```bash
curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="server --disable=traefik --disable=servicelb --cluster-init" sh -s -
```

`cluster-init` нужен, если вы хотите установать дополнительные ноды. После кортокой установки я проверил работоспособность k3s, проверив статус службы k3s и используя команду kubectl для получения списка доступных узлов.  
Для виртуализации я использую proxmox и если у вас, как и у меня возникла идея развернуть ноду в lxc контейнере, имеет смысл пробовать только с локальным хранением данных без longhorn. После плясок с бубном мне удалось его заставить работать, но он напрочь не хочет работать с longhorn, там [какой-то баг с iscsi](https://github.com/longhorn/longhorn/issues/2585).

Итак, чтобы развернуть k3s в lxc контейнер proxmox, нужен дистрибутив с ubuntu. Затем убедитесь, что на хостовой машине гипервизора включены модули ядра.  
Для работы k3s в контейнере LXC, необходимо включить модули ядра `overlay` и `br_netfilter`. Добавьте их в файл `/etc/modules`:

```
overlay
br_netfilter
```

Затем выполните следующие команды, чтобы активировать модули без перезагрузки системы:

```bash
  /sbin/lsmod | /bin/grep overlay || /sbin/modprobe overlay
  /sbin/lsmod | /bin/grep br_netfilter || /sbin/modprobe br_netfilter
```

У меня Proxmox версии 6, в 7 параметры отличаются. Добавьте следующие настройки к стандартным параметрам контейнера LXC в Proxmox:

```bash
mp0: /lib/modules,mp=/lib/modules
lxc.hook.autodev: sh -c "mknod -m 0644 ${LXC_ROOTFS_MOUNT}/dev/kmsg c 1 11"
lxc.apparmor.profile: unconfined
lxc.cap.drop:
lxc.cgroup.devices.allow: a
lxc.mount.auto: proc:rw sys:rw
```

После установки k3s, отредактируйте файл `/etc/systemd/system/k3s-agent.service` и добавьте следующую строку перед первым `ExecStartPre`:

```bash
ExecStartPre=mount --make-rshared /
```

Эта настройка позволяет k3s-agent использовать разделяемые монтирования корневой файловой системы. После выполнения этих шагов, вы сможете успешно развернуть k3s в контейнере LXC на платформе Proxmox и использовать его для управления вашими контейнеризированными приложениями.

Для корректного завершения работы виртуальной машины с k3s и остановки всех связанных процессов при выключении системы, рекомендуется добавить специальную системную службу. Эта служба будет запускать скрипт `k3s-killall.sh` перед завершением работы системы. Иначе остановка всех процессов виртуальной машины может занять до 10 минут.  
Создайте файл `/etc/systemd/system/shutdown-k3s.service` с следующим содержимым:

```bash
[Unit]
Description=Kill containerd-shims on shutdown
DefaultDependencies=false
Before=shutdown.target umount.target

[Service]
ExecStart=/usr/local/bin/k3s-killall.sh
Type=oneshot

[Install]
WantedBy=shutdown.target
```

После создания файла активируйте службу с помощью команды:

```bash
systemctl enable shutdown-k3s.service
```

Обратите внимание, что имя этой службы shutdown-k3s не должно начинаться с `k3s-`. В противном случае скрипт `k3s-killall.sh` будет пытаться остановить эту службу, что может вызвать проблемы.  
Добавление и активация этой службы гарантирует корректное завершение работы всех связанных с k3s процессов при выключении системы и предотвращает возможные проблемы, связанные с неконтролируемым завершением работы контейнеров.

## Развертывание NetBox

Начинать нужно создания namespace. Одним из важнейших аспектов эффективного управления ресурсами Kubernetes является использование пространств имен. Создавая новое пространство имен для каждого ресурса, вы можете лучше организовать, изолировать и управлять приложениями и компонентами в кластере k3s. Есть несколько преимуществ:

- группировка связанных ресурсов в одном пространстве имен помогает поддерживать более чистую и понятную структуру кластера. Такой подход упрощает задачи управления и облегчает поиск определенных ресурсов в случае необходимости
- изоляция ресурсов. Разделение ресурсов на отдельные пространства имен обеспечивает дополнительный уровень изоляции, гарантируя, что проблема в одном пространстве имен не окажет прямого влияния на ресурсы в другом пространстве имен.
- настраиваемые ограничения ресурсов: пространства имен позволяют устанавливать определенные лимиты ресурсов и квоты для различных групп ресурсов, что упрощает управление потреблением ресурсов в кластере. Выделяя соответствующие лимиты ресурсов для каждого пространства имен, вы можете гарантировать, что ни одно приложение или компонент не будет перегружать ресурсы за счет других.
- контроль доступа: такой подход позволяет предоставлять определенные разрешения пользователям или командам, позволяя им управлять ресурсами в пределах назначенных пространств имен и ограничивая доступ к другим частям кластера.

```yaml
apiVersion: v1
kind: Namespace
metadata:
  name: netbox
```

Затем нам потребуется БД postgresql, устанавливать его я буду с помощью Helm и чарта Bitnami. Практически везде предлагают устанавливать Helm чарты командой `helm install`, однако я рекомендую использовать для этого манифест:

```yaml
apiVersion: helm.cattle.io/v1
kind: HelmChart
metadata:
  name: netbox-postgresql
  namespace: kube-system
spec:
  chart: postgresql
  repo: https://charts.bitnami.com/bitnami
  targetNamespace: netbox
  version: 12.2.6
  valuesContent: |-
    auth:
      database: "netbox"
      username: "netbox"
      existingSecret: netbox-postgresql-secrets
    persistence.size: 1Gi
    primary:
      persistence:
        enabled: true
        existingClaim: netbox-postgresql-pvc
```

- `apiVersion: helm.cattle.io/v1`: эта строка указывает версию API для ресурса HelmChart, предоставляемого проектом Rancher.
- `kind: HelmChart`: тип создаваемого ресурса.
- `metadata`: этот раздел содержит метаданные о ресурсе HelmChart, включая имя и пространство имен.
- `name`: имя ресурса HelmChart.
- `namespace: kube-system`: пространство имен, в котором будет создан ресурс HelmChart. В данном случае это пространство имен "kube-system".
- `spec`: этот раздел содержит спецификацию для ресурса HelmChart, включая информацию о чарте, репозитории и значениях конфигурации.
- `chart: postgresql`: название чарта Helm, которая будет развернута, в данном случае - PostgreSQL.
- `repo: https://charts.bitnami.com/bitnami`: URL-адрес репозитория Helm.
- `targetNamespace: netbox`: пространство имен, в котором будет создан deployment PostgreSQL.
- `version: 12.2.6`: версия образа PostgreSQL, который будет развернут, рекомендуется указывать конкретное значение, а не latest, чтобы не было сюрпризов с обновлением.
- `valuesContent`: в этом разделе содержатся пользовательские значения.
- `auth`: этот подраздел содержит конфигурации, связанные с аутентификацией.
- `database`: "netbox": название создаваемой базы данных.
- `username`: "netbox": имя пользователя для базы данных.
- `existingSecret: netbox-postgresql-secrets`: имя существующего секрета Kubernetes, содержащего учетные данные базы данных.
- `persistence.size: 1Gi`: размер постоянного хранилища, выделяемого для базы данных PostgreSQL.

Теперь создадим PVC для предоставления хранилища для базы данных PostgreSQL, указав размер хранилища 1Gi и используя класс хранилища Longhorn.

```yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: netbox-postgresql-pvc
  namespace: netbox
spec:
  storageClassName: longhorn
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
```

Хранение учетных данных базы данных в Kubernetes Secret  
Затем я создал секрет Kubernetes Secret для безопасного хранения учетных данных базы данных, включая имя базы данных NetBox, имя пользователя и пароль, а также пароль PostgreSQL. Позже этот секрет был использован в конфигурации HelmChart для развертывания PostgreSQL.

Обратите внимание, что при использовании `data` вам потребуется все значения преобразовать в base64 с ключом `-n`:

```bash
echo -n 'password123' | base64   
cGFzc3dvcmQxMjM=
```

Можно использовать `stringData`, тогда перобразовывать не нужно, однако вы можете столкнуться с ограничениями yaml по содержанию спец. символов.

```yaml
apiVersion: v1
kind: Secret
metadata:
  name: netbox-postgresql-secrets
  namespace: netbox
type: Opaque
data:
  database: bmV0Ym94
  username: bmV0Ym94
  password: cGFzc3dvcmQxMjM=
  postgres-password: cGFzc3dvcmQxMjM=
```

Еще нам нужен Redis, развернем и его:

```yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: netbox-redis-pvc
  namespace: netbox
spec:
  storageClassName: longhorn
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi

---

apiVersion: v1
kind: Secret
metadata:
  name: netbox-redis-secrets
  namespace: netbox
type: Opaque
data:
  password: cGFzc3dvcmQxMjM=

---

apiVersion: helm.cattle.io/v1
kind: HelmChart
metadata:
  name: netbox-redis
  namespace: kube-system
spec:
  chart: redis
  repo: https://charts.bitnami.com/bitnami
  targetNamespace: netbox
  version: 17.9.2
  valuesContent: |-
    master:
      persistence:
        enabled: true
        existingClaim: netbox-redis-pvc
    replica:
      replicaCount: 0
      persistence:
        enabled: false
    auth:
      enabled: true
      existingSecret: netbox-redis-secrets
      existingSecretPasswordKey: "password"
```

Ну и сам Netbox:

```yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: netbox-pvc
  namespace: netbox
spec:
  storageClassName: longhorn
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi

---

apiVersion: helm.cattle.io/v1
kind: HelmChart
metadata:
  name: netbox
  namespace: kube-system
spec:
  chart: netbox
  repo: https://charts.boo.tc
  targetNamespace: netbox
  version: 4.1.1
  valuesContent: |-
    replicaCount: 1
    postgresql:
      enabled: false
      postgresqlUsername: "netbox"
      postgresqlDatabase: "netbox"
    redis:
      enabled: false
    persistence:
      enabled: true
      existingClaim: "netbox-pvc"
    externalDatabase:
      host: "netbox-postgresql"
      existingSecretName: netbox-postgresql-secrets
      existingSecretKey: password
    cachingRedis:
      host: "netbox-redis-master"
      existingSecretName: netbox-redis-secrets
      existingSecretKey: "password"
    tasksRedis:
      host: "netbox-redis-master"
      existingSecretName: netbox-redis-secrets
      existingSecretKey: "password"
    secretKey: "XXXXXXXXXX"
```

И остался ingress, для того, чтобы можно было получить к интерфейсу доступ по доменному имени, ограничим доступ локальной сетью:

```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: netbox-ingress
  namespace: netbox
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
    nginx.ingress.kubernetes.io/whitelist-source-range: 192.168.1.0/24
spec:
  ingressClassName: nginx
  rules:
  - host: netbox.example.com
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: netbox
            port:
              number: 80
  tls:
    - hosts:
      - netbox.example.com
      secretName: netbox-secret-tls
```

## Заключение

Мой опыт использования k3s в домашней лаборатории был неоднозначным. Простота установки и администрирования k3s мне понравилась. Но на изучение манифестов и понимание логики у меня ушло более недели времени по вечерам. Я рекомендую вам тоже испытать эти неземные ощущения от использования Kubernetes дома.
