Основы 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

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

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

  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.

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

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   <none>    Ready      3  192.168.0.11   <none>    Ready      4  192.168.0.12   <none>    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   <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" 

Вывод

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   <none>                192.168.3.2     443/TCP kubernetes-ro   component=apiserver,provider=kubernetes   <none>                192.168.3.1     80/TCP nginx-http      <none>                                    run-container=nginx   192.168.3.66    80/TCP nginx-https     <none>                                    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   <none>       192.168.3.2     443/TCP kubernetes-ro   component=apiserver,provider=kubernetes   <none>       192.168.3.1     80/TCP nginx           <none>                                    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.

На этом всё, спасибо за внимание
К сожалению, всю информацию, которую хочется передать, не получается уместить в одну статью.

Использование материалы:

ссылка на оригинал статьи http://habrahabr.ru/post/258443/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *