Спустя некоторое время после написания первой статьи, где я ловко управлялся с jsonnet и гитлабом, я понял что пайплайны это конечно хорошо, но излишне сложно и неудобно.
В большинстве случаев требуется типовая задача: "сгенерировать YAML и положить его в Kubernetes". Собственно, с чем Argo CD замечательно и справляется.
Argo CD позволяет подключить Git-репозиторий и синкать его состояние в Kubernetes. По умолчанию есть поддержка нескольких видов приложений: Kustomize, Helm чарты, Ksonnet, голый Jsonnet или просто директории с YAML/JSON манифестами.
Большинству пользователей этого набора будет достаточно, но не всем. Для того чтобы удовлетворить потребности всех и каждого в Argo CD имеется возможность использовать custom tooling.
В первую очередь интересует возможность добавления поддержки qbec и git-crypt, которые с полна были рассмотренны в предыдущей статье.
Прежде чем приступить к конфигурации, нужно сначала разобраться с тем как именно Argo CD работает.
Для каждого добавленного приложения он имеет две фазы:
- init — начальная подготовка перед деплоем, здесь может быть всё что угодно: скачивание зависимостей, распаковка секретов и другое.
- generate — выполнение непосредственно команды генерации манифестов, вывод должен быть валидным 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"']
(команда init не используется)
$ 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
В переменной ENVIRONMENT мы передаём имя окружения для которого нужно выполнять генерацию манифестов.
применим и посмотрим что у нас получилось:
приложение задеплоилось, отлично!
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 "%s\n" "%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:
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 в наш репозиторий в качестве коллаборатора и он сможет автоматически расшифровывать его на лету.
Импортируем ключ на локальный компьютер:
$ GNUPGHOME=/app/config/gpg/keys gpg --armor --export 8CB8B24F50B4797D > 8CB8B24F50B4797D.pem
$ gpg --import 8CB8B24F50B4797D.pem
Настроим уровень доверия:
$ gpg --edit-key 8CB8B24F50B4797D
trust
5
Добавим argo в качестве коллаборатора в наш проект:
$ git-crypt add-gpg-user 8CB8B24F50B4797D
Ссылки по теме:
gecube
Очень круто — спасибо — но… почему не fluxcd?
И вместо git-crypt ( с которым вроде как можно случайно промахнуться) они же предлагают sops...
kvaps Автор
Flux крутой, не не даёт такой гибкости как Argo CD.
Кроме того, мне очень импонирует идея Argo не позволять утилитам выполнять деплой самостоятельно, а только генерацию манифестов.
Таким образом Argo следит за создаваемыми ресурсами, а так же отображает их текущее состояние в красивой web-панельке.
Почему не sops, сложилось исторически. Но по сути sops и git-crypt выполняют одинаковую работу. Разница в том, что sops поддерживает JSON и YAML и шифрует данные в них выборочно, а git-crypt шифрует файлы целиком вне зависимости от их формата.
Что значит "случайно промахнуться"?