Устанавливаем кластер Kubernetes в LXC-контейнерах Proxmox

от автора

Введение

Если вы когда-либо разворачивали Kubernetes-кластер на виртуальных машинах (ВМ), то знаете, насколько это может быть ресурсоёмко. Особенно это ощущается на одноплатных компьютерах вроде Orange Pi 5 Plus, даже несмотря на его 16 ГБ оперативной памяти. Для домашнего лабораторного стенда или лёгкой продакшн-инфраструктуры хочется чего-то более лёгкого и быстрого.

Здесь на помощь приходят контейнеры LXC. Они позволяют запускать полноценные окружения, почти как ВМ, но с гораздо меньшими накладными расходами. В этой статье я покажу, как развернуть кластер Kubernetes не на ВМ, а внутри LXC-контейнеров под управлением Proxmox.

На практике это означает, что в контейнеры LXC можно «упаковать» больше узлов Kubernetes, чем в ВМ, особенно на ограниченном по ресурсам железе.

Исходные условия

В распоряжении — компактный, но мощный одноплатный сервер Orange Pi 5 Plus с 16 ГБ оперативной памяти. На нём уже установлен гипервизор Proxmox VE, который обеспечивает удобную платформу для управления виртуализацией и развёртывания ВМ и контейнеров LXC.

В рамках этой статьи мы не будем углубляться в установку самого Proxmox — подобных инструкций в интернете предостаточно. Вместо этого сосредоточимся на следующем:

  • как создать базовый шаблон LXC-контейнера;

  • как подготовить его для работы с Kubernetes;

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

  • как всё это реализовать в рамках домашней лаборатории — для обучения, тестирования или разработки.

Важно понимать, что полноценный отказоустойчивый кластер Kubernetes разворачивается минимум на трёх управляющих узлах. Это позволяет системе продолжать функционировать даже при выходе одного из них. По мере роста кластера можно добавлять больше управляющих узлов — например, пять или семь — чтобы обеспечить стабильность, масштабируемость и отказоустойчивость при увеличении нагрузки.

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

Шаг 1. Подготовка шаблона LXC

Перед созданием контейнеров убедимся, что на сервере Proxmox есть нужные шаблоны LXC-образов. В Proxmox это делается очень просто:

pveam update  # обновляем репозиторий pveam available # можем посмотреть список всех доступных образов 

У меня архитектура arm64  (Orange Pi 5 Plus), поэтому я использовал подходящий шаблон Ubuntu:

pveam download local ubuntu-jammy-20231124_arm64.tar.xz 

Скачать образ можно и в веб-интерфейсе  proxmox.

Шаг 2. Создание LXC-контейнера

Контейнер можно создать и через GUI, но я предпочитаю CLI — так проще повторять и автоматизировать. А потом если что можно подкорректировать через GUI, кому как удобнее. Вот пример через CLI

pct create 2000 \ /var/lib/vz/template/cache/ubuntu-jammy-20231124_arm64.tar.xz \  --unprivileged 0 \ --features nesting=1 \ --hostname k8s-node \  --cores 2 \  --memory 4096 \  --swap 0 \  --arch arm64 \ --net0 name=eth0,bridge=vmbr0,ip=192.168.1.20/24,gw=192.168.1.1 \  --storage local \  --rootfs local:20 \  --nameserver 192.168.1.1 \ --password your_root_password_here \  --ssh-public-keys /path/to/your/public_key.pub  

Детализация параметров:

ID контейнера (2000) Уникальный числовой идентификатор контейнера в кластере Proxmox.
Шаблон ОС Путь к предварительно загруженному образу Ubuntu (Jammy) для ARM64.
--unprivileged 0 Контейнер работает с правами root (не изолирован). Необходимо для некоторых системных операций.
--features nesting=1 Активирует поддержку вложенной виртуализации (например, для запуска Docker/Kubernetes).
--hostname Сетевое имя ноды в кластере. Должно быть уникальным в пределах сети.
--cores, --memory, --swap Ресурсы 2 ядра CPU 4 ГБ RAM Swap отключен (рекомендуется для K8s)
--arch arm64 Явное указание архитектуры контейнера (должно совпадать с шаблоном и хостовой системой).
--net0 Сеть интерфейс eth0, подключение к мосту vmbr0, статический IPv4: 192.168.1.20/24 Шлюз: 192.168.1.1
--storage Используется стандартное хранилище Proxmox
--rootfs 20 ГБ дискового пространства
--nameserver Указание DNS-сервера (часто совпадает с шлюзом в домашних сетях).
--password Пароль root (не рекомендуется для production — лучше использовать ключи SSH)
--ssh-public-keysПубличный SSH-ключ для безопасного доступа.

Шаг 3. Настройка конфигурации контейнера

Перед первым запуском контейнера нужно отредактировать его конфигурационный файл, чтобы обеспечить совместимость с kubelet. Конфиги LXC лежат по пути:

vim /etc/pve/lxc/2000.conf 

Добавляем в конец файла:

lxc.apparmor.profile: unconfined lxc.cap.drop: lxc.cgroup.devices.allow: a lxc.mount.auto: proc:rw sys:rw 

Эти параметры снимают часть ограничений безопасности, которые мешают kubelet работать корректно.

Еще нужно проверить установку модулей на хосте proxmox

lsmod | grep overlay lsmod | grep br_netfilter 

Если их нет в ядре, то нужно установить командами

modprobe overlay modprobe br_netfilter 

Шаг 4. Установка SSH-сервера

Контейнер создан, теперь его можно запустить:

pct start 2000 

Переходим в его консоль и обновляем пакеты:

pct enter 2000 

далее обновляем пакеты:

apt update apt install -y openssh-server 

Теперь можно подключаться к контейнеру через SSH, что особенно удобно при настройке кластера.

Шаг 5. Подготовка контейнера к работе с Kubernetes

Ubuntu в контейнере достаточно «чистая». Чтобы kubelet корректно запускался, необходимо добавить поддержку /dev/kmsg:

ln -s /dev/console /dev/kmsg echo 'L /dev/kmsg - - - - /dev/console' > /etc/tmpfiles.d/kmsg.conf 

Это обходной путь, потому что в LXC нет настоящего /dev/kmsg, но Kubernetes его ожидает.

Еще нужно разрешить маршрутизацию ip-трафика в ноде, выполнив команды:

echo -e "net.bridge.bridge-nf-call-ip6tables = 1\nnet.bridge.bridge-nf-call-iptables = 1\nnet.ipv4.ip_forward = 1" > /etc/sysctl.d/10-k8s.conf sysctl -f /etc/sysctl.d/10-k8s.conf 

Отключить swap можно командами, это делать если не сделали при создании контейнера

swapoff -a sed -i '/ swap / s/^/#/' /etc/fstab 

Для проверки отключения файла подкачки выполним команду:

swapon -s 

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

sysctl net.bridge.bridge-nf-call-iptables net.bridge.bridge-nf-call-ip6tables net.ipv4.ip_forward 

Шаг 6. Контейнерный runtime.

На момент настройки я не стал использовать docker, так как Kubernetes официально отказался от его поддержки, начиная с версии 1.24. Теоретически, можно было бы использовать cri-dockerd, но это лишняя прослойка, и я решил пойти более прямым путём — попробовать containerd и cri-o. Испытал оба рантайма на контейнерах LXC, оба работают нормально.

Здесь приведу пример как я настраивал containerd, а далее как настраивал cri-o. Нужно установить один из них на ваш выбор.

6.1 Устанавливаем containerd.

Все можно сделать по инструкции здесь

Если лень выполнять каждый пункт вот написал скрипт нужно только выбрать архитектуру, скрипт установит последние стабильные версии

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

wget https://github.com/containerd/containerd/releases/download/v2.0.4/containerd-2.0.4-linux-arm64.tar.gz 

Распаковываем

tar Cxzvf /usr/local containerd-1.6.2-linux-amd64.tar.gz 

Скачаем файл для юнит

wget https://raw.githubusercontent.com/containerd/containerd/main/containerd.service 

Перед использованием  юнит-файла, нужно закомментировать одну строку. Зайдите редактором и закомментируйте строку ExecStartPre=-/sbin/modprobe overlay или удалите ее

mv containerd.service /usr/lib/systemd/system/containerd.service systemctl daemon-reload systemctl enable --now containerd 

Скачаем runc для своей архитектуры здесь

 wget https://github.com/opencontainers/runc/releases/download/v1.2.6/runc.arm64 

Инсталлируем, выполнив команду

install -m 755 runc.amd64 /usr/local/sbin/runc 

Установим сетевые плагины CNI, выбираем для своей архитектуры здесь

wget https://github.com/containernetworking/plugins/releases/download/v1.6.2/cni-plugins-linux-arm64-v1.6.2.tgz 

Создадим директорию для бинарей

mkdir -p /opt/cni/bin 

И туда распакуем архив

tar Cxzvf /opt/cni/bin cni-plugins-linux-amd64-v1.1.1.tgz 

Теперь можно сделать так

systemctl start containerd.service 

6.2 Установка cri-o

Установить можно по инструкции из официальной документации здесь

Здесь опишу как устанавливал я. Установим переменные:

export KUBERNETES_VERSION=v1.32 export CRIO_VERSION=v1.32 

Если нет в системе, установим это

apt-get install -y software-properties-common 

Пакет software-properties-common в системах на базе Debian/Ubuntu предоставляет набор инструментов для удобного управления репозиториями и источниками пакетов (PPA). Основная причина установки этого пакета — доступ к команде add-apt-repository, которая позволяет Добавлять PPA-репозитории (Personal Package Archives) для установки актуальных версий ПО. А так же управление ключами GPG

Дальше скачиваем ключ

curl -fsSL https://download.opensuse.org/repositories/isv:/cri-o:/stable:/$CRIO_VERSION/deb/Release.key | gpg --dearmor -o /etc/apt/keyrings/cri-o-apt-keyring.gpg 

Добавляем репозиторий

echo "deb [signed-by=/etc/apt/keyrings/cri-o-apt-keyring.gpg] https://download.opensuse.org/repositories/isv:/cri-o:/stable:/$CRIO_VERSION/deb/ /" | tee /etc/apt/sources.list.d/cri-o.list 

Обновляем пакетный менеджер и устанавливаем cri-o

apt-get update apt-get install -y cri-o 

Перегружаем демоны systemd и включаем cri-o в автозагрузку

systemctl daemon-reload systemctl enable --now crio.service 

Шаг 7. Установка kubeadm, kubectl, kubelet

Для развертывания кластера Kubernetes вручную на базе LXC-контейнеров (или физических/виртуальных машин) нам необходимо установить три ключевых компонента:

kubeadm — это официальная утилита от Kubernetes, предназначенная для быстрого и безопасного развертывания и настройки кластеров.

Она автоматизирует множество рутинных операций:

  • генерацию необходимых ключей и сертификатов;

  • установку и настройку control-plane компонентов (kube-apiserver, kube-controller-manager, kube-scheduler);

  • создание необходимых конфигураций;

  • регистрацию worker-нод в кластер.

При этом kubeadm не управляет кластером после развёртывания — он лишь выполняет его инициализацию. В дальнейшем для управления используется kubectl.

kubectl — основная командная утилита (CLI) для взаимодействия с кластером Kubernetes.

С её помощью можно:

  • просматривать и управлять подами, сервисами, деплойментами, namespace’ами и другими объектами;

  • выполнять команды внутри подов (kubectl exec);

  • следить за логами (kubectl logs);

  • применять манифесты (kubectl apply -f …);

  • масштабировать приложения;

  • обновлять ресурсы и многое другое.

Эта утилита устанавливается на любой машине, с которой предполагается управление кластером. Обычно это master-нода или отдельная рабочая станция.

kubelet — это агент, который должен работать на каждой ноде (и master, и worker).

Он:

  • следит за запуском, работой и завершением подов;

  • взаимодействует с контейнерным рантаймом (в нашем случае это CRI-O);

  • опрашивает kube-apiserver, чтобы получить инструкции, какие поды должны быть запущены на узле;

  • сообщает состояние подов и ноды обратно в кластер;

  • следит за healthy-пробами, перезапускает контейнеры при падении и т.д.

Kubelet не управляет сам по себе контейнерами напрямую — он взаимодействует с ними через CRI (Container Runtime Interface).

Выполняем установку компонентов согласно официальной документации Kubernetes.

Установим дополнения, если нет в системе

apt-get install -y apt-transport-https ca-certificates curl gpg 

Скачаем и установим ключ репозитория

curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.32/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg 

Добавим репозиторий в файл ресурсов

echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.32/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list 

Обновим менеджер пакетов и установим kubelet kubeadm kubectl

apt-get update apt-get install -y kubelet kubeadm kubectl 

Чтобы избежать непредвиденного обновления этих пакетов при apt upgrade, закрепим их версии:

apt-mark hold kubelet kubeadm kubectl sudo systemctl enable --now kubelet 

На этом подготовка контейнера завершена. Терь из него можно сделать шаблон, и потом на его основе создавать контейнер уже готовый для развертывания в нем кластера kubernetes

На хосте proxmox выполните команды

pct stop 2000 pct template 2000 

Мы получили шаблон контейнера с установленным ПО. На его основе можно собрать кластер kubernetes. Можно автоматизировать все в terraform или ansible.

Для примера приведу код terraform. Провайдер использовал telmate/proxmox

Как настраивать провайдер и переменные для него можно посмотреть в документации

Показать/скрыть конфигурацию Terraform ▼
 variable "kube_count" {   description = "Number of LXC to create"   default     = 5 } resource "proxmox_lxc" "kubernetes" { count        = var.kube_count target_node  = "pimox2" hostname     = format("k8s-node-%02d", 1 + count.index) arch         = "arm64" clone        = "2000" full         = true password     = "" cores        = 4 memory       = 4096 start        = true vmid         = format("44%02d", count.index+21) ssh_public_keys = <<-EOT ssh-ed25519 AA*****@Macmini.local ssh-ed25519 AAA*************@MacBookAir.local EOT rootfs { storage = "local" size    = count.index < 2 ? "20G" : "50G" } network { name   = "eth0" bridge = "vmbr0" ip     = format("192.168.1.%d/24", count.index + 21) gw     = "192.168.1.1" type   = "veth" } } 

Для своей домашней лаборатории соберу кластер из 5 нод, две мастер ноды и три воркер ноды. Знаю что для высоко доступного кластера нужно минимум три мастер ноды, но для экспериментов хватит и двух.

Шаг 8. Развёртывание отказоустойчивого кластера Kubernetes

На этом этапе мы перейдём к развертыванию полноценного кластера Kubernetes с высокой доступностью. В моём случае в качестве управляющих (мастер) узлов участвуют две виртуальные машины: kube-master1 и kube-master2.

Отказоустойчивость в Kubernetes: в чём суть

Отказоустойчивость — ключевая особенность Kubernetes, и она реализуется на двух уровнях:

  1. Рабочие нагрузки (Pods)

  2. Управление кластером (Control Plane)

1. Отказоустойчивость рабочих нагрузок

Kubernetes не просто запускает контейнеры — он управляет их жизненным циклом. Вместо ручного запуска контейнеров, как в Docker, администратор описывает желаемое состояние: сколько экземпляров приложения должно быть, какие ресурсы требуются, какие ограничения, и так далее.

Контроллеры Kubernetes (например, Deployment) следят за тем, чтобы желаемое состояние совпадало с фактическим. Если по какой-то причине приложение (Pod) упало или узел, на котором оно работало, вышел из строя — Kubernetes обнаружит это и перезапустит приложение на другом доступном узле. Всё это происходит автоматически и практически без участия пользователя.

2. Отказоустойчивость  управляющих узлов

Управляющий узел — это мозг кластера. Он принимает решения, где и что запускать, и поддерживает общее состояние кластера. Его высокая доступность критична. Она обеспечивается за счёт:

  • Распределённого хранилища etcd: Конфигурация кластера хранится в нескольких копиях на разных узлах. Если один узел с etcd выйдет из строя, данные не будут потеряны.

  • Балансировщика нагрузки (load balancer): Он обеспечивает доступ к API Kubernetes даже в случае отказа одного из управляющих узлов. Все управляющие узлы — равноправны, и через балансировщик можно обратиться к любому доступному.

Балансировщик нагрузки

Для отказоустойчивого доступа к API Kubernetes нам потребуется внешний компонент, который будет балансировать трафик между kube-master1 и kube-master2. Я использую классическую связку:

  • Keepalived — обеспечивает работу виртуального IP-адреса, который будет автоматически перекидываться между kube-master1 и kube-master2, если один из них выйдет из строя.

  • HAProxy — реверс-прокси, который принимает запросы на виртуальный IP и равномерно распределяет их между работающими API-серверами Kubernetes.

В результате:

  • Клиент или компонент кластера всегда обращается к одному IP-адресу — виртуальному.

  • Keepalived следит за здоровьем узлов и перемещает IP на живой мастер при отказе.

  • HAProxy на каждом мастере перенаправляет запросы на локальный API-сервер или — при необходимости — на другой мастер.

На мастер нодах выполним установку и настройку:

apt install -y keepalived haproxy 

И так, создаем файл настроек /etc/keepalived/keepalived.conf

mkdir -p /etc/keepalived && sudo tee /etc/keepalived/keepalived.conf << 'EOF' >/dev/null global_defs {     enable_script_security     script_user nobody } vrrp_script check_apiserver {   script "/etc/keepalived/check_apiserver.sh"   interval 3 } vrrp_instance VI_1 {     state BACKUP     interface ens33     virtual_router_id 5     priority 100     advert_int 1     nopreempt     authentication {         auth_type PASS         auth_pass qwerty     }     virtual_ipaddress {         192.168.1.20     }     track_script {         check_apiserver     } } EOF 

Вместо ens33 нужно прописать имя адаптера LXC контейнера, а в virtual_ipaddress прописать свободный ip адрес из своей подсети. Далее создадим скрипт /etc/keepalived/check_apiserver.sh  Скрипт предназначен для проверки доступности серверов

#!/bin/sh APISERVER_VIP=192.168.1.20 APISERVER_DEST_PORT=3636 PROTO=http errorExit() {    echo "*** $*" 1>&2    exit 1 } curl --silent --max-time 2 --insecure ${PROTO}://localhost:${APISERVER_DEST_PORT}/ -o /dev/null || errorExit "Error GET ${PROTO}://localhost:${APISERVER_DEST_PORT}/" if ip addr | grep -q ${APISERVER_VIP}; then    curl --silent --max-time 2 --insecure ${PROTO}://${APISERVER_VIP}:${APISERVER_DEST_PORT}/ -o /dev/null || errorExit "Error GET ${PROTO}://${APISERVER_VIP}:${APISERVER_DEST_PORT}/" fi 

Поменяем атрибуты скрипта, сделаем его запускаемым, включаем в автозагрузку

chmod +x /etc/keepalived/check_apiserver.sh systemctl enable keepalived systemctl start keepalived 

Файл настроек haproxy /etc/haproxy/haproxy.cfg откроем удалим все и вставим следующее содержимое

Показать/скрыть файл ▼
 # Global settings #--------------------------------------------------------------------- global    log /dev/log local0 info alert    log /dev/log local1 notice alert    daemon #--------------------------------------------------------------------- common defaults that all the 'listen' and 'backend' sections will use if not designated in their block #--------------------------------------------------------------------- defaults mode                    http log                     global option                  httplog option                  dontlognull option http-server-close option forwardfor       except 127.0.0.0/8 option                  redispatch retries                 1 timeout http-request    10s timeout queue           20s timeout connect         5s timeout client          20s timeout server          20s timeout http-keep-alive 10s timeout check           10s #--------------------------------------------------------------------- apiserver frontend which proxys to the control plane nodes #--------------------------------------------------------------------- frontend apiserver bind *:3636 mode tcp option tcplog default_backend apiserver #--------------------------------------------------------------------- round robin balancing for apiserver #--------------------------------------------------------------------- backend apiserver option httpchk GET /healthz http-check expect status 200 mode tcp option ssl-hello-chk balance     roundrobin server node1 192.168.1.21:6443 check server node2 192.168.1.22:6443 check 

Файл нужно подкорректировать в соответствии с вашими настройками, в поле от server node1 до server nodeN нужно прописать ip адреса мастер нод.

Запустим демона haproxy

systemctl enable haproxy systemctl restart haproxy 

Следующим шагом запустим первый управляющий узел

kubeadm init --pod-network-cidr=10.244.0.0/16 --control-plane-endpoint "192.168.1.20:3636" --upload-certs 

После успешного выполнения команды kubeadm init, в консоли появится сообщение:

Это означает, что кластер успешно инициализирован, и мастер-узел готов к работе. Но чтобы начать управлять кластером через команду kubectl, нужно указать, где взять конфигурационный файл — admin.conf.


Что такое admin.conf и зачем он нужен?

Файл admin.conf содержит:

  • Информацию о кластере (куда подключаться).

  • Данные для аутентификации (сертификаты и ключи).

  • Контекст пользователя (от имени кого выполняются команды).

По сути, это паспорт администратора кластера, с помощью которого kubectl знает, куда обращаться и с какими правами.

Нужно создать папку .kube в домашнем каталоге и скопировать туда файл admin.conf под именем config. Команды:

mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config 

После этого можно использовать kubectl без дополнительных параметров, например:

kubectl get nodes 

Если работать на удаленной машине по ssh то нужно создать папку .kube в домашнем каталоге на своем компьютере и скопировать туда файл admin.conf под именем config.


Далее в выводе после успешной установки должна появится строка для добавления управляющих узлов. Здесь приведу для примера свою строку, у вас она будет другая

kubeadm join 192.168.1.20:3636 --token 00lshb.zpz2k4u8dkloo7yz \\         --discovery-token-ca-cert-hash sha256:328188a8c34532754899b59265129896fd215 \\         --control-plane --certificate-key 59c31907694464356789765439871f4c844c26215a565b 

Ее нужно скопировать и запустить на второй управляющей ноде, после этого вторая нода должна появится в кластере.

Для добавления рабочих узлов будет другая строка, примерное содержание будет таким

kubeadm join 192.168.1.20:3636 --token 00lshb.zpz2k4u8dkloo7yz \\ --discovery-token-ca-cert-hash sha256:328188a5436875348789879344b2ea68ba6ab59265129896fd215         

Ее нужно запустить на всех рабочих нодах.

И в конце нужно установить сетевой плагин. На управляющей ноде запустить команду

kubectl apply \-f [https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml](https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml) 

Кластер готов к употреблению

Заключение

Развёртывание Kubernetes-кластера в LXC-контейнерах на базе гипервизора Proxmox — это отличный способ создать компактную, гибкую и производительную лабораторию для тестирования, обучения или разработки. Использование лёгких контейнеров LXC позволяет экономно расходовать ресурсы, а Proxmox обеспечивает удобный интерфейс и продвинутые возможности управления виртуализацией.

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

Благодаря использованию Terraform и автоматизации развёртывания вы получаете воспроизводимую инфраструктуру, которую можно быстро развернуть заново или адаптировать под новые задачи. Это особенно ценно в условиях быстроменяющейся среды разработки и постоянного эксперимента.

Всё это делает Proxmox + LXC идеальной платформой для построения домашних и экспериментальных Kubernetes-кластеров.

P.S.

Это моя первая публикация — буду рад конструктивной критике, замечаниям или предложениям по улучшению. Пусть мой опыт станет небольшой опорой в вашему пути по миру Kubernetes 🙌


ссылка на оригинал статьи https://habr.com/ru/articles/901260/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *