
La necesidad de
tomar los recursos del grupo Kubernetes puede surgir en condiciones de combate cuando no puedes simplemente recrearlos con las herramientas Helm. Se pueden distinguir dos razones principales:
- Será simple, independientemente de si tiene una nube o un metal desnudo.
- Tras la eliminación, se pueden perder los servicios en las nubes, así como los equilibradores de carga asociados en Kubernetes.
En nuestro caso, se requería la solución para detectar el
ingreso de trabajo
-nginx 's mientras se integra nuestro operador Kubernetes.
Es estrictamente inaceptable para Helm que los recursos que administra no sean creados por él.
"Si los recursos de liberación de su equipo se pueden cambiar manualmente, prepárese para enfrentar los problemas descritos en la sección: [ERROR] Después de la implementación, el estado de los recursos de liberación en el clúster no corresponde al gráfico Helm descrito" . (de nuestro último artículo )
Como se señaló anteriormente, Helm funciona de la siguiente manera:
- Cada instalación (instalación de
helm install
, comandos de helm upgrade
) Helm guarda el manifiesto de lanzamiento generado en el backend de almacenamiento . De manera predeterminada, se usa ConfigMaps: para cada revisión de una versión, ConfigMap se crea en el mismo espacio de nombres en el que se ejecuta Tiller. - Durante las implementaciones repetidas (
helm upgrade
) Helm compara el nuevo manifiesto generado con el antiguo manifiesto de la última revisión de lanzamiento DESPLEGADA de ConfigMap, y aplica la diferencia resultante en Kubernetes.
En base a estas características, hemos llegado a la conclusión de que es suficiente parchear ConfigMap (versión de back-end de almacenamiento) para
recoger , es decir
adoptar los recursos existentes en el clúster.
Tiller se refiere a ConfigMap en el siguiente formato:
%RELEASE_NAME.v%REVISION
. Para obtener
entradas existentes, debe ejecutar el comando
kubectl get cm -l OWNER=TILLER --namespace kube-system
(de forma predeterminada, Tiller está instalado en el espacio de nombres
kube-system
; de lo contrario, debe especificar el que se usa).
$ kubectl get cm -l OWNER=TILLER -n kube-system NAME DATA AGE release_name_1.v618 1 5d release_name_1.v619 1 1d release_name_2.v1 1 2d release_name_2.v2 1 3d
Cada ConfigMap se presenta en este formato:
apiVersion: v1 data: release: H4sIAHEEd1wCA5WQwWrDMAyG734Kwc52mtvwtafdAh27FsURjaljG1kp5O3nNGGjhcJ21M/nT7+stVZvcEozO7LAFAgLnSNOdG4boSkHFCpNIb55R2bBKSjM/ou4+BQt3Fp19XGwcNoINZHggIJWAayaH6leJ/24oTIBewplpQEwZ3Ode+JIdanxqXkw/D4CGClMpoyNG5HlmdAH05rDC6WPRTC6p2Iv4AkjXmjQ/WLh04dArEomt9aVJVfHMcxFiD+6muTEsl+i74OF961FpZEvJN09HEXyHmdOklwK1X7s9my7eYdK7egk8b8/6M+HfwNgE0MSAgIAAA== kind: ConfigMap metadata: creationTimestamp: 2019-02-08T11:12:38Z labels: MODIFIED_AT: "1550488348" NAME: release_name_1 OWNER: TILLER STATUS: DEPLOYED VERSION: "618" name: release_name_1.v618 namespace: kube-system resourceVersion: "298818981" selfLink: /api/v1/namespaces/kube-system/configmaps/release_name_1.v618 uid: 71c3e6f3-2b92-11e9-9b3c-525400a97005
OU4 + BQt3Fp19XGwcNoINZHggIJWAayaH6leJ / 24oTIBewplpQEwZ3Ode + JIdanxqXkw / D4CGClMpoyNG5HlmdAH05rDC6WPRTC6p2Iv4AkjXmjQ / WLh04dArEomt9aVJVfHMcxFiD + 6muTEsl + i74OF961FpZEvJN09HEXyHmdOklwK1X7s9my7eYdK7egk8b8 / 6M + HfwNgE0MSAgIAAA == apiVersion: v1 data: release: H4sIAHEEd1wCA5WQwWrDMAyG734Kwc52mtvwtafdAh27FsURjaljG1kp5O3nNGGjhcJ21M/nT7+stVZvcEozO7LAFAgLnSNOdG4boSkHFCpNIb55R2bBKSjM/ou4+BQt3Fp19XGwcNoINZHggIJWAayaH6leJ/24oTIBewplpQEwZ3Ode+JIdanxqXkw/D4CGClMpoyNG5HlmdAH05rDC6WPRTC6p2Iv4AkjXmjQ/WLh04dArEomt9aVJVfHMcxFiD+6muTEsl+i74OF961FpZEvJN09HEXyHmdOklwK1X7s9my7eYdK7egk8b8/6M+HfwNgE0MSAgIAAA== kind: ConfigMap metadata: creationTimestamp: 2019-02-08T11:12:38Z labels: MODIFIED_AT: "1550488348" NAME: release_name_1 OWNER: TILLER STATUS: DEPLOYED VERSION: "618" name: release_name_1.v618 namespace: kube-system resourceVersion: "298818981" selfLink: /api/v1/namespaces/kube-system/configmaps/release_name_1.v618 uid: 71c3e6f3-2b92-11e9-9b3c-525400a97005
Los manifiestos generados se almacenan en forma binaria (en el ejemplo anterior con la clave
.data.release
), por lo que decidimos crear el lanzamiento utilizando las herramientas estándar de Helm, pero con un
código auxiliar especial, que luego se reemplaza con los manifiestos de los recursos seleccionados.
Implementación
El algoritmo de solución es el siguiente:
- Estamos preparando un archivo
manifest.yaml
con manifiestos de recursos para su adopción (este elemento se analizará con más detalle a continuación). - Creamos un gráfico en el que hay una sola plantilla con un ConfigMap temporal, porque Helm no puede crear una versión sin recursos.
- Creamos un
templates/stub.yaml
con un stub que tendrá la misma longitud que la cantidad de caracteres en manifest.yaml
(durante los experimentos resultó que la cantidad de bytes debe coincidir). Como código auxiliar, se debe seleccionar un conjunto de caracteres reproducible, que permanecerá después de la generación y se almacenará en el backend de almacenamiento. Por simplicidad y claridad, #
usa #
, #
decir:
{{ repeat ${manifest_file_length} "#" }}
- Instale la tabla:
helm install
helm upgrade --install
y helm upgrade --install
. - Reemplazamos el código auxiliar en la versión de back-end de almacenamiento con los
manifest.yaml
recursos de manifest.yaml
que se seleccionaron para su adopción en el primer paso:
stub=$(printf '#%.0s' $(seq 1 ${manifest_file_length})) release_data=$(kubectl get -n ${tiller_namespace} cm/${release_name}.v1 -o json | jq .data.release -r) updated_release_data=$(echo ${release_data} | base64 -d | zcat | sed "s/${stub}/$(sed -z 's/\n/\\n/g' ${manifest_file_path} | sed -z 's/\//\\\//g')/" | gzip -9 | base64 -w0) kubectl patch -n ${tiller_namespace} cm/${release_name}.v1 -p '{"data":{"release":"'${updated_release_data}'"}}'
- Verificamos que Tiller esté disponible y recogimos nuestros cambios.
- Elimine el ConfigMap temporal (del segundo paso).
- Además, el trabajo con el lanzamiento no es diferente del habitual.
Gist con la implementación descrita anteriormente está disponible en:
$ ./script.sh Example: ./script.sh foo bar-prod manifest.yaml Usage: ./script.sh CHART_NAME RELEASE_NAME MANIFEST_FILE_TO_ADOPT [TILLER_NAMESPACE]
Como resultado del script, se crea el lanzamiento de
RELEASE_NAME
. Se asocia con recursos cuyos manifiestos se describen en el archivo
MANIFEST_FILE_TO_ADOPT
. También se
CHART_NAME
gráfico
CHART_NAME
, que se puede utilizar para acompañar aún más los manifiestos y lanzamientos en particular.
Al preparar un manifiesto con recursos, es necesario eliminar los campos de servicio que utilizan Kubernetes (estos son datos de servicio dinámico, por lo tanto, es incorrecto versionarlos en Helm). En un mundo ideal, el entrenamiento se reduce a un comando:
kubectl get RESOURCE -o yaml --export
. Después de todo, la documentación dice:
--export=false: If true, use 'export' for the resources. Exported resources are stripped of cluster-specific information.
... pero, como ha demostrado la práctica, la opción
--export
todavía
--export
, por lo que se requerirá un formato de manifiesto adicional. En el
service/release-name-habr
continuación, debe eliminar los
selfLink
creationTimestamp
y
selfLink
.
versión kubectl $ kubectl version Client Version: version.Info{Major:"1", Minor:"13", GitVersion:"v1.13.3", GitCommit:"721bfa751924da8d1680787490c54b9179b1fed0", GitTreeState:"clean", BuildDate:"2019-02-01T20:08:12Z", GoVersion:"go1.11.5", Compiler:"gc", Platform:"linux/amd64"} Server Version: version.Info{Major:"1", Minor:"13", GitVersion:"v1.13.3", GitCommit:"721bfa751924da8d1680787490c54b9179b1fed0", GitTreeState:"clean", BuildDate:"2019-02-01T20:00:57Z", GoVersion:"go1.11.5", Compiler:"gc", Platform:"linux/amd64"}
kubectl obtener servicio / release-name-habr -o yaml --export apiVersion: v1 kind: Service metadata: annotations: kubectl.kubernetes.io/last-applied-configuration: | {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"labels":{"app.kubernetes.io/instance":"release-name","app.kubernetes.io/managed-by":"Tiller","app.kubernetes.io/name":"habr","helm.sh/chart":"habr-0.1.0"},"name":"release-name-habr","namespace":"default"},"spec":{"ports":[{"name":"http","port":80,"protocol":"TCP","targetPort":"http"}],"selector":{"app.kubernetes.io/instance":"release-name","app.kubernetes.io/name":"habr"},"type":"ClusterIP"}} creationTimestamp: null labels: app.kubernetes.io/instance: release-name app.kubernetes.io/managed-by: Tiller app.kubernetes.io/name: habr helm.sh/chart: habr-0.1.0 name: release-name-habr selfLink: /api/v1/namespaces/default/services/release-name-habr spec: ports: - name: http port: 80 protocol: TCP targetPort: http selector: app.kubernetes.io/instance: release-name app.kubernetes.io/name: habr sessionAffinity: None type: ClusterIP status: loadBalancer: {}
Los siguientes son ejemplos del uso del script. Ambos demuestran cómo usar el script para adoptar recursos que trabajan en el clúster y luego eliminarlos usando las herramientas Helm.
Ejemplo 1

Ejemplo 2

Conclusión
La solución descrita en el artículo se puede finalizar y utilizar no solo para adoptar los recursos de Kubernetes desde cero, sino también para agregarlos a las versiones existentes.
Por el momento no hay soluciones que permitan aprovechar los recursos existentes en el clúster, transferirlos a la gestión de Helm. Es posible que en Helm 3 se implemente una solución que cubra este problema (al menos hay una
propuesta sobre este tema).
PS
Otros del ciclo de consejos y trucos de K8s:
Lea también en nuestro blog: