[Из песочницы] Основы Kubernetes

В этой публикации я хотел рассказать об интересной, но незаслуженно мало описанной на Хабре, системе управления контейнерами Kubernetes.image

Что такое 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 Подготовка нод Требования для запуска: На всех нодах установлен 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-нода указывается первой # Ноды указываются в формате разделитель — пробел # В качестве пользователя указывается тот пользователь для которого по нодам разложены 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.Пример вывода скрипта Starting cluster using provider: ubuntu … calling verify-prereqs … calling kube-up Deploying master and minion on machine 192.168.0.10 <Список копируемых файлов> [sudo] password to copy files and start node: etcd start/running, process 16384 Connection to 192.168.0.10 closed. Deploying minion on machine 192.168.0.11 <Список копируемых файлов> [sudo] password to copy files and start minion: etcd start/running, process 12325 Connection to 192.168.0.11 closed. Deploying minion on machine 192.168.0.12 <Список копируемых файлов> [sudo] password to copy files and start minion: etcd start/running, process 10217 Connection to 192.168.0.12 closed. Validating master Validating core@192.168.0.10 Validating core@192.168.0.11 Validating core@192.168.0.12 Kubernetes cluster is running. The master is running at: http://192.168.0.10 … calling validate-cluster Found 3 nodes. 1 NAME LABELS STATUS 2 192.168.0.10 Ready 3 192.168.0.11 Ready 4 192.168.0.12 Ready Validate output: NAME STATUS MESSAGE ERROR etcd-0 Healthy {«action»: «get», «node»:{«dir»: true, «nodes»:[{«key»:»/coreos.com», «dir»: true, «modifiedIndex»:11, «createdIndex»:11},{«key»:»/registry», «dir»: true, «modifiedIndex»:5, «createdIndex»:5}], «modifiedIndex»:5, «createdIndex»:5}} nil controller-manager Healthy ok nil scheduler Healthy ok nil Cluster validation succeeded Done, listing cluster services: Kubernetes master is running at http://192.168.0.10:8080 Посмотрим, какие ноды и сервисы присутствуют в новом кластере: 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 192.168.3.2 443/TCP kubernetes-ro component=apiserver, provider=kubernetes 192.168.3.1 80/TCP NAME LABELS STATUS 192.168.0.10 Ready 192.168.0.11 Ready 192.168.0.12 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» Вывод POD IP CONTAINER (S) IMAGE (S) HOST LABELS STATUS CREATED MESSAGE nginx-3gii4 172.16.58.4 192.168.0.11/192.168.0.11 run-container=nginx Running 9 seconds nginx nginx Running 9 seconds nginx-3xudc 172.16.62.6 192.168.0.10/192.168.0.10 run-container=nginx Running 9 seconds nginx nginx Running 8 seconds nginx-igpon 172.16.58.6 192.168.0.11/192.168.0.11 run-container=nginx Running 9 seconds nginx nginx Running 8 seconds nginx-km78j 172.16.58.5 192.168.0.11/192.168.0.11 run-container=nginx Running 9 seconds nginx nginx Running 8 seconds nginx-sjb39 172.16.83.4 192.168.0.12/192.168.0.12 run-container=nginx Running 9 seconds nginx nginx Running 8 seconds nginx-zk1wv 172.16.62.7 192.168.0.10/192.168.0.10 run-container=nginx Running 9 seconds nginx nginx Running 8 seconds CONTROLLER CONTAINER (S) IMAGE (S) SELECTOR REPLICAS nginx nginx nginx run-container=nginx 6 Был создан 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» Вывод 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 192.168.3.2 443/TCP kubernetes-ro component=apiserver, provider=kubernetes 192.168.3.1 80/TCP nginx-http run-container=nginx 192.168.3.66 80/TCP nginx-https run-container=nginx 192.168.3.172 443/TCP Для проверки запущенности можно зайти на любую из нод и выполнить в консоли: 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содержимое 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 192.168.3.2 443/TCP kubernetes-ro component=apiserver, provider=kubernetes 192.168.3.1 80/TCP nginx name=nginx 192.168.3.214 80/TCP 12.0.0.5 443/TCP

Для проверки запущенности можно зайти на любую из нод и выполнить в консоли: 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. В связи с получением инвайта хотел спросить, что вы хотите увидеть в следующих статьях?

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

© Habrahabr.ru