Fusi贸n de 3 v铆as en werf: despliegue en Kubernetes con Helm "con esteroides"

Algo sucedi贸 que nosotros (y no solo nosotros) hemos estado esperando: werf , nuestra utilidad de c贸digo abierto para crear aplicaciones y entregarlas a Kubernetes, 隆ahora admite la aplicaci贸n de cambios utilizando parches de combinaci贸n de 3 v铆as! Adem谩s de esto, se hizo posible adoptar los recursos K8 existentes en las versiones de Helm sin volver a crear estos recursos.



Si es muy corto, configure WERF_THREE_WAY_MERGE=enabled : obtenemos la implementaci贸n "como en kubectl apply ", compatible con las instalaciones existentes en Helm 2 e incluso un poco m谩s.

Pero comencemos con la teor铆a: 驴qu茅 son los parches de fusi贸n de 3 v铆as en general, c贸mo llegaron las personas al enfoque con su generaci贸n y por qu茅 son importantes en los procesos de CI / CD con infraestructura basada en Kubernetes? Y despu茅s de eso, veamos qu茅 es la combinaci贸n de 3 v铆as en werf, qu茅 modos se usan por defecto y c贸mo administrarlo.

驴Qu茅 es un parche de fusi贸n de 3 v铆as?


Entonces, comencemos con la tarea de desplegar los recursos descritos en los manifiestos de YAML en Kubernetes.

Para trabajar con recursos, la API de Kubernetes ofrece las siguientes operaciones b谩sicas: crear, parchar, reemplazar y eliminar. Se supone que con su ayuda es necesario construir un despliegue continuo conveniente de recursos para el cl煤ster. Como?

Equipos de kubectl imperativo


El primer enfoque para administrar objetos en Kubernetes es usar los imperativos comandos kubectl para crear, modificar y eliminar estos objetos. En pocas palabras:

  • kubectl run comando kubectl run puede ejecutar Deployment o Job:

     kubectl run --generator=deployment/apps.v1 DEPLOYMENT_NAME --image=IMAGE 
  • kubectl scale : cambie el n煤mero de r茅plicas:

     kubectl scale --replicas=3 deployment/mysql 
  • etc.

Tal enfoque puede parecer conveniente a primera vista. Sin embargo, hay problemas:

  1. Es dif铆cil de automatizar .
  2. 驴C贸mo reflejar la configuraci贸n en Git? 驴C贸mo revisar los cambios que ocurren en un cl煤ster?
  3. 驴C贸mo asegurar la reproducibilidad de la configuraci贸n al reiniciar?
  4. ...

Est谩 claro que este enfoque no encaja bien con el almacenamiento de la aplicaci贸n y la infraestructura como c贸digo (IaC; o incluso GitOps como una opci贸n m谩s moderna, ganando popularidad en el ecosistema de Kubernetes) con el c贸digo. Por lo tanto, estos equipos no recibieron m谩s desarrollo en kubectl.

Crear, obtener, reemplazar y eliminar operaciones


Con la creaci贸n primaria , todo es simple: enviamos el manifiesto a la operaci贸n de create de kube api y se crea el recurso. La representaci贸n YAML del manifiesto se puede almacenar en Git, y para crear, use el kubectl create -f manifest.yaml .

La eliminaci贸n tambi茅n es simple: sustituimos el mismo manifest.yaml de Git en el kubectl delete -f manifest.yaml .

La operaci贸n de replace permite reemplazar completamente la configuraci贸n del recurso con una nueva, sin volver a crear el recurso. Esto significa que antes de realizar un cambio en un recurso, es l贸gico solicitar la versi贸n actual con la operaci贸n de get , cambiarla y actualizar con la operaci贸n de replace . El bloqueo optimista est谩 integrado en el kiser apiserver, y si el objeto ha cambiado despu茅s de la operaci贸n de get , la operaci贸n de replace fallar谩.

Para almacenar la configuraci贸n en Git y actualizar usando replace, debe realizar una operaci贸n get , mantener la configuraci贸n de Git con lo que obtuvimos y realizar replace . Normalmente, kubectl solo le permite usar el kubectl replace -f manifest.yaml , donde manifest.yaml es el manifest.yaml totalmente preparado (en nuestro caso, adjunto) que necesita ser instalado. Resulta que el usuario necesita implementar manifiestos de fusi贸n, pero este no es un asunto trivial ...

Tambi茅n vale la pena se帽alar que, aunque manifest.yaml est谩 almacenado en Git, no podemos saber de antemano si necesitamos crear un objeto o actualizarlo; esto deber铆a hacerlo el software del usuario.

En pocas palabras: 驴podemos construir un despliegue continuo solo con crear, reemplazar y eliminar, asegurando que la configuraci贸n de la infraestructura se almacene en Git junto con el c贸digo y un conveniente CI / CD?

B谩sicamente, podemos ... Para hacer esto, necesitamos implementar la operaci贸n de fusi贸n de los manifiestos y alg煤n tipo de enlace que:

  • comprueba la presencia de un objeto en el cl煤ster,
  • realiza la creaci贸n inicial del recurso,
  • lo actualiza o lo elimina.

Al actualizar, debe tener en cuenta que el recurso puede haber cambiado desde la 煤ltima vez y manejar autom谩ticamente el caso de bloqueo optimista: realice intentos repetidos para actualizar.

Sin embargo, 驴por qu茅 reinventar la rueda cuando kube-apiserver ofrece otra forma de actualizar recursos: la operaci贸n de patch , que elimina algunos de los problemas descritos por el usuario?

Parche


Entonces llegamos a los parches.

Los parches son la forma principal de aplicar cambios a los objetos existentes en Kubernetes. La operaci贸n de patch funciona para que:

  • El usuario de kube-apiserver necesita enviar el parche en formato JSON y especificar el objeto,
  • y el mismo servidor se ocupar谩 del estado actual del objeto y lo llevar谩 a la forma deseada.

El bloqueo optimista en este caso no es necesario. Esta operaci贸n es m谩s declarativa en comparaci贸n con reemplazar, aunque al principio puede parecer al rev茅s.

De esta manera:

  • usando la operaci贸n de create , creamos un objeto desde el manifiesto de Git,
  • usando delete - delete si el objeto ya no es necesario,
  • usando patch : modificamos el objeto, llev谩ndolo al formulario descrito en Git.

Sin embargo, para hacer esto, debe crear el parche correcto .

C贸mo funcionan los parches en Helm 2: combinaci贸n de 2 v铆as


La primera vez que se instala una versi贸n, Helm realiza una operaci贸n de create en los recursos del gr谩fico.

Al actualizar la versi贸n de Helm para cada recurso:

  • cuenta el parche entre la versi贸n del recurso del gr谩fico anterior y la versi贸n actual del gr谩fico,
  • aplica este parche

Llamaremos a ese parche parche de fusi贸n de 2 v铆as , porque 2 manifiestos participan en su creaci贸n:

  • Manifiesto de recursos de la versi贸n anterior,
  • El manifiesto del recurso del recurso actual.

Al eliminar, se llama a la operaci贸n de delete en kube apiserver para los recursos que se declararon en la versi贸n anterior pero que no se declararon en la versi贸n actual.

El enfoque con el parche de fusi贸n de 2 v铆as tiene un problema: conduce a una desincronizaci贸n del estado real del recurso en el cl煤ster y el manifiesto en Git .

Un ejemplo de un problema.


  • En Git, un manifiesto se almacena en el gr谩fico en el que el campo image implementaci贸n tiene el valor de ubuntu:18.04 .
  • El usuario a trav茅s de kubectl edit cambi贸 el valor de este campo a ubuntu:19.04 .
  • Cuando vuelve a implementar el gr谩fico, Helm no genera un parche , porque el campo de image en la versi贸n anterior de la versi贸n y en el gr谩fico actual son los mismos.
  • Despu茅s del despliegue repetido de la image , ubuntu:19.04 permanece, aunque ubuntu:18.04 est谩 escrito en la tabla.

Obtuvimos desincronizaci贸n y perdimos declaratividad.

驴Qu茅 es un recurso sincronizado?


En t茅rminos generales, es imposible obtener una coincidencia completa entre un manifiesto de recursos en un cl煤ster en ejecuci贸n y un manifiesto de Git. Debido a que en el manifiesto real puede haber anotaciones / etiquetas de servicio, contenedores adicionales y otros datos agregados y eliminados din谩micamente por algunos controladores del recurso. No podemos y no queremos mantener estos datos en Git. Sin embargo, queremos que cuando se despliegue, los campos que especificamos expl铆citamente en Git tomen los valores apropiados.

Resulta esta regla general de un recurso sincronizado : cuando despliega un recurso, puede cambiar o eliminar solo aquellos campos que se especifican expl铆citamente en el manifiesto de Git (o se registraron en la versi贸n anterior, pero ahora se eliminan).

Parche de fusi贸n de 3 v铆as


La idea principal del parche de combinaci贸n de 3 v铆as : generamos un parche entre la 煤ltima versi贸n aplicada del manifiesto de Git y la versi贸n de destino del manifiesto de Git, teniendo en cuenta la versi贸n actual del manifiesto del cl煤ster de trabajo. El parche final debe cumplir con la regla de recursos sincronizados:

  • los nuevos campos agregados a la versi贸n de destino se agregan usando el parche;
  • los campos previamente existentes en la 煤ltima versi贸n aplicada y no existentes en el campo de destino se restablecen usando el parche;
  • Los campos en la versi贸n actual del objeto que difieren de la versi贸n de destino del manifiesto se actualizan utilizando el parche.

Es por este principio que se generan parches de kubectl apply :

  • la 煤ltima versi贸n aplicada del manifiesto se almacena en la anotaci贸n del objeto mismo,
  • target: tomado del archivo YAML especificado,
  • actual: de un cl煤ster de trabajo.

Ahora que hemos descubierto la teor铆a, es hora de contarte lo que hicimos en werf.

Aplicar cambios a werf


Anteriormente, werf, como Helm 2, usaba parches de fusi贸n de 2 v铆as.

Parche de reparaci贸n


Para cambiar a un nuevo tipo de parches, fusi贸n de 3 v铆as, el primer paso fue la introducci贸n de los llamados parches de reparaci贸n .

Cuando se implementa, se usa el parche est谩ndar de combinaci贸n de 2 v铆as, pero werf adicionalmente genera un parche que sincroniza el estado real del recurso con lo que est谩 escrito en Git (dicho parche se crea usando la misma regla de recursos sincronizados descrita anteriormente).

En el caso de un rassynchron, al final de la implementaci贸n, el usuario recibe una ADVERTENCIA con el mensaje y el parche apropiados, que deben aplicarse para llevar el recurso a una forma sincronizada. Adem谩s, este parche se graba en una anotaci贸n especial werf.io/repair-patch . Se supone que el usuario mismo aplicar谩 este parche con las manos: werf no lo aplicar谩 en principio.

La generaci贸n de parches de reparaci贸n es una medida temporal que le permite probar realmente la creaci贸n de parches seg煤n el principio de la fusi贸n de 3 v铆as, pero no aplique estos parches autom谩ticamente. Por el momento, este modo de operaci贸n est谩 habilitado por defecto.

Parche de combinaci贸n de 3 v铆as solo para nuevos lanzamientos


A partir del 1 de diciembre de 2019, las versiones beta y alfa de werf comienzan por defecto a usar parches completos de combinaci贸n de 3 v铆as para aplicar cambios solo para las nuevas versiones de Helm implementadas a trav茅s de werf. Las versiones existentes continuar谩n utilizando el enfoque de parche de reparaci贸n de combinaci贸n de 2 v铆as.

Puede habilitar este modo de operaci贸n expl铆citamente estableciendo WERF_THREE_WAY_MERGE_MODE=onlyNewReleases ahora.

Nota : la funci贸n apareci贸 en werf durante varios lanzamientos: en el canal alfa se prepar贸 a partir de la versi贸n v1.0.5-alpha.19 , y en el canal beta con v1.0.4-beta.20 .

Parche de combinaci贸n de 3 v铆as para todos los lanzamientos


A partir del 15 de diciembre de 2019, las versiones beta y alfa de werf comienzan a usar parches completos de combinaci贸n de 3 v铆as de forma predeterminada para aplicar cambios en todas las versiones.

Este modo de operaci贸n se puede WERF_THREE_WAY_MERGE_MODE=enabled expl铆citamente estableciendo WERF_THREE_WAY_MERGE_MODE=enabled ahora.

驴Qu茅 hacer con los recursos de escalado autom谩tico?


Kubernetes tiene 2 tipos de autoescalado: HPA (horizontal) y VPA (vertical).

Horizontal selecciona autom谩ticamente el n煤mero de r茅plicas, vertical: el n煤mero de recursos. Tanto el n煤mero de r茅plicas como los requisitos de recursos se especifican en el manifiesto de recursos (consulte spec.replicas o spec.containers[].resources.limits.cpu , spec.containers[].resources.limits.memory y otros ).

Problema: si un usuario configura un recurso en el gr谩fico para que muestre valores espec铆ficos para recursos o r茅plicas y los autoescaladores est茅n habilitados para este recurso, entonces con cada despliegue werf restablecer谩 estos valores a lo que est谩 escrito en el manifiesto del gr谩fico.

Hay dos soluciones al problema. Para empezar, es mejor descartar especificando expl铆citamente los valores de autoescala en el manifiesto del gr谩fico. Si por alguna raz贸n esta opci贸n no encaja (por ejemplo, porque es conveniente establecer los l铆mites de recursos iniciales y el n煤mero de r茅plicas en el gr谩fico), werf ofrece las siguientes anotaciones:

  • werf.io/set-replicas-only-on-creation=true
  • werf.io/set-resources-only-on-creation=true

Si tal anotaci贸n est谩 presente, werf no restablecer谩 los valores correspondientes en cada implementaci贸n, sino que solo los establecer谩 en la creaci贸n inicial del recurso.

Para obtener m谩s informaci贸n, consulte la documentaci贸n del proyecto para HPA y VPA .

Negar el uso del parche de fusi贸n de 3 v铆as


El usuario a煤n puede prohibir el uso de nuevos parches en werf utilizando la variable de entorno WERF_THREE_WAY_MERGE_MODE=disabled . Sin embargo, a partir del 1 de marzo de 2020, esta prohibici贸n dejar谩 de funcionar y solo ser谩 posible usar parches de fusi贸n de 3 v铆as.

Adopci贸n de recursos en werf


El dominio del m茅todo de aplicaci贸n de cambios en parches de fusi贸n de 3 v铆as nos permiti贸 implementar de inmediato una caracter铆stica como la adopci贸n de recursos existentes en el cl煤ster en la versi贸n Helm.

Helm 2 tiene un problema: no puede agregar un recurso a un manifiesto de gr谩fico que ya existe en el cl煤ster sin volver a crear este recurso desde cero (consulte # 6031 , # 3275 ). Ense帽amos a werf a aceptar los recursos existentes en un comunicado. Para hacer esto, debe establecer una anotaci贸n en la versi贸n actual del recurso desde un cl煤ster en ejecuci贸n (por ejemplo, usando kubectl edit ):

 "werf.io/allow-adoption-by-release": RELEASE_NAME 

Ahora, el recurso debe describirse en el gr谩fico y, en el pr贸ximo despliegue, mediante el lanzamiento de werf del lanzamiento con el nombre correspondiente, el recurso existente ser谩 aceptado en este lanzamiento y permanecer谩 bajo su control. Adem谩s, en el proceso de aceptar el recurso para su liberaci贸n, werf traer谩 el estado actual del recurso desde el cl煤ster de trabajo al estado descrito en el gr谩fico utilizando los mismos parches de combinaci贸n de 3 v铆as y la regla de recursos sincronizados.

Nota : la configuraci贸n de WERF_THREE_WAY_MERGE_MODE no afecta la adopci贸n de recursos; en el caso de la adopci贸n, siempre se usa un parche de fusi贸n de 3 v铆as.

Los detalles est谩n en la documentaci贸n .

Conclusiones y planes futuros


Espero que despu茅s de este art铆culo se haya aclarado qu茅 son los parches de combinaci贸n de 3 v铆as y por qu茅 vinieron a ellos. Desde un punto de vista pr谩ctico del desarrollo del proyecto werf, su implementaci贸n fue otro paso hacia la mejora de la implementaci贸n similar a Helm. Ahora puede olvidarse de los problemas con la sincronizaci贸n de la configuraci贸n, que a menudo ocurr铆an al usar Helm 2. Al mismo tiempo, se agreg贸 una nueva caracter铆stica 煤til de la adopci贸n de los recursos de Kubernetes ya cargados en la versi贸n de Helm.

Todav铆a hay algunos problemas y dificultades en la implementaci贸n similar a Helm, como el uso de plantillas Go, y continuaremos resolvi茅ndolos.

Tambi茅n se puede encontrar informaci贸n sobre m茅todos de actualizaci贸n de recursos y adopci贸n en esta p谩gina de documentaci贸n .

Tim贸n 3


Una nota especial es digna de la nueva versi贸n principal lanzada recientemente de Helm, v3, que tambi茅n usa parches de combinaci贸n de 3 v铆as y elimina Tiller. La nueva versi贸n de Helm requiere la migraci贸n de las instalaciones existentes para convertirlas en un nuevo formato de almacenamiento de lanzamiento.

Werf, por su parte, ahora ha eliminado el uso de Tiller, cambi贸 a la combinaci贸n de 3 v铆as y agreg贸 mucho m谩s , sin dejar de ser compatible con las instalaciones existentes en Helm 2 (no se necesitan scripts de migraci贸n). Por lo tanto, hasta que werf se cambie a Helm 3, los usuarios de werf no perder谩n las principales ventajas de Helm 3 sobre Helm 2 (tambi茅n existen en werf).

Sin embargo, cambiar werf a la base de c贸digo Helm 3 es inevitable y suceder谩 en un futuro pr贸ximo. Presumiblemente ser谩 werf 1.1 o werf 1.2 (en este momento, la versi贸n principal de werf es 1.0; para m谩s detalles sobre el dispositivo de versiones de werf, consulte aqu铆 ). Durante este tiempo, Helm 3 tendr谩 tiempo para estabilizarse.

PS


Lea tambi茅n en nuestro blog:

Source: https://habr.com/ru/post/476646/


All Articles