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

1. Введение
Kubernetes стал стандартом де-факто для управления контейнеризированными приложениями. Его установка и настройка во многом упростились, благодаря таким инструментам, как kubeadm
, которые берут на себя генерацию сертификатов, запуск компонентов и базовую конфигурацию кластера.
Однако, за удобством скрывается сложная архитектура, понимание которой критично при проектировании отказоустойчивых решений, создании собственных автоматизаций или отладке проблем в продакшене. Чтобы по-настоящему понять, как работает кластер Kubernetes, важно пройти путь развёртывания вручную — от инициализации, до полной готовности.
Kubernetes The Hard Way — это руководство, в котором кластер разворачивается поэтапно, без использования kubeadm
и других автоматизированных инструментов. Вместо чёрного ящика — последовательное выполнение всех шагов, которые обычно выполняются под капотом.
Каждый этап соответствует конкретной фазе kubeadm init
или kubeadm join
, но реализован вручную, с явной генерацией ключей, подготовкой конфигураций, запуском процессов и проверкой состояния системы.
💡 Результат — полнофункциональный Kubernetes-кластер, практически неотличимый от собранного через
kubeadm
, но подготовленный с полным пониманием всех внутренних связей.
Статья рассчитана на читателя, который уже знаком с базовыми концепциями контейнеризации и Kubernetes в целом. Без этого уровень детализации будет избыточным. Если вы только начинаете, рекомендуем ознакомиться с официальным Kubernetes Bootcamp.
🔧 Предисловие: Почему важен порядок запуска
Некоторые системы устроены так, что компоненты взаимозависимы, а управление ими частично выполняется внутри самой системы. Это требует чёткого порядка действий:
⚙️ Взаимозависимость компонентов
Один компонент не может стартовать без другого.
Пример: API требует хранилище, а хранилище — сеть и конфигурацию.⏱ Нель зя запускать всё одновременно
Параллельный запуск приводит к нежелательным результатам.
Пример: Планировщик ждёт API, а API — загрузку и инициализацию данных.🔄 Часть компонентов запускается извне
До готовности системы некоторые процессы стартуют через окружение.
Пример:kubelet
запускается через systemd, а не как часть кластера.🛠 Нужен bootstrap-этап
Конфиги, сертификаты, адреса — всё подготавливается вручную.
Пример: Начальная генерация root CA,kubeconfig
, static pod-манифестов.🤖 Переход к самоуправлению
После запуска система начинает управлять своими процессами и состоянием.
Пример: Управляющие компоненты начинают контролировать друг друга через API.
Без строго определённой последовательности такая система не заработает.
Именно поэтому существуют инструменты и утилиты, например, kubeadm
, которые устраняют проблему
«курицы и яйца» и задают корректный порядок развёртывания.
Главы:
- 1. Введение
- 2. Почему «The Hard Way»
- 3. Архитектура развертывания
- 4. Создание инфраструктуры
- 5. Базовая настройка узлов
- 6. Загрузка модулей ядра
- 7. Настройка параметров sysctl
- 8. Установка компонентов
- 9. Настройка компонентов
- 10. Проверка готовности компонентов
- 11. Работа с сертификатами
- 12. Создание корневых сертификатов
- 13. Создание сертификатов приложений
- 14. Создание ключа подписи ServiceAccount
- 15*. Создание всех сертификатов
- 16. Создание конфигураций kubeconfig
- 17*. Создание всех kubeconfig
- 18. Проверка блока сертификатов
- 19. Создание static pod-ов управляющего контура
- 20*. Создание всех static pod-ов управляющего контура
- 21. Создание static pod-ов ETCD кластера
- 22. Запуск службы Kubelet
- 23. Проверка состояния кластера
- 24. Настройка ролевой модели
- 25. Загрузка конфигурации в кластер
- 26. Загрузка корневых сертификатов в кластер
- 27. Маркировка и ограничение узлов

2. Почему «The Hard Way»
Развёртывание Kubernetes вручную требует дополнительных усилий. Однако, данный подход обладает рядом преимуществ:
- Обеспечивает углублённое понимание архитектуры и внутренней логики работы компонентов Kubernetes.
- Позволяет гибко настраивать каждый компонент кластера под конкретные технические требования.
3. Архитектура развертывания
Уровень компонентов
Технологический слой.
Ниже приведён список компонентов, необходимых для ручного развёртывания кластера. Дл я обеспечения совместимости все версии должны быть синхронизированы между собой.
Компонент | Версия | Назначение |
---|---|---|
containerd | 1.7.19 | Контейнерный рантайм, управляющий жизненным циклом контейнеров. |
runc | v1.1.12 | Низкоуровневый инструмент для запуска контейнеров с использованием средств ядра Linux. |
crictl | v1.30.0 | Утилита для отладки CRI-сред с поддержкой взаимодействия с containerd. |
kubectl | v1.30.4 | Клиент для взаимодействия с Kubernetes API. |
kubeadm | v1.30.4 | Инструмент для автоматизации установки и конфигурации Kubernetes (используется для валидации конфигурации). |
kubelet | v1.30.4 | Агент, запускаемый на каждом узле, обеспечивающий выполнение и контроль состояния подов. |
etcd | 3.5.12-0 | Распределённое хранилище типа "ключ–значение" для хранения конфигурации и состояния кластера. |
kube-apiserver | v1.30.4 | Компонент, предоставляющий REST API для взаимодействия с кластером. |
kube-controller-manager | v1.30.4 | Управляет состоянием объектов в кластере с помощью встроенных контроллеров. |
kube-scheduler | v1.30.4 | Отвечает за планирование размещения подов на узлах. |
conntrack | v1.4.+ | Утилита для отслеживания сетевых соединений (используется iptables и kubelet). |
socat | 1.8.+ | Утилита для проброса портов и организации TCP-туннелей (используется при отладке и проксировании). |
Уровень коммутации
Сетевая диаграмма развертывания.
Компонент | Порт | Протокол |
---|---|---|
etcd-server | 2379 | TCP |
etcd-peer | 2380 | TCP |
etcd-metrics | 2381 | TCP |
kube-apiserver | 6443 | TCP |
kube-controller-manager | 10257 | TCP |
kube-scheduler | 10259 | TCP |
kubelet-healthz | 10248 | TCP |
kubelet-server | 10250 | TCP |
kubelet-read-only-port | 10255 | TCP |
Уровень балансировки
Технологический слой.
IP-адрес | Целевая группа | Порт | Целевой порт |
---|---|---|---|
VIP-LB | - NODE-IP-1 - NODE-IP-2 - NODE-IP-3 | 6443 | 6443 |
DNS-записи
A-запись | IP-адрес | TTL |
---|---|---|
api.my-first-cluster.example.com | VIP-LB | 60s |
master-1.my-first-cluster.example.com | NODE-IP-1 | 60s |
master-2.my-first-cluster.example.com | NODE-IP-2 | 60s |
master-3.my-first-cluster.example.com | NODE-IP-3 | 60s |
4. Создание инфраструктуры
На этом этапе задаётся базовая архитектура кластера, включая его сетевую схему, управляющие узлы и основные параметры.
Сведения о кластере
Имя | Внешний домен | Версия Kubernetes |
---|---|---|
my-first-cluster | example.com | v1.30.4 |
Управляющие узлы
Имя | IP-адрес | Операционная система | Ресурсы |
---|---|---|---|
master-1.my-first-cluster.example.com | NODE-IP-1 | ubuntu-24-04-lts | 2CPU / 2RAM / 20GB |
master-2.my-first-cluster.example.com | NODE-IP-2 | ubuntu-24-04-lts | 2CPU / 2RAM / 20GB |
master-3.my-first-cluster.example.com | NODE-IP-3 | ubuntu-24-04-lts | 2CPU / 2RAM / 20GB |
5. Базовая настройка узлов
Этот раздел посвящён базовой подготовке узлов Kubernetes перед установкой компонентов. В нём описывается настройка переменных окружения, смена имени хоста и установка необходимых системных утилит. Эти шаги являются обязательными для корректной работы kubelet и других компонентов управляющего контура.
Базовая настройка узлов
● Обязателен к применению
Базовая настройка узлов
● Обязателен к применению
Базовые настройки узлов
- Переменные окружения узла.
- Изменение им ени узла.
- Установка зависимостей.
Переменные окружения узла
- master-1
- master-2
- master-3
export HOST_NAME=master-1
export HOST_NAME=master-2
export HOST_NAME=master-3
export CLUSTER_NAME="my-first-cluster"
export BASE_DOMAIN="example.com"
export CLUSTER_DOMAIN="cluster.local"
export FULL_HOST_NAME="${HOST_NAME}.${CLUSTER_NAME}.${BASE_DOMAIN}"
Изменение имени узла
hostnamectl set-hostname ${FULL_HOST_NAME}
Установка зависимостей
- apt
- yum
- dnf
sudo apt update
sudo apt install -y conntrack socat jq tree
sudo yum update
sudo yum install -y conntrack-tools socat jq tree
sudo dnf update
sudo dnf install -y conntrack-tools socat jq tree
6. Загрузка модулей ядра
Этот раздел посвящён загрузке модулей ядра, необходимых для корректной работы Kubernetes. Настройка включает конфигурацию modprobe и активацию модулей overlay и br_netfilter, обеспечивающих поддержку контейнерной файловой системы и сетевых функций. Эти действия обязательны для функционирования сетевых политик, iptables и контейнерных рантаймов.
Загрузка модулей ядра
● Обязателен к применению
Загрузка модулей ядра
● Обязателен к применению
Этапы установки компонента:
- Конфигурация modprobe.
- Загрузка модулей.
- Bash
- Cloud-init
Модуль overlay используется файловой системой OverlayFS для управления слоями контейнеров. Он позволяет объединять несколько директорий в единую виртуальную файловую систему. Применяется такими рантаймами, как Docker и containerd.
Модуль br_netfilter обеспечивает обработку трафика сетевых мостов через подсистему netfilter. Это необходимо для корректной работы iptables в Kubernetes.
7. Настройка параметров sysctl
Этот раздел посвящён настройке параметров ядра с помощью sysctl, необходимых для сетевой работы Kubernetes. Вносятся изменения, обеспечивающие маршрутизацию трафика между подами и корректную работу iptables для мостов. Эти параметры обязательны для включения пересылки IP-пакетов и фильтрации сетевых потоков в кластере.
Настройка параметров sysctl
● Обязателен к применению
Настройка параметров sysctl
● Обязателен к применению
Этапы установки компонента:
- Конфигурация sysctl.
- Загрузка модулей.
Сетевые параметры
Для корректной маршрутизации и фильтрации трафика необходимо задать параметры ядра.
- Bash
- Cloud-init
Если параметр net.ipv4.ip_forward
не активирован, система не будет пересылать IP-пакеты между интерфейсами. Это может привести к сетевым сбоям внутри кластера, недоступности сервисов и потере связи между подами.
- Bash
- Cloud-init
8. Установка компонентов
В этом разделе описан процесс установки основных компонентов, необходимых для работы Kubernetes-кластера. Установка выполняется вручную и позволяет подготовить окружение для последующих этапов инициализации и настройки управляющего контура.
- runc
- containerd
- kubelet
- etcd
- kubectl
- crictl
- kubeadm
Установка runc
● Обязателен к применению
Установка runc
● Обязателен к применению
Этапы установки компонента
- Создание рабочих директорий.
- Переменные окружения.
- Инструкция загрузки.
- Настройка прав.
- Сервис загрузки.
- Запуск сервиса загрузки.
- Bash
- Cloud-init
Создание рабочих директорий
mkdir -p /etc/default/runc
Переменные окружения
cat <<EOF > /etc/default/runc/download.env
COMPONENT_VERSION="v1.1.12"
REPOSITORY="https://github.com/opencontainers/runc/releases/download"
EOF
Инструкция загрузки
cat <<"EOF" > /etc/default/runc/download-script.sh
#!/bin/bash
set -Eeuo pipefail
COMPONENT_VERSION="${COMPONENT_VERSION:-v1.1.12}"
REPOSITORY="${REPOSITORY:-https://github.com/opencontainers/runc/releases/download}"
PATH_BIN="${REPOSITORY}/${COMPONENT_VERSION}/runc.amd64"
PATH_SHA256="${REPOSITORY}/${COMPONENT_VERSION}/runc.sha256sum"
INSTALL_PATH="/usr/local/bin/runc"
LOG_TAG="runc-installer"
TMP_DIR="$(mktemp -d)"
logger -t "$LOG_TAG" "[INFO] Checking current runc version..."
CURRENT_VERSION=$($INSTALL_PATH --version 2>/dev/null | head -n1 | awk '{print $NF}') || CURRENT_VERSION="none"
COMPONENT_VERSION_CLEAN=$(echo "$COMPONENT_VERSION" | sed 's/^v//')
logger -t "$LOG_TAG" "[INFO] Current: $CURRENT_VERSION, Target: $COMPONENT_VERSION_CLEAN"
if [[ "$CURRENT_VERSION" != "$COMPONENT_VERSION_CLEAN" ]]; then
logger -t "$LOG_TAG" "[INFO] Download URL: $PATH_BIN"
logger -t "$LOG_TAG" "[INFO] Updating runc to version $COMPONENT_VERSION..."
cd "$TMP_DIR"
logger -t "$LOG_TAG" "[INFO] Working directory: $PWD"
logger -t "$LOG_TAG" "[INFO] Downloading runc..."
curl -fsSL -o runc.amd64 "$PATH_BIN" || { logger -t "$LOG_TAG" "[ERROR] Failed to download runc"; exit 1; }
logger -t "$LOG_TAG" "[INFO] Downloading checksum file..."
curl -fsSL -o runc.sha256sum "$PATH_SHA256" || { logger -t "$LOG_TAG" "[ERROR] Failed to download checksum file"; exit 1; }
logger -t "$LOG_TAG" "[INFO] Verifying checksum..."
grep "runc.amd64" runc.sha256sum | sha256sum -c - || { logger -t "$LOG_TAG" "[ERROR] Checksum verification failed!"; exit 1; }
logger -t "$LOG_TAG" "[INFO] Installing runc..."
install -m 755 runc.amd64 "$INSTALL_PATH"
logger -t "$LOG_TAG" "[INFO] runc successfully updated to $COMPONENT_VERSION."
rm -rf "$TMP_DIR"
else
logger -t "$LOG_TAG" "[INFO] runc is already up to date. Skipping installation."
fi
EOF
Настройка прав
chmod +x /etc/default/runc/download-script.sh
Сервис загрузки
cat <<EOF > /usr/lib/systemd/system/runc-install.service
[Unit]
Description=Install and update in-cloud component runc
After=network.target
Wants=network-online.target
[Service]
Type=oneshot
EnvironmentFile=-/etc/default/runc/download.env
ExecStart=/bin/bash -c "/etc/default/runc/download-script.sh"
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
EOF
Загрузки
systemctl enable runc-install.service
systemctl start runc-install.service
Переменные окружения
- path: /etc/default/runc/download.env
owner: root:root
permissions: '0644'
content: |
COMPONENT_VERSION="v1.1.12"
REPOSITORY="https://github.com/opencontainers/runc/releases/download"
Инструкция загрузки/Настройка прав
- path: /etc/default/runc/download-script.sh
owner: root:root
permissions: '0755'
content: |
#!/bin/bash
set -Eeuo pipefail
COMPONENT_VERSION="${COMPONENT_VERSION:-v1.1.12}"
REPOSITORY="${REPOSITORY:-https://github.com/opencontainers/runc/releases/download}"
PATH_BIN="${REPOSITORY}/${COMPONENT_VERSION}/runc.amd64"
PATH_SHA256="${REPOSITORY}/${COMPONENT_VERSION}/runc.sha256sum"
INSTALL_PATH="/usr/local/bin/runc"
LOG_TAG="runc-installer"
TMP_DIR="$(mktemp -d)"
logger -t "$LOG_TAG" "[INFO] Checking current runc version..."
CURRENT_VERSION=$($INSTALL_PATH --version 2>/dev/null | head -n1 | awk '{print $NF}') || CURRENT_VERSION="none"
COMPONENT_VERSION_CLEAN=$(echo "$COMPONENT_VERSION" | sed 's/^v//')
logger -t "$LOG_TAG" "[INFO] Current: $CURRENT_VERSION, Target: $COMPONENT_VERSION_CLEAN"
if [[ "$CURRENT_VERSION" != "$COMPONENT_VERSION_CLEAN" ]]; then
logger -t "$LOG_TAG" "[INFO] Download URL: $PATH_BIN"
logger -t "$LOG_TAG" "[INFO] Updating runc to version $COMPONENT_VERSION..."
cd "$TMP_DIR"
logger -t "$LOG_TAG" "[INFO] Working directory: $PWD"
logger -t "$LOG_TAG" "[INFO] Downloading runc..."
curl -fsSL -o runc.amd64 "$PATH_BIN" || { logger -t "$LOG_TAG" "[ERROR] Failed to download runc"; exit 1; }
logger -t "$LOG_TAG" "[INFO] Downloading checksum file..."
curl -fsSL -o runc.sha256sum "$PATH_SHA256" || { logger -t "$LOG_TAG" "[ERROR] Failed to download checksum file"; exit 1; }
logger -t "$LOG_TAG" "[INFO] Verifying checksum..."
grep "runc.amd64" runc.sha256sum | sha256sum -c - || { logger -t "$LOG_TAG" "[ERROR] Checksum verification failed!"; exit 1; }
logger -t "$LOG_TAG" "[INFO] Installing runc..."
install -m 755 runc.amd64 "$INSTALL_PATH"
logger -t "$LOG_TAG" "[INFO] runc successfully updated to $COMPONENT_VERSION."
rm -rf "$TMP_DIR"
else
logger -t "$LOG_TAG" "[INFO] runc is already up to date. Skipping installation."
fi
Сервис загрузки
- path: /usr/lib/systemd/system/runc-install.service
owner: root:root
permissions: '0644'
content: |
[Unit]
Description=Install and update in-cloud component runc
After=network.target
Wants=network-online.target
[Service]
Type=oneshot
EnvironmentFile=-/etc/default/runc/download.env
ExecStart=/bin/bash -c "/etc/default/runc/download-script.sh"
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target