Разворачиваем k8s с помощью kubespray

0
(0)

Для этой лаборатории я создам 4 ВМ. Инсталяция будет небольшая, один мастер, одна ингресс нода и 2 воркер ноды. Kubespray предлагает также различные addons, которые можно поставить вместе с кластером. Поэтому мы включим metric server, ingress контроллер и metallb. Некоторое из этого я описывал в предыдущих статьях на сайте. Сейчас мы понимаем как это работает и можем опустить работы с каждым компонентом по отдельности. Вообще в addons есть много всего еще, но для стартового setup-а хватит и этого.

Создадим Vagrantfile следующего содержания.

Ruby
servers = [
  { hostname: "k8s-master", ip: "192.168.10.10", ssh_port: 2228 },
  { hostname: "k8s-worker1", ip: "192.168.10.11", ssh_port: 2223 },
  { hostname: "k8s-worker2", ip: "192.168.10.12", ssh_port: 2224 },
  { hostname: "k8s-ingress1", ip: "192.168.10.13", ssh_port: 2226 },
]

Vagrant.configure("2") do |config|
  config.vm.box = "bento/ubuntu-22.04"
  config.vm.box_check_update = false

  config.vm.provider "virtualbox" do |vb|
    vb.gui = false
    vb.memory = 3096
    vb.cpus = 2
    vb.check_guest_additions = false
  end

  servers.each do |machine|
    config.vm.define machine[:hostname] do |node|
      node.vm.hostname = machine[:hostname]
      node.vm.network "public_network", ip: machine[:ip], bridge: "wlo1"
      node.vm.network "forwarded_port", id: "ssh", host: machine[:ssh_port], guest: 22

      if machine[:hostname] == "k8s-master"
        node.vm.provider "virtualbox" do |vb|
          vb.memory = 2048
        end
      end

      node.vm.provider "virtualbox" do |vb|
        disk_name = "#{machine[:hostname]}_disk.vdi"
        disk_path = "/vm_data/k8s/#{disk_name}"

        unless File.exist?(disk_path)
          system("VBoxManage", "createhd", "--filename", disk_path, "--size", "40720")
        end
        
        controller_name = "SATA Controller"

        vb.customize [
          "storageattach", :id,
          "--storagectl", controller_name,
          "--port", "1",
          "--device", "0",
          "--type", "hdd",
          "--medium", disk_path
        ]
      end
    end
  end
end

Тут я выделяю отдельный диск на 40 гигов под всякие нужны k8s. Например туда будет смонтирован /var/lib как дефолт где kubelet будет запускать контейнеры и тома для longhorn, о котором я уже писал ранее.

Запускаем и заходим на первый воркер:

Bash
vagrant up
vagrant ssh k8s-worker1

У меня картина по дискам выглядит вот так:

sdb наш диск, который мы создали из Vagrantfile. Также уже есть VG том. Для начала разметим диск:

Bash
fdisk /dev/sdb
n
p
w

После разметки будет доступен раздел sdb1. Создадим там файловую систему:

Bash
mkfs.ext4 /dev/sdb1

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

Узнать имя текущей группы можно командой vgs.

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

Bash
pvcreate /dev/sdb1
vgextend ubuntu-vg /dev/sdb1
lvcreate -L50G -n var_lib ubuntu-vg

В итоге получаем что-то вроде:

Далее создадим также файловую систему и смонтируем том. Но перед этим, у меня в /var/lib естественно были уже данные, их нужно скопировать куда-то, например в отдельную директорию в /tmp и после монтирования, скопировать обратно. При установке не на Vagrant образ такого не будет, если изначально диски на машине будут разбиты так, как нужно.

Bash
mkfs.ext4 /dev/ubuntu-vg/var_lib
mount /dev/ubuntu-vg/var_lib /var/lib/
cat /etc/mtab | tail -n1 >> /etc/fstab

Эту же операцию нужно проделать на оставшихся машинах.

Тут уже можно накидать скрипт для автоматизации или написать ansible роль.

Bash
#!/bin/bash
set -e

if [[ $EUID -ne 0 ]]; then
   echo "Ошибка: Этот скрипт должен быть выполнен от имени root." 
   exit 1
fi

DEVICE="/dev/sdb"
PARTITION="${DEVICE}1"
VG="ubuntu-vg"
LV="var_lib"
MOUNT_POINT="/var/lib"
TMP_DIR="/tmp/var"

log() {
    echo "[$(date +'%Y-%m-%d\ %H:%M:%S')] $1"
}

log "Создание нового раздела на ${DEVICE}..."
(
echo n    # Создать новый раздел
echo p    # Тип: первичный
echo 1    # Номер раздела
echo      # Первый сектор по умолчанию
echo      # Последний сектор по умолчанию
echo w    # Записать изменения и выйти
) | fdisk "$DEVICE"

partprobe "$DEVICE"

log "Форматирование ${PARTITION} в файловую систему ext4..."
mkfs.ext4 "$PARTITION"

log "Создание физического тома на ${PARTITION}..."
pvcreate "$PARTITION" -y

log "Расширение группы томов ${VG} с использованием ${PARTITION}..."
vgextend "$VG" "$PARTITION"

log "Создание логического тома ${LV} размером 50G в группе томов ${VG}..."
lvcreate -L50G -n "$LV" "$VG"

LV_PATH="/dev/${VG}/${LV}"
log "Форматирование логического тома ${LV_PATH} в файловую систему ext4..."
mkfs.ext4 "$LV_PATH"

log "Копирование содержимого ${MOUNT_POINT} в ${TMP_DIR} с сохранением прав доступа..."
mkdir -p "$TMP_DIR"
cp -a "${MOUNT_POINT}/." "$TMP_DIR/"

log "Монтирование ${LV_PATH} в ${MOUNT_POINT}..."
mount "$LV_PATH" "$MOUNT_POINT"

log "Добавление записи о монтировании ${LV_PATH} в /etc/fstab..."
cat /etc/mtab | tail -n1 >> /etc/fstab

log "Восстановление содержимого из ${TMP_DIR} обратно в ${MOUNT_POINT}..."
cp -a "${TMP_DIR}/." "$MOUNT_POINT/"

log "Очистка временной директории ${TMP_DIR}..."
rm -rf "$TMP_DIR"

log "Автоматизация завершена успешно."

И можно просто запустить данный скрипт на всех машинах.

Далее я положу свой открытый ssh ключ на эти машины в /root/.ssh/authorized_keys.

На этом подготовительный этап для самих ВМ закончен.

Далее нам нужно скачать kubespray и подготовить его к установке kubernetes.

Bash
git clone https://github.com/kubernetes-sigs/kubespray.git
cd kubespray
VENVDIR=kubespray-venv
KUBESPRAYDIR=kubespray
python3 -m venv $VENVDIR
source $VENVDIR/bin/activate
pip install -U -r requirements.txt
pip install ruamel.yaml ### Без этого модуля не будет работать

Далее нужно создать директорию с inventory под наш будущий кластер.

Bash
cp -rfp inventory/sample inventory/kubernetes.dev.local

Можно воспользоваться билдером inventory файла, но я просто сразу же приложу готовый hosts.yml

YAML
all:
  hosts:
    master:
      ansible_host: 192.168.10.10
      ansible_user: root
      ansible_ssh_private_key_file: ~/.ssh/home_virt
      ip: 192.168.10.10
      access_ip: 192.168.10.10
    worker1:
      ansible_host: 192.168.10.11
      ansible_user: root
      ansible_ssh_private_key_file: ~/.ssh/home_virt
      ip: 192.168.10.11
      access_ip: 192.168.10.11
      node_labels:
        node-role.kubernetes.io/worker: ""
    worker2:
      ansible_host: 192.168.10.12
      ansible_user: root
      ansible_ssh_private_key_file: ~/.ssh/home_virt
      ip: 192.168.10.12
      access_ip: 192.168.10.12
      node_labels:
        node-role.kubernetes.io/worker: ""
    ingress1:
      ansible_host: 192.168.10.13
      ansible_user: root
      ansible_ssh_private_key_file: ~/.ssh/home_virt
      ip: 192.168.10.13
      access_ip: 192.168.10.13
      node_labels:
        node-role.kubernetes.io/ingress: ""
      node_taints:
        - "node-role.kubernetes.io/ingress=:NoSchedule"
  children:
    kube_control_plane:
      hosts:
        master:
    kube_node:
      hosts:
        worker1:
        worker2:
        ingress1:
    etcd:
      hosts:
        master:
    k8s_cluster:
      children:
        kube_control_plane:
        kube_node:
    calico_rr:
      hosts: {}

Я добавил пользователя и ssh ключ для авторизации на серверах, а также добавил кастомный лейбл на ingress ноду и taint для нее же, чтобы только определенные поды, это будет ingress controller и speaker metallb, могли быть размещены на этой ноде.

Далее нунжно отредактировать inventory/kubernetes.dev.local/group_vars/all/all.yml. Ниже я буду у всех измененных файлов приводить собственно только сами изменения.

YAML
upstream_dns_servers:
  - 8.8.8.8
  - 192.168.10.100
  
ntp_enabled: true
ntp_manage_config: true
ntp_servers:
  - "0.pool.ntp.org iburst"
  - "1.pool.ntp.org iburst"
  - "2.pool.ntp.org iburst"
  - "3.pool.ntp.org iburst"

192.168.10.100 — это мой локальный DNS сервер.

Далее редактируем inventory/kubernetes.dev.local/group_vars/k8s_cluster/addons.yml

YAML
helm_enabled: true
metrics_server_enabled: true
ingress_nginx_enabled: true
ingress_nginx_host_network: true
ingress_nginx_service_type: LoadBalancer
ingress_publish_status_address: ""
ingress_nginx_nodeselector:
  node-role.kubernetes.io/ingress: ""
ingress_nginx_tolerations:
  - key: "node-role.kubernetes.io/ingress"
    operator: "Exists"
    effect: "NoSchedule"
ingress_nginx_namespace: "ingress-nginx"
ingress_nginx_insecure_port: 80
ingress_nginx_secure_port: 443
ingress_nginx_class: nginx

metallb_enabled: true
metallb_speaker_enabled: "{{ metallb_enabled }}"
metallb_namespace: "metallb-system"
metallb_version: v0.13.9
metallb_protocol: "layer2"
metallb_port: "7472"
metallb_memberlist_port: "7946"
metallb_config:
  speaker:
    nodeselector:
      kubernetes.io/os: "linux"
    tolerations:
      - key: "node-role.kubernetes.io/ingress"
        operator: "Exists"
        effect: "NoSchedule"
  controller:
    nodeselector:
      kubernetes.io/os: "linux"
  address_pools:
    primary:
      ip_range:
        - 192.168.10.101/32
      auto_assign: true
  layer2:
    - primary

На этом с аддонами можно закончить. Я ставлю metric server, ingress controller на базе nginx и metallb. Последний выдаст адрес только для ingress svc и этого будет достаточно, поэтому 32 маска (один адрес).

Редактируем inventory/kubernetes.dev.local/group_vars/k8s_cluster/k8s-cluster.yml

YAML
kube_network_plugin: flannel
kube_proxy_mode: ipvs
kube_proxy_strict_arp: true
dns_mode: coredns
enable_nodelocaldns: true
enable_nodelocaldns_secondary: false
nodelocaldns_ip: 169.254.25.10
nodelocaldns_health_port: 9254
nodelocaldns_second_health_port: 9256
nodelocaldns_bind_metrics_host_ip: false
nodelocaldns_secondary_skew_seconds: 5

Так как я использую vagrant, то flannel по умолчанию возьмет NAT интерфейс для сети, это не подойдет и сеть в кластере толком не будет работать при таком варианте, поэтому изменим это в roles/network_plugin/flannel/defaults/main.yml

YAML
flannel_interface: eth1

На этом можно закончить с настройкой и приступить к установке.

YAML
ansible-playbook -i inventory/kubernetes.dev.local/hosts.yml --become --become-user=root cluster.yml

И я сразу поймал ошибку:

Это из-за того, что kubespray использует также и свои модули для ansible, а сам ansible я использую тот, что установлен на хосте, а не из requirements.txt, приложенного в репе. В документации советуют переопределить переменные модулей, давайте посмотрим куда смотрит ansible установленный через pip внутри venv.

Bash
pip show ansible

Нужно взять путь из Location:

И выполнить экспорт нужных переменных:

Bash
export ANSIBLE_LIBRARY=/home/andrei/kubespray/kubespray-venv/lib/python3.10/site-packages/ansible/modules
export ANSIBLE_MODULE_UTILS=/home/andrei/kubespray/kubespray-venv/lib/python3.10/site-packages/ansible/module_utils

После перезапуска, лично у меня ошибка сохранилась, поэтому я также переопределил PYTHONPATH:

Bash
export PYTHONPATH=/home/andrei/kubespray/kubespray-venv/lib/python3.10/site-packages:$PYTHONPATH

После чего запускаем заново:

Bash
ansible-playbook -i inventory/kubernetes.dev.local/hosts.yml --become --become-user=root cluster.yml

Установка должна пройти успешно. На master ноде можно забрать админский kubeconfig, он будет лежать в /etc/kubernetes/admin.conf. Для использования его не с мастер ноды, нужно поменять внутри файла адрес до api сервера с 127.0.0.1 на тот, который соответствует мастер ноде.

Установим kubernetes dashboard:

Bash
helm repo add kubernetes-dashboard https://kubernetes.github.io/dashboard/
helm --kubeconfig ~/local_k8s.conf upgrade --install kubernetes-dashboard kubernetes-dashboard/kubernetes-dashboard --create-namespace --namespace kubernetes-dashboard

Создадим ингресс:

Bash
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  namespace: kubernetes-dashboard
  name: kubernetes-dashboard-ingress
  annotations:
    kubernetes.io/ingress.class: "nginx"
spec:
  tls:
  - hosts:
    - k8s-dashboard.dev.local
  rules:
  - host: k8s-dashboard.dev.local
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service: 
            name: kubernetes-dashboard-web
            port: 
              number: 8000
Bash
kubectl apply -f dash.yaml

Ингресс работает.

Можно установить и другие компоненты, например longhorn и keycloak, о них есть статьи на сайте. Только в этой инсталяции у longhorn defaultDataPath лучше оставить по умолчанию, он как раз будет в нашей точке монтирования — /var/lib.

Проверить работу metric server:

Bash
kubectl top node
kubectl top pod -n kube-system

Насколько статья полезна?

Нажмите на звезду, чтобы оценить!

Средняя оценка 0 / 5. Количество оценок: 0

Оценок пока нет. Поставьте оценку первым.

Оставить комментарий