Русский
Русский
English
Статистика
Реклама

Kubernetes

Как мы собираем общие сведения о парке из Kubernetes-кластеров

16.06.2021 10:13:29 | Автор: admin

Имея в обслуживании большой (более 150) парк Kubernetes-кластеров, всегда хотелось иметь удобное представление их общего состояния, в том числе и для того, чтобы поддерживать их гомогенными. В первую очередь нас интересовали следующие данные:

  • версия Kubernetes чтобы все кластеры были on the edge;

  • версия Deckhouse (наша Kubernetes-платформа) для лучшего планирования релизных циклов;

  • количество узлов с разбивкой по типам (управляющие, виртуальные и статические) для отдела продаж;

  • количество ресурсов (CPU, memory) на управляющих узлах;

  • на какой инфраструктуре запущен кластер (виртуальные облачные ресурсы, bare metal или гибридная конфигурация);

  • какой облачный провайдер используется.

И вот каким был наш путь к тому, чтобы превратить эту потребность в наглядную реальность

Истоки и проверка концепции

В какой-то момент времени мы стали использовать Terraform для раскатки инфраструктуры в облака и вопрос отслеживания соответствия желаемых конфигураций реальности встал еще острее. Мы храним Terraform state в самих кластерах и проверку соответствия их с реальностью проверяет отдельно написанный Prometheus exporter. Хотя ранее у нас уже была информация для реагирования на изменения (через соответствующие алерты в системе управления инцидентами), хотелось ещё иметь полное представление о ситуации в отдельной аналитической системе.

Итак, изначально в качестве PoC был несложный Bash-скрипт, которым мы вручную время от времени собирали интересующие данные с K8s-кластеров по SSH. Он выглядел примерно так:

((kubectl -n d8-system get deploy/deckhouse -o json | jq .spec.template.spec.containers[0].image -r | cut -d: -f2 | tr "\n" ";") &&(kubectl get nodes -l node-role.kubernetes.io/master="" -o name | wc -l | tr "\n" ";") &&(kubectl get nodes -l node-role.kubernetes.io/master="" -o json | jq "if .items | length > 0 then .items[].status.capacity.cpu else 0 end" -r | sort -n | head -n 1 | tr "\n" ";") &&(kubectl get nodes -l node-role.kubernetes.io/master="" -o json | jq "if .items | length > 0 then .items[].status.capacity.memory else \"0Ki\" end | rtrimstr(\"Ki\") | tonumber/1000000 | floor" | sort -n | head -n 1 | tr "\n" ";") &&(kubectl version -o json | jq .serverVersion.gitVersion -r | tr "\n" ";") &&(kubectl get nodes -o wide | grep -v VERSION | awk "{print \$5}" | sort -n | head -n 1 | tr "\n" ";") &&echo "") | tee res.csvsed -i '1ideckhouse_version;mastersCount;masterMinCPU;masterMinRAM;controlPlaneVersion;minimalKubeletVersion' res.csv

(Здесь приведен лишь фрагмент для демонстрации общей идеи.)

Однако количество клиентов и кластеров росло стало ясно, что дальше так жить нельзя. Мы ведь инженеры, поэтому всё, что может быть автоматизировано, должно быть автоматизировано.

Так начался наш путь разработки волшебного агента для кластеров, который бы:

  • собирал желаемую информацию,

  • агрегировал ее,

  • отправлял в какое-то централизованное хранилище.

а заодно соответствовал каноном высокой доступности и cloud native.

Этот путь дал начало истории модуля в Kubernetes-платформе Deckhouse, развёрнутой на всех наших кластерах, и сопутствующего ему хранилища.

Реализация

Хуки на shell-operator

В первой итерации источником данных в клиентских кластерах служили Kubernetes-ресурсы, параметры из ConfigMap/Deckhouse, версия образа Deckhouse и версия control-plane из вывода kubectl version. Для соответствующей реализации лучше всего подходил shell-operator.

Были написаны хуки (да, снова на Bash) с подписками на ресурсы и организована передача внутренних values. По результатам работы этих хуков мы получали список желаемых Prometheus-метрик (их экспорт поддерживается в shell-operator из коробки).

Вот пример хука, генерирующего метрики из переменных окружения, он прост и понятен:

#!/bin/bash -efor f in $(find /frameworks/shell/ -type f -iname "*.sh"); do  source $fdonefunction __config__() {  cat << EOF    configVersion: v1    onStartup: 20EOF}function __main__() {  echo '  {    "name": "metrics_prefix_cluster_info",    "set": '$(date +%s)',    "labels": {      "project": "'$PROJECT'",      "cluster": "'$CLUSTER'",      "release_channel": "'$RELEASE_CHANNEL'",      "cloud_provider": "'$CLOUD_PROVIDER'",      "control_plane_version": "'$CONTROL_PLANE_VERSION'",      "deckhouse_version": "'$DECKHOUSE_VERSION'"    }  }' | jq -rc >> $METRICS_PATH}hook::run "$@"

Отдельно хочу обратить ваше внимание на значение метрики (параметр set). Изначально мы писали туда просто 1, но возник резонный вопрос: Как потом получить через PromQL именно последние, свежие labels, включая те series, которые уже две недели не отправлялась? Например, в том же MetricsQL от VictoriaMetrics для этого есть специальная функция last_over_time. Оказалось, достаточно в значение метрики отправлять текущий timestamp число, которое постоянно инкрементируется во времени. Вуаля! Теперь стандартная функция агрегации max_over_time из Prometheus выдаст нам самые последние значения labels по всем series, которые приходили хоть раз в запрошенном периоде.

Чуть позже к источникам данных добавились метрики из Prometheus в кластерах. Для их получения был написан еще один хук, который через curl ходил в кластерный Prometheus, подготавливал полученные данные и экспортировал их в виде метрик.

Чтобы вписаться в парадигму cloud-native и обеспечить HA агента, мы запустили его в несколько реплик на управляющих узлах кластера.

Grafana Agent

Оставалось как-то донести полученные метрики до централизованного хранилища, а также обеспечить их кэширование на стороне кластера на случай временной недоступности хранилища, связанной с его обслуживанием или модернизацией.

Выбор пал на разработку Grafana Labs, а именно Grafana Agent. Он умеет делать scrape метрик с endpointов, отправлять их по протоколу Prometheus remote write, а также (что немаловажно!) ведет свой WAL на случай недоступности принимающей стороны.

Задумано сделано: и вот приложение из shell-operator и sidecarом с grafana-agent уже способно собирать необходимые данные и гарантировать их поступление в центральное хранилище.

Конфигурация агента делается довольно просто благо, все параметры подробно описаны в документации. Вот пример нашего итогового конфига:

server:  log_level: info  http_listen_port: 8080prometheus:  wal_directory: /data/agent/wal  global:    scrape_interval: 5m  configs:  - name: agent    host_filter: false    max_wal_time: 360h    scrape_configs:    - job_name: 'agent'      params:        module: [http_2xx]      static_configs:      - targets:        - 127.0.0.1:9115      metric_relabel_configs:      - source_labels: [__name__]        regex: 'metrics_prefix_.+'      - source_labels: [job]        action: keep        target_label: cluster_uuid        replacement: {{ .Values.clusterUUID }}      - regex: hook|instance        action: labeldrop    remote_write:    - url: {{ .Values.promscale.url }}      basic_auth:        username: {{ .Values.promscale.basic_auth.username }}        password: {{ .Values.promscale.basic_auth.password }}

Пояснения:

  • Директория /data это volumeMount для хранения WAL-файлов;

  • Values.clusterUUID уникальный идентификатор кластера, по которому мы его идентифицируем при формировании отчетов;

  • Values.promscale содержит информацию об endpoint и параметрах авторизации для remote_write.

Хранилище

Разобравшись с отправкой метрик, необходимо было решить что-то с централизованным хранилищем.

Ранее у нас были попытки подружиться с Cortex, но, по всей видимости, на тот момент инженерная мысль его разработчиков не достигла кульминации: пугающая обвязка вокруг него в виде Cassandra и других компонентов не дала нам успеха. Поэтому мы данную затею отложили и, памятуя о прошлом опыте, использовать его не стали.

NB. Справедливости ради, хочется отметить, что на данный момент Cortex выглядит уже вполне жизнеспособным, оформленным как конечный продукт. Очень вероятно, что через какое-то время вернемся к нему и будем использовать. Уж очень сладко при мысли о generic S3 как хранилище для БД: никаких плясок с репликами, бэкапами и растущим количеством данных

К тому времени у нас была достаточная экспертиза по PostgreSQL и мы выбрали Promscale как бэкенд. Он поддерживает получение данных по протоколу remote-write, а нам казалось, что получать данные используя pure SQL это просто, быстро и незатратно: сделал VIEWхи и обновляй их, да выгружай в CSV.

Разработчики Promscale предоставляют готовый Docker-образ, включающий в себя PostgreSQL со всеми необходимыми extensions. Promscale использует расширение TimescaleDB, которое, судя по отзывам, хорошо справляется как с большим количеством данных, так и позволяет скейлиться горизонтально. Воспользовались этим образом, задеплоили connector данные полетели!

Далее был написан скрипт, создающий необходимые views, обновляющий их время от времени и выдающий на выход желаемый CSV-файл. На тестовом парке dev-кластеров всё работало отлично: мы обрадовались и выкатили отправку данных со всех кластеров.

Но с хранилищем всё не так просто

Первую неделю всё было отлично: данные идут, отчет генерируется. Сначала время работы скрипта составляло около 10 минут, однако с ростом количества данных это время увеличилось до получаса, а однажды и вовсе достигло 1 часа. Смекнув, что что-то тут не так, мы пошли разбираться.

Как оказалось, ходить в таблицы базы данных мимо магических оберток, предоставляемых Promscale (в виде своих функций и views, опирающихся в свою очередь на функции TimescaleDB), невероятно неэффективно.

Было решено перестать ковыряться в потрохах данных и положиться на мощь и наработки разработчиков Promscale. Ведь их connector может не только складывать данные в базу через remote-write, но и позволяет получать их привычным для Prometheus способом через PromQL.

Одним Bashем уже было не обойтись мы окунулись в мир аналитики данных с Python. К нашему счастью, в сообществе уже были готовы необходимые инструменты и для походов с PromQL! Речь про замечательный модуль prometheus-api-client, который поддерживает представление полученных данных в формате Pandas DataFrame.

В этот момент еще сильнее повеяло взрослыми инструментами из мира аналитики данных Мотивированные на пощупать интересное и доселе неизведанное, мы двинулись в этом направлении и не прогадали. Лаконичность и простота верчения этой кучей данных через Pandas DataFrame доставила массу позитивных эмоций. И по сей день поддержка полученной кодовой базы, добавление новых параметров и всевозможные правки отображения финальных данных воспринимаются как праздник программиста и не требуют большого количества времени.

Изначально мы выбрали период скрейпинга данных grafana-agentом равным одной минуте, что отразилось на огромных аппетитах конечной БД в диске: ~800 мегабайт данных в день. Это, конечно, не так много в масштабах одного кластера (~5 мегабайт), но когда кластеров много суммарный объём начинает пугать. Решение оказалось простым: увеличили период scrapeа в конфигах grafana-agentов до одного раза в 5 минут. Прогнозируемый суммарный объем хранимых данных с retentionом в 5 лет уменьшился с 1,5 Тб до 300 Гб, что, согласитесь, уже выглядит не так ужасающе.

Некоторый профит от выбора PostgreSQL как конечного хранилища мы уже получили: для успешного переезда хранилища в финальный production-кластер достаточно было отреплицировать базу. Единственное текущий недостаток пока не получилось самостоятельно собрать свой кастомный PostgreSQL с необходимыми расширениями. После пары неудачных попыток мы остались на готовом образе от разработчиков Promscale.

Получившаяся архитектура выглядит так:

Итоги и перспективы

Мы смотрим в будущее и планируем отказаться от отчётов в формате CSV в пользу красивого интерфейса собственной разработки. А по мере разработки собственной биллинговой системы начнём отгружать данные и туда для нужд отдела продаж и развития бизнеса. Но даже те CSV, что мы получили сейчас, уже сильно упрощают рабочие процессы всего Фланта.

А пока не дошли руки до фронтенда, мы сделали dashboard для Grafana (почему бы и нет, раз всё в стандартах Prometheus?..). Вот как это выглядит:

Общая сводная таблица по кластерам с Terraform-состояниямиОбщая сводная таблица по кластерам с Terraform-состояниямиРаспределение кластеров по облачным провайдерамРаспределение кластеров по облачным провайдерамРазбивка по используемым Inlet в Nginx Ingress-контроллерахРазбивка по используемым Inlet в Nginx Ingress-контроллерахКоличество podов Nginx Ingress-контроллеров с разбивкой по версиямКоличество podов Nginx Ingress-контроллеров с разбивкой по версиям

Впереди нас ждет продолжение пути автоматизации всего и вся с уменьшением необходимости ручных действий. В числе первых горячо ожидаемых плюшек переход к автоматическому применению изменений конфигураций Terraform, если таковые не подразумевают удаление каких-либо ресурсов (не являются деструктивными для кластера).

P.S.

Читайте также в нашем блоге:

Подробнее..

Интервью с Марселем Ибраевым о распиле монолита или Успех распила монолита грамотный менеджмент

17.06.2021 20:21:19 | Автор: admin
Я как-то видел, когда в команду разработки закинули задачу распилить монолит. И всё. Люди должны были работать в два раза больше это ужасно.
Когда поступает похожий запрос, важно не наворотить дел и понять, как избежать новых трудностей. Об этом рассказал Марсель Ибраев, технический директор Слёрма.

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



Давай представим, что перед нами стоит задача распилить монолит.

Начнем с причин и решим, действительно ли вообще нужно что-то распиливать. Я сталкивался с кейсом, когда монолит пилили просто потому, что один разработчик назвал это решением всех проблем, другие идею подхватили, а вот детально никто ничего не проработал. И вот в этом случае проблема была не столько в монолите, сколько в плохом коде и неверных подходах.

Так, и какими дальше будут наши действия?

Первое, что нужно сделать, это организовать грамотный менеджмент. Начните с выбора технического лидера человека, обладающего должными компетенциями и достаточным количеством свободных ресурсов. Это может быть и текущий технический лидер компании, например, CTO или тимлид. Но важно, чтобы у него хватило ресурса и времени на курирование задачи по распилу.

Далее он вместе с командой проводит ретроспективу, где выясняет, как они дошли до жизни такой, какие проблемы сейчас актуальны и почему они возникли. Они могут быть следствием логичного развития проекта: кодовая база сильно выросла, появилось много зависимостей, выкатка релизов производится недостаточно часто. Или это следствие неправильных подходов и плохого кода, что бывает гораздо чаще, на мой взгляд,. Честный ответ на вопросы упростит всем жизнь в будущем.

Есть мнение, что монолит это плохо и нужно сразу делать микросервис. Это так?

Нет. Я в корне не согласен с этим. Монолит и микросервис это две разные архитектуры под две разные задачи. Например, если проект представляет собой магазин носков, который ежедневно посещают пять человек и в будущем их будет не сильно больше, то, скорее всего, лучше делать монолит. Так будет быстрее и проще. Главное не упустить момент, когда требования к проекту и подходам сильно изменились, а ваш магазин стал вторым Amazon. :)

А есть ли какое-то решение в обход распила монолита?
Допустим, мы мигрируем в облако, и нам требуется при миграции подготовить свое приложение к Кубу, а именно распилить на сервисы.


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

То есть не получится запихать legacy в контейнер и сказать, вот, пожалуйста, влезло.

Нет. Такая идея может возникнуть. Например, кто-то написал какое-нибудь древнее legacy, которое все боятся трогать. Допустим, этот человек пару лет назад уволился, а сейчас кто-нибудь предлагает взять и запихать его код в контейнер со словами: Пусть это там само работает. Звучит дико, согласен. И я бы не поверил, что такое может быть, если бы не увидел своими глазами.

Ты говоришь, что если мы собираемся пилить монолит, то нам нужен человек, который будет курировать процесс работы, прикинет затраты.
А вот что делать, если ни у кого нет опыта в этом?


Нужно дополнительно обучаться или же брать человека с необходимым бэкграундом. Три составляющей успешного распила: компетентный лидер, компетентные разработчики и компетентная инфраструктурная команда. И лучше не идти в этот процесс, если хоть где-то мы проседаем или в перспективе не доберем компетенций.

Если компания всю жизнь разрабатывала на монолите, то, вполне возможно, что у разработчиков не будет опыта разработки микросервисной архитектуры. Это может быть неочевидно, так как сам по себе разработчик может быть хорошим, но это плохо для компании с точки зрения выполнения задачи. Подходы в монолитной и микросервисной архитектуре различны. Даже хороший монолитчик с большим стажем, который всю жизнь делает монолиты и даже слышал что-то про 12 факторов, не сможет хорошо выполнить работу с микросервисами именно из-за разницы в подходах. Но мое мнение, что успех работы на 80% зависит от менеджмента. Без человека, управляющего процессом, ничего путного не выйдет.

Почему?

Давай лучше приведу пример. Я как-то видел, когда в команду разработки закинули задачу распилить монолит. Просто поставили тикет, как будто разработать очередную фичу. И всё. Как команда должна решить этот вопрос, с какой стороны подступиться, непонятно. В итоге люди должны были работать в два раза больше это ужасно. Ну и положительного результата там тоже не случилось. Поэтому нужен человек, который возьмет на себя планирование. Это не должны быть детальные планы вплоть до дней и часов, это должны быть ключевые пойнты.

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

Есть какие-то выходы из этой ситуации?

Это, скорее всего, будут кадровые решения и набор компромиссов. Можно нанять новых сотрудников или отправить на учебу имеющихся, как я уже сказал ранее. Можно какие-то кадровые перестановки сделать. Также стоит учесть, что теперь команде нужен человек, который будет понимать инфраструктуру будущего проекта. Идея просто засунуть всё в Kubernetes кажется легкой, конечно, но не стоит забывать что Kubernetes это дополнительная абстракция в вашей инфраструктуре. Да, она позволяет решать ряд операционных задач проще, но по факту делает жизнь админов и поддержку проекта сложнее.

Ты сказал, что людей нужно обучить. Можно ли упаковать информацию по распилу в какое-то единое знание? Будь то гайд или курс для разработчиков или админов.

Думаю можно. Хоть каждый случай особенный и неповторимый, какие-то основные паттерны от кейса к кейсу будут похожи. Причины распила будут примерно одинаковые, языки, на которых всё будет происходить, будут примерно одинаковые и шишки, которые люди набьют, тоже будут примерно одинаковые. Образовательный продукт интересен тем, что тема горяча и процесс распила монолита похож на ремонт он не заканчивается никогда.

Оно же как бывает: бизнес требует фичи, а разработчики пилят монолит. Получается так, что из-за срочности реализации фич эти фичи добавляются в монолит. Причем добавляются они быстрее, чем монолит пилится. И это легко может превратиться в бесконечный цикл. Без правильного менеджмента никуда, как я уже не единожды это проговорил. Если говорить об образовательном продукте, то это, наверное, ряд лучших практик технических, инфраструктурных и управленческих.

Проекты бывают разные. У кого-то интернет-магазин, у кого-то платформа для торгов. Отличаются ли аспекты распила для них?

Подход плюс-минус будет одним и тем же. Главное, чтобы у вас был четкий план работы. Но тут важно отметить, что при этом нужно оставаться гибкими. Например, приходит к тебе бизнес и говорит, что в ближайшие две недели будет распродажа, поэтому прод не нужно трогать. Важно не отвечать на подобное жестким нет и вообще-то, у нас в эту неделю переезд каталога. Важно найти компромисс и понимать, что интересы бизнеса первичны. Распил монолита, как уже было сказано, это большая задача и большой менеджерский вызов.

Копнём про гибкость глубже. Распил какой-то части отодвигается на месяц, как тогда быть?

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

Нам будет выгоднее отложить эту задачу на месяц или нанять кого-то, кто будет заниматься задачей прямо сейчас? Нам будет выгоднее пилить фичу прямо сейчас или можно отложить на неделю и потом сразу релизить эту фичу в микросервис? Нужно всегда отслеживать процесс работы, корректировать его и предлагать эффективные решения. В этом и заключается гибкость.

Ты несколько раз упомянул про фичи, которые прилетают от бизнеса и которые нужно добавлять в монолит. Получается, нужно добавлять их и в распиленные микросервисы.
Я правильно понимаю, что тут речь идет об обратной совместимости с монолитом, когда фичи должны быть и там, и там?


Я встречал разные имплементации. В одном из проектов нам удалось договориться с бизнесом о том, что мы новые фичи сразу пилим в микросервисы. У нас был некий центральный балансировщик, который распределял трафик на монолитную часть по одним урлам, а по другим отправлял его в Куб. По итогу мы сошлись на том, что все новые фичи стали делать сразу в Кубе. Но так договориться получится не всегда, поэтому оптимальным решением будет соблюдение правильного баланса всех имеющихся ресурсов.

С кем важно договориться в первую очередь?

С бизнесом. Важно понимать, что-то 100% пойдет не по плану. Это часть процесса, так точно будет. Как бы хорошо и правильно вы всё не распланировали, вы с этим точно столкнетесь, к сожалению. Но может быть, просто мне так не везло, и бывает что все проходит идеально.

И снова обращусь к примеру. Разные части монолита разрабатывались много лет разными командами и разными людьми. Большая часть сотрудников, разрабатывающих ранние куски кода, уже ушла из компании. В коде вскрылись различные зависимости, о которых никто не знал. Документацию, естественно, никто не вел. Всё это в совокупности формирует те самые непредсказуемые изменения, о которых я говорил ранее. И что еще раз подчеркивает идею о том, что важно быть гибким и договариваться с бизнесом.

Сколько нужно разработчиков и админов для распила монолита? Кто из них будет работать дольше или больше?

Определенно, что работа с инфраструктурой в целом менее затратна, чем работа с кодом. Иногда нужно что-то заново разрабатывать, вносить новые архитектурные паттерны. Получается, что разработчиков нужно больше, чем админов. Со стороны инфраструктуры нужно будет что-то поднять и оперативно настраивать. В самом начале работы с инфраструктурой будет много, ведь придется все отстраивать с нуля. Хотя опять же всё зависит от проекта. Но в целом разработчики работают больше, и в компаниях их обычно больше, чем людей, занимающихся инфраструктурой.

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

Да, у нас было такое. Пришел к нам клиент, которому нужен был Kubernetes. Поскольку мы ребята опытные, то сразу задаём встречный вопрос: А вам это зачем? Бывали случаи, когда мы отказывали клиентам, потому что не видели необходимость в Kubernetes, естественно всё объясняли. Человек услышал что-то про Kubernetes, решил, что это круто, и захотел. Расскажу про ситуацию, когда мы не отказали.

Клиент был в процессе переезда на микросервисную архитектуру. Первая проблема была в отсутствии DevOps-инженеров в команде. Выполняли всё силами бедных разработчиков, которые пилили фичи, микросервисы и занимались инфраструктурой. В какой-то момент поняли, что ничего не получается.

Часть монолита хостилась на нескольких серверах в разных частях. Примерно всей кодовой базы была неизвестна: туда никто не лез, потому что люди, которые ее писали, уволились, и изучение того, что там, оставляли на потом. Проект был в сложном состоянии. Контейнеры крутились в Docker Swarm. В одном контейнере был просто запущен кусок монолита, хотя ради справедливости стоит отметить, что было и несколько микросервисов. В итоге, конечно, мы разобрались со всем этим.

И всем стало хорошо?

Вроде бы и да, но счастье почему-то всё не наступало. То есть технически проект стал работать стабильнее, деплой стал удобнее. Но радости от этого со стороны заказчика мы почему-то не ощущали. Мы поняли, что стоило бы глубже обсудить вопрос, зачем все же клиенту нужен был Kubernetes.

Оказалось, история была следующей. У проекта изначально были некоторые бизнес-проблемы, в которых видели техническую причину. Один из разработчиков тогда сказал, что причина, почему так все плохо в монолите. В тот же день поставили задачу переезд на микросервисы. И действительно даже начали распил и частично заехали в Docker SWARM. Только счастье все равно не наступало, просвета не наблюдалось, проблемы не решались. Тогда решили, что проблема в Docker Swarm. И обратились к нам.

Так вот по итогу переехали в Kubernetes, не идеально конечно, кое где полупереписанное legacy заехало, по желанию клиента и с обещанием переписать в дальнейшем. Но счастье снова не наступило. Оказалось, что да, инфраструктура стала работать стабильнее, но первоначальных проблем это так и не решило. Скорость разработки не увеличилась, фичи как пишутся вяло, так и пишутся, и скорость релизов неудовлетворительная. Когда мы наконец поняли, что проблема счастья клиента носит не инфраструктурный характер, мы встретились со стейкхолдерам компании и решили провести аудит.

Что именно вы делали?

Мы провели полный технический и менеджерский аудит всей компании: проверили, как работают разработчики, как устроены процессы, заново всех прособеседовали. И по результатам вскрылось очень многое, в том числе неприятных подробностей. Там внутри был и саботаж процессов, и перекладывание ответственности, и просто отсутствие компетенций. Не буду вдаваться в детали, но по итогу несколько человек пришлось уволить, а нескольким сделали последнее предупреждение. Помимо этого, мы увидели, что многие процессы были выстроены неправильно, поэтому и не работали так, как надо. Дали клиенту ряд рекомендаций и если не изменяет память, помогли с наймом новых сотрудников.

Не знал, что Southbridge оказывает такую услугу (прим. Кейс с того времени, когда Маресль работал инженером в Southbridge).

На моей памяти это была разовая история. Так что мой основной поинт по нашей теме: в процессе переезда важнее всего менеджмент и управление, а технические нюансы уже вторичны. Хоть и тоже крайне важны.

Как понять, что мы действительно распилили монолит на микросервисы, а не сделали микросервисный монолит?

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

Приведи пример.

Микросервис представляет собой некий кусочек приложения, выполняющий конкретную функцию. Этот кусочек независим. Если всё сделано хорошо, то падение или недоступность одного микросервиса не вызывает падение или недоступность всего проекта. Например, магазин, сортировка, личный кабинет всё работает в обычном режиме, но вот положить товар в корзину мы не можем, потому что какой-то один микросервис приуныл. Конечно, нюансов там гораздо больше чем, я описал, но это просто пример правильной работы.

Подведём итог. Рецепт распила монолита состоит в следующем: компетентные кадры, грамотное планирование, четкое понимание целей и гибкость в работе. Ничего не упустил?

Забыл упомянуть менеджмент. Кстати, если этот рецепт переложить на выполнение других задач, то их реализация тоже получится хорошей. Тут я Америку никому не открыл. Я понимаю, что инженерам и программистам интересна техническая сторона вопроса, но предлагаю взглянуть на распил с точки зрения управления процессом. Если про это забыть, то ничего не получится.

Поделитесь своим мнением на наши вопросы, варианты могут не охватывать всё, можно дополнить свой ответ в комментариях.
Подробнее..

Перевод Простое ускорение Java с помощью Quarkus и JHipster

01.06.2021 20:15:10 | Автор: admin

К старту курса о разработке на Java делимся переводом вводной статьи о Quarkus "родной" для Kubernetes Java-платформе для создания высокопроизводительных веб-, бессерверных (serverless) и нативных приложений (оптимизированных для используемых микропроцессоров). В ней используются предварительная компиляция AOT и агрессивная оптимизация, например сканирование путей к классам, перезагрузка конфигурации и предварительная конфигурация самозагрузки приложения в процессе сборки. Результатом становится впечатляющая скорость загрузки. Другими словами, приложения, созданные с Quarkus, запускаются не просто быстро, а очень быстро!


Так же как для платформ Spring и Micronaut, в Quarkus можно использовать преимущество GraalVM для преобразования JVM-приложений в нативные исполняемые файлы, что ещё больше повышает их быстродействие.

Такой прирост производительности позволяет этой Java-платформе конкурировать с бессерверными, облачными и созданными на основе Kubernetes средами, создавая приложения Supersonic Subatomic Java. В Quarkus используются стандарты Java (такие, как MicroProfile, JAX-RS), а также лучшие из существующих Java-библиотек (например, Hibernate и Vert.x). Есть даже поддержка аннотаций Spring.

Quarkus + JHipster = простое ускорение Java

История технологий разработки наглядно демонстрирует, что совместить различные компоненты бывает очень непросто. Разработчики любят, когда им предлагается что-то авторитетно-одобренное, и как раз здесь на сцену выходит JHipster.

JHipster это сопровождаемая сообществом разработчиков полнофункциональная платформа разработки, позволяющая создавать, развивать и развёртывать веб-приложения и ориентированные на микросервисы архитектуры. Стандартной серверной платформой в JHipster является Spring Boot, но постоянно появляются всё новые и новые возможности. Одним из таких вариантов стала blueprint-схема JHipster Quarkus.

Помимо прочего, одной из ключевых возможностей, определяющих успех JHipster, стала его расширяемость с помощью blueprint-схем. Blueprint-схема действует как плагин JHipster и позволяет вам переопределять стандартное поведение, настраивая его на свой вкус.

Например, blueprint-схема Kotlin с JHipster является популярным дополнением, которое любят использовать разработчики. Использование blueprint-схемам многократно расширяет границы возможностей. Вот почему в JHipster есть даже реализации без Java (такие, как Node + NestJS и .NET Core). Эта публикация познакомит вас с основными шагами по использованию JHipster, blueprint-схемой Quarkus, а также протоколом OAuth для создания оптимизированного для работы на конкретных микропроцессорах и безопасного полнофункционального приложения.

Создание Java-приложения с Quarkus

Прежде чем приступить к работе, необходимо установить несколько инструментов.

Предварительные требования:

Установите JHipster и его blueprint-схему Quarkus, используя npm:

# Install JHipster globallynpm install -g generator-jhipster@6.10.5# Install the JHipster Quarkus blueprintnpm install -g generator-jhipster-quarkus@1.1.1

Команда jhipster-quarkus сокращение для jhipster --blueprints quarkus. Посмотреть все варианты её синтаксиса можно с помощью команды --help.

$ jhipster-quarkus --help

Создание приложения в среде JHipster Quarkus

mkdir okta-jhipster-quarkus-example && cd okta-jhipster-quarkus-example# oh-my-zsh users: take okta-jhipster-quarkus-example

Чтобы запустить мастер создания приложений, можно выполнить команду jhipster-quarkus:

jhipster-quarkus

В этой вводной статье основным вопросом будет выполнение аутентификации.

Среда JHipster Quarkus позволяет использовать JWT (с возможностью управления пользователями в базе данных приложений) или аутентификацию OAuth 2.0/OIDC с применением поставщиков идентификационной информации, таких как Keycloak и Okta. OIDC расшифровывается как OpenID Connect, этот протокол представляет собой тонкий слой поверх протокола аутентификации OAuth 2.0. Его основная задача обеспечить аутентификацию и идентификацию пользователя.

Ниже представлен пример ответов на вопросы мастера создания приложений.

После того как вы ответите на все эти вопросы, JHipster создаст код вашего приложения и выполнит команду npm install.

{  "generator-jhipster": {    "promptValues": {      "packageName": "com.mycompany.myapp",      "nativeLanguage": "en"    },    "jhipsterVersion": "6.10.5",    "applicationType": "monolith",    "baseName": "jhipster",    "packageName": "com.mycompany.myapp",    "packageFolder": "com/mycompany/myapp",    "serverPort": "8080",    "authenticationType": "oauth2",    "cacheProvider": "no",    "enableHibernateCache": true,    "websocket": false,    "databaseType": "sql",    "devDatabaseType": "h2Disk",    "prodDatabaseType": "mysql",    "messageBroker": false,    "buildTool": "maven",    "embeddableLaunchScript": false,    "useSass": true,    "clientPackageManager": "npm",    "clientFramework": "angularX",    "clientTheme": "none",    "clientThemeVariant": "",    "creationTimestamp": 1614834465776,    "jhiPrefix": "jhi",    "entitySuffix": "",    "dtoSuffix": "DTO",    "otherModules": [      {        "name": "generator-jhipster-quarkus",        "version": "1.1.1"      }    ],    "enableTranslation": true,    "nativeLanguage": "en",    "languages": ["en"],    "blueprints": [      {        "name": "generator-jhipster-quarkus",        "version": "1.1.1"      }    ]  }}

В этом файле содержатся все ответы на исходные вопросы мастера JHipster, что позволит создать приложение без дополнительных запросов.

Убедитесь, что выполняемая с помощью Keycloak аутентификация OAuth 2.0/OIDC действительно работает

Помимо прочих файлов JHipster Quarkus создаёт файлы для инструментального средства Docker Compose, помогающие загрузить среду разработки, настроенную для вашего только что созданного приложения. При этом даже выполняется импорт данных Keycloak, заданных по умолчанию для пользователей и приложений, и вам не придётся делать это самим.

https://gitlab.webant.ru/russia_quiz/frontend/-/merge_requests

Если Keycloak запущен и работает, вы сможете выполнить вход в систему. Запустите ваше приложение с помощью Maven:

./mvnw

Вы можете видеть, что приложение запускается за 3,351 с, также выводится обширный список расширений Quarkus (включая oidc). Перейдите по адресу http://localhost:8080 в предпочитаемом вами браузере и нажмите ссылку sign in (вход).

Вы будете перенаправлены в Keycloak для входа в систему. При запросе идентификационных данных введите admin/admin.

После успешного прохождения аутентификации вы будете перенаправлены обратно в ваше приложение Quarkus.

Как работает поддержка протокола аутентификации OAuth 2.0 в JHipster Quarkus

Одной из проблем при разработке blueprint-схем JHipster, таких как Quarkus, является необходимость найти правильный баланс между повторным использованием существующих механизмов и добавлением индивидуальных реализаций.

С самого первого дня своего появления JHipster осуществлял интеграцию всех современных платформ для создания веб-приложений (Angular, React или даже Vue.js), совмещая их с фактически стандартными Java-технологиями, такими как Spring Boot и Spring Cloud.

В реализации JHipster Quarkus OAuth 2.0 за основу взято URI-перенаправление login/oauth2/code/oidc, предполагающее использование платформы аутентификации Spring Security. Настоятельно рекомендуем выполнять аутентификации на стороне сервера, поскольку это намного безопаснее (так как у браузера нет необходимости управлять какими-либо идентификационными данными и хранить какие-либо маркеры доступа).

В среде JHipster Quarkus, чтобы повторно использовать без изменений клиентские приложения и реализовать на серверной стороне механизм аутентификации по протоколу OAuth 2.0, в бэкенде должны быть открыты два HTTP-маршрута. Вот почему в JHipster Quarkus предусмотрен контроллер UserOauth2Controller и настраиваемые свойства Quarkus OIDC, позволяющие обеспечить правильную работу всего механизма.

Это своего рода интеграционная ловушка, на которую разработчики не хотят тратить время. Коллективный разум сообщества JHipster, объединяющий мнения самых эффективных разработчиков, объясняет высокую популярность и качество данной платформы.

Объединение JHipster Quarkus с сервисом Okta

Из этого раздела вы узнаете, как использовать сервис Okta в качестве провайдера протокола аутентификации OAuth 2.0/OIDC. В Okta предусмотрены два варианта для конфигурирования OIDC-приложения. Это можно выполнить либо в консоли разработчика, либо с помощью инфраструктуры Okta CLI.

Использование инфраструктуры Okta CLI для конфигурирования JHipster

Инфраструктура Okta CLI автоматизирует для вас все конфигурации JHipster и Okta. Для установки Okta CLI можно воспользоваться популярными менеджерами пакетов.

macOS (с помощью Homebrew):

brew install --cask oktadeveloper/tap/okta

Linux (с помощью Flatpak):

# Add Flathub repoflatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo# install the packageflatpak install com.okta.developer.CLI# add this to your appropriate dot filealias okta="flatpak run com.okta.developer.CLI"

Windows (с помощью Chocolatey):

choco install okta -version 0.8.0

Вы также можете просто передать его в оболочку bash:

curl https://raw.githubusercontent.com/okta/okta-cli/master/cli/src/main/scripts/install.sh | bash

Если у вас ещё нет аккаунта разработчика в Okta, откройте терминал, перейдите в каталог приложения Quarkus и выполните okta register. Если у вас есть аккаунт, выполните okta login.

$ okta registerFirst name: DanielLast name: PetismeEmail address: daniel.petisme@gmail.comCompany: OktaCreating new Okta Organization, this may take a minute:OrgUrl: https://dev-9323263.okta.comAn email has been sent to you with a verification code.Check your emailVerification code: 232819New Okta Account created!Your Okta Domain: https://dev-9323263.okta.comTo set your password open this link:https://dev-9323263.okta.com/welcome/drpt2SjbRAPR-gvVHhnm

Если у вас уже есть аккаунт разработчика Okta, выполните команду okta login. Затем из каталога приложения Quarkus выполните okta apps create jhipster. Примите предлагаемые по умолчанию предложения для перенаправления URI.

$ okta apps create jhipsterApplication name [okta-jhipster-quarkus-example]:Redirect URICommon defaults:  Spring Security - http://localhost:8080/login/oauth2/code/okta  Quarkus OIDC - http://localhost:8080/callback  JHipster - http://localhost:8080/login/oauth2/code/oidcEnter your Redirect URI(s) [http://localhost:8080/login/oauth2/code/oidc, http://localhost:8761/login/oauth2/code/oidc]:Enter your Post Logout Redirect URI(s) [http://localhost:8080/, http://localhost:8761/]:Configuring a new OIDC Application, almost done:Created OIDC application, client-id: 0oa5ozjxyNQPPbKc65d6Creating Authorization Server claim 'groups':Adding user daniel.petisme@gmail.com to groups: [ROLE_USER, ROLE_ADMIN]Creating group: ROLE_USERCreating group: ROLE_ADMIN

Конфигурация приложения Okta записывается здесь: /Users/daniel/workspace/okta-jhipster-quarkus-example/.okta.env

ПРИМЕЧАНИЕ: идентификаторы URI, перенаправленные http://localhost:8761*, предназначены для реестра JHipster, который часто используется при создании микросервисов с помощью JHipster. В инфраструктуре Okta CLI они добавляются по умолчанию. Они не требуются для приложения, создаваемого в этой вводной статье, но если их оставить, то никакого вреда точно не будет.

Инфраструктура Okta CLI создаст файл .okta.env в текущем каталоге. Если посмотреть на него, то вы увидите, что в нём содержатся несколько ключей и значений, предназначенных для протокола OIDC.

$ cat .okta.envexport QUARKUS_OIDC_AUTH_SERVER_URL="http://personeltest.ru/aways/dev-9323263.okta.com/oauth2/default"export QUARKUS_OIDC_CLIENT_ID="0oa5ozjxyNQPPbKc65d6"export QUARKUS_OIDC_CREDENTIALS_SECRET="KEJ0oNOTFEUEFHP7i1TELLING1xLm1XPRn"export QUARKUS_OIDC_AUTHENTICATION_REDIRECT_PATH="/login/oauth2/code/oidc"export JHIPSTER_OIDC_LOGOUT_URL="http://personeltest.ru/aways/dev-9323263.okta.com/oauth2/default/v1/logout"

Установите файл, чтобы задать переменные среды, и запустите ваше приложение с помощью Maven.

source .okta.env./mvnw

Обязательно добавьте \*.env в ваш файл .gitignore, чтобы в коммиты не попадал ваш секрет клиента.

Как только приложение будет запущено, в окне в режиме инкогнито откройте http://localhost:8080 и выполните вход. Вам будет предложено ввести свои идентификационные данные Okta.

После успешного прохождения аутентификации вы будете перенаправлены обратно в ваше приложение. На начальной странице должен отображаться ваш адрес электронной почты.

Инфраструктура Okta CLI упрощает конфигурирование JHipster и выполняет следующие полезные операции.

  1. Создаётся приложение OIDC с правильным перенаправлением URI.

  2. Заводятся группы ROLE_ADMIN и ROLE_USER, требуемые для JHipster.

  3. Ваш текущий пользователь добавляется в группы ROLE_ADMIN и ROLE_USER.

  4. Создаётся заявка на группы на заданном по умолчанию сервере авторизации, и к ней добавляются эти пользовательские группы.

Но вдруг вы не любите работать из командной строки? Не паникуйте, тут есть кому прикрыть вашу спину! Инфраструктура Okta CLI проста в использовании, но для тех, кому это необходимо, есть возможность выполнить настройки с помощью интерфейса пользователя. Именно поэтому я так подробно рассматриваю каждый шаг по настройке приложения OIDC, работающего с JHipster Quarkus.

Использование консоли разработчика Okta для настройки JHipster

Если у вас нет аккаунта разработчика Okta, необходимо нажать ссылку sign up (вход). Тут нет ничего сверхсложного: просто укажите имя, фамилию, адрес электронной почты, выберите надёжный пароль и всё вы готовы двигаться дальше. Выполнив вход в систему, вы попадаете в консоль разработчика:

Разверните вложенное меню Applications (Приложения) на панели навигации слева, затем выберите в меню Applications > Create App Integration (Приложения > Создать интеграцию приложений), чтобы запустить мастер создания приложений.

Выберите OIDC и Web Application. Затем нажмите кнопку Next (Далее).

Теперь вам необходимо будет указать некоторые настройки для приложения.

  • Name (Имя): можете указать любое имя на свой вкус, но разве JHipster Quarkus чем-то не подходит?

  • Login redirect URIs (Перенаправление URI при входе): определяет, будет ли Okta выполнять перенаправление в браузере клиента после аутентификации. Установите для него http://localhost:8080/login/oauth2/code/oidc, именно это значение настроено по умолчанию.

  • Logout redirect URIs (Перенаправление URI при выходе): http://localhost:8080 указывается, куда будет перенаправляться пользователь после выполнения выхода.

  • Group assignments (Назначения групп): определяет, какие группы могут использовать это приложение.

Используйте предлагаемые значения по умолчанию для всех остальных настроек. Завершив ввод требуемых настроек для своего приложения, нажмите Save (Сохранить). На экране отобразятся подробные сведения о вашем приложении.

Наиболее важными являются следующие значения:

  • идентификационные данные клиента (ID и секрет клиента). Они позволяют приложению Java выполнять аутентификацию в сервисах Okta для последующей обработки потоков аутентификации и авторизации пользователей;

  • домен Okta, из которого Quarkus будет выводить конечные точки адресов URL для протоколов OAuth/OIDC.

Создание групп пользователей

Теперь настало время для создания групп пользователей. По умолчанию для JHipster требуются две следующие группы:

  • ROLE_USER: для аутентифицированных пользователей;

  • ROLE_ADMIN: для аутентифицированных пользователей с административными правами для этого приложения.

В консоли разработчика выберите в меню Directory > Groups (Каталог > Группы). Нажмите Add Group (Добавить группу) и создайте группу ROLE_ADMIN

Теперь добавьте группу ROLE_USER.

Всё, вы завершили создание групп, теперь займёмся добавлением пользователей.

Создание пользователей

Чтобы увидеть различие между обычными и административным пользователями, создайте пользователей в каждой из групп. Используя консоль разработчика Okta, выберите в меню Directory > People (Каталог > Люди). Нажмите Add Person (Добавить человека). Начните с создания пользователя Administrator.

Основным моментом тут является привязка пользователя Administrator к группе ROLE_ADMIN. Чтобы определить исходный пароль, который пользователь должен будет изменить, исключительно в демонстрационных целях для данной вводной статьи использована стратегию выбора пароля Set by Admin (Задаётся администратором). В реальном проекте я рекомендую использовать стратегию Set by User (Задаётся пользователем) с активацией по электронной почте. Теперь добавим обычного пользователя.

Убедитесь, что пользователь User входит в группу ROLE_USER. Очень важно использовать действующий адрес электронной почты, так как он может понадобиться при восстановлении пароля. Выберите в меню Applications > JHipster Quarkus (Приложения > JHipster Quarkus) и нажмите Assignments (Назначения). Назначьте группам пользователей, которых только что создали.

Добавление заявки на группы к маркеру ID

Последнее, о чём необходимо вам позаботиться, это настроить заявку на группы, включающую группы пользователей в маркер ID. Выберите в меню Security > API (Безопасность > API), а затем нажмите default (по умолчанию). Щёлкните Claims > Add Claim (Заявки > Добавить заявку). Введите следующие значения:

  • Name (Имя): groups (группы);

  • Include in token type (Включить тип маркера): ID Token (Маркер ID);

  • Value type (Тип значения): groups (группы);

  • Filter (Фильтр): Matches regex with a value of .* (Соответствует регулярному выражению со значением .*).

Нажмите Create (Создать). Конфигурация Okta для JHipster готова!

Настройка Quarkus OIDC для Okta

В этот момент необходимо, чтобы приложение JHipster Quarkus уже было запущено и для использования Keycloak в качестве провайдера идентификационных данных для него. Давайте изменим настройки для работы с Okta. Во-первых, необходимо выйти из веб-приложения JHipster, чтобы предотвратить конфликт файлов cookie. Выберите Account > Sign Out (Аккаунт > Выход).

ПРИМЕЧАНИЕ: можно оставить приложение запущенным. Quarkus поддерживает работу в так называемом режиме для разработки (Dev Mode), обеспечивающем горячую перезагрузку любых исходных или ресурсных файлов при каждом их обновлении. Это исключительно удобно!

Откройте для редактирования файл src/main/resources/application.properties и найдите в нём раздел со следующей конфигурацией OIDC.

# OAuth 2.0 and OIDCquarkus.oidc.enabled=truequarkus.oidc.auth-server-url=http://localhost:9080/auth/realms/jhipster/%dev.quarkus.oidc.client-id=web_app%dev.quarkus.oidc.credentials.secret=web_appquarkus.oidc.application-type=hybridquarkus.oidc.authentication.scopes=profile,address,email,address,phone,offline_accessquarkus.oidc.authentication.cookie-path=/quarkus.oidc.authentication.redirect-path=/login/oauth2/code/oidcquarkus.oidc.authentication.restore-path-after-redirect=falsejhipster.oidc.logout-url=http://localhost:9080/auth/realms/jhipster/protocol/openid-connect/logout%test.quarkus.oidc.client-id=dummy%test.quarkus.oidc.application-type=service%test.jhipster.oidc.logout-url=some-dummy-logoutUrl

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

  • quarkus.oidc.auth-server-url: корневой URL для API Okta, полученный из домена приложения OIDC;

  • quarkus.oidc.client-id: ID клиента для приложения OIDC;

  • quarkus.oidc.credentials.secret: секрет клиента для приложения OIDC;

  • jhipster.oidc.logout-url: в JHipster браузер будет запускать выход из системы. Серверная сторона должна предоставлять эту информацию (пока её невозможно получить с помощью OIDC-поиска).

После того как вы обновите этот файл, ваши настройки должны выглядеть примерно так:

# OAuth 2.0 and OIDCquarkus.oidc.enabled=truequarkus.oidc.auth-server-url=https://dev-9323263.okta.com/oauth2/defaultquarkus.oidc.client-id=0oaajhdr9q9jxbBM95d6quarkus.oidc.credentials.secret=NEVERSHOWSECRETSquarkus.oidc.application-type=hybridquarkus.oidc.authentication.scopes=profile,address,email,address,phonequarkus.oidc.authentication.cookie-path=/quarkus.oidc.authentication.redirect-path=/login/oauth2/code/oidcquarkus.oidc.authentication.restore-path-after-redirect=falsejhipster.oidc.logout-url=https://dev-9323263.okta.com/oauth2/default/v1/logout

Перезапустите приложение, перейдите по адресу http://localhost:8080. Нажмите sign in (вход) и вы будете перенаправлены на страницу входа Okta.

Введите admin в качестве имени пользователя и пароль, который вы задали. Okta определит, что это ваш первый вход в систему, и предложит сменить пароль. После выполнения вы будете перенаправлены в ваше приложение Quarkus.

Переход в нативный формат с помощью Quarkus и GraalVM

Заключительным шагом в данной вводной статье будет упаковка приложения Java в виде нативного выполняемого файла (оптимизированного для используемого микропроцессора). И снова JHipster надёжно прикрывает ваши тылы, делая для вас всё необходимое. Просто выполните в Maven команду package с профилем native:

./mvnw package -Pnative -DskipTests

Вы знаете, мы знаем, все знают: мы пропустили этап тестирования в этом демонстрационном примере всё работает идеально. Но, пожалуйста, никогда не пропускайте тестирование в условиях реальной промышленной разработки.Если в используемый вами JDK не включён GraalVM, вы получите ошибку:

[error]: Build step io.quarkus.deployment.pkg.steps.NativeImageBuildStep#build threw an exception: java.lang.RuntimeException: Cannot find the `native-image` in the  GRAALVM_HOME, JAVA_HOME and System PATH. Install it using `gu install native-image`

Самым простым способом решения этой проблемы будет применение SDKMAN для установки Java 11 с GraalVM:

sdk install java 21.0.0.2.r11-grl

Затем выполните gu install native-image:

$ gu install native-imageDownloading: Component catalog from www.graalvm.orgProcessing Component: Native ImageDownloading: Component native-image: Native Image  from github.comInstalling new component: Native Image (org.graalvm.native-image, version 21.0.0.2)

Как только процесс завершится, перезапустите команду package в Maven:

./mvnw package -Pnative -DskipTests

На нативную компиляцию требуется немало времени. У вас может возникнуть желание прогуляться, выпить чашку-другую кофе или несколько раз отжаться. Но, если вы предпочитаете остаться за компьютером, можно потратить время на следующее:

  • ознакомиться с проектом JHipster Quarkus Blueprint;

  • исследовать сайт для разработчиков Okta;

  • изучить эти великолепные руководства по Quarkus.

Спустя примерно три минуты нативный выполняемый файл должен быть готов:

Запустите его как нативный выполняемый файл, используя команду target/*runner:

Ваше старое доброе Java-приложение запустится через 1 секунду! Помните, я рассказывал о приросте памяти? Ниже привожу команду, как посмотреть потребление памяти в мегабайтах:

$ ps -o pid,rss,command | grep --color jhipster | awk '{$2=int($2/1024)"M";}{ print;}'30951 46M ./target/jhipster-1.0.0-SNAPSHOT-runner31433 0M grep --color=auto --exclude-dir=.bzr --exclude-dir=CVS --exclude-dir=.git --exclude-dir=.hg --exclude-dir=.svn --color jhipster

Ваше приложение потребляет менее 50 МБ памяти. Перейдите по адресу http://localhost:8080 и убедитесь, что всё исправно работает. Теперь наслаждайтесь своим успехом!

Дальнейшая разработка с помощью JHipster Quarkus

Надеюсь, вы получили удовольствие от данной вводной статьи, пройдя все этапы создания нативного приложения и применяя для этого Java, Quarkus и JHipster. Не правда ли, впечатляет, как JHipster и Okta CLI делают для вас основную тяжёлую работу?! Вы можете найти пример, созданный в этой вводной статье, на GitHub. Если вы заинтересованы в дополнительном изучении blueprint-схем Quarkus, посмотрите проект generator-jhipster-quarkus, также размещённый на GitHub.

Совсем недавно Java считали медленным языком программирования, а вернее было бы сказать, что медленной была виртуальная машина Java, но это не помешало языку стать одним из самых востребованных по версии TIOBE. Сегодня Java становится всё быстрее, а это значит, что она ещё надолго сохранит способность конкурировать с новыми языками; кроме того, в последнее время наметилась тенденция к упрощению синтаксиса Java. Если вам интересен этот язык и работа с ним в перспективе, то вы можете посмотреть на программу обучения нашего курса по Java, где студенты за 14 месяцев получают всестороннюю подготовку к трудоустройству в качестве Java-разработчика.

Узнайте, как прокачаться и в других специальностях или освоить их с нуля:

Другие профессии и курсы
Подробнее..

CICD монолита Авито от коммита до моржа

03.06.2021 12:19:11 | Автор: admin

Всем привет, меня зовут Александр Данковцев, я lead engineer команды Antimonolith. В этой статье я расскажу, как построен CI/CD монолита Авито. Речь пойдёт про нашу архитектуру стейджинга, pre-receive хуки, то, что из себя представляет сборка и деплой, как устроен прогон автотестов и какие проверки происходят на merge. А ещё рассмотрим after-merge actions.

Перед началом повествования введу пару понятий, которые будут использоваться дальше:

  • Релиз-кандидат версия кода, которому предстоит пройти процесс валидации тестов и деплой в продакшн при успехе.

  • Срез релиз-кандидата Git-ветка, созданная от мастера в процессе запуска сборки релиза.

Архитектура стейджинга Авито

Начнём со схемы архитектуры стейджинга. Это то, где вращается сам сайт Авито и компоненты, из которых он состоит:

Авито сайт, как и любой другой микросервис в стейджинг-окружении, деплоится встейджинговый Kubernetes-кластер. У нас есть отдельный namespace avito-site-tests, вкотором расположены ресурсы сайта: базы данных, баунсеры, сервис очередей, Sphinx, прочие репликации, всякие демоны. Это сделано для того, чтобы экономить ресурсы вкластере. Непосредственно каждая ветка сайта деплоится в собственный отдельный namespace, и все бэкенды натравливаются на ресурсы, которые расположены в отдельном неймспейсе. Особняком стоит Frontend Delivery Network (FDN), куда сгружается статика сайта.

Эта инфраструктура очень сложно деплоится: раскатить весь стейджинг одной кнопкой нельзя. Поэтому стейджинг поделен на Helm-релизы.

Отдельным релизом мы катим сами ресурсы, потому что это требуется очень редко. Ресурсы это база данных, Sphinx, Redis-ы, RabbitMQ. Также отдельно по срезу релиз-кандидатов или по требованию обновляются демоны монолита. На схеме это PGQ daemons, DataBus consumers и QaaS consumers. Мы вынесли их в отдельный деплой, чтобы перераскатка не пересоздавала базу данных, потому что база достаточно большая (около 10 Гб) и её пересоздание это сброс всех данных, созданных в процессе её работы, т. е. пропадут все созданные объявления и прочие ресурсы. Мы получим неконсистентное состояние с другими сервисами.

Деплой ресурсов стартует в 7 утра, когда все разработчики ещё спят, и к утру у нас есть готовая инфраструктура. Это позволяет нам тестировать в том числе и асинрохронное взаимодействие компонентов сайта. После раскатки ресурсов происходит пересоздание релиза кронов, демонов, консумеров.

Каждый сайт из второй колонки схемы деплоится по отдельности. Грубо говоря, это происходит по пушу в ветку: мы закоммитили, запушили, и ветка собралась в отдельный namespace. Отдельно деплоится статика, об этом расскажу позже.

Каждый бэкенд сайта Kubernetes Pod состоит из пяти контейнеров:

Nginx это точка входа плюс некоторая отдача статики.

Php-fpm сам бэкенд.

Envoy-core, который отвечает за прокси в сервисы, выполняет роль DNS resolve и keepalive. Например, мы можем стучаться по http://localhost:8888/service-item и попасть в service-item. Это всё нужно для большей производительности стейджинга.

Netramesh добавляет контекст в исходящие запросы: заголовок X-Source. Он также выполняет роль динамической подмены upstream-а сервисов по входящему заголовку X-Route. Формат такой: X-Router: src_host:src_port=dst_host:dst_port.

Navigator межкластерный маршрутизатор, который резолвит ClusterIP в набор PodIP во всех доступных дата-центрах.

Посмотрим на сетевой стек стейджинга. Когда мы запрашиваем произвольную ветку сайта, запрос проходит через фронденд nginx. В нашем случае это какой-то из подмножества avi-http, который отправляет запросы на Ingress. С Ingress мы попадаем в routing-gateway. Routing-gateway занимается подмешиванием заголовка X-Route и дальше прокидывает запрос в API-gateway. API-gateway балансит, куда отправить запрос: либо это будет Avito Site, либо какой-нибудь API-сomposition. API-сomposition это десктоп-сайт на Node.js или гошный сервис, либо любой другой сервис на любом другом языке.

Quality gates

Когда мы в общих чертах поняли, как выглядит стейджинг, рассмотрим quality gates, которые применяются для Avito Site.

Основные этапы прохождения коммита таковы:

  1. Гитовые pre-receive хуки.

  2. TeamCity: CI/CD прогоняет тесты, линтеры и так далее.

  3. Проверки на merge, флаги и обязательности.

  4. Действия после merge.

Давайте же покоммитим в монолит. Предположим, мы решили что-то поправить.

Pre-receive хуки

Pre-receive хуки помогают проверить, всё ли в порядке с коммитом до его пуша в репозиторий. Таких хуков у нас не очень много.

Мы проверяем, чтобы каждый коммит содержал имя задачи в Jira. Также есть элементарные проверки синтаксиса. Допустим, если где-то точку с запятой поставили не так, pre-receive хук скажет, что допущена синтаксическая ошибка. Мы проверяем и стиль, например, если пропущен нужный пробел между операторами, линтер сообщит об этом и не даст запушить ветку.

Плюс есть проверка на то, что коммит наш доменный, он принадлежит Авито, а не какой-то левый.

Что происходит в TeamCity

Вот максимально упрощённый граф зависимостей нашего CI/CD, я собрал основные этапы прохождения:

У нас есть всякие схема-чеки, DI-чеки, юнит-тесты, но лишь вершина айсберга. Из всей схемы я рассмотрю самый основной и сложный процесс прохождение end-to-end тестов от сборки Docker до прогона тестов.

Сборка

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

В сборке образа мы используем паттерн build-образ и push-образ. Build-образ большой и тяжёлый, с кучей зависимостей, сишных библиотек и прочих утилит, которые нужны, чтобы собирать непосредственно код. Для рантайма же этих зависимостей не должно быть. То есть унас собирается некоторый артефакт на этапе build code, после этого сбилженная статика загружается в Ceph. А остальная часть собранного кода запекается в Docker-образ и позже пушится.

Вот как происходит build code:

  1. Установка composer-зависимостей.

  2. Кодогенерация, например, генерация client-shop.

  3. Сборка DI, у нас используется Symfony.

  4. Сборка фронденда. Собирается npm, собирается Twig в кэши.

  5. Деплой хранимых процедур. Мы получаем свободного юзера, назначаем ему схему, search pass, и накатываем туда хранимки.

  6. Сборка словарей файловое представление справочных данных из базы или сервисов.

  7. Если всё прошло успешно, последний этап сборка артефактов, то есть генерация app.toml, swagger, rev.txt. Rev.txt это идентификатор сборки, окружение, в котором она собиралась, и прочая отладочная информация.

Деплой

Когда образ собрался и запушился, приходит следующая TeamCity сборка деплой.

Процесс следующий:

  1. Деплой в Kubernetes-кластер.

  2. Валидация доступности хоста: делается curl с небольшим прогрессирующим шагом, пока хост не станет отдавать код 200. Helm предоставляет информацию о том, что он успешно раскатился, проходят health-чеки и так далее.

  3. Регистрация хоста в routing-gateway.

  4. Автоматическая отбивка в Slack, о том, что хост собрался.

Автотесты

Хост собрался, самое время запускать end-to-end тесты. Если говорить простым языком, у нас сначала отрабатывает сборка Build E2E. В отдельный архив собирается папка с тестами и пушится в Artifactory. От E2E-тестов идёт много связей, я приводил их на первой картинке проTeamCity.

Для простоты рассмотрим одну из них E2E Test Suite. В этом билде настроено, какой тип тестов запускать, допустим, web или api, или заданы какие-то дополнительные параметры, например, относящиеся к конкретному юниту Авито. Этот билд общается с сервисом параллельного запуска автотестов: посылает ему задачу, сервис её исполняет и получает ответ.

Если копнуть немного глубже, то билд, прогоняющий тесты, представляет собой TeamCity meta runner, который через Docker запускает небольшое приложение, где реализован клишный parallel-client. Parallel-client делает запрос в parallel manager и передаёт ему все метаданные сборки: какие тесты к какому юниту относятся, какой артефакт был запушен в Artifactory. Parallel manager, получив результат, перекладывает всё в очередь и отвечает клиенту, что всё окей, я принял результат. После этого клиент начинает периодически поллить parallel manager на информацию о том, прошли тесты или нет.

Когда задача поставлена в очередь на исполнение тестов, подключается parallel worker. Воркер вычитывает очередь заданий, которые ему поставили для выполнения тестов, и находит в данных мета-информацию о месте хранения этих тестов. В мета-информации есть ссылка на скачивание архива, который на раннем этапе был загружен в Artifactory. Воркер скачивает себе архив с тестами и запускает command для их прогона.

Тесты ходят в Selenium, который ходит по сайту через Firefox, Chrome или любой другой браузер. Тесты также пользуются файловой системой (fs-qa), куда сохраняют скриншоты, html-странички и прочее. На результат выполнения смотрит воркер: для успеха код ответа 0, для провала 1.

После этого parallel worker пушит каждый выполненный кусочек задачи в отдельную очередь результаты выполнения тестов. Эту очередь слушает parallel manager. Когда менеджер получает результаты, он отгружает их в tests reporters backend, где хранятся отчёты о том, что такой-то тест столько-то выполнялся и прочая информация.

Parallel client через cli-команду запрашивает у test reporter backend финальную информацию отом, что все тесты пройдены. В ней отмечено, сколько тестов прошли, например, 150 из 170. На основе этой информации билд становится зелёным или красным. Также после получения этой информации в TeamCity создаётся артефакт со ссылкой на frontend test reporter. Мы можем зайти в него из TeamCity и визуально посмотреть полную отчётность о том, какие тесты прошли и сколько они выполнялись.

Вот как весь процесс выглядит схематически:

Каждый Е2Е-тест создаёт свою коллекцию тестов, а каждая коллекция тестов создаёт свои репорты, и их довольно много. На прогоне ветки для сайта у нас получается семь Е2Е-сьютов, а для релиза их порядка 50 штук. Когда все отчёты собрались и пришли отбивки в stash, мы можем зайти в stash и замерджить ветку.

Проверки на merge

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

Merge-плагин анализирует diff, то, какие файлы были изменены, и выставляет флаги для текущего pull request. Если в pull request были изменения PHP-файлов, он ставит backend changes, если были изменения JS или CSS frontend changes. Маркируются и случаи, когда произошли изменения в папке с Е2Е-тестами, например, buyer test changes. В итоге работы плагина у pull request появляются флаги, что в нём потрогали: frontend, backend и тесты байеров.

Наименование флага

Значение

Условие

Backend changes

Yes

*.php

Frontend changes

No

*.css, *.js

Buyer E2E changes

No

tests/e2e/BuyerTests/*

DataBase changes

Yes

*.sql

Также у нас есть карта всех билдов, которые мы хотим валидировать на pull request Avito Site. Но мы не хотим, чтобы каждое изменение в read.me порождало требование пройти 30проверок. Мы хотим, чтобы если потрогали какой-нибудь текстовый файлик, было наличие только одного апрува. Это достигается тем, что каждая проверка маркируется списком флажков, на которые она должна срабатывать.

Наименование линтера

Список флажков

UnitTests

Backend changes

FrontendCI

Frontend changes

DockerBuild

Backend changes, Frontend changes, Database changes

BuyerE2E Suite

Buyer E2E changes

Например, для "BuyerE2E Suite" проверка прогоняется в том случае, если на pull request был выставлен флаг "Buyer E2E changes". Если мы не трогали папку с байерами, то проверки не будут обязательными, на pull request не будет требоваться, что они должны быть зелёными. Если потрогали SQL-ки, значит, обязательно должны пройти интеграционные тесты на базе, и так далее.

Действия после merge

Когда все тесты прошли, все условия соблюдены, все нужные галочки стоят, мы наконец-то можем слить ветку в мастер. Но здесь всё не останавливается. После этого у нас срабатывает механика after-merge.

Её реализует плагин в нашем stash, который слушает изменения на merge и триггерит соответствующие сборки в TeamCity. Прежде всего, он делает удаление хоста, что экономит нам ресурсы: Kubernetes не резиновый. Ceph тоже не резиновый, поэтому после мерджа происходит очистка статики. И в конце плагин снимает регистрацию с routing-gateway.

Теперь всё, мы в мастере.

А что с релизами?

В статье я не стал рассматривать релизы Авито. Они, в принципе, очень похожи на описанный выше процесс. В релизах просто происходит не pull request в мастер, а срез ветки. Востальном смысл тот же: сборка сайта, прогоны Е2Е-тестов, просто их больше, а вместо проверки в stash на merge-check валидация релиз-инженерами.

Подробнее..

Перевод Антипаттерны деплоя в Kubernetes. Часть 2

04.06.2021 12:23:32 | Автор: admin

Перед вами вторая часть руководства по антипаттернам деплоя в Kubernetes. Советуем также ознакомиться с первой частью.

Список антипаттернов, которые мы рассмотрим:

  1. Использование образов с тегом latest

  2. Сохранение конфигурации внутри образов

  3. Использование приложением компонентов Kubernetes без необходимости

  4. Использование для деплоя приложений инструментов для развёртывания инфраструктуры

  5. Изменение конфигурации вручную

  6. Использование кubectl в качестве инструмента отладки

  7. Непонимание сетевых концепций Kubernetes

  8. Использование неизменяемых тестовых окружений вместо динамических сред

  9. Смешивание кластеров Production и Non-Production

  10. Развёртывание приложений без Limits

  11. Неправильное использование Health Probes

  12. Не используете Helm

  13. Не собираете метрики приложений, позволяющие оценить их работу

  14. Отсутствие единого подхода к хранению конфиденциальных данных

  15. Попытка перенести все ваши приложения в Kubernetes

6. Использование кubectl в качестве инструмента отладки

Утилита kubectl незаменима при работе с кластерами Kubernetes. Но не стоит использовать её как единственный отладочный инструмент.

При возникновении проблем, особенно в production кластере, вашим первым импульсом не должен быть запуск kubectl. Если вы поступаете именно так, то вы уже проиграли битву, особенно если сейчас 3 часа ночи, сервисы не работают, и вы на связи.

kubectl get nskubectl get pods -n saleskubectl describe pod prod-app-1233445 -n saleskubectl get svc - n saleskubectl describe...

Вместо этого все ваши кластеры Kubernetes должны иметь надлежащие системы мониторинга, трассировки и сбора логов, которые можно использовать для своевременного выявления проблем. Если вам нужно запустить kubectl для проверки чего-либо, это означает, что вы не учли это, настраивая вашу систему мониторинга.

Сложно переоценить ценность метрик и трассировок, мы расскажем о них отдельно в одном из следующих антипаттернов.

Даже если вы просто хотите проверить конфигурацию кластера, вы также можете использовать для этой цели специальный инструмент. Сегодня существует множество решений для работы с кластерами Kubernetes.

Например, обратите внимание на Kubevious, это универсальная панель управления Kubernetes, которая позволяет вам искать и отображать ресурсы Kubernetes в соответствии с настраиваемыми правилами.

7. Непонимание сетевых концепций Kubernetes

Прошли те времена, когда единственный балансировщик нагрузки был всем, что необходимо для настройки подключения к вашему приложению. Kubernetes представляет свою собственную сетевую модель, и вам нужно разобраться с её основными концепциями. По крайней мере, вы должны быть знакомы с типами сервисов - Load Balancer, Cluster IP, Node Port и понимать, чем сервисы отличаются от Ingress.

Сервисы типа Сluster IP используются внутри кластера, Node Port также могут использоваться внутри кластера, но при этом доступны снаружи, а балансировщики нагрузки могут принимать только входящий трафик.

Но это ещё не всё потребуется разобраться, как в кластере Kubernetes работает DNS. И не забывать, что весь трафик между служебными компонентами шифруется и нужно контролировать срок действия сертификатов.

Будет полезно разобраться, что такое Service Mesh и какие проблемы она решает. Необязательно разворачивать Service Mesh в каждом кластере Kubernetes. Но желательно понимать, как она работает и зачем вам это нужно.

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

Как разработчик, вы должны знать, как трафик достигает вашего приложения. Если запрос должен выполнить 5 хопов между подами, узлами и службами, при этом каждый хоп имеет потенциальную задержку 100 мс, то ваши пользователи могут столкнуться с задержкой в 500 мс при посещении веб-страницы. Вы должны знать об этом, чтобы ваши усилия по оптимизации времени отклика были сосредоточены на устранении узких мест.

8. Использование неизменяемых тестовых окружений вместо динамических сред

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

Один из наиболее распространенных подходов наличие как минимум трех сред (QA / Staging / Production), в зависимости от размера компании их может быть больше. Наиболее важной из них является интеграционная, которая содержит результат слияния всех обновлений с основной веткой.

Когда вы используете неизменяемые тестовые среды, вы платите за них, даже если они не используются. Но наиболее серьёзной проблемой является изоляция обновлений для последующего тестирования.

Если вы используете единое окружение для интеграции, когда несколько разработчиков выполняют слияние результатов работы и что-то ломается, не сразу становится понятно, какая из функций вызвала проблему. Если 3 разработчика объединяют свои функции в промежуточной среде и развертывание завершается неудачно (либо сборка завершается неудачно, либо тесты интеграции терпят неудачу, либо показатели взрываются...), тогда существует несколько сценариев:

  1. Обновление A содержит ошибки, B и C в порядке

  2. Обновление B содержит ошибки, A и C в порядке

  3. Обновление C содержит ошибки, B и A в порядке

  4. Каждое обновление в отдельности работает, но при работе вместе с обновлениями A и B возникает ошибка

  5. Каждое обновление в отдельности работает, но при работе вместе с обновлениями A и C возникает ошибка

  6. Каждое обновление в отдельности работает, но при работе вместе с обновлениями B и C возникает ошибка

  7. Каждое обновление в отдельности работает, но все 3 обновления вместе не работают

В результате, если после обновления возникли проблемы в работе сервиса, будет непросто понять, какое из них стало причиной возникновения проблемы.

Способы избежать подобных проблем:

  1. "Резервирование" промежуточного (stage) окружения разработчиками, чтобы они имели возможность тестировать свои обновления изолированно.

  2. Компания создает несколько staging сред, которые разработчики используют для тестирования своих обновлений (поскольку единственное staging окружение может быстро стать узким местом).

Эта ситуация по-прежнему приводит к проблемам, потому что разработчики должны не только координировать действия между собой при выборе сред, но также потому, что вы должны отслеживать действия по очистке каждой среды. Например, если нескольким разработчикам требуется набор изменений базы данных, им необходимо убедиться, что база данных содержит только необходимые данные, а не данные предыдущего разработчика, который использовал эту среду.

Кроме того, несколько промежуточных (staging) сред могут страдать от проблемы дрейфа конфигурации, когда среды должны быть одинаковыми, но после нескольких изменений это уже не так.

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

Есть много способов реализовать этот сценарий, по крайней мере, каждый запрос Pull Request должен создавать динамическую среду, содержащую только этот запрос Pull Request и ничего больше. Среда автоматически уничтожается, когда Pull Request объединяется / закрывается или по прошествии определенного времени.

Преимущество динамических сред это полная свобода разработчиков. Если я разработчик и только что закончил работу с обновлением A, а мой коллега завершил работу над обновлением B, я должен иметь возможность:

git checkout mastergit checkout -b feature-a-b-togethergit merge feature-agit merge feature-bgit push origin feature-a-b-together

Как по волшебству, будет создана динамическая среда:feature-a-b-together.staging.company.comилиstaging.company.com/feature-a-b-together.

Для создания динамических сред вы можете использовать, например, пространства имен Kubernetes.

Обратите внимание, что это нормально, если у вашей компании есть постоянные staging среды для специализированных нужд, таких как нагрузочное тестирование, тестирование на проникновения, развертывание A / B и т. д. Но для базового сценария Я разработчик и хочу, чтобы моё обновление работало изолированно и запускались интеграционные тесты динамические среды лучшее решение.

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

9. Смешивание кластеров Production и Non-Production

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

Прежде всего, смешивание Production и Non-Production кластеров может привести к нехватке ресурсов.

Так как приложение, работающее некорректно, может утилизировать слишком много ресурсов.

Для разработчиков самая большая проблема связана с безопасностью. Если вы не предпринимаете дополнительных шагов по настройке Kubernetes, то вопреки распространенному мнению, под из одного пространства имен может свободно взаимодействовать с подом из другого пространства имен. Также есть некоторые ресурсы Kubernetes, которые вообще не имеют пространства имен.

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

Если вы объедините этот антипаттерн со вторым (сохранение конфигурации внутри образов), должно стать очевидно, что может произойти множество негативных сценариев, например:

  1. Разработчик создает новое пространство имен, используемое для тестирования и production.

  2. Далее развертывается приложение и запускаются интеграционные тесты.

  3. Интеграционные тесты записывают фиктивные данные или очищают БД.

  4. К сожалению, в контейнерах были рабочие URL-адреса и конфигурация внутри них, и, таким образом, все интеграционные тесты фактически повлияли на работу production!

Чтобы не попасть в эту ловушку, гораздо проще просто создать Production и Non-Production кластера. К сожалению, многие руководства описывают сценарий при котором, пространства имен можно использовать для разделения сред, и даже в официальной документации Kubernetes есть примеры с пространствами имен prod / dev.

Обратите внимание, что в зависимости от размера вашей компании у вас может быть больше кластеров, чем два, например:

  1. Production

  2. Pre Production (Shadow) клон production, но с меньшими ресурсами

  3. Development кластер, который мы уже обсуждали выше

  4. Специализированный кластер для нагрузочного тестирования / тестирования безопасности

  5. И даже отдельный кластер для служебных инструментов

10. Развёртывание приложений без Limits

По умолчанию приложение, развернутое в Kubernetes, не имеет ограничений на использование ресурсов. Это означает, что ваше приложение потенциально может мешать работе всего кластера, поскольку утечка памяти или высокая загрузка ЦП нанесут ущерб остальным приложениям, работающим в кластере.

Это означает, что все ваши приложения (независимо от окружения) должны иметь ограничения на использование ресурсов.

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

Одно из преимуществ Kubernetes эластичность ресурсов. Если кластер убивает / перезапускает ваше приложение, когда оно начинает обрабатывать значительную нагрузку (например, ваш интернет-магазин испытывает всплеск трафика), вы не используете все преимущества Kubenetes. Вы можете решить эту проблему с использованием vertical pod auto-scaler (VPA).

Подробнее..

Перевод Kubevious революционная панель управления Kubernetes

05.06.2021 16:05:21 | Автор: admin

Стандартная утилита для управления кластером Kubernetes (kubectl) достаточно мощный инструмент администрирования, отладки и мониторинга. Но если вы хотите получить сводную информацию о кластере Kubernetes или работать с несколькими ресурсами одновременно, она уже не так эффективна.

Сегодня существует большое количество графических панелей для управления Kubernetes, вероятно, вы уже используете одну из них. Например, это может быть стандартный Kubernetes Dashboard или панель управления и мониторинга, предоставляемая вашим облачным провайдером. Все эти инструменты очень похожи по методу группировки информации (простая иерархия для пространств имен / ресурсов) и не могут быть легко использованы для ответов на общие вопросы, касающиеся вашего кластера.

В качестве примера предположим, что у вас большой кластер Kubernetes и вы хотите своевременно получать ответы на следующие вопросы:

  1. Сможете ли вы быстро найти все Pods, для которых нет ограничений по ресурсам?

  2. Сможете ли вы быстро найти все Role Bindings, которые не используются?

  3. Сможете ли вы быстро определить, какое пространство имен утилизирует больше всего ресурсов в кластере?

  4. Можете ли вы быстро определить, какие образы используют тег "latest"?

Столкнувшись с этими вопросами, администраторам кластера приходится делать выбор. Либо терять время, используяkubectl, либо создавать специальные сценарии или инструменты, которые исследуют кластер и пытаются найти эти конкретные проблемы.

Было бы неплохо, если графический инструмент мог сразу же ответить на эти вопросы.

Этот отсутствующий графический инструмент Kubevious. Вы можете увидеть живую демонстрацию на https://demo.kubevious.io/, а посмотреть исходный код на https://github.com/kubevious/kubevious.

Переосмысление возможностей Kubernetes Dashboard

Kubevious это революционная панель управления Kubernetes, которая намного функциональнее существующих аналогов. Она имеет множество интересных функций, но в этой статье мы сосредоточимся на описании мощного механизма правил, который позволяет вам находить и группировать ресурсы Kubernetes для решения поставленной задачи.

Kubevious dashboardKubevious dashboard

Kubevious имеет иерархическую структуру, которая будет вам знакома, поскольку он группирует объекты по пространству имен и группирует их в древовидной структуре непосредственно под пространством имен. Однако настоящее волшебство случается, когда вы понимаете, что Kubevious выполняет дополнительный анализ для каждого типа ресурса и маркирует его в соответствии со встроенными или настраиваемыми правилами.

Например, вы без дополнительных настроек увидите значок шпион на объектах, которые имеют доступ к Kubernetes API за пределами их собственного пространства имен.

Spy ObjectsSpy Objects

Возможность получить подобную информацию, особенно в больших кластерах, очень важна, так как значительно сокращает время, затрачиваемое на использование kubectl.

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

Unused Cluster role bindingsUnused Cluster role bindings

Вы можете создавать собственные правила для решения других задач.

Рассуждения о ресурсах Kubernetes

Kubevious имеет собственный механизм правил, который позволяет находить ресурсы Kubernetes с указанными вами характеристиками. Редактор правил также является частью GUI:

Rule editorRule editor

Каждое правило определяется на простом языке Kubik, синтаксис которого напоминает Javascript. Для каждого правила вы определяете тело правила (какой ресурс Kubernetes искать) вместе с маркером (что делать с затронутым ресурсом). Маркеры представляют собой комбинацию значка, имени и цвета, которую вы можете использовать для маркировки затронутых объектов.

Уже существуют библиотеки правил, которые вы можете повторно использовать в своем кластере. Например, вы можете быстро найти модули без ограничений по ресурсам:

Pods without limitsPods without limits
for(var container of item.config.spec.containers){  if (!container.resources.limit)  {    warning('No resource limit set');  }}

В качестве другого примера найдём пространство имен с ресурсами, которые потребляют более 40% ЦП или памяти.

select('Namespace')    .filter(({item}) => {        const cpu = item.getProperties('cluster-consumption').cpu;        const memory = item.getProperties('cluster-consumption').memory;        return (unit.percentage(cpu) >= 40) ||                          (unit.percentage(memory) >= 40);    })

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

Вы можете найти дополнительную информацию о Rule Engine в официальной документации.

Перекрестные проверки и корреляции ресурсов

Другая впечатляющая возможность Kubevious это корреляция между различными ресурсами Kubernetes. После создания правила, которое соответствует определенному количеству ресурсов, вам не нужно вручную прокручивать область просмотра, пытаясь найти, какие из них подходят.

GUI предоставляет вам информацию о том, какие ресурсы затронуты:

Affected resourcesAffected resources

При нажатии на любой из затронутых ресурсов панель управления отобразит сведения об этом ресурсе.

Эта возможность корреляции работает всегда, независимо от выбора объектов Kubevious. Таким образом, если Kubevious может обнаруживать связь между определенными ресурсами, вы сможете её увидеть на общей панели.

Shared resourcesShared resources

Заключение

Механизм правил это лишь одна из функций, предлагаемых Kubevious. Kubevious имеет несколько других интересных функций, таких как полнотекстовый поиск всех ресурсов кластера, а также машина времени для обнаружения изменений конфигурации. Если вы управляете большими кластерами и не хотите больше играть в вопросы с kubectl, то Kubevious легко установить и контролировать в своем кластере.

Посетите сайт https://kubevious.io/ для получения дополнительной информации.

Подробнее..

Перевод Построение кластеров Kubernetes средствами самого Kubernetes

10.06.2021 18:12:02 | Автор: admin

Думаете, я сошел с ума? Я уже сталкивался с такой реакцией, когда впервые предложил развертывать кластеры Kubernetes с помощью Kubernetes.

Но я убежден, что для автоматизации облачной инфраструктуры нет более эффективного инструмента, чем сам Kubernetes.С помощью одного центрального кластера K8s мы можем создать сотни других подконтрольных кластеров K8s. В этой статье я покажу, как это делается.

Примечание.SAP Concur использует AWS EKS, но рассматриваемые здесь концепции также применимы к Google GKE, Azure AKS и любым другим реализациям Kubernetes от облачных провайдеров.

Готовность к эксплуатации в рабочей среде

Создать кластер Kubernetes у любого из распространенных облачных провайдеров стало проще простого. Например, в AWS EKS кластер поднимается одной командой:

$ eksctl create cluster

Совсем другое дело, если нужно получить кластер Kubernetes, готовый к эксплуатации в рабочей среде production-ready Понятие production-ready может толковаться по-разному, но в SAP Concur используются следующие четыре этапа для создания и предоставления кластеров Kubernetes, готовых к эксплуатации в рабочей среде.

Четыре этапа сборки

  • Предварительное тестирование.Перечень простых тестов целевой среды AWS, которые проверяют соответствие всем необходимым требованиям до начала сборки кластера. Например, проверяются доступные IP-адреса в подсетях, экспортируемые параметры для AWS, параметры SSM или другие переменные.

  • Уровень управления EKS и группа узлов.Непосредственно сборка кластера AWS EKS с подключением рабочих узлов.

  • Установка дополнений.Добавим в кластер любимую приправу. :) По желанию можно установить такие дополнения, как Istio, Logging Integration, Autoscaler и пр.

  • Валидация кластера.На этом этапе мы проверяем кластер (основные компоненты EKS и дополнения) с функциональной точки зрения перед его передачей в эксплуатацию. Чем больше тестов вы напишете, тем крепче будете спать. (Особенно, если в техподдержке именно вы на дежурстве!)

Склеиваем все вместе

Четыре этапа сборки включают в себя разные инструменты и методы (мы вернемся к ним позже). Нам нужен был универсальный инструмент для всех этапов, который склеил бы все вместе, поддерживал последовательное и параллельное выполнение, был событийно-ориентированным и, желательно, визуализировал сборку.

В результате мы нашли семейство решений Argo, в частности инструменты Argo Events и Argo Workflows. Они оба запускаются в Kubernetes как CRD и полагаются на декларативную концепцию YAML, как и множество других развертываний Kubernetes.

У нас получилась идеальная комбинация: императивная оркестрация и декларативная автоматизация

Кластер K8s, готовый к эксплуатации в рабочей среде. Создан с помощью Argo WorkflowsКластер K8s, готовый к эксплуатации в рабочей среде. Создан с помощью Argo Workflows

Поэтапная реализация процесса в Argo Workflows

Argo Workflows это движок рабочих процессов с открытым кодом и нативной поддержкой контейнеров, предназначенный для оркестрации параллельных заданий в среде Kubernetes. Argo Workflows реализован как Kubernetes CRD.

Примечание.Если вы знакомы с K8s YAML, обещаю, что вы разберетесь.

Давайте посмотрим, как все эти четыре этапа сборки могут выглядеть в Argo Workflows.

1. Предварительное тестирование

Предварительные тесты выполняются параллельно, с повторением попыток в случае сбоевПредварительные тесты выполняются параллельно, с повторением попыток в случае сбоев

Мы пишем тесты на фреймворке BATS. Написать предварительный тест в BATS очень просто:

#!/usr/bin/env bats@test More than 100 available IP addresses in subnet MySubnet {AvailableIpAddressCount=$(aws ec2 describe-subnets --subnet-ids MySubnet | jq -r .Subnets[0].AvailableIpAddressCount) [ ${AvailableIpAddressCount} -gt 100 ]}

Параллельный запуск приведенного выше тестового файла BATS (avail-ip-addresses.bats) вместе с тремя другими вымышленными тестами BATS через Argo Workflows выглядит следующим образом:

 name: preflight-tests  templateRef:     name: argo-templates    template: generic-template  arguments:    parameters:     name: command      value: {{item}}  withItems:   bats /tests/preflight/accnt-name-export.bats   bats /tests/preflight/avail-ip-addresses.bats   bats /tests/preflight/dhcp.bats   bats /tests/preflight/subnet-export.bats

2. Уровень управления EKS и группа узлов

Уровень управления EKS и группа узлов с зависимостямиУровень управления EKS и группа узлов с зависимостями

Для построения кластера EKS можно использовать любой удобный инструмент. Например, eksctl, CloudFormation или Terraform. Двухэтапное построение базового кластера EKS с зависимостями в Argo Workflows с помощью шаблонов CloudFormation (eks-controlplane.yaml и eks-nodegroup.yaml) реализуется следующим образом.

 name: eks-controlplane  dependencies: [preflight-tests]  templateRef:     name: argo-templates    template: generic-template arguments:   parameters:    name: command     value: |       aws cloudformation deploy \       --stack-name {{workflow.parameters.CLUSTER_NAME}} \       --template-file /eks-core/eks-controlplane.yaml \       --capabilities CAPABILITY_IAM- name: eks-nodegroup  dependencies: [eks-controlplane]  templateRef:     name: argo-templates    template: generic-template  arguments:    parameters:     name: command      value: |        aws cloudformation deploy \        --stack-name {{workflow.parameters.CLUSTER_NAME}}-nodegroup \        --template-file /eks-core/eks-nodegroup.yaml \        --capabilities CAPABILITY_IAM

3. Установка дополнений

Параллельная установка дополнений с зависимостямиПараллельная установка дополнений с зависимостями

Для установки дополнений можно применить kubectl, helm, kustomize или их комбинацию. Например, установка дополнения metrics-server с шаблоном helm и kubectl, при условии что запрошена установка metrics-server, может выглядеть в Argo Workflows следующим образом.

 name: metrics-server  dependencies: [eks-nodegroup]  templateRef:     name: argo-templates    template: generic-template  when: {{workflow.parameters.METRICS-SERVER}} != none  arguments:    parameters:     name: command      value: |        helm template /addons/{{workflow.parameters.METRICS-SERVER}}/ \        --name metrics-server \        --namespace kube-system \        --set global.registry={{workflow.parameters.CONTAINER_HUB}} | \        kubectl apply -f -

4. Валидация кластера

Параллельная валидация кластера с повторением попыток в случае сбоевПараллельная валидация кластера с повторением попыток в случае сбоев

Для валидации дополнений мы применяем BATS-библиотеку DETIK, которая заметно упрощает написание тестов для K8s.

#!/usr/bin/env batsload lib/utilsload lib/detikDETIK_CLIENT_NAME=kubectlDETIK_CLIENT_NAMESPACE="kube-system"@test verify the deployment metrics-server {  run verify there are 2 pods named metrics-server [ $status -eq 0 ]  run verify there is 1 service named metrics-server [ $status -eq 0 ]  run try at most 5 times every 30s to find 2 pods named metrics-server with status being running [ $status -eq 0 ]  run try at most 5 times every 30s to get pods named metrics-server and verify that status is running [ $status -eq 0 ]}

Запуск приведенного выше тестового файла BATS DETIK (metrics-server.bats), при условии что установлено дополнение metrics-server, можно реализовать в Argo Workflows так:

 name: test-metrics-server  dependencies: [metrics-server]  templateRef:    name: worker-containers    template: addons-tests-template  when: {{workflow.parameters.METRICS-SERVER}} != none  arguments:    parameters:     name: command      value: |        bats /addons/test/metrics-server.bats

Только представьте, сколько еще можно сюда подключить тестов. Нужны тесты Sonobuoy, Popeye или Fairwinds Polaris? Просто подключите их через Argo Workflows!

К этому моменту у вас должен получиться полнофункциональный, готовый к эксплуатации в рабочей среде кластер AWS EKS с установленным дополнением metrics-server. Все тесты пройдены, кластер можно принимать в работу. Дело сделано!

Но мы еще не прощаемся самое интересное я оставил напоследок.

Шаблоны рабочих процессов

Argo Workflows поддерживает многоразовые шаблоны рабочих процессов (WorkflowTemplates). Каждый из четырех этапов сборки представляет собой такой шаблон. По сути, мы получили сборочные элементы, которые можно произвольно комбинировать друг с другом. Все этапы сборки можно выполнять по порядку через главный рабочий процесс (как в примере выше) или можно запускать их независимо друг от друга. Такая гибкость стала возможной благодаря Argo Events.

Argo Events

Argo Events это событийно-ориентированный фреймворк для Kubernetes, который позволяет инициировать объекты K8s, Argo Workflows, бессерверные рабочие нагрузки и другие операции на основе различных триггеров, таких как веб-хуки, события в S3, расписания, очереди сообщений, Google Cloud Pub/Sub, SNS, SQS и пр.

Сборка кластера запускается посредством API-вызова (Argo Events) с использованием полезной нагрузки из JSON. Кроме того, каждый из четырех этапов сборки (WorkflowTemplates) имеет собственную конечную точку API. Операторы Kubernetes (тоесть люди) получают явные преимущества:

  • Не уверены, в каком состоянии находится облачная среда? Вызывайте API предварительных тестов.

  • Хотите собрать голый кластер EKS? Вызывайте API eks-core (control-plane и nodegroup).

  • Хотите установить или переустановить дополнения в существующем кластере EKS? Вызывайте API дополнений.

  • Кластер начал чудить и вам нужно быстро его протестировать? Вызывайте API тестирования.

Возможности Argo

Решения Argo Events и Argo Workflows предлагают широкий функционал прямо из коробки, не нагружая вас лишней работой.

Вот семь самых востребованных функций:

  • Параллелизм

  • Зависимости

  • Повторные попытки (см. выделенные красным предварительные тесты и тесты валидации на рисунках выше: они завершались сбоем, но Argo повторял их до успешного прохождения)

  • Условия

  • Поддержка S3

  • Шаблоны рабочих процессов

  • Параметры сенсоров событий

Заключение

Мы подружили множество различных инструментов и смогли через них императивно задать желаемое состояние инфраструктуры. Мы получили гибкое, бескомпромиссное и быстрое в реализации решение на основе Argo Events и Workflows. В планах приспособить эти инструменты под другие задачи автоматизации. Возможности безграничны.


Перевод материала подготовлен в рамках курса Инфраструктурная платформа на основе Kubernetes. Всех желающих приглашаем на двухдневный онлайн-интенсив Примитивы, контроллеры и модели безопасности k8s. На нем будет обзор и практика по основным примитивам и контроллерам к8с. Рассмотрим, чем отличаются и в каких случаях используются. Регистрация здесь

Подробнее..

Kubernetes Headless Service А если Pod исчез?

17.06.2021 18:06:38 | Автор: admin

Мы столкнулись с достаточно занятным поведением при работе с Headless-сервисом в Kubernetes. В нашем случае проблема возникла с mongos, но она актуальна для любого Headless-сервиса. Приглашаю вас почитать нашу историю и самим попробовать поиграться с этой проблемой локально.

На одном из проектов мы используем MongoDB и Kubernetes. У MongoDB есть компонент: mongos. Через него выполняются запросы в шардированном MongoDB кластере (можно считать, что это просто хитрый proxy). До переезда в Kubernetes сервисы mongos устанавливались непосредственно на каждый хост.

При переезде сервисов в Kubernetes мы поселили пул mongos в Headless-сервис с автоматическим масштабированием Deployment через HPA (Horizontal Pod Autoscaler).

Через некоторое время выяснилось, что приложению при уменьшении количества Pod с mongos становится не очень хорошо.

Путем отладки выяснилось, что приложение подвисает именно при попытке установить подключение с mongos (net.Dialв терминах Go) и по времени совпадает с остановкой какого-либо Pod.

Для начала надо уточнить, что такое Headless-сервис: это сервис, который не использует отдельный IP-адрес для маршрутизации запросов (ClusterIP: None). В этом случае под DNS-именем сервиса видны IP всех Pod, которые в этот сервис входят.

Headless-сервисы полезны, когда приложение само должно управлять тем, к какому Pod подключаться, например:

  • mongodb-клиент использует IP сервера, с которым он работает, для того, чтобы запросы для одного курсора шли на один хост (курсор живёт на mongos). В случае использованияClusterIPмогут теряться курсоры даже для коротких запросов.

  • gRPC-клиенты держат по одному соединению с сервисами и сами управляют запросами, мультиплексируя запросы к одному серверу. В случае использованияClusterIPклиент может создать одно подключение и нагружать ровно один Pod сервера.

Так как клиент сам управляет, к каким Pod он подключается, возможна ситуация, когда клиент помнит IP-адрес уже удалённого Pod. Причины этого просты:

  • список Pod передаётся через DNS, а DNS кэшируется;

  • клиент сам по себе кэширует ответы от DNS и список сервисов.

Что же происходит в случае, если клиент пытается подключиться к уже несуществующему Pod?

А в этом случае запросы уходят уже на немаршрутизируемый хост и на них никто не отвечает. Так как ответа нет, клиент начинает слать повторные запросы на подключение пока не пробьёт таймаут.

При этом, в случае если Pod еще не поднялся или был отстрелен по Out of Memory, но еще не был удалён, то при попытке подключиться клиент получает ошибку connection refused практически сразу. И это гораздо более гуманное решение, чем ждать у моря погоды пока не пробьём таймаут.

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

  • Мы добавили ожидание сигналаSIGTERMв Pod с mongos. При получении этого сигнала мы продолжали работать еще 45 секунд до времени инвалидации DNS (чтобы адреса новых Pod доехали до клиента). После этой паузы завершали mongos и делали еще одну паузу в 15 секунд (чтобы переподключение по старому IP отшивалось по ошибке connection refused, а не таймауту).

  • Мы выставилиterminationGracePeriodSecondsв две минуты, чтобы Pod принудительно не отстрелили до его завершения.

Небольшая ремарка по поводу minReadySeconds

Проблема с остановкой Pod наиболее ярко проявляет себя при перевыкатке сервисов.

Изначально первопричиной казалось то, что выкатка успевает завершиться быстрее, чем обновляются кэши IP-адресов сервиса в клиентском приложении (клиент пытается идти на старые Pod которых нет, а про новые он еще не знает).

Для исправления мы просто замедлили выкатку с помощью параметраminReadySeconds. Это сделало проблему менее острой, но не решило её: остались таймауты при подключении к IP для уже не существующего Pod.

Тем не менее параметрminReadySecondsполезен из-за того, что выкатка не ждёт завершения удаления Pod после перехода его в состояниеTerminating. В результате при раскатке сервиса мы можем на время добавленных пауз получить x2 Pod.

К тому же, если на клиенте не возникает нежелательных эффектов от недоступности части IP-адресов сервиса, то задержку для инвалидации DNS можно переместить вminReadySeconds.

Примером, для которого достаточно толькоminReadySecondsявляются gRPC-сервисы: там клиент держит по одному подключению к каждому серверу и раскидывает запросы между уже имеющимся подключениями, а не подключается к сервису при создании клиентской сессии.

Как поиграться с этой проблемой локально?

Эту ситуацию можно легко воспроизвести в MiniKube на примере nginx.

Для этого надо понадобится headless Service (service.yml):

---apiVersion: v1kind: Servicemetadata:  name: nginxspec:  clusterIP: None  selector:    app: nginx  ports:    - protocol: TCP      port: 80      targetPort: 80

И тестовая утилита (dialer.go):

package mainimport ("fmt""net""os""time")const timeFormat = "15:04:05.999"func main() {address := os.Args[1]last := ""ticker := time.NewTicker(time.Millisecond * 100)t := time.Now()fmt.Printf("%s: === %s\n", t.Format(timeFormat), address)for {conn, err := net.DialTimeout("tcp", address, time.Millisecond*100)var msg stringif conn != nil {msg = fmt.Sprintf("connected (%s)", conn.RemoteAddr())_ = conn.Close()}if err != nil {msg = err.Error()}if last != msg {now := time.Now()if last != "" {fmt.Printf("%s: --- %s: %v\n", now.Format(timeFormat), last, now.Sub(t))}last = msgfmt.Printf("%s: +++ %s\n", now.Format(timeFormat), last)t = now}<-ticker.C}}

Запустим тестовую утилиту для подключения к сервису nginx по 80-му порту. Она будет выводить результат попытки подключиться к сервису (пока не успешный, так как сервис смотрит вникуда):

#!/bin/bashecho "tee dialer.go << EEOF$(cat dialer.go)EEOFgo run dialer.go nginx:80" | kubectl --context=minikube run -i --rm "debug-$(date +'%s')" \            --image=golang:1.16 --restart=Never --

Вывести она должна что-то вида:

16:57:19.986: === nginx:8016:57:19.988: +++ dial tcp: lookup nginx on 10.96.0.10:53: server misbehaving

Пока оставим окно с утилитой и потом будем в него посматривать.

Простой Deployment без задержек

Добавим в сервис Deployment (nginx.yml):

---apiVersion: apps/v1kind: Deploymentmetadata:  name: nginxspec:  replicas: 1  selector:    matchLabels:      app: nginx  template:    metadata:      labels:        app: nginx    spec:      containers:        - name: nginx          image: nginx:1.14.2          ports:            - containerPort: 80

Параметрreplicasдля эксперимента равен единице, чтобы не скакать между IP-адресами.

На боевом Deployment должны быть так жеlivenessProbeиreadinessProbe. Но в данном эксперименте они будут только мешать.

И сделаем обновление Deployment:

#!/bin/bashkubectl --context minikube rollout restart deployment/nginx

От этой команды произойдёт перевыкатка Deployment. При этом важно отметить, что схема выкатки по умолчанию: поднять новый Pod и только затем погасить старый Pod. То есть всегда будет запущен как минимум один Pod.

В выводе тестовой утилиты мы увидим примерно следующее (комментарии добавлены отдельно):

# Здесь мы подключились к созданному Deployment и до обновления попытки# подключения были успешны17:04:08.288: +++ connected (172.17.0.10:80)17:07:32.187: --- connected (172.17.0.10:80): 3m23.899438044s# Здесь завершился nginx при остановке Pod, но клиент еще идет по старому# кэшированному IP.# Так как Pod существует, мы быстро получаем ошибку "connection refused"17:07:32.187: +++ dial tcp 172.17.0.10:80: connect: connection refused17:07:32.488: --- dial tcp 172.17.0.10:80: connect: connection refused: 301.155902ms# Старый Pod уже удалён, но клиент всё еще идет по старому кэшированному IP.# Так как по IP-адресу уже никто не отвечает, мы пробиваем таймаут.17:07:32.488: +++ dial tcp 172.17.0.10:80: i/o timeout17:07:38.448: --- dial tcp 172.17.0.10:80: i/o timeout: 5.960150161s# Старый IP покинул кэш и мы подключились к новому Pod.17:07:38.448: +++ connected (172.17.0.7:80)

Добавляем задержку перед удалением Pod

Добавим в Deployment паузу после завершения сервиса, чтобы вместо долгого таймаута получать быстрый connection refused:

#!/bin/bashkubectl --context minikube patch deployment nginx --output yaml --patch '---spec:  template:    spec:      containers:        - name: nginx          command: [ "sh" ]          # Добавляем паузу после завершения nginx          args:            - "-c"            - "nginx -g \"daemon off;\" && sleep 60"          # К сожалению, sh не пробрасывает SIGTERM в дочерний процесс          lifecycle:            preStop:              exec:                command: ["sh", "-c", "nginx -s stop"]      # Увеличиваем время, которое отводится на остановку Pod-а перед      # его безусловным завершением      terminationGracePeriodSeconds: 180'

Эта пауза нужна только при корректном завершении Pod (в этом случае процесс получаетSIGTERM). Если процесс завершается, к примеру, по Out Of Memory или Segmentation fault, то её быть не должно.

И еще раз сделаем обновление Deployment:

#!/bin/bashkubectl --context minikube rollout restart deployment/nginx

В выводе тестовой утилиты мы увидим примерно следующее (комментарии добавлены отдельно):

# Здесь мы подключились к созданному Deployment и до обновления попытки# подключения были успешны17:58:10.389: +++ connected (172.17.0.7:80)18:00:53.687: --- connected (172.17.0.7:80): 2m43.29763747s# Здесь завершился nginx при остановке Pod, но клиент еще идет по старому# кэшированному IP.# Так как Pod существует, мы быстро получаем ошибку "connection refused".# Существовать Pod будет до тех пор пока не завершится sleep после nginx.18:00:53.687: +++ dial tcp 172.17.0.7:80: connect: connection refused18:01:10.491: --- dial tcp 172.17.0.7:80: connect: connection refused: 16.804114254s# Старый IP покинул кэш и мы подключились к новому Pod.18:01:10.491: +++ connected (172.17.0.10:80)

Добавляем задержку перед остановкой Pod

Добавим в Deployment паузу перед завершением сервиса, чтобы сервис отвечал, пока адрес Pod не покинет кэш на клиенте:

#!/bin/bashkubectl --context minikube patch deployment nginx --output yaml --patch '---spec:  template:    spec:      containers:        - name: nginx          # Добавляем задержку перед остановкой nginx          lifecycle:            preStop:              exec:                command: ["sh", "-c", "sleep 60 && nginx -s stop"]      # Увеличиваем время, которое отводится на остановку Pod перед      # его безусловным завершением      terminationGracePeriodSeconds: 180'

И еще раз сделаем обновление Deployment:

#!/bin/bashkubectl --context minikube rollout restart deployment/nginx

В выводе тестовой утилиты мы увидим примерно следующее (комментарии добавлены отдельно):

# Здесь мы подключились к созданному Deployment и до обновления попытки# подключения были успешны18:05:10.589: +++ connected (172.17.0.7:80)18:07:10.689: --- connected (172.17.0.7:80): 2m0.099149168s# Старый IP покинул кэш и мы подключились к новому Pod.# Старый Pod еще отвечает и из-за этого переключение прошло гладко.18:07:10.689: +++ connected (172.17.0.10:80)

Какие нужны задержки?

Итого: для гладкого переключения необходимо две задержки.

  • МеждуSIGTERMи остановкой приложения чтобы на момент отключения клиента он не мог получить из DNS-кэша ровно тот же Pod и пойти на него.

    Эта задержка должна быть не меньше, чем время жизни записи в DNS-кэше.

    Делать эту паузу больше, чем сумма времени жизни записи в DNS-кэше и времени жизни записи в кэше приложения не имеет особого смысла.

    Если на клиенте не возникает нежелательных эффектов от недоступности части IP-адресов сервиса, то вместо паузы послеSIGTERMможно использоватьminReadySeconds.

  • Между остановкой приложения и завершением Pod, чтобы при попытке клиента подключиться/переподключиться к этому Pod мы получали быстрый connection refused, а не ждали всё время таймаута.

    Эта задержка должна быть подобрана так, чтобы с момента полученияSIGTERMи до завершения Pod прошло время не меньше суммы времени жизни записи в DNS кэше и времени жизни записи в кэше приложения.

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

Конкретные длительности задержек надо подбирать индивидуально.

Подробнее..

Kubernetes как сервис изучение рынка

24.05.2021 20:06:11 | Автор: admin

Привет, Хабр! Как и многие другие, в прошлом году мне пришлось внезапно мигрировать из тесного привычного офиса к себе домой. Я и раньше работал из дома, когда была такая нужда. Но чтобы несколько месяцев подряд такое со мной случилось впервые. Появилось свободное время, которое я сначала не знал, куда девать. Но потом приспособился, начав изучать вещи, до которых раньше не доходили руки.

Я поковырялся в инвестиционных играх на бирже, познакомился с облачным геймингом, а ещё успел почитать о том, что за зверь такой появился, про который из каждого умного утюга говорят Kubernetes. Начав с блога Фланта, я убедился, что конкретно мне и конкретно сейчас эта штука не нужна, но выглядит она интересно.

Почитал, одобрил и забыл, да вот только буржуйский Facebook про меня забывать не хотел. И где-то с неделю показывал мне рекламу с самыми наивыгодными предложениями этого самого кубернетиса. В результате я в очередной раз продемонстрировал моральную слабость и решил лично познакомиться с этим зверем.

Дисклеймер

Главное, что я понял, так это что если вы не слышали про Kubernetes и не понимаете, как его использовать в вашей работе, то в 99% случаев он вам и не нужен. Но сама идея сокращения цикла разработки за счёт быстрой доставки пользователю и возможности тестировать версии приложений на узких сегментах аудитории прекрасна. Посмотрел, что получилось, после чего можно распространить версию приложения на всех пользователей или немедленно её откатить.

Но продолжу тему знакомства с этой модной платформой управления контейнерами. Я решил не ограничивать себя одной компанией, наиболее часто мозолившей глаз в Facebook. А выбрать несколько более-менее крупных фирм, у которых есть толковое предложение.

Как выбирал

Вы удивитесь, но поиском. Погуглил Kubernetes в облаке, пролистал пару страниц. Так и нашёл семь компаний, которые наиболее активно продвигают эту услугу: Mail.ru Cloud Solutions, Cloud4Y, CloudMTS, Yandex.Cloud, КРОК, DataLine, Selectel.

Было бы круто воспользоваться сервисом подбора провайдеров, где можно указать фильтры по ценам, возможностям и прочему. Но увы, я такого сервиса не нашёл, так что делал всё ручками. И если кого шибко важного и крупного не указал, так это не по причине моей зловредности, а потому что реклама у них слабенькая. Ну, и или страницы в поиске глубоко находятся. В общем, кого нет в списке, того я не нашёл. Прошу понять, простить и не гнобить.

Про субъективность

Это абсолютно необъективная статья. Было просто интересно, что это за зверь, какой сервис выглядит удобнее и надёжнее остальных, какой проще и понятнее именно для меня. Так что учитывайте, что мой опыт это всего лишь мой опыт. У вас могут быть другие предпочтения и знания. Рассматривайте данный текст просто как информацию, абстрактное общее сравнение, а не прямое руководство к действию.

Ещё момент: я собирал информацию, которая есть в открытом доступе, стараясь не задалбывать менеджеров глупыми частыми вопросами по почте и телефону. Мне было неловко звонить и расспрашивать, если покупать всё равно не планирую. Тратить чужое время из-за неуёмного моего любопытства было как-то стыдно. Ну захотелось мне проверить, будет ли работать их сервис, нравится ли он мне как пользователю. Были некоторые моменты, которые я узнавал по телефону и почте, но в основном все данные брал из документации и маркетинговых материалов.

Здесь возникли первые сложности. Я старался звонить по-минимуму, но тот же Яндекс без звонка хоть что-то сообщать отказывался. Остальные без проблем написали о том, что они предлагают и как всё это работает. Ну и ладно, подумал я. Информации в свободном доступе тоже хватает. Вернусь позднее и пообщаюсь, раз они такие буки. Или не пообщаюсь, если к тому времени уже натестируюсь по самый докер. Это была зима 2020, и у меня было много свободного времени.

Знакомство

Звонить и писать сразу всем компаниям гиблое дело, особенно если не готов покупать, а хочешь лишь проверить, как оно работает. Поэтому я потихоньку запрашивал тестовый доступ у каждой компании по очереди. Быстрее всех ответили Selectel, Cloud4Y и MCS, а вот Крок и DataLine оказались не столь быстрыми. Мне даже пришлось несколько раз ментально их пнуть напоминать о себе, чтобы получить какой-то ответ.

И уже на этом этапе круг подопытных кроликов провайдеров сузился. DataLine вообще никак не захотел со мной общаться. Письма ушли в молоко. Ну и ладно, подумал я. И нечего нас дёргать со всякими глупостями, подумал неизвестный мне менеджер DataLine. КРОК вежливо извинился и сообщил, что мелочь вроде меня им не интересна. Неприятно, но честно. Спасибо за это. Лучше честный отказ, чем игнор или затягивание времени.

Отмечу, что прямо-таки агрессивных продаж не наблюдалось. Понятно, что я не самая крупная рыбка, которая водится в облачном море. Но всё же. Порадовало, что мне не впаривают, а предлагают. Убеждают. Правда, почти все компании сочли своим долгом сделать меня подписчиком полезных корпоративных рассылок. Но к этому я был готов.

Чуть забегая вперёд скажу, что поскольку компаний стало меньше, а я вошёл во вкус, то в процессе тестирования сервисов Kubernetes вспомнил и про Яндекс. А не позвонить ли мне им?, подумалось мне. Как оказалось, не зря у этих ребят оказалось не самое плохое решение из тех, что мне удалось посмотреть.

Тестирование

Теперь давайте посмотрим, чего я натестировал.

Компания

Selectel

Mail.ru Cloud

МТС Cloud

Yandex.Cloud

Cloud4Y

Платформа, на которой построено решение

OpenStack + KVM

OpenStack + KVM

Стек технологий виртуализации VMware vSphere и NSX-T

Собственная разработка

Предоставляется на базе Container Service Extension (CSE) в VMware Cloud Director

В большинстве своём решение строится на OpenStack + KVM. Это часто используемая связка. OpenStack это тот же Kubernetes, но захватывающий уровень виртуальных машин. Он бесплатен, у него большая комьюнити, и есть ответы на многие вопросы. Но иногда поиск этих ответов занимает много времени, а в случае работы с виртуальной инфраструктурой время часто бывает критически малым ресурсом. Кроме того, специалистов, умеющих работать с OpenStack, на рынке не так много.

Также провайдеры используют стек технологий виртуализации VMwarevSphereиNSX-T. NSX-T предназначен для гибридных окружений, как в смысле поддержки разных гипервизоров (ESXi и KVM), так и в плане поддержки облачных инфраструктур (например, AWS). VMware платный, но зато предлагает гарантированную поддержку от вендора с ответами на сложные вопросы и кейсы. И специалистов по VMware найти проще.

У Яндекса собственная разработка. Я даже накопал классную информацию об этом. Подход имеет место быть. Насколько удобно решение для пользователей должны сказать сами пользователи. Явных косяков я не заметил. Неявных найти не удалось из-за недостаточной экспертизы в этом вопросе. Если хотите, пните меня в комментариях.

Все решения дают обширный функционал в реализации облаков. Я не могу сказать, что какое-то из них лучше или хуже. Разве что самопис может насторожить. То есть выбор платформы дело вкуса.

Облачные балансировщики нагрузки

Компания

Selectel

Mail.ru Cloud

МТС Cloud

Yandex.Cloud

Cloud4Y

Балансировщик

Создаётся вместе с кластером

Создаётся автоматически с кластером

По умолчанию открыты только 80 и 443 порты. Балансировщик добавляется автоматически при создании кластера

Создаётся дополнительный балансер

Создаётся дополнительный балансер

Если говорить про балансировщик, распределяющий нагрузку, то в Selectel он создаётся вместе с кластером. Автоматическое создание балансировщика вместе с кластером предлагает Mail.ru, у него возможно автоматическое и быстрое масштабирование до 1000 узлов. А вот МТС удивил. Да, балансировщик создаётся автоматически при создании кластера. Но умолчанию открыты только 80 и 443 порты. Дополнительные порты можно открывать только через поддержку. Яндекс и Cloud4Y тоже предлагают создание дополнительного балансировщика. На этот случай есть специально написанные инструкции, ссылку на которые они мне заботливо прислали.

Управление

Если говорить про управление нашими драгоценными контейнерами и кластерами, то стоит сказать, что компании используют проверенные типовые решения:

Компания

Selectel

Mail.ru Cloud

МТС Cloud

Yandex.Cloud

Cloud4Y

Управление

Web/API

Web/API

API

Web/Console Yandex.Cloud

Web/API

Mail.ru предлагает варианты управления кластером как через Kubernetes Dashboard, так и через kubectl. Web/API возможны в Cloud4Y и Selectel. У МТС только API. К тому же, управление docker-контейнерами из интерфейса не предусмотрено. В интерфейсе происходит управление кластерами Kubernetes. Яндекс пошёл своим путём. У них предлагается Web и Console Yandex.Cloud. Мне показалось не очень удобным и логичным, когда для работы требуется специальная яндексовая консоль. Может, есть и какой-то другой способ, только я его не нашёл.

Хранилища данных

Компания

Selectel

Mail.ru Cloud

МТС Cloud

Yandex.Cloud

Cloud4Y

Persistent Volumes

Поддерживает блочные и сетевые устройства NFS

Блочное устройство накладывает на ограничения по readwritemany

Persistent Volumes идёт через блочное устройство что накладывает на ограничения по readwritemany а можно только одной ноде писать на него

Блочные устройства могут работать только в ReadWriteOnce

Поддерживает блочные и сетевые устройства NFS

Persistent Volumes можно считать аналогом нод в самом кластере Kubernetes. Зачем вообще нужна эта штука? Допустим, у вас есть несколько разных хранилищ. К примеру, одно быстрое на SSD, а другое медленное на HDD. Вы можете создать два Persistent Volumes в соответствии с этим, а затем выделять подам место в этих томах. Kubernetes поддерживает огромное количество подключаемых томов с помощью плагинов.

Вот тут у провайдеров используются разные решения. Cloud4Y и Selectel поддерживают как блочные, так и сетевые устройства NFS. И это хорошо. У Mail.ru блочное устройство накладывается на ограничения по ReadWriteMany(RWX). В документации указано, что механизм Persistent Volume позволяет подключить существующий Cinder Volume, находящийся на кластере Ceph в качестве постоянного хранилища данных к подам. Хранилище на базе Ceph обеспечивает сохранность данных за счёт трёхкратной репликации. У МТС Cloud Persistent Volumes тоже идёт через блочное устройство, что накладывается на ограничения по ReadWriteMany. Можно только одной ноде писать на него. В Yandex.Cloud блочные устройства могут работать только в ReadWriteOnce(RWO).

Контроллеры Ingress

Компания

Selectel

Mail.ru Cloud

МТС Cloud

Yandex.Cloud

Cloud4Y

Ingress

Добавлять нужно самостоятельно

Можно выбрать при установке кластера

Можно добавить убрать при создании кластера

Ingress на базе LoadBalancer

В случае необходимости разворачивается вручную по инструкции

У Selectel в версии Managed Kubernetes Ingress Controller не предустанавливаются в кластеры. Для создания объектов Ingress потребуется самостоятельно установить Ingress Controller. Обратите внимание, что при создании Ingress Controller у вас, вероятнее всего, будет создан Service с типом LoadBalancer помимо самого приложения Ingress Controller. В таком случае в вашем проекте будет автоматически создан дополнительный балансировщик нагрузки, который отобразится в разделе Балансировщики нагрузки.

Mail.ru позволяет выбрать Ingress при создании кластера. Кластеры Kubernetes, устанавливаемые в MCS содержат преднастроенный Ingress Controller на базе балансировщика нагрузки Nginx, который может обеспечить доступ к вашим сервисам, используя один и тот же выделенный балансировщик нагрузки OpenStack. МТС позволяет добавить/убрать Ingress при создании кластера. В документации МТС сразу говорится, что NGINXIngress разворачивается в стандартной настройке при запуске кластера при выборе соответствующего параметра. У Яндекса Ingress построен на базе LoadBalancer. Cloud4Y предлагает разворачивать Ingress в ручном режиме при необходимости.

Масштабирование

Компания

Selectel

Mail.ru Cloud

МТС Cloud

Yandex.Cloud

Cloud4Y

Масштабирование

Нет

Есть autoscaling, по определенным параметрам загрузки нод, добавляет и убавляется автоматически

Нет

Есть autoscaling, по определенным параметрам загрузки нод, добавляет и убавляется автоматически

AutoScaling на уровне pod определяется на основании конфигурации k8s. Autoscaling на уровне воркеров\нод реализован через vCloud или вручную прописываемые скрипты

У Селектела и МТС нет автоматического масштабирования. Работайте ручками, господа! Надо уточнить, что в МТС автоскейлинг будет добавлен в этом (2021)году. Яндекс и Майл предлагают более удобные условия. У них есть autoscaling по определенным параметрам загрузки нод, добавляется и убавляется автоматически. Cloud4Y предлагает автоскейлинг на уровне pod который определяется на основании конфигурации k8s. Autoscaling на уровне воркеров\нод на лету отсутствует, но можно зайти в vCloud и докинуть ноду или написать скрипты, которые делают то же самое, только запрограммированно

Автоскейлинг удобен, потому что он выполняет мониторинг приложений и автоматически настраивает ресурсы для поддержания стабильной прогнозируемой производительности при минимально возможных затратах. То есть приложения получают нужный объём ресурсов в нужное время. Это важный параметр. И отсутствие автоскейлинга несколько напрягает.

Мониторинг

Компания

Selectel

Mail.ru Cloud

МТС Cloud

Yandex.Cloud

Cloud4Y

Мониторинг

Добавлять нужно самостоятельно

При создании кластера можно добавить Prometheus, Grafana

По стандарту ничего нет, нужно добавлять самостоятельно

Автоматически ничего нет, самому нужно все ставить

Добавлять нужно самостоятельно

Мониторинг это неотъемлемая часть любой инфраструктуры. Правильно настроенный мониторинг четко отражает здоровье ИТ-системы и про активно диагностирует её состояние. В данном случае все используют стандартное решение для VMware в виде плагина. У него есть свои ограничения, 1 мастер нода и 5 воркеров.

Cloud4Y по запросу прислал инструкции, как сделать 3 мастер ноды и добавить балансировщик. Ссылками и вложенными файлами с руководством поделились и другие провайдеры. Правда, Selectel и МТС очень долго раскачивались. Видимо, искали, где у них это всё написано.

Мне показалось довольно удобным решение, когда плагин позволяет быстро и легко развернуть среду и начать работать с ней в минимальном, но достаточном для старта объёме. Новичкам в самый раз. Компании, которые уже давно используют функционал, способны развернуть среду самостоятельно, без плагина и пользуясь только IaaS-инфраструктурой.

Персональные данные

Компания

Selectel

Mail.ru Cloud

МТС Cloud

Yandex.Cloud

Cloud4Y

ПоддержкаФЗ-152

Для самой Облачной платформы проведена оценка, для УЗ 3-4. Для остальных сервисов на её базе, в том числе Kubernetes такая оценка возможна позднее

Полное соответствие в ФЗ 152 О защите персональных данных, сервера находятся на территории России;

Поддержки 152-фз нет. Kubernetes планируется развернуть в защищенном сегменте в 2021 году.

Не получил внятного ответа

В ФЗ облаке оно присутствует и полностью соответствует ФЗ.

ФЗ-152 сейчас в тренде, и провайдеры активно развивают это направление. Selectel, Mail.ru Cloud, Cloud4Y обещают, что их решения соответствуют требованиям закона о персональных данных. С Yandex.Cloud я не понял, есть ли такая штука. Похоже, что нет, ведь иначе об этом где-нибудь, да говорилось бы. МТС Cloud пока лишь дорабатывает свою систему.

Так или иначе, работать с персональными данными в кластерах Kubernetes можно у большинства облачных провайдеров.

Оплата

Компания

Selectel

Mail.ru Cloud

МТС Cloud

Yandex.Cloud

Cloud4Y

Какой биллинг

Биллинг почасовой

Pay-as-you-Go почасовой биллинг

Биллинг помесячный

Есть разные варианты, включая помесячный биллинг

Биллинг помесячный

Тут, наверное, и говорить нечего. Особых нововведений в плане оплаты нет.

Отличия от конкурентов

Компания

Selectel

Mail.ru Cloud

МТС Cloud

Yandex.Cloud

Cloud4Y

Отличия решения от конкурентов

Адаптировали Kubernetes в Облачной платформе, чтобы пользователи могли получить стандартный Kubernetes с поддержкой от Selectel.

Тройная репликация и отказоустойчивость (все копии хранятся в трёх томах на разных серверах); пропускная способность (канал трафика 1ГБ/сек); мощный процессор Intel Xeon E5-2660v4.

Принципиально отличается более отказоустойчивой системой виртуализации VMware

Просто хороший сервис.

Квалифицированная поддержка по данному продукту, а также необходимая инфа в КБ.

Самый интересный пункт, на который я рассчитывал. Но увы. Все провайдеры говорили примерно одно и то же. Каких-то особых плюшек не придумано. Это не в укор провайдерам. Это скорее вопрос к их рекламщикам почему решения так сильно похожи друг на друга?

Mail.ru делает упор на тройную репликацию данных и отказоустойчивость, Selectel на привычный типовой формат работы с Kubernetes, Cloud4Y обещает мощную техническую поддержку и развитую документацию в базе знаний. МТС подчёркивает преимущества VMware в плане отказоустойчивости, а Яндекс порадовал формулировкой. Их сервис хороший, и это его ключевое отличие от остальных.

Выводы

Возможно, вы ждёте, что я сейчас скажу, что какой-то сервис хороший, а остальные плохие. А вот и нет! Чисто субъективно мне понравились две компании, но у вас могут быть совершенно другое мнение. Поэтому рекомендовать никого не буду. На вкус и цвет все фломастеры разные.

Отмечу, что техническая поддержка у некоторых компаний оставляет желать лучшего. Чтобы завести кластер или решить проблему, приходилось долго общаться с инженерами. А менеджеры радостно шлют ссылки на видео, где рассказывается совсем не про то, что мне нужно. Возможно, это из-за того, что я был на тестовом периоде, и денег с меня получить было нельзя.

Также есть сложности с тем, чтобы вообще разобраться с платформой. Видно, что предложение ещё не у всех допилено. В одном случае я не смог создать кластер, пока напрямую не пообщаться с инженером провайдера. Хотя у другого всё сделал сам и почти без проблем. В целом, все предложения рабочие, но провайдерам надо развиваться, чтобы их решения могли использовать даже люди с минимальными знаниями в этой сфере.

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

Подробнее..

Перевод Сеть контейнеров это не сложно

26.05.2021 22:23:37 | Автор: admin

Работа с контейнерами многим кажется волшебством, пришло время разобраться как работает сеть контейнеров. Мы покажем на примерах, что это совсем не сложно. Помните, что контейнеры - всего лишь изолированные процессы Linux.

В этой статье мы ответим на следующие вопросы:

  • Как виртуализировать сетевые ресурсы, чтобы контейнеры думали, что у каждого из них есть выделенный сетевой стек?

  • Как превратить контейнеры в дружелюбных соседей, не дать им мешать друг другу и научить хорошо общаться?

  • Как настроить сетевой доступ из контейнера во внешний мир (например, в Интернет)?

  • Как получить доступ к контейнерам, работающим на сервере, из внешнего мира (публикация портов)?

Отвечая на эти вопросы, мы настроим сеть контейнеров с нуля, используя стандартные инструменты Linux. В результате станет очевидно, что сеть контейнеров - это не что иное, как простая комбинация хорошо известных возможностей Linux:

  • Network namespaces

  • Virtual Ethernet devices (veth)

  • Virtual network switches (bridge)

  • IP маршрутизация и преобразование сетевых адресов (NAT)

Нам потребуется немного сетевой магии и никакого кода ...

С чего начать?

Все примеры в статье были сделаны на виртуальной машине CentOS 8. Но вы можете выбрать тот дистрибутив, который вам нравится.

Создадим виртуальную машину с помощью Vagrant и подключимся к ней по SSH:

$ vagrant init centos/8$ vagrant up$ vagrant ssh[vagrant@localhost ~]$ uname -aLinux localhost.localdomain 4.18.0-147.3.1.el8_1.x86_64

Мы не будем использовать какое-либо популярное решение для контейнеризации (например, docker или podman). Вместо этого мы сосредоточимся на основных концепциях и воспользуемся минимальным набором инструментов для достижения наших учебных целей.

Изоляция контейнеров с помощью Network namespaces

Что составляет сетевой стек Linux? Ну, очевидно, набор сетевых устройств. Что еще? Набор правил маршрутизации. И не забываем про настройку netfilter, создадим необходимые правила iptables.

Напишем небольшой скрипт inspect-net-stack.sh:

#!/usr/bin/env bashecho "> Network devices"ip linkecho -e "\n> Route table"ip routeecho -e "\n> Iptables rules"iptables --list-rules

Но прежде, чем запускать его, давайте внесём изменения в правила iptables, чтобы идентифицировать их в дальнейшем:

$ sudo iptables -N ROOT_NS

Запускаем скрипт:

$ sudo ./inspect-net-stack.sh> Network devices1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:002: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000    link/ether 52:54:00:e3:27:77 brd ff:ff:ff:ff:ff:ff> Route tabledefault via 10.0.2.2 dev eth0 proto dhcp metric 10010.0.2.0/24 dev eth0 proto kernel scope link src 10.0.2.15 metric 100> Iptables rules-P INPUT ACCEPT-P FORWARD ACCEPT-P OUTPUT ACCEPT-N ROOT_NS

Обратите внимание на текущий результат работы скрипта, потому что мы хотим быть уверены, что каждый из контейнеров, которые мы собираемся создать в ближайшее время, получит отдельный сетевой стек.

Мы уже упоминали об одном из Linux namespaces, используемых для изоляции контейнеров, которое называет сетевое пространство имён (Network namespace). Если заглянуть в man ip-netns, то мы прочтём, что Network namespace логически является копией сетевого стека со своими собственными маршрутами, правилами брандмауэра и сетевыми устройствами. Мы не будем затрагивать другие Linux namespaces в этой статье и ограничимся только областью видимости сетевого стека.

Для создания Network namespace нам достаточно утилиты ip, которая входим в популярный пакет iproute2. Создадим новое сетевое пространство имён:

$ sudo ip netns add netns0$ ip netnsnetns0

Новое сетевое пространство имён создано, но как начать его использовать? Воспользуемся командой Linux под названием nsenter. Она осуществляет вход в одно или несколько указанных пространств имен, а затем выполняет в нём указанную программу:

$ sudo nsenter --net=/var/run/netns/netns0 bash# The newly created bash process lives in netns0$ sudo ./inspect-net-stack.sh> Network devices1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00> Route table> Iptables rules-P INPUT ACCEPT-P FORWARD ACCEPT-P OUTPUT ACCEPT

Приведённый выше пример показывает, что процесс bash, работающий внутри пространства имён netns0, видит совершенно другой сетевой стек. Отсутствуют правила маршрутизации, и правила iptables, есть только один loopback interface. Все идет по плану...

Подключаем контейнер к хосту через virtual Ethernet devices (veth)

Выделенный сетевой стек будет бесполезен, если к нему отсутствует доступ. К счастью, Linux предоставляет подходящее средство для этого - virtual Ethernet devices (veth)! Согласно man veth, veth-device - это виртуальные устройства Ethernet. Они работают как туннели между сетевыми пространствами имён для создания моста к физическому сетевому устройству в другом пространстве имён, а также могут использоваться как автономные сетевые устройства.

Виртуальные Ethernet устройства всегда работают парами. Создадим их прямо сейчас:

$ sudo ip link add veth0 type veth peer name ceth0

С помощью этой единственной команды мы только что создали пару взаимосвязанных виртуальных Ethernet устройств. Имена veth0 и ceth0 были выбраны произвольно:

$ ip link1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:002: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000    link/ether 52:54:00:e3:27:77 brd ff:ff:ff:ff:ff:ff5: ceth0@veth0: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000    link/ether 66:2d:24:e3:49:3f brd ff:ff:ff:ff:ff:ff6: veth0@ceth0: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000    link/ether 96:e8:de:1d:22:e0 brd ff:ff:ff:ff:ff:ff

И veth0, и ceth0 после создания находятся в сетевом стеке хоста (также называемом Root Network namespace). Чтобы связать корневое пространство имён с пространством имён netns0, нам нужно сохранить одно из устройств в корневом пространстве имён и переместить другое в netns0:

$ sudo ip link set ceth0 netns netns0# List all the devices to make sure one of them disappeared from the root stack$ ip link1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:002: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000    link/ether 52:54:00:e3:27:77 brd ff:ff:ff:ff:ff:ff6: veth0@if5: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000    link/ether 96:e8:de:1d:22:e0 brd ff:ff:ff:ff:ff:ff link-netns netns0

Как только мы включаем устройства и назначаем правильные IP-адреса, любой пакет, происходящий на одном из них, немедленно появляется на его одноранговом устройстве, соединяющем два пространства имён. Начнем с корневого пространства имён:

$ sudo ip link set veth0 up$ sudo ip addr add 172.18.0.11/16 dev veth0

Продолжим сnetns0:

$ sudo nsenter --net=/var/run/netns/netns0$ ip link set lo up  # whoops$ ip link set ceth0 up$ ip addr add 172.18.0.10/16 dev ceth0$ ip link1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:005: ceth0@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000    link/ether 66:2d:24:e3:49:3f brd ff:ff:ff:ff:ff:ff link-netnsid 0

Проверяем подключение:

# From netns0, ping root's veth0$ ping -c 2 172.18.0.11PING 172.18.0.11 (172.18.0.11) 56(84) bytes of data.64 bytes from 172.18.0.11: icmp_seq=1 ttl=64 time=0.038 ms64 bytes from 172.18.0.11: icmp_seq=2 ttl=64 time=0.040 ms--- 172.18.0.11 ping statistics ---2 packets transmitted, 2 received, 0% packet loss, time 58msrtt min/avg/max/mdev = 0.038/0.039/0.040/0.001 ms# Leave netns0$ exit# From root namespace, ping ceth0$ ping -c 2 172.18.0.10PING 172.18.0.10 (172.18.0.10) 56(84) bytes of data.64 bytes from 172.18.0.10: icmp_seq=1 ttl=64 time=0.073 ms64 bytes from 172.18.0.10: icmp_seq=2 ttl=64 time=0.046 ms--- 172.18.0.10 ping statistics ---2 packets transmitted, 2 received, 0% packet loss, time 3msrtt min/avg/max/mdev = 0.046/0.059/0.073/0.015 ms

Обратите внимание, если мы попытаемся проверить доступность любых других адресов из пространства имен netns0, у нас ничего не получится:

# Inside root namespace$ ip addr show dev eth02: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000    link/ether 52:54:00:e3:27:77 brd ff:ff:ff:ff:ff:ff    inet 10.0.2.15/24 brd 10.0.2.255 scope global dynamic noprefixroute eth0       valid_lft 84057sec preferred_lft 84057sec    inet6 fe80::5054:ff:fee3:2777/64 scope link       valid_lft forever preferred_lft forever# Remember this 10.0.2.15$ sudo nsenter --net=/var/run/netns/netns0# Try host's eth0$ ping 10.0.2.15connect: Network is unreachable# Try something from the Internet$ ping 8.8.8.8connect: Network is unreachable

Для таких пакетов в таблице маршрутизации netns0 просто нет маршрута. В настоящий момент существует единственный маршрут до сети 172.18.0.0/16:

# From netns0 namespace:$ ip route172.18.0.0/16 dev ceth0 proto kernel scope link src 172.18.0.10

В Linux есть несколько способов заполнения таблицы маршрутизации. Один из них - извлечение маршрутов из подключенных напрямую сетевых интерфейсов. Помните, что таблица маршрутизации в netns0 была пустой сразу после создания пространства имен. Но затем мы добавили туда устройство ceth0 и присвоили ему IP-адрес 172.18.0.10/16. Поскольку мы использовали не простой IP-адрес, а комбинацию адреса и сетевой маски, сетевому стеку удалось извлечь из него информацию о маршрутизации. Каждый пакет, предназначенный для сети 172.18.0.0/16, будет отправлен через устройство ceth0. Но все остальные пакеты будут отброшены. Точно так же есть новый маршрут в корневом пространстве имен:

# From root namespace:$ ip route# ... omitted lines ...172.18.0.0/16 dev veth0 proto kernel scope link src 172.18.0.11

На этом этапе мы ответили на первый вопрос. Теперь мы знаем, как изолировать, виртуализировать и подключать сетевые стеки Linux.

Объединение контейнеров с помощью virtual network switch (bridge)

Вся идея контейнеризации сводится к эффективному совместному использованию ресурсов. То есть мы крайне редко запускаем на хосте единственный контейнер. Вместо этого стараемся запустить как можно больше изолированных процессов на одном хосте. Итак, чтобы произошло, если бы мы разместили несколько контейнеров на одном хосте, следуя описанному выше подходу? Добавим второй контейнер:

# From root namespace$ sudo ip netns add netns1$ sudo ip link add veth1 type veth peer name ceth1$ sudo ip link set ceth1 netns netns1$ sudo ip link set veth1 up$ sudo ip addr add 172.18.0.21/16 dev veth1$ sudo nsenter --net=/var/run/netns/netns1$ ip link set lo up$ ip link set ceth1 up$ ip addr add 172.18.0.20/16 dev ceth1

Проверим доступность:

# From netns1 we cannot reach the root namespace!$ ping -c 2 172.18.0.21PING 172.18.0.21 (172.18.0.21) 56(84) bytes of data.From 172.18.0.20 icmp_seq=1 Destination Host UnreachableFrom 172.18.0.20 icmp_seq=2 Destination Host Unreachable--- 172.18.0.21 ping statistics ---2 packets transmitted, 0 received, +2 errors, 100% packet loss, time 55mspipe 2# But there is a route!$ ip route172.18.0.0/16 dev ceth1 proto kernel scope link src 172.18.0.20# Leaving netns1$ exit# From root namespace we cannot reach the netns1$ ping -c 2 172.18.0.20PING 172.18.0.20 (172.18.0.20) 56(84) bytes of data.From 172.18.0.11 icmp_seq=1 Destination Host UnreachableFrom 172.18.0.11 icmp_seq=2 Destination Host Unreachable--- 172.18.0.20 ping statistics ---2 packets transmitted, 0 received, +2 errors, 100% packet loss, time 23mspipe 2# From netns0 we CAN reach veth1$ sudo nsenter --net=/var/run/netns/netns0$ ping -c 2 172.18.0.21PING 172.18.0.21 (172.18.0.21) 56(84) bytes of data.64 bytes from 172.18.0.21: icmp_seq=1 ttl=64 time=0.037 ms64 bytes from 172.18.0.21: icmp_seq=2 ttl=64 time=0.046 ms--- 172.18.0.21 ping statistics ---2 packets transmitted, 2 received, 0% packet loss, time 33msrtt min/avg/max/mdev = 0.037/0.041/0.046/0.007 ms# But we still cannot reach netns1$ ping -c 2 172.18.0.20PING 172.18.0.20 (172.18.0.20) 56(84) bytes of data.From 172.18.0.10 icmp_seq=1 Destination Host UnreachableFrom 172.18.0.10 icmp_seq=2 Destination Host Unreachable--- 172.18.0.20 ping statistics ---2 packets transmitted, 0 received, +2 errors, 100% packet loss, time 63mspipe 2

Что-то пошло не так... По какой-то причине мы не можем подключиться из netns1 к root namespace. А из root namespace мы не можем подключиться к netns1. Однако, поскольку оба контейнера находятся в одной IP-сети 172.18.0.0/16, есть доступ к veth1 хоста из контейнера netns0. Интересно...

Возможно, мы столкнулись с конфликтом маршрутов. Давайте проверим таблицу маршрутизации в root namespace:

$ ip route# ... omitted lines ...172.18.0.0/16 dev veth0 proto kernel scope link src 172.18.0.11172.18.0.0/16 dev veth1 proto kernel scope link src 172.18.0.21

После добавления второй пары veth в таблице маршрутизации root namespace появился новый маршрут 172.18.0.0/16 dev veth1 proto kernel scope link src 172.18.0.21, но маршрут до этой подсети уже существовал! Когда второй контейнер пытается проверить связь с устройством veth1, используется первый маршрут и мы видим ошибку подключения. Если бы мы удалили первый маршрут sudo ip route delete 172.18.0.0/16 dev veth0 proto kernel scope link src 172.18.0.11 и перепроверили подключение, то увидели бы обратную ситуацию, то есть подключение netns1 будет восстановлено, но netns0 останется в подвешенном состоянии.

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

Рассмотрим Linux Bridge - еще один виртуализированный сетевой объект! Linux Bridge ведёт себя как коммутатор. Он пересылает пакеты между подключенными к нему интерфейсами. А поскольку это коммутатор, то он работает на уровне L2 (то есть Ethernet).

Чтобы предыдущие этапы нашего эксперимента в дальнейшем не вносили путаницы, удалим существующие сетевые пространства имён:

$ sudo ip netns delete netns0$ sudo ip netns delete netns1# But if you still have some leftovers...$ sudo ip link delete veth0$ sudo ip link delete ceth0$ sudo ip link delete veth1$ sudo ip link delete ceth1

Заново создаём два контейнера. Обратите внимание, мы не назначаем IP-адреса новым устройствам veth0 и veth1:

$ sudo ip netns add netns0$ sudo ip link add veth0 type veth peer name ceth0$ sudo ip link set veth0 up$ sudo ip link set ceth0 netns netns0$ sudo nsenter --net=/var/run/netns/netns0$ ip link set lo up$ ip link set ceth0 up$ ip addr add 172.18.0.10/16 dev ceth0$ exit$ sudo ip netns add netns1$ sudo ip link add veth1 type veth peer name ceth1$ sudo ip link set veth1 up$ sudo ip link set ceth1 netns netns1$ sudo nsenter --net=/var/run/netns/netns1$ ip link set lo up$ ip link set ceth1 up$ ip addr add 172.18.0.20/16 dev ceth1$ exit

Убедимся, что на хосте нет новых маршрутов:

$ ip routedefault via 10.0.2.2 dev eth0 proto dhcp metric 10010.0.2.0/24 dev eth0 proto kernel scope link src 10.0.2.15 metric 100

И, наконец, создадим bridge интерфейс:

$ sudo ip link add br0 type bridge$ sudo ip link set br0 up

Теперь подключим к нему veth0 и veth1:

$ sudo ip link set veth0 master br0$ sudo ip link set veth1 master br0

... и проверим возможность подключения между контейнерами:

$ sudo nsenter --net=/var/run/netns/netns0$ ping -c 2 172.18.0.20PING 172.18.0.20 (172.18.0.20) 56(84) bytes of data.64 bytes from 172.18.0.20: icmp_seq=1 ttl=64 time=0.259 ms64 bytes from 172.18.0.20: icmp_seq=2 ttl=64 time=0.051 ms--- 172.18.0.20 ping statistics ---2 packets transmitted, 2 received, 0% packet loss, time 2msrtt min/avg/max/mdev = 0.051/0.155/0.259/0.104 ms
$ sudo nsenter --net=/var/run/netns/netns1$ ping -c 2 172.18.0.10PING 172.18.0.10 (172.18.0.10) 56(84) bytes of data.64 bytes from 172.18.0.10: icmp_seq=1 ttl=64 time=0.037 ms64 bytes from 172.18.0.10: icmp_seq=2 ttl=64 time=0.089 ms--- 172.18.0.10 ping statistics ---2 packets transmitted, 2 received, 0% packet loss, time 36msrtt min/avg/max/mdev = 0.037/0.063/0.089/0.026 ms

Прекрасно! Все отлично работает. При этом мы даже не настраивали интерфейсы veth0 и veth1. Мы назначили только два IP-адреса интерфейсам ceth0 и ceth1. Но поскольку они оба находятся в одном сегменте Ethernet (подключены к виртуальному коммутатору), существует возможность подключения на уровне L2:

$ sudo nsenter --net=/var/run/netns/netns0$ ip neigh172.18.0.20 dev ceth0 lladdr 6e:9c:ae:02:60:de STALE$ exit$ sudo nsenter --net=/var/run/netns/netns1$ ip neigh172.18.0.10 dev ceth1 lladdr 66:f3:8c:75:09:29 STALE$ exit

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

Настраиваем сетевой доступ из контейнера во внешний мир (IP routing and masquerading)

Сейчас контейнеры могут подключаться друг к другу. Но будут ли удачны подключения к хосту, то есть к корневому пространству имён?

$ sudo nsenter --net=/var/run/netns/netns0$ ping 10.0.2.15  # eth0 addressconnect: Network is unreachable

Интерфейс eth0 не доступен. Всё очевидно, в netns0 отсутствует маршрут для этого подключения:

$ ip route172.18.0.0/16 dev ceth0 proto kernel scope link src 172.18.0.10

Корневое пространство имён также не может взаимодействовать с контейнерами:

# Use exit to leave netns0 first:$ ping -c 2 172.18.0.10PING 172.18.0.10 (172.18.0.10) 56(84) bytes of data.From 213.51.1.123 icmp_seq=1 Destination Net UnreachableFrom 213.51.1.123 icmp_seq=2 Destination Net Unreachable--- 172.18.0.10 ping statistics ---2 packets transmitted, 0 received, +2 errors, 100% packet loss, time 3ms$ ping -c 2 172.18.0.20PING 172.18.0.20 (172.18.0.20) 56(84) bytes of data.From 213.51.1.123 icmp_seq=1 Destination Net UnreachableFrom 213.51.1.123 icmp_seq=2 Destination Net Unreachable--- 172.18.0.20 ping statistics ---2 packets transmitted, 0 received, +2 errors, 100% packet loss, time 3ms

Чтобы установить связь между корневым пространством имён и пространством имён контейнера, нам нужно назначить IP-адрес сетевому интерфейсу моста:

$ sudo ip addr add 172.18.0.1/16 dev br0

Теперь после того, как мы назначили IP-адрес интерфейсу моста, мы получили маршрут в таблице маршрутизации хоста:

$ ip route# ... omitted lines ...172.18.0.0/16 dev br0 proto kernel scope link src 172.18.0.1$ ping -c 2 172.18.0.10PING 172.18.0.10 (172.18.0.10) 56(84) bytes of data.64 bytes from 172.18.0.10: icmp_seq=1 ttl=64 time=0.036 ms64 bytes from 172.18.0.10: icmp_seq=2 ttl=64 time=0.049 ms--- 172.18.0.10 ping statistics ---2 packets transmitted, 2 received, 0% packet loss, time 11msrtt min/avg/max/mdev = 0.036/0.042/0.049/0.009 ms$ ping -c 2 172.18.0.20PING 172.18.0.20 (172.18.0.20) 56(84) bytes of data.64 bytes from 172.18.0.20: icmp_seq=1 ttl=64 time=0.059 ms64 bytes from 172.18.0.20: icmp_seq=2 ttl=64 time=0.056 ms--- 172.18.0.20 ping statistics ---2 packets transmitted, 2 received, 0% packet loss, time 4msrtt min/avg/max/mdev = 0.056/0.057/0.059/0.007 ms

Контейнер, вероятно, также получил возможность пинговать интерфейс моста, но они все ещё не могут связаться с хостом eth0. Нам нужно добавить маршрут по умолчанию для контейнеров:

$ sudo nsenter --net=/var/run/netns/netns0$ ip route add default via 172.18.0.1$ ping -c 2 10.0.2.15PING 10.0.2.15 (10.0.2.15) 56(84) bytes of data.64 bytes from 10.0.2.15: icmp_seq=1 ttl=64 time=0.036 ms64 bytes from 10.0.2.15: icmp_seq=2 ttl=64 time=0.053 ms--- 10.0.2.15 ping statistics ---2 packets transmitted, 2 received, 0% packet loss, time 14msrtt min/avg/max/mdev = 0.036/0.044/0.053/0.010 ms# And repeat the change for netns1

Теперь наш хост является маршрутизатором, а интерфейс моста стал шлюзом по умолчанию для контейнеров.

Отлично, нам удалось добиться сетевой связности контейнеров с корневым пространством имён. Теперь давайте попробуем подключить их к внешнему миру. По умолчанию переадресация пакетов (ip packet forwarding), то есть функциональность маршрутизатора в Linux отключена. Нам нужно её включить

# In the root namespacesudo bash -c 'echo 1 > /proc/sys/net/ipv4/ip_forward'

Теперь самое интересное - проверка подключения:

$ sudo nsenter --net=/var/run/netns/netns0$ ping 8.8.8.8# hangs indefinitely long for me...

Всё равно не работает. Мы что-то упустили? Если бы контейнер отправлял пакеты во внешний мир, сервер-получатель не смог бы отправлять пакеты обратно в контейнер, потому что IP-адрес контейнера является частным и правила маршрутизации для этого конкретного IP-адреса известны только в локальной сети. К тому же многие контейнеры в мире имеют один и тот же частный IP-адрес 172.18.0.10. Решение этой проблемы называется преобразованием сетевых адресов (NAT). Принцип работы, следующий - перед отправкой во внешнюю сеть пакеты, отправленные контейнерами, заменяют свои исходные IP-адреса (source IP addesses) на адрес внешнего интерфейса хоста. Хост также будет отслеживать все существующие сопоставления (mapping) и по прибытии будет восстанавливать IP-адреса перед пересылкой пакетов обратно в контейнеры. Звучит сложно, но у меня для вас хорошие новости! Нам нужна всего одна команда, чтобы добиться требуемого результата:

$ sudo iptables -t nat -A POSTROUTING -s 172.18.0.0/16 ! -o br0 -j MASQUERADE

Команда довольно проста. Мы добавляем новое правило в таблицу nat цепочки POSTROUTING с просьбой выполнить MASQUERADE всех исходящих пакетов из сети 172.18.0.0/16, но не через интерфейс моста.

Проверьте подключение:

$ sudo nsenter --net=/var/run/netns/netns0$ ping -c 2 8.8.8.8PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.64 bytes from 8.8.8.8: icmp_seq=1 ttl=61 time=43.2 ms64 bytes from 8.8.8.8: icmp_seq=2 ttl=61 time=36.8 ms--- 8.8.8.8 ping statistics ---2 packets transmitted, 2 received, 0% packet loss, time 2msrtt min/avg/max/mdev = 36.815/40.008/43.202/3.199 ms
$ sudo nsenter --net=/var/run/netns/netns0$ ping -c 2 8.8.8.8PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.64 bytes from 8.8.8.8: icmp_seq=1 ttl=61 time=43.2 ms64 bytes from 8.8.8.8: icmp_seq=2 ttl=61 time=36.8 ms--- 8.8.8.8 ping statistics ---2 packets transmitted, 2 received, 0% packet loss, time 2msrtt min/avg/max/mdev = 36.815/40.008/43.202/3.199 ms

Помните, что политика iptables по умолчанию - ACCEPT для каждой цепочки, она может быть довольно опасной в реальных условиях:

sudo iptables -S-P INPUT ACCEPT-P FORWARD ACCEPT-P OUTPUT ACCEPT

В качестве хорошего примера Docker вместо этого ограничивает все по умолчанию, а затем разрешает только для известных маршрутов:

$ sudo iptables -t filter --list-rules-P INPUT ACCEPT-P FORWARD DROP-P OUTPUT ACCEPT-N DOCKER-N DOCKER-ISOLATION-STAGE-1-N DOCKER-ISOLATION-STAGE-2-N DOCKER-USER-A FORWARD -j DOCKER-USER-A FORWARD -j DOCKER-ISOLATION-STAGE-1-A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT-A FORWARD -o docker0 -j DOCKER-A FORWARD -i docker0 ! -o docker0 -j ACCEPT-A FORWARD -i docker0 -o docker0 -j ACCEPT-A DOCKER -d 172.17.0.2/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport 5000 -j ACCEPT-A DOCKER-ISOLATION-STAGE-1 -i docker0 ! -o docker0 -j DOCKER-ISOLATION-STAGE-2-A DOCKER-ISOLATION-STAGE-1 -j RETURN-A DOCKER-ISOLATION-STAGE-2 -o docker0 -j DROP-A DOCKER-ISOLATION-STAGE-2 -j RETURN-A DOCKER-USER -j RETURN$ sudo iptables -t nat --list-rules-P PREROUTING ACCEPT-P INPUT ACCEPT-P POSTROUTING ACCEPT-P OUTPUT ACCEPT-N DOCKER-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE-A POSTROUTING -s 172.17.0.2/32 -d 172.17.0.2/32 -p tcp -m tcp --dport 5000 -j MASQUERADE-A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER-A DOCKER -i docker0 -j RETURN-A DOCKER ! -i docker0 -p tcp -m tcp --dport 5005 -j DNAT --to-destination 172.17.0.2:5000$ sudo iptables -t mangle --list-rules-P PREROUTING ACCEPT-P INPUT ACCEPT-P FORWARD ACCEPT-P OUTPUT ACCEPT-P POSTROUTING ACCEPT$ sudo iptables -t raw --list-rules-P PREROUTING ACCEPT-P OUTPUT ACCEPT

Настроим сетевой доступ из внешнего мира в контейнеры (port publishing)

Публикация портов контейнеров для некоторых (или всех) интерфейсов хоста - популярная практика. Но что на самом деле означает публикация порта?

Представьте, что у нас есть сервис, работающий внутри контейнера:

$ sudo nsenter --net=/var/run/netns/netns0$ python3 -m http.server --bind 172.18.0.10 5000

Если мы попытаемся отправить HTTP-запрос этому сервису с хоста, все будет работать (ну, есть связь между корневым пространством имён и всеми интерфейсами контейнера, почему бы и нет?):

# From root namespace$ curl 172.18.0.10:5000<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"># ... omitted lines ...

Однако, если бы мы получили доступ к этому серверу из внешнего мира, какой IP-адрес мы бы использовали? Единственный IP-адрес, который мы можем знать, - это адрес внешнего интерфейса хоста eth0:

$ curl 10.0.2.15:5000curl: (7) Failed to connect to 10.0.2.15 port 5000: Connection refused

Таким образом, нам нужно найти способ перенаправить все пакеты, поступающие на порт 5000 интерфейса eth0 хоста, на адрес172.18.0.10:5000. Или, другими словами, нам нужно опубликовать порт 5000 контейнера на интерфейсе eth0 хоста.

# External trafficsudo iptables -t nat -A PREROUTING -d 10.0.2.15 -p tcp -m tcp --dport 5000 -j DNAT --to-destination 172.18.0.10:5000# Local traffic (since it doesn't pass the PREROUTING chain)sudo iptables -t nat -A OUTPUT -d 10.0.2.15 -p tcp -m tcp --dport 5000 -j DNAT --to-destination 172.18.0.10:5000

Кроме того, нам нужно включить iptables intercepting traffic over bridged networks (перехватывать трафик bridged networks):

sudo modprobe br_netfilter

Время проверить!

curl 10.0.2.15:5000<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"># ... omitted lines ...

Разбираемся в работе Docker network drivers

Но что же вам сделать теперь со всеми этими бесполезными знаниями? Например, мы могли бы попытаться разобраться в некоторых сетевых режимах Docker!

Начнем с режима --network host. Попробуйте сравнить вывод следующих команд ip link и sudo docker run -it --rm --network host alpine ip link. Сюрприз, они совпадут! Таким образом host mode Docker просто не использует изоляцию сетевого пространства имён и контейнеры работают в корневом сетевом пространстве имён и совместно используют сетевой стек с хост-системой.

Следующий режим, который нужно проверить, - это --network none. Вывод команды sudo docker run -it --rm --network none alpine ip link показывает только один сетевой интерфейс обратной loopback. Это очень похоже на наши наблюдения за только что созданным сетевым пространством имен. То есть до того момента, когда мы добавляли какие-либо veth устройства.

И последнее, но не менее важное: режим --network bridge (по умолчанию), это именно то, что мы пытались воспроизвести в этой статье.

Сети и rootless контейнеры

Одной из приятных особенностей диспетчера контейнеров podman является его ориентация на rootless контейнеры. Однако, как вы, вероятно, заметили, в этой статье мы использовали много эскалаций sudo и без root-прав настроить сеть невозможно. При настройке сетей rootful контейнеров Podman очень близок к Docker. Но когда дело доходит до rootless контейнеров, Podman полагается на проект slirp4netns:

Начиная с Linux 3.8, непривилегированные пользователи могут создавать network_namespaces (7) вместе с user_namespaces (7). Однако непривилегированные сетевые пространства имен оказались не очень полезными, потому что для создания пар veth (4) в пространствах имен хоста и сети по-прежнему требуются привилегии root (иначе доступ в Интернету будет отсутствовать).

slirp4netns позволяет получить доступ из сетевое пространства имен в Интернет непривилегированным пользователям, подключая устройство TAP в сетевом пространстве имен к стеку TCP/IP usermode (slirp).

Сеть rootless контейнера весьма ограничена: технически сам контейнер не имеет IP-адреса, потому что без привилегий root невозможно настроить сетевое устройство. Более того, проверка связи (ping) из rootless контейнера не работает, поскольку в нем отсутствует функция безопасности CAP_NET_RAW, которая необходима для работы команды ping.

Заключение

Рассмотренный в этой статье подход к организации сети контейнеров является лишь одним из возможных (ну, пожалуй, наиболее широко используемым). Есть еще много других способов, реализованных через официальные или сторонние плагины, но все они сильно зависят от средств виртуализации сети Linux. Таким образом, контейнеризацию по праву можно рассматривать как технологию виртуализации.

Подробнее..

Представляем OpenShift Pipelines

27.05.2021 16:21:56 | Автор: admin

3 мая 2021 года Red Hat выпустила первую общедоступную версию OpenShift Pipelines, облачно-ориентированной системы непрерывной интеграции на базе СПО-проекта Tekton. Решение реализует Kubernetes фреймворк CI/CD для разработки и выполнения конвейеров, в которых каждый шаг запускается в своем собственном контейнере, что позволяет масштабировать шаги независимо друг от друга. Сегодня мы вкратце рассмотрим ключевые особенности и преимущества этого решения, а также приведем список дополнительных ресурсов для дальнейшего знакомства с ней и освоения.

Но, прежде чем переходить к OpenShift Pipelines, освежим в памяти основные концепты Tekton.

Основные концепты Kubernetes-нативной CI/CD

OpenShift Pipelines дополняет Kubernetes/OpenShift соответствующими CRD (ресурсами, определяемыми пользователем) для концептов CI/CD, таких как конвейер (pipeline), задача (task), шаг (step). В результате, эти концепты становятся своими (native) инстанцируемыми их можно создавать в виде отдельных экземпляров и, как следствие, полноценно масштабировать и развертывать, а также обеспечивать их безопасность средствами Kubernetes.

Поэтому для начала вспомним, что такое концепты Tekton:

Рис. 1. КонцептыTektonРис. 1. КонцептыTekton

По сути, основные концепты Tekton делятся на два вида: те, что задают конвейер, и те, что запускают конвейер.

Концепты, задающие конвейер (define pipeline)

  • Task повторно используемые и слабо связанные серии шагов (step), которые выполняют определенную задачу, например, сборку контейнерного образа.

  • Pipeline описание конвейера и задач (Task), которые он должен выполнять.

Концепты, запускающие конвейер (run pipelines)

  • TaskRun запуск и результаты выполнения экземпляра Task.

  • PipelineRun запуск и результаты выполнения экземпляра конвейера, который включает в себя ряд концептов TaskRun.

Подробнее об этих концептах можно почитать в официальной документации.

Теперь разберемся, что такое OpenShift Pipelines и для чего он нужен

Что такого особенного в OpenShift Pipelines?

OpenShift Container Platform это ведущая отраслевая Kubernetes-платформа корпоративного класса, которая предоставляет разработчикам множество функций, среди которых есть и CI/CD.

OpenShift Pipelines базируется на СПО-проекте Tekton и расширяет функционал платформы OpenShift стандратными методами, что сильно облегчает жизнь разработчикам.

Установка OpenShift Pipelines через механизм Operator

OpenShift Pipelines поддерживается на уровне механизма операторов, поэтому он легко устанавливается и обновляется, и, соответственно, легко администрируется.

OpenShift Pipelines доступен на сайте OperatorHub, где представлены более 450 различных операторов для OpenShift Container Platform:

Установка OpenShift Pipelines предельно проста, и он сразу устанавливается как оператор уровня кластера, автоматически становясь доступным для всех проектов:

OpenShift Pipelines также дополняет OpenShift соответствующими CR, которые позволят добиться еще большей универсальности в плане конфигурации и интеграции с консолью OpenShift и т.д.

При появлении в OperatorHub новой версии OpenShift Pipelines, вы как администратор можете выбрать обновление до следующей версии, задав нужный канал обновления.

Развитый UI в рамках консоли OpenShift

Tekton тоже дополняет стандартную поставку OpenShift концептами CI/CD, но в нем при создании и запуске конвейеров сложно обойтись без создания YAML-кода, и этого кода требуется писать очень много, тысячи строк. Поэтому Red Hat реализовала в консоли OpenShift полноценный UI для запуска и визуализации конвейеров (как тех, что работают сейчас, так и тех, что уже отработали), а также для графического создания конвейеров. При этом все необходимые YAML-файлы создаются автоматически, без написания какого-либо кода.

Ниже показано графическое представление конвейера, который уже был выполнен на платформе OpenShift, причем прямо из него можно получить доступ к журналам и событиям всех задач этого конвейера:

Рис. 2. Конвейеры в консоли OpenShiftРис. 2. Конвейеры в консоли OpenShift

При желании можно легко просмотреть полный лог выбранной задачи:

Чтобы еще больше облегчить жизнь разработчикам, OpenShift Pipelines позволяет рисовать конвейеры прямо в консоли OpenShift, поэтому вам больше не нужен черный пояс по YAML, чтобы создать свой первый конвейер Tekton:

Рис. 3. Графическое проектирование конвейера в консоли OpenShiftРис. 3. Графическое проектирование конвейера в консоли OpenShift

Но если вы, как обладатель черного пояса по YAML, захотите что-то подправить, это всегда можно сделать прямо из консоли OpenShift:

Рис. 4. YAML примеры и снипеты в консоли OpenShiftРис. 4. YAML примеры и снипеты в консоли OpenShift

Более того, OpenShift Pipelines пригодится, даже если вы решите пойти по пути чистого YAML, и предложит вам готовые примеры и снипеты кода для более быстрой разработки конвейеров YAML. Кроме того, в систему можно интегрировать ваши корпоративные снипеты, сделав их доступными всем разработчикам. Именно для этого мы и добавили специальный CRD под названием ConsoleYAMLSamples.

События как триггеры для запуска конвейеров

Хотите увязать запуск конвейеров с некими внешними событиями (в терминах Tekton это называется Trigger), например, push- или pull-запросами Github или Gitlab? Вообще не проблема, в OpenShift Pipelines это есть из коробки, причем поддерживаются различные вендоры, включая Github, Gitlab, BitBucket и т.д.

Рис. 5. Добавление триггера в консоли OpenShiftРис. 5. Добавление триггера в консоли OpenShift

Вы просто создаете нужный триггер в UI, а OpenShift сам формирует все необходимые концепты, такие как EventListeners, TriggerTemplates (подробнее о них можно почитать в официальной документации).

Готовые повторно используемые задачи и кастомизация

OpenShift Pipelines из коробки содержит десятки готовых задач, которые можно использовать в составе конвейеров, включая задачи по клонированию кода из репозитория, сборки приложений на различных языках программирования, таких как java, dotnet core, python go, nodejs или использования инструментов сборки вроде maven, развертывания приложений и т.д. Список доступных задач можно увидеть в консоли OpenShift, раздел ClusterTasks, меню Pipelines -> Tasks:

Рис. 6. OpenShift Pipelines из коробки предлагает десятки готовых задачРис. 6. OpenShift Pipelines из коробки предлагает десятки готовых задач

Кроме того, этот список можно легко расширить. Для этого достаточно добавить ClusterTasks в кластер, после чего вам станут доступны сотни дополнительных задач на сайте TektonHub, который представляет собой публичный репозиторий для обмена задачами Tekton:

Рис. 7.TektonHub публичный репозиторий повторно используемых задач и конвейеров TektonРис. 7.TektonHub публичный репозиторий повторно используемых задач и конвейеров Tekton

Интеграция с IDE

Разработчики, использующие командную строку и IDE, могут воспользоваться преимуществами Tekton CLI, расширения Tekton для Visual Studio Code и плагина Tekton для IntelliJ, чтобы взаимодействовать с конвейерами прямо из своей обычной среды разработки и создавать, запускать, просматривать и выполнять действия на кластере непосредственно из командной строки.

Рис. 8. Расширение VSCode для OpenShift PipelinesРис. 8. Расширение VSCode для OpenShift Pipelines

Полезные ресурсы

В заключение советуем посмотреть видеоверсию этой статьи на английском:

А также рекомендуем следующие ресурсы (EN):

Видеоролики на русском:

Вебинары:

Подробнее..

Fintech на практике как Quadcode технологии для трейдинга и банкинга разрабатывает

01.06.2021 12:20:22 | Автор: admin

Привет, самое хардовое IT комьюнити Рунета! Я Саша, главный архитектор в компании Quadcode. Мы пришли на Хабр для того, чтобы показать кухню Fintech варимся мы во всем этом 8 лет, поэтому уже можем поделиться опытом. В своем блоге будем рассказывать об архитектурах, технологиях, инструментах и лайфхаках.

Этот пост первый в списке, его можно считать знакомством. Под катом я расскажу про структуру нашей команды, про продукты Quadcode это платформа для трейдинга, банкинг и внутренние разработки, а также про наши первые шаги на пути к IPO.

Наша команда

Команда Quadcode уже 8 лет работает в финтехе. Цель компании создавать удобные финтех-инструменты для B2B клиентов со всего мира.

В разработке мы руководствуемся Agile принципами, да и в принципе склонны к гибким методологиям. Именно они позволяют достигать баланса в скорости и качестве разработки продуктов, поэтому разработка представляет из себя набор Scrum команд.

Во главе каждой команды стоит Team Lead. Сами команды сгруппированы в отделы, работающие над определенными предметными областями. Например, есть отдел Finance Development, в котором команды разрабатывают финансовые сервисы для платформы. Есть ветка, где располагаются владельцы продукта (product owners), задача которых развивать и улучшать наши продукты. Сейчас у нас в разработке 230+ опытных (реально опытных, у каждого много лет практики) специалистов. Это порядка 24 команд и 6 Product Owners. Джуниоров мы берем редко. Но с каждым годом искать опытных специалистов становится все сложнее, так что все больше в эту сторону смотрим.

Задачи по разработке выстраиваются на основе продуктовых Roadmap. Это план развития продукта с целью получения определенных бизнес-показателей. Роадмап выстраивается для каждого продукта и может быть составлен на разные временные промежутки: полгода, год, три года и т.д. Из готовых продуктовых роадмапов выстраивается общий план: когда, какие фичи и для каких продуктов должны быть сделаны.

Роадмап в нашем понимании это связующее звено между бизнесом, продуктом и разработкой.

Каждый квартал происходит важное событие в жизни компании квартальное планирование. Из общего таймлайна выделяются фичи, которые нужно реализовать в первую очередь. В итоге получается план того, что мы действительно можем сделать текущими ресурсами. Мы не приветствуем авральный стиль, поэтому учимся ловить дзен и находить оптимальный баланс между работой и личной жизнью. Каждый сотрудник может зайти и посмотреть роадмап компании, чтобы правильно спланировать и распределить свое время на важные и интересные задачи.

Работаем удаленно или в офисе в Санкт-Петербурге каждый выбирает самостоятельно. Я для себя выбрал работу из дома, но по офису тоже скучаю, он у нас классный с тренажерным залом, массажным кабинетом и прочими плюшками.

Технологический стек

Наши основные языки для разработки Golang и C++. Из дополнительных технологий на бэкенде PHP, Python, NodeJS, на фронте JavaScript (ReactJS), в аналитике Python, Scala, а в автотестах Java.

Инфраструктура в компании гибридная. Мы арендуем собственные сервера в датацентрах. Все stateless приложения стараемся эксплуатировать в Kubernetes, если для этого нет ограничений, хотя бывает и такое. Kubernetes-кластера также преимущественно работают на наших серверах. То, что требует гарантированных ресурсов, например нагруженные базы данных, мы эксплуатируем на железе. Конечно, используем и облака там, где это приносит пользу. Например в задачах, где требуется обработать большое количество данных, чтобы предоставить отчет заказчику. Для таких задач нужно временно получить ресурсы для анализа, но после получения результата они не нужны.

Для точечных целей применяем технологии, которые позволяют решить специфические задачи. Например, наше Desktop приложение под Windows, Mac и Web написано на С++ и имеет единую кодовую базу. В данном случае С++ дает нам кроссплатформенность и отличную производительность при рендере графики. Однако мы практически не используем С++ для Backend разработки, потому что это дорого. Основной язык разработки для Backend у нас Go. В то же время мы не используем его как инструмент для тестирования. Для этих целей применяем Java, так как это намного удобнее и является уже практически промышленным стандартом в индустрии.

Какие продукты создает команда Quadcode

Наш флагманский продукт платформа для трейдинга. За 7 лет развития количество пользователей платформы выросло с 950 тысяч до 88 миллионов в 170+ странах.

Начиная с 2020 года развиваем трейдинговую платформу как SaaS решение, на базе которого любой желающий может организовать собственного брокера. И у нас уже есть первые клиенты в этой области.

Кроме того планируем расширять географический охват, выходить на новые рынки. Для этого обновляем платформу с учетом всех технических и юридических требований конкретных регионов. Все это поможет нам выйти на IPO.

А теперь кратко о наших продуктах:

SaaS Trading Platform

Команда с нуля разработала платформу с аптаймом 99.5%, на базе которой более 7 лет успешно функционирует брокер.

Платформа предоставляет клиенты под Windows, MacOS, Anrdoid, iOS, а также WEB трейдрум.

На платформе можно торговать следующими инструментами:

  • Digital опционы

  • FX опционы

  • CFD

  • Forex

  • Crypto и др.

Основной язык для разработки платформы Golang. Платформа начала свое существование с монолитной архитектуры классического для своего времени стека: PHP+PostgreSQL+Redis+JS.

Через 3 года эксплуатации было решено перейти на микросервисную архитектуру, так как монолит уже не давал гибкости и не мог обеспечить необходимые темпы разработки. С миграцией на микросервисную архитектуру мы также ушли с PHP в сторону Go, о чем не жалеем.

Go отличный язык, обеспечивающий хорошую производительность с довольно небольшим порогом входа. Для разработки микросервисов просто идеальный кандидат.

С прошлого года наша платформа развивается как SaaS решение. На базе решения любой желающий может без больших усилий организовать своего собственного брокера, все есть в коробке под ключ: трейдинговый сервис, процедуры KYC, биллинг, support, crm. Словом, все, чтобы быстро стартануть бизнес. Любого нового брокера можно поднять за месяц. Чтобы обеспечить вариативность в функционале, мы разрабатываем гибкую систему модулей для SaaS-решения.

* Для того, чтобы наглядно объяснить, что такое SaaS, и показать, куда мы в итоге хотим прийти, приведем пример с пиццей. Это так называемая модель Pizza-as-a-service, вкусно и полезно.* Для того, чтобы наглядно объяснить, что такое SaaS, и показать, куда мы в итоге хотим прийти, приведем пример с пиццей. Это так называемая модель Pizza-as-a-service, вкусно и полезно.

Команда Quadcode обеспечивает внутренний риск менеджмент, бэк офис и антифрод поэтому брокеру не нужно думать обо всем этом. В итоге клиент получает полностью работающее кросс-платформенное решение. Мы также решили вопрос с регистрацией и авторизацией. Вся IT поддержка лежит на наших плечах. Мы помогаем с построением лигал структуры и во многом другом.

Сейчас добиваемся того, чтобы в экосистеме платформы был максимально широкий спектр инструментов: Forex, СFD и инвестиционные продукты в удобной для пользователя форме. Идеальный вариант сделать платформу подходящей как для банков, так и для их клиентов. Мы собираем паззл продукта из мельчайших деталей. Процесс этот не такой быстрый, но пока все получается. Быстро и не получится ни в правовом плане, ни в плане технологий.

Примеры задач, которые стоят перед командой в этом году:

  1. Конфигуратор платформы. В качестве примера возьмем конфигуратор сборки машин BMW. Вы можете зайти на сайт и собрать свой автомобиль, то есть выбрать цвет, диски, дополнительные возможности. Мы хотим сделать точно такой же функционал, только с возможностью сборки своего брокера прямо на сайте.

  2. Также один из крупных проектов это разработка собственного движка Margin Forex & MCFD.

  3. Проработка Prediction Churn. Фича основана на анализе данных и предсказывает момент, когда пользователь решит уйти. Сейчас результат Prediction Churn достоверен с вероятностью 82%. Когда система предсказывает, что пользователь готов уйти с платформы,в работу включаются менеджеры, чтобы создать удобные для трейдера условия работы на платформе. Это позволяет продлить срок работы с трейдером. Чем дальше, тем точнее будет работать Prediction Churn, и тем лучше мы сможем держать контакт с пользователем.

Banking

Это второй наш продукт. В основе направления находится собственный лицензированный провайдер финансовых услуг, который зарегистрирован в Великобритании. Продукт предоставляет следующие функции B2B и B2C клиентам:

  • Дистанционный онбординг для физических и юридических лиц.

  • Доступ к счету через мобильное приложение и онлайн-банкинг.

  • Мультивалютные счета в формате IBAN.

  • SEPA, TARGET2 и SWIFT переводы.

  • Выпуск пластиковых и виртуальных карт.

Технологический стек классический: ядро системы работает под управлением JAVA. А также применяется PHP+JS для реализации административных интерфейсов управления и web приложений.

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

Внутренние разработки

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

Из наиболее интересных можно выделить следующие:

  1. Шина данных IQ Bus. Мы исповедуем микросервсиную архитектуру. В самом начале, когда возник вопрос, а что выбрать для обеспечения коммуникации между микросервисами, мы решили создать свое решение IQ Bus. Это шина, которая абстрагирует сервисы от транспортного уровня и предоставляет им простой унифицированный протокол для общения.

  2. Sandbox. В сложных многокомпонентных, а в нашем случае системах с большим количеством сервисов, всегда возникает проблема с тестированием. Важно иметь возможность получать воспроизводимое окружение для тестирования, так называемые тестовые стенды. Еще в самом начале пути мы создали Sandbox систему, с помощью которой можно собирать копии платформы с различными конфигурациями. Это своего рода конструктор, куда можно зайти, выбрать какая функциональность нужна - будет создана сборка, запущены необходимые микросервисы и можно тестировать. Все это работает на базе Docker + Kubernetes.

  3. Central Information System. Всегда возникает необходимость в инструменте, который может объединить в себе все системы компании. Речь не только про разработку, но и про КДП, HR, Финансовый отдел. Такая система должна помогать находить ответы на различные вопросы. Например, что за команда такая A, какие у нее сотрудники, кто руководитель, какой у нее ФОТ, что она сделала за прошедший квартал. И плюс еще много всяких индивидуальных хотелок. Найти такой продукт, имеющий в себе все, достаточно проблематично, да и выглядят такие системы довольно монструозно. Хороший пример SAP. Мы же вкладываемся в собственную разработку такой системы, которая реализует все потребности различных отделов и интегрируется с другими системами: Gitlab, таск трекер, финансовые системы (1C).

Вместо заключения

За 2020 мы проделали большой путь по разработке SAAS решения и внедрения нового банкинг продукта, сейчас появилось еще несколько важных целей. Компания использует стратегическое планирование, мы движемся в сторону присутствия на рынках всех стран, удвоения показателя EBITDA и выхода на IPO.

В будущих статьях на Хабре мы расскажем более подробно о нашем подходе к разработке, планированию и работе с командами. Вместо рекламной паузы ссылка на наши вакансии. Если остались вопросы, то пишите в ТГ @wolverinoid.

Важный момент этот пост является знакомством, о чем и говорилось выше, поэтому здесь так много мы, нам и т.п. Все будущие статьи, которые станем публиковать, готовятся с прицелом на то, чтобы поделиться опытом и знаниями, которые могут оказаться полезными всему сообществу. Ведь Хабр для этого и создан, верно?

Подробнее..

Self-Hosted, или Kubernetes для богатых почему самостоятельное развертывание кластера не всегда способ сэкономить

02.06.2021 18:06:54 | Автор: admin


Идея самостоятельно развернуть кластер Kubernetes на собственных серверах или в облаке выглядит привлекательной: кажется, что это дешевле, чем платить за Managed-решение от провайдера. На самом деле все не так однозначно: на практике можно обнаружить скрытые расходы и подводные камни.


При этом для крупных компаний Self-Hosted может быть вариантом, так как у них есть условно бесплатные ресурсы и штат специалистов для поддержки технологии, а иногда еще горячее желание построить и развивать свою платформу во что бы то ни стало. А вот с малым и средним бизнесом ситуация немного другая, решение нужно взвесить со всех сторон.


Я Дмитрий Лазаренко, директор по продукту облачной платформы Mail.ru Cloud Solutions (MCS). В статье расскажу, в чем особенности развертывания Self-Hosted-кластера Kubernetes и о чем нужно знать перед запуском.


Для старта понадобятся время, деньги и администраторы, разбирающиеся в Kubernetes


Первая статья расходов на специалистов, которые умеют работать с этой системой и смогут обслуживать кластер. Это дорогие ребята, на рынке их немного, и нанять трудно.


Почему Kubernetes сильно увеличивает расходы на специалистов? Вроде бы развернуть кластер несложно, для этого есть официальная документация и инсталляторы, например Kubespray или Kubeadm. Однако если в компании есть инженер, который может прочитать строчку документации и разобраться, как поставить Kubernetes на серверы с помощью одной команды, это еще не все, этим его работа не ограничится.


В реальности развернуть кластер только половина дела. В таком виде он будет работать до первой проблемы, которая неизбежно возникнет через неделю или месяц. Например, перестанут создаваться поды из-за неверной конфигурации ресурсов на controller-manager. Или кластер начнет работать нестабильно из-за проблем с дисками у etcd. Или запущенные СronJob из-за ошибок controller-manager начнут бесконечно плодить новые поды. Или в кластере будут возникать сетевые ошибки из-за неправильного выбора конфигурации DNS.


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


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


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


Конечно, если запускать Kubernetes только ради деплоя контейнеров, то можно не разбираться и не развивать кластер. Но тогда возникает вопрос: зачем вам Kubernetes? Можно взять более простой в настройке и поддержке инструмент, тот же Docker Swarm. Если вы хотите от Kubernetes что-то простое, просто его не используйте. Нет смысла тратить время на развертывание кластера лишь ради запуска простого кода. Эта технология предназначена для проектов, где постоянно идет разработка, часто запускаются новые релизы и нужно выдерживать требования HighLoad.

По этой причине Self-Hosted Kubernetes в большинстве случаев могут успешно запустить только крупные компании, где есть возможность выделить сотрудников для обслуживания кластера и нет потребности экономить ресурсы.


Кроме того, самостоятельное развертывание кластера дело небыстрое. Если понадобится запустить кластер в короткие сроки для проекта или тестовых сред, то на Self-Hosted это не выйдет: развертывание займет несколько часов, а то и недель. К этому стоит быть готовыми. Для сравнения: в облаке вы запустите кластер KaaS за 10 минут и сможете сразу его использовать, но это получается потому, что над инфраструктурной частью уже заранее поработали специалисты провайдера.


Kubernetes требует прокачки: он не работает сам по себе


Как я уже говорил выше, Kubernetes отдельная экосистема, которой нужно заниматься и подключать к ней дополнительные инструменты. Если брать Self-Hosted, то все это придется делать самостоятельно.


Все инструменты, дополняющие Kubernetes, Open Source-решения, которые нужно настраивать. В кластер потребуется установить систему мониторинга, реализовать балансировку нагрузки, сбор и хранение логов, настройки безопасности и авторизации пользователей, сети и многое другое.


Например, понадобится мониторить и сам кластер, и приложения в нем. Причем стандартного мониторинга через Zabbix вам не хватит, потребуется специфический Prometheus или Telegraph.


С логами аналогичная ситуация: из коробки вы получите только историю логов для уже запущенных приложений, при передеплое она исчезнет. Вручную собирать логи с Kubernetes не получится, нужно подключать сборщики логов вроде Fluentd и систему хранения, например Elasticsearch или Loki. Отдельно придется заниматься балансировкой нагрузки: понадобится отказоустойчивый балансер вроде MetalLB.


Системы хранения для Self-Hosted Kubernetes еще одна головная боль


Kubernetes изначально разработан для Stateless-приложений они ничего не хранят внутри контейнеров. При работе со Stateful-приложениями, хранящими данные, встает вопрос подключения внешних хранилищ.


Самый простой вариант, к которому часто прибегают, поднять один NFS-сервер, но это решение для бедных: оно не обеспечит высокую доступность и сохранность данных. Если в медленный и ненадежный NFS будут ходить продакшен-сервисы с важными данными, могут возникнуть большие проблемы.


Для нормальной работы приложения без изменения его логики понадобятся Persistent Volumes хранилища, связанные с подами. Они подключаются внутрь контейнеров как локальные директории, позволяя приложению хранить данные под собой. Среди рабочих вариантов CephFS, Glusterfs, FC (Fiber Channel), полный список СХД можно посмотреть в официальной документации.


Интеграция Kubernetes c Persistent Volumes нетривиальная задача. Чтобы развернуть тот же Ceph, недостаточно взять мануал с Хабра и выполнить ряд команд. Плюс в дальнейшем СХД должен кто-то заниматься опять нужен отдельный инженер, а то и несколько.


Если же Self-Hosted-кластер развернут не на железе, а на виртуальных машинах в облаке, то все немного проще собственный кластер Ceph поднимать не нужно. Можно взять кластер хранилища у провайдера и научить его работать с кластером K8s, если провайдер готов предоставить вам API к своей системе хранения данных, что есть не везде. Писать интеграцию при этом придется самостоятельно.


Правда, у провайдеров, предоставляющих IaaS, можно арендовать объектное хранилище или облачную СУБД, но только если логика приложения позволяет их использовать. А в Managed-решениях Kubernetes уже из коробки есть интегрированные Persistent Volumes.


Отказоустойчивость кластера отдельная проблема


С Kubernetes проще обеспечить отказоустойчивость приложений, однако потребуется еще и реализовать отказоустойчивость кластера.


В Kubernetes есть мастер-нода, непосредственно управляющая кластером и содержащая его конфигурацию, метаданные и статусы объектов Kubernetes. Отказоустойчивый кластер включает три мастер-ноды, отдельные от самого кластера и дублирующие друг друга. Каждая нода отдельный сервер или виртуальная машина, их не могут использовать бизнес-приложения. То есть их нужно отдельно подключать и обслуживать либо оплачивать аренду в облаке.


Это создает сложности для малого бизнеса: раньше для всех приложений требовалось всего два сервера, а с Kubernetes только ради отказоустойчивости нужно три дополнительных сервера.


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


Резерв зависит от того, какое количество вышедших из строя нод вероятно в вашем случае:


  • Если у вас одна стойка с серверами в одном дата-центре, то одномоментно, скорее всего, выйдет из строя максимум одна нода на одном сервере, например из-за ошибок ОС. Значит, нужен резерв на одну ноду. Конечно, может сломаться стойка, но тут уже нужно резервирование не средствами Kubernetes.
  • Если у вас несколько стоек с серверами, то есть вероятность потери одной стойки, например из-за проблем со свичем, когда все серверы в ней станут недоступны. Значит, нужен резерв в размере количества серверов в одной стойке.
  • Если у вас несколько дата-центров, то в каждом нужно держать резерв по размеру другого дата-центра, чтобы приложения работали в случае его выхода из строя.

Если проще, то это выглядит так: когда в кластере 10 нод и вы хотите без проблем пережить потерю одной ноды, то вам потребуется 10-процентный запас ресурсов. Если же приложения должны работать даже при потере 50% кластера, значит, на всех нодах нужен запас в 50%.


При этом лучше, если ноды в кластере небольшие по объему, но их много. Допустим, у вас есть пул ресурсов 100 ГБ оперативной памяти и 100 ядер CPU. Такой объем позволяет запустить 10 виртуалок и 10 нод кластера Kubernetes. И в случае выхода из строя одной ноды вы теряете только 10% кластера.

На железных серверах такую конфигурацию не создашь. Например, используя 300 ГБ оперативной памяти и 50 ядер CPU, вы развернете всего 23 ноды кластера. И в случае выхода из строя одной ноды рискуете сразу потерять 3050% кластера.

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


Автомасштабирование кластера нетривиальная задача


Чтобы кластер всегда был готов к любой нагрузке и новые ноды подключались и отключались по необходимости, нужно реализовать автомасштабирование. То есть сделать так, чтобы ваши приложения автоматически получали нужные ресурсы в необходимом объеме.


Автоскейлинг приложений в кластере возможен на любой инфраструктуре это делается средствами Kubernetes. А вот автоскейлинг кластера, который позволяет автоматически подключать и отключать ноды при изменении нагрузки, на Bare Metal реализуется только покупкой дополнительных серверов. Значит, заказываем их и ждем сразу масштабироваться не выйдет.


Плюс если мы говорим о Self-Hosted на Bare Metal, то все серверы, необходимые для работы приложений на случай нагрузки, придется держать в рабочем состоянии и постоянно за них платить.


Если Self-Hosted-кластер развернут на IaaS, то схема похожая: инженер добавляет новую виртуальную машину и вносит ее в кластер. Другой вариант взять API провайдера, если он его предоставляет, подключить через него кластер Kubernetes, научить его запускать для себя новые серверы и так реализовать автомасштабирование. Но потребуется разрабатывать отдельное решение это сложная задача, предполагающая высокий уровень экспертности в Kubernetes и облаках.


Кроме того, для быстрого масштабирования Self-Hosted-кластера на IaaS придется резервировать нужное количество ресурсов провайдера и создавать из них новые виртуальные машины по мере надобности. И за эти зарезервированные ресурсы придется платить: практика брать плату за выключенные ресурсы бывает у реселлеров VMware. На нашей платформе в случае отключенных ВМ вы не платите за ресурсы, только за диски. В некоторых Managed-решениях автоскейлинг включается по кнопке, уточните эту возможность у вашего провайдера.


Подводные камни Self-Hosted Kubernetes


  1. Для самостоятельной эксплуатации кластера нужен специалист на фултайм, который хорошо знает технологию и понимает, как все работает внутри Kubernetes.
  2. В кластере потребуется настроить мониторинг, сбор логов, балансировку нагрузки и многое другое.
  3. Отдельная проблема развернуть и интегрировать с кластером систему хранения данных.
  4. Чтобы обеспечить отказоустойчивость кластера, потребуются дополнительные серверы или виртуалки это дополнительные затраты.
  5. Для масштабирования кластера под нагрузкой нужен запас серверов или виртуалок это еще одна статья дополнительных расходов.

Рассчитывайте ваши возможности при старте проекта. То, какие ресурсы есть у вашей компании, ваш бэкграунд, навыки и другие детали сильно влияют на выбор решения, насколько вам будет выгодно разворачивать Kubernetes самостоятельно или лучше это сделать в облаке с помощью готового сервиса. И не забываем главный вопрос всего Kubernetes: нужна ли вообще эта технология на вашем проекте, как и для чего вы собираетесь ее использовать?


Тут можно почитать, как устроен наш Kubernetes aaS на платформе Mail.ru Cloud Solutions: что у него под капотом и что в него еще входит, кроме собственно Kubernetes.
Подробнее..

Перевод Как увеличить скорость реакции Kubernetes на отказ узлов кластера?

06.06.2021 14:21:06 | Автор: admin

Kubernetes спроектирован так, чтобы быть надежным и устойчивым к сбоям, а также иметь возможность автоматически восстанавливаться. И он отлично справляется со всем этим! Однако рабочие узлы могут по разным причинам терять подключение к кластеру или выходить из строя. В этих случаях необходимо, чтобы Kubernetes быстро среагировал на инцидент.

Когда узел выходит из строя, pods сломанного узла все еще работают в течение некоторого времени. При этом они продолжают получать запросы, и эти запросы фейлятся. Скорее всего, совсем не то поведение, которое вы ожидали от Kubernetes, верно?

Чтобы разобраться, как Kubernetes реагирует на выход узла из строя, сначала рассмотрим взаимодействие между Kubelet и Controller Manager:

  1. Kubelet периодически уведомляет kube-apiserver о своём статусе с интервалом, заданным в параметре --node-status-update-frequency. Значение по умолчанию 10 секунд.

  2. Controller manager проверяет статус Kubelet каждые -node-monitor-period. Значение по умолчанию 5 секунд.

  3. Если от Kubelet получена информация в пределах --node-monitor-grace-period, Controller manager считает Kubelet исправным. Значение по умолчанию 40 секунд.

В случае отказа узла кластера происходит следующий алгоритм:

  1. Kubelet отправляет свой статус kube-apiserver, используя - node-status-update-frequency = 10 сек.

  2. Узел выходит из строя.

  3. Controller manager будет пытаться проверять статус узла, сообщаемый Kubelet, каждые --node-monitor-period = 5 сек.

  4. Controller manager увидит, что узел не отвечает, и даст ему тайм-аут --node-monitor-grace-period в 40 сек. Если за это время Controller manager не сочтет узел исправным, он установит статус NotReady.

  5. Kube Proxy удалит endpoints, указывающие на pods внутри этого узла из всех сервисов, поэтому pods сбойного узла больше не будут доступны.

В этом сценарии будет возможны ошибки при обращении в pods, работающим на этом узле, потому что модули будут продолжать получать трафик до тех пор, пока узел не будет считаться неработающим (NotReady) через 45 сек.

Есть множество параметров для настройки в Kubelet и Controller Manager.

Быстрое обновление и быстрая реакция

Чтобы увеличить скорость реакции Kubernetes на отказ узлов кластера, вы можете изменить эти параметры:

-node-status-update-frequency установить значение 1 сек (по умолчанию 10 сек)

--node-monitor-period установить значение 1 сек (по умолчанию 5 сек )

--node-monitor-grace-period установить значение 4 сек (по умолчанию 40 сек)

Протестируем изменения

Чтобы проверить изменения в тестовой среде, мы можем создать кластер Kubernetes с помощью Kind или любого другого инструмента. Мы создали конфигурационный файл для Kind Cluster с параметрами, указанными в предыдущем разделе, чтобы протестировать поведение кластера.

kind: ClusterapiVersion: kind.x-k8s.io/v1alpha4kubeadmConfigPatches:- |  apiVersion: kubelet.config.k8s.io/v1beta1  kind: KubeletConfiguration  nodeStatusUpdateFrequency: 1snodes:- role: control-plane  kubeadmConfigPatches:  - |    kind: ClusterConfiguration    controllerManager:        extraArgs:          node-monitor-period: 1s          node-monitor-grace-period: 4s- role: worker

Затем мы устанавливаем deployment с двумя репликами Nginx, размещенными в control-plane и на worker. Также мы дополнительно создали на control-plane pod с Ubuntu, чтобы проверить доступность Nginx, когда worker станет недоступен.

#!/bin/bash# create a K8S cluster with Kindkind create cluster --config kind.yaml # create a Ubuntu pod in control-plane Nodekubectl run ubuntu --wait=true --image ubuntu --overrides='{"spec": { "nodeName": "kind-control-plane"}}' sleep 30d# untaint control-plane node in order to schedule pods on itkubectl taint node kind-control-plane node-role.kubernetes.io/master-# create Nginx deployment with 2 replicas, one on each nodekubectl create deploy ng --image nginxsleep 30kubectl scale deployment ng --replicas 2# expose Nginx deployment so that is reachable on port 80kubectl expose deploy ng --port 80  --type ClusterIP# install curl in Ubuntu podkubectl exec ubuntu -- bash -c "apt update && apt install -y curl"

Чтобы проверить доступность Nginx, мы обратились к сервису с помощью curl из pod с Ubuntu, размещенного в control-plane, а также наблюдали за endpoints, принадлежащими сервису Nginx из терминала.

# test Nginx service access from Ubuntu podkubectl exec ubuntu -- bash -c 'while true ; do echo "$(date +"%T.%3N") - Status: $(curl -s -o /dev/null -w "%{http_code}" -m 0.2 -i ng)" ; done'# show Nginx service endpointswhile true; do  gdate +"%T.%3N"; kubectl get endpoints ng -o json | jq '.subsets' | jq '.[] | .addresses' | jq '.[] | .nodeName'; echo "------";done

Наконец, чтобы смоделировать сбой узла, мы остановили контейнер Kind, в котором запущен рабочий узел. Мы также добавили отметки времени, чтобы узнать когда узел был отключен и когда узел был обнаружен как NotReady.

#!/bin/bash# kill Kind worker nodeecho "Worker down at $(gdate +"%T.%3N")"docker stop kind-worker > /dev/nullsleep 15# show when the node was detected to be downecho "Worker detected in down state by Control Plane at "kubectl get event --field-selector reason=NodeNotReady --sort-by='.lastTimestamp' -oyaml | grep time | tail -n1# start worker node againdocker start kind-worker > /dev/null

После запуска теста мы заметили, что узел отключился в 12:50:22, а Controller manager обнаружил, что он отключился в 12:50:26, что и следовало ожидать через 4 секунды.

Worker down at 12:50:22.285Worker detected in down state by Control Plane at      time: "12:50:26Z"

Аналогичный результат при тестировании с терминала. Служба начала возвращать сообщения об ошибках в 12:50:23, потому что трафик был направлен на отказавший узел. А в 12:50:26.744 Kube Proxy удалил endpoint, указывающую на отказавший узел, и доступность службы была полностью восстановлена.

...12:50:23.115 - Status: 20012:50:23.141 - Status: 20012:50:23.161 - Status: 20012:50:23.190 - Status: 00012:50:23.245 - Status: 20012:50:23.269 - Status: 20012:50:23.291 - Status: 00012:50:23.503 - Status: 20012:50:23.520 - Status: 00012:50:23.738 - Status: 00012:50:23.954 - Status: 00012:50:24.166 - Status: 00012:50:24.385 - Status: 20012:50:24.407 - Status: 00012:50:24.623 - Status: 00012:50:24.839 - Status: 00012:50:25.053 - Status: 00012:50:25.276 - Status: 20012:50:25.294 - Status: 00012:50:25.509 - Status: 20012:50:25.525 - Status: 20012:50:25.541 - Status: 20012:50:25.556 - Status: 20012:50:25.575 - Status: 00012:50:25.793 - Status: 20012:50:25.809 - Status: 20012:50:25.826 - Status: 20012:50:25.847 - Status: 20012:50:25.867 - Status: 20012:50:25.890 - Status: 00012:50:26.110 - Status: 00012:50:26.325 - Status: 00012:50:26.549 - Status: 00012:50:26.604 - Status: 20012:50:26.669 - Status: 00012:50:27.108 - Status: 20012:50:27.135 - Status: 20012:50:27.162 - Status: 20012:50:27.188 - Status: 200......------12:50:26.523"kind-control-plane""kind-worker"------12:50:26.618"kind-control-plane""kind-worker"------12:50:26.744"kind-control-plane"------12:50:26.878"kind-control-plane"------...

Заключение

Мы убедились, что скорость реакции Kubernetes на инцидент значительно возросла. Возможны разные комбинации параметров для конкретных случаев, и у вас может возникнуть соблазн снизить значения, чтобы система Kubernetes реагировала быстрее, но примите во внимание, что этот сценарий создает накладные расходы на etcd, поскольку каждый узел будет постоянно пытаться обновлять свой статус через 1 секунду. Например, если в кластере 1000 узлов, будет происходить 60000 обновлений узлов в минуту, что может потребовать увеличения ресурсов контейнеров etcd или даже выделенных узлов для etcd.

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

Подробнее..

Стажировка в Southbridge набор в июне

09.06.2021 10:16:43 | Автор: admin

image


В мае мы запустили DevOps-стажировку в Southbridge для начинающих инженеров. Как проходит стажировка для первого потока, мы уже рассказывали. Сейчас начался второй поток, и мы ведем набор на третий. Подробности о стажировке под катом.


Кого приглашаем


Ищем начинающих инженеров, которым интересна сфера DevOps. Важно знать Linux на уровне администрирования. Отлично, если есть опыт написания скриптов на Bash, Python. Но это не обязательно.


Ждем тех, кто хочет не только получить технические навыки, но и развить софт-скилы: работу в команде, коммуникабельность, навык самообразования.


О программе


Стажировка проходит удалённо, занимает примерно 4-5 часов в день, которые можно выделить тогда, когда удобно. Проводим общие встречи в Zoom с наставниками и скрам-мастером, командные встречи.


Программа рассчитана на три месяца.
Что изучаем и с чем работаем на стажировке: Git, Docker, Kubernetes, мониторинг и логирование в Kubernetes, CI/CD и не только. Проходим курсы и сертификации в Слёрме.


Подробная программа

Этап 1 (2 недели)


а) самостоятельное изучение необходимого стека технологий для работы с веб-проектами,


б) выполнение проверочной работы.


Этап 2 (1 неделя)


а) прохождение видеокурса по Git,


б) прохождение видеокурса Docker: from zero to hero,


в) сдача сертификации.


Этап 3 (1 неделя + 5 дней практикум)


Задачи:


а) прохождение курса Kubernetes: База,


б) сдача сертификации,


б) выполнение практикума,


в) работа в команде.


Этап 4 (1 неделя)


Задачи:


а) прохождение курса Мониторинг и логирование в Kubernetes,


б) сдача сертификации.


Этап 5 (2 недели)


Задачи:


а) прохождение курса DevOps: Tools & Cheats,


б) выполнение практической работы из интенсива на выделенном стенде,


в) прохождение видеокурса CI/CD.


Важно: список тем может быть изменен для следующих потоков.


Каждый этап программы стажировки включает в себя сертификацию или практическую работу. При успешном выполнении контрольных и проверочных работ стажёр допускается до следующего этапа программы.


Старт третьего потока стажировки в середине июня. Резюме и ваши вопросы присылайте на почту job@southbridge.ru.

Подробнее..

Перевод Использование микросервисов в работе с Kubernetes и GitOps

10.06.2021 18:12:02 | Автор: admin

Архитектуры микросервисов продолжают развиваться в инженерных организациях, поскольку команды стремятся увеличить скорость разработки. Микросервисы продвигают идею модульности как объекты первого класса в распределенной архитектуре, обеспечивая параллельную разработку и компоненты с независимыми циклами выпуска. Как и при принятии любых технологических решений, необходимо учитывать компромиссы. В случае микросервисов они включают потенциальную потерю централизованных стандартов разработки, а также повышенную сложность эксплуатации.

К счастью, существуют стратегии решения этих проблем. Сначала мы рассмотрим рефакторинг сервиса на основе Kafka Streams с использованием Microservices Framework, который обеспечивает стандарты для тестирования, конфигурации и интеграции. Затем мы используем существующий проект streaming-ops для создания, проверки и продвижения нового сервиса из среды разработки в рабочую среду. Хотя это и не обязательно, но вы если хотите выполнить шаги, описанные в этой заметке, то вам понадобится собственная версия проекта streaming-ops, как описано в документации.

Проблемы микросервисной архитектуры

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

  • Множественные решения общих потребностей в рамках всей организации нарушают принцип "Не повторяйся".

  • Разработчики, желающие сменить команду или перейти в другую, сталкиваются с необходимостью изучения нескольких технологических стеков и архитектурных решений.

  • Операционные команды, которые проверяют и развертывают несколько приложений, сталкиваются с трудностями, поскольку им приходится учитывать технологические решения каждой команды.

Spring Boot

Чтобы снизить эти риски, разработчики обращаются к микросервисным фреймворкам для стандартизации общих задач разработки, и Spring Boot (расширение фреймворка Spring) является популярным примером одного из таких фреймворков.

Spring Boot предоставляет согласованные решения для общих проблем разработки программного обеспечения, например, конфигурация, управление зависимостями, тестирование, веб-сервисы и другие внешние системные интеграции, такие как Apache Kafka. Давайте рассмотрим пример использования Spring Boot для переписывания существующего микросервиса на основе Kafka Streams.

Сервис заказов

Проект streaming-ops - это среда, похожая на рабочую, в которой работают микросервисы, основанные на существующих примерах Kafka Streams. Мы рефакторизовали один из этих сервисов для использования Spring Boot, а полный исходный код проекта можно найти в репозитории GitHub. Давайте рассмотрим некоторые основные моменты.

Интеграция Kafka

Библиотека Spring for Apache Kafka обеспечивает интеграцию Spring для стандартных клиентов Kafka, Kafka Streams DSL и приложений Processor API. Использование этих библиотек позволяет сосредоточиться на написании логики обработки потоков и оставить конфигурацию и построение зависимых объектов на усмотрение Spring dependency injection (DI) framework. Здесь представлен компонент сервиса заказов Kafka Streams, который агрегирует заказы и хранит их по ключу в хранилище состояний:

@Autowiredpublic void orderTable(final StreamsBuilder builder) {  logger.info("Building orderTable");  builder    .table(this.topic,    Consumed.with(Serdes.String(), orderValueSerde()),    Materialized.as(STATE_STORE))    .toStream()    .peek((k,v) -> logger.info("Table Peek: {}", v));}

Аннотация @Autowired выше предписывает фреймворку Spring DI вызывать эту функцию при запуске, предоставляя инстанс StreamsBuilder, который мы используем для построения нашего DSL-приложения Kafka Streams. Этот метод позволяет нам написать класс с узкой направленностью на бизнес-логику, оставляя детали построения и конфигурирования объектов поддержки Kafka Streams фреймворку.

Конфигурация

Spring предоставляет надежную библиотеку конфигурации, позволяющую использовать различные методы для внешней настройки вашего сервиса. Во время выполнения Spring может объединять значения из файлов свойств, переменных окружения и аргументов программы для конфигурирования приложения по мере необходимости (порядок приоритета доступен в документации).

В примере с сервисом заказов мы решили использовать файлы свойств Spring для конфигурации, связанной с Apache Kafka. Значения конфигурации по умолчанию предоставляются во встроенном ресурсе application.properties, и мы переопределяем их во время выполнения с помощью внешних файлов и функции Profiles в Spring. Здесь вы можете увидеть сниппет ресурсного файла application.properties по умолчанию:

# ################################################ For Kafka, the following values can be# overridden by a 'traditional' Kafka# properties filebootstrap.servers=localhost:9092...# Spring Kafkaspring.kafka.properties.bootstrap.servers=${bootstrap.servers}...

Например, значение spring.kafka.properties.bootstrap.servers обеспечивается значением в bootstrap.servers с использованием синтаксиса плейсхолдер ${var.name} .

Во время выполнения Spring ищет папку config в текущем рабочем каталоге запущенного процесса. Файлы, найденные в этой папке, которые соответствуют шаблону application-<profile-name>.properties, будут оценены как активная конфигурация. Активными профилями можно управлять, устанавливая свойство spring.profiles.active в файле, в командной строке или в переменной окружения. В проекте streaming-ops мы разворачиваем набор файлов свойств, соответствующих этому шаблону, и устанавливаем соответствующие активные профили с помощью переменной окружения SPRING_PROFILES_ACTIVE.

Управление зависимостями

В приложении сервиса заказов мы решили использовать Spring Gradle и плагин управления зависимостями Spring. dependency-management plugin впоследствии будет управлять оставшимися прямыми и переходными зависимостями за нас, как показано в файле build.gradle:

plugins {  id 'org.springframework.boot' version '2.3.4.RELEASE'  id 'io.spring.dependency-management' version '1.0.10.RELEASE'  id 'java'}

Следующие библиотеки Spring могут быть объявлены без конкретных номеров версий, поскольку плагин предоставит совместимые версии от нашего имени:

dependencies {  implementation 'org.springframework.boot:spring-boot-starter-web'  implementation 'org.springframework.boot:spring-boot-starter-actuator'  implementation 'org.springframework.boot:spring-boot-starter-webflux'  implementation 'org.apache.kafka:kafka-streams'  implementation 'org.springframework.kafka:spring-kafka'  ...

REST-сервисы

Spring предоставляет REST-сервисы с декларативными аннотациями Java для определения конечных точек HTTP. В сервисе заказов мы используем это для того, чтобы использовать фронтенд API для выполнения запросов в хранилище данных Kafka Streams. Мы также используем асинхронные библиотеки, предоставляемые Spring, например, для неблокирующей обработки HTTP-запросов:

@GetMapping(value = "/orders/{id}", produces = "application/json")public DeferredResult<ResponseEntity> getOrder(  @PathVariable String id,  @RequestParam Optional timeout) {     final DeferredResult<ResponseEntity> httpResult =     new DeferredResult<>(timeout.orElse(5000L));...

Смотрите полный код в файле OrdersServiceController.java.

Тестирование

Блог Confluent содержит много полезных статей, подробно описывающих тестирование Spring для Apache Kafka (например, смотрите Advanced Testing Techniques for Spring for Apache Kafka). Здесь мы кратко покажем, как легко можно настроить тест с помощью Java-аннотаций, которые будут загружать Spring DI, а также встроенный Kafka для тестирования клиентов Kafka, включая Kafka Streams и использование AdminClient:

@RunWith(SpringRunner.class)@SpringBootTest@EmbeddedKafka@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)public class OrderProducerTests {...

С помощью этих полезных аннотаций и фреймворка Spring DI создание тестового класса, использующего Kafka, может быть очень простым:

@Autowiredprivate OrderProducer producer;...@Testpublic void testSend() throws Exception {  ...  List producedOrders = List.of(o1, o2);  producedOrders.forEach(producer::produceOrder);  ...

Смотрите полный файл OrderProducerTests.java для наглядного примера.

Проверка в dev

Код сервиса заказов содержит набор интеграционных тестов, которые мы используем для проверки поведения программы; репозиторий содержит задания CI, которые вызываются при появлении PR или переносе в основную ветвь. Убедившись, что приложение ведет себя так, как ожидается, мы развернем его в среде dev для сборки, тестирования и дальнейшего подтверждения поведения кода.

Проект streaming-ops запускает свои рабочие нагрузки микросервисов на Kubernetes и использует подход GitOps для управления операционными проблемами. Чтобы установить наш новый сервис в среде dev, мы изменим развернутую версию в dev, добавив переопределение Kustomize в сервис заказов Deployment, и отправим PR на проверку.

Когда этот PR будет объединен, запустится процесс GitOps, модифицируя объявленную версию контейнера службы заказов. После этого контроллеры Kubernetes развертывают новую версию, создавая заменяющие Поды и завершая работу предыдущих версий.

После завершения развертывания мы можем провести валидацию новой службы заказов, проверив, правильно ли она принимает REST-звонки, и изучив ее журналы. Чтобы проверить конечную точку REST, мы можем открыть приглашение внутри кластера Kubernetes с помощью хелпер-команды в предоставленном Makefile, а затем использовать curl для проверки конечной точки HTTP:

$ make promptbash-5.0# curl -XGET http://orders-servicecurl: (7) Failed to connect to orders-service port 80: Connection refused

Наша конечная точка HTTP недостижима, поэтому давайте проверим журналы:

kubectl logs deployments/orders-service | grep ERROR2020-11-22 20:56:30.243 ERROR 21 --- [-StreamThread-1] o.a.k.s.p.internals.StreamThread     : stream-thread [order-table-4cca220a-53cb-4bd5-8c34-d00a5aa77e63-StreamThread-1] Encountered the following unexpected Kafka exception during processing, this usually indicate Streams internal errors:           org.apache.kafka.common.errors.GroupAuthorizationException: Not authorized to access group: order-table

Эти ошибки, скорее всего, ортогональны и поэтому потребуют независимых исправлений. Не имеет значения, как они будут устранены, необходимо быстро вернуть нашу систему в работоспособное состояние. GitOps предоставляет хороший путь для ускорения этого процесса путем отмены предыдущего коммита. Мы используем функцию возврата GitHub PR, чтобы организовать последующий PR, который отменяет изменения.

Как только PR будет объединен, процесс GitOps применит отмененные изменения, возвращая систему в предыдущее функциональное состояние. Для лучшей поддержки этой возможности целесообразно сохранять изменения небольшими и инкрементными. Среда dev полезна для отработки процедур отката.

Мы выявили две проблемы в новом сервисе, которые вызвали эти ошибки. Обе они связаны со значениями конфигурации по умолчанию в этом сервисе, которые отличаются от первоначальных.

  1. HTTP-порт по умолчанию был другим, из-за чего служба Kubernetes не могла правильно направить трафик сервису заказов.

  2. Идентификатор приложения Kafka Streams по умолчанию отличался от настроенного списка контроля доступа (ACL) в Confluent Cloud, что лишало наш новый сервис заказов доступа к кластеру Kafka.

Мы решили отправить новый PR, исправляющий значения по умолчанию в приложении. Изменения содержатся в конфигурационных файлах, расположенных в развернутых ресурсах Java Archive (JAR).

В файле application.yaml мы изменяем порт HTTP-сервиса по умолчанию:

Server:  Port: 18894

А в файле application.properties (который содержит соответствующие конфигурации Spring для Apache Kafka) мы модифицируем ID приложения Kafka Streams на значение, заданное декларациями Confluent Cloud ACL:

spring.kafka.streams.application-id=OrdersService

Когда новый PR будет отправлен, процесс CI/CD на основе GitHub Actions запустит тесты. После слияния PR другой Action опубликует новую версию Docker-образа службы заказов.

Еще один PR с новой версией службы заказов позволит нам развернуть новый образ с правильными настройками по умолчанию обратно в среду dev и повторно провести валидацию. На этот раз после развертывания мы сможем взаимодействовать с новым сервисом заказов, как и ожидалось.

$ make promptbash-5.0# curl http://orders-service/actuator/health{"status":"UP","groups":["liveness","readiness"]}bash-5.0# curl -XGET http://orders-service/v1/orders/284298{"id":"284298","customerId":0,"state":"FAILED","product":"JUMPERS","quantity":1,"price":1.0}

Наконец, с нашего устройства разработки мы можем использовать Confluent Cloud CLI для потоковой передачи заказов из темы orders в формате Avro (см. документацию Confluent Cloud CLI для инструкций по настройке и использованию CLI).

 ccloud kafka topic consume orders --value-format avroStarting Kafka Consumer. ^C or ^D to exit{"quantity":1,"price":1,"id":"284320","customerId":5,"state":"CREATED","product":"UNDERPANTS"}{"id":"284320","customerId":1,"state":"FAILED","product":"STOCKINGS","quantity":1,"price":1}{"id":"284320","customerId":1,"state":"FAILED","product":"STOCKINGS","quantity":1,"price":1}^CStopping Consumer.

Продвижение в prd

Имея на руках наш новый отрефакторенный и валидированный сервис заказов, мы хотим завершить работу, продвинув его в продакшн. С нашим инструментарием GitOps это простой процесс. Давайте посмотрим, как это сделать.

Сначала оценим хелпер-команду, которую можно запустить для проверки разницы в объявленных версиях сервиса заказов в каждой среде. С устройства разработчика в репозитории проекта мы можем использовать Kustomize для сборки и оценки окончательно материализованных манифестов Kubernetes, а затем поиска в них визуальной информации о сервисе заказов. Наш проект streaming-ops предоставляет полезные команды Makefile для облегчения этой задачи:

 make test-prd test-dev >/dev/null; diff .test/dev.yaml .test/prd.yaml | grep "orders-service"< image: cnfldemos/orders-service:sha-82165db > image: cnfldemos/orders-service:sha-93c0516

Здесь мы видим, что версии тегов образов Docker отличаются в средах dev и prd. Мы сохраним финальный PR, который приведет среду prd в соответствие с текущей версией dev. Для этого мы модифицируем тег изображения, объявленный в базовом определении для службы заказов, и оставим на месте переопределение dev. В данном случае оставление dev-переопределения не оказывает существенного влияния на развернутую версию службы заказов, но облегчит будущие развертывания на dev. Этот PR развернет новую версию на prd:

Перед слиянием мы можем повторно выполнить наши тестовые команды, чтобы убедиться, что в развернутых версиях службы заказов не будет различий, о чем свидетельствует отсутствие вывода команд diff и grep:

 make test-prd test-dev >/dev/null; diff .test/dev.yaml .test/prd.yaml | grep "orders-service"

Этот PR был объединен, и контроллер FluxCD в среде prd развернул нужную версию. Используя jq и kubectl с флагом --context, мы можем легко сравнить развертывание сервиса заказов на кластерах dev и prd:

 kubectl --context= get deployments/orders-service -o json | jq -r '.spec.template.spec.containers | .[].image'cnfldemos/orders-service:sha-82165db kubectl --context= get deployments/orders-service -o json | jq -r '.spec.template.spec.containers | .[].image'cnfldemos/orders-service:sha-82165db

Мы можем использовать curl внутри кластера, чтобы проверить, что развертывание работает правильно. Сначала установите контекст kubectl на ваш рабочий кластер:

 kubectl config use-context <your-prd-k8s-context>Switched to context "kafka-devops-prd".

Хелпер-команда подсказки в репозитории кода помогает нам создать терминал в кластере prd, который мы можем использовать для взаимодействия с REST-сервисом службы заказов:

 make promptLaunching-util-pod-------------------------------- kubectl run --tty -i --rm util --image=cnfldemos/util:0.0.5 --restart=Never --serviceaccount=in-cluster-sa --namespace=defaultIf you don't see a command prompt, try pressing enter.bash-5.0#

Внутри кластера мы можем проверить работоспособность (здоровье - health) службы заказов:

bash-5.0# curl -XGET http://orders-service/actuator/health{"status":"UP","groups":["liveness","readiness"]}bash-5.0# exit

Наконец, мы можем убедиться, что заказы обрабатываются правильно, оценив журналы из orders-and-payments-simulator:

 kubectl logs deployments/orders-and-payments-simulator | tail -n 5Getting order from: http://orders-service/v1/orders/376087   .... Posted order 376087 equals returned order: OrderBean{id='376087', customerId=2, state=CREATED, product=STOCKINGS, quantity=1, price=1.0}Posting order to: http://orders-service/v1/orders/   .... Response: 201Getting order from: http://orders-service/v1/orders/376088   .... Posted order 376088 equals returned order: OrderBean{id='376088', customerId=5, state=CREATED, product=STOCKINGS, quantity=1, price=1.0}Posting order to: http://orders-service/v1/orders/   .... Response: 201Getting order from: http://orders-service/v1/orders/376089   .... Posted order 376089 equals returned order: OrderBean{id='376089', customerId=1, state=CREATED, product=JUMPERS, quantity=1, price=1.0}

Симулятор заказов и платежей взаимодействует с конечной точкой REST сервиса заказов, публикуя новые заказы и получая их обратно от конечной точки /v1/validated. Здесь мы видим код 201 ответа в журнале, означающий, что симулятор и сервис заказов взаимодействуют правильно, и сервис заказов правильно считывает заказы из хранилища состояния Kafka Streams.

Резюме

Успешное внедрение микросервисов требует тщательной координации в вашей инженерной организации. В этом посте вы увидели, как микросервисные фреймворки полезны для стандартизации практики разработки в ваших проектах. С помощью GitOps вы можете уменьшить сложность развертывания и расширить возможности таких важных функций, как откат. Если у вас есть идеи относительно областей, связанных с DevOps, о которых вы хотите узнать от нас, пожалуйста, не стесняйтесь задать вопрос в проекте, или, что еще лучше - PRs открыты для этого!

Все коды на изображениях для копирования доступны здесь.


Перевод материала подготовлен в рамках курса Microservice Architecture. Всех желающих приглашаем на открытый урок Атрибуты качества, тактики и паттерны. На этом вебинаре рассмотрим, что такое качественная архитектура, основные атрибуты качества и тактики работы с ними.

Подробнее..

Перевод Tоп 10 PromQL запросов для мониторинга Kubernetes

12.06.2021 10:05:57 | Автор: admin

В этой статье приведены примеры популярных запросов Prometheus для мониторинга Kubernetes.

Если вы только начинаете работать с Prometheus и у вас возникают сложности при создании запросов PromQL, советуем обратиться к руководству по началу работы с PromQL. Здесь мы пропустим теорию и сразу перейдём к практике.

Рейтинг основан на опыте компании Sysdig, ежедневно оказывающей сотням клиентов помощь в настройке мониторинга их кластеров:

1. Количество pods в каждом namespace

Информация о количестве pod в каждом namespace может быть полезна для обнаружения аномалий в кластере, например, слишком большого количества pods в отдельном namespace:

sum by (namespace) (kube_pod_info)

2. Количество контейнеров без CPU limits в каждом namespace

Важно правильно задавать лимиты для оптимизации производительности приложений и кластера. Этот запрос находит контейнеры без CPU limits:

count by (namespace)(sum by (namespace,pod,container)(kube_pod_container_info{container!=""}) unless sum by (namespace,pod,container)(kube_pod_container_resource_limits{resource="cpu"}))

3. Количество перезагрузок pods в каждом namespace

С помощью этого запроса вы получите список pods, которые перезапускались. Это важный показатель, поскольку большое количество перезагрузок pod обычно означает CrashLoopBackOff:

sum by (namespace)(changes(kube_pod_status_ready{condition="true"}[5m]))

4. Pods в статусе Not Ready в каждом namespace

Запрос выводит все pods, при работе которых возникла проблема. Это может быть первым шагом к её локализации и устранению:

sum by (namespace)(kube_pod_status_ready{condition="false"})

5. Превышение ресурсов кластера ЦП

Необходимо избегать ситуации, когда CPU limits превышают ресурсы кластера. В противном случае вы можете столкнуться с проблемой троттлинга процессора. Обнаружить превышение лимитами ресурсов кластера можно с помощью запроса:

sum(kube_pod_container_resource_limits{resource="cpu"}) - sum(kube_node_status_capacity_cpu_cores)

6. Превышение ресурсов кластера Память

Если все Memory limits суммарно превышают ёмкость кластера, то это может привести к PodEviction, если на узле не будет хватать памяти. Для проверки используйте запрос PromQL:

sum(kube_pod_container_resource_limits{resource="memory"}) - sum(kube_node_status_capacity_memory_bytes)

7. Количество исправных узлов кластера

Запрос выведет количество исправных узлов кластера:

sum(kube_node_status_condition{condition="Ready", status="true"}==1)

8. Количество узлов кластера, которые могут работать некорректно

Найти узлы кластера, которые периодически меняют состояние с Ready на Not Ready:

sum(changes(kube_node_status_condition{status="true",condition="Ready"}[15m])) by (node) > 2

9. Обнаружение простаивающих ядер ЦП

Планировании ресурсов кластера Kubernetes не самая простая задача. Этот запрос поможет вам определить, сколько ядер ЦП простаивают:

sum((rate(container_cpu_usage_seconds_total{container!="POD",container!=""}[30m]) - on (namespace,pod,container) group_left avg by (namespace,pod,container)(kube_pod_container_resource_requests{resource="cpu"})) * -1 >0)

10. Обнаружение неиспользуемой памяти

Этот запрос поможет снизить ваши затраты, предоставив информацию о неиспользуемой памяти:

sum((container_memory_usage_bytes{container!="POD",container!=""} - on (namespace,pod,container) avg by (namespace,pod,container)(kube_pod_container_resource_requests{resource="memory"})) * -1 >0 ) / (1024*1024*1024)

Хотите узнать больше?

Рекомендуем изучить нашу шпаргалку по PromQL, чтобы узнать, как писать более сложные запросы PromQL.

Также воспользуйтесь отличной коллекцией Awesome Prometheus alerts collection. Она включает несколько сотен Prometheus alert rules, вы можете изучить их, чтобы узнать больше о PromQL и Prometheus.

Подробнее..

Kubernetes в Hetzner при помощи Rancher (с картинками)

13.06.2021 18:05:09 | Автор: admin

Доброго времени суток. Последние несколько месяцев я пытался запустить кластер kubernetes для взаимных пыток и изучения. За это время я прочитал большое количество разных статей, многие из которых были хорошими, но совершенно не подходящими для человека, не сталкивавшегося с кубером.

Дисклеймер

В этой статье я не преследую цель рассказать "как правильно", я хочу рассказать как сделать чтобы работало и дать возможность людям учиться дальше, а не лысеть и седеть раньше положенного возраста

Изначально я хотел запустить кластер на dedicated сервере с виртуалками на hyper-v, однако узнал, что "просто запустить мастер и подключить ноды" недостаточно для полноценного кластера, еще нужно установить сетевой плагин, озаботиться Persistent volumes и желательно каким-никаким loadbalancer-ом.

Дальнейшие попытки привели меня в hetzner и к rancher.

Почему hetzner? У них есть одно очень весомое преимущество. Вот цена на виртуальный сервер в hetzner:

И на его аналог в Mail Cloud:

Да, в hetzner нет managed баз данных, очередей, s3 хранилища и прочего, но того, что у них уже есть - вполне достаточно для того, чтобы получить рабочий кластер, а все остальное можно запустить в нем же, либо на соседних серверах/dedicated (физический сервер можно подключить к приватной сети виртуальных серверов)

Rancher же берет на себя весь (или почти весь) геморрой по сборке кластера, его администрированию и расширению, а так же позволяет управлять им через ui, а не через консоль.

Прежде чем начать, нам понадобится домен для rancher и аккаунт в hetzner cloud, которые, я надеюсь, вы сможете сделать без инструкций.

Итак, приступим.

Первым делом создаем приватную сеть. Заходим в Hetzner Cloud, создаем проект, переходим на вкладку Networks, жмем Create Network, меняем подсеть с 16 на 8, запоминаем название нашей сети, оно нам еще пригодится.

Далее переходим на вкладку Security -> API Tokens, жмем Generate API Token, ставим переключатель в Read & Write:

Подтверждаем, сохраняем токен, нам его больше не покажут:

Далее нужно создать сервер, на который поставим rancher. Переходим во вкладку Servers и жмем Add Server. Локацию выбираем по желанию, я выбрал Helsinki (запоминаем, локация еще пригодится). Тип сервера - Standart CX21 (или больше, если вы богатый).

В графе Network выбираем сеть, которую создали ранее. Ниже добавляем свой публичный ключ, еще ниже даем имя сервера (например rancher), после чего жмем Create & Buy now. Ждем пару секунд, пока создается сервер.

Далее нужно направить на него ваш домен (создать A запись) и подождать, пока сервер будет по нему доступен.

После этого подключаемся к серверу по ssh (через putty или другой ssh клиент) и приступаем к установке rancher.

Сначала ставим docker:

apt-get updateapt install -y docker.iosystemctl start dockersystemctl enable docker

После запускаем контейнер с rancher:

docker run -d --restart=unless-stopped \  -p 80:80 -p 443:443 \  -v /root/rancher:/var/lib/rancher \  --privileged \  --name rancher-server \  rancher/rancher:latest \  --acme-domain your.domain.com

Измените your.domain.com на домен, который вы привязали к серверу. Ждем несколько минут, пока rancher сделает свои дела и получит ssl сертификат.

Про rancher и docker

В документации rancher рекомендуется использовать такой метод установки (Single Node Using Docker) только для тестирования и разработки, но мы этим и занимаемся

Переходим по нашему домену и наблюдаем приветственное окно. Устанавливаем пароль администратора, указываем, что хотим администрировать множество кластеров, принимаем лицензионное соглашение

Далее подтверждаем, что rancher правильно определил домен и попадаем на главную страницу.

Теперь нужно установить расширения для работы с hetzner. Для этого переходим в Tools > Drivers

Далее вкладка Node Drivers и жмем Add Node Driver

В открывшемся окне нужно указать ссылки на скачивание плагина:

В репозитории драйвера берем ссылку на последний релиз linux_amd64 (на данный момент это https://github.com/JonasProgrammer/docker-machine-driver-hetzner/releases/download/3.3.0/docker-machine-driver-hetzner_3.3.0_linux_amd64.tar.gz) и копируем в поле "Download Url".

В репозитории UI плагина берем ссылку на Custom UI URL (на данный момент это https://storage.googleapis.com/hcloud-rancher-v2-ui-driver/component.js) и вставляем ее в поле "Custom UI URL"

Добавляем в White List домен storage.googleapis.com

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

Переходим на главную страницу (выпадающее меню справа от лого > Global), нажимаем на "Add Cluster". В списке появился Hetzner, нажимаем

В следующем окне обзываем кластер по желанию (поле Cluster Name), вписываем Prefix для имени ноды, ставим галки в колонки etc и Control Pane, после жмем на кнопку Add Node Template:

В открывшемся окне вводим API Token Hetzner (я говорил, что он еще понадобится).
Далее выбираем локацию сервера (там, где вы разместили самый первый сервер), ОС, тип сервера (CP21 или выше). Обязательно выбираем в списке приватную сеть и ставим галку "Use private network" и даем имя шаблону.

После создания шаблона нужно добавить пул рабочих нод, для этого жмем кнопку Add Node Pool, в новой строчке опять вводим префикс, ставим галку в Worker, указываем желаемое количество и шаблон сервера (выбрать такой же, как у мастера, либо добавить новый, помощнее).

После чего спускаемся ниже, до Cluster Options, разворачиваем Kubernetes Options, указываем желаемую версию кубера (лучше последнюю, т.к. hetzner поддерживает только три последних версии), Network provider - Flannel, Cloud Provider - external, после чего нажимаем кнопку "Edit as YAML".

Перед нажатием на Edit as YAML можно спуститься чуть ниже и поменять настройки, если вы знаете, что они делают

В открывшемся окошке нужно добавить следующий текст в rancher_kubernetes_engine_config:

...rancher_kubernetes_engine_config:  ...  addons: |-    ---    apiVersion: v1    stringData:      token: <API Token>      network: <Network name>    kind: Secret    metadata:      name: hcloud      namespace: kube-system    ---    apiVersion: v1    stringData:      token: <API Token>    kind: Secret    metadata:      name: hcloud-csi      namespace: kube-system  addons_include:    - https://github.com/hetznercloud/hcloud-cloud-controller-manager/releases/latest/download/ccm-networks.yaml    - https://raw.githubusercontent.com/hetznercloud/csi-driver/master/deploy/kubernetes/hcloud-csi.yml...

<API Token> заменяем на токен hetzner (в двух местах), <Network name> на имя приватной сети, которую указывали у серверов. Последние две строки с ссылками содержат в себе Cloud Controller Manager и Container Storage Interface driver. На странице репозиториев есть таблицы с этими ссылками, выбираем подходящие под версию kubernetes (CCM берем With Networks support)
Должно получиться что-то вроде этого:

Жмем на "Create" и наблюдаем за магией: rancher создаст сервера, установит на них все необходимое и объеденит в кластер. После создания кластера жмем на кнопку Cluster explorer (в шапке) и попадаем в админку. На этом установка кластера завершена, можно тыкать по менюшкам и изучать содержимое kubernetes.

В следующей статье расскажу про деплой и установку приложений через helm (на примере gitlab runner).


PS При создании сервиса LoadBalancer, он не сможет запуститься самостоятельно, т.к. нужно указать место его физического расположения. Для этого переходим в Services, жмем три точки у нужного сервиса > Edit Config > Labels & Annotations и добавить аннотации

load-balancer.hetzner.cloud/location - датацентр
load-balancer.hetzner.cloud/use-private-ip true

P.P.S.: Здесь указано, что этого можно избежать, если добавить параметры в переменные кластера по умолчанию, но я не знаю как это сделать.

Подробнее..
Категории: Kubernetes , Rancher , Hetzner

Перевод Как оптимизировать ограничения ресурсов Kubernetes

15.06.2021 10:13:02 | Автор: admin

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

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

Prometheus одно из самых популярных решений для мониторинга кластеров Kubernetes. Поэтому каждый шаг в этом руководстве содержит примеры запросов PromQL.

Обнаружение контейнеров без ограничения ресурсов

Первым шагом для выбора правильных лимитов является обнаружение контейнеров без каких-либо ограничений.

Отсутствие лимитов в контейнере может оказать негативное воздействие на узлы кластера. В лучшем случае узлы начнут выселять pods с учетом QoS. При этом также возникнут проблемы с производительностью из-за троттлинга процессора. Но в худшем случае узел может перестать работать из-за нехватки ресурсов.

Контейнеры без CPU Limit в каждом namespace

sum by (namespace)(count by (namespace,pod,container)(kube_pod_container_info{container!=""}) unless sum by (namespace,pod,container)(kube_pod_container_resource_limits{resource="cpu"}))

Контейнеры без Memory Limit в каждом namespace

sum by (namespace)(count by (namespace,pod,container)(kube_pod_container_info{container!=""}) unless sum by (namespace,pod,container)(kube_pod_container_resource_limits{resource="memory"}))

Допустим, вы нашли несколько контейнеров без ограничений ресурсов. Как определить потенциально опасные? Легко! Нужно найти контейнеры, которые используют больше всего ресурсов и при этом не имеют лимитов.

Топ-10 контейнеров без CPU Limits, потребляющих больше всего ресурсов CPU

topk(10,sum by (namespace,pod,container)(rate(container_cpu_usage_seconds_total{container!=""}[5m])) unless sum by (namespace,pod,container)(kube_pod_container_resource_limits{resource="cpu"}))

Топ-10 контейнеров без Memory Limits, потребляющих больше памяти

topk(10,sum by (namespace,pod,container)(container_memory_usage_bytes{container!=""}) unless sum by (namespace,pod,container)(kube_pod_container_resource_limits{resource="memory"}))

Обнаружение контейнеров со слишком строгими ограничениями

Обнаружение контейнеров со слишком строгими CPU Limits

Если утилизация контейнером ресурсов процессора приближается к установленному лимиту, то его производительность будет снижаться из-за троттлинга ЦП.

Выполните этот запрос, чтобы найти контейнеры, для которых использование ЦП близко к пределу:

(sum by (namespace,pod,container)(rate(container_cpu_usage_seconds_total{container!=""}[5m])) / sum by (namespace,pod,container)(kube_pod_container_resource_limits{resource="cpu"})) > 0.8

Обнаружение контейнеров со слишком строгими Memory Limits

Если контейнер превысит лимит по памяти, он будет убит.

На этой диаграмме показано, как при резком повышении потребления контейнером памяти достигнут установленный лимит и контейнер был убит.

Используйте этот запрос, чтобы найти контейнеры, для которых потребление памяти близко к установленному лимиту:

(sum by (namespace,pod,container)(container_memory_usage_bytes{container!=""}) / sum by (namespace,pod,container)(kube_pod_container_resource_limits{resource="memory"})) > 0.8

Как выбрать оптимальные значения для лимитов?

Один из способов выбрать оптимальные значения для лимитов наблюдение за утилизацией ресурсов контейнером в течение некоторого времени. Мы можем использовать две стратегии:

Консервативная

Мы находим наибольшее значение утилизации ресурсов контейнером за период времени. Устанавливаем ограничение ресурсов, равное этому значению.

Находим оптимальный CPU Limit с помощью консервативной стратегии:

max by (namespace,owner_name,container)((rate(container_cpu_usage_seconds_total{container!="POD",container!=""}[5m])) * on(namespace,pod) group_left(owner_name) avg by (namespace,pod,owner_name)(kube_pod_owner{owner_kind=~"DaemonSet|StatefulSet|Deployment"}))

Находим оптимальный Memory Limit с помощью консервативной стратегии:

max by (namespace,owner_name,container)((container_memory_usage_bytes{container!="POD",container!=""}) * on(namespace,pod) group_left(owner_name) avg by (namespace,pod,owner_name)(kube_pod_owner{owner_kind=~"DaemonSet|StatefulSet|Deployment"}))

Агрессивная

Задаем ограничение ресурсов по 99 квантилю. Это позволит не учитывать 1% наиболее высоких значений. Это хорошая стратегия, если есть редкие аномалии или пики, которые вы хотите игнорировать.

Находим оптимальный CPU Limit с помощью агрессивной стратегии:

quantile by (namespace,owner_name,container)(0.99,(rate(container_cpu_usage_seconds_total{container!="POD",container!=""}[5m])) * on(namespace,pod) group_left(owner_name) avg by (namespace,pod,owner_name)(kube_pod_owner{owner_kind=~"DaemonSet|StatefulSet|Deployment"}))

Находим оптимальный Memory Limit с помощью агрессивной стратегии:

quantile by (namespace,owner_name,container)(0.99,(container_memory_usage_bytes{container!="POD",container!=""}) * on(namespace,pod) group_left(owner_name) avg by (namespace,pod,owner_name)(kube_pod_owner{owner_kind=~"DaemonSet|StatefulSet|Deployment"}))

Достаточно ли ресурсов в вашем кластере?

Узлы кластера гарантируют, что запланированные в них pods будут иметь достаточно ресурсов на основе параметра Requests контейнера каждого pods. К тому же, узлы резервируют за каждым контейнером указанный объем памяти и количество ядер ЦП.

Необходимо также контролировать, что сумма всех ограничений ресурсов Kubernetes не превышает ёмкость ресурсов в вашем кластере.

Когда вы утилизируете большую часть ресурсов кластера, контейнеры могут работать без проблем при обычной нагрузке, но в сценариях с высокой нагрузкой контейнеры могут начать использовать ЦП и память до предела. Это приведет к тому, что узел начнет выселять pods, а в критических ситуациях узел перестанет работать из-за нехватки ресурсов.

Как обнаружить превышение доступных ресурсов кластера?

Процент превышения доступных ресурсов по памяти:

100 * sum(kube_pod_container_resource_limits{container!="",resource="memory"} ) / sum(kube_node_status_capacity_memory_bytes)

Процент превышения доступных ресурсов по ЦП:

100 * sum(kube_pod_container_resource_limits{container!="",resource="cpu"} ) / sum(kube_node_status_capacity_cpu_cores)

Как правило, не все контейнеры полностью утилизируют запрашиваемые ресурсы одновременно. Если заданные лимиты составляют 100% от ресурсов кластера это повлечет за собой дополнительные затраты на инфраструктуру, которая никогда не будет использоваться.

Лучшим решением будет выбрать консервативную стратегию, гарантирующую, что избыточное использование составляет менее 125%, или агрессивную стратегию, которая позволяет лимитам достичь 150% емкости вашего кластера.

Не менее важным будет проверка соответствия лимитов емкости каждого узла. Например, у нас есть контейнер с CPU Requests - 2 и CPU Limit - 8. Этот контейнер можно запланировать на узле с 4 ядрами, но лимиты не дадут нужного эффекта, потому что на узле недостаточно ядер.

Процент превышения доступных ресурсов узла по памяти:

sum by (node)(kube_pod_container_resource_limits{container!=,resource=memory} ) / sum by (node)(kube_node_status_capacity_memory_bytes)

Процент превышения доступных ресурсов узла по ЦП:

sum by (node)(kube_pod_container_resource_limits{container!=,resource=cpu} ) / sum by (node)(kube_node_status_capacity_cpu_cores)

Подведем итоги

В этой статье вы узнали, почему так важно правильно использовать Kubernetes Limits and Requests, как определять наличие неоптимальных параметров в вашем кластере и какие стратегии вы могли бы использовать, чтобы установить правильные ограничения ресурсов Kubernetes.

Если вы хотите узнать больше, рекомендуем прочитать статью рациональное использовании ресурсов в Kubernetes.

Подробнее..

Перевод Контролируем удаление с финализаторами

18.06.2021 08:07:31 | Автор: admin

image


В Kubernetes не так-то просто что-то удалить вы уверены, что удалили объект, но оказывается, что он все еще присутствует в кластере. Вы, конечно, можете выполнять команду kubectl delete в повседневных операциях и надеяться на лучшее, но знание принципов работы delete команд в Kubernetes поможет вам понять, почему некоторые объекты остаются после удаления.


В этой статье мы рассмотрим:


  • Какие свойства ресурса влияют на удаление.
  • Как финализаторы и ссылки на родителя-владельца управляют удалением объектов.
  • Как можно использовать propagationPolicy, чтобы изменить порядок удаления.
  • Как работает удаление, с примерами.

Для простоты во всех примерах мы используем ConfigMaps и базовые команды kubectl. Мы увидим, как работают эти команды, и обсудим результаты их использования на практике.


Базовая команда delete


В Kubernetes есть несколько команд для создания, чтения, обновления и удаления объектов. Здесь мы поговорим о четырех командах kubectl: create, get, patch и delete.
Примеры базовой команды kubectl delete:


kubectl create configmap mymapconfigmap/mymap created

kubectl get configmap/mymapNAME    DATA   AGEmymap   0      12s

kubectl delete configmap/mymapconfigmap "mymap" deleted

kubectl get configmap/mymapError from server (NotFound): configmaps "mymap" not found

Перед командой стоит знак $, далее следует ее результат. Как видите, мы начали с kubectl create configmap mymap, чтобы создать пустой configmap mymap. Теперь нужно выполнить get и убедиться, что он существует. Затем мы удалили configmap. Если снова сделать get, получим ошибку HTTP 404, потому что configmap не найден.


У команды deleteочень простая диаграмма состояний:


image
Диаграмма состояний для delete


Операция незамысловатая, но ей могут помешать некоторые факторы, включая финализаторы и ссылки на родителя-владельца.


Как работают финализаторы


Чтобы понять, как удаляются ресурсы в Kubernetes и почему они иногда все-таки не удаляются, полезно разобраться с финализаторами.


Финализаторы это ключи в манифесте ресурса, отвечающие за операции, которые надо выполнить до удаления объекта. Они контролируют сборку мусора для ресурсов и сообщают контроллерам, какие операции очистки нужно выполнить до удаления ресурса. Они не обязательно обозначают код, который нужно выполнить. По сути, это просто списки ключей, вроде аннотаций. Как и аннотациями, ими также можно управлять.


Скорее всего, вы встречали следующие финализаторы:


  • kubernetes.io/pv-protection
  • kubernetes.io/pvc-protection

Они используются для постоянных томов и не дают случайно удалить их. Точно так же некоторые финализаторы могут использоваться для предотвращения удаления любого ресурса.


Ниже приводится configmap без свойств, но с финализатором:


cat <<EOF | kubectl create -f -apiVersion: v1kind: ConfigMapmetadata:  name: mymap  finalizers:  - kubernetesEOF

Контроллер ресурса configmap не понимает, что делать с ключом финализатора kubernetes. Такие финализаторы для configmap я называю мертвыми, и обычно они используются с неймспейсами. Вот что происходит при попытке удалить configmap:


kubectl delete configmap/mymap &configmap "mymap" deletedjobs[1]+  Running kubectl delete configmap/mymap

Kubernetes сообщает, что объект удален, но на самом деле, в традиционном понимании, никуда он не удален. Скорее он находится в процессе удаления. Если мы снова выполним get, то узнаем, что объект изменен и теперь содержит временную метку удаления deletionTimestamp.


kubectl get configmap/mymap -o yamlapiVersion: v1kind: ConfigMapmetadata:  creationTimestamp: "2020-10-22T21:30:18Z"  deletionGracePeriodSeconds: 0  deletionTimestamp: "2020-10-22T21:30:34Z"  finalizers:  - kubernetes  name: mymap  namespace: default  resourceVersion: "311456"  selfLink: /api/v1/namespaces/default/configmaps/mymap  uid: 93a37fed-23e3-45e8-b6ee-b2521db81638

Если в двух словах, объект был изменен, но не удален. Все потому, что Kubernetes увидел, что у объекта есть финализаторы, и поместил его в состояние read-only. Временная метка удаления показывает, что объект можно только читать а еще из него можно удалить апдейты ключа финализатора. Другими словами, удаление будет завершено, только когда мы удалим из объекта финализатор.


Удалить финализаторы можно с помощью команды patch. Фоновое удаление завершится, и объект будет удален. Если мы опять выполним get configmap, то ничего не увидим.


kubectl patch configmap/mymap \    --type json \    --patch='[ { "op": "remove", "path": "/metadata/finalizers" } ]'configmap/mymap patched[1]+  Done  kubectl delete configmap/mymapkubectl get configmap/mymap -o yamlError from server (NotFound): configmaps "mymap" not found

Диаграмма состояния для финализации выглядит так:


image
Диаграмма состояний для финализации


Если мы попытаемся удалить объект с финализатором, он останется на этапе финализации, пока контроллер не удалит ключи финализаторов или финализаторы не будут удалены через Kubectl. Когда список финализаторов будет пуст, Kubernetes поместит его в очередь на удаление из реестра.


Ссылки на владельца


Ссылки на владельца (owner references) описывают отношения между группами объектов. Это свойства ресурсов, которые указывают, как они связаны друг с другом, чтобы можно было удалять целые деревья ресурсов.


Правила финализатора обрабатываются, если есть ссылки на владельца. Ссылки на владельца состоят из имени и UID. Они связывают ресурсы в одном неймспейсе и им нужен UID. Ссылки на владельца для подов обычно указывают на replica set, которому они принадлежат. Если удалить deploy или stateful set, дочерние replica set и pod тоже удалятся.


Посмотрим на примере, как работают ссылки на владельца. В первом примере мы сначала создаем родительский объект, а потом дочерний. В результате получаем простейший configmap со ссылками на владельца, то есть родителя:


cat <<EOF | kubectl create -f -apiVersion: v1kind: ConfigMapmetadata:  name: mymap-parentEOFCM_UID=$(kubectl get configmap mymap-parent -o jsonpath="{.metadata.uid}")cat <<EOF | kubectl create -f -apiVersion: v1kind: ConfigMapmetadata:  name: mymap-child  ownerReferences:  - apiVersion: v1    kind: ConfigMap    name: mymap-parent    uid: $CM_UIDEOF

Если мы удалим дочерний объект, в котором есть ссылка на владельца, родитель никуда не денется:


kubectl get configmapNAME           DATA   AGEmymap-child    0      12m4smymap-parent   0      12m4skubectl delete configmap/mymap-childconfigmap "mymap-child" deletedkubectl get configmapNAME           DATA   AGEmymap-parent   0      12m10s

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


kubectl get configmapNAME           DATA   AGEmymap-child    0      10m2smymap-parent   0      10m2skubectl delete configmap/mymap-parentconfigmap "mymap-parent" deletedkubectl get configmapNo resources found in default namespace.

Получается, что если в дочернем объекте есть ссылка на родителя, он автоматически удаляется при удалении родителя. Это называется cascade. По умолчанию cascade имеет значение true, но можно указать --cascade=false в kubectl delete, чтобы удалить родительский объект, не трогая дочерние.


В следующем примере у нас есть родительский и дочерний объекты, а еще ссылки на владельца. Если мы удалим родителя, указав --cascade=false, дочерние объекты сохранятся:


kubectl get configmapNAME           DATA   AGEmymap-child    0      13m8smymap-parent   0      13m8skubectl delete --cascade=false configmap/mymap-parentconfigmap "mymap-parent" deletedkubectl get configmapNAME          DATA   AGEmymap-child   0      13m21s

Параметр --cascade указывает на политику распространения в API, которая позволяет менять порядок объектов удаления в дереве. В следующем примере используется отправка запроса к API через curl для удаления с фоновой политикой распространения (propagation policy):


kubectl proxy --port=8080 &Starting to serve on 127.0.0.1:8080curl -X DELETE \  localhost:8080/api/v1/namespaces/default/configmaps/mymap-parent \  -d '{ "kind":"DeleteOptions", "apiVersion":"v1", "propagationPolicy":"Background" }' \  -H "Content-Type: application/json"{  "kind": "Status",  "apiVersion": "v1",  "metadata": {},  "status": "Success",  "details": { ... }}

Политику распространения нельзя указать в командной строке с помощью kubectl только при прямом вызове API. Просто создайте прокси, чтобы иметь доступ к серверу API со своего компьютера, и выполните команду curl с одним URL, чтобы послать команду delete.


Есть три варианта политики распространения:


  • Foreground: дочерние объекты удаляются до родительского (обратный порядок).
  • Background: родительский объект удаляется до дочерних (прямой порядок).
  • Orphan: ссылки на владельца игнорируются.

Если вы удаляете объект, в котором есть ссылки на владельца, то финализаторы также будут учитываться. В результаты деревья объектов могут сохраняться, и удаление будет частичным. В этом случае изучите имеющиеся ссылки на владельца и финализаторы, чтобы понять, что происходит.


Принудительное удаление неймспейса


В некоторых случаях для удаления неймспейсов может потребоваться принудительная финализация: если вы удалили неймспейс и очистили все объекты в нем, но неймспейс по-прежнему существует, его можно удалить принудительно, обновив его подресурс, finalize. Так контроллер неймспейса поймет, что нужно удалить финализатор и выполнить очистку:


cat <<EOF | curl -X PUT \  localhost:8080/api/v1/namespaces/test/finalize \  -H "Content-Type: application/json" \  --data-binary @-{  "kind": "Namespace",  "apiVersion": "v1",  "metadata": {    "name": "test"  },  "spec": {    "finalizers": null  }}EOF

Действовать нужно осторожно, иначе неймспейс будет удален, а осиротевшие объекты останутся, и Kubernetes запутается. В таком случае нужно заново создать неймспейс вручную, и тогда оставшиеся объекты снова в нем появятся, чтобы их можно было очистить и восстановить.


Итоги


Как показывают примеры, финализаторы могут помешать удалению ресурсов в Kubernetes, особенно если речь о родительских и дочерних объектах. Обычно финализаторы добавляются в код не просто так, поэтому нужно все проверить, прежде чем удалять их вручную. С помощью ссылок на владельца можно указывать и удалять деревья ресурсов, хотя и в этом случае будут учитываться финализаторы. Наконец, можно использовать политику распространения, чтобы указывать порядок удаления в прямых вызовах API и контролировать процесс удаления объектов. Теперь вы чуть больше знаете о том, как работает удаление в Kubernetes, и можете поэкспериментировать самостоятельно в продакшен-кластере.


Видео от автора:


Подробнее..

Категории

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

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru