Настройка HA-кластера Kubernetes на «голом железе» с GlusterFS & MetalLB. Часть 2-3

в 20:16, , рубрики: devops, glusterfs, k8s, metallb, Блог компании Southbridge, Серверное администрирование, системное администрирование

Настройка HA-кластера Kubernetes на «голом железе» с GlusterFS & MetalLB. Часть 2-3 - 1

Часть 1/3 тут

Привет и с возвращением! Это вторая часть статьи о настройке кластера Kubernetes на «голом железе». Ранее мы настраивали НА-кластер Kubernetes с помощью внешнего etcd, схемы «ведущий-ведущий» и балансировки нагрузки. Ну а теперь пришло время настроить дополнительную среду и утилиты, чтобы сделать кластер полезнее и максимально приближенным к рабочему состоянию.

В этой части статьи мы сосредоточимся на настройке внутреннего балансировщика нагрузки сервисов кластера — это будет MetalLB. Также мы установим и настроим распределенное хранилище файлов между нашими рабочими нодами. Будем использовать GlusterFS для постоянных томов, которые доступны в Kubernetes.
После выполнения всех действий схема нашего кластера будет выглядеть следующим образом:

Настройка HA-кластера Kubernetes на «голом железе» с GlusterFS & MetalLB. Часть 2-3 - 2

1. Настройка MetalLB в качестве внутреннего балансировщика нагрузки.

Несколько слов о MetalLB, непосредственно со страницы документа:

MetalLB — это реализация балансировщика нагрузки для кластеров Kubernetes на «голом железе» со стандартными протоколами маршрутизации.

Kubernetes не предлагает реализацию сетевых балансировщиков нагрузки (служб типа LoadBalancer) для «голого железа». Все варианты реализации Network LB, с которыми поставляется Kubernetes, — это связующий код, он обращается к различным платформам IaaS (GCP, AWS, Azure и др.). Если вы не работаете на платформе, поддерживаемой IaaS (GCP, AWS, Azure и др.), LoadBalancer при создании останется в состоянии «ожидания» на неопределенный срок.

Операторы BM-серверов располагают двумя менее эффективными инструментами для ввода трафика пользователя в свои кластеры, службы «NodePort» и «externalIPs». Оба этих варианта имеют существенные недостатки в продакшене, что превращает BM-кластеры в граждан второго сорта в экосистеме Kubernetes.

MetalLB стремится исправить этот дисбаланса, предлагая реализацию Network LB, которая интегрируется со стандартным сетевым оборудованием, так что внешние службы на BM-кластерах также «просто работают» на максималках.

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

Ранее в примере мы выбрали для нужд нашего кластера подсеть 192.168.0.0/24. Теперь возьмем некоторую часть этой подсети для будущего балансировщика нагрузки.

Входим в систему машины с настроенной утилитой kubectl и запускаем:

control# kubectl apply -f https://raw.githubusercontent.com/google/metallb/v0.7.3/manifests/metallb.yaml

Это развернет MetalLB в кластере, в неймспейсе metallb-system. Убедитесь, что все компоненты MetalLB функционируют нормально:

control# kubectl get pod --namespace=metallb-system 
NAME                          READY   STATUS    RESTARTS   AGE 
controller-7cc9c87cfb-ctg7p   1/1     Running   0          5d3h 
speaker-82qb5                 1/1     Running   0          5d3h 
speaker-h5jw7                 1/1     Running   0          5d3h 
speaker-r2fcg                 1/1     Running   0          5d3h

Теперь настроим MetalLB с помощью configmap. В этом примере мы используем настройку Layer 2. За информацией о других вариантах настройки обращайтесь к документации MetalLB.

Создайте файл metallb-config.yaml в любой директории внутри выбранного IP-диапазона подсети нашего кластера:

control# vi metallb-config.yaml
apiVersion: v1 
kind: ConfigMap 
metadata: 
  namespace: metallb-system 
  name: config 
data: 
  config: | 
    address-pools: 
    - name: default 
      protocol: layer2 
      addresses: 
      - 92.168.0.240-192.168.0.250

И примените эту настройку:

control# kubectl apply -f metallb-config.yaml

При необходимости проверьте и измените configmap позднее:

control# kubectl describe configmaps -n metallb-system
control# kubectl edit configmap config -n metallb-system

Теперь у нас есть собственный настроенный локальный балансировщик нагрузки. Проверим, как он работает, на примере службы Nginx.

control# vi nginx-deployment.yaml
apiVersion: apps/v1 
kind: Deployment 
metadata: 
  name: nginx-deployment 
spec: 
  selector: 
    matchLabels: 
      app: nginx 
  replicas: 3
  template: 
    metadata: 
      labels: 
        app: nginx 
    spec: 
      containers: 
      - name: nginx 
        image: nginx:latest 
        ports: 
        - containerPort: 80

control# vi nginx-service.yaml
apiVersion: v1 
kind: Service 
metadata: 
  name: nginx 
spec: 
  type: LoadBalancer 
  selector: 
    app: nginx 
  ports: 
  - port: 80 
    name: http

Затем создайте тестовый деплой и службу Nginx:

control# kubectl apply -f nginx-deployment.yaml
control# kubectl apply -f nginx-service.yaml

А теперь — проверим результат:

control# kubectl get po 
NAME                               READY STATUS    RESTARTS   AGE 
nginx-deployment-6574bd76c-fxgxr    1/1  Running   0          19s 
nginx-deployment-6574bd76c-rp857    1/1  Running   0          19s 
nginx-deployment-6574bd76c-wgt9n    1/1  Running   0          19s
control# kubectl get svc
NAME   TYPE         CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE 
nginx  LoadBalancer 10.100.226.110 192.168.0.240 80:31604/TCP   107s

Создано 3 пода Nginx, как мы и указали в деплое ранее. Служба Nginx будет направлять трафик на все эти поды по схеме циклической балансировки. И вы также можете увидеть внешний IP, полученный от нашего балансировщика нагрузки MetalLB.

Теперь попробуйте свернуться на IP-адрес 192.168.0.240, и вы увидите страницу Nginx index.html. Не забудьте удалить тестовое развертывание и службу Nginx.

control# kubectl delete svc nginx 
service "nginx" deleted
control# kubectl delete deployment nginx-deployment 
deployment.extensions "nginx-deployment" deleted

Хорошо, на этом с MetalLB всё, идем дальше — настроим GlusterFS для томов Kubernetes.

2. Настройка GlusterFS с Heketi на рабочих нодах.

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

В Kubernetes тома доступны в различных вариантах, выбирайте подходящие. В этом примере я продемонстрирую, как создается хранилище GlusterFS для любых внутренних приложений, оно — как постоянные тома. Ранее я использовал для этого «системную» установку GlusterFS на всех рабочих нодах Kubernetes, а затем просто создавал тома типа hostPath в каталогах GlusterFS.

Теперь у нас есть новый удобный инструмент Heketi.

Несколько слов из документации Heketi:

Инфраструктура управления томами на основе RESTful для GlusterFS.

Heketi предлагает интерфейс управления RESTful, который можно использовать для управления жизненным циклом томов GlusterFS. Благодаря Heketi облачные сервисы, например OpenStack Manila, Kubernetes и OpenShift, могут динамически предоставлять тома GlusterFS с любым поддерживаемым типом надежности. Heketi автоматически определяет местоположение блоков в кластере, обеспечивая расположение блоков и их реплик в разных областях отказов. Heketi также поддерживает любое количество кластеров GlusterFS, позволяя облачным службам предлагать сетевое хранилище файлов, не ограничиваясь единственным кластером GlusterFS.

Звучит неплохо, и, кроме того, этот инструмент приблизит наш ВМ-кластер к большим облачным кластерам Kubernetes. В конце концов вы сможете создавать PersistentVolumeClaims, которые будут формироваться автоматически, и многое другое.

Можно взять дополнительные системные жесткие диски для настройки GlusterFS или просто создать несколько фиктивных блочных устройств. В этом примере я воспользуюсь вторым методом.

Создайте фиктивные блочные устройства на всех трех рабочих нодах:

worker1-3# dd if=/dev/zero of=/home/gluster/image bs=1M count=10000

Вы получите файл размером около 10 ГБ. Затем используйте losttup — чтобы добавить его в эти ноды, в качестве петлевого устройства:

worker1-3# losetup /dev/loop0 /home/gluster/image

Обратите внимание: если у вас уже есть некое петлевое устройство 0, то вам потребуется выбрать любой другой номер.

Я не пожалел времени и выяснил, почему Heketi не хочет работать должным образом. Поэтому, чтобы предотвратить любые проблемы в будущих конфигурациях, сначала убедимся, что мы загрузили модуль ядра dm_thin_pool и установили пакет glusterfs-client на всех рабочих нодах.

worker1-3# modprobe dm_thin_pool
worker1-3# apt-get update && apt-get -y install glusterfs-client

Хорошо, теперь нужно, чтобы на всех рабочих нодах присутствовали файл /home/gluster/image и устройство /dev/loop0. Не забудьте создать службу systemd, которая будет автоматически запускать losetup и modprobe при каждой загрузке этих серверов.

worker1-3# vi /etc/systemd/system/loop_gluster.service
[Unit]
Description=Create the loopback device for GlusterFS
DefaultDependencies=false
Before=local-fs.target
After=systemd-udev-settle.service
Requires=systemd-udev-settle.service
[Service]
Type=oneshot
ExecStart=/bin/bash -c "modprobe dm_thin_pool && [ -b /dev/loop0 ] || losetup /dev/loop0 /home/gluster/image"
[Install]
WantedBy=local-fs.target

И включите ее:

worker1-3# systemctl enable /etc/systemd/system/loop_gluster.service
Created symlink /etc/systemd/system/local-fs.target.wants/loop_gluster.service → /etc/systemd/system/loop_gluster.service.

Подготовительные работы закончены, и мы готовы деплоить GlusterFS и Heketi в наш кластер. Для этого я буду использовать вот это прикольное руководство. Большинство команд запускаем с внешнего управляющего компьютера, а очень маленькие команды запускаются с любой мастер-ноды внутри кластера.

Сначала скопируем репозиторий и создадим DaemonSet GlusterFS:

control# git clone https://github.com/heketi/heketi
control# cd heketi/extras/kubernetes
control# kubectl create -f glusterfs-daemonset.json

Теперь пометим наши три рабочие ноды для GlusterFS; после нанесения меток на них будут созданы поды GlusterFS:

control# kubectl label node worker1 storagenode=glusterfs
control# kubectl label node worker2 storagenode=glusterfs
control# kubectl label node worker3 storagenode=glusterfs
control# kubectl get pod
NAME                     READY   STATUS    RESTARTS   AGE
glusterfs-5dtdj          1/1     Running   0          1m6s
glusterfs-hzdll          1/1     Running   0          1m9s
glusterfs-p8r59          1/1     Running   0          2m1s

Теперь создадим учетную запись службы Heketi:

control# kubectl create -f heketi-service-account.json

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

control# kubectl create clusterrolebinding heketi-gluster-admin --clusterrole=edit --serviceaccount=default:heketi-service-account

Теперь давайте создадим секретный ключ Kubernetes, который блокирует конфигурацию нашего экземпляра Heketi:

control# kubectl create secret generic heketi-config-secret --from-file=./heketi.json

Создайте первый исходный под Heketi, который мы используем для первых операций по настройке и впоследствии удалим:

control# kubectl create -f heketi-bootstrap.json
service "deploy-heketi" created
deployment "deploy-heketi" created
control# kubectl get pod
NAME                            READY     STATUS    RESTARTS   AGE
deploy-heketi-1211581626-2jotm  1/1       Running   0          2m
glusterfs-5dtdj                 1/1       Running   0          6m6s
glusterfs-hzdll                 1/1       Running   0          6m9s
glusterfs-p8r59                 1/1       Running   0          7m1s

После создания и запуска службы Bootstrap Heketi нам потребуется перейти на один из наших мастер-нодов, там мы запустим несколько команд, поскольку наш внешний управляющий нод не находится внутри нашего кластера, поэтому мы не можем получить доступ к работающим подам и внутренней сети кластера.

Сначала давайте загрузим утилиту heketi-client и скопируем ее в системную папку bin:

master1# wget https://github.com/heketi/heketi/releases/download/v8.0.0/heketi-client-v8.0.0.linux.amd64.tar.gz
master1# tar -xzvf ./heketi-client-v8.0.0.linux.amd64.tar.gz
master1# cp ./heketi-client/bin/heketi-cli /usr/local/bin/
master1# heketi-cli 
heketi-cli v8.0.0

Теперь найдем IP-адрес пода heketi и экспортируем его как системную переменную:

master1# kubectl --kubeconfig /etc/kubernetes/admin.conf describe pod deploy-heketi-1211581626-2jotm 
For me this pod have a 10.42.0.1 ip
master1# curl http://10.42.0.1:57598/hello
Handling connection for 57598
Hello from Heketi
master1# export HEKETI_CLI_SERVER=http://10.42.0.1:57598

Теперь давайте предоставим Heketi информацию о кластере GlusterFS, которым он должен управлять. Мы предоставляем ее через файл топологии. Топология — это манифест JSON со списком всех нод, дисков и кластеров, используемых GlusterFS.

ПРИМЕЧАНИЕ. Убедитесь, что hostnames/manage указывает на точное название, как в разделе kubectl get node, и что hostnames/storage — это IP-адрес нодов хранилища.

master1:~/heketi-client# vi topology.json
{
    "clusters": [
        {
            "nodes": [
                {
                    "node": {
                        "hostnames": {
                            "manage": [
                                "worker1"
                            ],
                            "storage": [
                                "192.168.0.7"
                            ]
                        },
                        "zone": 1
                    },
                    "devices": [
                        "/dev/loop0"
                    ]
                },
                {
                    "node": {
                        "hostnames": {
                            "manage": [
                                "worker2"
                            ],
                            "storage": [
                                "192.168.0.8"
                            ]
                        },
                        "zone": 1
                    },
                    "devices": [
                        "/dev/loop0"
                    ]
                },
                {
                    "node": {
                        "hostnames": {
                            "manage": [
                                "worker3"
                            ],
                            "storage": [
                                "192.168.0.9"
                            ]
                        },
                        "zone": 1
                    },
                    "devices": [
                        "/dev/loop0"
                    ]
                }
            ]
        }
    ]
}

Затем загрузите этот файл:

master1:~/heketi-client# heketi-cli topology load --json=topology.json
Creating cluster ... ID: e83467d0074414e3f59d3350a93901ef
        Allowing file volumes on cluster.
        Allowing block volumes on cluster.
        Creating node worker1 ... ID: eea131d392b579a688a1c7e5a85e139c
                Adding device /dev/loop0 ... OK
        Creating node worker2 ... ID: 300ad5ff2e9476c3ba4ff69260afb234
                Adding device /dev/loop0 ... OK
        Creating node worker3 ... ID: 94ca798385c1099c531c8ba3fcc9f061
                Adding device /dev/loop0 ... OK

Далее мы используем Heketi, чтобы предоставить тома для хранения базы данных. Название команды немного странное, но все в порядке. Также создайте хранилище heketi:

master1:~/heketi-client# heketi-cli setup-openshift-heketi-storage
master1:~/heketi-client#  kubectl --kubeconfig /etc/kubernetes/admin.conf create -f heketi-storage.json 
secret/heketi-storage-secret created
endpoints/heketi-storage-endpoints created
service/heketi-storage-endpoints created
job.batch/heketi-storage-copy-job created

Это все команды, которые нужно запустить с мастер-ноды. Вернемся к управляющей ноде и продолжим оттуда; в первую очередь убедитесь, что последняя запущенная команда успешно выполнена:

control# kubectl get pod
NAME                          READY   STATUS      RESTARTS   AGE
glusterfs-5dtdj               1/1     Running     0          39h
glusterfs-hzdll               1/1     Running     0          39h
glusterfs-p8r59               1/1     Running     0          39h
heketi-storage-copy-job-txkql 0/1     Completed   0          69s

И задание heketi-storage-copy-job выполнено.

Если на данный момент на ваших рабочих нодах отсутствует установленный пакет glusterfs-client, то имеет место ошибка.

Пришло время удалить установочный файл Bootstrap Heketi и произвести небольшую очистку:

control# kubectl delete all,service,jobs,deployment,secret --selector="deploy-heketi"

На последнем этапе нам нужно создать долгосрочный экземпляр Heketi:

control# cd ./heketi/extras/kubernetes
control:~/heketi/extras/kubernetes# kubectl create -f heketi-deployment.json
secret/heketi-db-backup created
service/heketi created
deployment.extensions/heketi created
control# kubectl get pod
NAME                          READY   STATUS    RESTARTS   AGE
glusterfs-5dtdj               1/1     Running   0          39h
glusterfs-hzdll               1/1     Running   0          39h
glusterfs-p8r59               1/1     Running   0          39h
heketi-b8c5f6554-knp7t        1/1     Running   0          22m

Если на данный момент на ваших рабочих нодах отсутствует установленный пакет glusterfs-client, то имеет место ошибка. А мы почти закончили, теперь база данных Heketi сохраняется в томе GlusterFS и не сбрасывается при каждом перезапуске пода Heketi.

Чтобы начать использовать кластер GlusterFS с динамическим выделением ресурсов, нам нужно создать StorageClass.

Сначала давайте найдем конечную точку хранилища Gluster, которая будет передана в StorageClass в качестве параметра (heketi-storage-endpoints):

control# kubectl get endpoints
NAME                 ENDPOINTS                            AGE
heketi               10.42.0.2:8080                       2d16h
....... ... .. 

Теперь создайте несколько файлов:

control# vi storage-class.yml
apiVersion: storage.k8s.io/v1beta1
kind: StorageClass
metadata:
  name: slow
provisioner: kubernetes.io/glusterfs
parameters:
  resturl: "http://10.42.0.2:8080"
control# vi test-pvc.yml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: gluster1
  annotations:
    volume.beta.kubernetes.io/storage-class: "slow"
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi

Используйте эти файлы для создания class и pvc:

control# kubectl create -f storage-class.yaml
storageclass "slow" created
control# kubectl get storageclass
NAME   PROVISIONER               AGE
slow   kubernetes.io/glusterfs   2d8h
control# kubectl create -f test-pvc.yaml
persistentvolumeclaim "gluster1" created
control# kubectl get pvc
NAME   STATUS   VOLUME  CAPACITY   ACCESS MODES STORAGECLASS  AGE gluster1 Bound  pvc-27f733cd-1c77-11e9-bb07-7efe6b0e6fa5   1Gi        RWO            slow           2d8h

Мы также можем просмотреть том PV:

control# kubectl get pv
NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM              STORAGECLASS   REASON   AGE
pvc-27f733cd-1c77-11e9-bb07-7efe6b0e6fa5   1Gi        RWO            Delete           Bound    default/gluster1   slow                    2d8h

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

Создайте простой под Nginx и протестируйте его:

control# vi nginx-test.yml

apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod1
  labels:
    name: nginx-pod1
spec:
  containers:
  - name: nginx-pod1
    image: gcr.io/google_containers/nginx-slim:0.8
    ports:
    - name: web
      containerPort: 80
    volumeMounts:
    - name: gluster-vol1
      mountPath: /usr/share/nginx/html
  volumes:
  - name: gluster-vol1
    persistentVolumeClaim:
      claimName: gluster1
control# kubectl create -f nginx-test.yaml
pod "nginx-pod1" created

Просмотрите под (подождите несколько минут, возможно, потребуется загрузить образ, если он еще не существует):

control# kubectl get pods 
NAME                     READY   STATUS    RESTARTS   AGE
glusterfs-5dtdj          1/1     Running   0          4d10h
glusterfs-hzdll          1/1     Running   0          4d10h
glusterfs-p8r59          1/1     Running   0          4d10h
heketi-b8c5f6554-knp7t   1/1     Running   0          2d18h
nginx-pod1               1/1     Running   0          47h

Теперь войдите в контейнер и создайте файл index.html:

control# kubectl exec -ti nginx-pod1 /bin/sh
# cd /usr/share/nginx/html
# echo 'Hello there from GlusterFS pod !!!' > index.html
# ls
index.html
# exit

Потребуется найти внутренний IP-адрес пода и свернуться в него из любой мастер-ноды:

master1# curl 10.40.0.1
Hello there from GlusterFS pod !!!

При этом мы просто тестируем наш новый постоянный том.

Несколько полезных команд для проверки нового кластера GlusterFS: heketi-cli cluster list и heketi-cli volume list. Их можно запустить на своем компьютере при наличии установленного heketi-cli. В данном примере это нода master1.

master1# heketi-cli cluster list
Clusters:
Id:e83467d0074414e3f59d3350a93901ef [file][block]
master1# heketi-cli volume list
Id:6fdb7fef361c82154a94736c8f9aa53e    Cluster:e83467d0074414e3f59d3350a93901ef    Name:vol_6fdb7fef361c82154a94736c8f9aa53e
Id:c6b69bd991b960f314f679afa4ad9644    Cluster:e83467d0074414e3f59d3350a93901ef    Name:heketidbstorage

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

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

Оставайтесь на связи, и всего хорошего!

Автор: nAbdullin

Источник


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js