Создание кластера Kubernetes с помощью kubeadm в Debian 9

Kubernetes — это система оркестровки контейнеров, а также управления ими и масштабирования. Изначально система Kubernetes была разработана компанией Google на основе ее опыта применения контейнеров в рабочей среде. Kubernetes – это инструмент с открытым исходным кодом, в его разработке принимают активное участие представители сообщества со всего мира.

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

Kubeadm – это инструмент для автоматизации установки и настройки компонентов Kubernetes (в том числе сервера API, Controller Manager и Kube DNS). Но этот инструмент не умеет создавать пользователей и не выполняет установку и настройку зависимостей операционной системы. Для таких подготовительных задач можно использовать инструменты управления конфигурацией, например, Ansible или SaltStack. Они упростят создание дополнительных или восстановление существующих кластеров, а также снизят вероятность возникновения ошибок.

В данном мануале вы создадите кластер Kubernetes с помощью Ansible и Kubeadm, а затем развернете в нем контейнеризованное приложение Nginx.

Цели

Вашему кластеру понадобятся такие физические ресурсы:

  • Одна главная нода, master (в контексте Kubernetes под нодой подразумевается сервер). Она будет отвечать за управление состоянием кластера. Именно здесь будет работать система Etcd, которая хранит данные кластера и распределяет рабочую нагрузку по рабочим нодам.
  • Две рабочие ноды, workers, которые будут обслуживать рабочие задачи (контейнеризованные приложения и сервисы). Рабочая нода продолжит обрабатывать задачу, даже если после распределения задач главная нода прекратит работу. Добавляя рабочие ноды, вы можете увеличить объем кластера.

Выполнив этот мануал, вы получите кластер, готовый к запуску контейнеризованных приложений (для этого серверы кластера должны иметь достаточно ресурсов процессора и оперативной памяти). Контейнеризовать и запустить в кластере можно практически любое стандартное приложение Unix (веб-приложения, базы данных, демоны и утилиты командной строки). Сам кластер использует примерно 300-500 МБ оперативной памяти и 10% процессора каждой ноды.

Настроив кластер, мы развернем веб-сервер Nginx, чтобы убедиться, что рабочие задачи правильно выполняются.

Требования

1: Настройка рабочего пространства и инвентаря Ansible

Сначала на локальной машине нужно создать каталог, который будет рабочим пространством. Настройте Ansible локально, чтобы он мог взаимодействовать с командами на удаленных серверах. После этого нужно создать файл инвентаря hosts, содержащий информацию об оркестровке (IP-адреса серверов и группы, к которым принадлежит каждый сервер).

Примечание: Мы используем условные IP-адреса; для главной ноды это master_ip, а для рабочих нод – worker_1_ip и worker_2_ip. Их вам нужно заменить своими данными.

На локальной машине в домашнем каталоге создайте каталог ~/kube-cluster и перейдите в него:

mkdir ~/kube-cluster
cd ~/kube-cluster

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

Создайте файл ~/kube-cluster/hosts, используя nano или другой текстовый редактор:

nano ~/kube-cluster/hosts

Добавьте в файл следующие строки (это информация о логической структуре кластера).

[masters]
master ansible_host=master_ip ansible_user=root
[workers]
worker1 ansible_host=worker_1_ip ansible_user=root
worker2 ansible_host=worker_2_ip ansible_user=root
[all:vars]
ansible_python_interpreter=/usr/bin/python3

Файлы инвентаря в Ansible используются для определения информации о сервере (IP-адреса, удаленных пользователей и т.д.) и для группировки серверов в единый блок (и дальнейшего выполнения команд в этом блоке). Файл ~/kube-cluster/hosts будет таким файлом инвентаря, в нем указаны две группы Ansible (masters и workers), в которых определяется логическая структура кластера.

В группе masters есть запись сервера «master», которая определяет IP-адрес ведущей ноды (master_ip) и указывает, что Ansible должен запускать удаленные команды в качестве пользователя root.

Аналогично, в группе workers есть две записи рабочих серверов (worker_1_ip и worker_2_ip), которые также указывают ansible_user как root.

Последняя строка файла позволяет Ansible для выполнения своих операций использовать интерпретаторы Python 3 на удаленных серверах.

Сохраните и закройте файл.

2: Создание пользователей sudo на удаленных серверах

Чтоб не работать через пользователя root, нам нужно создать пользователей с привилегиями sudo на всех удаленных серверах. Так вы сможете подключаться по SSH вручную в качестве непривилегированного пользователя. Это может быть полезно, например, если вам нужно получить системную информацию с помощью таких команд как top/htop, просмотреть список запущенных контейнеров или изменить файлы конфигурации, принадлежащие root. Эти операции обычно выполняются во время обслуживания кластера, а применение для таких задач пользователя без прав root минимизирует риск потери и повреждения важных файлов или случайного выполнения опасных операций.

В рабочем пространстве создайте ~/kube-cluster/initial.yml:

nano ~/kube-cluster/initial.yml

Затем добавьте в файл следующий код, чтобы создать пользователя с привилегиями sudo на всех серверах кластера. Плей в Ansible представляет собой набор действий, которые необходимо выполнить на целевых серверах и группах. Этот плей создаст пользователя sudo:

- hosts: all
become: yes
tasks:
- name: create the '8host' user
user: name=8host append=yes state=present createhome=yes shell=/bin/bash
- name: allow '8host' to have passwordless sudo
lineinfile:
dest: /etc/sudoers
line: '8host ALL=(ALL) NOPASSWD: ALL'
validate: 'visudo -cf %s'
- name: set up authorized keys for the 8host user
authorized_key: user=8host key="{{item}}"
with_file:
- ~/.ssh/id_rsa.pub

Этот плейбук:

  • Создает пользователя sudo по имени 8host.
  • Настраивает файл sudoers, чтобы предоставить пользователю 8host права sudo без пароля.
  • Добавляет открытый ключ локальной машины (обычно это ~/.ssh/id_rsa.pub) в список ключей удаленного пользователя 8host. Это позволит вам подключаться к серверам по SSH как пользователь 8host.

Сохраните и закройте файл.

Запустите плейбук локально:

ansible-playbook -i hosts ~/kube-cluster/initial.yml

Команда будет выполняться в течение 2-5 минут. Затем вы увидите такой вывод:

PLAY [all] ****
TASK [Gathering Facts] ****
ok: [master]
ok: [worker1]
ok: [worker2]
TASK [create the '8host' user] ****
changed: [master]
changed: [worker1]
changed: [worker2]
TASK [allow '8host' user to have passwordless sudo] ****
changed: [master]
changed: [worker1]
changed: [worker2]
TASK [set up authorized keys for the 8host user] ****
changed: [worker1] => (item=ssh-rsa AAAAB3...)
changed: [worker2] => (item=ssh-rsa AAAAB3...)
changed: [master] => (item=ssh-rsa AAAAB3...)
PLAY RECAP ****
master                     : ok=5    changed=4    unreachable=0    failed=0
worker1                    : ok=5    changed=4    unreachable=0    failed=0
worker2                    : ok=5    changed=4    unreachable=0    failed=0

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

3: Установка зависимостей Kubernetes

Теперь нужно установить пакеты, необходимые для работы Kubernetes, это можно сделать с помощью менеджера пакетов Debian. Вот список нужных пакетов:

  • Docker – среда выполнения контейнеров. Поддержка других рабочих сред, таких как rkt, находится в Kubernetes в активной разработке.
  • kubeadm – CLI, позволяет устанавливать и настраивать компоненты кластера стандартным образом.
  • kubelet – системный сервис, запускается на всех нодах и выполняет операции уровня ноды.
  • kubectl – CLI, выполняет команды в кластере через API.

В рабочем пространстве создайте файл ~/kube-cluster/kube-dependencies.yml:

nano ~/kube-cluster/kube-dependencies.yml

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

- hosts: all
become: yes
tasks:
- name: install remote apt deps
apt:
name: "{{ item }}"
state: present
with_items:
- apt-transport-https
- ca-certificates
- gnupg2
- software-properties-common
- name: add Docker apt-key
apt_key:
url: https://download.docker.com/linux/debian/gpg
state: present
- name: add Docker's APT repository
apt_repository:
repo: deb https://download.docker.com/linux/debian stretch stable
state: present
filename: 'docker'
- name: install Docker
apt:
name: docker-ce
state: present
update_cache: true
- name: add Kubernetes apt-key
apt_key:
url: https://packages.cloud.google.com/apt/doc/apt-key.gpg
state: present
- name: add Kubernetes' APT repository
apt_repository:
repo: deb http://apt.kubernetes.io/ kubernetes-xenial main
state: present
filename: 'kubernetes'
- name: install kubelet
apt:
name: kubelet=1.14.0-00
state: present
update_cache: true
- name: install kubeadm
apt:
name: kubeadm=1.14.0-00
state: present
- hosts: master
become: yes
tasks:
- name: install kubectl
apt:
name: kubectl=1.14.0-00
state: present
force: yes

Первый плей в плейбуке делает следующее:

  • Определяет зависимости, пакеты которых нужно добавить, проверить и установить из сторонних репозиториев.
  • Добавляет apt-key репозитория Docker для верификации Устанавливает Docker.
  • Добавляет apt-key репозитория Kubernetes для верификации.
  • Добавляет репозиторий APT Kubernetes в список источников удаленных серверов.
  • Устанавливает kubelet и kubeadm.

Второй плей включает одну задачу – установку kubectl на главную ноду.

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

Сохраните и закройте файл.

Теперь запустите плейбук локально:

ansible-playbook -i hosts ~/kube-cluster/kube-dependencies.yml

Команда вернет такой вывод:

PLAY [all] ****
TASK [Gathering Facts] ****
ok: [worker1]
ok: [worker2]
ok: [master]
TASK [install Docker] ****
changed: [master]
changed: [worker1]
changed: [worker2]
TASK [install APT Transport HTTPS] *****
ok: [master]
ok: [worker1]
changed: [worker2]
TASK [add Kubernetes apt-key] *****
changed: [master]
changed: [worker1]
changed: [worker2]
TASK [add Kubernetes' APT repository] *****
changed: [master]
changed: [worker1]
changed: [worker2]
TASK [install kubelet] *****
changed: [master]
changed: [worker1]
changed: [worker2]
TASK [install kubeadm] *****
changed: [master]
changed: [worker1]
changed: [worker2]
PLAY [master] *****
TASK [Gathering Facts] *****
ok: [master]
TASK [install kubectl] ******
ok: [master]
PLAY RECAP ****
master                     : ok=9    changed=5    unreachable=0    failed=0
worker1                    : ok=7    changed=5    unreachable=0    failed=0
worker2                    : ok=7    changed=5    unreachable=0    failed=0

После выполнения плейбука на всех удаленных серверах будут установлены Docker, kubeadm и kubelet. kubectl не является обязательным компонентом и нужен только для выполнения команд кластера. В этом контексте имеет смысл устанавливать его только на главную ноду, так как вы будете запускать команды kubectl только с нее. Однако обратите внимание, что команды kubectl можно запускать с любой из рабочих нод и вообще с любого компьютера, на котором его можно установить и подключить к кластеру.

4: Настройка главной ноды

Теперь можно настроить главную ноду. Но для начала нужно ознакомиться с понятиями подов и сетевых плагинов Kubernetes.

Под – это базовый элемент, который управляет одним или несколькими контейнерами. Эти контейнеры совместно используют ресурсы, такие как тома и сетевые интерфейсы. Под – основная единица планирования в Kubernetes: все контейнеры в поде работают на той ноде, на которой запланирован данный под.

Каждому поду присваивается собственный IP-адрес; под одной ноды должен иметь доступ к поду на другой ноде по IP-адресу. Контейнеры на одной ноде могут легко связываться через локальный интерфейс. Однако связь между контейнерами сложнее, для этого требуется отдельный сетевой компонент, который может легко маршрутизировать трафик пода на одной ноде к поду на другой.

Эту функцию выполняют сетевые плагины. Один из них, Flannel, мы будем использовать в нашем кластере.

Создайте на локальной машине плейбук master.yml .

nano ~/kube-cluster/master.yml

Добавьте в файл такой плей, чтобы инициализировать кластер и установить Flannel:

- hosts: master
become: yes
tasks:
- name: initialize the cluster
shell: kubeadm init --pod-network-cidr=10.244.0.0/16 >> cluster_initialized.txt
args:
chdir: $HOME
creates: cluster_initialized.txt
- name: create .kube directory
become: yes
become_user: 8host
file:
path: $HOME/.kube
state: directory
mode: 0755
- name: copy admin.conf to user's kube config
copy:
src: /etc/kubernetes/admin.conf
dest: /home/8host/.kube/config
remote_src: yes
owner: 8host
- name: install Pod network
become: yes
become_user: 8host
shell: kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/a70459be0084506e4ec919aa1c114638878db11b/Documentation/kube-flannel.yml >> pod_network_setup.txt
args:
chdir: $HOME
creates: pod_network_setup.txt

Этот файл выполняет следующие действия:

  • Первая задача инициализирует кластер, запустив kubeadm init. Аргумент —pod-network-cidr=10.244.0.0/16 задает частную подсеть, из которой будут присваиваться IP-адреса. Flannel использует указанную выше подсеть по умолчанию; теперь kubeadm тоже будет использовать эту подсеть.
  • Вторая задача создает каталог .kube в /home/8host. В нем будут храниться конфигурации (ключи админов для подключения к кластеру и API-адрес кластера).
  • Третья задача копирует файл /etc/kubernetes/admin.conf, сгенерированный командой kubeadm init, в домашний каталог пользователя. Это позволяет использовать kubectl для доступа к кластеру.
  • Последняя задача запускает kubectl apply, чтобы установить Flannel. Синтаксис kubectl apply -f descriptor.[yml|json] создает объекты, описанные в файле descriptor.[yml|json]. Файл kube-flannel.yml содержит описание объектов, необходимых для настройки Flannel в кластере.

Сохраните и закройте файл.

Запустите плейбук локально:

ansible-playbook -i hosts ~/kube-cluster/master.yml

Вы получите такой результат:

PLAY [master] ****
TASK [Gathering Facts] ****
ok: [master]
TASK [initialize the cluster] ****
changed: [master]
TASK [create .kube directory] ****
changed: [master]
TASK [copy admin.conf to user's kube config] *****
changed: [master]
TASK [install Pod network] *****
changed: [master]
PLAY RECAP ****
master                     : ok=5    changed=4    unreachable=0    failed=0

Чтобы проверить состояние главной ноды, подключитесь к ней по SSH.

ssh 8host@master_ip

Затем выполните команду:

kubectl get nodes

Вы должны увидеть:

NAME      STATUS    ROLES     AGE       VERSION
master    Ready     master    1d        v1.14.0

В выводе сказано, что главная нода выполнила задачи инициализации и находится в состоянии Ready, то есть может принимать рабочие ноды и выполнять другие задачи, отправленные на сервер API. Теперь вы можете добавить рабочие ноды с локальной машины.

5: Настройка рабочих нод

Чтобы добавить рабочие ноды, нужно выполнить на каждой из них одну команду. Эта команда содержит необходимую информацию о кластере (IP-адрес, порт API сервера мастера и токен). В кластер будут добавлены только те ноды, которые могут предъявить правильный токен.

В рабочем пространстве создайте плейбук workers.yml:

nano ~/kube-cluster/workers.yml

Добавьте в него следующие строки, чтобы подключить рабочие ноды к кластеру:

- hosts: master
become: yes
gather_facts: false
tasks:
- name: get join command
shell: kubeadm token create --print-join-command
register: join_command_raw
- name: set join command
set_fact:
join_command: "{{ join_command_raw.stdout_lines[0] }}"
- hosts: workers
become: yes
tasks:
- name: join cluster
shell: "{{ hostvars['master'].join_command }} >> node_joined.txt"
args:
chdir: $HOME
creates: node_joined.txt

Этот плейбук выполняет такие действия:

  • Первый плей получает команду join, которая должна выполняться на рабочих нодах. Эта команда имеет следующий формат: kubeadm join —token <token> <master-ip>:<master-port> —discovery-token-ca-cert-hash sha256:<hash>. Как только он получит команду с соответствующим токеном и хэшем, следующий плей сможет получить эту информацию.
  • Второй плей содержит одну задачу, которая запускает команду join на всех рабочих нодах. В результате две ноды станут частью кластера.

Сохраните и закройте файл.

Выполните плейбук локально:

ansible-playbook -i hosts ~/kube-cluster/workers.yml

Он вернет такой вывод:

PLAY [master] ****
TASK [get join command] ****
changed: [master]
TASK [set join command] *****
ok: [master]
PLAY [workers] *****
TASK [Gathering Facts] *****
ok: [worker1]
ok: [worker2]
TASK [join cluster] *****
changed: [worker1]
changed: [worker2]
PLAY RECAP *****
master                     : ok=2    changed=1    unreachable=0    failed=0
worker1                    : ok=2    changed=1    unreachable=0    failed=0
worker2                    : ok=2    changed=1    unreachable=0    failed=0

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

6: Тестирование кластера

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

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

ssh 8host@master_ip

Затем выполните эту команду:

kubectl get nodes
NAME      STATUS    ROLES     AGE       VERSION
master    Ready     master    1d        v1.14.0
worker1   Ready     <none>    1d        v1.14.0
worker2   Ready     <none>    1d        v1.14.0

Если все ноды в столбце STATUS имеют значение Ready, значит, они являются частью кластера и готовы к обслуживанию рабочих задач.

Но если некоторые из нод имеют статус NotReady, это может значить, что они еще не завершили свою настройку. Подождите 5-10 минут, а затем попробуйте перезапустить kubectl get node. Если в выводе все еще будут ноды со статусом NotReady, возможно, вам придется проверить и повторно запустить команды из предыдущих разделов мануала.

Теперь можно попробовать запустить в кластере Nginx.

7: Запуск приложения в кластере

Теперь вы можете развернуть в своем кластере любое контейнеризованное приложение. Для примера с помощью развертываний и сервисов можно попробовать развернуть Nginx. Вы можете также использовать нижеприведенные команды для развертывания других приложений (для этого измените имя образа Docker и все соответствующие параметры, например, ports и volumes).

Оставаясь на мастер-ноде, запустите следующую команду, чтобы создать развертывание nginx:

kubectl create deployment nginx —image=nginx

Развертывание – это объект Kubernetes, который следит за тем, чтобы на определенном шаблоне всегда работало заданное минимальное количество подов (даже если под выходит из строя во время работы кластера). Вышеупомянутое развертывание создаст под с одним контейнером из образа Nginx Docker.

Затем запустите команду, чтобы создать сервис nginx, который откроет приложению доступ к сети. Это выполняется через NodePort – схему, которая сделает под доступным через произвольный порт, открытый на каждой ноде кластера:

kubectl expose deploy nginx --port 80 --target-port 80 --type NodePort

Сервисы – это еще один тип объектов Kubernetes, который раскрывает внутренние сервисы кластера для клиентов (как внутренних, так и внешних). Они также способны выполнять балансировку нагрузки на несколько подов и являются неотъемлемым компонентом Kubernetes, поскольку часто взаимодействуют с другими компонентами.

Запустите команду:

kubectl get services

Вы получите примерно такой вывод:

NAME         TYPE        CLUSTER-IP       EXTERNAL-IP           PORT(S)             AGE
kubernetes   ClusterIP   10.96.0.1        <none>                443/TCP             1d
nginx        NodePort    10.109.228.209   <none>                80:nginx_port/TCP   40m

В третьей строке вывода вы увидите порт, на котором работает Nginx. Kubernetes автоматически присваивает случайный порт выше 30000, предварительно проверив, что порт не занят другим сервисом.

Чтобы убедиться, что все работает, откройте в браузере на локальной машине http://worker_1_ip:nginx_port или http://worker_2_ip:nginx_port. Вы увидите стандартное приветствие Nginx.

Чтобы удалить приложение Nginx, сначала удалите сервис с главной ноды.

kubectl delete service nginx

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

kubectl get services
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP           PORT(S)        AGE
kubernetes   ClusterIP   10.96.0.1        <none>                443/TCP        1d

После этого удалите развертывание:

kubectl delete deployment nginx

Чтобы убедиться, что оно удалилось, введите:

kubectl get deployments
No resources found.

Заключение

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

  • На сайте Docker можно найти подробные примеры, описывающие, как контейнеризовать приложения.
  • Этот обзор рассказывает, как работают поды, и объясняет их связь с другими объектами в Kubernetes.
  • Этот обзор многое расскажет о развертываниях в Kubernetes. Понимать, как работают развертывания, очень важно, поскольку они часто используются при масштабировании и автоматическом восстановлении приложений.
  • По этой ссылке вы найдете обзор сервисов.

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

Kubernetes поддерживает много полезных функций. Официальная документация Kubernetes – лучшее средство для изучения этого инструмента, поиска руководств по конкретным задачам и ссылок API для различных объектов.

Tags: , , , , ,