Розбираємось з Custom Tooling в Argo CD

Розбираємось з Custom Tooling в Argo CD

Через деякий час після написання першої статті, де я вправно справлявся з jsonnet і гітлабом, я зрозумів що пайплайни це звичайно добре, але надмірно складно та незручно.

У більшості випадків потрібне типове завдання: "згенерувати YAML і покласти його в Kubernetes". Власне, з чим Argo CD чудово справляється.

Argo CD дозволяє підключити Git-репозиторій та синкати його стан у Kubernetes. За замовчуванням є підтримка кількох видів програм: Kustomize, Helm чарти, Ksonnet, голий Jsonnet або просто директорії з маніфестами YAML/JSON.

Більшість користувачів цього набору буде достатньо, але не всім. Для того щоб задовольнити потреби всіх і кожного Argo CD є можливість використовувати custom tooling.

Насамперед цікавить можливість додавання підтримки qbec и git-crypt, які з повного часу були розглянуті в попередній статті.

Перш ніж приступити до конфігурації, потрібно спочатку розібратися з тим, як саме Argo CD працює.

Для кожного доданого додатку він має дві фази:

  • ініціалізації - Початкова підготовка перед деплоєм, тут може бути все що завгодно: скачування залежностей, розпакування секретів та інше.
  • породжувати - Виконання безпосередньо команди генерації маніфестів, висновок повинен бути валідним YAML stream, це саме те, що буде застосовано в кластер.

Примітно, що Argo застосовує цей підхід для будь-якого типу додатків, у тому числі і для Helm. Тобто Argo CD Helm не займається депломом релізів в кластер, а використовується тільки для генерації маніфестів.

Зі свого боку Argo вміє нативно обробляти Helm-хуки, що дозволяє не порушувати логіки застосування релізів.

QBEC

Qbec дозволяє зручно описувати програми за допомогою jsonnet, а також має можливість рендерити Helm-чарти, а так як Argo CD вміє нормально обробляти Helm-хуки, то використання цієї можливості з Argo CD дозволяє домогтися ще коректніших результатів.

Для того, щоб додати підтримку qbec в argocd потрібно дві речі:

  • у конфізі Argo CD повинен бути визначений ваш custom plugin і команди для генерації маніфестів.
  • потрібні бінарники мають бути доступні в образі argocd-repo-server.

перше завдання вирішується досить просто:

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

(команда ініціалізації не використовується)

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

Для додавання бінарників пропонується зібрати новий образ, або використовувати трюк з 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)"

Тепер подивимося як виглядатиме маніфест нашої програми:

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

У змінній НАВКОЛИШНЄ СЕРЕДОВИЩЕ ми передаємо ім'я оточення, для якого потрібно виконувати генерацію маніфестів.

застосуємо та подивимося що у нас вийшло:

Розбираємось з Custom Tooling в Argo CD

додаток задепло, чудово!

git-crypt

Git-crypt дозволяє настроїти прозоре шифрування репозиторію. Це простий та безпечний спосіб зберігати конфіденційні дані прямо у git.

З імплементацією git-crypt виявилося складніше.

Теоретично ми могли б виконувати git-crypt unlock на init-стадії нашого custom-плагіну, але це не дуже зручно, тому що не дозволило б використовувати нативні методи деплою. Наприклад у випадку Helm і Jsonnet, ми втрачаємо гнучкий GUI-інтерфейс, який дозволяє спростити налаштування програми (values-файли та інше).

Саме тому хотілося виконувати роздрукування репозиторію ще більш ранній стадії, при клонуванні.

Оскільки Argo CD не надає можливості для опису будь-яких хуків для синхронізації репозиторію, довелося обійти це обмеження хитрим шелл скриптом-оберткою, який замінює собою команду 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 виконує git fetch щоразу перед операцією деплою. Саме на цю команду ми й повісимо виконання git-crypt unlock для розблокування репозиторію.

для тестів можете використати мій docker-образ в якому вже є все необхідне:

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

Тепер нам потрібно подумати про те, як Argo розшифровуватиме наші репозиторії. А саме згенерувати gpg-ключ для нього:

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

$ printf "%sn" 
    "%no-protection" 
    "Key-Type: default" 
    "Subkey-Type: default" 
    "Name-Real: YOUR NAME" 
    "Name-Email: YOUR 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'

Збережемо ім'я ключа 8CB8B24F50B4797D для подальших кроків. Експортуємо сам ключ:

$ gpg --list-keys
gpg: WARNING: unsafe ownership on homedir '/home/argocd/.gnupg'
/home/argocd/.gnupg/pubring.kbx
-------------------------------
pub   rsa3072 2020-09-04 [SC]
      9A1FF8CAA917CE876E2562FC8CB8B24F50B4797D
uid           [ultimate] YOUR NAME <YOUR EMAIL@example.com>
sub   rsa3072 2020-09-04 [E]

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

І додамо його у вигляді окремого секрету:

# 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

Єдине, що нам залишилося, це прокинути його в контейнер argocd-repo-serverдля цього відредагуємо deployment:

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

І замінимо існуючий gpg-keys volume на projected, де і вкажемо наш секрет:

   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 автоматично підвантажує gpg-ключі з цієї директорії при старті контейнера, таким чином він завантажить наш приватний ключ.

перевіримо:

$ kubectl -n argocd exec -ti deploy/argocd-repo-server -- bash
$ GNUPGHOME=/app/config/gpg/keys gpg --list-secret-keys
gpg: WARNING: unsafe ownership on homedir '/app/config/gpg/keys'
/app/config/gpg/keys/pubring.kbx
--------------------------------
sec   rsa2048 2020-09-05 [SC] [expires: 2021-03-04]
      ED6285A3B1A50B6F1D9C955E5E8B1B16D47FFC28
uid           [ultimate] Anon Ymous (ArgoCD key signing key) <noreply@argoproj.io>

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

Добре, ключ завантажений! Тепер нам достатньо додати Argo CD до нашого репозиторія як колаборатора і він зможе автоматично розшифровувати його на льоту.

Імпортуємо ключ на локальний комп'ютер:

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

Налаштуємо рівень довіри:

$ gpg --edit-key 8CB8B24F50B4797D
trust
5

Додамо argo як колаборатор у наш проект:

$ git-crypt add-gpg-user 8CB8B24F50B4797D

Посилання по темі:

Джерело: habr.com