Основы Kubernetes

Kate

Administrator
Команда форума

Что такое Kubernetes?


Kubernetes является проектом с открытым исходным кодом, предназначенным для управления кластером контейнеров Linux как единой системой. Kubernetes управляет и запускает контейнеры Docker на большом количестве хостов, а так же обеспечивает совместное размещение и репликацию большого количества контейнеров. Проект был начат Google и теперь поддерживается многими компаниями, среди которых Microsoft, RedHat, IBM и Docker.
Компания Google пользуется контейнерной технологией уже более десяти лет. Она начинала с запуска более 2 млрд контейнеров в течение одной недели. С помощью проекта Kubernetes компания делится своим опытом создания открытой платформы, предназначенной для масштабируемого запуска контейнеров.

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

Концепции Kubernetes


Nodes (node.md): Нода это машина в кластере Kubernetes.
Pods (pods.md): Pod это группа контейнеров с общими разделами, запускаемых как единое целое.
Replication Controllers (replication-controller.md): replication controller гарантирует, что определенное количество «реплик» pod'ы будут запущены в любой момент времени.
Services (services.md): Сервис в Kubernetes это абстракция которая определяет логический объединённый набор pod и политику доступа к ним.
Volumes (volumes.md): Volume(раздел) это директория, возможно, с данными в ней, которая доступна в контейнере.
Labels (labels.md): Label'ы это пары ключ/значение которые прикрепляются к объектам, например pod'ам. Label'ы могут быть использованы для создания и выбора наборов объектов.
Kubectl Command Line Interface (kubectl.md): kubectl интерфейс командной строки для управления Kubernetes.

Архитектура Kubernetes


Работающий кластер Kubernetes включает в себя агента, запущенного на нодах (kubelet) и компоненты мастера (APIs, scheduler, etc), поверх решения с распределённым хранилищем. Приведённая схема показывает желаемое, в конечном итоге, состояние, хотя все ещё ведётся работа над некоторыми вещами, например: как сделать так, чтобы kubelet (все компоненты, на самом деле) самостоятельно запускался в контейнере, что сделает планировщик на 100% подключаемым.
image


Нода Kubernetes


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

Kubelet


Kubelet управляет pod'ами их контейнерами, образами, разделами, etc.

Kube-Proxy


Также на каждой ноде запускается простой proxy-балансировщик. Этот сервис запускается на каждой ноде и настраивается в Kubernetes API. Kube-Proxy может выполнять простейшее перенаправление потоков TCP и UDP (round robin) между набором бэкендов.

Компоненты управления Kubernetes


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

etcd


Состояние мастера хранится в экземпляре etcd. Это обеспечивает надёжное хранение конфигурационных данных и своевременное оповещение прочих компонентов об изменении состояния.

Kubernetes API Server


Kubernetes API обеспечивает работу api-сервера. Он предназначен для того, чтобы быть CRUD сервером со встроенной бизнес-логикой, реализованной в отдельных компонентах или в плагинах. Он, в основном, обрабатывает REST операции, проверяя их и обновляя соответствующие объекты в etcd (и событийно в других хранилищах).

Scheduler


Scheduler привязывает незапущенные pod'ы к нодам через вызов /binding API. Scheduler подключаем; планируется поддержка множественных scheduler'ов и пользовательских scheduler'ов.

Kubernetes Controller Manager Server


Все остальные функции уровня кластера представлены в Controller Manager. Например, ноды обнаруживаются, управляются и контролируются средствами node controller. Эта сущность в итоге может быть разделена на отдельные компоненты, чтобы сделать их независимо подключаемыми.

ReplicationController — это механизм, основывающийся на pod API. В конечном счете планируется перевести её на общий механизм plug-in, когда он будет реализован.

Пример настройки кластера


В качестве платформы для примера настройки была выбрана Ubuntu-server 14.10 как наиболее простая для примера и, в то же время, позволяющая продемонстрировать основные параметры настройки кластера.

Для создания тестового кластера будут использованы три машины для создания нод и отдельная машина для проведения удалённой установки. Можно не выделять отдельную машину и производить установку с одной из нод.

Список используемых машин:
  • Conf
  • Node1: 192.168.0.10 — master, minion
  • Node2: 192.168.0.11 — minion
  • Node3: 192.168.0.12 — minion

Подготовка нод


Требования для запуска:


  1. На всех нодах установлен docker версии 1.2+ и bridge-utils
  2. Все машины связаны друг с другом, необходимости в доступе к интернету нет (в этом случае необходимо использовать локальный docker registry)
  3. На все ноды можно войти без ввода логина/пароля, с использованием ssh-ключей

Установка ПО на ноды


Установку Docker можно произвести по статье в официальных источниках:

node% sudo apt-get update $ sudo apt-get install wget
node% wget -qO- https://get.docker.com/ | sh


Дополнительная настройка Docker после установки не нужна, т.к. будет произведена скриптом установки Kubernetes.
Установка bridge-utils:

node% sudo apt-get install bridge-utils


Добавление ssh-ключей


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

conf% ssh-keygen


Копируем ключи на удалённые машины, предварительно убедившись в наличии на них необходимого пользователя, в нашем случае core.

conf% ssh-copy-id core@192.168.0.10
conf% ssh-copy-id core@192.168.0.11
conf% ssh-copy-id core@192.168.0.12


Установка Kubernetes


Далее мы займёмся установкой непосредственно Kubernetes. Для этого в первую очередь скачаем и распакуем последний доступный релиз с GitHub:

conf% wget https://github.com/GoogleCloudPlatform/kubernetes/releases/download/v0.17.0/kubernetes.tar.gz
conf% tar xzf ./kubernetes.tar.gz
conf% cd ./kubernetes


Настройка


Настройка Kubernetes через стандартные скрипты примеров полностью производится перед установкой производится через конфигурационные файлы. При установке мы будем использовать скрипты папке ./cluster/ubuntu/.

В первую очередь изменим скрипт ./cluster/ubuntu/build.sh который скачивает и подготавливает необходимые для установки бинарники Kubernetes, etcd и flannel:

conf% vim ./cluster/ubuntu/build.sh


Для того, чтобы использовать последний, на момент написания статьи, релиз 0.17.0 необходимо заменить:

# k8s
echo "Download kubernetes release ..."
K8S_VERSION="v0.15.0"


На:

# k8s
echo "Download kubernetes release ..."
K8S_VERSION="v0.17.0"


И запустим:

conf% cd ./cluster/ubuntu/
conf% ./build.sh #Данный скрипт важно запускать именно из той папки, где он лежит.


Далее указываем параметры будущего кластера, для чего редактируем файл ./config-default.sh:

## Contains configuration values for the Ubuntu cluster
# В данном пункте необходимо указать все ноды будущего кластера, MASTER-нода указывается первой
# Ноды указываются в формате <user_1@ip_1> <user_2@ip_2> <user_3@ip_3> разделитель - пробел
# В качестве пользователя указывается тот пользователь для которого по нодам разложены ssh-ключи
export nodes="core@192.168.0.10 core@192.168.0.10 core@192.168.0.10"
# Определяем роли нод : a(master) или i(minion) или ai(master и minion), указывается в том же порядке, что и ноды в списке выше.
export roles=("ai" "i" "i")
# Определяем количество миньонов
export NUM_MINIONS=${NUM_MINIONS:-3}
# Определяем IP-подсеть из которой, в последствии будут выделяться адреса для сервисов.
# Выделять необходимо серую подсеть, которая не будет пересекаться с имеющимися, т.к. эти адреса будут существовать только в пределах каждой ноды.
#Перенаправление на IP-адреса сервисов производится локальным iptables каждой ноды.
export PORTAL_NET=192.168.3.0/24
#Определяем подсеть из которой будут выделяться подсети для создания внутренней сети flannel.
#flannel по умолчанию выделяет подсеть с маской 24 на каждую ноду, из этих подсетей будут выделяться адреса для Docker-контейнеров.
#Подсеть не должна пересекаться с PORTAL_NET
export FLANNEL_NET=172.16.0.0/16
# Admission Controllers определяет политику доступа к объектам кластера.
ADMISSION_CONTROL=NamespaceLifecycle,NamespaceAutoProvision,LimitRanger,ResourceQuota
# Дополнительные параметры запуска Docker. Могут быть полезны для дополнительных настроек
# например установка --insecure-registry для локальных репозиториев.
DOCKER_OPTS=""


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

Установка


Первым делом необходимо сообщить системе про наш ssh-agent и используемый ssh-ключ для этого выполняем:

eval `ssh-agent -s`
ssh-add /путь/до/ключа


Далее переходим непосредственно к установке. Для этого используется скрипт ./kubernetes/cluster/kube-up.sh которому необходимо указать, что мы используем ubuntu.

conf% cd ../
conf% KUBERNETES_PROVIDER=ubuntu ./kube-up.sh


В процессе установки скрипт потребует пароль sudo для каждой ноды. По окончанию установки проверит состояние кластера и выведет список нод и адреса Kubernetes api.

Пример вывода скрипта

Посмотрим, какие ноды и сервисы присутствуют в новом кластере:
conf% cp ../kubernetes/platforms/linux/amd64/kubectl /opt/bin/
conf% /opt/bin/kubectl get services,minions -s "http://192.168.0.10:8080"
NAME LABELS SELECTOR IP PORT(S)
kubernetes component=apiserver,provider=kubernetes <none> 192.168.3.2 443/TCP
kubernetes-ro component=apiserver,provider=kubernetes <none> 192.168.3.1 80/TCP
NAME LABELS STATUS
192.168.0.10 <none> Ready
192.168.0.11 <none> Ready
192.168.0.12 <none> Ready


Видим список из установленных нод в состоянии Ready и два предустановленных сервиса kubernetes и kubernetes-ro — это прокси для непосредственного доступа к Kubernetes API. Как и к любому сервису Kubernetes к kubernetes и kubernetes-ro можно обратиться непосредственно по IP адресу с любой из нод.

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


Для запуска сервиса необходимо подготовить docker контейнер, на основе которого будет создан сервис. Дабы не усложнять, в примере будет использован общедоступный контейнер nginx. Обязательными составляющими сервиса являются Replication Controller, обеспечивающий запущенность необходимого набора контейнеров (точнее pod) и service, который определяет, на каких IP адресе и портах будет слушать сервис и правила распределения запросов между pod'ами.

Любой сервис можно запустить 2-я способами: вручную и с помощью конфиг-файла. Рассмотрим оба.

Запуск сервиса вручную


Начнём с создания Replication Controller'а:

conf% /opt/bin/kubectl run-container nginx --port=80 --port=443 --image=nginx --replicas=6 -s "http://192.168.0.10:8080"


Где:
  • nginx — имя будущего rc
  • --port — порты на которых будут слушать контейнеры rc
  • --image — образ из которого будут запущены контейнеры
  • --replicas=6 — количество реплик

Посмотрим, что у нас получилось:

/opt/bin/kubectl get pods,rc -s "http://192.168.0.10:8080"


Вывод

Был создан Replication Controller с именем nginx и количеством реплик равным 6. Реплики в произвольном порядке запущены на нодах, местоположения каждой pod'ы указано в столбце HOST.
Вывод может отличаться от приведённого в некоторых случаях, например:
  • Часть pod находится в состоянии pending: это значит, что они ещё не запустились, необходимо немного подождать
  • У pod не определён HOST: это значит, что scheduler ещё не назначил ноду на которой будет запущен pod


Далее создаём service который будет использовать наш Replication Controller как бекенд.
Для http:

conf% /opt/bin/kubectl expose rc nginx --port=80 --target-port=80 --service-name=nginx-http -s "http://192.168.0.10:8080"


И для https:

conf% /opt/bin/kubectl expose rc nginx --port=443 --target-port=443 --service-name=nginx-https -s "http://192.168.0.10:8080"


Где:
  • rc nginx — тип и имя используемого ресурса (rc = Replication Controller)
  • --port — порт на котором будет «слушать» сервис
  • --target-port — порт контейнера на который будет производиться трансляция запросов
  • --service-name — будущее имя сервиса

Проверяем результат:

/opt/bin/kubectl get rc,services -s "http://192.168.0.10:8080"


Вывод

Для проверки запущенности можно зайти на любую из нод и выполнить в консоли:

node% curl http://192.168.3.66


В выводе curl увидим стандартную приветственную страницу nginx. Готово, сервис запущен и доступен.

Запуск сервиса с помощью конфигов


Для этого способа запуска необходимо создать конфиги для Replication Controller'а и service'а. Kubernetes принимает конфиги в форматах yaml и json. Мне ближе yaml поэтому будем использовать его.

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

conf% /opt/bin/kubectl delete services nginx-http nginx-https -s "http://192.168.0.10:8080"
conf% /opt/bin/kubectl stop rc nginx -s "http://192.168.0.10:8080"
Теперь приступим к написанию конфигов.


nginx_rc.yaml
содержимое


Применяем конфиг:

conf% /opt/bin/kubectl create -f ./nginx_rc.yaml -s "http://192.168.0.10:8080"


Проверяем результат:

conf% /opt/bin/kubectl get pods,rc -s "http://192.168.0.10:8080"


Вывод

Был создан Replication Controller с именем nginx и количеством реплик равным 6. Реплики в произвольном порядке запущены на нодах, местоположения каждой pod'ы указано в столбце HOST.

nginx_service.yaml
Содержимое


Можно заметить, что при использовании конфига за одним сервисом могут быть закреплены несколько портов.
Применяем конфиг:

conf% /opt/bin/kubectl create -f ./nginx_service.yaml -s "http://192.168.0.10:8080"


Проверяем результат:

/opt/bin/kubectl get rc,services -s "http://192.168.0.10:8080"


Вывод

Для проверки запущенности можно зайти на любую из нод и выполнить в консоли:

node% curl http://192.168.3.214
node% curl http://12.0.0.5


В выводе curl увидим стандартную приветственную страницу nginx.

Заметки на полях


В качестве заключения хочу описать пару важных моментов, о которые уже пришлось запнуться при проектировании системы. Связаны они были с работой kube-proxy, того самого модуля, который позволяет превратить разрозненный набор элементов в сервис.
PORTAL_NET. Сущность сама по себе интересная, предлагаю ознакомиться с тем, как же это реализовано.
Недолгие раскопки привели меня к осознанию простой, но эффективной модели, заглянем в вывод iptables-save:

-A PREROUTING -j KUBE-PORTALS-CONTAINER
-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
-A OUTPUT -j KUBE-PORTALS-HOST
-A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER
-A POSTROUTING -s 10.0.42.0/24 ! -o docker0 -j MASQUERADE
-A KUBE-PORTALS-CONTAINER -d 10.0.0.2/32 -p tcp -m comment --comment "default/kubernetes:" -m tcp --dport 443 -j REDIRECT --to-ports 46041
-A KUBE-PORTALS-CONTAINER -d 10.0.0.1/32 -p tcp -m comment --comment "default/kubernetes-ro:" -m tcp --dport 80 -j REDIRECT --to-ports 58340
-A KUBE-PORTALS-HOST -d 10.0.0.2/32 -p tcp -m comment --comment "default/kubernetes:" -m tcp --dport 443 -j DNAT --to-destination 172.16.67.69:46041
-A KUBE-PORTALS-HOST -d 10.0.0.1/32 -p tcp -m comment --comment "default/kubernetes-ro:" -m tcp --dport 80 -j DNAT --to-destination 172.16.67.69:58340


Все запросы к IP-адресу сервиса попавшие в iptables заворачиваются на порт на котором слушает kube-proxy. В связи с этим возникает одна проблема: Kubernetes, сам по себе, не решает проблему связи с пользователем. Поэтому придётся решать этот вопрос внешними средствами, например:
  • gcloud — платная разработка от Google
  • bgp — с помощью анонсирования подсетей
  • IPVS
  • и прочие варианты, которых множество

SOURCE IP Так же. при настройке сервиса nginx мне пришлось столкнуться с интересной проблемой. Она выглядела как строчка в мануале: «Using the kube-proxy obscures the source-IP of a packet accessing a Service». Дословно — при использовании kube-proxy скрывает адрес источника пакета, а это значит, что всю обработку построенную на основе source-IP придётся проводить до использования kube-proxy.

Источник статьи: https://habr.com/ru/post/258443/
 
Сверху