{"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\/es\/blog\/administrirovanie\/udalyaem-ustarevshuyu-feature-branch-v-kubernetes-klastere","title":{"rendered":"Eliminando la rama de caracter\u00edsticas obsoleta en el cl\u00faster de Kubernetes","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p><img decoding=\"async\" alt=\"Eliminando la rama de caracter\u00edsticas obsoleta en el cl\u00faster de Kubernetes\" src=\"\/wp-content\/uploads\/2020\/07\/d97c0cce385c587e16f26f6d19b9d108.png\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p>\u00a1Hola! <b>Rama de caracter\u00edsticas<\/b> (aka deploy preview, review app) \u2014 esto es cuando se despliega no solo la rama master, sino tambi\u00e9n cada pull request en una URL \u00fanica. Se puede comprobar si el c\u00f3digo funciona en un entorno de producci\u00f3n, o se puede mostrar la funci\u00f3n a otros programadores o product managers. Mientras trabajas en el pull request, cada nuevo commit elimina el despliegue actual para el c\u00f3digo antiguo y se despliega el nuevo c\u00f3digo. Pueden surgir preguntas cuando has fusionado el pull request en la rama master. La feature branch ya no la necesitas, pero los recursos de Kubernetes todav\u00eda est\u00e1n en el cl\u00faster.<\/p>\n<p><noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h2>M\u00e1s sobre feature branches<\/h2>\n<p><\/p>\n<p>Uno de los enfoques para hacer feature branches en Kubernetes es usar namespaces. En resumen, la configuraci\u00f3n de producci\u00f3n se ve as\u00ed:<\/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>Para la feature branch se crea un namespace con su identificador (por ejemplo, el n\u00famero del pull request) y alg\u00fan prefijo\/sufijo (por ejemplo, <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 general, escrib\u00ed <b>Kubernetes Operator<\/b> (una aplicaci\u00f3n que tiene acceso a los recursos del cl\u00faster), <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/dmytrostriletskyi\/stale-feature-branch-operator\">enlace al proyecto en Github<\/a><\/noindex>. Elimina los namespaces que pertenecen a las antiguas feature branches. En Kubernetes, si se elimina un namespace, otros recursos en ese namespace tambi\u00e9n se eliminan autom\u00e1ticamente.<\/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>Sobre c\u00f3mo implementar feature branches en el cl\u00faster, se puede leer <noindex><a rel=\"nofollow\" href=\"https:\/\/itnext.io\/feature-deployments-in-kubernetes-c74bdcff0d8e\">aqu\u00ed<\/a><\/noindex> y <noindex><a rel=\"nofollow\" href=\"https:\/\/codefresh.io\/kubernetes-tutorial\/dynamically-creating-k8s-namespaces-every-branch-pull-request-2\">aqu\u00ed<\/a><\/noindex>.<\/p>\n<p><\/p>\n<h2>Motivaci\u00f3n<\/h2>\n<p><\/p>\n<p>Veamos el ciclo de vida t\u00edpico de un pull request con integraci\u00f3n continua (<code>continuous integration<\/code>):<\/p>\n<p><\/p>\n<ol>\n<li>Hacemos push de un nuevo commit en la rama.<\/li>\n<li>En la compilaci\u00f3n, se ejecutan linters y\/o pruebas.<\/li>\n<li>Las configuraciones de Kubernetes del pull request se generan din\u00e1micamente (por ejemplo, su n\u00famero se inserta en una plantilla lista).<\/li>\n<li>Con kubectl apply, las configuraciones se env\u00edan al cl\u00faster (despliegue).<\/li>\n<li>El pull request se fusiona en la rama master.<\/li>\n<\/ol>\n<p><\/p>\n<p>Mientras trabajas en el pull request, cada nuevo commit elimina el despliegue actual para el c\u00f3digo antiguo y se despliega el nuevo c\u00f3digo. Pero cuando el pull request se fusiona en la rama master, solo se construir\u00e1 la rama master. Al final, resulta que nos olvidamos del pull request, pero sus recursos de Kubernetes a\u00fan est\u00e1n en el cl\u00faster.<\/p>\n<p><\/p>\n<h2>C\u00f3mo usar<\/h2>\n<p><\/p>\n<p>Instala el proyecto con el siguiente comando:<\/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>Crea un archivo con el siguiente contenido e inst\u00e1lalo a trav\u00e9s de <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>Par\u00e1metro <b>namespaceSubstring<\/b> se necesita, para filtrar los namespaces de los pull requests de otros namespaces. Por ejemplo, si en el cl\u00faster hay los siguientes namespaces: <code>habr-back-end<\/code>, <code>habr-front-end<\/code>, <code>habr-back-end-pr-17<\/code>, <code>habr-back-end-pr-33<\/code>, entonces los candidatos para eliminaci\u00f3n ser\u00e1n <code>habr-back-end-pr-17<\/code>, <code>habr-back-end-pr-33<\/code>.<\/p>\n<p><\/p>\n<p>Par\u00e1metro <b>afterDaysWithoutDeploy<\/b> se necesita para eliminar los antiguos namespaces. Por ejemplo, si se cre\u00f3 un namespace <code>hace 3 d\u00edas 1 hora<\/code> , y en el par\u00e1metro se indica <code>3 d\u00edas<\/code>, este namespace ser\u00e1 eliminado. Funciona al rev\u00e9s: si el namespace fue creado <code>hace 2 d\u00edas 23 horas<\/code> , y en el par\u00e1metro se indica <code>3 d\u00edas<\/code>, este namespace no ser\u00e1 eliminado.<\/p>\n<p><\/p>\n<p>Hay otro par\u00e1metro que indica con qu\u00e9 frecuencia escanear todos los namespaces y verificar los d\u00edas sin despliegue \u2014 <b>checkEveryMinutes<\/b>. Por defecto es <code>30 minutos<\/code>.<\/p>\n<p><\/p>\n<h2>\u00bfC\u00f3mo funciona?<\/h2>\n<p><\/p>\n<p>En la pr\u00e1ctica, ser\u00e1 necesario:<\/p>\n<p><\/p>\n<ol>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/get-docker\">Docker<\/a><\/noindex> para trabajar en un entorno aislado.<\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/kubernetes.io\/docs\/tasks\/tools\/install-minikube\">Minikube<\/a><\/noindex> levantar\u00e1 el cl\u00faster de Kubernetes localmente.<\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/kubernetes.io\/docs\/tasks\/tools\/install-kubectl\">kubectl<\/a><\/noindex> \u2014 interfaz de l\u00ednea de comandos para gestionar el cl\u00faster.<\/li>\n<\/ol>\n<p><\/p>\n<p>Levantamos el cl\u00faster de Kubernetes localmente:<\/p>\n<p><\/p>\n<pre><code class=\"bash\">$ minikube start --vm-driver=docker\nminikube v1.11.0 en Darwin 10.15.5\nUsando el driver docker basado en el perfil existente.\nIniciando el nodo del plano de control minikube en el cl\u00faster minikube.<\/code><\/pre>\n<p><\/p>\n<p>Especificamos <code>kubectl<\/code> usar el cl\u00faster local por defecto:<\/p>\n<p><\/p>\n<pre><code class=\"bash\">$ kubectl config use-context minikube\nSwitched to context \"minikube\".<\/code><\/pre>\n<p><\/p>\n<p>Descargamos configuraciones para el entorno de producci\u00f3n:<\/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>Dado que las configuraciones de producci\u00f3n est\u00e1n configuradas para verificar los antiguos namespaces, y en nuestro nuevo cl\u00faster no hay ninguno, reemplazaremos la variable de entorno <code>IS_DEBUG<\/code> en <code>true<\/code>. Con este valor, el par\u00e1metro <code>afterDaysWithoutDeploy<\/code> no se considera y los namespaces no se verifican por d\u00edas sin despliegue, solo por la coincidencia de subcadenas (<code>-pr-<\/code>).<\/p>\n<p><\/p>\n<p>Si est\u00e1s en <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 est\u00e1s en <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>Instalamos el proyecto:<\/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>Verificamos que el recurso ha aparecido en el cl\u00faster <code>StaleFeatureBranch<\/code>:<\/p>\n<p><\/p>\n<pre><code class=\"bash\">$ kubectl api-resources | grep stalefeaturebranches\nNOMBRE                 ... GRUPO DE APIS                             ... TIPO\nstalefeaturebranches ... feature-branch.dmytrostriletskyi.com ... StaleFeatureBranch<\/code><\/pre>\n<p><\/p>\n<p>Verificamos que el operador ha aparecido en el cl\u00faster:<\/p>\n<p><\/p>\n<pre><code class=\"bash\">$ kubectl get pods --namespace stale-feature-branch-operator\nNOMBRE                                           ... ESTADO  ... EDAD\nstale-feature-branch-operator-6bfbfd4df8-m7sch ... Ejecut\u00e1ndose ... 38s<\/code><\/pre>\n<p><\/p>\n<p>Si miramos sus registros, est\u00e1 listo para procesar recursos <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\":\"Versi\u00f3n del operador: 0.0.1\"}\n...\n... \"msg\":\"Iniciando EventSource\", ... , \"source\":\"tipo de fuente: \/, Tipo=\"}\n... \"msg\":\"Iniciando Controlador\", ...}\n... \"msg\":\"Iniciando trabajadores\", ..., \"worker count\":1}<\/code><\/pre>\n<p><\/p>\n<p>Instalamos los <code>fixtures<\/code> (configuraciones listas para modelar los recursos del cl\u00faster) para el recurso <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>Las configuraciones indican buscar namespaces que contengan la subcadena <code>-pr-<\/code> cada <code>1 minuto<\/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>El operador ha respondido y est\u00e1 listo para verificar los 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 rama de funci\u00f3n obsoleta est\u00e1 siendo procesada.\",\"namespaceSubstring\":\"-pr-\",\"afterDaysWithoutDeploy\":1,\"checkEveryMinutes\":1,\"isDebug\":\"true\"}<\/code><\/pre>\n<p><\/p>\n<p>Instalamos <code>fixtures<\/code>, que contienen dos namespaces (<code>project-pr-1<\/code>, <code>project-pr-2<\/code>) y sus <code>despliegues<\/code>, <code>servicios<\/code>, <code>ingress<\/code>, y as\u00ed sucesivamente:<\/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 created\ndeployment.apps\/project-pr-1 created\nservice\/project-pr-1 created\nhorizontalpodautoscaler.autoscaling\/project-pr-1 created\nsecret\/project-pr-1 created\nconfigmap\/project-pr-1 created\ningress.extensions\/project-pr-1 created\nnamespace\/project-pr-2 created\ndeployment.apps\/project-pr-2 created\nservice\/project-pr-2 created\nhorizontalpodautoscaler.autoscaling\/project-pr-2 created\nsecret\/project-pr-2 created\nconfigmap\/project-pr-2 created\ningress.extensions\/project-pr-2 created<\/code><\/pre>\n<p><\/p>\n<p>Verificamos que todos los recursos anteriores se han creado con \u00e9xito:<\/p>\n<p><\/p>\n<pre><code class=\"bash\">$ kubectl get namespace,pods,deployment,service,horizontalpodautoscaler,configmap,ingress -n project-pr-1 &amp;&amp; kubectl get namespace,pods,deployment,service,horizontalpodautoscaler,configmap,ingress -n project-pr-2\n...\nNAME                              ... READY ... STATUS  ... AGE\npod\/project-pr-1-848d5fdff6-rpmzw ... 1\/1   ... Running ... 67s\n\nNAME                         ... READY ... AVAILABLE ... AGE\ndeployment.apps\/project-pr-1 ... 1\/1   ... 1         ... 67s\n...<\/code><\/pre>\n<p><\/p>\n<p>Dado que hemos habilitado <code>debug<\/code>, namespaces <code>project-pr-1<\/code> y <code>project-pr-2<\/code>, por lo tanto, todos los dem\u00e1s recursos deben eliminarse de inmediato sin tener en cuenta el par\u00e1metro <code>afterDaysWithoutDeploy<\/code>. En los logs del operador esto se puede ver:<\/p>\n<p><\/p>\n<pre><code class=\"bash\">$ kubectl logs stale-feature-branch-operator-6bfbfd4df8-m7sch -n stale-feature-branch-operator\n... \"msg\":\"El namespace debe ser eliminado porque el modo de depuraci\u00f3n est\u00e1 habilitado.\",\"namespaceName\":\"project-pr-1\"}\n... \"msg\":\"El namespace est\u00e1 siendo procesado.\",\"namespaceName\":\"project-pr-1\",\"namespaceCreationTimestamp\":\"2020-06-16 18:43:58 +0300 EEST\"}\n... \"msg\":\"El namespace ha sido eliminado.\",\"namespaceName\":\"project-pr-1\"}\n... \"msg\":\"El namespace debe ser eliminado porque el modo de depuraci\u00f3n est\u00e1 habilitado.\",\"namespaceName\":\"project-pr-2\"}\n... \"msg\":\"El namespace est\u00e1 siendo procesado.\",\"namespaceName\":\"project-pr-2\",\"namespaceCreationTimestamp\":\"2020-06-16 18:43:58 +0300 EEST\"}\n... \"msg\":\"El namespace ha sido eliminado.\",\"namespaceName\":\"project-pr-2\"}<\/code><\/pre>\n<p><\/p>\n<p>Si se verifica la existencia de recursos, estar\u00e1n en estado <code>Terminating<\/code> (proceso de eliminaci\u00f3n) o ya eliminados (la salida del comando est\u00e1 vac\u00eda).<\/p>\n<p><\/p>\n<pre><code class=\"bash\">$ kubectl get namespace,pods,deployment,service,horizontalpodautoscaler,configmap,ingress -n project-pr-1 &amp;&amp; kubectl get namespace,pods,deployment,service,horizontalpodautoscaler,configmap,ingress -n project-pr-2\n...<\/code><\/pre>\n<p><\/p>\n<p>Puedes repetir el proceso de creaci\u00f3n <code>fixtures<\/code> varias veces y asegurarte de que ser\u00e1n eliminados en un minuto.<\/p>\n<p><\/p>\n<h2>Alternativas<\/h2>\n<p><\/p>\n<p>\u00bfQu\u00e9 se puede hacer en lugar de un operador que trabaja en un cl\u00faster? Hay varios enfoques, todos ellos son imperfectos (y sus desventajas son subjetivas), y cada uno decide qu\u00e9 es lo que mejor se adapta a su proyecto espec\u00edfico:<\/p>\n<p><\/p>\n<ol>\n<li>\n<p>Eliminar la rama de caracter\u00edsticas durante la integraci\u00f3n continua de la rama master.<\/p>\n<p><\/p>\n<ul>\n<li>Para esto es necesario saber a qu\u00e9 pull request se relaciona el commit que se est\u00e1 construyendo. Dado que el namespace de la rama de funci\u00f3n contiene el identificador del pull request: su n\u00famero o el nombre de la rama, siempre tendr\u00e1s que especificar el identificador en el commit.<\/li>\n<li>Las construcciones de ramas master est\u00e1n fallando. Por ejemplo, si tienes los siguientes pasos: descargar el proyecto, ejecutar pruebas, compilar el proyecto, hacer una liberaci\u00f3n, enviar notificaciones, limpiar la rama de funci\u00f3n del \u00faltimo pull request. Si la construcci\u00f3n falla al enviar notificaciones, tendr\u00e1s que eliminar todos los recursos en el cl\u00faster manualmente.<\/li>\n<li>Sin el contexto adecuado, eliminar la rama de funci\u00f3n en la construcci\u00f3n master no es obvio.<\/li>\n<\/ul>\n<p>\n<\/li>\n<li>\n<p>El uso de webhooks (<noindex><a rel=\"nofollow\" href=\"https:\/\/github.community\/t\/trigger-jenkins-job-when-a-pull-request-is-merged-to-a-branch\/1169\">ejemplo<\/a><\/noindex>).<\/p>\n<p><\/p>\n<ul>\n<li>Puede que este no sea tu enfoque. Por ejemplo, en <noindex><a rel=\"nofollow\" href=\"https:\/\/www.jenkins.io\">Jenkins<\/a><\/noindex>, solo un tipo de pipeline admite la posibilidad de guardar sus configuraciones en el c\u00f3digo fuente. Al usar webhooks, es necesario escribir tu propio script para su procesamiento. Este script deber\u00e1 ser colocado en la interfaz de Jenkins, lo cual es dif\u00edcil de mantener.<\/li>\n<\/ul>\n<p>\n<\/li>\n<li>\n<p>Escribir <noindex><a rel=\"nofollow\" href=\"https:\/\/kubernetes.io\/docs\/concepts\/workloads\/controllers\/cron-jobs\/\">Cronjob<\/a><\/noindex> y a\u00f1adir el cl\u00faster de Kubernetes.<\/p>\n<p><\/p>\n<ul>\n<li>El tiempo dedicado a escribir y mantener.<\/li>\n<li>El operador ya funciona en un estilo similar, est\u00e1 documentado y es mantenido.<\/li>\n<\/ul>\n<p>\n<\/li>\n<\/ol>\n<p><\/p>\n<p>Gracias por tu atenci\u00f3n al art\u00edculo. <strong><noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/dmytrostriletskyi\/stale-feature-branch-operator\">Enlace al proyecto en Github<\/a><\/noindex><\/strong>.<\/p>\n<p>Fuente: <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\/es\/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=\"es_ES\" \/>\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\/es\/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 Eliminamos la rama de caracter\u00edsticas obsoleta en el cl\u00faster de Kubernetes | ProHoster","description":"\u00a1Hola!","canonical_url":"https:\/\/prohoster.info\/es\/blog\/administrirovanie\/udalyaem-ustarevshuyu-feature-branch-v-kubernetes-klastere","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"es_ES","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\/es\/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\/es\/wp-json\/wp\/v2\/posts\/86902","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/comments?post=86902"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/posts\/86902\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/media\/86903"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/media?parent=86902"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/categories?post=86902"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/tags?post=86902"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}