Files
talos-kurs/talos-k8s/generate.sh

559 lines
18 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/bash
### Дефолты ###
IMAGE=factory.talos.dev/metal-installer/956b9107edd250304169d2e7a765cdd4e0c31f9097036e2e113b042e6c01bb98:v1.10.4
DEFAULT_K8S_VERSION=1.33.2
CONFIG_DIR="config"
# Цвета для вывода
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Helper function for checking uniqueness in an array
contains_element () {
local e match="$1"
shift
for e; do [[ "$e" == "$match" ]] && return 0; done
return 1
}
# Function for asking yes/no questions
ask_yes_no() {
local prompt="$1"
local default="$2"
local answer
while true; do
read -p "$prompt" answer
answer=${answer:-$default}
answer=$(echo "$answer" | tr '[:upper:]' '[:lower:]') # convert to lowercase
case "$answer" in
y|yes)
echo "y"
return 0
;;
n|no)
echo "n"
return 0
;;
*)
echo -e "${YELLOW}Некорректный ввод. Введите 'y'/'yes' или 'n'/'no'.${NC}"
;;
esac
done
}
echo -e "${GREEN}--- Интерактивный конфигуратор Talos K8s ---${NC}"
# --- Вопросы пользователю ---
# Список вопросов для прогресса
QUESTIONS=(
"Введите имя кластера [talos-demo]: "
"Введите версию Kubernetes [${DEFAULT_K8S_VERSION}]: "
"Введите имя сетевого адаптера (например, ens18 или eth0) [ens18]: "
"Введите количество control plane (нечетное, макс 7) [1]: "
"Введите количество worker-нод (макс 15, мин 0) [3]: "
"Введите общий gateway (например, 192.168.23.1): "
"Введите маску сети (например, 24) [24]: "
"Введите первый DNS сервер: (например, 8.8.8.8) [8.8.8.8]: "
"Введите второй DNS сервер: (например, 8.8.4.4) [8.8.4.4]: "
"Введите первый NTP сервер [1.ru.pool.ntp.org]: "
"Введите второй NTP сервер [2.ru.pool.ntp.org]: "
"Введите третий NTP сервер [3.ru.pool.ntp.org]: "
"Нужен ли VIP адрес? (y/n) [y]: "
"Введите VIP адрес (например, 192.168.23.20): "
"Нужен ли внешний балансировщик? (y/n) [n]: "
"Введите IP адрес внешнего балансировщика: "
"Введите диск для установки базовой ос [/dev/sda]: "
"Нужна ли поддержка drbd? (y/n) [y]: "
"Нужна ли поддержка zfs? (y/n) [n]: "
"Нужна ли поддержка spl? (y/n) [n]: "
"Нужна ли поддержка vfio_pci? (y/n) [n]: "
"Нужна ли поддержка vfio_iommu_type1? (y/n) [n]: "
"Нужна ли поддержка openvswitch? (y/n) [n]: "
"Хотите ли вы использовать зеркала timeweb.cloud и gcr.io для docker.io? (y/n) [y]: "
"Нужно ли установить maxPods: 512? (110 подов на ноду по умолчанию) (y/n) [n]: "
)
QUESTION_IDX=0
QUESTION_TOTAL=${#QUESTIONS[@]}
# Функция для вывода вопроса с прогрессом
ask_with_progress() {
local prompt="$1"
((QUESTION_IDX++))
read -p "[$QUESTION_IDX из $QUESTION_TOTAL] $prompt" "$2"
}
ask_yes_no_progress() {
local prompt="$1"
local default="$2"
local varname="$3"
((QUESTION_IDX++))
local answer
while true; do
read -p "[$QUESTION_IDX из $QUESTION_TOTAL] $prompt" answer
answer=${answer:-$default}
answer=$(echo "$answer" | tr '[:upper:]' '[:lower:]')
case "$answer" in
y|yes)
eval "$varname=\"y\""
return 0
;;
n|no)
eval "$varname=\"n\""
return 0
;;
*)
echo -e "${YELLOW}Некорректный ввод. Введите 'y'/'yes' или 'n'/'no'.${NC}"
;;
esac
done
}
# Имя кластера
ask_with_progress "Введите имя кластера [talos-demo]: " CLUSTER_NAME
CLUSTER_NAME=${CLUSTER_NAME:-talos-demo}
# Версия Kubernetes
ask_with_progress "Введите версию Kubernetes [${DEFAULT_K8S_VERSION}]: " K8S_VERSION
K8S_VERSION=${K8S_VERSION:-${DEFAULT_K8S_VERSION}}
# Имя сетевого адаптера
ask_with_progress "Введите имя сетевого адаптера (например, ens18 или eth0) [ens18]: " INTERFACE_NAME
INTERFACE_NAME=${INTERFACE_NAME:-ens18}
# Количество control plane
while true; do
ask_with_progress "Введите количество control plane (нечетное, макс 7) [1]: " CP_COUNT
CP_COUNT=${CP_COUNT:-1}
if (( CP_COUNT % 2 != 0 && CP_COUNT > 0 && CP_COUNT <= 7 )); then
break
else
echo -e "${YELLOW}Некорректное значение. Введите нечетное число от 1 до 7.${NC}"
fi
done
# Количество worker-нод
while true; do
ask_with_progress "Введите количество worker-нод (макс 15, мин 0) [3]: " WORKER_COUNT
WORKER_COUNT=${WORKER_COUNT:-3}
if (( WORKER_COUNT >= 0 && WORKER_COUNT <= 15 )); then
break
else
echo -e "${YELLOW}Некорректное значение. Введите число от 0 до 15.${NC}"
fi
done
# Общий шлюз
while true; do
ask_with_progress "Введите общий gateway (например, 192.168.23.1): " GATEWAY
if [[ -n "$GATEWAY" ]]; then
USED_IPS+=("$GATEWAY")
break
else
echo -e "${YELLOW}Gateway не может быть пустым.${NC}"
fi
done
# Маска сети
while true; do
ask_with_progress "Введите маску сети (например, 24) [24]: " NETMASK
NETMASK=${NETMASK:-24}
if [[ -n "$NETMASK" ]]; then
break
else
echo -e "${YELLOW}Маска сети не может быть пустой.${NC}"
fi
done
# DNS-серверы
while true; do
ask_with_progress "Введите первый DNS сервер: (например, 8.8.8.8) [8.8.8.8]: " DNS1
DNS1=${DNS1:-8.8.8.8}
if [[ -n "$DNS1" ]]; then
break
else
echo -e "${YELLOW}DNS сервер не может быть пустым.${NC}"
fi
done
while true; do
ask_with_progress "Введите второй DNS сервер: (например, 8.8.4.4) [8.8.4.4]: " DNS2
DNS2=${DNS2:-8.8.4.4}
if [[ -n "$DNS2" ]]; then
break
else
echo -e "${YELLOW}DNS сервер не может быть пустым.${NC}"
fi
done
# NTP-серверы
ask_with_progress "Введите первый NTP сервер [1.ru.pool.ntp.org]: " NTP1
NTP1=${NTP1:-1.ru.pool.ntp.org}
ask_with_progress "Введите второй NTP сервер [2.ru.pool.ntp.org]: " NTP2
NTP2=${NTP2:-2.ru.pool.ntp.org}
ask_with_progress "Введите третий NTP сервер [3.ru.pool.ntp.org]: " NTP3
NTP3=${NTP3:-3.ru.pool.ntp.org}
# VIP адрес
VIP_IP=""
USE_VIP="n"
if (( CP_COUNT > 1 )); then
ask_yes_no_progress "Нужен ли VIP адрес? (y/n) [y]: " "y" USE_VIP
if [[ "$USE_VIP" == "y" ]]; then
while true; do
ask_with_progress "Введите VIP адрес (например, 192.168.23.20): " VIP_IP
if [[ -z "$VIP_IP" ]]; then
echo -e "${YELLOW}VIP адрес не может быть пустым.${NC}"
continue
fi
if contains_element "$VIP_IP" "${USED_IPS[@]}"; then
echo -e "${YELLOW}Этот IP адрес уже используется. Введите уникальный адрес.${NC}"
else
USED_IPS+=("$VIP_IP")
break
fi
done
fi
fi
# Внешний балансировщик
EXT_BALANCER_IP=""
ask_yes_no_progress "Нужен ли внешний балансировщик? (y/n) [n]: " "n" USE_EXT_BALANCER
if [[ "$USE_EXT_BALANCER" == "y" ]]; then
while true; do
ask_with_progress "Введите IP адрес внешнего балансировщика: " EXT_BALANCER_IP_INPUT
if [[ -z "$EXT_BALANCER_IP_INPUT" ]]; then
echo -e "${YELLOW}IP адрес не может быть пустым.${NC}"
continue
fi
if contains_element "$EXT_BALANCER_IP_INPUT" "${USED_IPS[@]}"; then
echo -e "${YELLOW}Этот IP адрес уже используется. Введите уникальный адрес.${NC}"
else
EXT_BALANCER_IP=$EXT_BALANCER_IP_INPUT
USED_IPS+=("$EXT_BALANCER_IP")
break
fi
done
fi
# Диск
ask_with_progress "Введите диск для установки базовой ос [/dev/sda]: " DISK
DISK=${DISK:-/dev/sda}
# Вопросы по модулям ядра
ask_yes_no_progress "Нужна ли поддержка drbd? (y/n) [y]: " "y" USE_DRBD
ask_yes_no_progress "Нужна ли поддержка zfs? (y/n) [n]: " "n" USE_ZFS
ask_yes_no_progress "Нужна ли поддержка spl? (y/n) [n]: " "n" USE_SPL
ask_yes_no_progress "Нужна ли поддержка vfio_pci? (y/n) [n]: " "n" USE_VFIO_PCI
ask_yes_no_progress "Нужна ли поддержка vfio_iommu_type1? (y/n) [n]: " "n" USE_VFIO_IOMMU_TYPE1
ask_yes_no_progress "Нужна ли поддержка openvswitch? (y/n) [n]: " "n" USE_OPENVSWITCH
# Использовать зеркало mirror.gcr.io?
ask_yes_no_progress "Хотите ли вы использовать зеркала timeweb.cloud и gcr.io для docker.io? (y/n) [y]: " "y" USE_MIRROR_GCR
# Вопрос: нужен ли maxPods: 512 для kubelet?
ask_yes_no_progress "Нужно ли установить maxPods: 512? (110 подов на ноду по умолчанию) (y/n) [n]: " "n" USE_KUBELET_MAXPODS
# --- Генерация конфигурационных файлов ---
mkdir -p "$CONFIG_DIR"
PATCH_FILE="$CONFIG_DIR/patch.yaml"
# --- Создание patch.yaml ---
# Записываем первую часть файла
cat > "$PATCH_FILE" << EOF
machine:
EOF
# Добавляем certSANs если есть внешний балансировщик
if [[ -n "$EXT_BALANCER_IP" ]]; then
cat >> "$PATCH_FILE" << EOF
certSANs:
- ${EXT_BALANCER_IP}
EOF
fi
# Добавляем блок kernel для drbd, если нужно
if [[ "$USE_DRBD" == "y" ]] && (( WORKER_COUNT == 0 )); then
cat >> "$PATCH_FILE" << EOF
kernel:
modules:
- name: drbd
parameters:
- usermode_helper=disabled
EOF
if [[ "$USE_ZFS" == "y" ]]; then
cat >> "$PATCH_FILE" << EOF
- name: zfs
EOF
fi
if [[ "$USE_SPL" == "y" ]]; then
cat >> "$PATCH_FILE" << EOF
- name: spl
EOF
fi
if [[ "$USE_VFIO_PCI" == "y" ]]; then
cat >> "$PATCH_FILE" << EOF
- name: vfio_pci
EOF
fi
if [[ "$USE_VFIO_IOMMU_TYPE1" == "y" ]]; then
cat >> "$PATCH_FILE" << EOF
- name: vfio_iommu_type1
EOF
fi
if [[ "$USE_OPENVSWITCH" == "y" ]]; then
cat >> "$PATCH_FILE" << EOF
- name: openvswitch
EOF
fi
fi
# Добавляем основную часть machine и начало cluster
cat >> "$PATCH_FILE" << EOF
network:
nameservers:
- ${DNS1}
- ${DNS2}
install:
disk: ${DISK}
image: ${IMAGE}
EOF
if [[ "$USE_MIRROR_GCR" == "y" ]]; then
cat >> "$PATCH_FILE" << EOF
registries:
mirrors:
docker.io:
endpoints:
- https://dockerhub.timeweb.cloud
- https://mirror.gcr.io
EOF
fi
cat >> "$PATCH_FILE" << EOF
time:
servers:
- ${NTP1}
- ${NTP2}
- ${NTP3}
cluster:
EOF
# Добавляем allowSchedulingOnControlPlanes и финальную часть
if (( WORKER_COUNT == 0 )); then
echo -e "\n${YELLOW}Воркеры отсутствуют. Разрешение на запуск подов на control plane...${NC}"
cat >> "$PATCH_FILE" << EOF
allowSchedulingOnControlPlanes: true
network:
cni:
name: none
proxy:
disabled: true
EOF
else
cat >> "$PATCH_FILE" << EOF
network:
cni:
name: none
proxy:
disabled: true
EOF
fi
CP_IPS=()
# Генерация патчей для control plane
echo -e "\n${GREEN}--- Настройка Control Plane нод ---${NC}"
for i in $(seq 1 $CP_COUNT); do
while true; do
read -p "Введите IP адрес для control plane $i (например, 192.168.23.5${i}): " CP_IP
if [[ -z "$CP_IP" ]]; then
echo -e "${YELLOW}IP адрес не может быть пустым.${NC}"
continue
fi
if contains_element "$CP_IP" "${USED_IPS[@]}"; then
echo -e "${YELLOW}Этот IP адрес уже используется. Введите уникальный адрес.${NC}"
else
CP_IPS+=("$CP_IP")
USED_IPS+=("$CP_IP")
break
fi
done
HOSTNAME="cp-$i"
FILENAME="$CONFIG_DIR/cp$i.patch"
# Создание базового патча
cat > "$FILENAME" << EOF
machine:
EOF
if [[ "$USE_KUBELET_MAXPODS" == "y" ]]; then
cat >> "$FILENAME" << EOF
kubelet:
extraConfig:
maxPods: 512
EOF
fi
cat >> "$FILENAME" << EOF
network:
hostname: $HOSTNAME
interfaces:
- interface: $INTERFACE_NAME
dhcp: false
addresses:
- $CP_IP/$NETMASK
EOF
# Добавление VIP, если он используется
if [[ "$USE_VIP" == "y" && -n "$VIP_IP" ]]; then
cat >> "$FILENAME" << EOF
vip:
ip: $VIP_IP
EOF
fi
# Добавление маршрутов
cat >> "$FILENAME" << EOF
routes:
- network: 0.0.0.0/0
gateway: $GATEWAY
EOF
echo "Создан файл: $FILENAME"
done
# Генерация патчей для worker-нод
if (( WORKER_COUNT > 0 )); then
echo -e "\n${GREEN}--- Настройка Worker нод ---${NC}"
for i in $(seq 1 $WORKER_COUNT); do
while true; do
read -p "Введите IP адрес для worker $i (например, 192.168.23.10${i}): " WORKER_IP
if [[ -z "$WORKER_IP" ]]; then
echo -e "${YELLOW}IP адрес не может быть пустым.${NC}"
continue
fi
if contains_element "$WORKER_IP" "${USED_IPS[@]}"; then
echo -e "${YELLOW}Этот IP адрес уже используется. Введите уникальный адрес.${NC}"
else
USED_IPS+=("$WORKER_IP")
break
fi
done
HOSTNAME="worker-$i"
FILENAME="$CONFIG_DIR/worker$i.patch"
cat > "$FILENAME" << EOF
machine:
network:
hostname: $HOSTNAME
interfaces:
- deviceSelector:
physical: true
dhcp: false
addresses:
- $WORKER_IP/$NETMASK
routes:
- network: 0.0.0.0/0
gateway: $GATEWAY
EOF
# Добавляем drbd если выбрано
if [[ "$USE_DRBD" == "y" ]]; then
cat >> "$FILENAME" << EOF
kernel:
modules:
- name: drbd
parameters:
- usermode_helper=disabled
EOF
fi
echo "Создан файл: $FILENAME"
done
fi
# --- Вывод команд для выполнения ---
echo -e "\n${YELLOW}--------------------------------------------------${NC}"
echo -e "${GREEN}Конфигурация завершена. Собираю файлы в кучу:${NC}"
echo -e "${YELLOW}--------------------------------------------------${NC}"
# Генерация секретов
talosctl gen secrets -o $CONFIG_DIR/secrets.yaml
# Определение эндпоинта
ENDPOINT_IP=""
if [[ "$USE_VIP" == "y" && -n "$VIP_IP" ]]; then
ENDPOINT_IP=$VIP_IP
else
FIRST_CP_FULL_IP=${CP_IPS[0]}
ENDPOINT_IP=$(echo "$FIRST_CP_FULL_IP" | cut -d'/' -f1)
fi
# Генерация основной конфигурации
cd $CONFIG_DIR
echo "talosctl gen config --kubernetes-version $K8S_VERSION --with-secrets secrets.yaml $CLUSTER_NAME https://${ENDPOINT_IP}:6443 --config-patch @patch.yaml"
talosctl gen config --kubernetes-version $K8S_VERSION --with-secrets secrets.yaml $CLUSTER_NAME https://${ENDPOINT_IP}:6443 --config-patch @patch.yaml
# Применение патчей к control plane
for i in $(seq 1 $CP_COUNT); do
talosctl machineconfig patch controlplane.yaml --patch @cp$i.patch --output cp$i.yaml
echo "Создан файл: $CONFIG_DIR/cp$i.yaml"
done
# Применение патчей к worker-нодам
if (( WORKER_COUNT > 0 )); then
for i in $(seq 1 $WORKER_COUNT); do
talosctl machineconfig patch worker.yaml --patch @worker$i.patch --output worker$i.yaml
echo "Создан файл: $CONFIG_DIR/worker$i.yaml"
done
fi
# Обновление talosconfig с endpoints
echo -e "\n${GREEN}--- Обновление talosconfig ---${NC}"
# Создаем массив endpoints
ENDPOINTS=()
# Добавляем все control plane IPs
for cp_ip in "${CP_IPS[@]}"; do
ENDPOINTS+=("$cp_ip")
done
# Добавляем VIP если есть
if [[ "$USE_VIP" == "y" && -n "$VIP_IP" ]]; then
ENDPOINTS+=("$VIP_IP")
fi
# Добавляем внешний балансировщик если есть
if [[ "$USE_EXT_BALANCER" == "y" && -n "$EXT_BALANCER_IP" ]]; then
ENDPOINTS+=("$EXT_BALANCER_IP")
fi
# Объединяем endpoints через запятую с пробелом
ENDPOINTS_STRING=$(IFS="," ; echo "${ENDPOINTS[*]}")
# Обновляем talosconfig
if [[ -f "talosconfig" ]]; then
TMP_CONFIG=$(mktemp)
while IFS= read -r line; do
if [[ "$line" == *"endpoints: []"* ]]; then
echo "${line/endpoints: []/endpoints: [$ENDPOINTS_STRING]}" >> "$TMP_CONFIG"
else
echo "$line" >> "$TMP_CONFIG"
fi
done < "talosconfig"
mv "$TMP_CONFIG" "talosconfig"
echo -e "${GREEN}Обновлен talosconfig с endpoints: [$ENDPOINTS_STRING]${NC}"
else
echo -e "${YELLOW}Файл talosconfig не найден${NC}"
fi
cd ..
echo "Работа скрипта завершена"