Премахваме остарялата feature branch в Kubernetes клъстера

Премахваме остарялата feature branch в Kubernetes клъстера

Здравейте! Функционален клон (т.нар. преглед на деплой, прегледно приложение) — това е, когато деплойвате не само основния клон, но и всеки pull request на уникален URL. Можете да проверите дали кодът работи в продукционно обкръжение и да покажете функцията на други програмисти или продуктови специалисти. Докато работите по pull request, всеки нов commit премахва текущия деплой за стария код и новият деплой за новия код се създава. Въпроси могат да възникнат, когато сте слели pull request в основния клон. Функционалният клон вече не е нужен, но ресурсите на Kubernetes все още са в клъстера.

Повече за функционалните клонове

Един от подходите за създаване на функционални клонове в Kubernetes е да се използват namespace. Кратко, продукционната конфигурация изглежда така:

kind: Namespace
apiVersion: v1
metadata:
  name: habr-back-end
...

kind: Deployment
apiVersion: apps/v1
metadata:
  namespace: habr-back-end
spec:
  replicas: 3
...

За функционалния клон се създава namespace с неговия идентификатор (например, номера на pull request) и някакъв префикс/сукфикс (например, -pr-):

kind: Namespace
apiVersion: v1
metadata:
  name: habr-back-end-pr-17
...

kind: Deployment
apiVersion: apps/v1
metadata:
  namespace: habr-back-end-pr-17
spec:
  replicas: 1
...

Общо взето, написах Kubernetes Оператор (приложение, което има достъп до ресурсите на клъстера), линк към проекта в Github. То премахва namespace, които принадлежат на стари функционални клонове. В Kubernetes, ако изтриете namespace, другите ресурси в този namespace също се изтриват автоматично.

$ kubectl get pods --all-namespaces | grep -e "-pr-"
NAMESPACE            ... AGE
habr-back-end-pr-264 ... 4d8h
habr-back-end-pr-265 ... 5d7h

За това как да внедрите функционалните клонове в клъстера, можете да прочетете тук и тук.

Мотивация

Нека да разгледаме типичния жизнен цикъл на pull request с непрекъсната интеграция (непрекъсната интеграция):

  1. Пушваме нов commit в клона.
  2. При изграждането стартират линтери и/или тестове.
  3. Конфигурациите на Kubernetes за pull request се генерират на момента (например, номерът му се вмъква в готов шаблон).
  4. С помощта на kubectl apply конфигурациите попадат в клъстера (деплой).
  5. Pull request се слива в основния клон.

Докато работите по pull request, всеки нов commit премахва текущия деплой за стария код и новият деплой за новия код се създава. Но когато pull request се слее в основния клон, ще се изгражда само основният клон. В крайна сметка се получава, че за pull request вече сме забравили, а ресурсите му в Kubernetes все още са в клъстера.

Как да използвате

Инсталирайте проекта с командата по-долу:

$ kubectl apply -f https://raw.githubusercontent.com/dmytrostriletskyi/stale-feature-branch-operator/master/configs/production.yml

Създайте файл със следното съдържание и инсталирайте чрез kubectl apply -f:

apiVersion: feature-branch.dmytrostriletskyi.com/v1
kind: StaleFeatureBranch
metadata:
  name: stale-feature-branch
spec:
  namespaceSubstring: -pr-
  afterDaysWithoutDeploy: 3

Параметър namespaceSubstring необходим за да се филтрират namespace-и за pull request-ове от други namespace-и. Например, ако в кластера има следните namespace-и: habr-back-end, habr-front-end, habr-back-end-pr-17, habr-back-end-pr-33, тогава кандидатите за изтриване ще бъдат habr-back-end-pr-17, habr-back-end-pr-33.

Параметър afterDaysWithoutDeploy необходим за да се изтриват стари namespace-и. Например, ако namespace е създаден 3 дни 1 час назад, а в параметъра е посочено 3 дни, този namespace ще бъде изтрит. Работи и в обратна посока, ако namespace е създаден 2 дни 23 часа назад, а в параметъра е посочено 3 дни, този namespace няма да бъде изтрит.

Има още един параметър, който отговаря за това колко често да се сканират всички namespace-и и да се проверяват за дни без deploy — checkEveryMinutes. По подразбиране той е равен на 30 минути.

Как работи това

На практика, ще е необходимо:

  1. Docker за работа в изолирана среда.
  2. Minikube ще стартира Kubernetes клъстер локално.
  3. kubectl — интерфейс за команден ред за управление на клъстера.

Стартиране на Kubernetes клъстер локално:

$ minikube start --vm-driver=docker
minikube v1.11.0 on Darwin 10.15.5
Използване на драйвера docker, базирано на съществуващия профил.
Започване на контрольната площка minikube в клъстера minikube.

Указваме kubectl използвайте локалния клъстер по подразбиране:

$ kubectl config use-context minikube
Превключен към контекста "minikube".

Сваляме конфигурации за production среда:

$ curl https://raw.githubusercontent.com/dmytrostriletskyi/stale-feature-branch-operator/master/configs/production.yml > stale-feature-branch-production-configs.yml

Тъй като production конфигурациите са настроени да проверяват стари namespace-и, а в нашия ново стартиран клъстер няма, заменяме променливата на средата IS_DEBUG на истинно. При такава стойност параметърът afterDaysWithoutDeploy не се взема предвид и namespace-ите не се проверяват за дни без deploy, само за вхождат подстрока (-pr-).

Ако сте на Linux:

$ sed -i 's|false|true|g' stale-feature-branch-production-configs.yml

Ако сте на macOS:

$ sed -i "" 's|false|true|g' stale-feature-branch-production-configs.yml

Инсталираме проекта:

$ kubectl apply -f stale-feature-branch-production-configs.yml

Проверяваме, че в клъстера се е появил ресурс StaleFeatureBranch:

$ kubectl api-resources | grep stalefeaturebranches
ИМЕ                 ... APIGROUP                             ... ВИД
stalefeaturebranches ... feature-branch.dmytrostriletskyi.com ... StaleFeatureBranch

Проверяваме, че в клъстера се е появил оператор:

$ kubectl get pods --namespace stale-feature-branch-operator
ИМЕ                                           ... СТАТУС  ... ВЪЗРАСТ
stale-feature-branch-operator-6bfbfd4df8-m7sch ... Изпълнение ... 38с

Ако погледнете в логовете му, той е готов да обработва ресурси StaleFeatureBranch:

$ kubectl logs stale-feature-branch-operator-6bfbfd4df8-m7sch -n stale-feature-branch-operator
... "msg":"Версия на оператора: 0.0.1"}
...
... "msg":"Стартиране на EventSource", ... , "source":"kind source: /, Kind="}
... "msg":"Стартиране на контролера", ...}
... "msg":"Стартиране на работниците", ..., "брой работници":1}

Инсталираме готовите фикстури (готови конфигурации за моделиране на ресурсите на клъстера) за ресурса StaleFeatureBranch:

$ kubectl apply -f https://raw.githubusercontent.com/dmytrostriletskyi/stale-feature-branch-operator/master/fixtures/stale-feature-branch.yml

В конфигурацията е указано да се търсят namespace'и със подстринга -pr- веднъж на 1 минута.:

apiVersion: feature-branch.dmytrostriletskyi.com/v1
kind: StaleFeatureBranch
metadata:
  name: stale-feature-branch
spec:
  namespaceSubstring: -pr-
  afterDaysWithoutDeploy: 1 
  checkEveryMinutes: 1

Операторът реагира и е готов да проверява namespace'и:

$ kubectl logs stale-feature-branch-operator-6bfbfd4df8-m7sch -n stale-feature-branch-operator
... "msg":"Stale feature branch is being processing.","namespaceSubstring":"-pr-","afterDaysWithoutDeploy":1,"checkEveryMinutes":1,"isDebug":"true"}

Инсталираме фикстури, съдържащи два namespace'а (project-pr-1, project-pr-2) и техните deployments, услуги, ingress, и така нататък:

$ kubectl apply -f https://raw.githubusercontent.com/dmytrostriletskyi/stale-feature-branch-operator/master/fixtures/first-feature-branch.yml -f https://raw.githubusercontent.com/dmytrostriletskyi/stale-feature-branch-operator/master/fixtures/second-feature-branch.yml
...
namespace/project-pr-1 created
deployment.apps/project-pr-1 created
service/project-pr-1 created
horizontalpodautoscaler.autoscaling/project-pr-1 created
secret/project-pr-1 created
configmap/project-pr-1 created
ingress.extensions/project-pr-1 created
namespace/project-pr-2 created
deployment.apps/project-pr-2 created
service/project-pr-2 created
horizontalpodautoscaler.autoscaling/project-pr-2 created
secret/project-pr-2 created
configmap/project-pr-2 created
ingress.extensions/project-pr-2 created

Проверяваме дали всички горепосочени ресурси са създадени успешно:

$ kubectl get namespace,pods,deployment,service,horizontalpodautoscaler,configmap,ingress -n project-pr-1 && kubectl get namespace,pods,deployment,service,horizontalpodautoscaler,configmap,ingress -n project-pr-2
...
NAME                              ... READY ... STATUS  ... AGE
pod/project-pr-1-848d5fdff6-rpmzw ... 1/1   ... Running ... 67s

NAME                         ... READY ... AVAILABLE ... AGE
deployment.apps/project-pr-1 ... 1/1   ... 1         ... 67s
...

Тъй като включихме debug, namespace'ите project-pr-1 и project-pr-2, следователно и всички останали ресурси трябва да бъдат незабавно изтривани, без да се отчита параметъра afterDaysWithoutDeploy. В логовете на оператора това е видно:

$ kubectl logs stale-feature-branch-operator-6bfbfd4df8-m7sch -n stale-feature-branch-operator
... "msg":"Namespace should be deleted due to debug mode is enabled.","namespaceName":"project-pr-1"}
... "msg":"Namespace is being processing.","namespaceName":"project-pr-1","namespaceCreationTimestamp":"2020-06-16 18:43:58 +0300 EEST"}
... "msg":"Namespace has been deleted.","namespaceName":"project-pr-1"}
... "msg":"Namespace should be deleted due to debug mode is enabled.","namespaceName":"project-pr-2"}
... "msg":"Namespace is being processing.","namespaceName":"project-pr-2","namespaceCreationTimestamp":"2020-06-16 18:43:58 +0300 EEST"}
... "msg":"Namespace has been deleted.","namespaceName":"project-pr-2"}

Ако проверите наличието на ресурсите, те ще бъдат в статус Terminating (процес на изтриване) или вече изтрити (изходът на командата е пуст).

$ kubectl get namespace,pods,deployment,service,horizontalpodautoscaler,configmap,ingress -n project-pr-1 && kubectl get namespace,pods,deployment,service,horizontalpodautoscaler,configmap,ingress -n project-pr-2
...

Можете да повторите процеса на създаване фикстури няколко пъти и да се уверите, че те ще бъдат изтрити в рамките на минута.

Алтернативи

Какви алтернативи съществуват вместо оператора, който работи в клъстера? Има няколко подхода, всички те имат недостатъци (и техните недостатъци са субективни), и всеки сам решава какво най-добре подхожда на конкретния проект:

  1. Да се изтрива feature branch по време на билд на master клона по непрекъсната интеграция.

    • За целта трябва да се знае кой pull request е свързан с commit-а, който се билдва. Тъй като namespace на feature branch съдържа идентификатор на pull request-а — неговия номер или името на клона, идентификаторът винаги трябва да се посочва в commit-a.
    • Билдовете на master клоновете се провалят. Например, имате следните стъпки: изтеглете проекта, стартирайте тестовете, съберете проекта, направете релиз, изпратете уведомления, изчистете feature branch на последния pull request. Ако билдът се провали при изпращането на уведомлението, ще трябва да изтривате всички ресурси в клъстера ръчно.
    • Без необходимия контекст, изтриването на feature branch в master билда не е очевидно.

  2. Използване на webhook-ове (пример).

    • Възможно е това да не е вашият подход. Например, в Jenkins, само един вид пайплайн поддържа възможността да запази конфигурациите си в изходния код. При използването на webhook-ове е необходимо да напишете свой скрипт за тяхната обработка. Този скрипт ще трябва да се постави в интерфейса на Jenkins, което е трудно за поддържане.

  3. Да напишете Cronjob и да добавите Kubernetes клъстера.

    • Разход на време за написването и поддръжката.
    • Операторът вече работи в подобен стил, е документиран и се поддържа.

Благодарим за вниманието към статията. Линк към проекта в Github.

Източник: habr.com

Купете надежден хостинг за сайтове със защита от DDoS, VPS и VDS сървъри 🔥 Купете надежден хостинг за сайтове със защита от DDoS, VPS и VDS сървъри | ProHoster