{"id":53120,"date":"2019-11-24T00:00:00","date_gmt":"2019-11-23T21:00:00","guid":{"rendered":"https:\/\/prohoster.info\/blog\/blog_prohoster\/3-way-merge-v-werf-deploj-v-kubernetes-s-helm-na-steroidah"},"modified":"2020-02-18T14:00:59","modified_gmt":"2020-02-18T11:00:59","slug":"3-way-merge-v-werf-deploj-v-kubernetes-s-helm-na-steroidah","status":"publish","type":"post","link":"https:\/\/prohoster.info\/es\/blog\/administrirovanie\/3-way-merge-v-werf-deploj-v-kubernetes-s-helm-na-steroidah","title":{"rendered":"Fusi\u00f3n 3 v\u00edas en werf: despliegue en Kubernetes con Helm \"potenciado\"","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Ocurri\u00f3 lo que nosotros (y no solo nosotros) hemos estado esperando durante mucho tiempo: <noindex><a rel=\"nofollow\" href=\"https:\/\/werf.io\/\">werf<\/a><\/noindex>, nuestra herramienta Open Source para construir aplicaciones y entregarlas en Kubernetes, ahora soporta la aplicaci\u00f3n de cambios mediante parches de fusi\u00f3n de 3 v\u00edas. Adem\u00e1s, existe la posibilidad de adoptar recursos K8s existentes en los lanzamientos de Helm sin recrear esos recursos.<\/p>\n<p><img decoding=\"async\" alt=\"Fusi\u00f3n 3 v\u00edas en werf: despliegue en Kubernetes con Helm &quot;potenciado&quot;\" src=\"\/wp-content\/uploads\/2019\/11\/e5be3544c201f7b2c97fba216a49506d.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nEn resumen, establecemos <code>WERF_THREE_WAY_MERGE=enabled<\/code> \u2014 obtenemos un despliegue \u201ccomo en <code>kubectl apply<\/code>\", compatible con instalaciones existentes en Helm 2 y un poco m\u00e1s.<\/p>\n<p>Pero comencemos desde la teor\u00eda: \u00bfqu\u00e9 son exactamente los parches de fusi\u00f3n de 3 v\u00edas, c\u00f3mo se lleg\u00f3 al enfoque de su generaci\u00f3n y por qu\u00e9 son importantes en los procesos CI\/CD con infraestructura basada en Kubernetes? Despu\u00e9s de eso, veremos qu\u00e9 es realmente la fusi\u00f3n de 3 v\u00edas en werf, qu\u00e9 modos se utilizan por defecto y c\u00f3mo gestionar esto.<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h2>\u00bfQu\u00e9 es un parche de fusi\u00f3n de 3 v\u00edas?<\/h2>\n<p>\nAs\u00ed que comencemos con la tarea de implementar recursos descritos en manifiestos YAML en Kubernetes.<\/p>\n<p>Para trabajar con los recursos, Kubernetes API ofrece las siguientes operaciones b\u00e1sicas: crear, parchear, reemplazar y eliminar. Se supone que con su ayuda se debe construir un despliegue continuo conveniente de recursos en el cl\u00faster. \u00bfC\u00f3mo?<\/p>\n<h3>Comandos imperativos de kubectl<\/h3>\n<p>\nEl primer enfoque para administrar objetos en Kubernetes es utilizar comandos imperativos de kubectl para crear, modificar y eliminar esos objetos. En t\u00e9rminos simples:<\/p>\n<ul>\n<li> con el comando <code>kubectl run<\/code> puede iniciar un Deployment o Job:\n<pre><code class=\"bash\">kubectl run --generator=deployment\/apps.v1 NOMBRE_DEL_DEPLOYMENT --image=IMAGEN<\/code><\/pre>\n<\/li>\n<li> con el comando <code>kubectl scale<\/code> \u2014 cambiar el n\u00famero de r\u00e9plicas:\n<pre><code class=\"bash\">kubectl scale --replicas=3 deployment\/mysql<\/code><\/pre>\n<\/li>\n<li>etc.<\/li>\n<\/ul>\n<p>\nEste enfoque puede parecer conveniente a primera vista. Sin embargo, hay problemas: <\/p>\n<ol>\n<li> Es dif\u00edcil <b>automatizar<\/b>.<\/li>\n<li> \u00bfC\u00f3mo <b>reflejar la configuraci\u00f3n<\/b> en Git? \u00bfC\u00f3mo hacer la revisi\u00f3n de los cambios que ocurren en el cl\u00faster?<\/li>\n<li> \u00bfC\u00f3mo garantizar <b>la reproducibilidad<\/b> de la configuraci\u00f3n al reiniciar?<\/li>\n<li>\u2026<\/li>\n<\/ol>\n<p>\nEs evidente que este enfoque no se combina bien con el almacenamiento junto con el c\u00f3digo de la aplicaci\u00f3n y la infraestructura como c\u00f3digo (IaC; o incluso <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/458878\/\">GitOps<\/a><\/noindex> como una opci\u00f3n m\u00e1s moderna que est\u00e1 ganando popularidad en el ecosistema de Kubernetes). Por lo tanto, estos comandos en kubectl no recibieron m\u00e1s desarrollo.<\/p>\n<h3>Operaciones crear, obtener, reemplazar y eliminar<\/h3>\n<p>\nCon la creaci\u00f3n inicial <b>todo es simple: enviamos el manifiesto a la operaci\u00f3n<\/b> create <code>en kube api y el recurso se crea. La representaci\u00f3n en YAML del manifiesto se puede almacenar en Git, y para crearla se puede usar el comando<\/code> kubectl create -f manifest.yaml <code>para la eliminaci\u00f3n<\/code>.<\/p>\n<p>Con <b>tambi\u00e9n es simple: utilizamos el mismo<\/b> manifest.yaml <code>manifest.yaml<\/code> de Git a la estructura <code>kubectl delete -f manifest.yaml<\/code>.<\/p>\n<p>Operaci\u00f3n <b><code>replace<\/code><\/b> permite reemplazar completamente la configuraci\u00f3n del recurso por uno nuevo, sin necesidad de recrear el recurso. Esto significa que, antes de realizar un cambio en el recurso, es l\u00f3gico solicitar la versi\u00f3n actual mediante la operaci\u00f3n <code>get<\/code>, modificarla y actualizarla con la operaci\u00f3n <code>replace<\/code>. En kube apiserver se integra <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Optimistic_concurrency_control\">optimistic locking<\/a><\/noindex> y, si despu\u00e9s de la operaci\u00f3n <code>get<\/code> el objeto ha cambiado, la operaci\u00f3n <code>replace<\/code> no se llevar\u00e1 a cabo.<\/p>\n<p>Para almacenar la configuraci\u00f3n en Git y actualizar con replace, es necesario realizar la operaci\u00f3n <code>get<\/code>, fusionar la configuraci\u00f3n desde Git con lo que hemos obtenido y ejecutar <code>replace<\/code>. Por defecto, kubectl solo permite usar el comando <code>kubectl replace -f manifest.yaml<\/code>, donde <code>manifest.yaml<\/code> \u2014ya completado (en nuestro caso, fusionado) el manifiesto, que es necesario implementar. Eso significa que el usuario debe realizar la fusi\u00f3n de manifiestos, lo cual no es trivial\u2026<\/p>\n<p>Tambi\u00e9n cabe destacar que, aunque <code>manifest.yaml<\/code> se almacena en Git, no podemos saber de antemano si debemos crear o actualizar el objeto; eso debe hacerlo el software del usuario.<\/p>\n<p>Total: <b>\u00bfPodemos construir un despliegue continuo<\/b> solo con create, replace y delete, asegurando el almacenamiento de la configuraci\u00f3n de infraestructura en Git junto con el c\u00f3digo y proporcionando un CI\/CD conveniente?<\/p>\n<p>En principio, podemos\u2026 Para ello, <b>ser\u00e1 necesario implementar una operaci\u00f3n de fusi\u00f3n<\/b> de manifiestos y alg\u00fan tipo de envoltura que:<\/p>\n<ul>\n<li> verifique la existencia del objeto en el cl\u00faster,<\/li>\n<li> realice la creaci\u00f3n inicial del recurso,<\/li>\n<li> lo actualice o lo elimine.<\/li>\n<\/ul>\n<p>\nAl actualizar, debemos tener en cuenta que <i>el recurso podr\u00eda haber cambiado<\/i> desde la \u00faltima vez <code>get<\/code> y manejar autom\u00e1ticamente el caso de optimistic locking, realizando intentos de actualizaci\u00f3n.<\/p>\n<p>Sin embargo, \u00bfpor qu\u00e9 reinventar la rueda, cuando kube-apiserver ofrece otra forma de actualizar recursos: la operaci\u00f3n <code>patch<\/code>, que alivia al usuario de parte de los problemas descritos?<\/p>\n<h3>Patch<\/h3>\n<p>\nAqu\u00ed estamos, hablando de parches.<\/p>\n<p>Los parches son la principal forma de aplicar cambios a objetos existentes en Kubernetes. La operaci\u00f3n <code>patch<\/code> funciona de manera que:<\/p>\n<ul>\n<li> al usuario de kube-apiserver se le requiere enviar un parche en formato JSON e indicar el objeto,<\/li>\n<li> y el apiserver se encargar\u00e1 de interpretar el estado actual del objeto y llevarlo a la forma requerida.<\/li>\n<\/ul>\n<p>\nOptimistic locking no es necesario en este caso. Esta operaci\u00f3n es m\u00e1s declarativa en comparaci\u00f3n con replace, aunque inicialmente puede parecer lo contrario.<\/p>\n<p>As\u00ed que:<\/p>\n<ul>\n<li> con la operaci\u00f3n <code>en kube api y el recurso se crea. La representaci\u00f3n en YAML del manifiesto se puede almacenar en Git, y para crearla se puede usar el comando<\/code> creamos un objeto a partir del manifiesto en Git,<\/li>\n<li> usando <code>delete<\/code> \u2014eliminamos si el objeto ya no es necesario,<\/li>\n<li> usando <code>patch<\/code> \u2014 modificamos el objeto llev\u00e1ndolo a la forma descrita en Git.<\/li>\n<\/ul>\n<p>\nSin embargo, para hacer esto, es necesario crear <i>el parche correcto<\/i>!<\/p>\n<h3>C\u00f3mo funcionan los parches en Helm 2: fusi\u00f3n bidireccional<\/h3>\n<p>\nAl instalar por primera vez una versi\u00f3n, Helm realiza la operaci\u00f3n <code>en kube api y el recurso se crea. La representaci\u00f3n en YAML del manifiesto se puede almacenar en Git, y para crearla se puede usar el comando<\/code> para los recursos del chart.<\/p>\n<p>Al actualizar la versi\u00f3n, Helm para cada recurso:<\/p>\n<ul>\n<li> calcula el parche entre la versi\u00f3n del recurso del chart anterior y la versi\u00f3n actual del chart,<\/li>\n<li> aplica este parche.<\/li>\n<\/ul>\n<p>\nA este parche lo llamaremos <b>parche de fusi\u00f3n bidireccional<\/b>, porque en su creaci\u00f3n participan 2 manifiestos:<\/p>\n<ul>\n<li> el manifiesto del recurso de la versi\u00f3n anterior,<\/li>\n<li> el manifiesto del recurso de la versi\u00f3n actual.<\/li>\n<\/ul>\n<p>\nAl eliminar, la operaci\u00f3n <code>delete<\/code> en kube apiserver se llama para los recursos que fueron declarados en la versi\u00f3n anterior, pero no en la actual.<\/p>\n<p>El enfoque con el parche de fusi\u00f3n bidireccional tiene un problema: lleva a <b>una desincronizaci\u00f3n entre el estado real del recurso en el cl\u00faster y el manifiesto en Git<\/b>.<\/p>\n<h3>Ilustraci\u00f3n del problema con el ejemplo<\/h3>\n<p><\/p>\n<ul>\n<li> En Git, en el chart se almacena un manifiesto, en el que el campo <code>image<\/code> en el Deployment tiene el valor <code>ubuntu:18.04<\/code>.<\/li>\n<li> El usuario a trav\u00e9s de <code>kubectl edit<\/code> cambi\u00f3 el valor de este campo a <code>ubuntu:19.04<\/code>.<\/li>\n<li> Al redeplegar el chart de Helm, <i>no se genera un parche<\/i>, porque el campo <code>image<\/code> en la versi\u00f3n anterior de la versi\u00f3n y en el chart actual son iguales.<\/li>\n<li> Despu\u00e9s del redepliegue, <code>image<\/code> permanece <code>ubuntu:19.04<\/code>, aunque en el chart est\u00e1 escrito <code>ubuntu:18.04<\/code>.<\/li>\n<\/ul>\n<p>\nHemos tenido desincronizaci\u00f3n y hemos perdido la declaratividad.<\/p>\n<h3>\u00bfQu\u00e9 es un recurso sincronizado?<\/h3>\n<p>\nEn general, <i>la plena<\/i> coherencia entre el manifiesto del recurso en el cl\u00faster en funcionamiento y el manifiesto en Git es imposible de alcanzar. Porque en el manifiesto real pueden existir anotaciones\/etiquetas administrativas, contenedores adicionales y otros datos que se agregan y eliminan din\u00e1micamente del recurso por ciertos controladores. Estos datos no podemos y no queremos mantener en Git. Sin embargo, queremos que al desplegar, los campos que hemos especificado claramente en Git tomen los valores correspondientes.<\/p>\n<p>Se obtiene una regla general <b>para un recurso sincronizado<\/b>: al desplegar un recurso, solo se pueden modificar o eliminar aquellos campos que est\u00e1n claramente indicados en el manifiesto de Git (o que estaban indicados en una versi\u00f3n anterior y ahora han sido eliminados).<\/p>\n<h3>parche de fusi\u00f3n tridireccional<\/h3>\n<p>\nLa idea principal <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Merge_(version_control)#Three-way_merge\">parche de fusi\u00f3n tridireccional<\/a><\/noindex>: se genera un parche entre la \u00faltima versi\u00f3n aplicada del manifiesto de Git y la versi\u00f3n objetivo del manifiesto de Git teniendo en cuenta la versi\u00f3n actual del manifiesto del cl\u00faster en funcionamiento. El parche final debe cumplir con la regla del recurso sincronizado:<\/p>\n<ul>\n<li> Los nuevos campos a\u00f1adidos a la versi\u00f3n de destino se incluyen mediante un parche;<\/li>\n<li> Los campos que ya exist\u00edan en la \u00faltima versi\u00f3n aplicada y no existen en la de destino se anulan mediante un parche;<\/li>\n<li> Los campos en la versi\u00f3n actual del objeto que difieren de la versi\u00f3n de destino del manifiesto se actualizan mediante un parche.<\/li>\n<\/ul>\n<p>\nAs\u00ed es como se generan los parches <code>kubectl apply<\/code>:<\/p>\n<ul>\n<li> La \u00faltima versi\u00f3n aplicada del manifiesto se guarda en la anotaci\u00f3n del propio objeto, <\/li>\n<li> la de destino se toma del archivo YAML especificado,<\/li>\n<li> la actual se toma del cl\u00faster en funcionamiento.<\/li>\n<\/ul>\n<p>\nAhora que hemos aclarado la teor\u00eda, es hora de contar lo que hemos hecho en werf.<\/p>\n<h2>Aplicaci\u00f3n de cambios en werf<\/h2>\n<p>\nAnteriormente, werf, al igual que Helm 2, utilizaba parches de fusi\u00f3n bidireccional.<\/p>\n<h3>Parche de reparaci\u00f3n<\/h3>\n<p>\nPara pasar al nuevo tipo de parches, el parche de fusi\u00f3n tridireccional, el primer paso que hemos dado es introducir lo que se llama <b>parches de reparaci\u00f3n<\/b>.<\/p>\n<p>Al desplegar se utiliza el parche est\u00e1ndar de fusi\u00f3n bidireccional, pero werf genera adicionalmente un parche que sincroniza el estado real del recurso con lo que est\u00e1 escrito en Git (se crea dicho parche utilizando la misma regla de recurso sincronizado descrita anteriormente).<\/p>\n<p>En caso de que ocurra una desincronizaci\u00f3n, al final del despliegue el usuario recibe una ADVERTENCIA con el mensaje correspondiente y un parche que debe aplicarse para llevar el recurso a un estado sincronizado. Adem\u00e1s, este parche se registra en una anotaci\u00f3n especial <code>werf.io\/repair-patch<\/code>. Se supone que el usuario manualmente <b>mismo<\/b> aplicar\u00e1 este parche: werf no lo aplicar\u00e1 por principio.<\/p>\n<p>La generaci\u00f3n de parches de reparaci\u00f3n es una medida temporal que permite probar la creaci\u00f3n de parches seg\u00fan el principio de fusi\u00f3n tridireccional, pero no aplicar autom\u00e1ticamente estos parches. Actualmente, este modo de operaci\u00f3n est\u00e1 habilitado por defecto.<\/p>\n<h3>Parche de fusi\u00f3n tridireccional solo para nuevas versiones<\/h3>\n<p>\nA partir del 1 de diciembre de 2019, las versiones beta y alfa de werf comenzar\u00e1n <b>por defecto<\/b> a utilizar parches de fusi\u00f3n tridireccional completos para aplicar cambios solo para nuevas versiones de Helm lanzadas a trav\u00e9s de werf. Las versiones ya existentes continuar\u00e1n utilizando el enfoque con parches de fusi\u00f3n bidireccional + parches de reparaci\u00f3n.<\/p>\n<p>Este modo de operaci\u00f3n se puede habilitar expl\u00edcitamente configurando <code>WERF_THREE_WAY_MERGE_MODE=onlyNewReleases<\/code> ya ahora.<\/p>\n<p><i><b>Nota<\/b>: la funcionalidad ha aparecido en werf a lo largo de varias versiones: en el canal alfa se volvi\u00f3 estable a partir de la versi\u00f3n <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/flant\/werf\/releases\/tag\/v1.0.5-alpha.19\">v1.0.5-alpha.19<\/a><\/noindex>, y en el canal beta \u2014 desde <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/flant\/werf\/releases\/tag\/v1.0.4-beta.20\">v1.0.4-beta.20<\/a><\/noindex>.<\/i><\/p>\n<h3>parche de fusi\u00f3n tridireccional para todas las versiones<\/h3>\n<p>\nA partir del 15 de diciembre de 2019, las versiones beta y alpha de werf comenzar\u00e1n a utilizar por defecto parches de fusi\u00f3n de 3 v\u00edas completos para aplicar cambios en todas las versiones.<\/p>\n<p>Este modo de operaci\u00f3n se puede habilitar expl\u00edcitamente configurando <code>WERF_THREE_WAY_MERGE_MODE=enabled<\/code> ya ahora.<\/p>\n<h3>\u00bfC\u00f3mo manejar la escalabilidad autom\u00e1tica de recursos?<\/h3>\n<p>\nEn Kubernetes existen 2 tipos de escalabilidad autom\u00e1tica: HPA (horizontal) y VPA (vertical).<\/p>\n<p>El horizontal selecciona autom\u00e1ticamente la cantidad de r\u00e9plicas, el vertical \u2014 la cantidad de recursos. Tanto el n\u00famero de r\u00e9plicas como los requisitos de recursos se especifican en el manifiesto del recurso (ver <code>spec.replicas<\/code> o <code>spec.containers[].resources.limits.cpu<\/code>, <code>spec.containers[].resources.limits.memory<\/code> y <noindex><a rel=\"nofollow\" href=\"https:\/\/kubernetes.io\/docs\/concepts\/configuration\/manage-compute-resources-container\/#resource-requests-and-limits-of-pod-and-container\">otros<\/a><\/noindex>).<\/p>\n<p>Problema: si el usuario configura un recurso en el chart de tal manera que se especifiquen valores espec\u00edficos para los recursos o r\u00e9plicas y se habiliten los autoscalers para ese recurso, entonces en cada despliegue werf restablecer\u00e1 esos valores a lo que est\u00e1 escrito en el manifiesto del chart.<\/p>\n<p>Hay dos soluciones para el problema. Para empezar, lo mejor es evitar especificar valores escalables autom\u00e1ticamente en el manifiesto del chart. Sin embargo, si esta opci\u00f3n no es viable por alguna raz\u00f3n (por ejemplo, porque en el chart es conveniente establecer limitaciones iniciales de recursos y cantidad de r\u00e9plicas), werf ofrece las siguientes anotaciones:<\/p>\n<ul>\n<li> <code>werf.io\/set-replicas-only-on-creation=true<\/code><\/li>\n<li> <code>werf.io\/set-resources-only-on-creation=true<\/code><\/li>\n<\/ul>\n<p>\nCon esta anotaci\u00f3n, werf no restablecer\u00e1 los valores correspondientes en cada despliegue, sino que solo los establecer\u00e1 en la creaci\u00f3n inicial del recurso.<\/p>\n<p>Para m\u00e1s detalles, consulte la documentaci\u00f3n del proyecto sobre <noindex><a rel=\"nofollow\" href=\"https:\/\/werf.io\/documentation\/reference\/deploy_process\/resources_update_methods_and_adoption.html#deal-with-hpa\">HPA<\/a><\/noindex> y <noindex><a rel=\"nofollow\" href=\"https:\/\/werf.io\/documentation\/reference\/deploy_process\/resources_update_methods_and_adoption.html#deal-with-vpa\">VPA<\/a><\/noindex>.<\/p>\n<h3>Prohibir el uso de parches de fusi\u00f3n de 3 v\u00edas<\/h3>\n<p>\nPor ahora, el usuario puede prohibir el uso de nuevos parches en werf a trav\u00e9s de la variable de entorno <code>WERF_THREE_WAY_MERGE_MODE=disabled<\/code>. Sin embargo, a partir del <b>1 de marzo de 2020, esta prohibici\u00f3n dejar\u00e1 de ser efectiva<\/b> y solo se podr\u00e1n utilizar parches de fusi\u00f3n de 3 v\u00edas.<\/p>\n<h2>Adopci\u00f3n de recursos en werf<\/h2>\n<p>\nDominar el m\u00e9todo de aplicaci\u00f3n de cambios mediante parches de fusi\u00f3n de 3 v\u00edas nos permiti\u00f3 implementar de inmediato una funci\u00f3n como la adopci\u00f3n de recursos existentes en el cl\u00faster en un lanzamiento de Helm.<\/p>\n<p>Helm 2 tiene un problema: no se puede agregar un recurso que ya existe en el cl\u00faster a los manifiestos del chart sin recrear ese recurso desde cero (ver <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/helm\/helm\/issues\/6031#issuecomment-531579500\">#6031<\/a><\/noindex>, <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/helm\/helm\/issues\/3275\">#3275<\/a><\/noindex>). Hemos ense\u00f1ado a werf a aceptar recursos existentes en un lanzamiento. Para esto, se debe establecer en la versi\u00f3n actual del recurso del cl\u00faster en funcionamiento una anotaci\u00f3n (por ejemplo, mediante <code>kubectl edit<\/code>):<\/p>\n<pre><code class=\"plaintext\">\"werf.io\/allow-adoption-by-release\": RELEASE_NAME<\/code><\/pre>\n<p>\nAhora es necesario describir el recurso en el chart y, al hacer el siguiente despliegue del lanzamiento mediante werf con el nombre correspondiente, el recurso existente ser\u00e1 aceptado en este lanzamiento y permanecer\u00e1 bajo su gesti\u00f3n. Adem\u00e1s, en el proceso de aceptaci\u00f3n del recurso en el lanzamiento, werf llevar\u00e1 el estado actual del recurso del cl\u00faster en funcionamiento al estado descrito en el chart, utilizando los mismos parches de fusi\u00f3n 3-way y la regla de recurso sincronizado.<\/p>\n<p><i><b>Nota<\/b>: configuraci\u00f3n <code>WERF_THREE_WAY_MERGE_MODE<\/code> no afecta a la adopci\u00f3n de recursos; en caso de adopci\u00f3n, siempre se utiliza un parche de fusi\u00f3n 3-way.<\/i><\/p>\n<p>Los detalles est\u00e1n en <noindex><a rel=\"nofollow\" href=\"https:\/\/werf.io\/documentation\/reference\/deploy_process\/resources_update_methods_and_adoption.html#resources-adoption\">la documentaci\u00f3n<\/a><\/noindex>.<\/p>\n<h2>Conclusiones y planes futuros<\/h2>\n<p>\nEspero que despu\u00e9s de este art\u00edculo quede m\u00e1s claro qu\u00e9 son los parches de fusi\u00f3n 3-way y por qu\u00e9 se adoptaron. Desde un punto de vista pr\u00e1ctico del desarrollo del proyecto werf, su implementaci\u00f3n ha sido un paso m\u00e1s hacia la mejora del despliegue similar a Helm. Ahora podemos olvidarnos de los problemas de sincronizaci\u00f3n de configuraci\u00f3n que a menudo surg\u00edan al usar Helm 2. Al mismo tiempo, se ha a\u00f1adido una nueva caracter\u00edstica \u00fatil sobre la adopci\u00f3n de recursos de Kubernetes que ya estaban descargados en el lanzamiento de Helm.<\/p>\n<p>En el despliegue similar a Helm siguen existiendo algunos problemas y dificultades, como el uso de plantillas de Go, y continuaremos abord\u00e1ndolos.<\/p>\n<p>La informaci\u00f3n sobre los m\u00e9todos de actualizaci\u00f3n de recursos y adopci\u00f3n tambi\u00e9n se puede encontrar en <noindex><a rel=\"nofollow\" href=\"https:\/\/werf.io\/documentation\/reference\/deploy_process\/resources_update_methods_and_adoption.html\">esta p\u00e1gina de documentaci\u00f3n<\/a><\/noindex>.<\/p>\n<h3>Helm 3<\/h3>\n<p>\nVale la pena mencionar <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/news\/t\/475722\/\">el lanzamiento<\/a><\/noindex> hace apenas unos d\u00edas de la nueva versi\u00f3n mayor de Helm \u2014 v3, \u2014 que tambi\u00e9n utiliza parches de fusi\u00f3n 3-way y elimina a Tiller. La nueva versi\u00f3n de Helm requiere <noindex><a rel=\"nofollow\" href=\"https:\/\/helm.sh\/docs\/topics\/v2_v3_migration\/\">migraci\u00f3n<\/a><\/noindex> de las instalaciones existentes para convertirlas al nuevo formato de almacenamiento de lanzamientos.<\/p>\n<p>Por su parte, werf ya se ha deshecho del uso de Tiller, ha cambiado a 3-way-merge y ha a\u00f1adido <noindex><a rel=\"nofollow\" href=\"https:\/\/werf.io\/documentation\/reference\/deploy_process\/differences_with_helm.html\">muchas otras cosas<\/a><\/noindex>, manteni\u00e9ndose compatible con las instalaciones ya existentes en Helm 2 (no es necesario ejecutar scripts de migraci\u00f3n). Por lo tanto, mientras werf no se haya cambiado a Helm 3, los usuarios de werf no pierden las principales ventajas de Helm 3 sobre Helm 2 (tambi\u00e9n est\u00e1n presentes en werf).<\/p>\n<p>Sin embargo, el cambio de werf a la base de c\u00f3digo de Helm 3 es inevitable y ocurrir\u00e1 en un futuro cercano. Se presume que ser\u00e1 en werf 1.1 o werf 1.2 (en este momento, la versi\u00f3n principal de werf es 1.0; m\u00e1s detalles sobre el esquema de versionado de werf se pueden encontrar en <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/flant\/werf#backward-compatibility-promise\">aqu\u00ed<\/a><\/noindex>). Durante este tiempo, Helm 3 se estabilizar\u00e1.<\/p>\n<h2>P.D.<\/h2>\n<p>\nTambi\u00e9n puedes leer en nuestro blog:<\/p>\n<ul>\n<li> Ciclo de notas sobre novedades en werf:\n<ul>\n<li> \u00ab<noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/468049\/\">Uso de werf para el despliegue de charts complejos de Helm<\/a><\/noindex>\u00bb;<\/li>\n<li> \u00ab<noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/465131\/\">Soporte para monorepos y multirepos en werf y qu\u00e9 tiene que ver Docker Registry<\/a><\/noindex>\u00bb;<\/li>\n<li> \u00ab<noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/463613\/\">Ahora se pueden construir im\u00e1genes de Docker en werf utilizando un Dockerfile est\u00e1ndar<\/a><\/noindex>\u00bb.<\/li>\n<\/ul>\n<\/li>\n<li> \u00ab<noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/460351\/\">werf: nuestra herramienta para CI\/CD en Kubernetes (rese\u00f1a y video de la presentaci\u00f3n)<\/a><\/noindex>\u00bb;<\/li>\n<li> \u00ab<noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/469541\/\">Construcci\u00f3n y despliegue de microservicios similares con werf y GitLab CI<\/a><\/noindex>\u00bb;<\/li>\n<li> \u00ab<noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/453734\/\">Introducci\u00f3n a Helm 3<\/a><\/noindex>\u00bb.<\/li>\n<\/ul>\n<p>Fuente: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/476646\/\">habr.com<\/a><\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u0421\u043b\u0443\u0447\u0438\u043b\u043e\u0441\u044c \u0442\u043e, \u0447\u0435\u0433\u043e \u043c\u044b (\u0438 \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u043c\u044b) \u0434\u043e\u043b\u0433\u043e \u0436\u0434\u0430\u043b\u0438: werf, \u043d\u0430\u0448\u0430 Open Source-\u0443\u0442\u0438\u043b\u0438\u0442\u0430 \u0434\u043b\u044f \u0441\u0431\u043e\u0440\u043a\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 \u0438 \u0438\u0445 \u0434\u043e\u0441\u0442\u0430\u0432\u043a\u0438 \u0432 Kubernetes, \u0442\u0435\u043f\u0435\u0440\u044c \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e 3-way-merge-\u043f\u0430\u0442\u0447\u0435\u0439! \u0412 \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u043a \u044d\u0442\u043e\u043c\u0443, \u043f\u043e\u044f\u0432\u0438\u043b\u0430\u0441\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c adoption\u2019\u0430 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0445 K8s-\u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432 \u0432 Helm-\u0440\u0435\u043b\u0438\u0437\u044b \u0431\u0435\u0437 \u043f\u0435\u0440\u0435\u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u044d\u0442\u0438\u0445 \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432. \u0415\u0441\u043b\u0438 \u0441\u043e\u0432\u0441\u0435\u043c \u043a\u043e\u0440\u043e\u0442\u043a\u043e, \u0442\u043e \u0441\u0442\u0430\u0432\u0438\u043c WERF_THREE_WAY_MERGE=enabled \u2014 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0434\u0435\u043f\u043b\u043e\u0439 \u00ab\u043a\u0430\u043a \u0432 [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":53121,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-53120","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=\"\u0421\u043b\u0443\u0447\u0438\u043b\u043e\u0441\u044c \u0442\u043e, \u0447\u0435\u0433\u043e \u043c\u044b (\u0438 \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u043c\u044b) \u0434\u043e\u043b\u0433\u043e \u0436\u0434\u0430\u043b\u0438: werf, \u043d\u0430\u0448\u0430 Open Source-\u0443\u0442\u0438\u043b\u0438\u0442\u0430 \u0434\u043b\u044f \u0441\u0431\u043e\u0440\u043a\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 \u0438 \u0438\u0445 \u0434\u043e\u0441\u0442\u0430\u0432\u043a\u0438 \u0432 Kubernetes, \u0442\u0435\u043f\u0435\u0440\u044c \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\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\/3-way-merge-v-werf-deploj-v-kubernetes-s-helm-na-steroidah\" \/>\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\udd473-way merge \u0432 werf: \u0434\u0435\u043f\u043b\u043e\u0439 \u0432 Kubernetes \u0441 Helm \u00ab\u043d\u0430 \u0441\u0442\u0435\u0440\u043e\u0438\u0434\u0430\u0445\u00bb | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u0421\u043b\u0443\u0447\u0438\u043b\u043e\u0441\u044c \u0442\u043e, \u0447\u0435\u0433\u043e \u043c\u044b (\u0438 \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u043c\u044b) \u0434\u043e\u043b\u0433\u043e \u0436\u0434\u0430\u043b\u0438: werf, \u043d\u0430\u0448\u0430 Open Source-\u0443\u0442\u0438\u043b\u0438\u0442\u0430 \u0434\u043b\u044f \u0441\u0431\u043e\u0440\u043a\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 \u0438 \u0438\u0445 \u0434\u043e\u0441\u0442\u0430\u0432\u043a\u0438 \u0432 Kubernetes, \u0442\u0435\u043f\u0435\u0440\u044c \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/es\/blog\/administrirovanie\/3-way-merge-v-werf-deploj-v-kubernetes-s-helm-na-steroidah\" \/>\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=\"2019-11-23T21:00:00+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2020-02-18T11:00:59+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 fusi\u00f3n 3-way en werf: despliegue en Kubernetes con Helm \u00abpotenciado\u00bb | ProHoster","description":"Sucedi\u00f3 lo que hemos esperado (y no solo nosotros) durante mucho tiempo: werf, nuestra herramienta de c\u00f3digo abierto para construir aplicaciones y su entrega en Kubernetes, ahora es compatible.","canonical_url":"https:\/\/prohoster.info\/es\/blog\/administrirovanie\/3-way-merge-v-werf-deploj-v-kubernetes-s-helm-na-steroidah","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\udd473-way merge \u0432 werf: \u0434\u0435\u043f\u043b\u043e\u0439 \u0432 Kubernetes \u0441 Helm \u00ab\u043d\u0430 \u0441\u0442\u0435\u0440\u043e\u0438\u0434\u0430\u0445\u00bb | ProHoster","og:description":"\u0421\u043b\u0443\u0447\u0438\u043b\u043e\u0441\u044c \u0442\u043e, \u0447\u0435\u0433\u043e \u043c\u044b (\u0438 \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u043c\u044b) \u0434\u043e\u043b\u0433\u043e \u0436\u0434\u0430\u043b\u0438: werf, \u043d\u0430\u0448\u0430 Open Source-\u0443\u0442\u0438\u043b\u0438\u0442\u0430 \u0434\u043b\u044f \u0441\u0431\u043e\u0440\u043a\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 \u0438 \u0438\u0445 \u0434\u043e\u0441\u0442\u0430\u0432\u043a\u0438 \u0432 Kubernetes, \u0442\u0435\u043f\u0435\u0440\u044c \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442.","og:url":"https:\/\/prohoster.info\/es\/blog\/administrirovanie\/3-way-merge-v-werf-deploj-v-kubernetes-s-helm-na-steroidah","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":"2019-11-23T21:00:00+00:00","article:modified_time":"2020-02-18T11:00:59+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"53120","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":"2026-01-24 06:11:19","breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-02-28 20:31:28","updated":"2026-01-24 06:11:19","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\/53120","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=53120"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/posts\/53120\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/media\/53121"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/media?parent=53120"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/categories?post=53120"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/tags?post=53120"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}