28 August 2019

Разворачиваем среду для работы с микросервисами. Часть 1 установка Kubernetes HA на bare metal (Debian)

System administrationDevOpsKubernetes
Tutorial


Здравствуйте уважаемые читатели Хабра!


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


Данный цикл будет состоять минимум из четырех статей:


  1. В первой из них я расскажу, как на голое железо установить отказоустойчивый кластер kubernetes, как установить стандартный дашборд и настроить доступ к нему, как установить ingress контроллер.
  2. Во второй статье я расскажу, как развернуть отказоустойчивый кластер Ceph и как начать использовать RBD тома в нашем кластере Kubernetes. Также немного затрону остальные виды стораджей (storages) и более подробно рассмотрю local-storage. Дополнительно расскажу, как на базе созданного кластера CEPH организовать отказоустойчивое хранилище S3
  3. В третьей статье я расскажу, как в нашем кластере Kubernetes развернуть отказоустойчивый кластер MySql, а именно — Percona XtraDB Cluster on Kubernetes. И также опишу все проблемы с которыми мы столкнулись, когда решили перенести БД в kubernetes.
  4. В четвертой статье я постараюсь собрать все вместе и рассказать, как задеплоить и запустить приложение, которое будет использовать БД и тома ceph. Расскажу, как настроить ingress контроллер для доступа к нашему приложению извне и сервис автоматического заказа сертификатов от Let's Encrypt. Еще — как автоматически поддерживать данные сертификаты в актуальном состоянии. Также немного затронем тему RBAC в контексте доступа до панели управления. Расскажу в двух словах про Helm и его установку.
    Если Вам интересна информация данных публикаций, то — добро пожаловать !

Вступление:


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


Что я не буду подробно рассматривать в данной серии публикаций:


  • подробно объяснять, что такое примитивы kubernetes, такие как: pod, deployment, service, ingress и тд.
  • CNI (Container Networking Interface) я рассмотрю очень поверхностно, мы используем callico поэтому другие решения, я только перечислю.
  • процесс сборки docker образов.
  • процессы CI\CD. (Возможно отдельной публикацией, но после всего цикла)
  • helm; про него написано уже достаточно много, я затрону лишь процесс его инсталляции в кластер и настройку клиента.

Что я хочу рассмотреть подробно:


  • пошаговый процесс развертывания кластера Kubernetes. Я буду использовать kubeadm. Но при этом я подробно шаг за шагом рассмотрю процесс инсталляции кластера на голое железо, различные виды инсталляции ETCD, составление конфигфайлов для kube admina. Постараюсь разъяснить все варианты балансировки для Ingress контроллера и отличие в разнообразных схемах доступа worknodes до api сервера.
    Я знаю, что сегодня для развертывания kubernetes есть много прекрасных инструментов, например, kubespray или тот же rancher. Возможно, кому-то будет более удобно использовать их. Но, я думаю, найдется немало инженеров которые захотят рассмотреть вопрос более детально.
  • терминологию CEPH и пошаговую инсталляцию кластера CEPH, а также пошаговую инструкцию подключение хранилища ceph к созданному Kubernetes кластеру.
  • local-storages, подключение к кластеру kubernetes, а также отличия от подключений типа hostpath и тд.
  • kubernetes операторы и развертывание Percona XtraDB Cluster с помощью оператора, а также постараюсь рассказать про плюсы и минусы такого решения после полугодового опыта использования в продакшене. А также немного поделюсь планами по доработке оператора от percona.

Оглавление:


  1. Cписок хостов, ресурсы хостов, версии ОС и ПО
  2. Схема HA кластера kubernetes
  3. До начала работы или before you begin
  4. Заполняем файл create-config.sh
  5. Обновление ядра ОС
  6. Подготовка нод Установка Kubelet, Kubectl, Kubeadm и docker
  7. Установка ETCD (различные варианты)
  8. Запуск первого мастера kubernetes
  9. Установка CNI Callico
  10. Запуск второго и третьего мастера kubernetes
  11. Добавляем worker нод в кластер
  12. Устанавливаем haproxy на worker ноды для HA
  13. Установка Ingress контроллера
  14. Установка Web UI (Dashboard)


Список и назначение хостов


Все ноды моего кластера будут располагаться на виртуальных машинах с предустановленной системой Debian 9 stretch c ядром 4.19.0-0.bpo.5-amd64. Для виртуализации я использую Proxmox VE.


Таблица ВМ и их ТТХ:


Name IP адрес Comment CPU MEM DISK1 DISK2
master01 10.73.71.25 master node 4vcpu 4Gb HDD ---
master02 10.73.71.26 master node 4vcpu 4Gb HDD ---
master03 10.73.71.27 master node 4vcpu 4Gb HDD ---
worknode01 10.73.75.241 work node 4vcpu 4Gb HDD SSD
worknode02 10.73.75.242 work node 4vcpu 4Gb HDD SSD
worknode03 10.73.75.243 work node 4vcpu 4Gb HDD SSD

Не обязательно, что у Вас должна быть именно такая конфигурация машин, но все таки я советую придерживаться рекомендациям официальной документации, а для мастеров увеличить количество оперативной памяти минимум до 4Гб. Забегая вперед скажу, что при более маленьком количестве, я ловил глюки в работе CNI Callico
Ceph тоже достаточно прожорлив к памяти и производительности дисков.
Наши продакшн инсталляции работают без виртуализации на голом железе, но я знаю много примеров, где хватало и виртуальных машин с достаточно скромными ресурсами. Все зависит от ваших потребностей и нагрузок.


Список и версии ПО


Name Version
Kubernetes 1.15.1
Docker 19.3.1

Kubeadm, начиная с версии 1.14, перестал поддерживать API версии v1alpha3 и полностью перешел на API версии v1beta1, которую будет поддерживать в ближайшем будущем, поэтому в статье я буду рассказывать только про v1beta1.
Итак, мы считаем, что у Вы подготовили машины для kubernetes кластера. Они все доступны друг другу по сети, имеют "выход" в интернет и на них установлена "чистая" операционная система.
Для каждого шага установки я буду уточнять, на каких именно машинах выполняется команда или блок команд. Все команды выполнять из-под пользователя root, если не указано иного.
Все файлы конфигурации, а также скрипт для их подготовки доступны для скачивания в моем github
Итак, начнем.



Схема HA кластера kubernetes



Примерная схема HA кластера. Художник из меня так себе, если честно, но постараюсь объяснить в двух словах и достаточно упрощенно, особо не углубляясь в теорию.
Итак, наш кластер будет состоять из трех master нод и трех worker нод. На каждой master ноде kubernetes у нас будет работать etcd (на схеме зеленые стрелочки) и служебные части kubernetes; назовем их обобщенно — kubeapi.
Через кластер etcd master ноды обмениваются состоянием кластера kubernetes. Эти же адреса я укажу как точки входа ingress контроллера для внешнего трафика (на схеме красные стрелочки)
На worker нодах у нас работает kubelet, который связывается с api сервером kubernetes через haproxy установленным локально на каждой worker ноде. В качестве адреса api сервера для kubelet я буду использовать локалхост 127.0.0.1:6443, а haproxy по roundrobin будет раскидывать запросы по трем master нодам, также он будет проверять доступность master нод. Данная схема позволит нам создать HA, и в случае выхода из строя одной из master нод, worker ноды будут спокойно посылать запросы на две оставшихся в живых master ноды.



Перед началом работы


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


apt-get update && apt-get install -y curl apt-transport-https git

На master ноды скопируем репозиторий с шаблонами конфигов


sudo -i
git clone https://github.com/rjeka/kubernetes-ceph-percona.git

Проверим, что ip адрес хостов на мастерах соответствует тому, на котором будет слушать API сервер kubernetes


hostname && hostname -i
master01
10.73.71.25

и так для всех master нод.


Обязательно отключите SWAP, иначе kubeadm будет выдавать ошибку


[ERROR Swap]: running with swap on is not supported. Please disable swap

Отключить можно командой


swapoff -a

Не забудьте закомментировать в файле /etc/fstab



Заполняем файл create-config.sh


Для автоматического заполнения конфигов, нужных для установки кластера kubernetes, я накидал небольшой скриптик create-config.sh. Вам нужно заполнить в нем буквально 8 строчек. Указать IP адреса и hostname своих мастеров. А также указать etcd tocken, можете его не менять. Я приведу ниже ту часть скрипта в которой нужно сделать изменения.


#!/bin/bash

#######################################
# all masters settings below must be same
#######################################

# master01 ip address
export K8SHA_IP1=10.73.71.25 

# master02 ip address
export K8SHA_IP2=10.73.71.26

# master03 ip address
export K8SHA_IP3=10.73.71.27

# master01 hostname
export K8SHA_HOSTNAME1=master01

# master02 hostname
export K8SHA_HOSTNAME2=master02

# master03 hostname
export K8SHA_HOSTNAME3=master03

#etcd tocken:
export ETCD_TOKEN=9489bf67bdfe1b3ae077d6fd9e7efefd

#etcd version
export ETCD_VERSION="v3.3.10"


Обновление ядра ОС


Данный шаг является необязательным, так как ядро нужно будем обновлять из back портов, и делаете Вы это на свой страх и риск. Возможно, Вы никогда столкнетесь с данной проблемой, а если и столкнетесь, то обновить ядро можно и после разворачивания kubernetes. В общем, решать Вам.
Обновление ядра требуется для устранения старого бага docker, который исправили только в ядре linux версии 4.18. Более подробно про этот баг можно почитать вот здесь. Выражался баг в периодическом зависании сетевого интерфейса на нодах kubernetes c ошибкой:


waiting for eth0 to become free. Usage count = 1

У меня после установки ОС было ядро версии 4.9


uname -a
Linux master01 4.9.0-7-amd64 #1 SMP Debian 4.9.110-3+deb9u2 (2018-08-13) x86_64 GNU/Linux

На каждой машине для kubernetes выполняем
Шаг №1
Добавляем back ports в source list


echo deb http://ftp.debian.org/debian stretch-backports main > /etc/apt/sources.list
apt-get update
apt-cache policy linux-compiler-gcc-6-x86

Шаг №2
Установка пакетов


apt install -y -t stretch-backports linux-image-amd64 linux-headers-amd64

Шаг №3
Перезагрузка


reboot

Проверяем что все ОК


uname -a
Linux master01 4.19.0-0.bpo.5-amd64 #1 SMP Debian 4.19.37-4~bpo9+1 (2019-06-19) x86_64 GNU/Linux


Подготовка нод Установка Kubelet, Kubectl, Kubeadm и docker


Установка Kubelet, Kubectl, Kubeadm


Ставим на все ноды кластера, согласно документации kubernetes


apt-get update && apt-get install -y apt-transport-https curl
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
cat <<EOF >/etc/apt/sources.list.d/kubernetes.list
deb https://apt.kubernetes.io/ kubernetes-xenial main
EOF
apt-get update
apt-get install -y kubelet kubeadm kubectl
apt-mark hold kubelet kubeadm kubectl

Установка Docker


Устанавливаем docker по инструкции из документации


apt-get remove docker docker-engine docker.io containerd runc
apt-get install apt-transport-https ca-certificates curl gnupg2 software-properties-common

curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add -
apt-key fingerprint 0EBFCD88

add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/debian \
   $(lsb_release -cs) \
   stable"

apt-get update
apt-get install docker-ce docker-ce-cli containerd.io

Установка Установка Kubelet, Kubectl, Kubeadm и docker c помощью ansible
git clone https://github.com/rjeka/kubernetes-ceph-percona.git
cd kubernetes-ceph-percona/playbooks
vim masters.ini 

В группе masters прописываем ip мастеров.
В группе workers прописываем ip рабочих нод.


#Если sudo c паролем
ansible-playbook -i hosts.ini kubelet.yaml -K
ansible-playbook -i hosts.ini docker.yaml -K
#Если sudo безпарольный 
ansible-playbook -i hosts.ini kubelet.yaml
ansible-playbook -i hosts.ini docker.yaml

Если Вы по каким либо причинам не хотите использовать docker, есть возможность использовать любые CRI. Почитать об этом можно, например тут, но эта тема выходит за рамки данной статьи.



Установка ETCD


Не буду сильно углубляться в теорию, в двух словах: etcd — это распределенное хранилище типа «ключ-значение» c открытым исходным кодом. etcd написан на GO и используется в kubernetes фактически, как база данных для хранения состояния кластера. Для более подробного ознакомления можно почитать в документации kubernetes.
etcd можно устанавливать многими способами. Можно устанавливать локально и запускать, как демона, можно запускать в docker контейнерах, можно устанавливать даже как поды кубернетеса. Можно устанавливать руками, а можно установить с помощью kubeadm (я данный способ не пробовал). Можно устанавливать на машины кластера или отдельные сервера.
Я буду устанавливать etcd локально на master ноды и запускать, как демон, через systemd, а также рассмотрю установку в докере. Я использую etcd без TLS, если вам нужен TLS обратитесь к документации самого etcd или kubernetes
Также в моем github будет выложен ansible-playbook для установки etcd с запуском через systemd.


Вариант №1
Установка локально, запуск через systemd


На всех мастерах: (на рабочих нодах кластера этот шаг выполнять не нужно)
Шаг №1
Скачиваем и распаковываем архив с etcd:


mkdir archives
cd archives
export etcdVersion=v3.3.10
wget https://github.com/coreos/etcd/releases/download/$etcdVersion/etcd-$etcdVersion-linux-amd64.tar.gz
tar -xvf etcd-$etcdVersion-linux-amd64.tar.gz -C /usr/local/bin/ --strip-components=1

Шаг №2
Создаем конфиг файл для ETCD


cd ..
./create-config.sh etcd

Скрипт принимает на вход значение etcd, и формирует конфиг файл в каталоге etcd. После работы скрипта готовый конфиг файл будет находится в каталоге etcd.
Для всех других конфигов, скрипт работает по тому же принципу. Принимает на вход какое-то значение и создает конфиг в определенном каталоге.


Шаг №3
Запускаем etcd кластер и проверяем его работоспособность


systemctl start etcd

Проверяем работоспособность демона


systemctl status etcd
● etcd.service - etcd
   Loaded: loaded (/etc/systemd/system/etcd.service; disabled; vendor preset: enabled)
   Active: active (running) since Sun 2019-07-07 02:34:28 MSK; 4min 46s ago
     Docs: https://github.com/coreos/etcd
 Main PID: 7471 (etcd)
    Tasks: 14 (limit: 4915)
   CGroup: /system.slice/etcd.service
           └─7471 /usr/local/bin/etcd --name master01 --data-dir /var/lib/etcd --listen-client-urls http://0.0.0.0:2379,http://0.0.0.0:4001 --advertise-client-urls http://10.73.71.25:2379,http://10.73.71.

Jul 07 02:34:28 master01 etcd[7471]: b11e73358a31b109 [logterm: 1, index: 3, vote: 0] cast MsgVote for f67dd9aaa8a44ab9 [logterm: 2, index: 5] at term 554
Jul 07 02:34:28 master01 etcd[7471]: raft.node: b11e73358a31b109 elected leader f67dd9aaa8a44ab9 at term 554
Jul 07 02:34:28 master01 etcd[7471]: published {Name:master01 ClientURLs:[http://10.73.71.25:2379 http://10.73.71.25:4001]} to cluster d0979b2e7159c1e6
Jul 07 02:34:28 master01 etcd[7471]: ready to serve client requests
Jul 07 02:34:28 master01 etcd[7471]: serving insecure client requests on [::]:4001, this is strongly discouraged!
Jul 07 02:34:28 master01 systemd[1]: Started etcd.
Jul 07 02:34:28 master01 etcd[7471]: ready to serve client requests
Jul 07 02:34:28 master01 etcd[7471]: serving insecure client requests on [::]:2379, this is strongly discouraged!
Jul 07 02:34:28 master01 etcd[7471]: set the initial cluster version to 3.3
Jul 07 02:34:28 master01 etcd[7471]: enabled capabilities for version 3.3
lines 1-19

И работоспособность самого кластера:


etcdctl cluster-health
member 61db137992290fc is healthy: got healthy result from http://10.73.71.27:2379
member b11e73358a31b109 is healthy: got healthy result from http://10.73.71.25:2379
member f67dd9aaa8a44ab9 is healthy: got healthy result from http://10.73.71.26:2379
cluster is healthy

etcdctl member list
61db137992290fc: name=master03 peerURLs=http://10.73.71.27:2380 clientURLs=http://10.73.71.27:2379,http://10.73.71.27:4001 isLeader=false
b11e73358a31b109: name=master01 peerURLs=http://10.73.71.25:2380 clientURLs=http://10.73.71.25:2379,http://10.73.71.25:4001 isLeader=false
f67dd9aaa8a44ab9: name=master02 peerURLs=http://10.73.71.26:2380 clientURLs=http://10.73.71.26:2379,http://10.73.71.26:4001 isLeader=true

Установка etcd локально c помощью ansible, запуск через systemd

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


git clone https://github.com/rjeka/kubernetes-ceph-percona.git
cd kubernetes-ceph-percona/playbooks
vim masters.ini 

В группе masters прописываем ip мастеров.
etcd_version — версия etcd. Посмотреть можно на страничке etcd в github. На момент написания статьи была версия v3.3.13 я использую v3.3.10.
etcdToken — можете оставить такой же, или сгенерировать свой.
Запускаем плейбук командой


#Если sudo c паролем
ansible-playbook -i hosts.ini -l masters etcd.yaml -K
BECOME password: <sudo пароль>
#Если sudo безпарольный
ansible-playbook -i hosts.ini -l masters etcd.yaml

Если вы хотите запустить etcd в докере, то под спойлером инструкция.


Установка etcd c помощью docker-compose, запуск в докер

Данные команды нужно выполнить на всех мастер нодах кластера.
С github клонируем репозиторий с кодом


git clone https://github.com/rjeka/kubernetes-ceph-percona.git
cd kubernetes-ceph-percona

etcd_version — версия etcd. Посмотреть можно на страничке etcd в github. На момент написания статьи была версия v3.3.13 я использую v3.3.10.
etcdToken — можете оставить такой же, или сгенерировать свой.


Ставим docker-compose


apt-get install -y docker-compose

Генерируем конфиг


./create-config.sh docker

Запускаем установку кластера etcd в докере


docker-compose --file etcd-docker/docker-compose.yaml up -d

Проверяем что контейнеры поднялись


docker ps

И статус кластера etcd


root@master01:~/kubernetes-ceph-percona# docker exec -ti etcd etcdctl cluster-health
member 61db137992290fc is healthy: got healthy result from http://10.73.71.27:2379
member b11e73358a31b109 is healthy: got healthy result from http://10.73.71.25:2379
member f67dd9aaa8a44ab9 is healthy: got healthy result from http://10.73.71.26:2379

cluster is healthy
root@master01:~/kubernetes-ceph-percona# docker exec -ti etcd etcdctl member list
61db137992290fc: name=etcd3 peerURLs=http://10.73.71.27:2380 clientURLs=http://10.73.71.27:2379,http://10.73.71.27:4001 isLeader=false
b11e73358a31b109: name=etcd1 peerURLs=http://10.73.71.25:2380 clientURLs=http://10.73.71.25:2379,http://10.73.71.25:4001 isLeader=true
f67dd9aaa8a44ab9: name=etcd2 peerURLs=http://10.73.71.26:2380 clientURLs=http://10.73.71.26:2379,http://10.73.71.26:4001 isLeader=false

Если что то пошло не так то


docker logs etcd


Запуск первого мастера kubernetes


Первым делом нам нужно сгенерировать конфиг для kubeadmin


./create-config.sh kubeadm

Разбираем конфиг для kubeadm
apiVersion: kubeadm.k8s.io/v1beta1
kind: InitConfiguration
localAPIEndpoint:
  advertiseAddress: 10.73.71.25 #Адрес на котором слушает API-сервер
---
apiVersion: kubeadm.k8s.io/v1beta1
kind: ClusterConfiguration
kubernetesVersion: stable #Версия кластера которую мы будем устанавливать
apiServer: #Список хостов для которых kubeadm генерирует сертификаты
  certSANs:
  - 127.0.0.1
  - 10.73.71.25
  - 10.73.71.26
  - 10.73.71.27
controlPlaneEndpoint: 10.73.71.25 #адрес мастера или балансировщика нагрузки
etcd: #адреса кластера etc
  external:
    endpoints:
    - http://10.73.71.25:2379 
    - http://10.73.71.26:2379
    - http://10.73.71.27:2379
networking:
  podSubnet: 192.168.0.0/16 # подсеть для подов, у каждого CNI она своя.

Почитать про подсети CNI можно в документации kubernetes
Это минимально рабочий конфиг. Для кластера с тремя мастерами Вы можете изменять его под конфигурацию своего кластера. Например, если Вы хотите использовать 2 мастера, то просто укажите в certSANs два адреса.
Все параметра конфига можно найти в описании API kubeadm.


Инициируем первый мастер


kubeadm init  --config=kubeadmin/kubeadm-init.yaml

Если kubeadm отработает без ошибок, то на выходе мы получим примерно следующий вывод:


You can now join any number of control-plane nodes by copying certificate authorities
and service account keys on each node and then running the following as root:

  kubeadm join 10.73.71.25:6443 --token ivwoap.259retezqf34amx8 \
    --discovery-token-ca-cert-hash sha256:b5c93e32457c8e6478782ff62e8ef77acf72738dda59cd603cdf4821abe12ca3 \
    --control-plane

Then you can join any number of worker nodes by running the following on each as root:

kubeadm join 10.73.71.25:6443 --token ivwoap.259retezqf34amx8 \
    --discovery-token-ca-cert-hash sha256:b5c93e32457c8e6478782ff62e8ef77acf72738dda59cd603cdf4821abe12ca3


Установка CNI Calico


Пришло время установить сеть, в которой будут работать наши поды. Я использую calico, ее и будем ставить.
А для начала настроим доступ для kubelet. Все команды выполняем на master01
Если вы работаете из-под root


export KUBECONFIG=/etc/kubernetes/admin.conf

Если из-под простого пользователя


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

Также Вы можете управлять кластером со своего ноутбука или любой локальной машины. Для этого скопируйте файл /etc/kubernetes/admin.conf на свой ноутбук или любую другую машину в $HOME/.kube/config


Ставим CNI согласно документации kubernetes


kubectl apply -f https://docs.projectcalico.org/v3.8/manifests/calico.yaml

Ждем, пока все поды поднимутся


watch -n1 kubectl get pods -A
NAMESPACE     NAME                                       READY   STATUS    RESTARTS   AGE
kube-system   calico-kube-controllers-59f54d6bbc-psr2z   1/1     Running   0          96s
kube-system   calico-node-hm49z                          1/1     Running   0          96s
kube-system   coredns-5c98db65d4-svcx9                   1/1     Running   0          77m
kube-system   coredns-5c98db65d4-zdlb8                   1/1     Running   0          77m
kube-system   kube-apiserver-master01                    1/1     Running   0          76m
kube-system   kube-controller-manager-master01           1/1     Running   0          77m
kube-system   kube-proxy-nkdqn                           1/1     Running   0          77m
kube-system   kube-scheduler-master01                    1/1     Running   0          77m


Запуск второго и третьего мастера kubernetes


Перед тем, как запустить master02 и master03, нужно с master01 скопировать сертификаты, которые сгенерировал kubeadm при создании кластера. Я буду копировать через scp
На master01


export master02=10.73.71.26
export master03=10.73.71.27
scp -r /etc/kubernetes/pki $master02:/etc/kubernetes/ 
scp -r /etc/kubernetes/pki $master03:/etc/kubernetes/

На master02 и master03
Создаем конфиг для kubeadm


./create-config.sh kubeadm

И добавляем в кластер master02 и master03


kubeadm init  --config=kubeadmin/kubeadm-init.yaml

Глюки при нескольких сетевых интерфейсах !!!!

В продакшене я использую kubernetes v1.13.5 и calico v3.3. И у меня таких глюков не было.
Но при подготовке статьи и использовании версии stable (на момент написания статьи это была v1.15.1 kubernetes и версия 3.8 callico) я столкнулся с проблемой, которая выражалась в ошибки старта CNI


root@master01:~/kubernetes-ceph-percona# kubectl get pods  -A -w
NAMESPACE     NAME                                       READY   STATUS              RESTARTS   AGE
kube-system   calico-kube-controllers-658558ddf8-t6gfs   0/1     ContainerCreating   0          11s
kube-system   calico-node-7td8g                          1/1     Running             0          11s
kube-system   calico-node-dthg5                          0/1     CrashLoopBackOff    1          11s
kube-system   calico-node-tvhkq                          0/1     CrashLoopBackOff    1          11s

Это глюк calico daemon set, в том случае, когда у сервера несколько сетевых интерфейсов
На githab есть issue по данному глюку https://github.com/projectcalico/calico/issues/2720
Решается редактированием daemon set calico-node и добавлением в env параметра IP_AUTODETECTION_METHOD


kubectl edit -n kube-system ds calico-node

добавляем параметр IP_AUTODETECTION_METHOD с именем Вашего интерфейса, на котором работает мастер; в моем случае это ens19


- name: IP_AUTODETECTION_METHOD
  value: ens19


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


# kubectl get nodes
NAME       STATUS   ROLES    AGE   VERSION
master01   Ready    master   28m   v1.15.1
master02   Ready    master   26m   v1.15.1
master03   Ready    master   18m   v1.15.1

И что жива calica


# kubectl get pods -A -o wide | grep calico
kube-system   calico-kube-controllers-59f54d6bbc-5lxgn   1/1     Running   0          27m
kube-system   calico-node-fngpz                          1/1     Running   1          24m
kube-system   calico-node-gk7rh                          1/1     Running   0          8m55s
kube-system   calico-node-w4xtt                          1/1     Running   0          25m


Добавляем worker ноды в кластер


На данный момент у нас работает кластер, в котором запущены три master ноды. Но master ноды — это машины, на которых работает api, scheduler и прочие сервисы кластера kubernetes. Для того, чтобы мы могли запускать свои поды, нам нужны так называемые worker ноды.
Если Вы ограничены в ресурсах, то можно запускать поды и на master нодах.Но я лично не советую так делать.


Запуск подов на мастернодах

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


kubectl taint nodes --all node-role.kubernetes.io/master-

Устанавливаем на worker ноды kubelet, kubeadm, kubectl и докер как на master нодах


Установка kubelet, kubeadm, kubectl и докер
apt-get update && apt-get install -y apt-transport-https curl
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
cat <<EOF >/etc/apt/sources.list.d/kubernetes.list
deb https://apt.kubernetes.io/ kubernetes-xenial main
EOF
apt-get update
apt-get install -y kubelet kubeadm kubectl
apt-mark hold kubelet kubeadm kubectl

Установка Docker


Устанавливаем docker по инструкции из документации


apt-get remove docker docker-engine docker.io containerd runc
apt-get install apt-transport-https ca-certificates curl gnupg2 software-properties-common

curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add -
apt-key fingerprint 0EBFCD88

add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/debian \
   $(lsb_release -cs) \
   stable"

apt-get update
apt-get install docker-ce docker-ce-cli containerd.io

Установка Установка Kubelet, Kubectl, Kubeadm и docker c помощью ansible
git clone https://github.com/rjeka/kubernetes-ceph-percona.git
cd kubernetes-ceph-percona/playbooks
vim masters.ini 

В группе masters прописываем ip мастеров.
В группе workers прописываем ip рабочих нод.


#Если sudo c паролем
ansible-playbook -i hosts.ini kubelet.yaml -K
ansible-playbook -i hosts.ini docker.yaml -K
#Если sudo безпарольный 
ansible-playbook -i hosts.ini kubelet.yaml
ansible-playbook -i hosts.ini docker.yaml

Теперь пора время вернуться к той строчке, которую нам сгенерировал kubeadm при установке мастер ноды.
У меня она выглядит так.


kubeadm join 10.73.71.25:6443 --token ivwoap.259retezqf34amx8 \
    --discovery-token-ca-cert-hash sha256:b5c93e32457c8e6478782ff62e8ef77acf72738dda59cd603cdf4821abe12ca3

Нужно выполнить данную команду на каждой worker ноде.
Если Вы не записали токен, то можно сгенерировать новый


kubeadm token create --print-join-command --ttl=0

После того, как kubeadm отработает, Ваша новая нода введена в кластер и готова для работы


This node has joined the cluster:
* Certificate signing request was sent to apiserver and a response was received.
* The Kubelet was informed of the new secure connection details.

Run 'kubectl get nodes' on the control-plane to see this node join the cluster.

Теперь посмотрим на результат


root@master01:~# kubectl get nodes
NAME         STATUS   ROLES    AGE     VERSION
master01     Ready    master   10d     v1.15.1
master02     Ready    master   10d     v1.15.1
master03     Ready    master   10d     v1.15.1
worknode01   Ready    <none>   5m44s   v1.15.1
worknode02   Ready    <none>   59s     v1.15.1
worknode03   Ready    <none>   51s     v1.15.1


Устанавливаем haproxy на worknodes


Теперь мы имеем рабочий кластер с тремя master нодами и тремя worker нодами.
Проблема в том, что сейчас наши worker ноды не имеют HA режима.
Если посмотреть на конфиг файл kubelet, то мы увидим, что наши worker ноды обращаются только к одной master ноде из трех.


root@worknode01:~# cat /etc/kubernetes/kubelet.conf | grep server:
    server: https://10.73.71.27:6443

В моем случае это master03. При данной конфигурации, в случае падения master03, worker нода потеряет связь с API сервером кластера. Чтобы наш кластер стал полностью HA, мы установим на каждый из воркеров Load Balancer (Haproxy), который по round robin будет раскидывать запросы на три master ноды, а в конфигах kubelet на worker нодах мы поменяем адрес сервера на 127.0.0.1:6443
Для начала установим HAProxy на каждую worker ноду.
Есть неплохая шпаргалка по установке


curl https://haproxy.debian.net/bernat.debian.org.gpg | \
      apt-key add -
echo deb http://haproxy.debian.net stretch-backports-2.0 main | \
      tee /etc/apt/sources.list.d/haproxy.list
apt-get update
apt-get install haproxy=2.0.\*

После того, как HAproxy, установлен нам нужно создать для него конфиг.
Если на worker нодах нет каталога с конфиг файлами, то клонируем его


git clone https://github.com/rjeka/kubernetes-ceph-percona.git
cd kubernetes-ceph-percona/

И запускаем скрипт конфига с флагом haproxy


./create-config.sh haproxy

Скрипт сконфигурирует и перезапустит haproxy.
Проверим, что haproxy стал слушать порт 6443.


root@worknode01:~/kubernetes-ceph-percona# netstat -alpn | grep 6443
tcp        0      0 127.0.0.1:6443          0.0.0.0:*               LISTEN      30675/haproxy
tcp        0      0 10.73.75.241:6443       0.0.0.0:*               LISTEN      30675/haproxy

Теперь нам нужно сказать kubelet, чтобы он обращался на localhost вместо master ноды. Для этого нужно отредактировать значение server в файлах /etc/kubernetes/kubelet.conf и /etc/kubernetes/bootstrap-kubelet.conf на всех worker нодах.


vim  /etc/kubernetes/kubelet.conf
vim /etc/kubernetes/bootstrap-kubelet.conf

Значение server должно принять вот такой вид:


server: https://127.0.0.1:6443

После внесения изменений нужно перезапустить службы kubelet и docker


systemctl restart kubelet && systemctl restart docker

Проверим, что все ноды работают исправно


kubectl get nodes
NAME         STATUS   ROLES    AGE     VERSION
master01     Ready    master   29m     v1.15.1
master02     Ready    master   27m     v1.15.1
master03     Ready    master   26m     v1.15.1
worknode01   Ready    <none>   25m     v1.15.1
worknode02   Ready    <none>   3m15s   v1.15.1
worknode03   Ready    <none>   3m16s   v1.15.1

Пока что у нас нет приложений в кластере, чтобы проверить работу HA. Но мы можем остановить работу kubelet на первой master ноде и убедится, что наш кластер остался дееспособным.


systemctl stop kubelet && systemctl stop docker

Проверяем со второй master ноды


root@master02:~# kubectl get nodes
NAME         STATUS     ROLES    AGE   VERSION
master01     NotReady   master   15h   v1.15.1
master02     Ready      master   15h   v1.15.1
master03     Ready      master   15h   v1.15.1
worknode01   Ready      <none>   15h   v1.15.1
worknode02   Ready      <none>   15h   v1.15.1
worknode03   Ready      <none>   15h   v1.15.1

Все ноды функционируют нормально, кроме той, на которой мы остановили службы.
Не забываем включить обратно службы kubernetes на первой master ноде


systemctl start kubelet && systemctl start docker


Установка Ingress контроллера


Ingress контроллер — это дополнение Kubernetes, c помощью которого мы можем получить доступ до наших приложений снаружи. Подробное описание есть в документации Kuberbnetes. Ingress контролеров существует достаточное большое количество, я пользуюсь контроллером от Nginx. Про его установку я и буду рассказывать. Документацию по работе, настройке и установке Ingress контроллера от Nginx можно почитать на официальном сайте


Приступим к установке, все команды можно выполнять с master01.
Устанавливаем сам контроллер


kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/mandatory.yaml

А теперь — сервис, через который будет доступен ингресс
Для этого подготовим конфиг


./create-config.sh ingress

И отправим его в наш кластер


kubectl apply -f ingress/service-nodeport.yaml

Проверим, что наш Ingress работает на нужных адресах и слушает нужные порты


# kubectl get svc -n ingress-nginx
NAME            TYPE       CLUSTER-IP    EXTERNAL-IP                           PORT(S)                      AGE
ingress-nginx   NodePort   10.99.35.95   10.73.71.25,10.73.71.26,10.73.71.27   80:31669/TCP,443:31604/TCP   10m

 kubectl describe svc -n ingress-nginx ingress-nginx
Name:                     ingress-nginx
Namespace:                ingress-nginx
Labels:                   app.kubernetes.io/name=ingress-nginx
                          app.kubernetes.io/part-of=ingress-nginx
Annotations:              kubectl.kubernetes.io/last-applied-configuration:
                            {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"labels":{"app.kubernetes.io/name":"ingress-nginx","app.kubernetes.io/par...
Selector:                 app.kubernetes.io/name=ingress-nginx,app.kubernetes.io/part-of=ingress-nginx
Type:                     NodePort
IP:                       10.99.35.95
External IPs:             10.73.71.25,10.73.71.26,10.73.71.27
Port:                     http  80/TCP
TargetPort:               80/TCP
NodePort:                 http  31669/TCP
Endpoints:                192.168.142.129:80
Port:                     https  443/TCP
TargetPort:               443/TCP
NodePort:                 https  31604/TCP
Endpoints:                192.168.142.129:443
Session Affinity:         None
External Traffic Policy:  Cluster
Events:                   <none>


Установка Web UI (Dashboard)


У Kubernetes есть стандартный Web UI, через который иногда удобно быстро глянуть состояние кластера или отдельных его частей. Я в своей работе часто использую dashboard для первичной диагностики деплоя или состояния частей кластера.
Ссылка на документацию находится на сайте kubernetes
Установка. Я использую стабильную версию, 2.0 пока что не пробовал.


#Стабильная версия
kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v1.10.1/src/deploy/recommended/kubernetes-dashboard.yaml
#Версия 2.0
kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0-beta1/aio/deploy/recommended.yaml

После того, как мы установили панель в наш кластер, панель стала доступна по адресу


http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/.

Но для того, чтобы на нее попасть, нам нужно с локальной машины пробросить порты с помощью kubectl proxy. Для меня эта схема очень не удобная. Поэтому я изменю service панели управления, для того чтобы dashboard стал доступен на адресе любой ноды кластера на порту 30443. Есть еще другие способы получить доступ до дашборда, например, через ingress. Возможно, я рассмотрю этот способ в следующих публикациях.
Для изменения сервиса запустим деплой измененного сервиса


kubectl apply -f dashboard/service-nodeport.yaml

Осталось создать admin пользователя и token для доступа к кластеру через дашборд


kubectl apply -f dashboard/rbac.yaml
kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | grep admin-user | awk '{print $1}')

После этого можно залогинится в панеле управления по адресу https://10.73.71.25:30443

Главный экран dashboard


Поздравляю! Если Вы дошли до этого шага, то у Вас есть работающий HA кластер kubernetes, который готов для деплоя Ваших приложений.
Kubernetes является костяком микросервисной инфраструктуру, на который требуется устанавливать различные дополнения. Про некоторые из них я планирую рассказать в следующих публикациях.
На все вопросы постараюсь ответить в комментариях, на GitHub, или в любых соцсетях, указанных в моем профиле.
C уважением, Евгений Родионов.

Tags:kubernetesdevopslinux
Hubs: System administration DevOps Kubernetes
+14
28.4k 219
Comments 4
Popular right now
Administrator Linux. Professional
January 18, 202180,000 ₽OTUS
Безопасность Linux
February 12, 202130,000 ₽OTUS
Kubernetes
December 21, 202031,900 ₽Luxoft Training
Профессия Product Manager
December 2, 2020108,500 ₽Нетология
Python для анализа данных
December 2, 202019,000 ₽SkillFactory