Translate

четвер, 14 грудня 2017 р.

Kubernetes. Part IV: Setup HA Cluster With Kubespray

Kubespray (раніше Kargo) - це набір Ansible ролей для установки та конфігурації системи оркестрації контейнерами Kubernetes. У якості IaaS-у в цьому випадку може виступати AWS, GCE, Azure, OpenStack чи звичайні віртуальні машини. Це проект з відкритим вихідним кодом та відкритою моделлю розробки, тому за бажанням кожен може вплинути на його життєвий цикл.

Нещодавно я писав про інсталяцію Kubernetes за допомогою Kubeadm, але в цьому способі є значні недоліки: він і досі не підтримує мультимайстер конфігурацій та, часом, є не дуже гнучким. Kubespray, хоч і використовує Kubeadm під капотом, вже має функціонал забезпечення високої доступності як для майстра, так і для etcd на етапі інсталяції. Про його порівняння із іншими методами установки Kubernetes можна почитати за наступним посиланням https://github.com/kubernetes-incubator/kubespray/blob/master/docs/comparisons.md

У цій статті ми створимо 5 серверів на ОС Ubuntu 16.04. У моєму випадку їх перелік наступний:

192.168.20.10  k8s-m1.me
192.168.20.11  k8s-m2.me
192.168.20.12  k8s-m3.me
192.168.20.13  k8s-s1.me
192.168.20.14  k8s-s2.me

Додаємо їх до /etc/hosts всіх вузлів, в тому числі локальної системи, або ж до dns-серверу. Фаерволи та інші обмеження в мережі цих вузлів мають бути деактивовані. Окрім цього, має бути дозволений IPv4 forwarding та кожен із хостів повинен мати вільний доступ до мережі Інтернет задля завантаження docker-образів.

Копіюємо публічний rsa-ключ до кожного серверу зі списку:

$ ssh-copy-id ubuntu@server.me

Вказуємо необхідного користувача та ключ для підключення з локальної машини:

$ vim ~/.ssh/config
...
Host *.me
    User ubuntu
    ServerAliveInterval 60
    IdentityFile ~/.ssh/id_rsa

Де ubuntu - користувач, від імені якого буде відбуватись підключення до сервера, id_rsa - приватний ключ. Більш того, цей користувач потребує можливості виконувати команди sudo без паролю.

Клонуємо репозиторій Kubespray:

$ git clone https://github.com/kubernetes-incubator/kubespray.git


Після копіюємо директорію inventory задля редагування її вмісту:

$ cp -r inventory my_inventory
$ cd my_inventory

У якості прикладу використаємо inventory.example:

$ mv inventory.example inventory
$ vim inventory

k8s-m1.me ip=192.168.20.10
k8s-m2.me ip=192.168.20.11
k8s-m3.me ip=192.168.20.12
k8s-s1.me ip=192.168.20.13
k8s-s2.me ip=192.168.20.14

[kube-master]
k8s-m1.me
k8s-m2.me
k8s-m3.me

[etcd]
k8s-m1.me
k8s-m2.me
k8s-m3.me

[kube-node]
k8s-s1.me
k8s-s2.me

[k8s-cluster:children]
kube-node
kube-master

Виходячи з поданого вище, ми виконаємо установку HA інсталяції Kubernetes: etcd, сховище параметрів конфігурації кластеру, буде складатись із 3-ох вузлів задля присутності кворума, а сервіси Kubernetes Master (kube-apiserver, controller-manager, scheduler і т.д.) будуть також продубльовані тричі. Звісно, що нічого не заважає винести вузли etcd повністю окремо.

На цьому етапі хотілося б трохи детальніше розповісти про те як реалізований режим HA для майстрів. На кожному воркері Kubernetes (у нашому випадку це k8s-s*.me) будуть встановлені локальні балансувальники Nginx, в upstream яких будуть додані всі майстри Kubernetes:

stream {
        upstream kube_apiserver {
            least_conn;
            server kube-master_ip1:6443;
            server kube-master_ip2:6443;
            server kube-master_ip3:6443;
                    }

        server {
            listen        127.0.0.1:6443;
            proxy_pass    kube_apiserver;
            proxy_timeout 10m;
            proxy_connect_timeout 1s;

        }

Відповідно у разі падіння одного із майстрів, Nginx виключить його із upstream та перестане надсилати запити на такий сервер.


Така схема дозволяє виключити єдину точку відмови: у разі падіння майстра його роботу прийме на себе інший майстер, а відповідальний за переадресування запитів Nginx працює на кожному вузлі воркера.

На етапі установки кластера є можливість відключити внутрішній балансувальник воркерів і вже піклуватись про його доступність самостійно. Це може бути, наприклад, якийсь сторонній балансувальник. Проте варто не забувати, що для забезпечення їх високої доступності вони мають працювати в парі, між членами якої за необхідності має мігрувати Virtual IP. VIP може бути реалізований за допомогою різноманітних технологій таких, як Keepalived, Heartbeat, Pacemaker і т.п.

На майстрі kube-apiserver працює одночасно на 2-х портах: локальному 8080 без шифрування (для служб, що працюють на одному сервері) та зовнішньому HTTPS 6443. Останній, як я вже згадав, використовується для зв'язку з воркерами та може стати у пригоді, якщо сервіси одного майстра (kubelet, kube-proxy та ін) необхідно рознести на інші вузли.

Продовжимо роботу по створенню тестового кластеру. Відредагуємо group_vars/all.yml:

$ vim group_vars/all.yml
...
bootstrap_os: ubuntu
...
kubelet_load_modules: true

Окрім Ubuntu 16.04 Kubespray також підтримує інсталяцію на вузли з CoreOS, Debian Jessie, CentOS/RHEL 7, тобто на всі основні актуальні дистрибутиви.

За необхідності варто також заглянути до group_vars/k8s-cluster.yml, де можна вказати необхідну версію Kubernetes, котра буде проінстальована, обрати плагін для оверлейної мережі (за-замовчуванням це calico, але доступні також інші варіанти), установити efk (elasticsearch/fluentd/kibana), helm, istio, netchecker і т.п.

Також рекомендую переглянути roles/kubernetes/preinstall/tasks/verify-settings.yml. Тут знаходяться базові перевірки, що будуть виконані перед початком установки Kubernetes. Наприклад, перевірки наявності достатньої кількості оперативної пам'яті (на даний момент, це не менше 1500MB для майстрів і 1000MB для нод), кількості etcd серверів (задля забезпечення кворуму їх має бути непарна кількість) та інше. В останніх релізах Kubespray з'явилась додаткова вимога щодо swap-у: він має бути вимкнений на всіх вузлах кластеру.

Якщо Ansible ще відсутній в локальній системі, встановимо його разом із модулем netaddr:

# pip install ansible
# pip install netaddr

Важливо звернути увагу на те, що модуль netaddr та ansible мають працювати з тією ж самою версією Python.

Після цього можемо приступати до установки кластера Kubernetes:

$ ansible-playbook -i my_inventory/inventory cluster.yml -b -v

Як варіант, передавати rsa-ключ та користувача для підключення можна передати аргументами, наприклад:

$ ansible-playbook -u ubuntu -i my_inventory/inventory cluster.yml -b -v --private-key=~/.ssh/id_rsa
Зазвичай установка кластера займає біля 15-20 хв. Після ми можемо перевірити чи все коректно працює, для чого підключимось до будь-якого вузла кластера:

root@k8s-m1:~# kubectl get nodes
NAME      STATUS    ROLES     AGE       VERSION
k8s-m1    Ready     master    28m       v1.8.4+coreos.0
k8s-m2    Ready     master    28m       v1.8.4+coreos.0
k8s-m3    Ready     master    28m       v1.8.4+coreos.0
k8s-s1    Ready     node      28m       v1.8.4+coreos.0
k8s-s2    Ready     node      28m       v1.8.4+coreos.0

root@k8s-m1:~# kubectl get pods --all-namespaces
NAMESPACE     NAME                                    READY     STATUS    RESTARTS   AGE
kube-system   calico-node-2z6jz                       1/1       Running   0          27m
kube-system   calico-node-6d6q6                       1/1       Running   0          27m
kube-system   calico-node-96rgg                       1/1       Running   0          27m
kube-system   calico-node-nld9z                       1/1       Running   0          27m
kube-system   calico-node-pjcjs                       1/1       Running   0          27m
kube-system   kube-apiserver-k8s-m1                   1/1       Running   0          27m
kube-system   kube-apiserver-k8s-m2                   1/1       Running   0          26m
kube-system   kube-apiserver-k8s-m3                   1/1       Running   0          27m
kube-system   kube-controller-manager-k8s-m1          1/1       Running   0          28m
kube-system   kube-controller-manager-k8s-m2          1/1       Running   0          28m
kube-system   kube-controller-manager-k8s-m3          1/1       Running   0          28m
kube-system   kube-dns-cf9d8c47-gnxcd                 3/3       Running   0          26m
kube-system   kube-dns-cf9d8c47-qcthn                 3/3       Running   0          26m
kube-system   kube-proxy-k8s-m1                       1/1       Running   0          26m
kube-system   kube-proxy-k8s-m2                       1/1       Running   0          27m
kube-system   kube-proxy-k8s-m3                       1/1       Running   0          27m
kube-system   kube-proxy-k8s-s1                       1/1       Running   0          26m
kube-system   kube-proxy-k8s-s2                       1/1       Running   0          27m
kube-system   kube-scheduler-k8s-m1                   1/1       Running   0          28m
kube-system   kube-scheduler-k8s-m2                   1/1       Running   0          28m
kube-system   kube-scheduler-k8s-m3                   1/1       Running   0          28m
kube-system   kubedns-autoscaler-86c47697df-4p7b8     1/1       Running   0          26m
kube-system   kubernetes-dashboard-85d88b455f-f5dm4   1/1       Running   0          26m
kube-system   nginx-proxy-k8s-s1                      1/1       Running   0          28m
kube-system   nginx-proxy-k8s-s2                      1/1       Running   0          28m

Як бачимо, по-замовчуванню, одразу була встановлена веб-панель kubernetes-dashboard. Деталі щодо її роботи можна знайти за наступним посиланням https://github.com/kubernetes/dashboard

Виключно для базової перевірки задеплоїмо под з двома контейнерами:

$ vim first-pod.yaml

apiVersion: v1
kind: Pod
metadata:
  name: first-pod
spec:
  containers:
  - name: sise
    image: mhausenblas/simpleservice:0.5.0
    ports:
    - containerPort: 9876
    resources:
      limits:
        memory: "64Mi"
        cpu: "500m"
  - name: shell
    image: centos:7
    command:
      - "bin/bash"
      - "-c"
      - "sleep 10000"

$ kubectl apply -f first-pod.yaml
pod "first-pod" created

$ kubectl get pods
NAME              READY     STATUS    RESTARTS   AGE
first-pod        2/2       Running   0          16s

$ kubectl exec first-pod -c sise -i -t -- bash
[root@first-pod /]# curl localhost:9876/info
{"host": "localhost:9876", "version": "0.5.0", "from": "127.0.0.1"}

Досить дивно, але у якості системи контейнеризації був встановлений Docker 17.03.1-ce, хоча ж знову, в офіційній документації згадується, що ліпше за все використовувати саме версію 1.13. Версія Docker, котра буде встановлена, описана в roles/docker/defaults/main.yml і, теоретично, її можна перезаписати в файлах конфігурації вище чи передати значення аргументом.

Ansible скрипти Kubespray також підтримують масштабування кількості вузлів. Для цього оновимо inventory, в якому додамо нову ноду (worker):

$ vim my_inventory/inventory

k8s-m1.me ip=192.168.20.10
k8s-m2.me ip=192.168.20.11
k8s-m3.me ip=192.168.20.12
k8s-s1.me ip=192.168.20.13
k8s-s2.me ip=192.168.20.14
k8s-s3.me ip=192.168.20.15

[kube-master]
k8s-m1.me
k8s-m2.me
k8s-m3.me

[etcd]
k8s-m1.me
k8s-m2.me
k8s-m3.me

[kube-node]
k8s-s1.me
k8s-s2.me
k8s-s3.me

[k8s-cluster:children]
kube-node
kube-master

Звісно, вузол k8s-s3.me має бути відповідним чином налаштований, як і попередні ноди. Тепер можемо запускати масштабування кластеру:

$ ansible-playbook -i my_inventory/inventory scale.yml -b -v

Згідно документації Kubespray, для цього можна використовувати і попередню процедуру з cluster.yml, проте зі scale.yml це займе значно менше часу. Як результат наразі можемо спостерігати новий вузол через kubectl:

$ kubectl get nodes
NAME      STATUS    ROLES     AGE       VERSION
k8s-m1    Ready     master    6h        v1.8.4+coreos.0
k8s-m2    Ready     master    6h        v1.8.4+coreos.0
k8s-m3    Ready     master    6h        v1.8.4+coreos.0
k8s-s1    Ready     node      6h        v1.8.4+coreos.0
k8s-s2    Ready     node      6h        v1.8.4+coreos.0
k8s-s3    Ready     node      19m       v1.8.4+coreos.0

Ось і все. Для занурення в тему Kubernetes рекомендую свої попередні статті і посилання внизу сторінок.

Посилання:
https://kubespray.io/
https://github.com/kubernetes-incubator/kubespray
https://github.com/kubernetes-incubator/kubespray/blob/master/docs/getting-started.md
https://github.com/kubernetes-incubator/kubespray/blob/master/docs/ansible.md
https://github.com/kubernetes-incubator/kubespray/blob/master/docs/ha-mode.md
https://dickingwithdocker.com/2017/08/deploying-kubernetes-vms-kubespray/
https://medium.com/@olegsmetanin/how-to-setup-baremetal-kubernetes-cluster-with-kubespray-and-deploy-ingress-controller-with-170cdb5ac50d
https://kubernetes.io/docs/admin/high-availability/

2 коментарі:

  1. в первоисточнике немного не так...
    https://github.com/kubernetes-incubator/kubespray

    не розумію, достобіса шо вони тут роблять і що це в них за IP-адреси? (10.10.1.Х)
    Update Ansible inventory file with inventory builder
    declare -a IPS=(10.10.1.3 10.10.1.4 10.10.1.5)
    CONFIG_FILE=inventory/mycluster/hosts.ini python3 contrib/inventory_builder/inventory.py ${IPS[@]}

    ВідповістиВидалити
    Відповіді
    1. У цій статті запропоновано власноруч заповнити inventory файл Ansible. В git-проекті ж наведена його автоматична генерація з використанням python-скрипта https://github.com/kubernetes-incubator/kubespray/blob/master/contrib/inventory_builder/inventory.py. IP адреси (10.10.1.3 10.10.1.4 10.10.1.5) - це адреси підготованих віртуальних машин, тобто вони вже працюють і мають мережевий доступ. У разі використання inventory.py вузли будуть заповнено рандомно по групах в inventory файлі.

      Видалити