Установка KeyStack LCM

LCM (Lifecycle Manager) поддерживает два варианта установки:

  • single-node — на одном узле;

  • multi-node — в режиме высокой доступности на трёх Control-узлах, образующих Kubernetes-кластер на базе k0s. Этот режим обеспечивает отказоустойчивость самой системы управления жизненным циклом платформы.

В процессе установки поддерживается возможность задать в качестве хранилища данных сторонний CSI-драйвер. С требованиями к стороннему CSI можно ознакомиться в разделе Хранилище данных LCM.

Требования к инфраструктуре

Перед началом установки убедитесь, что инфраструктура подготовлена в соответствии со следующими требованиями:

Серверы

Для развёртывания в режиме высокой доступности требуется три LCM-сервера. Минимальные требования к каждому:

  • CPU — не менее 32 ядер.

  • RAM — не менее 64 ГБ.

  • Два SSD-диска (системный и дополнительный), не менее 240 ГБ каждый.

Диски

На каждом LCM-сервере требуется наличие следующих дисков:

  • sda — системный диск с заранее установленной ОС.

  • sdb — дополнительный диск без разделов; по умолчанию задействуется встроенным CSI (Ceph OSD).

    Предупреждение

    Дополнительный диск не должен быть локальным и не должен использовать Ceph в качестве бэкенда.

Сеть

Должны быть настроены сетевые интерфейсы: IP-адреса, маски подсети, маршруты на каждом сервере.

В DNS должны быть зарегистрированы следующие записи:

  • vault.<lcm_name>.vm.lab.itkey.com

  • nexus.<lcm_name>.vm.lab.itkey.com

  • docker.<lcm_name>.vm.lab.itkey.com (указывает на nexus_vip)

  • netbox.<lcm_name>.vm.lab.itkey.com

  • gitlab.<lcm_name>.vm.lab.itkey.com

  • grafana.<lcm_name>.vm.lab.itkey.com

  • docs.<lcm_name>.vm.lab.itkey.com

  • s3.<lcm_name>.vm.lab.itkey.com

  • vmauth.<lcm_name>.vm.lab.itkey.com (указывает на grafana_vip)

  • <lcm_name>-lcm-01.vm.lab.itkey.com

  • <lcm_name>-lcm-02.vm.lab.itkey.com

  • <lcm_name>-lcm-03.vm.lab.itkey.com

В сети должны быть зарезервированы следующие VIP-адреса (их значения потребуются при настройке lcm-config.yaml):

  • mgt_vip_address

  • vault_vip

  • nexus_vip

  • netbox_vip

  • gitlab_vip

  • grafana_vip

  • docs_vip

  • s3_vip

Пользователь и доступ по SSH

  • Требуется наличие непривилегированного пользователя (например, kolla) на каждом сервере.

  • На первом LCM-узле должна быть сгенерирована SSH-ключевая пара; публичный ключ — добавлен в ~/.ssh/authorized_keys на втором и третьем узлах.

  • Пользователю должно быть предоставлено право выполнять команды от суперпользователя без пароля (NOPASSWD в sudoers).

Синхронизация времени

На всех серверах должна быть настроена синхронизация времени (NTP). Расхождение времени между узлами может привести к нестабильной работе Kubernetes и Ceph.

Swap

Swap должен быть отключён на всех серверах, поскольку этого требует Kubernetes:

$ sudo swapoff -a
$ sudo sed -i '/swap/d' /etc/fstab

Установка инсталлятора

Установка выполняется на первом LCM-узле.

Примечание

Все команды выполняются от имени непривилегированного пользователя (например, kolla). Не распаковывайте дистрибутив в домашнюю директорию — как правило, там недостаточно свободного места.

  1. Подключитесь по SSH к первому LCM-узлу.

  2. Скопируйте на него дистрибутив KeyStack любым удобным способом.

  3. Распакуйте дистрибутив платформы:

    $ tar -xf installer-k0s-ks2026.2-sberlinux-offline
    $ cd installer
    $ tar -xf lcm-k0s-ks2026.2.tar.gz
    
  4. Откройте файл конфигурации:

    $ vi lcm-k0s/lcm-config.yaml
    
  5. Настройте параметры установки.

    Предупреждение

    Параметры, перечисленные ниже, необходимо задать корректно до запуска этапов установки k0s кластера и инфраструктуры. Способов устранить ошибку в значении любого из них без полного повторного развёртывания нет — в этом случае потребуется полная переустановка платформы с чистой установкой ОС на каждом узле (ручная очистка артефактов k0s и Ceph нецелесообразна).

    Основные параметры:

    • ssh_username — имя непривилегированного пользователя, от которого выполняется установка.

    • control_plane — список FQDN узлов control plane кластера k0s (LCM-узлов). Имена узлов должны быть зарегистрированы в DNS — инсталлятор использует их для определения IP-адресов при формировании конфигурации.

    • domain_name — доменная зона LCM, в которой регистрируются все сервисы LCM. Зона не привязана к конкретному региону: один экземпляр LCM может обслуживать несколько регионов.

    • loadbalancer_ip_range — диапазон адресов MetalLB, из которого выделяются VIP-адреса сервисов LCM. Должен входить в управляющую подсеть и включать все зарезервированные VIP-адреса.

    • mgt_vip_address — VIP-адрес управления Kubernetes (k0s).

    • vault_loadbalancer_ip — VIP-адрес сервиса Vault (используется при install_vault: true).

    • nexus_loadbalancer_ip, netbox_loadbalancer_ip, gitlab_loadbalancer_ip, grafana_loadbalancer_ip, docs_loadbalancer_ip, s3_loadbalancer_ip — VIP-адреса соответствующих сервисов LCM. Каждый адрес должен входить в loadbalancer_ip_range и быть зарегистрирован в DNS в зоне domain_name.

    • keepalived_passwd — пароль для keepalived (максимум 8 символов).

    • install_ironic — управление сервисом Ironic для работы с Baremetal-узлами. При первичной установке укажите false. Ironic можно включить позже — см. раздел Включение сервиса Ironic (опционально).

    Если планируется использование стороннего CSI-драйвера вместо встроенного хранилища, дополнительно задайте следующие параметры:

    • enable_user_csi — возможность использования стороннего CSI-драйвера вместо встроенного хранилища. Задайте в данном параметре значение "true".

    • user_csi_storage_class — имя StorageClass стороннего CSI. Все компоненты LCM (GitLab, NetBox, Nexus3, Garage) будут использовать это имя при создании томов хранилища. StorageClass должен поддерживать режимы доступа ReadWriteMany (RWX) и ReadWriteOnce (RWO) и быть установлен как StorageClass по умолчанию в кластере.

    • user_csi_namespace — namespace, в котором развёрнут CSI-драйвер. Указанный namespace автоматически добавляется в исключения политик Kyverno, что позволяет подам CSI запрашивать привилегированный доступ к узлу. Убедитесь, что значение задано корректно, иначе поды CSI-драйвера будут заблокированы политиками Kyverno с ошибкой следующего вида: cannot set allowPrivilegeEscalation to false and privileged to true.

    Установка самого CSI-драйвера в этом случае будет выполняться после этапа установки инфраструктуры.

    Пример конфигурации:

    # Настройка K0s
    ssh_username: "kolla"
    control_plane:
      - "<lcm_name>-lcm-01.vm.lab.itkey.com"
      - "<lcm_name>-lcm-02.vm.lab.itkey.com"
      - "<lcm_name>-lcm-03.vm.lab.itkey.com"
    mgt_vip_address: "<mgt_vip_address>"
    ...
    # Сеть и DNS
    domain_name: "<lcm_name>.vm.lab.itkey.com"
    loadbalancer_ip_range: "<loadbalancer_ip_range>"
    vault_loadbalancer_ip: "<vault_vip>"
    vault_fqdn: "vault.<lcm_name>.vm.lab.itkey.com"
    nexus_loadbalancer_ip: "<nexus_vip>"
    nexus_fqdn: "nexus.<lcm_name>.vm.lab.itkey.com"
    docker_fqdn: "docker.<lcm_name>.vm.lab.itkey.com"
    netbox_loadbalancer_ip: "<netbox_vip>"
    netbox_fqdn: "netbox.<lcm_name>.vm.lab.itkey.com"
    gitlab_loadbalancer_ip: "<gitlab_vip>"
    gitlab_fqdn: "gitlab.<lcm_name>.vm.lab.itkey.com"
    grafana_loadbalancer_ip: "<grafana_vip>"
    grafana_fqdn: "grafana.<lcm_name>.vm.lab.itkey.com"
    vmauth_fqdn: "vmauth.<lcm_name>.vm.lab.itkey.com"
    s3_loadbalancer_ip: "<s3_vip>"
    s3_fqdn: "s3.<lcm_name>.vm.lab.itkey.com"
    docs_loadbalancer_ip: "<docs_vip>"
    docs_fqdn: "docs.<lcm_name>.vm.lab.itkey.com"
    

    Остальные параметры (размеры томов, настройки мониторинга) можно оставить по умолчанию или настроить согласно требованиям.

    Примечание

    В файле lcm-config.yaml доступны дополнительные параметры для настройки мониторинга и сервиса Ironic:

    • Мониторинг: параметры для настройки Grafana, Prometheus и алертинга можно найти в секции мониторинга файла конфигурации. По умолчанию включён базовый мониторинг компонентов LCM.

    • Ironic: после первичной установки платформы сервис Ironic можно активировать (см. раздел Включение сервиса Ironic (опционально)). Ironic необходим для управления Baremetal-узлами через IPMI/Redfish.

  6. Сохраните и закройте файл.

  7. Запустите установку:

    $ ./installer.sh lcm-k0s/lcm-config.yaml
    

    Скрипт установит необходимые пакеты в систему и выполнит настройку ОС. На этом этапе применяются все необходимые настройки ядра и устанавливаются системные зависимости.

    Примечание

    Изменение настроек SELinux требует перезагрузки узлов после выполнения данного шага.

Настройка NFS (Single-node)

Примечание

Данный этап выполняется только для single-node при использовании встроенного хранилища (NFS CSI Driver). При использовании стороннего CSI-драйвера пропустите этот шаг.

  1. Проверьте, что операционная система установлена на диск sda, а диск sdb не содержит разделов:

    $ lsblk
    

    Ожидаемый вывод:

     NAME   MAJ:MIN RM   SIZE RO TYPE MOUNTPOINTS
     sda      8:0    0   200G  0 disk
     ├─sda1   8:1    0   540M  0 part /boot/efi
     ├─sda2   8:2    0     8M  0 part
     └─sda3   8:3    0 199.5G  0 part /
     sdb      8:16   0   200G  0 disk
    

    Если пустым оказался диск sda, замените значение переменной DISK в скрипте с sdb на sda.

  2. Выполните скрипт настройки NFS:

    #!/usr/bin/env bash
    set -e
    DISK="/dev/sdb"
    sudo parted $DISK --script mklabel gpt
    sudo parted $DISK --script mkpart primary ext4 0% 100%
    sudo partprobe $DISK
    sudo mkfs.ext4 -F ${DISK}1
    sudo mkdir -p /mnt/NFS_EXPORT
    UUID=$(sudo blkid -s UUID -o value ${DISK}1)
    grep -q "$UUID" /etc/fstab || echo "UUID=$UUID /mnt/NFS_EXPORT ext4 defaults 0 0" | sudo tee -a /etc/fstab >/dev/null
    sudo mount -a
    sudo mkdir -p /mnt/NFS_EXPORT/k0s
    sudo chown -R nobody:nobody /mnt/NFS_EXPORT/k0s
    sudo chmod 777 /mnt/NFS_EXPORT/k0s
    grep -q "/mnt/NFS_EXPORT/k0s" /etc/exports || echo "/mnt/NFS_EXPORT/k0s *(rw,sync,no_subtree_check,no_root_squash)" | sudo tee -a /etc/exports >/dev/null
    sudo exportfs -ra
    sudo systemctl enable --now rpcbind
    sudo systemctl enable --now nfs-server
    

Подключение к внешнему Vault (опционально)

По умолчанию (install_vault: true) LCM развёртывает встроенный Vault, а cert-manager выпускает TLS-сертификаты сервисов через внутренний центр сертификации. Дополнительная настройка не требуется.

При необходимости LCM можно подключить к внешнему Vault (или совместимому secrets manager). В этом режиме внешний Vault используется как хранилище секретов и, опционально, как backend центра сертификации для cert-manager. Задайте install_vault: false и настройте параметры подключения в lcm-config.yaml до запуска этапов установки.

Единственный поддерживаемый метод аутентификации — AppRole. Идентификатор роли задаётся в конфигурации, а secret-id передаётся через переменную окружения при запуске этапа установки инфраструктуры (см. ниже).

install_vault: false
vault_addr: "https://vault.example.com"      # HTTPS-эндпоинт внешнего Vault
vault_namespace: ""                          # namespace Vault Enterprise / SecMan; пусто — root
vault_app_role_id: "<role-id>"               # role-id AppRole
vault_ca_bundle_file: "vault-server-ca.pem"  # CA, подписавший TLS-сертификат Vault; PEM рядом с lcm-config.yaml

Где:

  • vault_addr — URL HTTPS-эндпоинта внешнего Vault.

  • vault_namespace — namespace Vault Enterprise или secrets manager (заголовок X-Vault-Namespace); пусто — root namespace.

  • vault_app_role_id — идентификатор AppRole; secret-id передаётся отдельно через VAULT_SECRET_ID.

  • vault_ca_bundle_file — файл с CA, которым подписан TLS-сертификат HTTPS-эндпоинта Vault (PEM, в одном каталоге с lcm-config.yaml). Заполняется и для публичного CA.

Получить CA-сертификат сервера Vault:

$ openssl s_client -showcerts -connect <vault-host>:443 </dev/null 2>/dev/null \
    | sed -n '/BEGIN CERTIFICATE/,/END CERTIFICATE/p' \
    | awk '/BEGIN CERTIFICATE/{n++} n>1' > vault-server-ca.pem

Хранение секретов

Внешний Vault используется как KV-хранилище секретов региона. Задайте mount и префикс пути:

vault_engine: "secret_v2"                       # mount KV v2
vault_prefix: "deployments/gitlab.example.com"  # префикс пути; по умолчанию deployments/<gitlab_fqdn>

Выпуск TLS-сертификатов через Vault PKI

При install_vault: false cert-manager может выпускать TLS-сертификаты сервисов через PKI-движок внешнего Vault. Включите vault_pki_enabled и укажите mount, роль и корневой CA:

vault_pki_enabled: true
vault_pki: "pki_int"                          # mount pki, где выписываются сертификаты
vault_role_pki: "cert-manager"                # роль pki, выписывающая сертификаты
vault_pki_ca_bundle_file: "vault-pki-ca.pem"  # корневой CA Vault PKI (PEM рядом с lcm-config.yaml)

Чтобы получить корневой CA Vault PKI, используйте следующие команды:

$ curl -sf --cacert vault-server-ca.pem \
    https://<vault-host>/v1/pki_int/ca_chain -o vault-pki-ca.pem

Флаг -f завершает curl с ошибкой при HTTP-ответе 4xx/5xx, а --cacert проверяет TLS-соединение с Vault.

Проверьте, что в файл записана валидная цепочка сертификатов. Эндпоинт ca_chain может вернуть несколько сертификатов (промежуточный и корневой), поэтому выведите все:

$ openssl crl2pkcs7 -nocrl -certfile vault-pki-ca.pem | openssl pkcs7 -print_certs -noout

Параметры генерируемых сертификатов (ключ и subject) задаются отдельно:

vault_pki_key_algorithm: "RSA"
vault_pki_key_encoding: "PKCS1"
vault_pki_key_size: 2048
vault_pki_subject_organization: "<организация>"
vault_pki_subject_organizational_unit: "<подразделение>"
vault_pki_subject_street: "<адрес>"
vault_pki_subject_country: "<код страны>"

Здесь:

  • vault_pki_key_algorithm, vault_pki_key_encoding, vault_pki_key_size — алгоритм, кодировка и длина ключа сертификата.

  • vault_pki_subject_organization, vault_pki_subject_organizational_unit, vault_pki_subject_street, vault_pki_subject_country — поля subject (O, OU, STREET, C) выпускаемого сертификата.

Важно

Значения ключа и subject должны быть разрешены ролью Vault PKI (vault_role_pki). Если роль ограничивает эти параметры, cert-manager не сможет выписать сертификат.

Передача secret-id при установке

Secret-id AppRole не хранится в lcm-config.yaml. Его необходимо передать через переменную окружения VAULT_SECRET_ID при запуске задачи установки инфраструктуры:

$ VAULT_SECRET_ID=<secret-id> task k8s-install-infra-multi-node

Важно

Это команда этапа установки инфраструктуры, который выполняется после этапа установки k0s кластера. Не запускайте её до выполнения задачи k0s-install-multi-node (или k0s-install-single-node для single-node) — установка инфраструктуры требует уже развёрнутого Kubernetes-кластера. Приведённая здесь команда показывает только способ передачи secret-id; выполняйте её в порядке, описанном в разделе Этап установки инфраструктуры.

cert-manager создаст из secret-id Kubernetes-секрет vault-approle в namespace cert-manager и будет использовать его для аутентификации во внешнем Vault.

Этап установки k0s кластера

На первом этапе устанавливается Kubernetes-кластер. В режиме multi-node при использовании встроенного хранилища дополнительно развёртывается система хранения данных Ceph.

  1. Для первичной установки или переустановки этапа выполните:

    $ cd lcm-k0s
    $ task k0s-install-multi-node
    

    Для переустановки этапа в случае необходимости повторите выполнение команды.

  2. Проверьте состояние узлов кластера. Должен отобразиться список узлов в статусе Ready:

    $ kubectl get nodes
    
    NAME                                    STATUS   ROLES           AGE     VERSION
    <lcm_name>-lcm-01.vm.lab.itkey.com   Ready    control-plane   7m14s   v1.35.3+k0s.0
    <lcm_name>-lcm-02.vm.lab.itkey.com   Ready    control-plane   6m41s   v1.35.3+k0s.0
    <lcm_name>-lcm-03.vm.lab.itkey.com   Ready    control-plane   6m41s   v1.35.3+k0s.0
    

Этап установки инфраструктуры

  1. Запустите установку инфраструктуры:

    $ task k8s-install-infra-multi-node
    

    Для переустановки этапа в случае необходимости повторите выполнение команды.

    Примечание

    При подключении к внешнему Vault (install_vault: false) передайте secret-id AppRole через переменную VAULT_SECRET_ID, например VAULT_SECRET_ID=<secret-id> task k8s-install-infra-multi-node. Подробнее — в разделе Подключение к внешнему Vault (опционально).

  2. Проверьте успешное завершение установки:

    Проверьте состояние OSD-дисков Ceph. Должны отобразиться три пода в статусе Running:

    $ kubectl get pod -l app.kubernetes.io/name=ceph-osd -n ceph-cluster
    
    NAME                              READY   STATUS    RESTARTS   AGE
    rook-ceph-osd-0-6cb69b89b6-2n5fc  2/2     Running   0          60s
    rook-ceph-osd-1-cf7476bcf-vnlj6   2/2     Running   0          60s
    rook-ceph-osd-2-54884b7c48-2s746  2/2     Running   0          59s
    

    Если отображается No resources found in ceph-cluster namespace., ожидайте сборки кластера. Скорость сборки зависит от скорости дисков — в среднем процесс занимает около 15 минут. Дождитесь появления необходимого статуса, прежде чем переходить к следующему шагу.

    Проверьте состояние кластера Ceph. Должен отобразиться кластер в фазе Ready со статусом здоровья HEALTH_WARN:

    $ kubectl get -n ceph-cluster cephclusters.ceph.rook.io
    
    NAME           DATADIRHOSTPATH   MONCOUNT   AGE     PHASE         MESSAGE                 HEALTH   EXTERNAL   FSID
    ceph-cluster   /var/lib/rook     3          8m43s   Ready         Cluster created successfully                HEALTH_WARN              c597b53f-6c23-4b33-8d24-3e750e2a0f5b
    

Установка пользовательского CSI (опционально)

Примечание

Данный этап выполняется только при enable_user_csi: "true" в lcm-config.yaml. Параметры конфигурации стороннего CSI (enable_user_csi, user_csi_storage_class, user_csi_namespace) должны быть заданы заранее — до запуска этапов установки.

  1. Убедитесь, что namespace, в котором будет развёрнут CSI-драйвер, задан в параметре user_csi_namespace в lcm-config.yaml. Этот namespace автоматически добавляется в исключения политик Kyverno, поскольку подам CSI-драйвера обычно требуется привилегированный доступ к узлу (host-доступ). Если значение задано некорректно, поды CSI-драйвера будут заблокированы политиками Kyverno с ошибкой следующего вида: cannot set allowPrivilegeEscalation to false and privileged to true.

  2. Установите CSI-драйвер согласно документации выбранного драйвера.

    Важно

    В k0s путь к данным kubelet — /var/lib/k0s/kubelet, а не стандартный /var/lib/kubelet. Укажите этот путь в соответствующем параметре при установке CSI-драйвера (обычно kubeletDir или аналогичный).

  3. Создайте StorageClass с именем, совпадающим со значением user_csi_storage_class из lcm-config.yaml. Этот единый StorageClass используется helm-чартами всех компонентов LCM (GitLab, NetBox, Nexus3, Garage) при создании томов хранилища, поэтому отдельные StorageClass для каждого компонента создавать не требуется. StorageClass должен удовлетворять следующим требованиям:

    • имя совпадает со значением user_csi_storage_class;

    • поддерживает режимы доступа ReadWriteMany (RWX) и ReadWriteOnce (RWO);

    • установлен как StorageClass по умолчанию в кластере (аннотация storageclass.kubernetes.io/is-default-class: "true").

    Пример конфигурации:

    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
      name: <user_csi_storage_class>
      annotations:
        storageclass.kubernetes.io/is-default-class: "true"
    provisioner: <имя драйвера>  # из документации CSI-драйвера
    reclaimPolicy: Delete
    volumeBindingMode: WaitForFirstConsumer
    

Этап установки приложений

Подключение к Active Directory

Для интеграции ролевой модели KeyStack с централизованным каталогом организации выполните настройку подключения к Active Directory или LDAP-серверу. Подробные сведения о ролевой модели и принципах её работы описаны в разделе Ролевая модель KeyStack.

Для корректной интеграции требуется:

  • LDAP-сервер с поддержкой TLS (LDAPS).

  • Доступ к серверу исключительно по DNS-имени (для корректной проверки TLS-сертификатов).

  • Корневой сертификат центра сертификации в формате PEM.

  • Учётная запись для привязки (bind) с правами чтения каталога.

  1. Создайте файл с корневым сертификатом. Файл должен быть в формате PEM, иметь имя, указанное в параметре ldap_ca_cert_file, и находиться в том же каталоге, что и lcm-config.yaml. Если корневой CA выдаёт сертификаты через промежуточные центры, поместите в файл всю цепочку (корневой и промежуточные сертификаты, каждый в блоке BEGIN/END CERTIFICATE):

    $ cat <<-EOF > ~/installer/lcm-k0s/ldap-root-cert.crt
    -----BEGIN CERTIFICATE-----
    <содержимое промежуточного сертификата>
    -----END CERTIFICATE-----
    -----BEGIN CERTIFICATE-----
    <содержимое корневого сертификата>
    -----END CERTIFICATE-----
    EOF
    $ chmod 600 ~/installer/lcm-k0s/ldap-root-cert.crt
    

    Проверьте, что в файл записаны все сертификаты цепочки, а не только первый:

    $ openssl crl2pkcs7 -nocrl -certfile ~/installer/lcm-k0s/ldap-root-cert.crt | openssl pkcs7 -print_certs -noout
    

    Убедитесь, что этой цепочкой проверяется TLS-сертификат вашего сервера LDAP (значения берутся из ldap_host и ldap_port):

    $ openssl s_client -connect <ldap_host>:<ldap_port> -servername <ldap_host> \
        -CAfile ~/installer/lcm-k0s/ldap-root-cert.crt -verify_return_error </dev/null 2>/dev/null \
        | grep "Verify return code"
    

    Команда должна вывести Verify return code: 0 (ok). Любой другой код означает, что цепочка не проверяет сертификат сервера (неполная цепочка, не тот CA) или имя ldap_host отсутствует в SAN сертификата.

  2. Откройте файл конфигурации:

    $ vi ~/installer/lcm-k0s/lcm-config.yaml
    
  3. Настройте блок Active Directory:

    # Active Directory
    ldap_enable: "true"
    ldap_host: "dc-01.domain.loc"
    ldap_port: 636
    ldap_ca_cert_file: "ldap-root-cert.crt"
    ldap_bind_dn: "CN=test,CN=Users,DC=domain,DC=loc"
    ldap_user_search_basedn: "CN=Users,DC=domain,DC=loc"
    ldap_group_search_basedn: "CN=Users,DC=domain,DC=loc"
    ldap_reader_group_dn: "CN=Администраторы домена,CN=Users,DC=domain,DC=loc"
    ldap_auditor_group_dn: "CN=Администраторы домена,CN=Users,DC=domain,DC=loc"
    ldap_admin_group_dn: "CN=Администраторы домена,CN=Users,DC=domain,DC=loc"
    

    Здесь:

    • ldap_enable — включение интеграции с LDAP ("true" или "false").

    • ldap_host — FQDN сервера Active Directory. В SAN сертификата должно быть указано это имя.

    • ldap_port — порт LDAP.

    • ldap_ca_cert_file — имя файла с корневым сертификатом ЦА в формате PEM.

    • ldap_bind_dn — DN учётной записи для привязки к LDAP.

    • ldap_user_search_basedn — базовый DN для поиска пользователей.

    • ldap_group_search_basedn — базовый DN для поиска групп.

    • ldap_reader_group_dn, ldap_auditor_group_dn, ldap_admin_group_dn — DN групп AD, соответствующих ролям KeyStack.

  4. Создайте Kubernetes-секреты для привязки к LDAP. Способ зависит от того, включена ли автоматическая ротация паролей (см. Автоматическая ротация паролей LDAP (опционально)):

    1. Введите пароль от LDAP в окружение:

      $ export ldap_bind_password='<пароль учётной записи привязки>'
      
    2. Задайте режим установки в переменной MODE (multi-node или single-node — в соответствии с выбранным режимом) и создайте секреты.

      Секрет для NetBox:

      $ export MODE=multi-node
      $ export netbox_namespace=$(helmfile -e "${MODE}" -l name=netbox list --output json | yq -p json '.[0].namespace')
      $ kubectl create namespace "${netbox_namespace}" --dry-run=client -o yaml | kubectl apply -f -
      $ kubectl create -n "${netbox_namespace}" secret generic netbox-ldap \
      --from-literal=email_password='' \
      --from-literal=secret_key=$(openssl rand -base64 48 | tr -d '\n') \
      --from-literal=ldap_bind_password="${ldap_bind_password}" \
      --from-literal=ldap_bind_dn='<DN учётной записи привязки>'
      

      Секрет для GitLab:

      $ export gitlab_namespace=$(helmfile -e "${MODE}" -l name=gitlab list --output json | yq -p json '.[0].namespace')
      $ kubectl create namespace "${gitlab_namespace}" --dry-run=client -o yaml | kubectl apply -f -
      $ kubectl create -n "${gitlab_namespace}" secret generic gitlab-ldap \
      --from-literal=ldap_bind_password="${ldap_bind_password}" \
      --from-literal=bind_dn='<DN учётной записи привязки>'
      

    Важно

    Ключ bind_dn в секрете gitlab-ldap обязателен: GitLab читает DN привязки из секрета, и без этого ключа под не запустится.

Подробная информация о ролях KeyStack и их привилегиях описана в разделах Настройка интеграции с LDAP/AD и Описание ролевой модели.

Автоматическая ротация паролей LDAP (опционально)

При enable_lcm_ad_passwords_rotation: true LCM синхронизирует пароли учётных записей привязки GitLab и NetBox из внешнего Vault и периодически ротирует их с помощью CronJob. Функция работает только с внешним Vault (install_vault: false); при install_vault: true ротация отключается.

Для каждого приложения в Vault LDAP Secrets Engine должны существовать две static-role с именами <mask>-1 и <mask>-2 (рабочая и резервная). CronJob проверяет TTL неактивной роли и при достижении порога ротирует её пароль, после чего helmfile переключает приложение на обновлённую учётную запись.

Настройте блок в lcm-config.yaml:

enable_lcm_ad_passwords_rotation: true
lcm_exp_threshold_days: 30                  # ротировать, если до истечения TTL роли осталось меньше порога (дней)
lcm_pass_rotation_job_sch: "0 7 * * *"      # cron-расписание CronJob (ежедневно в 07:00)
vault_secman: false                         # true → SecMan (endpoint creds/), false → Vault (endpoint static-cred/)
vault_ldap: "ldap"                          # mount-путь LDAP Secrets Engine в Vault
vault_ldap_static_endpoint: ""             # авто: static-cred (Vault) или creds (SecMan); задать при нестандартном пути
lcm_gitlab_ldap_roles_mask: "gitlab-ldap"   # базовое имя ролей GitLab: <mask>-1, <mask>-2
lcm_netbox_ldap_roles_mask: "netbox-ldap"   # базовое имя ролей NetBox: <mask>-1, <mask>-2
# Явные имена ролей (опционально); если заданы — маска игнорируется
lcm_gitlab_ldap_role_a: ""
lcm_gitlab_ldap_role_b: ""
lcm_netbox_ldap_role_a: ""
lcm_netbox_ldap_role_b: ""
# DN учётной записи привязки (аккаунт -1) соответствующего приложения
ldap_gitlab_bind_dn: "CN=gitlab-ldap-1,CN=Users,DC=domain,DC=loc"
ldap_netbox_bind_dn: "CN=netbox-ldap-1,CN=Users,DC=domain,DC=loc"

Где:

  • enable_lcm_ad_passwords_rotation — включение ротации паролей LDAP для GitLab и NetBox.

  • lcm_exp_threshold_days — порог TTL в днях; пароль ротируется, если до истечения осталось меньше указанного значения.

  • lcm_pass_rotation_job_sch — расписание CronJob в формате cron.

  • vault_secman — режим хранилища: true для SecMan, false для Vault.

  • vault_ldap — mount-путь LDAP Secrets Engine в Vault.

  • vault_ldap_static_endpoint — endpoint чтения учётных данных; по умолчанию определяется автоматически.

  • lcm_gitlab_ldap_roles_mask, lcm_netbox_ldap_roles_mask — базовые имена static-role; используются роли <mask>-1 и <mask>-2.

  • lcm_gitlab_ldap_role_a/_b, lcm_netbox_ldap_role_a/_b — явные имена ролей, если они не соответствуют шаблону <mask>-1/<mask>-2.

  • ldap_gitlab_bind_dn, ldap_netbox_bind_dn — DN учётной записи привязки (аккаунт -1) соответствующего приложения.

Параметры подключения к Vault (vault_addr, vault_namespace, vault_ca_bundle_file, vault_app_role_id) берутся из общей секции настроек Vault. CronJob аутентифицируется в Vault по AppRole; secret-id хранится в секрете cert-manager/vault-approle.

Примечание

Региональная ротация сервисных учётных записей (через globals.d/REGION.yml и переменные GitLab CI) описана отдельно в разделе Автоматическая ротация пользователей и паролей.

Сбор логов в сторонний сервис (опционально)

  1. Откройте файл конфигурации:

    $ vi ~/installer/lcm-k0s/lcm-config.yaml
    
  2. Настройте блоки Внешний Syslog (UDP) и Внешний OpenSearch/Elasticsearch:

    # Настройки экспорта логов во внешние системы
    # Внешний Syslog (UDP)
    external_syslog_enabled: "true"                         # Установить в "true", если нужно подключение к syslog
    external_syslog_endpoint: "syslog.example.com:514"      # Адрес:порт syslog сервера
    
    # Внешний OpenSearch/Elasticsearch
    external_opensearch_enabled: "true"                     # Установить в "true", если нужно подключение к opensearch
    external_opensearch_endpoint: "https://opensearch.example.com:9200"
    external_opensearch_username: "admin"                   # Логин для подключения
    external_opensearch_password: ""                        # Пароль для подключения
    external_opensearch_index: "k8s-logs-%Y.%m.%d"          # Паттерн индекса (опционально)
    external_opensearch_verify_tls: "true"                  # Включение проверки TLS сертификата
    external_opensearch_ca_cert_file: "opensearch-ca.crt"   # Файл сертификата в формате PEM; должен находиться в той же директории, что и lcm-config.yaml
    

Index Template OpenSearch

Создайте index template в OpenSearch до первой записи логов. Тип flat_object предотвращает mapper_parsing_exception, dynamic_templates обрабатывает поле log как объект или строку в зависимости от пода:

  • kubernetes.pod_labels, pod_annotations, namespace_labels, node_labels — Vector хранит Kubernetes metadata с точками (app.kubernetes.io/name) как вложенные объекты.

  • log — может быть строкой или JSON-объектом; dynamic_templates автоматически выбирает подходящий тип.

curl -sk -u "admin:<пароль>" -X PUT \
  "https://<endpoint>/_index_template/k8s-logs-mapping" \
  -H 'Content-Type: application/json' -d '{
    "index_patterns": ["k8s-logs-*"],
    "priority": 300,
    "template": {
      "settings": {"number_of_replicas": 0},
      "mappings": {
        "dynamic_templates": [
          {
            "log_as_object": {
              "path_match": "log",
              "match_mapping_type": "object",
              "mapping": {"type": "flat_object"}
            }
          },
          {
            "log_as_string": {
              "path_match": "log",
              "match_mapping_type": "string",
              "mapping": {
                "type": "text",
                "fields": {"keyword": {"type": "keyword", "ignore_above": 256}}
              }
            }
          }
        ],
        "properties": {
          "kubernetes": {
            "properties": {
              "pod_labels":       {"type": "flat_object"},
              "pod_annotations":  {"type": "flat_object"},
              "namespace_labels": {"type": "flat_object"},
              "node_labels":      {"type": "flat_object"}
            }
          }
        }
      }
    }
  }'

Примечание

Нижеследующие шаги выполняются всегда вне зависимости от необходимости авторизации в Active Directory или сбора логов.

Запустите установку приложений:

$ task k8s-install-apps-multi-node

Предупреждение

Команда ожидает готовности Ceph-кластера перед установкой приложений. Если Ceph ещё не завершил инициализацию OSD (фаза Progressing), команда завершится с ошибкой по таймауту. В этом случае дождитесь, пока кластер перейдёт в фазу Ready:

$ kubectl get -n ceph-cluster cephclusters.ceph.rook.io -w

После перехода в Ready запустите команду повторно.

Примечание

Команда устанавливает:

  • Vault + Vault Unsealer

  • GitLab (CNPG PostgreSQL + Redis HA + Gitaly + Garage S3)

  • Garage (S3-хранилище для GitLab)

  • NetBox (CNPG PostgreSQL + Redis HA)

  • Nexus3

  • Victoria Metrics K8s Stack (Grafana, vmstorage, vmselect, vminsert)

  • Victoria Logs Cluster

  • KeyStack Docs

  • Ironic (если включён)

Проверка успешного завершения

  1. Проверьте список установленных Helm-чартов:

    $ helm list -A
    

    Должны отобразиться все компоненты LCM в статусе deployed:

    • calico

    • cert-manager

    • cnpg-operator

    • external-snapshotter-crd

    • garage (S3-бэкенд для GitLab)

    • gateway-api-crd

    • gitlab

    • istio-base

    • istio-csr

    • istiod

    • keystack-docs

    • kyverno

    • kyverno-policies

    • metallb

    • netbox

    • nexus3

    • redis-ha (для GitLab и NetBox)

    • rook-ceph-cluster

    • rook-ceph-system

    • snapshot-controller

    • vault

    • vault-unsealer

    • vlc-cluster

    • vmks

  2. Проверьте состояние всех подов:

    $ kubectl get all -A
    

    Все поды должны быть в статусе Running или Completed.

  3. Проверьте состояние хранилищ:

    # S3-кластер Garage: статус нод (3 HEALTHY) и список бакетов
    kubectl exec -n lcm-garage garage-0 -c garage -- /usr/local/bin/garage status
    kubectl exec -n lcm-garage garage-0 -c garage -- /usr/local/bin/garage bucket list
    # Redis HA
    kubectl get pods -n lcm-gitlab | grep redis
    

    Примечание

    Объектное хранилище GitLab (артефакты CI/CD, LFS, uploads, packages, pages, terraform-state, backups) вынесено в отдельный S3-кластер Garage (namespace lcm-garage), заменивший встроенный MinIO. Redis HA обслуживает кэш GitLab и Sidekiq в режиме multi-node.

Наполнение LCM данными

После успешной установки всех компонентов выполните наполнение LCM начальными данными:

$ cd ..
$ bash upload.sh lcm-k0s/lcm-config.yaml

На каждый запуск скрипта создаётся лог-файл в формате upload_<дата>_<время>.log.

Настройка ролевой модели в GitLab и NetBox

Ролевая модель в GitLab и NetBox настраивается автоматически при включении интеграции с LDAP (см. раздел Ролевая модель KeyStack).

Важно

При включении интеграции с LDAP и ролевой модели учётная запись root в GitLab блокируется. Для автоматизированных операций в GitLab используется технический пользователь ks-admin. Пароль и персональный токен доступа (PAT) пользователя ks-admin сохраняются в Vault по пути secret_v2 / deployments / <LCM FQDN> / secrets / accounts.

После включения ролевой модели доступ к интерфейсам GitLab и NetBox получают пользователи с ролями admin, security_auditor и reader. Пользователи с ролями member, operator_vm, app_operator и os_operator не получают доступ к этим интерфейсам.

Если требуется дополнительная настройка, обратитесь к разделам Ролевая модель в NetBox и Ролевая модель в GitLab в Руководстве администратора.

Включение сервиса Ironic (опционально)

Сервис Ironic обеспечивает provisioning baremetal-узлов по протоколам IPMI/Redfish с загрузкой по сети (PXE). При первичной установке рекомендуется оставить install_ironic: false и включить Ironic после развёртывания основных компонентов LCM.

Сетевые требования

Ironic использует выделенную сеть provisioning (PXE). Перед включением убедитесь, что подготовлено следующее:

  • На каждом LCM-узле есть отдельный сетевой интерфейс, подключённый к PXE-сети.

  • Выделен VIP-адрес для Ironic API (ironic_ip_address) в подсети provisioning. Адрес управляется keepalived на интерфейсе provisioning.

  • В DNS зарегистрирована запись ironic.<lcm_name>.vm.lab.itkey.comironic_ip_address.

Подготовка самих baremetal-серверов описана в разделе Подготовка baremetal-узлов для установки KeyStack.

Настройка параметров

Откройте файл lcm-config.yaml и задайте параметры Ironic:

install_ironic: true                       # включение сервиса Ironic
ironic_interface: "eth2"                   # интерфейс PXE-сети на LCM-узлах
ironic_network_cidr: "192.168.140.0/24"    # подсеть provisioning
ironic_range_begin: "192.168.140.101"      # начало диапазона DHCP
ironic_range_end: "192.168.140.250"        # конец диапазона DHCP
ironic_dns_address: "192.168.140.254"
ironic_gateway_address: "192.168.140.254"
ironic_ip_address: "192.168.140.100"       # VIP Ironic API
ironic_fqdn: "ironic.<lcm_name>.vm.lab.itkey.com"
ironic_keepalived_vrid: 1                  # VRRP ID (1–255), уникален в L2-сегменте
ironic_keepalived_passwd: ""               # пароль VRRP (≤8 символов); пусто — без аутентификации

Где:

  • install_ironic — включение сервиса Ironic (true или false).

  • ironic_interface — сетевой интерфейс PXE-сети на LCM-узлах.

  • ironic_network_cidr — подсеть сети provisioning.

  • ironic_range_begin, ironic_range_end — границы диапазона DHCP для PXE-загрузки.

  • ironic_dns_address, ironic_gateway_address — DNS-сервер и шлюз, выдаваемые узлам по DHCP.

  • ironic_ip_address — VIP-адрес Ironic API.

  • ironic_fqdn — FQDN Ironic API; должен указывать на ironic_ip_address.

  • ironic_keepalived_vrid, ironic_keepalived_passwd — параметры VRRP keepalived для VIP Ironic. VRID должен быть уникален в пределах L2-сегмента.

Несколько DHCP-диапазонов (PXE relay)

Если provisioning выполняется в нескольких подсетях (например, через PXE relay по стойкам), задайте дополнительные диапазоны в параметре ironic_dhcp_ranges. В каждом диапазоне обязателен префикс в networkCIDR:

ironic_dhcp_ranges:
  - name: pxe-main
    networkCIDR: "192.168.140.0/24"
    rangeBegin: "192.168.140.101"
    rangeEnd: "192.168.140.250"
    gatewayAddress: "192.168.140.254"
  - name: pxe-rack1
    networkCIDR: "192.168.141.0/27"
    rangeBegin: "192.168.141.2"
    rangeEnd: "192.168.141.29"
    gatewayAddress: "192.168.141.30"

Запуск установки

Компоненты Ironic относятся к инфраструктурному (ironic-operator, mariadb-operator) и прикладному (ironic, proxy-nginx) этапам. После настройки параметров повторно выполните оба этапа:

$ cd lcm-k0s
$ task k8s-install-infra-multi-node
$ task k8s-install-apps-multi-node

При использовании нескольких DHCP-диапазонов (PXE relay) дополнительно настройте статические маршруты к relay-подсетям:

$ task ironic-host-routes

Проверка установки Ironic

Убедитесь, что компоненты Ironic развёрнуты, а поды находятся в статусе Running:

$ helm list -n lcm-ironic
$ kubectl get pods -n lcm-ironic