- PVSM.RU - https://www.pvsm.ru -
В жизни любого DevOps-инженера возникает необходимость создать playground для команды разработки. Как всегда он должен быть умным, шустрым и потреблять минимальное количество ресурсов. В этой статье я хочу рассказать о том, как я решал проблему создания такого зверя для микросервисного приложения на kubernetes.
Немного о том, что представляет из себя система для которой необходимо было создать playground:
Требования, которые мы смогли сформулировать при активном общении с тимлидом:
С самого начала было ясно, что наиболее логичным для создания параллельных пространств в k8s логичнее всего использовать родной инструмент виртуальных кластеров, или в терминологии k8s — namespaces. Задачу, так же, упрощает тот факт, что все взаимодействия внутри кластера производятся по коротким именам предоставляемым kube-dns, что означало, что запуск структуры можно произвести в отдельном namespace без потери связности.
У данного решения есть только одна проблема — необходимость разворачивать в namespace все имеющиеся сервисы, что долго, неудобно и потребляет большое количество ресурсов.
При создании любого сервиса k8s создаёт DNS-запись вида <service-name>.<namespace-name>.svc.cluster.local. Данный механизм позволяет общение через короткие имена внутри одного namespace благодаря изменениям вносимым в resolv.conf каждого запускаемого контейнера.
В обычном состоянии он выглядит вот так:
search <namespace-name>.svc.cluster.local svc.cluster.local cluster.local
nameserver 192.168.0.2
options ndots:5
Т.е к сервису в том же namespace можно обратится по имени <service-name>, в соседних namespace по имени <service-name>.<namespace-name>
В этот момент в голову приходит простая мысль "База общая, маршрутизацией запросов к сервисам занимается api-шлюз, почему бы не заставить его ходить сначала к сервису в своём namespace, а в случае его отсутствия в default?"
Да, подобное решение можно было организовать настройками namespace (мы же помним, что это nginx), но подобное решение вызовет разницу в настройках на pg и на прочих кластерах, что неудобно и может вызвать ряд проблем.
Так что, был выбран метод замены строки
search <namespace-name>.svc.cluster.local svc.cluster.local cluster.local
На
search <namespace-name>.svc.cluster.local svc.cluster.local cluster.local default.svc.cluster.local
Такой подход обеспечит автоматический переход в namespace default при отсутствии необходимого сервиса в своём namespace.
Подобного результата можно добиться в кластере следующим образом. Kubelet добавляет параметры search в контейнер из resolve.conf хост-машины, так что достаточно просто дописать в /etc/resolv.conf каждой ноды строку:
search default.svc.cluster.local
Если же вы не желаете, чтобы ноды ресолвили адреса сервисов, то можно использовать параметр --resolv-conf при запуске kubelet, что позволит указать любой другой файл вместо /etc/resolv.conf. Например файл /etc/k8s/resolv.conf с той же строкой.
Дальнейшее решение достаточно просто, нужно, только, принять следующие соглашения:
Конфиг nginx для перенаправления запросов к api-gw в соответствующих namespace
server_name ~^(?<namespace>.+).cluster.local;
location / {
resolver 192.168.0.2;
proxy_pass http://api-gw.$namespace.svc.cluster.local;
}
Для автоматизации процесса развёртывания используется плагин Jenkins Pipeline Multibranch Plugin [1].
В настройках проекта указываем собирать только ветки соответствующие шаблону play/* И добавляем Jenkinsfile в корень всех проектов, с которыми будет работать сборщик.
Для обработки используется groovy-скрипт, целиком приводить его не буду, только пара примеров. Остальной деплой принципиально ничем не отличается от обычного.
Получение имени ветки:
def BranchName() {
def Name = "${env.BRANCH_NAME}" =~ "play[/]?(.*)"
Name ? Name[0][1] : null
}
Минимальная конфигурация namespace требует развёрнутого api-шлюза, поэтому добавляем вызов проекта создающего namespace и разворачивающего в него api-шлюз:
def K8S_NAMESPACE = BranchName()
build job: 'Create NS', parameters: [[$class: 'StringParameterValue', name: 'K8S_NAMESPACE', value: "${K8S_NAMESPACE}"]]
build job: 'Create api-gw', parameters: [[$class: 'StringParameterValue', name: 'K8S_NAMESPACE', value: "${K8S_NAMESPACE}"]]
Серебряной пули не существует, но мне так и не удалось найти не только best practices, но и описаний того, как организованы песочницы у других, поэтому решил поделиться методом, который использовал при создании песочницы на базе k8s. Возможно это не идеальный способ, так что с радостью приму замечания или рассказы о том, как данная проблема решена у вас.
Автор: demonight
Источник [2]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/devops/264144
Ссылки в тексте:
[1] Pipeline Multibranch Plugin: https://wiki.jenkins.io/display/JENKINS/Pipeline+Multibranch+Plugin
[2] Источник: https://habrahabr.ru/post/338158/
Нажмите здесь для печати.