Выдаляем састарэлую feature branch у Kubernetes кластары

Выдаляем састарэлую feature branch у Kubernetes кластары

Прывітанне! Feature branch (aka deploy preview, review app) - гэта калі дэплоіцца не толькі master галінка, але і кожны pull request на ўнікальны URL. Можна праверыць ці працуе код у production-акружэнні, фічу можна паказаць іншым праграмістам або прадуктолагам. Пакуль вы працуеце ў pull request'е, кожны новы commit бягучы deploy для старога кода выдаляецца, а новы deploy для новага кода выкочваецца. Пытанні могуць узнікнуць тады, калі вы смярдзілі pull request у master галінку. Feature branch вам больш не патрэбна, але рэсурсы Kubernetes усё яшчэ знаходзяцца ў кластары.

Яшчэ пра feature branch'і

Адзін з падыходаў як зрабіць feature branch'і ў Kubernetes - выкарыстоўваць namespace'ы. Калі коратка, production канфігурацыі выглядае так:

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

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

Для feature branch ствараецца 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 Operator (дадатак, якое мае доступ да рэсурсаў кластара), спасылка на праект на Github. Ён выдаляе namespace'ы, якія адносяцца да старых feature branch'ам. У 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

Пра тое як укараніць feature branch'і ў кластар, можна пачытаць тут и тут.

Матывацыя

Давайце паглядзім на тыповы жыццёвы цыкл pull request'a з бесперапыннай інтэграцыяй (continuous integration):

  1. Пушым новы commit у галінку.
  2. На білдзе, запускаюцца лінтэры і/або тэсты.
  3. На лёце фармуюцца канфігурацыі Kubernetes pull request'a (напрыклад, у гатовы шаблон падстаўляецца яго нумар).
  4. З дапамогай kubectl apply канфігурацыі пападаюць у кластар (deploy).
  5. Pull request зліваецца ў master галінку.

Пакуль вы працуеце ў pull request'е, кожны новы commit бягучы deploy для старога кода выдаляецца, а новы deploy для новага кода выкочваецца. Але калі pull request зліваецца ў master галінку, будзе білдзіцца толькі master галінка. У выніку атрымліваецца, што пра 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. Докер для працы ў ізаляваным асяроддзі.
  2. Мінікубе падыме Kubernetes кластар лакальна.
  3. кубектль - Інтэрфейс каманднага радка для кіравання кластарам.

Падымаем Kubernetes кластар лакальна:

$ minikube start --vm-driver=docker
minikube v1.11.0 on Darwin 10.15.5
Using the docker driver based on existing profile.
Starting control plane node minikube in cluster minikube.

Паказваем kubectl выкарыстоўваць лакальны кластар па змаўчанні:

$ kubectl config use-context minikube
Switched to context "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 на true. Пры такім значэнні параметр 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
NAME                 ... APIGROUP                             ... KIND
stalefeaturebranches ... feature-branch.dmytrostriletskyi.com ... StaleFeatureBranch

Правяраем, што ў кластары з'явіўся аператар:

$ kubectl get pods --namespace stale-feature-branch-operator
NAME                                           ... STATUS  ... AGE
stale-feature-branch-operator-6bfbfd4df8-m7sch ... Running ... 38s

Калі зазірнуць у яго логі, ён гатовы апрацоўваць рэсурсы StaleFeatureBranch:

$ kubectl logs stale-feature-branch-operator-6bfbfd4df8-m7sch -n stale-feature-branch-operator
... "msg":"Operator Version: 0.0.1"}
...
... "msg":"Starting EventSource", ... , "source":"kind source: /, Kind="}
... "msg":"Starting Controller", ...}
... "msg":"Starting workers", ..., "worker count":1}

Усталёўваны гатовыя fixtures (гатовыя канфігурацыі для мадэлявання рэсурсаў кластара) для рэсурсу 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"}

Усталёўваны fixtures, якія змяшчаюць два namespace'а (project-pr-1, project-pr-2) і іх deployments, services, 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
...

Можаце паўтарыць працэс стварэння fixtures некалькі разоў і пераканацца, што яны будуць выдаленыя на працягу хвіліны.

альтэрнатывы

Што можна зрабіць замест аператара, які працуе ў кластары? Падыходаў некалькі, усе яны неідэальныя (і іх недахопы суб'ектыўныя), і кожны сам вырашае што лепш за ўсё падыдзе на пэўным праекце:

  1. Выдаляць feature branch падчас білда бесперапыннай інтэграцыі master галінкі.

    • Для гэтага трэба ведаць які pull request адносіцца да commit'у, які білдзіцца. Бо feature branch namespace утрымоўвае ў сабе ідэнтыфікатар pull request'a - яго нумар, ці назоў галінкі, ідэнтыфікатар заўсёды прыйдзецца паказваць у commit'e.
    • Білды master галінак фейлят. Напрыклад, у вас наступныя этапы: спампаваць праект, запусціць тэсты, сабраць праект, зрабіць рэліз, адправіць апавяшчэнні, ачысціць feature branch апошняга pull request'a. Калі білд сфейліцца на адпраўцы апавяшчэння, вам прыйдзецца выдаляць усе рэсурсы ў кластары рукамі.
    • Без належнага кантэксту, выдаленне feature branch'і ў master білдзе невідавочна.

  2. Выкарыстанне webhook'аў (прыклад).

    • Магчыма, гэта не ваш падыход. Напрыклад, у Джэнкінс, толькі адзін від пайплайна падтрымлівае магчымасць захоўваць яго канфігурацыі ў зыходным кодзе. Пры выкарыстанні webhook'аў трэба напісаць свой скрыпт для іх апрацоўкі. Гэты скрыпт давядзецца размяшчаць у інтэрфейсе Jenkins'а, што цяжка падтрымліваць.

  3. напісаць Кронджоб і дадаць Kubernetes кластар.

    • Выдатак часу на напісанне і падтрымку.
    • Аператар ужо працуе ў такім стылі, задакументаваны і падтрымліваецца.

Дзякуй за ўвагу да артыкула. Спасылка на праект на Github.

Крыніца: habr.com

Дадаць каментар