Подготовка к Production#

Введение#

Пример исходных данных

  • Имя кластера = my-cluster

  • Публичный домен = dapp.test.un.sbt

  • Адрес пула в Metallb (или используемого внешнего балансировщика) = <metallb-pool-ip>

  • Ingress-контроллеры соответствуют шаблону: *.my-cluster.dapp.test.un.sbt

  • Узлы соответствуют шаблону: dapp-cp-01-my-cluster.dapp.test.un.sbt, dapp-worker-01-my-cluster.dapp.test.un.sbt

  • Кластер имеет 3 control plane узла: <controlplane1-ip>, <controlplane2-ip>, <controlplane3-ip>

  • Кластер имеет 6 worker узлов: <worker1-ip> - <worker6-ip> (3 из них являются ingress-узлами)

В данном разделе описаны шаги по подготовке высокодоступного кластера для использования в Production-средах.

В качестве хранилища используется NFS-сервер и инструмент Csi-driver-nfs, входящий в состав DropApp. Этот инструмент (Tool) доступен к установке через Dapp-operator.

В разделе описаны настройки:

  • настройка сервера HAProxy на Platform V SberLinux OS для балансировки запросов к Kube-apiserver;

  • настройка NFS-сервера на Platform V SberLinux OS для постоянного хранения данных (PersistentVolumes);

  • настройка DropAppCongifuration:

    • создание выделенных узлов для Ingress-controller;

    • увеличение лимитов ресурсов Ingress-controller;

    • настройка сертификатов: ingressTLS и clusterTLS;

    • настройка Kube-apiserver (audit, extraSANs);

    • настройка controlPlaneEndpoint;

    • включение шифрования Etcd;

    • настройка PodSecurityAdmission по умолчанию;

    • подключение каталога LDAP для управления пользователями;

    • настройка подсети для pod и service K8S;

    • включение Hubble-ui для сетевого мониторинга.

  • установка инструмента Csi-driver-nfs;

  • установка компонентов:

    • Monitoring с постоянным хранением;

    • Alerting с постоянным хранением;

    • Logging с постоянным хранением;

    • Security;

    • AiOps для автоматического анализа ошибок в кластере;

    • GitOps, DevOps для работы с пользовательскими приложениями.

Подготовка инфраструктуры#

Кроме стандартных действий, описанных в разделе «Подготовка окружения», необходимо настроить:

  • серверы, которые будут выполнять функции балансировщиков нагрузки для запросов к API кластера;

  • NFS-сервер для постоянного хранения данных.

Сервер HAProxy#

Создайте машину на Platform V SberLinux OS с IP-адресом <haproxy-ip>. Подключите на нее репозитории BaseOS и AppStream. Зарегистрируйте в DNS запись:

<haproxy-ip> apiserver.my-cluster.dapp.test.un.sbt

Примечание

В примере указан один сервер HAProxy. Для большей доступности можно создать два сервера и установить на каждый Keepalived. Это позволит автоматически переключаться между серверами в случае сбоя одного из них. Адрес в DNS будет виртуальным IP (VIP), который предоставляет Keepalived.

Для настройки сервера HAProxy выполните следующие шаги:

  1. Создайте скрипт hp.sh:

    hp.sh
    #!/bin/bash
    set -euo pipefail
     
    # =============================================================================
    # Setup HAProxy for Kubernetes API
    # Mode: TCP passthrough with SNI routing
    # No TLS termination, no certs required
    # Stats available on HTTP port 8443
    # =============================================================================
     
    SCRIPT_NAME=$(basename "$0")
     
    usage() {
        cat << EOF
    Usage: $SCRIPT_NAME [OPTIONS]
     
    Setup HAProxy to load balance kube-apiserver in TCP passthrough mode.
    Uses SNI to route traffic for the given hostname.
     
    Options:
        --hostname HOSTNAME       DNS name for SNI (e.g. apiserver.my-cluster.dapp.test.un.sbt)
        --port PORT               kube-apiserver port (default: 6443)
        --stats-port PORT         HAProxy stats port (default: 8443)
        --controlplane LIST       Comma-separated list of control-plane IPs
        --cluster-name NAME       Cluster name (used in server names)
        --help                    Show this help
     
    Example:
        sudo $SCRIPT_NAME \\
          --hostname apiserver.my-cluster.dapp.test.un.sbt \\
          --port 6443 \\
          --stats-port 8443 \\
          --controlplane <controlplane1-ip>,<controlplane2-ip>,<controlplane3-ip> \\
          --cluster-name my-cluster
    EOF
    }
     
    # =============================================================================
    # Parse arguments
    # =============================================================================
     
    HOSTNAME=""
    API_PORT="6443"
    STATS_PORT="8443"
    CONTROLPLANE=""
    CLUSTER_NAME=""
     
    while [[ $# -gt 0 ]]; do
        case $1 in
            --hostname)
                HOSTNAME="$2"
                shift; shift
                ;;
            --port)
                API_PORT="$2"
                shift; shift
                ;;
            --stats-port)
                STATS_PORT="$2"
                shift; shift
                ;;
            --controlplane)
                CONTROLPLANE="$2"
                shift; shift
                ;;
            --cluster-name)
                CLUSTER_NAME="$2"
                shift; shift
                ;;
            --help)
                usage
                exit 0
                ;;
            *)
                echo "Error: Unknown argument $1"
                usage
                exit 1
                ;;
        esac
    done
     
    # Validate required args
    if [[ -z "$HOSTNAME" ]]; then
        echo "Error: --hostname is required"
        usage
        exit 1
    fi
     
    if [[ -z "$CONTROLPLANE" ]]; then
        echo "Error: --controlplane is required"
        usage
        exit 1
    fi
     
    if [[ -z "$CLUSTER_NAME" ]]; then
        echo "Error: --cluster-name is required"
        usage
        exit 1
    fi
     
    # Split IPs by comma
    IFS=',' read -r -a CP_ARRAY <<< "$CONTROLPLANE"
    if [[ ${#CP_ARRAY[@]} -eq 0 ]]; then
        echo "Error: No control-plane nodes specified"
        exit 1
    fi
     
    # Trim whitespace
    for i in "${!CP_ARRAY[@]}"; do
        CP_ARRAY[i]=$(echo "${CP_ARRAY[i]}" | xargs)
    done
     
    # =============================================================================
    # Check root privileges
    # =============================================================================
     
    if [[ $EUID -ne 0 ]]; then
        echo "Error: This script must be run as root (use sudo)"
        exit 1
    fi
     
    echo "=== Setting up HAProxy for Kubernetes API ==="
    echo "Hostname: $HOSTNAME"
    echo "API Port: $API_PORT"
    echo "Stats Port: $STATS_PORT"
    echo "Control-plane nodes: ${CP_ARRAY[*]}"
    echo "Cluster Name: $CLUSTER_NAME"
    echo "Mode: TCP passthrough (no TLS termination)"
    echo
     
    # =============================================================================
    # 1. Install HAProxy
    # =============================================================================
     
    echo "=== Installing HAProxy ==="
    if dnf list installed haproxy &>/dev/null; then
        echo "HAProxy already installed"
    else
        dnf install -y haproxy
        echo "HAProxy installed"
    fi
     
    # =============================================================================
    # 2. Generate HAProxy config
    # =============================================================================
     
    HAPROXY_CFG="/etc/haproxy/haproxy.cfg"
    HAPROXY_CFG_BAK="/etc/haproxy/haproxy.cfg.bak"
     
    if [[ -f "$HAPROXY_CFG" ]]; then
        if [[ ! -f "$HAPROXY_CFG_BAK" ]]; then
            cp "$HAPROXY_CFG" "$HAPROXY_CFG_BAK"
            echo "Backup saved: $HAPROXY_CFG_BAK"
        fi
    fi
     
    echo "=== Generating HAProxy configuration ==="
     
    # Backend servers
    BACKEND_SERVERS=""
    for i in "${!CP_ARRAY[@]}"; do
        IP=$(echo "${CP_ARRAY[i]}" | xargs)
        if ! [[ "$IP" =~ ^[0-9]{1,3}(\.[0-9]{1,3}){3}$ ]]; then
            echo "Error: Invalid IP address: $IP"
            exit 1
        fi
        printf -v INDEX "%02d" $(($i + 1))
        SERVER_NAME="dapp-cp-${INDEX}-${CLUSTER_NAME}"
        BACKEND_SERVERS+="    server $SERVER_NAME $IP:$API_PORT check fall 3 rise 2\n"
    done
     
    # Generate config
    cat > "$HAPROXY_CFG" << EOF
    #---------------------------------------------------------------------
    # Global settings
    #---------------------------------------------------------------------
    global
        log         <local2-ip> local2
        chroot      /var/lib/haproxy
        pidfile     /var/run/haproxy.pid
        maxconn     4000
        user        haproxy
        group       haproxy
        daemon
        stats socket /var/lib/haproxy/stats
     
    #---------------------------------------------------------------------
    # Common defaults
    #---------------------------------------------------------------------
    defaults
        mode                    tcp
        log                     global
        option                  tcplog
        option                  dontlognull
        timeout connect         5s
        timeout client          30s
        timeout server          30s
        timeout check           10s
        retries                 3
     
    #---------------------------------------------------------------------
    # Frontend: Kubernetes API with SNI
    #---------------------------------------------------------------------
    frontend k8s-api-frontend
        bind *:$API_PORT
        tcp-request inspect-delay 5s
        tcp-request content accept if { req.ssl_hello_type 1 }
        acl sni_ok req.ssl_sni -i $HOSTNAME
        tcp-request content reject if !sni_ok
        use_backend k8s-api-backend if sni_ok
     
    #---------------------------------------------------------------------
    # Backend: Control-plane nodes
    #---------------------------------------------------------------------
    backend k8s-api-backend
        balance roundrobin
        option tcp-check
    $(echo -e "$BACKEND_SERVERS")
     
    #---------------------------------------------------------------------
    # Stats page (HTTP)
    #---------------------------------------------------------------------
    listen stats
        bind *:$STATS_PORT
        mode http
        stats enable
        stats uri /stats
        stats realm Platform\ V\ DropApp\ Cluster\ Statistics
        stats auth admin:dapp
        stats hide-version
        stats refresh 5s
    EOF
     
    echo "Configuration written to $HAPROXY_CFG"
     
    # =============================================================================
    # 3. Validate config
    # =============================================================================
     
    echo "=== Validating HAProxy config ==="
    haproxy -c -f "$HAPROXY_CFG"
    if [[ $? -ne 0 ]]; then
        echo "Error: HAProxy configuration is invalid"
        exit 1
    fi
     
    # =============================================================================
    # 4. Configure firewalld
    # =============================================================================
     
    if systemctl is-active --quiet firewalld; then
        echo "=== Configuring firewalld ==="
        for PORT in "$API_PORT" "$STATS_PORT"; do
            if ! firewall-cmd --state &>/dev/null; then
                continue
            fi
            if ! firewall-cmd --list-ports | grep -q "\b$PORT/tcp"; then
                firewall-cmd --permanent --add-port="$PORT/tcp"
                echo "Port $PORT/tcp added to firewalld"
            else
                echo "Port $PORT/tcp already open"
            fi
        done
        firewall-cmd --reload
    else
        echo "firewalld is not active — skipping"
    fi
     
    # =============================================================================
    # 5. Configure SELinux
    # =============================================================================
     
    if command -v getenforce &>/dev/null && [[ $(getenforce) != "Disabled" ]]; then
        echo "=== Configuring SELinux ==="
        if ! command -v semanage &>/dev/null; then
            echo "Installing policycoreutils-python-utils..."
            dnf install -y policycoreutils-python-utils
        fi
     
        for PORT in "$API_PORT" "$STATS_PORT"; do
            if ! semanage port -l | grep -q "\b$PORT\b.*http_port_t"; then
                semanage port -a -t http_port_t -p tcp "$PORT"
                echo "SELinux: port $PORT added to http_port_t"
            else
                echo "SELinux: port $PORT already allowed"
            fi
        done
    else
        echo "SELinux is disabled or not installed — skipping"
    fi
     
    # =============================================================================
    # 6. Enable and start HAProxy
    # =============================================================================
     
    echo "=== Enabling and starting HAProxy ==="
    systemctl enable haproxy --now
    if ! systemctl is-active --quiet haproxy; then
        echo "Error: HAProxy failed to start"
        journalctl -u haproxy -n 20 --no-pager
        exit 1
    fi
    echo "HAProxy is now running and enabled on boot"
     
    # =============================================================================
    # Final message
    # =============================================================================
     
    echo
    echo "SUCCESS: HAProxy setup complete!"
    echo "Mode: TCP passthrough (no TLS termination)"
    echo "API endpoint: $HOSTNAME:$API_PORT"
    echo "Stats page: http://<IP>:$STATS_PORT/stats"
    echo "Login: admin / dapp"
    echo "Realm: Platform V DropApp Cluster Statistics"
    echo "Control-plane nodes: ${CP_ARRAY[*]}"
    echo "Server names: dapp-cp-01-$CLUSTER_NAME, dapp-cp-02-$CLUSTER_NAME, ..."
    echo "Note: Ensure DNS resolves $HOSTNAME to this machine"
    
  2. Сделайте файл исполняемым:

    chmod +x ./hp.sh
    
  3. Запустите скрипт:

    sudo ./hp.sh --hostname apiserver.my-cluster.dapp.test.un.sbt --controlplane <controlplane1-ip>,<controlplane2-ip>,<controlplane3-ip> --cluster-name my-cluster
    
  4. Проверьте службу haproxy, ее статус должен быть active (running):

    sudo systemctl status haproxy
    

    Пример вывода:

     haproxy.service - HAProxy Load Balancer
       Loaded: loaded (/usr/lib/systemd/system/haproxy.service; enabled; vendor preset: disabled)
       Active: active (running) since Tue 2025-09-02 07:59:30 UTC; 52s ago
      Process: 13760 ExecStartPre=/usr/sbin/haproxy -f $CONFIG -f $CFGDIR -c -q $OPTIONS (code=exited, status=0/SUCCESS)
     Main PID: 13763 (haproxy)
        Tasks: 2 (limit: 10881)
       Memory: 2.8M
       CGroup: /system.slice/haproxy.service
               ├─13763 /usr/sbin/haproxy -Ws -f /etc/haproxy/haproxy.cfg -f /etc/haproxy/conf.d -p /run/haproxy.pid
               └─13765 /usr/sbin/haproxy -Ws -f /etc/haproxy/haproxy.cfg -f /etc/haproxy/conf.d -p /run/haproxy.pid
     
    Sep 02 07:59:30 dapp-infra-01-my-cluster systemd[1]: Starting HAProxy Load Balancer...
    Sep 02 07:59:30 dapp-infra-01-my-cluster systemd[1]: Started HAProxy Load Balancer.
    
  5. Перейдите на страницу http://apiserver.my-cluster.dapp.test.un.sbt:8443/stats (статистика доступна по умолчанию на порту 8443).

  6. Используйте для входа данные admin/dapp (указанные, для примера, в скрипте hp.sh):

    haproxy-server.png

Сервер HAProxy настроен.

Сервер NFS#

Создайте машину на Platform V SberLinux OS с IP-адресом <nfs-ip>. Подключите на нее репозитории BaseOS и AppStream. Зарегистрируйте в DNS запись:

<nfs-ip> nfs.dapp.test.un.sbt

Для настройки сервера NFS выполните следующие шаги:

  1. Создайте скрипт nfs.sh.sh:

    nfs.sh
    #!/bin/bash
    # =============================================================================
    # nfs.sh
    # Purpose: Install and configure NFS server
    # =============================================================================
     
    set -euo pipefail
     
    # Configuration
    EXPORT_DIR="/export/nfs"
    NFS_SERVICE="nfs-server"
    FIREWALL_SERVICES=("nfs" "mountd" "rpc-bind")
     
    # Ensure running as root
    if [[ $EUID -ne 0 ]]; then
      echo "This script must be run as root" >&2
      exit 1
    fi
     
    echo "Installing NFS server..."
     
    # Step 1: Update and install nfs-utils
    if ! rpm -q nfs-utils >/dev/null 2>&1; then
      dnf update -y
      dnf install -y nfs-utils
    fi
     
    # Step 2: Create export directory
    mkdir -p "$EXPORT_DIR"
     
    # Step 3: Ensure permissions
    chmod 777 "$EXPORT_DIR"
    chown nobody:nobody "$EXPORT_DIR"
     
    # Step 4: Configure /etc/exports (only if entry not present)
    EXPORT_ENTRY="$EXPORT_DIR *(rw,sync,no_root_squash,no_all_squash,no_subtree_check)"
    if ! grep -Fqs "$EXPORT_DIR" /etc/exports; then
      echo "$EXPORT_ENTRY" > /etc/exports
    else
      # Ensure correct export line
      grep -v "^$EXPORT_DIR" /etc/exports > /tmp/exports.tmp || true
      echo "$EXPORT_ENTRY" >> /tmp/exports.tmp
      mv /tmp/exports.tmp /etc/exports
    fi
     
    # Step 5: Reload export configuration
    exportfs -rav
     
    # Step 6: Enable and start NFS server
    if ! systemctl is-enabled "$NFS_SERVICE" >/dev/null 2>&1; then
      systemctl enable "$NFS_SERVICE"
    fi
    systemctl restart "$NFS_SERVICE"
     
    # Step 7: Handle firewalld if active
    if systemctl is-active firewalld --quiet; then
      echo "firewalld is active. Adding NFS services to firewall..."
      for service in "${FIREWALL_SERVICES[@]}"; do
        if ! firewall-cmd --state >/dev/null 2>&1; then
          echo "firewalld is not running, starting..."
          systemctl start firewalld
        fi
     
        if ! firewall-cmd --list-services --permanent | grep -wq "$service"; then
          firewall-cmd --permanent --add-service="$service"
        fi
      done
      firewall-cmd --reload
    else
      echo "firewalld is not active. Skipping firewall configuration."
    fi
     
    # Step 8: Get primary IP address
    IP_ADDR=$(ip -4 addr show scope global | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | head -1)
    if [[ -z "$IP_ADDR" ]]; then
      echo "Failed to detect IP address" >&2
      exit 1
    fi
     
    # Step 9: Output result
    echo
    echo "NFS server setup complete"
    echo "Server IP: $IP_ADDR"
    echo "Export directory: $EXPORT_DIR"
    echo "Use in Kubernetes StorageClass:"
    echo "  server: $IP_ADDR"
    echo "  share: $EXPORT_DIR"
    

    При необходимости измените права на каталог export, или иной путь, заданный в переменной EXPORT_DIR скрипта.

  2. Сделайте файл исполняемым:

    chmod +x ./nfs.sh
    
  3. Запустите скрипт:

    sudo ./nfs.sh
    
  4. Проверьте службу nfs-server, ее статус должен быть active (exited):

    sudo systemctl status nfs-server
    

    Пример вывода:

     nfs-server.service - NFS server and services
       Loaded: loaded (/usr/lib/systemd/system/nfs-server.service; enabled; vendor preset: disabled)
      Drop-In: /run/systemd/generator/nfs-server.service.d
               └─order-with-mounts.conf
       Active: active (exited) since Tue 2025-09-02 08:25:39 UTC; 34s ago
      Process: 13959 ExecStart=/bin/sh -c if systemctl -q is-active gssproxy; then systemctl reload gssproxy ; fi (code=exited, status=0/SUCCESS)
      Process: 13946 ExecStart=/usr/sbin/rpc.nfsd (code=exited, status=0/SUCCESS)
      Process: 13944 ExecStartPre=/usr/sbin/exportfs -r (code=exited, status=0/SUCCESS)
     Main PID: 13959 (code=exited, status=0/SUCCESS)
     
    Sep 02 08:25:39 dapp-infra-01-my-cluster systemd[1]: Starting NFS server and services...
    Sep 02 08:25:39 dapp-infra-01-my-cluster systemd[1]: Started NFS server and services.
    
  5. Проверьте, что /export/nfs является ресурсом (share):

    • Выполните команду для просмотра конфигурации экспорта NFS:

      cat /etc/exports
      

      Вывод будет следующим:

      /export/nfs *(rw,sync,no_root_squash,no_all_squash,no_subtree_check)
      

      В выводе указано, что каталог /export/nfs доступен для всех клиентов (*), с правами на чтение и запись (rw), синхронной записью (sync) и опциями, отключающими проверки прав для суперпользователя (no_root_squash), проверки прав для всех пользователей (no_all_squash) и подкаталогов (no_subtree_check).

    • Выполните команду для отображения статуса экспортируемых файловых систем:

      sudo exportfs -v
      

      Вывод будет следующим:

      /export/nfs     <world>(sync,wdelay,hide,no_subtree_check,sec=sys,rw,secure,no_root_squash,no_all_squash)
      

      Вывод показывает, что каталог /export/nfs экспортируется с теми же параметрами, включая разрешения для всех клиентов (<world>), синхронность и отсутствие проверки подкаталогов. Опции:

      • sync указывает на синхронную запись, при которой изменения записываются на диск немедленно.

      • wdelay позволяет применять изменения с задержкой для оптимизации производительности.

      • hide скрывает определенные файлы и каталоги от клиентов, чтобы они не были видны при доступе.

      • no_subtree_check отключает проверку подкаталогов, что может улучшить производительность при работе с подкаталогами.

      • sec=sys указывает на использование стандартной системы безопасности.

      • rw позволяет клиентам читать и записывать данные в каталог.

      • secure файл может быть доступен только по защищенным каналам (например, через шифрование).

      • no_root_squash отключает сравнение прав для суперпользователя, позволяя ему получать доступ к каталогу с полными правами.

      • no_all_squash отключает приведение всех пользователей к никому, позволяя клиентам сохранять свои права доступа.

Сервер NFS настроен.

Подготовка DropAppConfiguration#

Настройте конфигурацию для создания кластера со всеми необходимыми секциями.

В качестве основного примера конфигурации используйте данные из документа Руководство по установке DropApp с помощью Dactl. Предполагается, что артефакты уже настроены и секция artifacts подготовлена.

Выделенные ingress-узлы#

В файле конфигурации da.yaml заполните секции compute и components:

Редактирование секций compute и components в da.yaml
apiVersion: config.dropapp.ru/v1alpha1
kind: DropAppConfiguration
metadata:
  name: "my-cluster"
spec:
  ...
  compute:
    sshAccess:
      user: dapp
      privateKeyPath: "~/.ssh/id_k8s"
    userProvisioned:
      nodes:
        controlPlane:
          - ip: "<controlplane1-ip>"
            nodePool: "control-plane"
          - ip: "<controlplane2-ip>"
            nodePool: "control-plane"
          - ip: "<controlplane3-ip>"
            nodePool: "control-plane"
        worker:
          - ip: "<worker1-ip>"
            nodePool: "worker"
          - ip: "<worker2-ip>"
            nodePool: "worker"
          - ip: "<worker3-ip>"
            nodePool: "worker"
          - ip: "<worker4-ip>"
            nodePool: "ingress"
            taints:
              - key: "dedicated"
                value: "ingress"
                effect: "NoSchedule"
          - ip: "<worker5-ip>"
            nodePool: "ingress"
            taints:
              - key: "dedicated"
                value: "ingress"
                effect: "NoSchedule"
          - ip: "<worker6-ip>"
            nodePool: "ingress"
            taints:
              - key: "dedicated"
                value: "ingress"
                effect: "NoSchedule"
  ...
  components:
  ...
  - name: load-balancer
    tools:
    - name: metallb
      values:
        speaker:
          affinity:
            nodeAffinity:
              requiredDuringSchedulingIgnoredDuringExecution:
                nodeSelectorTerms:
                  - matchExpressions:
                      - key: node-role.kubernetes.io/ingress
                        operator: Exists
  - name: ingress-controller
    tools:
    - name: ingress-nginx
      values:
        controller:
          kind: DaemonSet
          resources:
            limits:
              cpu: 2000m
              memory: 8Gi
          affinity:
            nodeAffinity:
              requiredDuringSchedulingIgnoredDuringExecution:
                nodeSelectorTerms:
                  - matchExpressions:
                      - key: node-role.kubernetes.io/ingress
                        operator: Exists
          tolerations:
          - key: "dedicated"
            operator: "Equal"
            value: "ingress"
            effect: "NoSchedule"

В примере указано:

  • выделение трех рабочих узлов на ingressиз шести;

  • настройка компонентов Load-balancer и Ingress-controller на выделенные узлы;

  • увеличение лимитов для pod ingress-nginx.

Конфигурация без Metallb#

Если в качестве балансировщика используется не Metallb, а внешний сервер, используйте следующую конфигурацию:

Редактирование секции network в da.yaml
apiVersion: config.dropapp.ru/v1alpha1
kind: DropAppConfiguration
metadata:
  name: "my-cluster"
spec:
  ...
  # в секции network необходимо убрать metallb!
  network:
    publicDomain: dapp.test.un.sbt
  compute:
    sshAccess:
      user: dapp
      privateKeyPath: "~/.ssh/id_k8s"
    userProvisioned:
      nodes:
        controlPlane:
          - ip: "<controlplane1-ip>"
            nodePool: "control-plane"
          - ip: "<controlplane2-ip>"
            nodePool: "control-plane"
          - ip: "<controlplane3-ip>"
            nodePool: "control-plane"
        worker:
          - ip: "<worker1-ip>"
            nodePool: "worker"
          - ip: "<worker2-ip>"
            nodePool: "worker"
          - ip: "<worker3-ip>"
            nodePool: "worker"
          - ip: "<worker4-ip>"
            nodePool: "ingress"
            taints:
              - key: "dedicated"
                value: "ingress"
                effect: "NoSchedule"
          - ip: "<worker5-ip>"
            nodePool: "ingress"
            taints:
              - key: "dedicated"
                value: "ingress"
                effect: "NoSchedule"
          - ip: "<worker6-ip>"
            nodePool: "ingress"
            taints:
              - key: "dedicated"
                value: "ingress"
                effect: "NoSchedule"
  ...
  components:
  ...
  - name: ingress-controller
    tools:
    - name: ingress-nginx
      values:
        controller:
          kind: DaemonSet
          resources:
            limits:
              cpu: 2000m
              memory: 8Gi          
          affinity:
            nodeAffinity:
              requiredDuringSchedulingIgnoredDuringExecution:
                nodeSelectorTerms:
                  - matchExpressions:
                      - key: node-role.kubernetes.io/ingress
                        operator: Exists
          tolerations:
          - key: "dedicated"
            operator: "Equal"
            value: "ingress"
            effect: "NoSchedule"

При такой конфигурации ingress-nginx преднастроится с такими values:

controller:
  dnsPolicy: ClusterFirstWithHostNet
  hostNetwork: true
  hostPort:
    enabled: true
    ports:
      http: 80
      https: 443
  kind: DaemonSet
  service:
    external:
      enabled: false

При этом значения из spec.components будут объединены со значениями выше.

Использование своих сертификатов#

Если параметры ingressTLS и clusterTLS не переданы, DropApp сгенерирует сертификаты для ingress и корневые сертификаты для кластера.

В случае необходимости можно предоставить собственные сертификаты, сгенерировать запрос на сертификат (CSR) с помощью скриптов и подписать его в корпоративной системе управления ключами (PKI). Полученный сертификат следует использовать в конфигурации кластера.

Корневые сертификаты Kubernetes#

Генерация корневых сертификатов кластера#
  1. Создайте скрипт hp.sh:

    k8s-ca-csr.sh
    #!/bin/bash
    
    OUTPUT_DIR="./k8s-ca"
    mkdir -p "$OUTPUT_DIR"
     
    # Параметры haproxy
    HAPROXY_IP="<haproxy-ip3>"
    HAPROXY_DNS="apiserver.my-cluster.dapp.test.un.sbt"
     
    # Создание конфигурационного файла для CSR с дополнительными SAN
    cat > "$OUTPUT_DIR/ca-csr.conf" <<EOF
    [ req ]
    default_bits       = 2048
    distinguished_name = req_distinguished_name
    req_extensions     = v3_req
    prompt             = no
     
    [ req_distinguished_name ]
    CN = kubernetes-ca
     
    [ v3_req ]
    basicConstraints = CA:true
    keyUsage = critical, digitalSignature, keyEncipherment, keyCertSign
    subjectAltName = @alt_names
     
    [ alt_names ]
    IP.1 = <haproxy-ip1>
    IP.2 = <haproxy-ip2>
    IP.3 = $HAPROXY_IP
    DNS.1 = kubernetes
    DNS.2 = kubernetes.default
    DNS.3 = kubernetes.default.svc
    DNS.4 = kubernetes.default.svc.cluster.local
    DNS.5 = $HAPROXY_DNS
    EOF
     
    # Генерация приватного ключа (этот ключ останется у пользователя)
    openssl genrsa -out "$OUTPUT_DIR/ca.key" 2048
     
    # Генерация Certificate Signing Request (CSR) - этот файл отправляется на подпись
    openssl req -new \
      -key "$OUTPUT_DIR/ca.key" \
      -out "$OUTPUT_DIR/ca.csr" \
      -config "$OUTPUT_DIR/ca-csr.conf" \
      -extensions v3_req
     
    # Вывод информации о созданном CSR
    echo "CSR создан с дополнительными SAN для haproxy"
    echo "Файлы:"
    echo "  Приватный ключ: $OUTPUT_DIR/ca.key (храните в секрете)"
    echo "  CSR для подписи: $OUTPUT_DIR/ca.csr (отправьте этот файл на подпись)"
    echo
    echo "Добавленные SAN:"
    echo "  IP: $HAPROXY_IP"
    echo "  DNS: $HAPROXY_DNS"
     
    # Вывод CSR в base64 для удобства копирования
    echo
    echo "-----BEGIN CERTIFICATE REQUEST (ca.csr base64)-----"
    base64 -w 0 "$OUTPUT_DIR/ca.csr"
    echo
    echo "-----END CERTIFICATE REQUEST-----"
     
    echo
    echo "После получения подписанного сертификата от корпоративного PKI:"
    echo "1. Сохраните подписанный сертификат как $OUTPUT_DIR/ca.crt"
    echo "2. Убедитесь, что цепочка сертификатов корректна"
    
  2. Сделайте файл исполняемым:

    chmod +x ./k8s-ca-csr.sh
    
  3. Запустите скрипт:

    sudo ./k8s-ca-csr.sh
    

Корневые сертификаты сгенерированы.

Подпись сертификатов в корпоративной системе управления ключами (PKI)#
  1. Создайте скрипт hp.sh:

    sign-k8s-ca.sh
    #!/bin/bash
     
    # Шаг 1: Задание переменных для каталогов
    CSR_DIR="./k8s-ca"
    CORPORATE_CA_DIR="./corporate-pki"
     
    echo "=== Эмуляция корпоративного PKI ==="
     
    # Шаг 2: Проверка наличия CSR
    if [ ! -f "$CSR_DIR/ca.csr" ]; then
        echo "Ошибка: Не найден файл CSR: $CSR_DIR/ca.csr"
        echo "Сначала запустите generate-csr.sh"
        exit 1
    fi
     
    echo "Найден CSR: $CSR_DIR/ca.csr"
     
    # Шаг 3: Подпись CSR корпоративным CA
    echo "Подпись CSR корпоративным CA..."
     
    # Шаг 4: Копирование конфигурации из CSR для использования в расширениях
    CSR_CONFIG="$CSR_DIR/ca-csr.conf"
     
    # Шаг 5: Подпись CSR
    openssl x509 -req \
      -in "$CSR_DIR/ca.csr" \
      -CA "$CORPORATE_CA_DIR/corporate-ca.crt" \
      -CAkey "$CORPORATE_CA_DIR/corporate-ca.key" \
      -CAcreateserial \
      -out "$CSR_DIR/ca.crt" \
      -days 365 \
      -extensions v3_req \
      -extfile "$CSR_CONFIG"
     
    echo "CSR подписан успешно!"
     
    # Шаг 6: Создание полной цепочки сертификатов (если нужно)
    echo "Создание полной цепочки сертификатов..."
    cat "$CSR_DIR/ca.crt" "$CORPORATE_CA_DIR/corporate-ca.crt" > "$CSR_DIR/ca-fullchain.crt"
     
    # Шаг 7: Вывод результатов
    echo
    echo "=== Результаты ==="
    echo "Корневой сертификат корпоративного PKI: $CORPORATE_CA_DIR/corporate-ca.crt"
    echo "Приватный ключ Kubernetes CA: $CSR_DIR/ca.key"
    echo "Подписанный сертификат Kubernetes CA: $CSR_DIR/ca.crt"
    echo "Полная цепочка сертификатов: $CSR_DIR/ca-fullchain.crt"
     
    # Шаг 8: Проверка
    echo
    echo "=== Проверка сертификата ==="
    echo "Subject:"
    openssl x509 -in "$CSR_DIR/ca.crt" -noout -subject
     
    echo "Issuer:"
    openssl x509 -in "$CSR_DIR/ca.crt" -noout -issuer
     
    echo "SAN (Subject Alternative Names):"
    openssl x509 -in "$CSR_DIR/ca.crt" -noout -text | grep -A 1 "Subject Alternative Name" || echo "SAN не найдены"
     
    echo
    echo "=== Готово! ==="
    echo "Теперь вы можете использовать:"
    echo "  $CSR_DIR/ca.key - приватный ключ"
    echo "  $CSR_DIR/ca.crt - подписанный сертификат"
    echo "  $CSR_DIR/ca-fullchain.crt - полная цепочка сертификатов"
    
  2. Сделайте файл исполняемым:

    chmod +x ./sign-k8s-ca.sh
    
  3. Запустите скрипт:

    sudo ./sign-k8s-ca.sh
    

Сертификаты в корпоративной системе управления ключами (PKI) подписаны.

Сохранение сертификата#

Сохраните сертификат и ключ в base64:

cat k8s-ca/ca.crt | base64 -w0
cat k8s-ca/ca.key | base64 -w0

В выводах команд будут отображены закодированные в Base64 сертификаты.

Добавление сертификатов в DropAppConfiguration описано в пункте «Добавление сертификатов в DropAppConfiguration».

Сертификаты для ingress#

Генерация сертификатов для ingress и подпись в корпоративной системе управления ключами (PKI)#
  1. Создайте скрипт hp.sh:

    sign-ingress-tls.sh
    #!/bin/bash
     
    # Каталоги
    OUTPUT_DIR="./ingress-cert"
    CORPORATE_CA_DIR="./corporate-pki"
     
    echo "=== Создание сертификата для Ingress (*.my-cluster.dapp.test.un.sbt) ==="
     
    # Шаг 1: Создание каталога
    mkdir -p "$OUTPUT_DIR"
     
    # Шаг 2: Проверка наличия корневого CA
    if [ ! -f "$CORPORATE_CA_DIR/corporate-ca.crt" ] || [ ! -f "$CORPORATE_CA_DIR/corporate-ca.key" ]; then
        echo "Ошибка: Не найден корневой CA. Сначала запустите эмуляцию корпоративного PKI."
        exit 1
    fi
     
    echo "Найден корневой CA: $CORPORATE_CA_DIR/corporate-ca.crt"
     
    # Шаг 3: Создание конфигурационного файла для сертификата ingress
    cat > "$OUTPUT_DIR/ingress.conf" <<EOF
    [ req ]
    default_bits = 2048
    distinguished_name = req_distinguished_name
    req_extensions = v3_req
    prompt = no
     
    [ req_distinguished_name ]
    CN = *.my-cluster.dapp.test.un.sbt
     
    [ v3_req ]
    basicConstraints = CA:FALSE
    keyUsage = critical, digitalSignature, keyEncipherment
    extendedKeyUsage = serverAuth
    subjectAltName = @alt_names
     
    [ alt_names ]
    DNS.1 = *.my-cluster.dapp.test.un.sbt
    DNS.2 = my-cluster.dapp.test.un.sbt
    EOF
     
    # Шаг 4: Генерация приватного ключа для ingress
    echo "Генерация приватного ключа для ingress..."
    openssl genrsa -out "$OUTPUT_DIR/tls.key" 2048
     
    # Шаг 5: Создание Certificate Signing Request (CSR)
    echo "Создание CSR для ingress..."
    openssl req -new \
      -key "$OUTPUT_DIR/tls.key" \
      -out "$OUTPUT_DIR/tls.csr" \
      -config "$OUTPUT_DIR/ingress.conf" \
      -extensions v3_req
     
    # Шаг 6: Подпись CSR корпоративным CA
    echo "Подпись CSR корпоративным CA..."
    openssl x509 -req \
      -in "$OUTPUT_DIR/tls.csr" \
      -CA "$CORPORATE_CA_DIR/corporate-ca.crt" \
      -CAkey "$CORPORATE_CA_DIR/corporate-ca.key" \
      -CAcreateserial \
      -out "$OUTPUT_DIR/tls.crt" \
      -days 365 \
      -extensions v3_req \
      -extfile "$OUTPUT_DIR/ingress.conf"
     
    # Шаг 7: Создание полной цепочки сертификатов
    echo "Создание полной цепочки сертификатов..."
    cat "$OUTPUT_DIR/tls.crt" "$CORPORATE_CA_DIR/corporate-ca.crt" > "$OUTPUT_DIR/tls-fullchain.crt"
     
    # Шаг 8: Вывод результатов
    echo
    echo "=== Результаты ==="
    echo "Приватный ключ ingress: $OUTPUT_DIR/tls.key"
    echo "Сертификат ingress:     $OUTPUT_DIR/tls.crt"
    echo "Полная цепочка:         $OUTPUT_DIR/tls-fullchain.crt"
    echo "CSR для ingress:        $OUTPUT_DIR/tls.csr"
     
    # Шаг 9: Проверка сертификата
    echo
    echo "=== Проверка сертификата ==="
    echo "Subject:"
    openssl x509 -in "$OUTPUT_DIR/tls.crt" -noout -subject
     
    echo "Issuer:"
    openssl x509 -in "$OUTPUT_DIR/tls.crt" -noout -issuer
     
    echo "SAN:"
    openssl x509 -in "$OUTPUT_DIR/tls.crt" -noout -text | grep -A 2 "Subject Alternative Name"
     
    echo "Срок действия:"
    openssl x509 -in "$OUTPUT_DIR/tls.crt" -noout -dates
     
    # Шаг 10: Проверка цепочки
    echo
    echo "=== Проверка цепочки ==="
    if openssl verify -CAfile "$CORPORATE_CA_DIR/corporate-ca.crt" "$OUTPUT_DIR/tls.crt" >/dev/null 2>&1; then
        echo "✅ Цепочка сертификатов КОРРЕКТНА"
    else
        echo "❌ Цепочка сертификатов НЕКОРРЕКТНА"
        openssl verify -CAfile "$CORPORATE_CA_DIR/corporate-ca.crt" "$OUTPUT_DIR/tls.crt"
    fi
     
    # Шаг 11: Проверка соответствия ключа и сертификата
    echo
    echo "=== Проверка соответствия ключа и сертификата ==="
    KEY_MODULUS=$(openssl rsa -noout -modulus -in "$OUTPUT_DIR/tls.key" 2>/dev/null | openssl md5)
    CERT_MODULUS=$(openssl x509 -noout -modulus -in "$OUTPUT_DIR/tls.crt" | openssl md5)
     
    if [ "$KEY_MODULUS" = "$CERT_MODULUS" ]; then
        echo "✅ Приватный ключ и сертификат СООТВЕТСТВУЮТ"
    else
        echo "❌ Приватный ключ и сертификат НЕ СООТВЕТСТВУЮТ"
    fi
     
    echo
    echo "=== Готово! ==="
    echo "Сертификат готов для использования в ingress controller"
    echo "Для создания Kubernetes secret:"
    echo "kubectl create secret tls ingress-tls \\"
    echo "  --cert=$OUTPUT_DIR/tls.crt \\"
    echo "  --key=$OUTPUT_DIR/tls.key"
    
  2. Сделайте файл исполняемым:

    chmod +x ./sign-ingress-tls.sh
    
  3. Запустите скрипт:

    sudo ./sign-ingress-tls.sh
    

Сертификаты для ingress сгенерированы и подписаны.

Сохранение сертификата#

Сохраните сертификат и ключ в base64:

cat ingress-cert/tls.crt | base64 -w0
cat ingress-cert/tls.key | base64 -w0

В выводах команд будут отображены закодированные в Base64 сертификаты.

Добавление сертификатов в DropAppConfiguration описано в пункте «Добавление сертификатов в DropAppConfiguration».

Добавление сертификатов в DropAppConfiguration#

Добавьте сертификаты в spec.k8s.clusterTLS и spec.k8s.ingressTLS:

apiVersion: config.dropapp.ru/v1alpha1
kind: DropAppConfiguration
metadata:
  name: "my-cluster"
spec:
  ...
  k8s:
    ...
    clusterTLS:
      cert: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0... # набор символов представлен для примера сертификата
      key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktL... # набор символов представлен для примера ключа
    ingressTLS:
      cert: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1... # набор символов представлен для примера сертификата
      key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0t... # набор символов представлен для примера ключа
  ...

Настройка Kube-apiserver в DropAppConfiguration#

Добавьте certSANs и подключите audit:

apiVersion: config.dropapp.ru/v1alpha1
kind: DropAppConfiguration
metadata:
  name: "my-cluster"
spec:
  ...
  k8s:
    ...
    apiServer:
      certSANs:
        - <certSANs-ip>
        - apiserver.my-cluster.dapp.test.un.sbt
      extraArgs:
        audit-policy-file: "/etc/kubernetes/policies/audit-policy.yaml"
        audit-log-path: "/var/log/kubernetes/audit.log"
        audit-log-format: "json"
        audit-log-maxbackup: "5"
        audit-log-maxsize: "128"
        audit-log-maxage: "7"
        audit-log-mode: "batch"
      extraVolumes:
      - name: policies
        hostPath: /etc/kubernetes/policies
        mountPath: /etc/kubernetes/policies
      - name: kube-audit
        hostPath: /var/log/kubernetes
        mountPath: /var/log/kubernetes
    ...
  ...

Настройка сети pod, сервисов и controlPlaneEndpoint#

Сеть pod и адрес control plane будут автоматически переданы в конфигурации статических pod control plane, а также в Cilium. Пример конфигурации:

apiVersion: config.dropapp.ru/v1alpha1
kind: DropAppConfiguration
metadata:
  name: "my-cluster"
spec:
  ...
  k8s:
    podSubnetCIDR: <podSubnetCIDR-ip>/16
    serviceSubnetCIDR: <serviceSubnetCIDR-ip>/16
    controlPlaneEndpoint: apiserver.my-cluster.dapp.test.un.sbt:6443
    ...
  ...

Шифрование Etcd#

Чтобы избежать утечек данных в случае компрометации Etcd, включите шифрование хранимых в Etcd секретов и configmap:

apiVersion: config.dropapp.ru/v1alpha1
kind: DropAppConfiguration
metadata:
  name: "my-cluster"
spec:
  ...
  k8s:
    ...
    etcd:
      encrypt:
        apiVersion: apiserver.config.k8s.io/v1
        kind: EncryptionConfiguration
        resources:
        - resources:
          - secrets
          - configmaps
          providers:
          - aescbc:
              keys:
                - name: key1
                  secret: ...
          - identity: {}
    ...
  ...

В примере используется алгоритм шифрования aescbc с собственным ключом, который необходимо указать в секции providers.aescbc.keys.secret. Ключ физически хранится в манифестах на узлах control plane.

Включение PodSecurityAdmission#

Для приведения настроек по умолчанию pod в соответствие со стандартами безопасности активируйте следующие параметры:

apiVersion: config.dropapp.ru/v1alpha1
kind: DropAppConfiguration
metadata:
  name: "my-cluster"
spec:
  ...
  k8s:
    ...    
    security:
      pods:
        enforce: "baseline"
        audit: "restricted"
        warn: "baseline"
    ...
  ...

В примере используется алгоритм шифрования aescbc с собственным ключом, который необходимо указать в секции providers.aescbc.keys.secret. Манифесты AdmissionConfiguration будут автоматически созданы на узлах control plane.

Включение hubble-ui#

для мониторинга сетевых взаимодействий внутри кластера включите Hubble:

apiVersion: config.dropapp.ru/v1alpha1
kind: DropAppConfiguration
metadata:
  name: "my-cluster"
spec:
  ...
  k8s:
    ...
    cni:
      cilium:
        values:
          hubble:
            relay:
              enabled: true
            ui:
              enabled: true
    ...
  ...

Hubble будет доступен по адресу https://hubble.my-cluster.dapp.test.un.sbt.

Подключение каталога с пользователями#

Для подключения LDAP-каталога настройте компонент Auth. Пользователи в каталоге должны иметь группы, соответствующие ролевой модели DropApp.

Пример конфигурации с подключением IPA Server:

apiVersion: config.dropapp.ru/v1alpha1
kind: DropAppConfiguration
metadata:
  name: "my-cluster"
spec:
  ...
  components:
  - name: auth
    tools:
    - name: dex
      values:
        config:
          connectors:
            - type: ldap
              name: Ipa Server
              id: ldap
              config:
                bindDN: uid=dapp-sync-svc,cn=users,cn=accounts,dc=dapp,dc=un,dc=sbt
                bindPW: ...
                groupSearch:
                  baseDN: cn=groups,cn=accounts,dc=dapp,dc=un,dc=sbt
                  filter: (objectClass=groupOfNames)
                  nameAttr: cn
                  userMatchers:
                    - groupAttr: member
                      userAttr: DN
                host: freeipa1.dapp.un.sbt:636
                insecureNoSSL: false
                insecureSkipVerify: true
                userSearch:
                  baseDN: cn=users,cn=accounts,dc=dapp,dc=un,dc=sbt
                  emailAttr: mail
                  filter: (objectClass=person)
                  idAttr: DN
                  nameAttr: uid
                  preferredUsernameAttr: uid
                  username: uid
              usernamePrompt: 'Username:'
  ...

Итоговая конфигурация#

Итоговый конфигурационный файл hp.sh:

complete-da.yaml
apiVersion: config.dropapp.ru/v1alpha1
kind: DropAppConfiguration
metadata:
  name: "my-cluster"
spec:
  network:
      publicDomain: dapp.test.un.sbt
      # Если не используется metallb - удалите данную секцию
      metallb:
        pools:
        - addresses: "<metallb-pool-ip>/32"
          name: "dapp"
          l2Advertisement: true
  artifacts:
    imageRegistry:
      host: ...
      path: ...
      credentials: ...
    rpmRepository:
      - name: "dapp-repo"
        baseUrl: http://...
        gpgCheck: 1
        enabled: 1
        gpgKey: "file:///etc/pki/rpm-gpg/RPM-GPG-KEY-SberLinux"
      - name: "slo-baseos"
        baseUrl: http://...
        gpgCheck: 1
        enabled: 1
        gpgKey: "file:///etc/pki/rpm-gpg/RPM-GPG-KEY-SberLinux"
      - name: "slo-appstream"
        baseUrl: http://...
        gpgCheck: 1
        enabled: 1
        gpgKey: "file:///etc/pki/rpm-gpg/RPM-GPG-KEY-SberLinux"
  compute:
    sshAccess:
      user: dapp
      privateKeyPath: "~/.ssh/id_dapp"
    userProvisioned:
      nodes:
        controlPlane:
          - ip: "<controlplane1-ip>"
            nodePool: "control-plane"
          - ip: "<controlplane2-ip>"
            nodePool: "control-plane"
          - ip: "<controlplane3-ip>"
            nodePool: "control-plane"
        worker:
          - ip: "<worker1-ip>"
            nodePool: "worker"
          - ip: "<worker2-ip>"
            nodePool: "worker"
          - ip: "<worker3-ip>"
            nodePool: "worker"
          - ip: "<worker4-ip>"
            nodePool: "ingress"
            taints:
              - key: "dedicated"
                value: "ingress"
                effect: "NoSchedule"
          - ip: "<worker5-ip>"
            nodePool: "ingress"
            taints:
              - key: "dedicated"
                value: "ingress"
                effect: "NoSchedule"
          - ip: "<worker6-ip>"
            nodePool: "ingress"
            taints:
              - key: "dedicated"
                value: "ingress"
                effect: "NoSchedule"
  k8s:
    podSubnetCIDR: <podSubnetCIDR-ip>/16
    serviceSubnetCIDR: <serviceSubnetCIDR-ip>/16
    controlPlaneEndpoint: apiserver.my-cluster.dapp.test.un.sbt:6443
    apiServer:
      certSANs:
        - <haproxy-ip>
        - apiserver.my-cluster.dapp.test.un.sbt
      extraArgs:
        audit-policy-file: "/etc/kubernetes/policies/audit-policy.yaml"
        audit-log-path: "/var/log/kubernetes/audit.log"
        audit-log-format: "json"
        audit-log-maxbackup: "5"
        audit-log-maxsize: "128"
        audit-log-maxage: "7"
        audit-log-mode: "batch"
      extraVolumes:
      - name: policies
        hostPath: /etc/kubernetes/policies
        mountPath: /etc/kubernetes/policies
      - name: kube-audit
        hostPath: /var/log/kubernetes
        mountPath: /var/log/kubernetes
    etcd:
      encrypt:
        apiVersion: apiserver.config.k8s.io/v1
        kind: EncryptionConfiguration
        resources:
        - resources:
          - secrets
          - configmaps
          providers:
          - aescbc:
              keys:
                - name: key1
                  secret: ...
          - identity: {}
    cni:
      cilium:
        values:
          hubble:
            relay:
              enabled: true
            ui:
              enabled: true
    clusterTLS:
      cert: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS... # набор символов представлен для примера сертификата
      key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVk... # набор символов представлен для примера ключа
    ingressTLS:
      cert: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS... # набор символов представлен для примера сертификата
      key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRV...  # набор символов представлен для примера ключа
  components:
  - name: auth
    tools:
    - name: dex
      values:
        config:
          connectors:
            - type: ldap
              name: Ipa Server
              id: ldap
              config:
                bindDN: uid=dapp-sync-svc,cn=users,cn=accounts,dc=dapp,dc=un,dc=sbt
                bindPW: ...
                groupSearch:
                  baseDN: cn=groups,cn=accounts,dc=dapp,dc=un,dc=sbt
                  filter: (objectClass=groupOfNames)
                  nameAttr: cn
                  userMatchers:
                    - groupAttr: member
                      userAttr: DN
                host: freeipa1.dapp.un.sbt:636
                insecureNoSSL: false
                insecureSkipVerify: true
                userSearch:
                  baseDN: cn=users,cn=accounts,dc=dapp,dc=un,dc=sbt
                  emailAttr: mail
                  filter: (objectClass=person)
                  idAttr: DN
                  nameAttr: uid
                  preferredUsernameAttr: uid
                  username: uid
              usernamePrompt: 'Username:'
  - name: load-balancer
    tools:
    - name: metallb
      values:
        speaker:
          affinity:
            nodeAffinity:
              requiredDuringSchedulingIgnoredDuringExecution:
                nodeSelectorTerms:
                  - matchExpressions:
                      - key: node-role.kubernetes.io/ingress
                        operator: Exists
  - name: ingress-controller
    tools:
    - name: ingress-nginx
      values:
        controller:
          kind: DaemonSet
          affinity:
            nodeAffinity:
              requiredDuringSchedulingIgnoredDuringExecution:
                nodeSelectorTerms:
                  - matchExpressions:
                      - key: node-role.kubernetes.io/ingress
                        operator: Exists
          tolerations:
          - key: "dedicated"
            operator: "Equal"
            value: "ingress"
            effect: "NoSchedule"

Сохраните конфигурационный файл как da.yaml и создайте кластер:

dactl create cluster --config ./da.yaml

Установка Csi-driver-nfs#

В качестве постоянного хранилища используйте NFS. Установите Csi-driver-nfs с помощью ресурса Tool:

apiVersion: config.dropapp.ru/v1alpha1
kind: Tool
metadata:
  name: csi-driver-nfs
spec:
  values:
    storageClass:
      parameters:
       server: <nfs-ip>
       share: /export/nfs
      mountOptions: # дополнительные опции при необходимости
       - nfsvers=4.1
       - hard
       - proto=tcp

В результате будет создан storageClass с именем nfs-csi и VolumeSnapshotClass для создания снимков томов.

Также будут установлены все Csi-компоненты Kubernetes (Resizer, Attacher, Snapshot-controller, Node-registrar).

Теперь storageclass может быть использован при установке компонентов.

Установка компонентов#

Установка компонентов более подробно описана в разделе «Установка дополнительных компонентов».

Alerting#

Для установки компонента Alerting с постоянным хранением выполните следующие шаги:

  1. Создайте файл alerting-persistence.yaml:

    apiVersion: config.dropapp.ru/v1alpha1
    kind: Component
    metadata:
      name: alerting
    spec:
      tools:
      - name: alertmanager
        values:
          persistence:
            enabled: true
            storageClass: nfs-csi
            size: 100Mi # укажите необходимый объем хранилища
    
  2. Примените файл:

    kubectl apply -f alerting-persistence.yaml
    

Monitoring#

Для установки компонента Monitoring с постоянным хранением выполните следующие шаги:

  1. Создайте файл monitoring-persistence.yaml:

    apiVersion: config.dropapp.ru/v1alpha1
    kind: Component
    metadata:
      name: monitoring
    spec:
      tools:
      - name: grafana
        values:
          persistence:
            enabled: true
            storageClassName: nfs-csi
            size: 1Gi # укажите необходимый объем хранилища
      - name: victoria-metrics-k8s-stack
        values:
          vmcluster:
            spec:
              vmstorage:
                storage:
                  volumeClaimTemplate:
                    spec:
                      storageClassName: nfs-csi
                      resources:
                        requests:
                          storage: 10Gi # укажите необходимый объем хранилища
              vmselect:
                storage:
                  volumeClaimTemplate:
                    spec:
                      storageClassName: nfs-csi
                      resources:
                        requests:
                          storage: 2Gi # укажите необходимый объем хранилища
    
  2. Примените файл:

    kubectl apply -f monitoring-persistence.yaml
    

Logging#

Для установки компонента Logging с постоянным хранением выполните следующие шаги:

  1. Создайте файл logging-persistence.yaml:

    apiVersion: config.dropapp.ru/v1alpha1
    kind: Component
    metadata:
      name: logging
    spec:
      tools:
      - name: vector-aggregator
        values:
          persistence:
            enabled: true
            storageClassName: nfs-csi
            size: 10Gi  # укажите необходимый объем хранилища
    
  2. Примените файл:

    kubectl apply -f logging-persistence.yaml
    

Security#

Для установки компонента Security выполните следующие шаги:

  1. Создайте файл component-security.yaml:

    apiVersion: config.dropapp.ru/v1alpha1
    kind: Component
    metadata:
      name: security
    
  2. Примените файл:

    kubectl apply -f component-security.yaml
    

GitOps#

Для установки компонента GitOps выполните следующие шаги:

  1. Создайте файл gitops.yaml:

    apiVersion: config.dropapp.ru/v1alpha1
    kind: Component
    metadata:
      name: gitops
    
  2. Примените файл:

    kubectl apply -f gitops.yaml
    

DevOps#

Для установки компонента DevOps выполните следующие шаги:

  1. Создайте файл devops.yaml:

    apiVersion: config.dropapp.ru/v1alpha1
    kind: Component
    metadata:
      name: devops
    
  2. Примените файл:

    kubectl apply -f devops.yaml
    

AiOps#

Для установки компонента AiOps выполните следующие шаги:

  1. Создайте файл aiops.yaml:

    apiVersion: config.dropapp.ru/v1alpha1
    kind: Component
    metadata:
      name: aiops
    
  2. Примените файл:

    kubectl apply -f aiops.yaml