Entendiendo Custom Tooling en Argo CD

Entendiendo Custom Tooling en Argo CD

Después de un tiempo desde que escribí el primer artículo, donde manejaba hábilmente jsonnet y GitLab, me di cuenta de que los pipelines son buenos, pero son excesivamente complicados e incómodos.

En la mayoría de los casos, se requiere una tarea típica: "generar YAML y colocarlo en Kubernetes". En resumen, eso es precisamente lo que Argo CD hace maravillosamente.

Argo CD permite conectar un repositorio Git y sincronizar su estado en Kubernetes. Por defecto, hay soporte para varios tipos de aplicaciones: Kustomize, gráficos de Helm, Ksonnet, Jsonnet puro o simplemente directorios con manifiestos YAML/JSON.

Para la mayoría de los usuarios de este conjunto, esto será suficiente, pero no para todos. Para satisfacer las necesidades de todos, Argo CD ofrece la posibilidad de utilizar herramientas personalizadas.

En primer lugar, me interesa la posibilidad de añadir soporte para qbec y git-crypt, que fueron discutidos en el artículo anterior.

Antes de comenzar con la configuración, primero debemos entender cómo funciona Argo CD.

Para cada aplicación añadida, tiene dos fases:

  • init — preparación inicial antes del despliegue, aquí puede haber de todo: descarga de dependencias, descompresión de secretos y más.
  • generate — ejecución de comandos de generación de manifiestos, la salida debe ser un flujo YAML válido, esto es precisamente lo que se aplicará en el clúster.

Es notable que Argo aplica este enfoque para cualquier tipo de aplicación, incluyendo Helm. Es decir, en Argo CD, Helm no se encarga de desplegar versiones en el clúster, sino que se usa solo para generar manifiestos.

De su parte, Argo puede manejar de forma nativa los ganchos de Helm, lo que permite no romper la lógica de aplicación de versiones.

QBEC

Qbec permite describir aplicaciones de manera conveniente con jsonnet, y además tiene la capacidad de renderizar gráficos de Helm; dado que Argo CD puede manejar correctamente los ganchos de Helm, el uso de esta capacidad con Argo CD permite lograr resultados aún más precisos.

Para añadir soporte para qbec en argocd se necesitan dos cosas:

  • en la configuración de Argo CD debe estar definido su plugin personalizado y los comandos para generar los manifiestos.
  • los binarios necesarios deben estar disponibles en la imagen argocd-repo-server.

La primera tarea se resuelve de manera bastante simple:

# cm.yaml
data:
  configManagementPlugins: |
    - name: qbec
      generate:
        command: [sh, -xc]
        args: ['qbec show "$ENVIRONMENT" -S --force:k8s-namespace "$ARGOCD_APP_NAMESPACE"']

(el comando init no se usa)

$ kubectl -n argocd patch cm/argocd-cm -p "$(cat cm.yaml)"

Para añadir los binarios, se sugiere reunir una nueva imagen, o usar un truco con el contenedor init:

# deploy.yaml
spec:
  template:
    spec:
      # 1. Define an emptyDir volume which will hold the custom binaries
      volumes:
      - name: custom-tools
        emptyDir: {}
      # 2. Use an init container to download/copy custom binaries into the emptyDir
      initContainers:
      - name: download-tools
        image: alpine:3.12
        command: [sh, -c]
        args:
        - wget -qO- https://github.com/splunk/qbec/releases/download/v0.12.2/qbec-linux-amd64.tar.gz | tar -xvzf - -C /custom-tools/
        volumeMounts:
        - mountPath: /custom-tools
          name: custom-tools
      # 3. Volume mount the custom binary to the bin directory (overriding the existing version)
      containers:
      - name: argocd-repo-server
        volumeMounts:
        - mountPath: /usr/local/bin/qbec
          name: custom-tools
          subPath: qbec
        - mountPath: /usr/local/bin/jsonnet-qbec
          name: custom-tools
          subPath: jsonnet-qbec

$ kubectl -n argocd patch deploy/argocd-repo-server -p "$(cat deploy.yaml)"

Ahora veamos cómo se verá el manifiesto de nuestra aplicación:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: qbec-app
  namespace: argocd
spec:
  destination: 
    namespace: default
    server: https://kubernetes.default.svc
  project: default
  source: 
    path: qbec-app
    plugin: 
      env: 
        - name: ENVIRONMENT
          value: default
      name: qbec
    repoURL: https://github.com/kvaps/argocd-play
  syncPolicy: 
    automated: 
      prune: true

En la variable ENVIRONMENT se pasa el nombre del entorno para el cual necesitamos generar los manifiestos.

aplicamos y vemos qué hemos conseguido:

Entendiendo Custom Tooling en Argo CD

la aplicación se ha desplegado, ¡excelente!

git-crypt

Git-crypt permite configurar un cifrado transparente del repositorio. Es una manera simple y segura de almacenar datos confidenciales directamente en git.

La implementación de git-crypt resultó ser más complicada.

Teóricamente podríamos ejecutar git-crypt unlock en la etapa de init de nuestro plugin personalizado, pero no es muy conveniente, ya que no permitiría utilizar métodos nativos de despliegue. Por ejemplo, en el caso de Helm y Jsonnet, perderíamos la interfaz gráfica flexible que permite simplificar la configuración de la aplicación (archivos de valores y demás).

Es precisamente por eso que quería realizar la desencriptación del repositorio en una etapa aún más temprana, al clonarlo.

Dado que en este momento Argo CD no ofrece la posibilidad de describir ningún gancho para la sincronización del repositorio, tuve que sortear esta limitación con un ingenioso script de shell envoltorio que reemplaza el comando git:

#!/bin/sh
$(dirname $0)/git.bin "$@"
ec=$?
[ "$1" = fetch ] && [ -d .git-crypt ] || exit $ec
GNUPGHOME=/app/config/gpg/keys git-crypt unlock 2>/dev/null
exit $ec

Argo CD ejecuta git fetch cada vez antes de la operación de despliegue. Precisamente a este comando es al que añadiremos la ejecución git-crypt unlock para desbloquear el repositorio.

para pruebas, puedes usar mi imagen de docker en la que ya hay todo lo necesario:

$ kubectl -n argocd set image deploy/argocd-repo-server argocd-repo-server=docker.io/kvaps/argocd-git-crypt:v1.7.3

Ahora necesitamos pensar en cómo Argo desencriptará nuestros repositorios. Es decir, generar una clave gpg para ello:

$ kubectl exec -ti deploy/argocd-repo-server -- bash

$ printf "%sn"
    "%no-protection"
    "Key-Type: default"
    "Subkey-Type: default"
    "Name-Real: TU NOMBRE"
    "Name-Email: TU EMAIL@example.com"
    "Expire-Date: 0"
    > genkey-batch

$ gpg --batch --gen-key genkey-batch
gpg: WARNING: unsafe ownership on homedir '/home/argocd/.gnupg'
gpg: keybox '/home/argocd/.gnupg/pubring.kbx' created
gpg: '/home/argocd/.gnupg/trustdb.gpg': trustdb created
gpg: key 8CB8B24F50B4797D marked as ultimately trusted
gpg: directory '/home/argocd/.gnupg/openpgp-revocs.d' created
gpg: revocation certificate stored as '/home/argocd/.gnupg/openpgp-revocs.d/9A1FF8CAA917CE876E2562FC8CB8B24F50B4797D.rev'

Guardaremos el nombre de la clave 8CB8B24F50B4797D para los siguientes pasos. Exportamos la clave:

$ gpg --list-keys
gpg: ADVERTENCIA: propiedad insegura en el directorio de inicio '\/home\/argocd\/ .gnupg'
\/home\/argocd\/.gnupg\/pubring.kbx
-------------------------------
pub   rsa3072 2020-09-04 [SC]
      9A1FF8CAA917CE876E2562FC8CB8B24F50B4797D
uid           [ultimate] TU NOMBRE <TU EMAIL@example.com>
sub   rsa3072 2020-09-04 [E]

$ gpg --armor --export-secret-keys 8CB8B24F50B4797D

Y lo agregaremos como un secreto separado:

# argocd-gpg-keys-secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: argocd-gpg-keys-secret
  namespace: argocd
stringData:
  8CB8B24F50B4797D: |-
    -----BEGIN PGP PRIVATE KEY BLOCK-----

    lQVYBF9Q8KUBDACuS4p0ctXoakPLqE99YLmdixfF/QIvXVIG5uBXClWhWMuo+D0c
    ZfeyC5GvH7XPUKz1cLMqL6o/u9oHJVUmrvN/g2Mnm365nTGw1M56AfATS9IBp0HH
    O/fbfiH6aMWmPrW8XIA0icoOAdP+bPcBqM4HRo4ssbRS9y/i
    =yj11
    -----END PGP PRIVATE KEY BLOCK-----

$ kubectl apply -f argocd-gpg-keys-secret.yaml

Lo único que nos queda es pasarlo al contenedor argocd-repo-server, para ello editaremos el deployment:

$ kubectl -n argocd edit deploy\/argocd-repo-server

Y reemplazaremos el existente gpg-keys volumen por projected, donde indicaremos nuestro secreto:

   spec:
     template:
       spec:
         volumes:
         - name: gpg-keys
           projected:
             defaultMode: 420
             sources:
             - secret:
                 name: argocd-gpg-keys-secret
             - configMap:
                 name: argocd-gpg-keys-cm

Argo CD carga automáticamente las claves gpg desde este directorio al iniciar el contenedor, así que también cargará nuestra clave privada.

verifiquemos:

$ kubectl -n argocd exec -ti deploy\/argocd-repo-server -- bash
$ GNUPGHOME=\/app\/config\/gpg\/keys gpg --list-secret-keys
gpg: ADVERTENCIA: propiedad insegura en el directorio de inicio '\/app\/config\/gpg\/keys'
\/app\/config\/gpg\/keys\/pubring.kbx
--------------------------------
sec   rsa2048 2020-09-05 [SC] [expira: 2021-03-04]
      ED6285A3B1A50B6F1D9C955E5E8B1B16D47FFC28
uid           [ultimate] Anon Ymous (clave de firma de ArgoCD) <noreply@argoproj.io>

sec   rsa3072 2020-09-03 [SC]
      9A1FF8CAA917CE876E2562FC8CB8B24F50B4797D
uid           [ultimate] TU NOMBRE <TU EMAIL@example.com>
ssb   rsa3072 2020-09-03 [E]

¡Excelente, la clave ha sido cargada! Ahora solo necesitamos agregar Argo CD a nuestro repositorio como colaborador y podrá descifrarlo automáticamente al vuelo.

Importamos la clave a la computadora local:

$ gpg --armor --export-secret 8CB8B24F50B4797D > 8CB8B24F50B4797D.pem
$ gpg --import 8CB8B24F50B4797D.pem

Configuraremos el nivel de confianza:

$ gpg --edit-key 8CB8B24F50B4797D
trust
5

Agregaremos argo como colaborador en nuestro proyecto:

$ git-crypt add-gpg-user 8CB8B24F50B4797D

Enlaces relacionados:

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