[Из песочницы] Основы Kubernetes
В этой публикации я хотел рассказать об интересной, но незаслуженно мало описанной на Хабре, системе управления контейнерами Kubernetes.
Что такое 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% подключаемым.Нода 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 Подготовка нод Требования для запуска: На всех нодах установлен docker версии 1.2+ и bridge-utils Все машины связаны друг с другом, необходимости в доступе к интернету нет (в этом случае необходимо использовать локальный docker registry) На все ноды можно войти без ввода логина/пароля, с использованием 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-нода указывается первой
# Ноды указываются в формате
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»
Вывод
CONTROLLER CONTAINER (S) IMAGE (S) SELECTOR REPLICAS
nginx nginx nginx run-container=nginx 6
NAME LABELS SELECTOR IP PORT (S)
kubernetes component=apiserver, provider=kubernetes
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содержимое apiVersion: v1beta3 kind: ReplicationController # Указываем имя ReplicationController metadata: name: nginx-controller spec: # Устанавливаем количество реплик replicas: 6 selector: name: nginx template: metadata: labels: name: nginx spec: containers: #Описываем контейнер — name: nginx image: nginx #Пробрасываем порты ports: — containerPort: 80 — containerPort: 443 livenessProbe: # включаем проверку работоспособности enabled: true type: http # Время ожидания после запуска pod’ы до момента начала проверок initialDelaySeconds: 30 TimeoutSeconds: 5 # http проверка httpGet: path: / port: 80 portals: — destination: nginx Применяем конфиг: 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» Вывод POD IP CONTAINER (S) IMAGE (S) HOST LABELS STATUS CREATED MESSAGE nginx-controller-0wklg 172.16.58.7 192.168.0.11/192.168.0.11 name=nginx Running About a minute nginx nginx Running About a minute nginx-controller-2jynt 172.16.58.8 192.168.0.11/192.168.0.11 name=nginx Running About a minute nginx nginx Running About a minute nginx-controller-8ra6j 172.16.62.8 192.168.0.10/192.168.0.10 name=nginx Running About a minute nginx nginx Running About a minute nginx-controller-avmu8 172.16.58.9 192.168.0.11/192.168.0.11 name=nginx Running About a minute nginx nginx Running About a minute nginx-controller-ddr4y 172.16.83.7 192.168.0.12/192.168.0.12 name=nginx Running About a minute nginx nginx Running About a minute nginx-controller-qb2wb 172.16.83.5 192.168.0.12/192.168.0.12 name=nginx Running About a minute nginx nginx Running About a minute CONTROLLER CONTAINER (S) IMAGE (S) SELECTOR REPLICAS nginx-controller nginx nginx name=nginx 6 Был создан Replication Controller с именем nginx и количеством реплик равным 6. Реплики в произвольном порядке запущены на нодах, местоположения каждой pod’ы указано в столбце HOST.nginx_service.yaml
Содержимое
apiVersion: v1beta3
kind: Service
metadata:
name: nginx
spec:
publicIPs:
— 12.0.0.5 # IP который будет присвоен сервису помимо автоматически назначенного.
ports:
— name: http
port: 80 #порт на котором будет слушать сервис
targetPort: 80 порт контейнера на который будет производиться трансляция запросов
protocol: TCP
— name: https
port: 443
targetPort: 443
protocol: TCP
selector:
name: nginx # поле должно совпадать с аналогичным в конфиге ReplicationController
Можно заметить, что при использовании конфига за одним сервисом могут быть закреплены несколько портов.Применяем конфиг:
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»
Вывод
CONTROLLER CONTAINER (S) IMAGE (S) SELECTOR REPLICAS
nginx-controller nginx nginx name=nginx 6
NAME LABELS SELECTOR IP PORT (S)
kubernetes component=apiserver, provider=kubernetes
Для проверки запущенности можно зайти на любую из нод и выполнить в консоли: 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.На этом всё, спасибо за вниманиеК сожалению, всю информацию, которую хочется передать, не получается уместить в одну статью.
Использование материалы: UPD. В связи с получением инвайта хотел спросить, что вы хотите увидеть в следующих статьях?
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.