5.1 Kubernetes Flashcards
Путь к Kubernetes. Монолит. (из-за чего пришли к Kubernetes)
- Жесткие зависимости затрудняли разработку и делали ее медленнее
- Отсутствие гибкости, из-за чего приложение было тяжело изменять
- Тяжело скалируется: если запросы выросли только к одному модулю, приходилось скалировать приложение целиком
- Медленные релизы и фичи
Микросервисы
Ответом на вызовы, которые создавала разработка приложений в монолитной архитектуре стала архитектура, делающая акцент на микросервисы.
Что нам это дало?
* Гибкость, так как можно было выбирать разные фреймворки/языки/инструменты
- Легко скалируются, потому что каждый модуль можно скалировать отдельно
- Быстрые релизы
Контейнеры и ВМ
Когда мы говорим про микросервисы, предполагается, что каждый из микросервисов мы будем упаковывать в контейнеры. Но изоляция окружений возникла до контейнеров: первоначально для этих целей использовались виртуальные машины (ВМ).
Зачем тогда понадобились контейнеры, если существовала такая концепция, как ВМ? Причина в том что, контейнеры более легковесные и с ними удобнее работать. Как видно на слайде, каждая ВМ включает в себя уровень операционной системы (Guest OS). Иными словами, каждая ВМ - мини-сервер с отдельной ОС. На обслуживание ВМ тратится много ресурсов. В контейнерах слой с ОС всего один (Host OS) и существует на уровне инфраструктуры. Поверх этого слоя работает Docker и оболочка Docker позволяет запускать внутри себя отдельные контейнеры.
Однако, контейнеры гораздо менее изолированы, чем ВМ. При запуске на одном сервере нескольких докер-контейнеров, из одного можно “пролезть” в другой. Об этом следует помнить в целях поддержки безопасности системы.
Kubernetes - для чего и что умеет?
Мы начали запускать все в контейнерах, и возник вопрос, как мы теперь будем управлять сотнями контейнеров? Приложение может состоять из тысяч функций, которые упакованы в свой докер-контейнер. Мы должны настроить между ними сетевые правила, следить за тем, чтобы контейнер был живым. Это достаточно большой скоп задач, их можно решать, написав свою оболочку для управления всеми контейнерами, но проще всего использовать Kubernetes. Что он умеет?
- Оркестрация контейнезированных приложений
- Управление жизненным циклом приложений
- Организация инфраструктуры для работы с приложениями
Основные концепции Kubernetes. At large scale.
Kubernetes - кластерная система, позволяющая внутри себя запускать и управлять приложениями.
1) Центральная часть - master node.
2) Рабочая нагрузка запускается на worker node. На них может работать, к примеру, Spark.
3) Пользователи - users Взаимодействие происходит путем API/CLI/UI.
Основные абстракции Kubernetes для DE
1) Pod
2) ReplicaSet
3) Deployment
4) Services
5) ClusterIP
6) NodePort
7) Loadbalancer
8) Ingress
Pod
- Атомарный объект или наименьший юнит работы в Kubernetes
- Pod-ы состоят из одного или более контейнеров, которые делят вольюмы и сети
Когда мы запускаем контейнер внутри Kubernetes, мы запускаем его, используя абстракцию Pod. Если внутри Pod будет несколько контейнеров, то они будут делить между собой диск и сетевые настройки.
Pod описывается через YAML:
* kind - описывает абстракцию, которую мы запускаем
* metadata - указывается имя (name) и уровни (labels)
* spec - указываются контейнеры, которые будут запускаться; в данном примере будет запущен один контейнер, который будет использовать образ nginx (image) с именем контейнера nginx (name)
Важно знать, что место на диске, которое доступно Pod’у для работы - не персистентно. Это означает, что если Pod перезапустится, что является нормальной ситуацией, все данные, которые были внутри Pod’a пропадут. Таким образом, их нельзя хранить внутри Pod’a.
Ресурсы одного pod-а деплоятся вместе
Pod-ы не используются для хранения данных (эфемерны)
ReplicaSet
ReplicaSet - абстракция более высокого порядка, чем Pod.
- Основной метод управления репликами Pod и их жизненным циклом
- Обеспечивает необходимое количество запущенных реплик
Pod’ы не запускаются напрямую. YAML, который был приведен выше в качестве примера, можно использовать для отладки, но обычно приложения не запускаются таким образом, так как если контейнер, запущенный внутри Pod, “умер”, Kubernetes ничего с этим делать не будет. Для перезапуска приложений/Pod’ов, управления числом реплик Pod’ов используется ReplicaSet.
ReplicaSet следит за тем, чтобы обеспечивать нужное количество Pod’ов в живом состоянии. Предположим, что нам нужно поддерживать 2 Pod’а в ReplicaSet в запущенном состоянии. Если один “умрет”, ReplicaSet автоматически запустит новый Pod с той же самой спецификацией, которая была описана в YAML.
Как выглядит YAML для ReplicaSet:
* replicas: требуемое количество экземпляров Pod
* selector: определяет все Pod’ы, управляемые этим ReplicaSet
Выполнив команду $ kubectl get pods (взаимодействие с Kubernetes посредством интерфейса командной строки), мы увидим, что запущено 3 Pod’a, с именем rs-example-xxxxx (xxxxx - случайно сгенерированная часть имени).
Deployment
Deployment - абстракция еще более высокого порядка, основной контроллер. При размещении приложения/рабочей нагрузки, чаще всего работа происходит именно с Deployment.
- Основной контроллер для управления Pods
- Управляют ReplicaSet
- Предоставляют возможность управления обновлениями и функциональность rollback’a.
YAML Deployment выглядит следующим образом:
- strategy: описывает метод обновления Pods на основе type
- recreate: все существующие поды убиваются до запуска новых
- rollingUpdate: циклическое обновление Pods на основе maxSurge и maxUnavailable
- maxSurge: определяет количество дополнительных реплик
- maxUnavailable: количество возможно недоступных реплик
- revisionHistoryLimit: сколько историй обновлений будет храниться в памяти
- template: задается шаблон Pod’a
Services
Services - абстракция, задающая правила сетевого доступа к Pods, описывается посредством YAML.
- Универсальный метод доступа к приложениям в Pods
- Внутренний балансировщик для Pods
- Имеет DNS имя, привязанное к namespace
Services используются для взаимодействия приложения с внешней средой, решают задачу пропуска трафика в приложение.
Существуют 3 типа Services:
1) ClusterIP
2) NodePort
3) Loadbalancer
1) ClusterIP открывает доступ только по внутреннему виртуальному IP, доступному только во внутренней сети кластера. Чаще всего ClusterIP используют для взаимодействия приложений, запущенных внутри Kubernetes.
Worker nodes A/B/C (Host A/B/C) имеют kube-proxy, который отвечает за настройку сетевых правил.
2) NodePort открывает во внешний доступ один из портов (выбранный явно или случайно из пула 30000-32767) на каждой node. В работе дата инженера используется редко (или вообще не используется).
Используется несколько наборов воркеров (A/B/C), на которых запущены Pods, доступ к которым можно будет получить, обратившись к порту, который был задан при создании.
3) Loadbalancer - “правильный” способ открыть доступ к приложению из внешней сети.
Для того, чтобы Loadbalancer функционировал, он должен интегрироваться с неким внешним Loadbalancer, который предоставляет облако или среда, в которой запущен Kubernetes. Если работа будет производиться с Kubernetes as a service в облаках, то вам не нужно будет думать о том, как реализован Loadbalancer и кто будет его предоставлять. Требуется лишь создать подобный YAML и облако самостоятельно выделит ВМ нужного размера, настроит правила, предоставит Loadbalancer для доступа к вашим приложениям.
Loadbalancer расположен на границе между внутренней сетью Kubernetes и внешней сетью, весь трафик идет через него.
Ingress
Ingress используется для того, чтобы запускать внешний трафик к приложениям, созданным внутри Kubernetes. В отличие от Loadbalancer, который способен работать только с одним приложением (или частью одного большого), Ingress предоставляет доступ сразу к нескольким приложениям с единого IP. Он так же предоставляется автоматически облачным провайдером.
- Используется как внешний gateway для доступа к приложению извне
- Поддерживает L7 policy
- IngressController – аналог nginx
- Ingress – аналог vhost
ConfigMaps
При работе с приложениями в Kubernetes могут возникнуть задачи сохранить какие-либо настройки. При наличии одного докер-контейнера, его можно запускать с разными параметрами или внутрь этого докер-контейнера пробрасывать разные переменные окружения, на основе которых он будет вести себя по-разному.
В качестве примера предположим, что у нас есть Spark приложение, упакованное в докер-контейнер, которое принимает на вход параметр с путем к данным (например, имя S3 бакета). Spark приложение использует его, чтобы прочитать данные и далее будет использована еще одна переменная - имя бакента, куда данные будут записаны после обработки. Для того, чтобы не “хардкодить” значения с именами бакетов, мы создадим Spark приложение, упакуем его в контейнер и оно будет принимать на вход эти 2 параметра входных и выходных данных. Для этого и используется следующая абстракция - ConfigMap.
ConfigMaps представляет собой хранилище параметров типа key/value (параметр data).
Secrets
Secrets - хранят данные в зашифрованном виде.
- Кодируют данные в base64
- Могут быть зашифрованы в etcd
- Используются для хранения паролей, сертификатов и других чувствительных данных
Env
ConfigMaps и Secrets нужны нам были для того, чтобы задавать переменные окружения.
В данном YAML мы видим пример абстракции Job, которая предоставляет функциональность разового запуска некой рабочей контейнезированной нагрузки внутри Kubernetes. Данный контейнер выведет переменную окружения CITY при своем запуске. Возьмет он ее из ConfigMap, который был создан ранее. Команда printenv CITY выведет значение данных, хранящихся по ключу CITY внутри ConfigMap.
Аналогичным образом работает и абстракция Secret в данном YAML. Команда printenv USERNAME выведет переменную окружения USERNAME, которая будет взята из Secret с именем secret-env-example.
Value from Volume
Можно задавать переменные окружения через подключения Volume.
Контейнер будет примонтирован по адресу /mysecret и в нем будут храниться данные из нашего Secret. Чтобы получить доступ, запущенный контейнер должен будет сходить в директорию /mysecret, внутри которой расположены файлы. Каждый файл имеет имя, которое будет совпадать со значениеми ключа внутри Secret.
CronJob
CronJob предназначен для запуска контейнеров по расписанию.
- Расширение Job, для выполнения конкретного расписания
- schedule: cron-расписание
Best practice для дата инженера использовать, например, Airflow, который будет запускать по расписанию внутри Kubernetes приложения, вместо CronJob.
Как хранить персистентные данные внутри Kubernetes
Хранилище внутри Pod’a эфемерно - это значит, что данные с запущенного приложения сохраняются в Pod’е и будут потеряны при его перезапуске.
Чтобы не потерять данные, в Kuber’e для этого предназначены абстракции PersistentVolume и PersistentVolumeClaim.
В Pod’e у нас есть раздел volumes: список объектов volume, присоединенных к Pod и volumeMounts: список параметров монтирования.
Здесь показан механизм монтирования постоянных PersistentVolumes. В Pod’е мы указываем PersistentVolumeClaim (PCV) - запрос к системе на предоставления диска для хранения постоянных данных (которые не пропадут после перезапуска).
В разделе PCV (PersistentVolumeClaim) мы указываем его имя, необходимый нам объем памяти и какого класса (class) мы хотим получить диск.
На основе PersistentVolumeClaim ваш облачный провайдер выделит из файлового хранилища PVC необходимого объема и типа.
Обратите внимание на:
- capacity.storage: общий объем запрашиваемого storage;
- accessModes: список поддерживаемых режимов подключения:
- ReadWriteOnce
- ReadWriteMany
- ReadOnlyMany
В облаке очень часто вам не нужно создавать отдельно PersistentVolume, вы создаете только запрос - абстракцию PersistentVolumeClaim. На ее основе облако выделит и создаст PersistentVolume требуемого объема и типа.
Важный момент, мы задаем:
- persistentVolumeReclaimPolicy - это поведение PVC, которые были удалены;
- Retain - остается;
- Delete - удаляется методом storage provider’a
- storageClassName: имя класса storage.
Вот так выглядит PersistentVolumeClaim.
Мы задаем тип, указываем имя, указываем storageClassName и указали в каком режиме мы будем его монтировать - ReadWriteOnce.
Если мы создадим такой вот PersistentVolumeClaim, то нам выделят из облака диск запрашиваемого объема. Этот диск будет связан с PVC, а он в свою очередь будет примонтирован к Pod’у, контейнеру и приложению.
Фазы PersistentVolume:
Available (доступный)
Bound (подключенный)
Released (изданный)
Failed (поврежденный)
Архитектура Kubernetes
Kubernetes - это кластерная система, которая состоит из Ctrl Plane (master Node) и Worker Node, на которых размещается наша нагрузка.
Ctrl Plane (master Node)
В Ctrl Plane (master Node) важные компоненты, это:
- etcd
- controller manager
- scheduler
- kube apiserver - можно считать самым важным, т.к. через него проходят все взаимодействия.
- kubectl - утилита командной строки, с помощью которой пользователь взаимодействует с кластером Kuber’a.
kube apiserver
- внешний REST API интерфейс;
- все клиенты и компоненты кластера взаимодействуют друг с другом через kube apiserver;
- выступает как единая точка входа в кластер, обрабатывая запросы на аутентификацию, авторизацию, валидацию запросов, изменения и контроль.
etcd
- выступает в качестве кластерного хранилища данных;
- обеспечивает надежное консистентное, высокодоступное key-value хранилище, в котором хранится вся конфигурация кластера;
- хранит объекты и информацию о конфигурации;
- использует кворумный алгоритм Raft.
controller manager
- реализует логику за контроллерами Kubernetes;
- это он говорит: “Эй, мне нужно поднять еще несколько подов”
scheduler
- оценивает требования приложений и пытается их разместить на подходящем сервере;
- оперирует лейблами, требованиями, лимитами по ресурсам, политиками affinity и т.д.
Архитектура Nodes
Здесь у нас три важных компонента:
kubelet - агент, работающий на каждом узле и управляющий состоянием Pods. Он опрашивает kube API на предмет подов, которые должен запустить.
kube-proxy - управляет сетевыми правилами на каждом узле, обеспечивает connection-forwarding и load-balancing для сервисов Kubernetes. В большинстве случаев используется iptables (в современных кластерах можно использовать ipvs)
container runtime - докер runtime, отвечает за запуск приложений, упакованных в контейнер.
Big Data + K8S
У нас в облаках есть возможность выстроить комплексную платформу для работы с данными, которая будет полным аналогом традиционного подхода.
Однако, если вы хотите оптимально использовать возможности облаков и не терять в качестве, устойчивости, рекомендуем использовать другую архитектуру. В рамках этого подхода, данные которые мы храним в Hadoop/HDFS, мы храним в S3.
Преимущества хранения данных в S3:
- упрощение процесса хранения и передачи данных с отказоустойчивостью 99,9999;
- экономия средств: S3 дешевле HDFS;
- разделение Storage и Compute-мощностей;
- независимый скейлинг Storage и Compute
Spark + K8S
Мы сложили данные в S3, но само S3 не умеет обрабатывать данные. Здесь в дело вступает Spark, который мы запускаем в Kubernetes. Он нам позволяет решать задачу обработки данных.
Spark + K8S это:
- изоляция сред (контейнеризация и depency management);
- гибкое масштабирование;
- Kubernetes Operator for Spark - Kubernetes native way;
- Spark Operator позволяет решить проблемы с доступом к логам, получением текущего статуса и состояния приложения.
Presto + Hive Metastore + K8S
Мы обработали данные, и теперь нам необходимо предоставить SQL-доступ. Для этого часто используется Presto. Связка Presto + Hive Metastore + K8S:
- решает задачу ad-hoc аналитики;
- позволяет получить доступ к данным в различных системах и источниках;
- предоставляет возможность горизонтального масштабирования, масштабирования воркеров на основе HPA;
- Presto K8S Operator от Starbust или Helm chart.
Superset + K8S
Далее нам необходимо решить задачу с BI и визуализацией. Это Superset + K8S:
- cloud-native;
- большой выбор встроенных инструментов для создания дашбордов;
- его можно использовать как SQL IDE;
- есть Helm chart.
Airflow + K8S
- Kubernetes Executor – создаем воркеры по мере необходимости, экономим
ресурсы во время простоя;
- Kubernetes Executor – необходимо принимать во внимание время на старт подов;
- KubernetesPodOperator – может работать с CeleryExecutor, дает больше гибкости;
- Храним логи в S3.
Amundsen + K8S
- Data Discovery Platform;
- Повышает продуктивность пользователей платформы;
- Помогает демократизировать доступ к данным, share tribal knowledge;
- Интеграция с Airflow;
- Есть Helm chart
JupyterHub + K8S
- Проведение индивидуальных экспериментов;
- Изолированность окружений, нагрузок, экспериментов;
- Эффективное использование ресурсов, автоматическое выключение простаивающих;
- Подробная инструкция и Helm chart.
Kuberflow или Mlfow
- База данных ML-экспериментов и моделей с кодом, параметрами, результатами;
- Удобный вывод ML-моделей в прод;
- Ускоряет работу DS и DE, повышает эффективность, позволяет получить полную
отдачу от результатов DS;
- Kubeflow — Kubernetes native, включает в себя Jupyter Notebooks;
- Mlflow — более стабильный, почти не дружит с Kubernetes.
Data + Kubernetes (основные инструменты на разных этапах работы с данными + K8S)
- S3 – отвечает за хранение;
- Spark – “молотилка” данных;
- Presto + Hive Metastore – ad hoc аналитика, схемы данных;
- Superset – BI, data exploration, SQL IDE;
- Airflow – оркестратор с возможностью масштабирования в k8s;
- Amundsen – data discovery platform;
- JupyterHub – проведение изолированных экспериментов;
- Kubeflow, MLflow – задачи MLOps, трекинг моделей, экспериментов.
Tarantool + K8S
Tarantool - это сверхбыстрая база
Упрощает развертывание и эксплуатацию кластерных приложений
- Сокращает Time-to-market решений;
- Есть K8s-оператор;
- Решает задачу автоматического добавления узлов, решардинга