Глава 1: Китайские достижения.
В наши дни весь современный бизнес имеет взлеты и падения - если мы говорим о команде, разрабатывающей свой продукт. Мы можем стать либо свидетелями рождения истории, либо её участниками. Именно так в 1987 году бывший офицер Шэньчжэнь создал свою компанию с капиталом в 20 000 юаней. Её название знает сейчас каждый инженер, но многие не задумываются в смысловом содержании этого названия Hua - “Китай” и Wei - “Достижение”.
“Так, стоп”, - мысленно подумал сейчас про себя каждый, - “Зачем я сейчас читаю про это? Я хочу узнать что-то про SberCloud, и как там обстоят дела с k8s”. А вот нельзя говорить о SberCloud и не говорить о Huawei Cloud, ведь по своей сути это один и тот же продукт, который предоставляется нам сейчас на рынке. Но не хотелось бы впадать в полемику и говорить о том, почему так и никак иначе. Лучше обсудим ряд вопросов, которые действительно интересуют любого инженера, решившего связать свою проектную деятельность со SberCloud.
Зачем нам SberCloud?
Можем ли мы там работать?
Какие потенциальные трудности мы можем обрести?
Что мы получим в конечном счете?
Глава 2: Бесконечность не предел.
Быстрый рост продукта, желанная аудитория пользователей, актуальность и потребность - именно эти тезисы вынуждают каждый проект увеличивать темпы разработки, придумывать новые фичи и оптимизировать свой продукт, чтобы быть на плаву. Если размышлять из логики, что у нас имеется prod- и stage-контур, и для счастья ничего больше не надо, то это ошибочное мнение. Всегда были и будут проекты, где помимо одного prod-контура нужно иметь ещё 5-10 stage-окружений, которые будут повторять инфраструктуру и сетевое взаимодействие основного контура. Каждый разработчик должен спокойно пушить свои релизы и не аффектить своими работами других сотрудников. При этом должна быть уверенность, что после деплоя в pre-prod ничего не изменится.
Поднимаем для каждого контура свой сервер, чтобы разграничить среды разработки, и приступаем к настройке сетевого взаимодействия. Все просто! Наше количество текущих серверов всего лишь 80 штук, они включают в себя stage-стенды для разработчиков, prod, базы данных, файловые хранилища, внутренние инструменты для взаимодействия и составления документации. И вот мы в точке невозврата: как обслуживать всё это, и при этом соответствовать всем требованиям по безопасности? Ведь каждый уважающий себя инженер думает не только о процессах оптимизации, но и о защите своей работы. Нельзя дать пропасть потраченному времени, бессонным ночам и выпитым чашкам кофе.
Ответ прост, как вопрос: нам нужно кастомизировать наш код, нам нужно облако, где будут managed-решения, призванные упростить обслуживание необходимых компонентов инфраструктуры, нам нужны процессы автоматизации, нам нужен IaC. Не долго думая но долго согласовывая наши глаза падают на недорогие ресурсы облачного провайдера SberCloud. Полдела сделано, но работы ещё бесконечно много.
Глава 3: Нужно работать не 12 часов, а головой.
Этап первый - разговариваем с разработкой.
Необходимо менять текущие подходы в разработке приложений и отказываться от явных костылей:
Создавайте обработку переменных окружения в конфигурациях для последующего их переопределения на этапе деплоя.
Проектируйте микросервис для работы процессов с единым портом и последующей возможностью распараллеливания задач.
Добавляйте корректное логирование без лишней информации.
Вносите комментарии в коде и пишите документацию к приложениям.
Идеальный микросервис - это не тот, который деплоится быстро, а тот, который деплоится в любую инфраструктуру.
Этап второй - больше не значит лучше.
Ранее мы уже осознали нашу проблему, так что самым важным этапом нашей задачи является создание не 80 VPS, а среды, которая бы поддерживала автоматизацию развертывания, масштабирования и управления контейнеризированными приложениями. Получается, что нам необходим Kubernetes.
На такой случай Sbercloud предлагает нам Cloud Container Engine (CCE). Но надо знать, что везде есть как плюсы так и минусы - поговорим о них прямо сейчас.
Ранее уже упоминалось в рамках данной статьи о применении подхода Infrastructure as Code (IaC) - для этих целей планировалось использовать Terraform, а значит нам необходим провайдер. У SberCloud он имеется в открытом доступе и в нем описана большая часть ресурсов, и здесь можно обнаружить сразу явный минус - отсутствие актуализации версии провайдера (последний релиз был 5 месяцев назад на момент написания данной статьи).
Если сравнить тот же провайдер, который имеется у ранее упомянутого Huawei, то нетрудно заметить, что нет значительной части приятного функционала для дальнейшего развертывания и обслуживания инфраструктуры.
Также, не отходя далеко от кассы провайдера, нужно сказать про главный минус CCE (помимо предложенных 3-х версий Kubernetes): стоит заметить, что присутствует функционал выбора развертывания мастеров, и если вы захотите выбрать 3 мастера (это и правильнее, когда речь идет про отказоустойчивость), с их созданием будут проблемы. Всё потому, что количество выделенных серверов под данный процесс в различных AZ ограничено, и их лимиты уже исчерпаны другими пользователями SberCloud.
Все эти минусы присущи почти любому облаку и нельзя сказать, что останавливают нас на пути к цели, поэтому, сжав зубы перед поставленной задачей, мы приступили к написанию Terraform-модуля. Но у нас остается принципиальный вопрос общего вида инфраструктуры. Для этих требований мы обратились к документации и выбрали стандартную концепцию с использованием Elastic Load Balance (ELB) для Advanced тарифа.
После ряда неудач и успехов с выбором необходимых конфигураций для ECS-нод, был развернут наш первый CCE-кластер и созданы node pool, но это лишь начало.
Этап третий - готовим инфраструктуру.
Когда речь идет про сетевое взаимодействие, то имеется ряд необходимых задач и вариантов решения. По настройке сетевого взаимодействия с текущей инфраструктурой проекта. К примеру корректный резолвинг внутренних доменов или FQDN, которые не принадлежат нашему будущему кластеру k8s. Исходно вложенный функционал CCE Sbercloud уже имел отличную функцию предустановки Helm Chart CoreDNS:
В нашем же случае дефолтные конфигурации данного ПО нас не устраивали, и было принято решение произвести корректировку чарта посредством Terraform и предложенной документации SberCloud по работе с ресурсами addon.
## Addon template
data "sbercloud_cce_addon_template" "coredns" {
cluster_id = var.cluster_id
name = "coredns"
version = "1.23.1”
}
resource "sbercloud_cce_addon" "addon" {
cluster_id = var.cluster_id
template_name = "coredns"
version = "1.23.1"
values {
basic_json = jsonencode(jsondecode(data.sbercloud_cce_addon_template.coredns.spec).basic)
custom_json = jsonencode(merge(
jsondecode(data.sbercloud_cce_addon_template.coredns.spec).parameters.custom,
{
stub_domains = {"site-example.com": ["10.10.10.10:53"]}
}
))
flavor_json = jsonencode(jsondecode(data.sbercloud_cce_addon_template.coredns.spec).parameters.flavor2)
}
}
Конечный результат, располагаемый в configmap coredns:
.:5353 {
bind {$POD_IP}
cache 30
errors
health {$POD_IP}:8080
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
}
loadbalance round_robin
prometheus {$POD_IP}:9153
forward . /etc/resolv.conf {
policy random
}
reload
}
site-example.com:5353 {
bind {$POD_IP}
errors
cache 30
forward . 10.10.10.10:53
}
Отлично, решение этой проблемы было найдено через документацию, но у нас присутствует ещё целевой ряд проблем с ограничением трафика к нодам, а также к ограничению трафика к ELB, который планируется у нас как входная точка трафика для пользователей. И тут уже простым решением правки конфигурации через ресурс обойтись сложно. Так как CCE является нашим managed решением, то идеологический вариант с ручным вмешательством в security groups серверов Node pool нам не подойдет, вследствие чего мы пошли масштабней и решили ограничивать трафик к подсети наших нод с помощью применения Network ACLs - этот механизм закрывает вопрос с доступом трафика к серверам, но остается задача с ELB.
На этапе поднятия provider Sbercloud предоставляет нам возможность автоматизированного создания балансировщика через установку Helm Chart ingress-nginx и annotations:
apiVersion: v1
kind: Service
metadata:
annotations:
kubernetes.io/elb.class: union
kubernetes.io/session-affinity-mode: SOURCE_IP
kubernetes.io/elb.subnet-id: 5083f225-9bf8-48fa-9c8b-67bd9693c4c0
kubernetes.io/elb.autocreate:
"{\"type\":\"public\",\"bandwidth_name\":
\"cce-bandwidth- 1551163379627\",
\"bandwidth_chargemode\":\"bandwidth\",\"bandwidth_size\":5,
\"bandwidth_sharetype\":\"PER\",
\"eip_type\":\"5_bgp\",\"name\":\"james\"}"
labels:
app: nginx
name: nginx
spec:
externalTrafficPolicy: Local
ports:
- name: service0
port: 80
protocol: TCP
targetPort: 80
selector:
app: nginx
type: LoadBalancer
После чего, в зависимости от описанных нами ранее портов и протоколов, поднимаются дополнительные компоненты балансировщика:
Listener - порты для входа трафика.
Backend Server Groups - конечные точки для проксирования.
Но при этом по умолчанию отсутствует немаловажный аспект Whitelist - это список ip, которым разрешен доступ до нашего порта балансировщика на уровне сетевого протокола L7, и уже тут возникают трудности.
Согласно документации SberCloud ELB можно создать Whitelist на уровне провайдера Terraform, но у нас остается вопрос закрепления Whitelist за Listener на уровне Template и provider Terraform его покрыть не может, ввиду отсутствия Data Sources у этой сущности. Получается процесс добавления и изменения списка доступных IP-адресов будет иметь ручные действия с нашей стороны? К сожалению это так, именно поэтому сразу хочется поставить большой минус.
Этап четвертый - внутреннее сетевое взаимодействие по локальной сети.
Security groups и Network ACLs прекрасно справляются, в свою очередь, с ограничением сетевого взаимодействия как на входящий, так и на исходящий трафик, но любой человек, который редко работает с сетями k8s, может столкнуться с интересным явлением получения запроса к любой конечной точке из контейнера нашего пода с ip-адресом этого контейнера. Первый вопрос каждого пользователя будет звучать приблизительно так: “Если контейнеры вращаются на нодах, у которых есть локальный сетевой интерфейс, как мой запрос может иметь IP-адрес контейнера?”.
Проблема, описанная в четвертом этапе, заключается непосредственно в использовании не совсем корректных правил фаервола, выставленных параметром настройки CCE Service Forwarding Mode, - режим iptables. Данный режим является стандартным и говорит о том, сетевой прокси, располагаемый на каждой ноде нашего кластера kubernetes, будет использовать для обработки трафика правила iptables, которые также включают в себя стандартный набор правил masquerade.
Основная функция маскарадинга заключается в изменении адреса пакетов, выходящих из container network на IP-адрес worker node. Но, не смотря на прямое наличие данных правил, их функционирование оставляло желать лучше и требовало бы в будущем ручного вмешательства на всех нодах кластера k8s, что является не приемлемым вариантом в нашем случае.
Для автоматизации данного процесса был найден - ip-masq-agent. Решение, найденное весьма интересным образом за чашкой кофе, является очень простым, и требует лишь написания корректной конфигурации в configmap с обозначением подсетей нашего CCE кластера, включая дополнительно подсеть Master node:
apiVersion: v1
data:
config: |
nonMasqueradeCIDRs:
- Master node Subnet
- Nodepool Subnet
- Service Network Segment
resyncInterval: 5s
masqLinkLocal: false
kind: ConfigMap
metadata:
name: ip-masq-agent
namespace: kube-system
И применением yaml манифеста DaemonSet с тегом образа контейнера агента:
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: ip-masq-agent
namespace: kube-system
spec:
selector:
matchLabels:
k8s-app: ip-masq-agent
template:
metadata:
labels:
k8s-app: ip-masq-agent
spec:
hostNetwork: true
containers:
- name: ip-masq-agent
image: registry.k8s.io/networking/ip-masq-agent:v2.8.0
securityContext:
privileged: false
capabilities:
add: ["NET_ADMIN", "NET_RAW"]
volumeMounts:
- name: config
mountPath: /etc/config
volumes:
- name: config
configMap:
# Note this ConfigMap must be created in the same namespace as the daemon pods - this spec uses kube-system
name: ip-masq-agent
optional: true
items:
# The daemon looks for its config in a YAML file at /etc/config/ip-masq-agent
- key: config
path: ip-masq-agent
Как следствие, получаем желаемый результат в виде локальных IP-адресов наших Node вместо ip контейнеров при обращении из контейнера к ресурсам локальной сети.
Этап пятый - мониторинг.
После приготовления всех основных архитектурных элементов, была острая необходимость обложиться метриками и к счастью или сожалению, здесь появилась одна из задач, решения которой нет до сих пор. Изначально было весьма неприятно узнавать о недоступности одного из stage-окружений от отдела разработки. Как минимум, это увеличивало время решения задачи из-за разности часовых поясов, и когда разработка уже знает, о том что релиз фичи придется отложить, не подозревающий системный администратор или DevOps инженер ещё только мечтает в кровати о том, какой кофе он будет пить на завтрак.
Основной целью этой задачи было применить все возможные средства для хранения метрик и удобного оповещения ответственных при возникновении проблем, и хотя применение стандартного решения в виде использования Helm-чарта Kube-prometheus-stack в сумме с blackbox exporter помогает отслеживать состояния нод, подов, стендов и основных компонентов k8s. Остается вопрос мониторинга Managed Database, располагаемых в SberCloud, и здесь начинается самое интересное.
Ранее мы уже проговаривали, что есть ряд требований по безопасности, которые необходимо учесть при построении инфраструктуры. В их число также входит и обеспечение безопасной сетевой связности между компонентами приложения и базой данных. В нашем случае подключение к MongoDB должно происходить только по защищенному SSL-соединению, и в этой части присутствует полное взаимопонимание со стороны отдела Кибербезопасности и DevOps-инженеров. Но есть одна маленькая загвоздка, связанная непосредственно с Document Database Service (MongoDB в Sbercloud), которая гласит о необходимости использования функции отключения проверки имени хоста в сертификате SSL c помощью параметра настройки “sslAllowInvalidHostnames”.
И хотя для MongoDB имеется официальный Percona-экспортер, в основе которого располагается mongo-go-driver, в нем отсутствует работа с данным параметром и, как следствие, его передача в строку подключения MongoDB не представляется возможным. Таким образом, данный вариант нам не подходит, но можно воспользоваться встроенными средствами мониторинга Cloud Eye, который записывает метрики и отображает их в виде графика. Для push-алертов с метрик достаточно лишь использовать аналогичный сервис отправки сообщений Simple Message Notification (SMN). Данный сервис включает в себя ряд стандартных ресурсов Topics, Subscriptions, Message Template, позволяющих после первичной настройки произвести отправку алерта в любого вида конечную точку, как по протоколу SMTP, так и по протоколу HTTP.
Все было бы хорошо, если бы не одно интересное “но”, которое нельзя не подметить. После создания любого рода доставщика до конечной точки необходимо подтвердить права владения endpoint’ом через форму подтверждения, присылаемую на этот endpoint. И если вопросов относительно Email нет (ведь все логично, и эта стандартная процедура многим знакома), то как быть с протоколами HTTP и HTTPS ? Вопрос оказался настолько интересным и занятным, что ни документация, ни техническая поддержка облака не может дать на него корректный ответ по сей день.
Многие из читающих, я уверен, сразу же подумали: “Зачем рассказывать о том, что не работает? Ну не доработали функционал, поступи как настоящий DevOps - возьми конечную точку API, access key и secret key и напиши exporter сам!” Вопрос данного решения остается лишь в трудозатратах и реализации.
У SberCloud присутствует весьма неплохая документация относительно взаимодействия c API, но, к сожалению, на написание универсального решения может понадобиться весьма большое количество времени, так как запросы к API имеют сложную структуру и непременно должны содержать ID сущностей или виртуальных машин. Это изрядно осложняет как задачу по созданию экспортера, так и задачу по поддержке и обслуживанию мониторинга со стороны других администраторов.
Глава 4: Параллельные прямые не пересекаются.
Поговорим о главном: в конечном счете, каждый человек, занимающийся той или иной проблемой, любит принимать участие в определённых этапах решения задачи. Кому-то нравится придумывать идеи и планировать решение задачи, кто-то любит, молча закрывшись от всех, реализовывать чужие задумки, а кто-то предпочитает показывать и представлять решение большой публике. В данном случае в нашей работе пришел момент, когда мы завершили все основные этапы работы с инфраструктурой клиента, и надо было представить результат.
Практическая идея была проста в своей сути, увеличение количества различных stage-стендов, путем расположения их в различные namespace kubernetes. Всё это дает возможность нескольким разработчикам без труда деплоить решения своих задач, не мешая другим. Установка limits и requests помогла предотвратить неожиданные проблемы связанные с потреблением оперативной памяти и CPU одним из стендов. Малое количество серверов с большими конфигурациями позволило отойти от горизонтального масштабирования и упростило жизнь системным администраторам, а клиент, в свою очередь, получил более быструю реализацию своих идей, придерживаясь минимального Time to market для продукта.
Глава 5: Обобщим про SberСloud.
SberСloud выглядит как весьма интересный продукт, которому есть к чему стремиться. Он помогает закрывать большую часть желаемых потребностей клиента и оптимизировать сроки развертывания вашей будущей инфраструктуры, но зачем хвалить там, где хвалить не надо? Как и у любого облака здесь имеются свои недоработки, пробелы в документации и вопросы к roadmap развития проекта, но и текущего функционала нам хватило, чтобы закрыть имеющиеся боли бизнеса.
Получается, все остались довольны? Все, но не всем. Ведь остались незакрытые вопросы, связанные с инфраструктурой, которые (само собой) отправились в backlog - это и острый вопрос относительно мониторинга MongoDB, и вопрос автоматизации процессов по изменению Whitelist для ELB. Однако основные поставленные задачи со стороны бизнеса были выполнены, а по поводу остального можно сказать лишь одну простую вещь “Нет предела совершенству”.
Комментарии (3)
rubero
03.04.2023 16:41+1А почему вообще SberCloud? Особенно, если, как написано, это связано с Huawei Cloud, не лучше ли напрямую, без посредников?
Gacblk Автор
03.04.2023 16:41+1Привет, спасибо за вопрос. Тут скорее требование со стороны заказчика, а не наше желание или выбор. Именно поэтому и решили описать проблемы, с которыми столкнулись и какие варианты решения были найдены.
r_andreev
Тут ещё стоило написать про ущербный набор сторадж-классов, поставляемых «из коробки». Например, если кубик многозонный, а вы используете обычные сетевые диски, то они не умеют в миграцию между зонами. Вытеснило под по ресурсам на другую ноду в другой зоне и привет.
Так что нужно быть готовом решать и эту задачу.