Премахване на остарял клон на функция в клъстер на Kubernetes
Привет! Функционален клон (известен още като deploy preview, review app) - това е, когато не само главният клон е разгърнат, но и всяка заявка за изтегляне към уникален URL адрес. Можете да проверите дали кодът работи в производствена среда; функцията може да бъде показана на други програмисти или продуктови специалисти. Докато работите в заявка за изтегляне, всяко ново прилагане на текущото разгръщане за стария код се изтрива и новото разгръщане за новия код се разгръща. Може да възникнат въпроси, когато обедините заявка за изтегляне в главния клон. Вече не се нуждаете от клона на функциите, но ресурсите на Kubernetes все още са в клъстера.
Повече за разклоненията на функциите
Един подход за създаване на разклонения на функции в Kubernetes е използването на пространства от имена. Накратко производствената конфигурация изглежда така:
За клон на функция се създава пространство от имена с неговия идентификатор (например номер на заявка за изтегляне) и някакъв вид префикс/постфикс (например, -пр-):
Общо взето писах Оператор Kubernetes (приложение, което има достъп до ресурсите на клъстера), връзка към проекта в Github. Той премахва пространства от имена, които принадлежат към стари разклонения на функции. В Kubernetes, ако изтриете пространство от имена, другите ресурси в това пространство от имена също се изтриват автоматично.
$ kubectl get pods --all-namespaces | grep -e "-pr-"
NAMESPACE ... AGE
habr-back-end-pr-264 ... 4d8h
habr-back-end-pr-265 ... 5d7h
Можете да прочетете как да внедрите разклонения на функции в клъстер тук и тук.
Мотивиране
Нека да разгледаме типичен жизнен цикъл на заявка за изтегляне с непрекъсната интеграция (continuous integration):
Изпращаме нов ангажимент към клона.
При изграждането се изпълняват линтери и/или тестове.
Конфигурациите на Kubernetes pull request се генерират в движение (например номерът му се вмъква в готовия шаблон).
Използвайки kubectl apply, конфигурациите се добавят към клъстера (разгръщане).
Заявката за изтегляне се обединява в главния клон.
Докато работите в заявка за изтегляне, всяко ново прилагане на текущото разгръщане за стария код се изтрива и новото разгръщане за новия код се разгръща. Но когато заявка за изтегляне се слее в главния клон, ще бъде изграден само главният клон. В резултат на това се оказва, че вече сме забравили за заявката за изтегляне и неговите ресурси на Kubernetes все още са в клъстера.
Параметър namespaceSubstring необходими за филтриране на пространства от имена за заявки за изтегляне от други пространства от имена. Например, ако клъстерът има следните пространства от имена: 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 необходими за изтриване на стари пространства от имена. Например, ако е създадено пространство от имена 3 дня 1 час назад, а параметърът показва 3 дня, това пространство от имена ще бъде изтрито. Работи и в обратна посока, ако пространството от имена е създадено 2 дня 23 часа назад, а параметърът показва 3 дня, това пространство от имена няма да бъде изтрито.
Има още един параметър, той отговаря за това колко често да се сканират всички пространства от имена и да се проверяват дни без внедряване - checkEveryMinutes. По подразбиране е равно 30 минутам.
Миникубе ще създаде локален клъстер на Kubernetes.
kubectl — интерфейс на командния ред за управление на клъстери.
Създаваме клъстер на 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".
Тъй като производствените конфигурации са конфигурирани да проверяват стари пространства от имена и нашият новоповдигнат клъстер ги няма, ние ще заменим променливата на средата IS_DEBUG на true. С тази стойност параметърът afterDaysWithoutDeploy не се взема предвид и пространствата от имена не се проверяват за дни без внедряване, а само за появата на подниза (-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 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":"Stale feature branch is being processing.","namespaceSubstring":"-pr-","afterDaysWithoutDeploy":1,"checkEveryMinutes":1,"isDebug":"true"}
инсталирам fixtures, съдържащ две пространства от имена (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, пространства от имена 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 няколко пъти и се уверете, че са премахнати в рамките на минута.
алтернативи
Какво може да се направи вместо оператор, който работи в клъстер? Има няколко подхода, всички те са несъвършени (а недостатъците им са субективни) и всеки сам решава кое е най-добро за даден проект:
Изтриване на клон на функция по време на изграждане на непрекъсната интеграция на главен клон.
За да направите това, трябва да знаете коя заявка за изтегляне се отнася до ангажимента, който се изгражда. Тъй като пространството от имена на клона на функцията съдържа идентификатора на заявката за изтегляне - неговия номер или името на клона, идентификаторът винаги ще трябва да бъде посочен в ангажимента.
Основните компилации на клонове се провалят. Например, имате следните етапи: изтегляне на проекта, изпълнение на тестове, изграждане на проекта, създаване на версия, изпращане на известия, изчистване на клона на функцията на последната заявка за изтегляне. Ако изграждането се провали при изпращане на известие, ще трябва да изтриете всички ресурси в клъстера ръчно.
Без подходящ контекст изтриването на клонове на функции в основната компилация не е очевидно.
Това може да не е вашият подход. Например в Дженкинс, само един тип тръбопровод поддържа възможността за запазване на неговите конфигурации в изходния код. Когато използвате webhooks, трябва да напишете свой собствен скрипт, за да ги обработвате. Този скрипт ще трябва да бъде поставен в интерфейса на Jenkins, който е труден за поддръжка.