ArgoCD помогает деплоить приложения в Kubernetes, используя GitOps подход, т.е. когда приложения, конфиги, манифесты и тому подобные данных хранятся в Git-репозитории.
Поддерживает работу с “голыми” манифестами Kubernetes, kustomize, ksonnet, jsonnet и то, чем пользуемся мы – Helm-шаблонами.
ArgoCD запускает свой контроллер в Kubernetes-кластере, и отслеживает изменения в Git-репозиториях, синхронизируя приложения в кластере с их манифестами в репозитории.
Из особых вкусностей – SSO с SAML, что позволит интегрировать ArgoCD с нашей Okta, возможность деплоить на несколько кластеров, поддержка Kubernetes RBAC, шикарный WebUI и наличие CLI, интеграция с вебхуками Github, GitLab, etc, плюс Prometehus-метрики из коробки и прекрасная документация.
Планируем использовать его для деплоя Helm-чартов вместо Jenkins, см. Helm: пошаговое создание чарта и деплоймента из Jenkins.
По GitOps и ArgoCD есть очень толковый доклад от Igor Borodin на XPDays вот тут>>>, правда 2019 – но с наглядными примерами.
И стоит почитать о недостатках GitOps в посте GitOps: The Bad and the Ugly.
Содержание
Компоненты
ArgoCD состоит из трёх компонентов – API-сервер, Repository Server и Application Controller.
- API-сервер (pod: argocd-server): отвечает за управление всем приложением, вызов всех операций, управление данными доступа, которые хранятся в виде Kubernetes Secrets, аутентификацию и прочее
- Repository Server (pod: argocd-repo-server): хранит локальные копии данных из Git-репозиториев проектов и генерирует Kubernetes-манифесты
- Application Controller (pod: argocd-application-controller): мониторит приложения в Kubernetes, и сравнивает их состояние с тем, которое описано в репозитории, плюс отвечает за вызов PreSync, Sync, PostSync хуков
Установка ArgoCD CLI
В macOS:
[simterm]
$ brew install argocd
[/simterm]
В Linux – из Github:
[simterm]
$ VERSION=$(curl --silent "https://api.github.com/repos/argoproj/argo-cd/releases/latest" | grep '"tag_name"' | sed -E 's/.*"([^"]+)".*/\1/') $ sudo curl -sSL -o /usr/local/bin/argocd https://github.com/argoproj/argo-cd/releases/download/$VERSION/argocd-linux-amd64 $ sudo chmod +x /usr/local/bin/argocd
[/simterm]
Проверяем:
[simterm]
$ argocd version argocd: v1.7.9+f6dc8c3 BuildDate: 2020-11-17T23:18:20Z GitCommit: f6dc8c389a00d08254f66af78d0cae1fdecf7484 GitTreeState: clean GoVersion: go1.14.12 Compiler: gc Platform: linux/amd64
[/simterm]
Запуск ArgoCD в Kubernetes
Ну и запустим ArgoCD. Тут всё просто – используем готовый манифест, который создаст CRD, ServiceAccount-ы, RBAC-роли и биндинги, ConfigMaps, Secrets, Services и Deployments.
Хотя вообще должен быть готовый Helm-чарт – но пока сделаем так, как описано в Getting Started.
Проще запускать в неймспейсе argocd, который предлагает сам ArgoCD в документации, но мы простых путей не ищем, поэтому – создаём свой namespace:
[simterm]
$ kubectl create namespace dev-1-devops-argocd-ns namespace/dev-1-devops-argocd-ns created
[/simterm]
Деплоим ресурсы:
[simterm]
$ kubectl apply -n dev-1-devops-argocd-ns -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
[/simterm]
Редактируем Sevrice argcd-server – меняем ему тип на LoadBalancer, что бы получить доступ к WebUI из мира:
[simterm]
$ kubectl -n dev-1-devops-argocd-ns patch svc argocd-server -p '{"spec": {"type": "LoadBalancer"}}' service/argocd-server patched
[/simterm]
Находим его URL:
[simterm]
$ kubectl -n dev-1-devops-argocd-ns get svc argocd-server NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE argocd-server LoadBalancer 172.20.142.44 ada***585.us-east-2.elb.amazonaws.com 80:32397/TCP,443:31693/TCP 4m21s
[/simterm]
Пароль ArgoCD генерируется во время установки, и по дефолту задан равным имени пода – получаем его:
[simterm]
$ kubectl -n dev-1-devops-argocd-ns get pods -l app.kubernetes.io/name=argocd-server -o name | cut -d'/' -f 2 argocd-server-794857c8fb-xqgmv
[/simterm]
Логинимся через CLI, на ошибку сертификата пока внимания не обращаем:
[simterm]
$ argocd login ada***585.us-east-2.elb.amazonaws.com WARNING: server certificate had error: x509: certificate is valid for localhost, argocd-server, argocd-server.dev-1-devops-argocd-ns, argocd-server.dev-1-devops-argocd-ns.svc, argocd-server.dev-1-devops-argocd-ns.svc.cluster.local, not ada***585.us-east-2.elb.amazonaws.com. Proceed insecurely (y/n)? y Username: admin Password: 'admin' logged in successfully Context 'ada***585.us-east-2.elb.amazonaws.com' updated
[/simterm]
Меняем пароль:
[simterm]
$ argocd account update-password *** Enter current password: *** Enter new password: *** Confirm new password: Password updated
[/simterm]
Открываем WebUI, снова игнорируем ошибку сертификата – сейчас всё настроим. Логинимся:
LoadBalancer, SSL и DNS
Окей – всё запустилось, настроим нормальное имя и SSL.
AWS ALB и ELB не поддерживают gRPC, см. AWS Application Load Balancers (ALBs) And Classic ELB (HTTP Mode), поэтому ALB Ingress Controller использовать не получится – используем просто Service с типом LoadBalancer, как уже сделали выше – он создаёт AWS Classic LoadBalancer.
Сертификат получаем в AWS Certificate Manager в том же регионе, где кластер и балансировщик, сохраняем его ARN.
Качаем файл манифеста:
[simterm]
$ wget https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
[/simterm]
Находим Service argocd-server:
--- apiVersion: v1 kind: Service metadata: labels: app.kubernetes.io/component: server app.kubernetes.io/name: argocd-server app.kubernetes.io/part-of: argocd name: argocd-server spec: ports: - name: http port: 80 protocol: TCP targetPort: 8080 - name: https port: 443 protocol: TCP targetPort: 8080 selector: app.kubernetes.io/name: argocd-server
В annotations добавляем service.beta.kubernetes.io/aws-load-balancer-ssl-cert
, в spec.type
– тип LoadBalancer
, и ограничиваем доступ через loadBalancerSourceRanges
:
--- apiVersion: v1 kind: Service metadata: labels: app.kubernetes.io/component: server app.kubernetes.io/name: argocd-server app.kubernetes.io/part-of: argocd name: argocd-server annotations: service.beta.kubernetes.io/aws-load-balancer-ssl-cert: "arn:aws:acm:us-east-1:534***385:certificate/ddaf55b0-***-53d57c5ca706" spec: type: LoadBalancer loadBalancerSourceRanges: - "31.***.***.117/32" - "194.***.***.24/29" ports: - name: http port: 80 protocol: TCP targetPort: 8080 - name: https port: 443 protocol: TCP targetPort: 8080 selector: app.kubernetes.io/name: argocd-server
Деплоим:
[simterm]
$ kubectl -n dev-1-devops-argocd-ns apply -f install.yaml
[/simterm]
Проверяем ELB SSL:
Создаём DNS:
Открываем URL – и ловим ERR_TOO_MANY_REDIRECTS:
ArgoCD SSL: ERR_TOO_MANY_REDIRECTS
Гуглим, находим https://github.com/argoproj/argo-cd/issues/2953.
Возвращаемся к install.yaml
, в Deployment
argocd-server добавляем --insecure
:
--- apiVersion: apps/v1 kind: Deployment metadata: labels: app.kubernetes.io/component: server app.kubernetes.io/name: argocd-server app.kubernetes.io/part-of: argocd name: argocd-server spec: selector: matchLabels: app.kubernetes.io/name: argocd-server template: metadata: labels: app.kubernetes.io/name: argocd-server spec: containers: - command: - argocd-server - --staticassets - /shared/app - --insecure ...
Снова деплоим, и проверяем:
Окей – тут готово.
ArgoCD: деплой из Github
Ну и не будем далеко уходить от гайда – задеплоим тестовое приложение. Helm-чарт будет в следующей части.
Кликаем New App, указываем имя и Project == default:
В Git указваем URL https://github.com/argoproj/argocd-example-apps.git и путь внутри репозитория в каталогу – guestbook:
В Destination – https://kubernetes.default.svc и default namespace, жмём Create:
Вроде бы создало, но почему в Sync Status – Unknown?
Что-то пошло не так:
[simterm]
$ argocd app get guestbook Name: guestbook Project: default Server: https://kubernetes.default.svc Namespace: default ... CONDITION MESSAGE LAST TRANSITION ComparisonError failed to load initial state of resource PersistentVolumeClaim: persistentvolumeclaims is forbidden: User "system:serviceaccount:dev-1-devops-argocd-ns:argocd-application-controller" cannot list resource "persistentvolumeclaims" in API group "" at the cluster scope 2020-11-19 15:35:51 +0200 EET ComparisonError failed to load initial state of resource PersistentVolumeClaim: persistentvolumeclaims is forbidden: User "system:serviceaccount:dev-1-devops-argocd-ns:argocd-application-controller" cannot list resource "persistentvolumeclaims" in API group "" at the cluster scope 2020-11-19 15:35:51 +0200 EET
[/simterm]
Пробуем sync
– и тоже не работает:
[simterm]
$ argocd app sync guestbook Name: guestbook Project: default Server: https://kubernetes.default.svc Namespace: default ... Sync Status: Unknown Health Status: Healthy Operation: Sync Sync Revision: Phase: Error Start: 2020-11-19 15:37:01 +0200 EET Finished: 2020-11-19 15:37:01 +0200 EET Duration: 0s Message: ComparisonError: failed to load initial state of resource Pod: pods is forbidden: User "system:serviceaccount:dev-1-devops-argocd-ns:argocd-application-controller" cannot list resource "pods" in API group "" at the cluster scope;ComparisonError: failed to load initial state of resource Pod: pods is forbidden: User "system:serviceaccount:dev-1-devops-argocd-ns:argocd-application-controller" cannot list resource "pods" in API group "" at the cluster scope FATA[0001] Operation has completed with phase: Error
[/simterm]
ArgocD: ComparisonError failed to load initial state of resource
Собственно, проще искать причину по ошибке “User “system:serviceaccount:dev-1-devops-argocd-ns:argocd-application-controller” cannot list resource “pods” in API group “” at the cluster scope“.
Проверяем ServiceAccount argocd-application-controller (пригодилось Kubernetes: ServiceAccounts, JWT-токены, аутентификация и RBAC-авторизация):
[simterm]
$ kubectl -n dev-1-devops-argocd-ns get serviceaccount argocd-application-controller NAME SECRETS AGE argocd-application-controller 1 36m
[/simterm]
Да, наш User system:serviceaccount:dev1-devops-argocd-ns:argocd-application-controller
есть.
И этому ServiceAccount мапится ClusterRoleBinding argocd-application-controller:
[simterm]
$ kubectl -n dev-1-devops-argocd-ns get clusterrolebinding argocd-application-controller NAME AGE argocd-application-controller 24h
[/simterm]
Но прав на выполнение list pods
у этого ServiceAccount нет:
[simterm]
$ kubectl auth can-i list pods -n dev-1-devops-argocd-ns --as system:serviceaccount:dev-1-devops-argocd-ns:argocd-application-controller no
[/simterm]
Хотя в ClusterRole все права есть:
--- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: labels: app.kubernetes.io/component: application-controller app.kubernetes.io/name: argocd-application-controller app.kubernetes.io/part-of: argocd name: argocd-application-controller rules: - apiGroups: - '*' resources: - '*' verbs: - '*' - nonResourceURLs: - '*' verbs: - '*'
Проверяем ClusterRoleBinding ещё раз, теперь с выводом всего через -o yaml
:
[simterm]
$ kubectl get clusterrolebinding argocd-application-controller -o yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding ... roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: argocd-application-controller subjects: - kind: ServiceAccount name: argocd-application-controller namespace: argocd
[/simterm]
namespace: argocd
– “Ага, вот эти ребята!” (c)
Находим две ClusterRoleBinding в install.yaml
– argocd-application-controller и argocd-server, меняем неймспейс:
--- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: labels: app.kubernetes.io/component: application-controller app.kubernetes.io/name: argocd-application-controller app.kubernetes.io/part-of: argocd name: argocd-application-controller roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: argocd-application-controller subjects: - kind: ServiceAccount name: argocd-application-controller namespace: dev-1-devops-argocd-ns --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: labels: app.kubernetes.io/component: server app.kubernetes.io/name: argocd-server app.kubernetes.io/part-of: argocd name: argocd-server roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: argocd-server subjects: - kind: ServiceAccount name: argocd-server namespace: dev-1-devops-argocd-ns
Собственно, вот, почему в начале говорил, что проще делать в дефолтном нейспейсе – если деплоить в кастомный, то придётся тут задавать неймспейсы. Но у нас свои naming convention для неймспейсов, стараемся их соблюдать.
Хотя тут ещё зависит от того, как деплоить сам ArgoCD – на каждый кластер свою копию, или делать центральный инстанс ArgoCD – и с него деплоить на разные кластера. Плюс, если делать через Helm-чарт – там наверняка можно в параметрах указывать.
Передеплоиваем, проверяем:
[simterm]
$ kubectl auth can-i list pods -n dev-1-devops-argocd-ns --as system:serviceaccount:dev-1-devops-argocd-ns:argocd-application-controller yes
[/simterm]
Пробуем ещё раз sync
:
[simterm]
$ argocd app sync guestbook TIMESTAMP GROUP KIND NAMESPACE NAME STATUS HEALTH HOOK MESSAGE 2020-11-19T15:47:08+02:00 Service default guestbook-ui Running Synced service/guestbook-ui unchanged 2020-11-19T15:47:08+02:00 apps Deployment default guestbook-ui Running Synced deployment.apps/guestbook-ui unchanged Name: guestbook Project: default Server: https://kubernetes.default.svc ... Sync Status: Synced to HEAD (6bed858) Health Status: Healthy Operation: Sync Sync Revision: 6bed858de32a0e876ec49dad1a2e3c5840d3fb07 Phase: Succeeded Start: 2020-11-19 15:47:06 +0200 EET Finished: 2020-11-19 15:47:08 +0200 EET Duration: 2s Message: successfully synced (all tasks run) GROUP KIND NAMESPACE NAME STATUS HEALTH HOOK MESSAGE Service default guestbook-ui Synced Healthy service/guestbook-ui unchanged apps Deployment default guestbook-ui Synced Healthy deployment.apps/guestbook-ui unchanged
[/simterm]
Работает:
И логи пода:
Следующий шаг – разобраться с деплоем Helm-чартов, и как прикрутить работу с Helm Secrets – там придётся или билдить кастомный Docker-образ с ArgoCD, в котором будет установлен Helm-плагин secrets – или делать через Kubernetes InitContainers – варианты есть. Посмотрим, как проще.