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:
Tal enfoque puede parecer conveniente a primera vista. Sin embargo, hay problemas:
- Es dif铆cil de automatizar .
- 驴C贸mo reflejar la configuraci贸n en Git? 驴C贸mo revisar los cambios que ocurren en un cl煤ster?
- 驴C贸mo asegurar la reproducibilidad de la configuraci贸n al reiniciar?
- ...
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: