{"id":86902,"date":"2020-07-01T07:42:28","date_gmt":"2020-07-01T05:42:28","guid":{"rendered":"https:\/\/prohoster.info\/blog\/administrirovanie\/udalyaem-ustarevshuyu-feature-branch-v-kubernetes-klastere"},"modified":"2020-07-01T07:42:28","modified_gmt":"2020-07-01T05:42:28","slug":"udalyaem-ustarevshuyu-feature-branch-v-kubernetes-klastere","status":"publish","type":"post","link":"https:\/\/prohoster.info\/fr\/blog\/administrirovanie\/udalyaem-ustarevshuyu-feature-branch-v-kubernetes-klastere","title":{"rendered":"Suppression de la branche de fonctionnalit\u00e9s obsol\u00e8te dans le cluster Kubernetes","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p><img decoding=\"async\" alt=\"Suppression de la branche de fonctionnalit\u00e9s obsol\u00e8te dans le cluster Kubernetes\" src=\"\/wp-content\/uploads\/2020\/07\/d97c0cce385c587e16f26f6d19b9d108.png\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p>Bonjour ! <b>Branche de fonctionnalit\u00e9<\/b> (aka d\u00e9ploiement de pr\u00e9visualisation, application de r\u00e9vision) \u2014 c'est lorsque non seulement la branche master est d\u00e9ploy\u00e9e, mais aussi chaque pull request sur une URL unique. Vous pouvez v\u00e9rifier si le code fonctionne dans un environnement de production, la fonctionnalit\u00e9 peut \u00eatre montr\u00e9e \u00e0 d'autres d\u00e9veloppeurs ou chefs de produits. Tant que vous travaillez dans la pull request, chaque nouveau commit supprime le d\u00e9ploiement actuel pour l'ancien code et un nouveau d\u00e9ploiement pour le nouveau code est cr\u00e9\u00e9. Des questions peuvent survenir lorsque vous avez fusionn\u00e9 la pull request dans la branche master. La branche de fonctionnalit\u00e9 n'est plus n\u00e9cessaire, mais les ressources Kubernetes sont toujours pr\u00e9sentes dans le cluster.<\/p>\n<p><noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h2>\u00c0 propos des branches de fonctionnalit\u00e9s<\/h2>\n<p><\/p>\n<p>Une des approches pour cr\u00e9er des branches de fonctionnalit\u00e9s dans Kubernetes consiste \u00e0 utiliser des namespaces. En r\u00e9sum\u00e9, la configuration de production ressemble \u00e0 ceci :<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">kind: Namespace\napiVersion: v1\nmetadata:\n  name: habr-back-end\n...\n\nkind: Deployment\napiVersion: apps\/v1\nmetadata:\n  namespace: habr-back-end\nspec:\n  replicas: 3\n...<\/code><\/pre>\n<p><\/p>\n<p>Pour la branche de fonctionnalit\u00e9, un namespace est cr\u00e9\u00e9 avec son identifiant (par exemple, le num\u00e9ro de la pull request) et un certain pr\u00e9fixe\/suffixe (par exemple, <b>-pr-<\/b>):<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">kind: Namespace\napiVersion: v1\nmetadata:\n  name: habr-back-end-pr-17\n...\n\nkind: Deployment\napiVersion: apps\/v1\nmetadata:\n  namespace: habr-back-end-pr-17\nspec:\n  replicas: 1\n...<\/code><\/pre>\n<p><\/p>\n<p>En g\u00e9n\u00e9ral, j'ai \u00e9crit <b>Kubernetes Operator<\/b> (une application qui a acc\u00e8s aux ressources du cluster), <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/dmytrostriletskyi\/stale-feature-branch-operator\">lien vers le projet sur Github<\/a><\/noindex>). Il supprime les namespaces associ\u00e9s aux anciennes branches de fonctionnalit\u00e9s. Dans Kubernetes, si vous supprimez un namespace, les autres ressources dans ce namespace sont \u00e9galement automatiquement supprim\u00e9es.<\/p>\n<p><\/p>\n<pre><code class=\"bash\">$ kubectl get pods --all-namespaces | grep -e \"-pr-\"\nNAMESPACE            ... AGE\nhabr-back-end-pr-264 ... 4d8h\nhabr-back-end-pr-265 ... 5d7h<\/code><\/pre>\n<p><\/p>\n<p>Pour savoir comment int\u00e9grer les branches de fonctionnalit\u00e9s dans le cluster, vous pouvez lire <noindex><a rel=\"nofollow\" href=\"https:\/\/itnext.io\/feature-deployments-in-kubernetes-c74bdcff0d8e\">ici<\/a><\/noindex> et <noindex><a rel=\"nofollow\" href=\"https:\/\/codefresh.io\/kubernetes-tutorial\/dynamically-creating-k8s-namespaces-every-branch-pull-request-2\">ici<\/a><\/noindex>.<\/p>\n<p><\/p>\n<h2>Motivation<\/h2>\n<p><\/p>\n<p>Examiner le cycle de vie typique d'une pull request avec int\u00e9gration continue (<code>int\u00e9gration continue<\/code>):<\/p>\n<p><\/p>\n<ol>\n<li>Nous poussons un nouveau commit dans la branche.<\/li>\n<li>Lors de la construction, des linters et\/ou des tests sont lanc\u00e9s.<\/li>\n<li>Les configurations Kubernetes de la pull request sont g\u00e9n\u00e9r\u00e9es \u00e0 la vol\u00e9e (par exemple, en ins\u00e9rant son num\u00e9ro dans un mod\u00e8le pr\u00eat).<\/li>\n<li>Les configurations sont appliqu\u00e9es au cluster \u00e0 l'aide de kubectl (d\u00e9ploiement).<\/li>\n<li>La pull request est fusionn\u00e9e dans la branche master.<\/li>\n<\/ol>\n<p><\/p>\n<p>Tant que vous travaillez dans la pull request, chaque nouveau commit supprime le d\u00e9ploiement actuel pour l'ancien code et un nouveau d\u00e9ploiement pour le nouveau code est cr\u00e9\u00e9. Mais lorsque la pull request est fusionn\u00e9e dans la branche master, seul la branche master sera construite. En fin de compte, nous avons oubli\u00e9 la pull request, mais ses ressources Kubernetes sont toujours pr\u00e9sentes dans le cluster.<\/p>\n<p><\/p>\n<h2>Comment utiliser<\/h2>\n<p><\/p>\n<p>Installez le projet avec la commande ci-dessous :<\/p>\n<p><\/p>\n<pre><code class=\"bash\">$ kubectl apply -f https:\/\/raw.githubusercontent.com\/dmytrostriletskyi\/stale-feature-branch-operator\/master\/configs\/production.yml<\/code><\/pre>\n<p><\/p>\n<p>Cr\u00e9ez un fichier avec le contenu suivant et installez-le via <code>kubectl apply -f<\/code>:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">apiVersion: feature-branch.dmytrostriletskyi.com\/v1\nkind: StaleFeatureBranch\nmetadata:\n  name: stale-feature-branch\nspec:\n  namespaceSubstring: -pr-\n  afterDaysWithoutDeploy: 3<\/code><\/pre>\n<p><\/p>\n<p>Param\u00e8tre <b>namespaceSubstring<\/b> n\u00e9cessaire pour filtrer les namespaces pour les pull requests des autres namespaces. Par exemple, si le cluster contient les namespaces suivants : <code>habr-back-end<\/code>, <code>habr-front-end<\/code>, <code>habr-back-end-pr-17<\/code>, <code>habr-back-end-pr-33<\/code>, alors les candidats \u00e0 la suppression seront <code>habr-back-end-pr-17<\/code>, <code>habr-back-end-pr-33<\/code>.<\/p>\n<p><\/p>\n<p>Param\u00e8tre <b>afterDaysWithoutDeploy<\/b> n\u00e9cessaire pour supprimer les anciens namespaces. Par exemple, si un namespace est cr\u00e9\u00e9 <code>il y a 3 jours et 1 heure<\/code> , et que le param\u00e8tre sp\u00e9cifie <code>3 jours<\/code>, ce namespace sera supprim\u00e9. Cela fonctionne aussi dans l'autre sens, si le namespace a \u00e9t\u00e9 cr\u00e9\u00e9 <code>il y a 2 jours et 23 heures<\/code> , et que le param\u00e8tre sp\u00e9cifie <code>3 jours<\/code>, ce namespace ne sera pas supprim\u00e9.<\/p>\n<p><\/p>\n<p>Il y a un autre param\u00e8tre qui d\u00e9termine \u00e0 quelle fr\u00e9quence tous les namespaces doivent \u00eatre scann\u00e9s et v\u00e9rifi\u00e9s pour les jours sans d\u00e9ploiement \u2014 <b>checkEveryMinutes<\/b>. Par d\u00e9faut, il est \u00e9gal \u00e0 <code>30 minutes<\/code>.<\/p>\n<p><\/p>\n<h2>Comment cela fonctionne<\/h2>\n<p><\/p>\n<p>En pratique, il faut :<\/p>\n<p><\/p>\n<ol>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/get-docker\">Docker<\/a><\/noindex> pour fonctionner dans un environnement isol\u00e9.<\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/kubernetes.io\/docs\/tasks\/tools\/install-minikube\">Minikube<\/a><\/noindex> va lancer un cluster Kubernetes localement.<\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/kubernetes.io\/docs\/tasks\/tools\/install-kubectl\">kubectl<\/a><\/noindex> \u2014 interface en ligne de commande pour g\u00e9rer le cluster.<\/li>\n<\/ol>\n<p><\/p>\n<p>Lan\u00e7ons le cluster Kubernetes localement :<\/p>\n<p><\/p>\n<pre><code class=\"bash\">$ minikube start --vm-driver=docker\nminikube v1.11.0 sur Darwin 10.15.5\nUtilisation du pilote docker bas\u00e9 sur le profil existant.\nD\u00e9marrage du n\u0153ud du plan de contr\u00f4le minikube dans le cluster minikube.<\/code><\/pre>\n<p><\/p>\n<p>Indiquons <code>kubectl<\/code> d'utiliser le cluster local par d\u00e9faut :<\/p>\n<p><\/p>\n<pre><code class=\"bash\">$ kubectl config use-context minikube\nContexte \"minikube\" activ\u00e9.<\/code><\/pre>\n<p><\/p>\n<p>T\u00e9l\u00e9chargeons les configurations pour l'environnement de production :<\/p>\n<p><\/p>\n<pre><code class=\"bash\">$ curl https:\/\/raw.githubusercontent.com\/dmytrostriletskyi\/stale-feature-branch-operator\/master\/configs\/production.yml &gt; stale-feature-branch-production-configs.yml<\/code><\/pre>\n<p><\/p>\n<p>Comme les configurations de production sont r\u00e9gl\u00e9es pour v\u00e9rifier les anciens namespaces, et qu'il n'y en a pas dans notre nouveau cluster, rempla\u00e7ons la variable d'environnement <code>IS_DEBUG<\/code> sur <code>true<\/code>. Avec cette valeur, le param\u00e8tre <code>afterDaysWithoutDeploy<\/code> n'est pas pris en compte et les namespaces ne sont pas v\u00e9rifi\u00e9s pour les jours sans d\u00e9ploiement, seulement pour la pr\u00e9sence de sous-cha\u00eenes (<code>-pr-<\/code>).<\/p>\n<p><\/p>\n<p>Si vous \u00eates sur <code>Linux<\/code>:<\/p>\n<p><\/p>\n<pre><code class=\"bash\">$ sed -i 's|false|true|g' stale-feature-branch-production-configs.yml<\/code><\/pre>\n<p><\/p>\n<p>Si vous \u00eates sur <code>macOS<\/code>:<\/p>\n<p><\/p>\n<pre><code class=\"bash\">$ sed -i \"\" 's|false|true|g' stale-feature-branch-production-configs.yml<\/code><\/pre>\n<p><\/p>\n<p>Installons le projet :<\/p>\n<p><\/p>\n<pre><code class=\"bash\">$ kubectl apply -f stale-feature-branch-production-configs.yml<\/code><\/pre>\n<p><\/p>\n<p>V\u00e9rifions que la ressource est apparue dans le cluster <code>StaleFeatureBranch<\/code>:<\/p>\n<p><\/p>\n<pre><code class=\"bash\">$ kubectl api-resources | grep stalefeaturebranches\nNOM                 ... APIGROUP                             ... TYPE\nstalefeaturebranches ... feature-branch.dmytrostriletskyi.com ... StaleFeatureBranch<\/code><\/pre>\n<p><\/p>\n<p>V\u00e9rifions que l'op\u00e9rateur est apparu dans le cluster :<\/p>\n<p><\/p>\n<pre><code class=\"bash\">$ kubectl get pods --namespace stale-feature-branch-operator\nNOM                                           ... \u00c9TAT  ... \u00c2GE\nstale-feature-branch-operator-6bfbfd4df8-m7sch ... En cours d'ex\u00e9cution ... 38s<\/code><\/pre>\n<p><\/p>\n<p>Si nous regardons ses logs, il est pr\u00eat \u00e0 traiter les ressources <code>StaleFeatureBranch<\/code>:<\/p>\n<p><\/p>\n<pre><code class=\"bash\">$ kubectl logs stale-feature-branch-operator-6bfbfd4df8-m7sch -n stale-feature-branch-operator\n... \"msg\":\"Version de l'op\u00e9rateur : 0.0.1\"}\n...\n... \"msg\":\"D\u00e9marrage de EventSource\", ... , \"source\":\"kind source: \/, Kind=\"}\n... \"msg\":\"D\u00e9marrage du contr\u00f4leur\", ...}\n... \"msg\":\"D\u00e9marrage des travailleurs\", ..., \"worker count\":1}<\/code><\/pre>\n<p><\/p>\n<p>Installons les <code>fixtures<\/code> (configurations pr\u00eates pour la mod\u00e9lisation des ressources de cluster) pour la ressource <code>StaleFeatureBranch<\/code>:<\/p>\n<p><\/p>\n<pre><code class=\"bash\">$ kubectl apply -f https:\/\/raw.githubusercontent.com\/dmytrostriletskyi\/stale-feature-branch-operator\/master\/fixtures\/stale-feature-branch.yml<\/code><\/pre>\n<p><\/p>\n<p>Les configurations indiquent de rechercher des namespaces contenant une sous-cha\u00eene <code>-pr-<\/code> toutes les <code>1 minute<\/code>.:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">apiVersion: feature-branch.dmytrostriletskyi.com\/v1\nkind: StaleFeatureBranch\nmetadata:\n  name: stale-feature-branch\nspec:\n  namespaceSubstring: -pr-\n  afterDaysWithoutDeploy: 1 \n  checkEveryMinutes: 1<\/code><\/pre>\n<p><\/p>\n<p>L'op\u00e9rateur a r\u00e9agi et est pr\u00eat \u00e0 v\u00e9rifier les namespaces :<\/p>\n<p><\/p>\n<pre><code class=\"bash\">$ kubectl logs stale-feature-branch-operator-6bfbfd4df8-m7sch -n stale-feature-branch-operator\n... \"msg\":\"La branche de fonctionnalit\u00e9 obsol\u00e8te est en cours de traitement.\",\"namespaceSubstring\":\"-pr-\",\"afterDaysWithoutDeploy\":1,\"checkEveryMinutes\":1,\"isDebug\":\"true\"}<\/code><\/pre>\n<p><\/p>\n<p>Nous installons <code>fixtures<\/code>, contenant deux namespaces (<code>project-pr-1<\/code>, <code>project-pr-2<\/code>) et leurs <code>d\u00e9ploiements<\/code>, <code>services<\/code>, <code>ingress<\/code>, et ainsi de suite :<\/p>\n<p><\/p>\n<pre><code class=\"bash\">$ 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\n...\nnamespace\/project-pr-1 cr\u00e9\u00e9\nd\u00e9ploiement.apps\/project-pr-1 cr\u00e9\u00e9\nservice\/project-pr-1 cr\u00e9\u00e9\nhorizontalpodautoscaler.autoscaling\/project-pr-1 cr\u00e9\u00e9\nsecret\/project-pr-1 cr\u00e9\u00e9\nconfigmap\/project-pr-1 cr\u00e9\u00e9\ningress.extensions\/project-pr-1 cr\u00e9\u00e9\nnamespace\/project-pr-2 cr\u00e9\u00e9\nd\u00e9ploiement.apps\/project-pr-2 cr\u00e9\u00e9\nservice\/project-pr-2 cr\u00e9\u00e9\nhorizontalpodautoscaler.autoscaling\/project-pr-2 cr\u00e9\u00e9\nsecret\/project-pr-2 cr\u00e9\u00e9\nconfigmap\/project-pr-2 cr\u00e9\u00e9\ningress.extensions\/project-pr-2 cr\u00e9\u00e9<\/code><\/pre>\n<p><\/p>\n<p>V\u00e9rifions que toutes les ressources ci-dessus ont \u00e9t\u00e9 cr\u00e9\u00e9es avec succ\u00e8s :<\/p>\n<p><\/p>\n<pre><code class=\"bash\">$ kubectl get namespace,pods,d\u00e9ploiement,service,horizontalpodautoscaler,configmap,ingress -n project-pr-1 &amp;&amp; kubectl get namespace,pods,d\u00e9ploiement,service,horizontalpodautoscaler,configmap,ingress -n project-pr-2\n...\nNOM                              ... PR\u00caT ... STATUT  ... \u00c2GE\npod\/project-pr-1-848d5fdff6-rpmzw ... 1\/1   ... En cours d'ex\u00e9cution ... 67s\n\nNOM                         ... PR\u00caT ... DISPONIBLE ... \u00c2GE\nd\u00e9ploiement.apps\/project-pr-1 ... 1\/1   ... 1         ... 67s\n...<\/code><\/pre>\n<p><\/p>\n<p>Puisque nous avons activ\u00e9 <code>debug<\/code>, namespaces <code>project-pr-1<\/code> et <code>project-pr-2<\/code>, tous les autres ressources devraient \u00e9galement \u00eatre supprim\u00e9es imm\u00e9diatement, sans tenir compte du param\u00e8tre <code>afterDaysWithoutDeploy<\/code>. Dans les logs de l'op\u00e9rateur, cela est visible :<\/p>\n<p><\/p>\n<pre><code class=\"bash\">$ kubectl logs stale-feature-branch-operator-6bfbfd4df8-m7sch -n stale-feature-branch-operator\n... \"msg\":\"Le namespace doit \u00eatre supprim\u00e9 car le mode d\u00e9bogage est activ\u00e9.\",\"namespaceName\":\"project-pr-1\"}\n... \"msg\":\"Le namespace est en cours de traitement.\",\"namespaceName\":\"project-pr-1\",\"namespaceCreationTimestamp\":\"2020-06-16 18:43:58 +0300 EEST\"}\n... \"msg\":\"Le namespace a \u00e9t\u00e9 supprim\u00e9.\",\"namespaceName\":\"project-pr-1\"}\n... \"msg\":\"Le namespace doit \u00eatre supprim\u00e9 car le mode d\u00e9bogage est activ\u00e9.\",\"namespaceName\":\"project-pr-2\"}\n... \"msg\":\"Le namespace est en cours de traitement.\",\"namespaceName\":\"project-pr-2\",\"namespaceCreationTimestamp\":\"2020-06-16 18:43:58 +0300 EEST\"}\n... \"msg\":\"Le namespace a \u00e9t\u00e9 supprim\u00e9.\",\"namespaceName\":\"project-pr-2\"}<\/code><\/pre>\n<p><\/p>\n<p>Si nous v\u00e9rifions la pr\u00e9sence des ressources, elles seront dans l'\u00e9tat <code>Terminating<\/code> (processus de suppression) ou d\u00e9j\u00e0 supprim\u00e9es (le r\u00e9sultat de la commande est vide).<\/p>\n<p><\/p>\n<pre><code class=\"bash\">$ kubectl get namespace,pods,d\u00e9ploiement,service,horizontalpodautoscaler,configmap,ingress -n project-pr-1 &amp;&amp; kubectl get namespace,pods,d\u00e9ploiement,service,horizontalpodautoscaler,configmap,ingress -n project-pr-2\n...<\/code><\/pre>\n<p><\/p>\n<p>Vous pouvez r\u00e9p\u00e9ter le processus de cr\u00e9ation <code>fixtures<\/code> assurez-vous plusieurs fois qu'ils seront supprim\u00e9s dans la minute.<\/p>\n<p><\/p>\n<h2>Alternatives<\/h2>\n<p><\/p>\n<p>Que peut-on faire \u00e0 la place d'un op\u00e9rateur fonctionnant dans un cluster ? Plusieurs approches sont possibles, toutes ne sont pas id\u00e9ales (et leurs inconv\u00e9nients sont subjectifs), et chacun d\u00e9cide ce qui convient le mieux \u00e0 son projet sp\u00e9cifique :<\/p>\n<p><\/p>\n<ol>\n<li>\n<p>Supprimer la branche feature pendant le build de la branche master d'int\u00e9gration continue.<\/p>\n<p><\/p>\n<ul>\n<li>Pour cela, il faut savoir quel pull request est associ\u00e9 au commit qui est en cours de construction. \u00c9tant donn\u00e9 que le namespace de la branche de fonctionnalit\u00e9 contient l'identifiant du pull request \u2014 son num\u00e9ro ou le nom de la branche, l'identifiant devra toujours \u00eatre indiqu\u00e9 dans le commit.<\/li>\n<li>Les constructions des branches master \u00e9chouent. Par exemple, vous avez les \u00e9tapes suivantes : t\u00e9l\u00e9charger le projet, ex\u00e9cuter les tests, construire le projet, effectuer la release, envoyer des notifications, nettoyer la branche de fonctionnalit\u00e9 du dernier pull request. Si la construction \u00e9choue lors de l'envoi de la notification, vous devrez supprimer manuellement toutes les ressources du cluster.<\/li>\n<li>Sans un contexte ad\u00e9quat, la suppression de la branche de fonctionnalit\u00e9 dans une construction master n'est pas \u00e9vidente.<\/li>\n<\/ul>\n<p>\n<\/li>\n<li>\n<p>L'utilisation de webhooks (<noindex><a rel=\"nofollow\" href=\"https:\/\/github.community\/t\/trigger-jenkins-job-when-a-pull-request-is-merged-to-a-branch\/1169\">exemple<\/a><\/noindex>).<\/p>\n<p><\/p>\n<ul>\n<li>Peut-\u00eatre que ce n'est pas votre approche. Par exemple, dans <noindex><a rel=\"nofollow\" href=\"https:\/\/www.jenkins.io\">Jenkins<\/a><\/noindex>, seul un type de pipeline prend en charge la possibilit\u00e9 de conserver ses configurations dans le code source. Lors de l'utilisation de webhooks, il est n\u00e9cessaire d'\u00e9crire son propre script pour les traiter. Ce script devra \u00eatre int\u00e9gr\u00e9 dans l'interface de Jenkins, ce qui est difficile \u00e0 maintenir.<\/li>\n<\/ul>\n<p>\n<\/li>\n<li>\n<p>\u00c9crire <noindex><a rel=\"nofollow\" href=\"https:\/\/kubernetes.io\/docs\/concepts\/workloads\/controllers\/cron-jobs\/\">Cronjob<\/a><\/noindex> et ajouter un cluster Kubernetes.<\/p>\n<p><\/p>\n<ul>\n<li>Une perte de temps pour \u00e9crire et maintenir.<\/li>\n<li>L'op\u00e9rateur fonctionne d\u00e9j\u00e0 dans un style similaire, est document\u00e9 et support\u00e9.<\/li>\n<\/ul>\n<p>\n<\/li>\n<\/ol>\n<p><\/p>\n<p>Merci pour votre attention \u00e0 l'article. <strong><noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/dmytrostriletskyi\/stale-feature-branch-operator\">Lien vers le projet sur Github<\/a><\/noindex><\/strong>.<\/p>\n<p>Source : <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/post\/508534\/\">habr.com<\/a> <\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u041f\u0440\u0438\u0432\u0435\u0442! Feature branch (aka deploy preview, review app) \u2014 \u044d\u0442\u043e \u043a\u043e\u0433\u0434\u0430 \u0434\u0435\u043f\u043b\u043e\u0438\u0442\u0441\u044f \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e master \u0432\u0435\u0442\u043a\u0430, \u043d\u043e \u0438 \u043a\u0430\u0436\u0434\u044b\u0439 pull request \u043d\u0430 \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0439 URL. \u041c\u043e\u0436\u043d\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043b\u0438 \u043a\u043e\u0434 \u0432 production-\u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u0438, \u0444\u0438\u0447\u0443 \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u0434\u0440\u0443\u0433\u0438\u043c \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0441\u0442\u0430\u043c \u0438\u043b\u0438 \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u043e\u043b\u043e\u0433\u0430\u043c. \u041f\u043e\u043a\u0430 \u0432\u044b \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442\u0435 \u0432 pull request&#8217;\u0435, \u043a\u0430\u0436\u0434\u044b\u0439 \u043d\u043e\u0432\u044b\u0439 commit \u0442\u0435\u043a\u0443\u0449\u0438\u0439 deploy \u0434\u043b\u044f \u0441\u0442\u0430\u0440\u043e\u0433\u043e \u043a\u043e\u0434\u0430 \u0443\u0434\u0430\u043b\u044f\u0435\u0442\u0441\u044f, \u0430 \u043d\u043e\u0432\u044b\u0439 [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":86903,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-86902","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.2 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u041f\u0440\u0438\u0432\u0435\u0442!\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/fr\/blog\/administrirovanie\/udalyaem-ustarevshuyu-feature-branch-v-kubernetes-klastere\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.2\" \/>\n\t\t<meta property=\"og:locale\" content=\"fr_FR\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47\u0423\u0434\u0430\u043b\u044f\u0435\u043c \u0443\u0441\u0442\u0430\u0440\u0435\u0432\u0448\u0443\u044e feature branch \u0432 Kubernetes \u043a\u043b\u0430\u0441\u0442\u0435\u0440\u0435 | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u041f\u0440\u0438\u0432\u0435\u0442!\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/fr\/blog\/administrirovanie\/udalyaem-ustarevshuyu-feature-branch-v-kubernetes-klastere\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"350\" \/>\n\t\t<meta property=\"og:image:height\" content=\"350\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2020-07-01T05:42:28+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2020-07-01T05:42:28+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"\ud83e\udd47 Suppression d'une branche feature obsol\u00e8te dans le cluster Kubernetes | ProHoster","description":"Bonjour !","canonical_url":"https:\/\/prohoster.info\/fr\/blog\/administrirovanie\/udalyaem-ustarevshuyu-feature-branch-v-kubernetes-klastere","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"fr_FR","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47\u0423\u0434\u0430\u043b\u044f\u0435\u043c \u0443\u0441\u0442\u0430\u0440\u0435\u0432\u0448\u0443\u044e feature branch \u0432 Kubernetes \u043a\u043b\u0430\u0441\u0442\u0435\u0440\u0435 | ProHoster","og:description":"\u041f\u0440\u0438\u0432\u0435\u0442!","og:url":"https:\/\/prohoster.info\/fr\/blog\/administrirovanie\/udalyaem-ustarevshuyu-feature-branch-v-kubernetes-klastere","og:image":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:secure_url":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:width":350,"og:image:height":350,"article:published_time":"2020-07-01T05:42:28+00:00","article:modified_time":"2020-07-01T05:42:28+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"86902","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"seo_analyzer_scan_date":null,"breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-02-28 14:03:55","updated":"2022-10-03 08:48:02","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/posts\/86902","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/comments?post=86902"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/posts\/86902\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/media\/86903"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/media?parent=86902"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/categories?post=86902"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/tags?post=86902"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}