Comparativa correcta entre Kubernetes Apply, Replace y Patch

Para Kubernetes hay varias opciones para actualizar recursos: apply, edit, patch y replace. Hay confusión sobre lo que cada uno de ellos hace y cuándo aplicarlos. Vamos a aclarar.

Comparativa correcta entre Kubernetes Apply, Replace y Patch

Si buscar en Google la frase "kubernetes apply vs replace", se encuentra una respuesta en StackOverflow, que no es correcta. Al buscar "kubernetes apply vs patch" el primer enlace es la documentación sobre kubectl patch, que no incluye una comparación aplicar y patch. En este artículo se examinarán las diferentes opciones, así como el uso correcto de cada una de ellas.

A lo largo del ciclo de vida de un recurso de Kubernetes (servicio, deployment, ingress, etc.), a veces es necesario cambiar, agregar o eliminar algunas propiedades de ese recurso. Por ejemplo, agregar una anotación, aumentar o disminuir el número de réplicas.

Kubernetes CLI

Si ya estás trabajando con clústeres de Kubernetes a través de CLI, ya estás familiarizado con aplicar y edit. El comando aplicar lee la especificación del recurso desde un archivo y realiza un "upsert" en el clúster de Kubernetes, es decir, crea el recurso si no existe, y lo actualiza si ya existe. El comando edit lee el recurso a través de la API, luego escribe la especificación del recurso en un archivo local, que luego se abre en un editor de texto. Después de que edites y guardes el archivo, kubectl enviará los cambios realizados de nuevo a través de la API, que aplicará cuidadosamente estos cambios al recurso.

No todos conocen los comandos patch y replace. El comando patch permite cambiar parte de la especificación del recurso proporcionando solo la parte modificada en la línea de comandos. El comando replace funciona de la misma manera que edit, pero solo se tiene que hacer manualmente: es necesario descargar la versión actual de la especificación del recurso, por ejemplo, usando kubectl get -o yaml, editarla, y luego usar replace para actualizar el recurso según la especificación modificada. El comando replace no funcionará si ha habido algún cambio entre la lectura y la sustitución del recurso.

Kubernetes API

Es probable que estés familiarizado con los métodos CoreV1().Pods().Update(), replaceNamespacedService o patch_namespaced_deployment, si trabajas con clústeres a través de una biblioteca de cliente para la API de Kubernetes usando algún lenguaje de programación. La biblioteca maneja estos métodos mediante solicitudes a través del protocolo HTTP, utilizando los métodos PUT y PATCH. A partir de esto, update y replace utilizan PUT, y patch, por muy banal que parezca, utiliza PATCH.

Cabe mencionar que kubectl también trabaja con clústeres a través de la API. En otras palabras, kubectles un envoltorio sobre la biblioteca cliente para el lenguaje Go, que proporciona en gran medida la posibilidad de presentar subcomandos de manera más compacta y legible además de las capacidades estándar de la API. Por ejemplo, como ya habrás notado, el método aplicar no se mencionó anteriormente en el párrafo anterior. Actualmente (mayo de 2020, nota del traductor) toda la lógica kubectl apply, es decir, la creación de recursos inexistentes y la actualización de los existentes, funciona completamente del lado del código kubectl. Se están realizando esfuerzos para trasladar la lógica aplicar al lado de la API, pero aún está en fase de beta. Lo detallaré más abajo.

Patch por defecto

Es mejor aplicar patch, si deseas actualizar un recurso. Así es como funcionan tanto las bibliotecas cliente sobre la API de Kubernetes como kubectl (no es sorprendente, ya que es un envoltorio de la biblioteca cliente, nota del traductor).

Trabajar estratégicamente

Todos los comandos kubectl aplicar, edit y patch utilizan el método PATCH en las solicitudes HTTP para actualizar un recurso existente. Si analizamos más a fondo la implementación de los comandos, todos utilizan el enfoque strategic-merge patching para la actualización de recursos, aunque el comando patch puede utilizar otros enfoques (más detalles sobre esto a continuación). El enfoque strategic-merge patching intenta "hacerlo bien" al combinar la especificación proporcionada con la especificación existente. Más concretamente, intenta combinar tanto objetos como arreglos, lo que significa que los cambios suelen ser aditivos. Por ejemplo, ejecutar el comando patch con una nueva variable de entorno en la especificación del contenedor pod, resulta en que esta variable de entorno se añade a las variables de entorno existentes, en lugar de sobrescribirlas. Para eliminar utilizando este enfoque, debes establecer forzosamente el valor del parámetro en null en la especificación proporcionada. ¿Cuáles de los comandos kubectl para actualizar es mejor utilizar?

Si estás creando y gestionando tus recursos utilizando kubectl apply, al actualizar siempre es mejor usar kubectl apply, para que kubectl ha podido gestionar la configuración y realizar un seguimiento correcto de los cambios solicitados de una aplicación a otra. La ventaja de usar siempre aplicar es que rastrea la especificación previamente aplicada, permitiéndole saber cuándo las propiedades de la especificación y los elementos del arreglo se eliminan explícitamente. Esto permite utilizar aplicar para eliminar propiedades y elementos de una matriz, mientras que la fusión estratégica normal no funcionará. Comandos edit y patch no actualizan las anotaciones que kubectl apply aplica para rastrear sus cambios, por lo que cualquier cambio que se rastree y se realice a través de la API de Kubernetes, pero que se haga mediante comandos edit y patch, no es visible para comandos posteriores aplicar, es decir, el aplicar no las elimina, incluso si no aparecen en la especificación de entrada para aplicar (La documentación dice que edit y patch realizan actualizaciones a las anotaciones utilizadas aplicar, pero en la práctica, no).

Si no utilizas el comando aplicar, se puede usar como edit, como patch, eligiendo el comando que mejor se adapte a la modificación que se realiza. Al agregar y modificar propiedades de la especificación, ambos enfoques son aproximadamente equivalentes. Al eliminar propiedades de la especificación o elementos de la matriz edit se comporta como una ejecución única aplicar, incluyendo el seguimiento de cuál era la especificación antes y después de su edición, por lo que se pueden eliminar explícitamente propiedades y elementos de la matriz del recurso. Es necesario establecer explícitamente el valor de la propiedad en null en la especificación para patch, para eliminarlo del recurso. Eliminar un elemento de la matriz utilizando una fusión estratégica es más complicado, ya que requiere el uso de directivas de fusión. Consulta otros enfoques de actualización a continuación para elegir alternativas más aceptables.

Para implementar métodos de actualización en la biblioteca del cliente que se comporten de manera similar a los comandos anteriores kubectl, se deben especificar en las solicitudes content-type en application/strategic-merge-patch+json. Si deseas eliminar propiedades en la especificación, debes establecer explícitamente sus valores en null, de manera similar a kubectl patch. Si necesitas eliminar elementos de la matriz, debes incluir directivas de fusión en la especificación de actualización o usar otro enfoque para las actualizaciones.

Otros enfoques para actualizaciones

En Kubernetes se admiten dos enfoques adicionales para actualizaciones: JSON merge patch y JSON patch. El enfoque de JSON merge patch acepta una especificación parcial de Kubernetes como entrada y admite la fusión de objetos de manera similar al enfoque de strategic-merge patching. La diferencia entre ellos es que solo admite la sustitución de matrices, incluida la matriz de contenedores en la especificación del pod. Esto significa que al usar JSON merge patch, debe proporcionar especificaciones completas para todos los contenedores en caso de cambiar alguna propiedad de cualquier contenedor. Por lo tanto, este enfoque es útil para eliminar elementos de una matriz en la especificación. En la línea de comandos, puede seleccionar JSON merge patch utilizando kubectl patch --type=merge. Al trabajar con la API de Kubernetes, debe utilizar el método de solicitud PATCH y la instalación content-type en application/merge-patch+json.

El enfoque de JSON patch, en lugar de proporcionar una especificación parcial del recurso, utiliza proporcionar los cambios que desea hacer en el recurso en forma de una matriz, donde cada elemento de la matriz representa una descripción del cambio que se está realizando en el recurso. Este enfoque es una forma más flexible y poderosa de expresar los cambios que se realizan, pero a costa de que la lista de cambios se presenta en un formato separado que no es Kubernetes, en lugar de enviar una especificación parcial del recurso. En kubectl puede elegir JSON patch utilizando kubectl patch --type=json. Al usar la API de Kubernetes, este enfoque funciona utilizando el método de solicitud PATCH y la instalación content-type en application/json-patch+json.

Se necesita certeza: utilizamos replace

En algunos casos, se necesita certeza de que no se realizarán cambios en el recurso entre el tiempo de lectura del recurso y su actualización. En otras palabras, es necesario asegurarse de que todos los cambios sean atómicos. En este caso, para actualizar recursos, se debe utilizar replace. Por ejemplo, si hay un ConfigMap con un contador que se actualiza desde varias fuentes, se debe tener la certeza de que dos fuentes no actualizarán el contador al mismo tiempo, lo que conduciría a una pérdida de actualización. Para demostrar, imagine una secuencia de eventos utilizando el enfoque patch:

  • A y B obtienen el estado actual del recurso desde la API
  • Cada uno de ellos actualiza localmente la especificación, aumentando el contador en uno, y también añadiendo "A" o "B", respectivamente, en la nota "updated-by"
  • A actualiza el recurso un poco más rápido
  • B actualiza el recurso

Como resultado, la actualización A se perdió. Última operación patch gana, el contador se incrementa en uno en lugar de dos, y el valor de la nota "updated-by" termina en "B" y no contiene "A". Comparémoslo con lo que ocurre cuando se realizan actualizaciones utilizando el enfoque replace:

  • A y B obtienen el estado actual del recurso desde la API
  • Cada uno de ellos actualiza localmente la especificación, aumentando el contador en uno, y también añadiendo "A" o "B", respectivamente, en la nota "updated-by"
  • A actualiza el recurso un poco más rápido
  • B intenta actualizar el recurso, pero la actualización es rechazada por el API porque la versión del recurso en la especificación replace no coincide con la versión actual del recurso en Kubernetes, ya que la versión del recurso se incrementó durante la operación de reemplazo por parte de A.

En el caso anterior, B tendrá que volver a extraer el recurso, realizar cambios en el nuevo estado y volver a intentarlo replace. Como resultado, el contador se incrementará en dos, y la nota "updated-by" contendrá "AB" al final.

El ejemplo anterior implica que durante la ejecución replace se realiza un reemplazo completo de todo el recurso. La especificación utilizada para replace, debe ser completa, no parcial, como en aplicar, incluido el añadido de resourceVersion a los metadatos de la especificación. Si no incluiste resourceVersion o la versión proporcionada no es la actual, el reemplazo será rechazado. Por lo tanto, el mejor enfoque para utilizar replace es leer el recurso, actualizarlo y reemplazarlo inmediatamente. Usando kubectl, esto puede verse así:

$ kubectl get deployment my-deployment -o json 
    | jq '.spec.template.spec.containers[0].env[1].value = "nuevo valor"' 
    | kubectl replace -f -

Cabe destacar que los siguientes dos comandos, ejecutados secuencialmente, se ejecutarán con éxito, ya que deployment.yaml no contiene la propiedad .metadata.resourceVersion

$ kubectl create -f deployment.yaml
$ kubectl replace -f deployment.yaml

Esto parece contradictorio con lo que se mencionó anteriormente, es decir, "agregar resourceVersion a los metadatos de la especificación". ¿Es incorrecto afirmarlo? No, no lo es, ya que si kubectl nota que no has especificado resourceVersion, la leerá del recurso y la añadirá a la especificación que proporcionaste, y solo entonces llevará a cabo replace. Dado que esta es potencialmente peligrosa, si se confía en la atomicidad, la magia funciona completamente del lado kubectl, no se debe confiar en ella cuando se utilizan bibliotecas de cliente que interactúan con el API. En este caso, tendrás que leer la especificación actual del recurso, actualizarla y luego realizar PUT la solicitud.

No se puede hacer patch – se hace replace

A veces es necesario realizar ciertos cambios que no pueden ser procesados a través de la API. En estos casos, se puede forzar la sustitución de un recurso eliminándolo y creándolo de nuevo. Esto se hace usando kubectl replace --force. Ejecutar el comando elimina inmediatamente los recursos y luego los recrea con la especificación proporcionada. No hay un manejador de "reemplazo forzado" en la API, y para hacerlo a través de la API, se deben realizar dos operaciones. Primero, se debe eliminar el recurso, estableciendo su gracePeriodSeconds en cero (0) y propagationPolicy en "Background", y luego recrear este recurso con la especificación deseada.

Atención: este enfoque es potencialmente peligroso y puede llevar a un estado indefinido

Apply del lado del servidor

Como se mencionó anteriormente, los desarrolladores de Kubernetes están trabajando en la implementación de la lógica aplicar de kubectl en la API de Kubernetes. La lógica aplicar está disponible en Kubernetes 1.18 a través de kubectl apply --server-side o a través de la API, utilizando el método PATCH con content-type application/apply-patch+YAML.

Nota: JSON también es un YAML válido, por lo que se puede enviar la especificación en formato JSON, incluso si content-type tendrán application/apply-patch+yaml.

Además de que la lógica kubectl se vuelve disponible para todos a través de la API, aplicar del lado del servidor sigue a los responsables de los campos en la especificación, permitiendo así un acceso múltiple seguro para su edición sin conflictos. En otras palabras, si aplicar del lado del servidor se vuelve más común, aparecerá una interfaz de gestión de recursos segura y universal para diferentes clientes, como kubectl, Pulumi o Terraform, GitOps, así como scripts personalizados que utilizan bibliotecas de cliente.

Resultados

Espero que esta breve revisión de las diferentes formas de actualizar recursos en los clústeres haya sido útil para usted. Es bueno saber que no se debe simplemente aplicar contra reemplazar, ya que se puede actualizar un recurso mediante apply, edit, patch o replace. En principio, cada enfoque tiene su área de aplicación. Para cambios atómicos, es preferible utilizar replace; de lo contrario, es mejor usar strategic-merge patch a través de apply. En última instancia, espero que haya entendido que no se debe confiar en Google o StackOverflow al buscar "kubernetes apply vs replace". Al menos hasta que este artículo reemplace la respuesta actual.

Comparativa correcta entre Kubernetes Apply, Replace y Patch

Fuente: habr.com

Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS 🔥 Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS | ProHoster