ArgoCD: деплой Helm-чарта и работа с Helm Secrets через AWS KMS | DevsDay.ru

IT-блоги ArgoCD: деплой Helm-чарта и работа с Helm Secrets через AWS KMS

rtfm.co.ua 21 ноября 2020 г. setevoy


 

В предыдущем посте ArgoCD: обзор, запуск, настройка SSL, деплой приложения потрогали ArgoCD, запустили тестовый инстанс, и задеплоили приложение из его готовых примеров.

Но наша цель — деплоить наши Helm-чарты, а потому посмотрим, как это можно сделать.

Самое интересное ожидаемо коснулось работы с Helm secrets. Пришлось покостылить, но в результате всё заработало так, как и ожидалось.

ArgCD: деплой Helm-чарта

Создаём тестовый чарт:

helm create test-helm-chart
Creating test-helm-chart

Проверяем его локально:

helm upgrade --install --namespace dev-1-test-helm-chart-ns --create-namespace test-helm-chart-release test-helm-chart/ --debug --dry-run
...
           {}
NOTES:
1. Get the application URL by running these commands:
 export POD_NAME=$(kubectl get pods --namespace dev-1-test-helm-chart-ns -l "app.kubernetes.io/name=test-helm-chart,app.kubernetes.io/instance=test-helm-chart-release" -o jsonpath="{.items[0].metadata.name}")
 export CONTAINER_PORT=$(kubectl get pod --namespace dev-1-test-helm-chart-ns $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
 echo "Visit http://127.0.0.1:8080 to use your application"
 kubectl --namespace dev-1-test-helm-chart-ns port-forward $POD_NAME 8080:$CONTAINER_PORT

Хорошо — чарт работает, пушим в репозиторий.

ArgoCD: подключение приватного репозитория

Github SSH-ключ

У нас Github-организация. Позже создадим отдельного пользователя для ArgoCD, у которого будет свой RSA-ключ доступа, пока добавим новый ключ своему Github-юзеру.

В общем-то можно сделать и через HTTPS и логин:токен, но через ключ уже привычнее.

Генерируем ключ:

ssh-keygen -f ~/.ssh/argocd-github-key
Generating public/private rsa key pair.
...

Добавляем его в Github — Settings > SSH keys:

ArgoCD repositories

Переходим в Settings — Reposistories:

Выбираем Connect repo using SSH:

Задаём имя, URL, приватный ключ:

Ключ будет сохранён в Kubernetes Secrets:

kk -n dev-1-devops-argocd-ns get secrets
NAME                                        TYPE                                  DATA   AGE
argocd-application-controller-token-mc457   kubernetes.io/service-account-token   3      45h
argocd-dex-server-token-74r75               kubernetes.io/service-account-token   3      45h
argocd-secret                               Opaque                                5      45h
argocd-server-token-54mfx                   kubernetes.io/service-account-token   3      45h
default-token-6mmr5                         kubernetes.io/service-account-token   3      45h
repo-332507798                              Opaque                                1      13m

repo-332507798 — вот он.

Жмём Connect.

Создание приложения

Создаём новое приложение:

Задаём имя, проект оставляем default, в Sync Policy можно включить опцию Auto-create namespace:

В Source оставляем Git, задаём URL репозитория, в Revision указываем бранч, в Path — путь к чарту.

В данном случае репозиторий devops-kubernetes, каталог с чартом — tests/test-helm-chart/, причём ArgoCD сам просканирует репозиторий, и предложит выбор каталогов:

В Destination выбираем локальный (в нашем случае) Kubernetes-кластер, указываем namespace, в который будем деплоить чарт:

В Destination, вместо Directory — указываем Helm, хотя Argo сам увидел, что это каталог с Helm-чартом, выбрал соответствующий тип и подгрузил значения из values.yaml в корне чарта, тут можно пока ничего не менять — позже в Values Files добавим наш secrets.yaml:

Готово:

Если кликнуть сейчас по приложению, то видим, что ArgoCD уже просканировал файлы шаблонов, и отобразил компоненты, которые будут задеплоены:

Кликаем Sync, и видим доступные опции — и Prune, с удалением, и Dry Run:

Кликаем Syncronize — пошёл деплой:

Всё задеплоилось и запустилось:

Проверим список приложений:

argocd app list
NAME             CLUSTER                         NAMESPACE                        PROJECT  STATUS  HEALTH   SYNCPOLICY  CONDITIONS  REPO                                                 PATH                   TARGET
guestbook        https://kubernetes.default.svc  default                          default  Synced  Healthy  <none>      <none>      https://github.com/argoproj/argocd-example-apps.git  guestbook              HEAD
test-helm-chart  https://kubernetes.default.svc  dev-1-devops-test-helm-chart-ns  default  Synced  Healthy  <none>      <none>      git@github.com:***/devops-kubernetes.git             tests/test-helm-chart  DVPS-458-ArgoCD

И под в неймспейсе:

kubectl -n dev-1-devops-test-helm-chart-ns get pod
NAME                               READY   STATUS    RESTARTS   AGE
test-helm-chart-67dccc9fb4-2m5rf   1/1     Running   0          2m27s

А теперь приступим к работе с Helm secrets.

ArgoCD и Helm Secrets

Всё хорошо и просто, пока мы не дошли до секретов, т.к. Helm в ArgoCD не имеет предустановленных плагинов для Helm.

Из вариантов — собирать свой образ, рекомендовано министерством argoхранения тут>>>, либо — устанавливать в Kubernetes InitContainer через shared-volume, как описано тут>>>.

InitContainer с shared-volume

Первым вариантом я попробовал InitContainer с shared-volume, и в целом оно-то работает — плагин установился.

Выглядел Deployment для argocd-repo-server так:

---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app.kubernetes.io/component: repo-server
    app.kubernetes.io/name: argocd-repo-server
    app.kubernetes.io/part-of: argocd
  name: argocd-repo-server
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: argocd-repo-server
  template:
    metadata:
      labels:
        app.kubernetes.io/name: argocd-repo-server
    spec:
      automountServiceAccountToken: false
      initContainers:
      - name: argo-tools
        image: alpine/helm
        command: [sh, -c]
        args:
        - apk add git &&
          apk add curl &&
          apk add bash &&
          helm plugin install https://github.com/futuresimple/helm-secrets
        volumeMounts:
        - mountPath: /root/.local/share/helm/plugins/
          name: argo-tools
      containers:
      - command:
        - uid_entrypoint.sh
        - argocd-repo-server
        - --redis
        - argocd-redis:6379
        image: argoproj/argocd:v1.7.9
        imagePullPolicy: Always
        name: argocd-repo-server
        ports:
        - containerPort: 8081
        - containerPort: 8084
        readinessProbe:
          initialDelaySeconds: 5
          periodSeconds: 10
          tcpSocket:
            port: 8081
        volumeMounts:
        - mountPath: /app/config/ssh
          name: ssh-known-hosts
        - mountPath: /app/config/tls
          name: tls-certs
        - mountPath: /app/config/gpg/source
          name: gpg-keys
        - mountPath: /app/config/gpg/keys
          name: gpg-keyring
        - mountPath: /home/argocd/.local/share/helm/plugins/
          name: argo-tools
      volumes:
      - configMap:
          name: argocd-ssh-known-hosts-cm
        name: ssh-known-hosts
      - configMap:
          name: argocd-tls-certs-cm
        name: tls-certs
      - configMap:
          name: argocd-gpg-keys-cm
        name: gpg-keys
      - emptyDir: {}
        name: gpg-keyring
      - emptyDir: {}
        name: argo-tools

Тут создаём emptyDir volume с именем argo-tools, запускаем initContainer с именем argo-tools, которому монтируем volume argo-tools в каталог /root/.local/share/helm/plugins/, устанавливаем git, curl и bash, и вызываем helm plugin install https://github.com/futuresimple/helm-secrets.

Этот же volume argo-tools монтируется к поду argocd-repo-server в каталог /home/argocd/.local/share/helm/plugins/ — и helm в контейнере argocd-repo-server плагин видит, и может использовать.

Но тут возникает проблема — как вызывать helm secrets install? Ведь ArgoCD по дефолту вызывает исполняемый файл /usr/local/bin/helm, и передать какие-то опции ему нельзя.

Поэтому пришлось всё-таки пилить костыль в виде кастомного образа, в который включаем helm-secrets, sops, и пишем wrapper-скрипт для вызова helm.

Сборка образа ArgoCD с плагином helm-secrets

Примеры решения нагуглились тут — How to Handle Kubernetes Secrets with ArgoCD and Sops.

Сначала — напишем наш wrapper-скрипт.

Задача скрипта — принимать вызовы к /usr/local/bin/helm с командами template, install, upgrade, lint и diff, которые понимает плагин helm-secrets, и передавать их в вызов helm secrets + все аргументы.

После выполнения helm secrets @arguments — выводится output выполнения helm secrets, из которого вырезается сообщение «removed ‘secrets.yaml.dec‘»:

#! /bin/sh
    
# helm secrets only supports a few helm commands
if [ $1 = "template" ] || [ $1 = "install" ] || [ $1 = "upgrade" ] || [ $1 = "lint" ] || [ $1 = "diff" ]
then 
    # Helm secrets add some useless outputs to every commands including template, namely
    # 'remove: <secret-path>.dec' for every decoded secrets.
    # As argocd use helm template output to compute the resources to apply, these outputs
    # will cause a parsing error from argocd, so we need to remove them.
    # We cannot use exec here as we need to pipe the output so we call helm in a subprocess and
    # handle the return code ourselves.
    out=$(helm.bin secrets $@) 
    code=$? 
    if [ $code -eq 0 ]; then
        # printf insted of echo here because we really don't want any backslash character processing
        printf '%s\n' "$out" | sed -E "/^removed '.+\.dec'$/d"      
        exit 0
    else
        exit $code
    fi
else
    # helm.bin is the original helm binary
    exec helm.bin $@
fi

Далее, надо собрать Docker-образ, в котором будут установлены helm-scerets, sops, и в котором вызов /usr/local/bin/helm будет заменён вызовом нашего скрипта.

Находим последнюю версию SOPS — https://github.com/mozilla/sops/releases/ и последнюю версию Helm-secrets — https://github.com/zendesk/helm-secrets/releases.

Пишем Dockerfile:

FROM argoproj/argocd:v1.7.9
    
ARG SOPS_VERSION="v3.6.1"
ARG HELM_SECRETS_VERSION="2.0.2" 

USER root  
COPY helm-wrapper.sh /usr/local/bin/
RUN apt-get update  --allow-insecure-repositories --allow-unauthenticated && \
    apt-get install -y \
    curl \
    gpg && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* && \
    curl -o /usr/local/bin/sops -L https://github.com/mozilla/sops/releases/download/${SOPS_VERSION}/sops-${SOPS_VERSION}.linux && \
    chmod +x /usr/local/bin/sops && \
    cd /usr/local/bin && \
    mv helm helm.bin && \
    mv helm2 helm2.bin && \
    mv helm-wrapper.sh helm && \
    ln helm helm2 && \
    chmod +x helm helm2
    
# helm secrets plugin should be installed as user argocd or it won't be found
USER argocd
RUN /usr/local/bin/helm.bin plugin install https://github.com/zendesk/helm-secrets --version ${HELM_SECRETS_VERSION}
ENV HELM_PLUGINS="/home/argocd/.local/share/helm/plugins/"

Собираем образ — репозиторий в Docker Hub публичный, можно использовать этот образ.

Тегаем версией ArgoCD, которая использовалась, и свою версию сборки, тут это 1:

docker build -t setevoy/argocd-helm-secrets:v1.7.9-1 .
docker push setevoy/argocd-helm-secrets:v1.7.9-1

Далее, нам надо обновить install.yaml, из которого деплоился ArgoCD (Helm-чарт пока не использовал).

SOPS и  AWS KMS — аутентификация

«Какая боль, какая боль!» (с)

В нашем случае для шифрования данных используется AWS Key Management Service, следовательно SOPS в контейнере который мы запустим из образа setevoy/argocd-helm-secrets:v1.7.9-1 должен иметь к нему доступ.

Для SOPS требуются файлы ~/.aws/credentials и ~/.aws/config, который создадим из Kubernetes Secrets.

Наверно, можно было бы попробовать ServiceAccount с IAM ролью, которая давала бы доступ к ключу — но пока сделаю так.

AWS IAM User

Создадим отдельно пользователя для доступа к ключу — переходим в AWS IAM, задаём ему Programmatic access:

Далее, создаём политику на ReadOnly только к ключу, который используется SOPS:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "kms:ListKeys",
                "kms:ListAliases",
                "kms:DescribeKey",
                "kms:ListKeyPolicies",
                "kms:GetKeyPolicy",
                "kms:GetKeyRotationStatus",
                "iam:ListUsers",
                "iam:ListRoles"
            ],
            "Resource": "arn:aws:kms:us-east-2:534***385:key/f73daf0d-***-440ca3b6547b",
            "Effect": "Allow"
        }
    ]
}

Сохраняем, и подключаем её:

Сохраняем пользователя, переходим в AWS KMS, и добавляем Key User:

Настраиваем новый локальный AWS-профиль:

aws configure --profile argocd-kms
AWS Access Key ID [None]: AKI***Q4F
AWS Secret Access Key [None]: S7c***6ya
Default region name [None]: us-east-2
Default output format [None]:

Проверяем доступ к ключу:

aws --profile argocd-kms kms describe-key --key-id f73daf0d-***-440ca3b6547b
{
"KeyMetadata": {
"AWSAccountId": "534***385",
"KeyId": "f73daf0d-***-440ca3b6547b",
"Arn": "arn:aws:kms:us-east-2:534***385:key/f73daf0d-***-440ca3b6547b",
...

Этот профиль будем использовать при шифровании секрета, и его же надо добавить в под argocd-repo-server.

AWS credentials и config

Cоздаём новый секрет, в котором описываем файлы ~/.aws/credentials и ~/.aws/config, которые потом замапим в под argocd-repo-server:

---     
apiVersion: v1
kind: Secret
metadata:
  name: argocd-aws-credentials
  namespace: dev-1-devops-argocd-ns
type: Opaque
stringData: 
  credentials: |
    [argocd-kms]
    aws_access_key_id = AKI***Q4F
    aws_secret_access_key = S7c***6ya
  config: | 
    [profile argocd-kms]
    region = us-east-2

Добавляем его в .gitignore:

cat .gitignore
argocd-aws-credentials.yaml

В будущем, когда будет делаться автоматизация для развёртывания ArgoCD, можно будет его создавать из Jenkins Secrets.

Создаём секрет:

kubectl apply -f argocd-aws-credentials.yaml
secret/argocd-aws-credentials created

Обновляем Deployment argocd-repo-server — меняем используемый образ, добавляем новый volume из нашего секрета, и монтируем его каталогом /home/argocd/.aws в контейнере с Argo:

---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app.kubernetes.io/component: repo-server
    app.kubernetes.io/name: argocd-repo-server
    app.kubernetes.io/part-of: argocd
  name: argocd-repo-server
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: argocd-repo-server
  template:
    metadata:
      labels:
        app.kubernetes.io/name: argocd-repo-server
    spec:
      automountServiceAccountToken: false
      containers:
      - command:
        - uid_entrypoint.sh
        - argocd-repo-server
        - --redis
        - argocd-redis:6379
#        image: argoproj/argocd:v1.7.9
        image: setevoy/argocd-helm-secrets:v1.7.9-1
        imagePullPolicy: Always
        name: argocd-repo-server
        ports:
        - containerPort: 8081
        - containerPort: 8084
        readinessProbe:
          initialDelaySeconds: 5
          periodSeconds: 10
          tcpSocket:
            port: 8081
        volumeMounts:
        - mountPath: /app/config/ssh
          name: ssh-known-hosts
        - mountPath: /app/config/tls
          name: tls-certs
        - mountPath: /app/config/gpg/source
          name: gpg-keys
        - mountPath: /app/config/gpg/keys
          name: gpg-keyring
        - mountPath: /home/argocd/.aws
          name: argocd-aws-credentials
      volumes:
      - configMap:
          name: argocd-ssh-known-hosts-cm
        name: ssh-known-hosts
      - configMap:
          name: argocd-tls-certs-cm
        name: tls-certs
      - configMap:
          name: argocd-gpg-keys-cm
        name: gpg-keys
      - emptyDir: {}
        name: gpg-keyring
      - name: argocd-aws-credentials
        secret:
          secretName: argocd-aws-credentials

Обновляем ArgoCD:

kubectl -n dev-1-devops-argocd-ns apply -f install.yaml

Проверяем поды:

kubectl -n dev-1-devops-argocd-ns get pod
NAME                                             READY   STATUS        RESTARTS   AGE
...
argocd-repo-server-64f4bbf4b7-jcs6x              1/1     Terminating   0          19h
argocd-repo-server-7c64775679-9jjq2              1/1     Running       0          12s

Проверяем файлы:

kubectl -n dev-1-devops-argocd-ns exec -ti argocd-repo-server-7c64775679-9jjq2 -- cat /home/argocd/.aws/credentials
[argocd-kms]
aws_access_key_id = AKI***Q4F
aws_secret_access_key = S7c***6ya

И пробуем использовать helm-secrets.

Добавление secrets.yaml

В репозитории с чартом создаём файл secrets.yaml:

somePassword: secretValue

Создаём .sops.yaml с указанием KMS-ключа для шифрования и AWS-профиля:

---
creation_rules:
  - kms: 'arn:aws:kms:us-east-2:534****385:key/f73daf0d-***-440ca3b6547b'
    aws_profile: argocd-kms

Шифруем файл:

helm secrets enc secrets.yaml
Encrypting secrets.yaml
Encrypted secrets.yaml

В тестовый чарт добавим использование секрета, например создание переменной TEST_SECRET_PASSWORD — обновляем файл templates/deployment.yaml:

...
      containers:
        - name: {{ .Chart.Name }}
          securityContext:
            {{- toYaml .Values.securityContext | nindent 12 }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
          imagePullPolicy: {{ .Values.image.pullPolicy }}
          env:
          - name: TEST_SECRET_PASSWORD
            value: {{ .Values.somePassword }}
...

Пушим изменения в репозиторий:

git add secrets.yaml templates/deployment.yaml
git commit -m "test secret added" && git push

Переходим в настройки приложения — App Details > Parameters, жмём Edit и добавляем values.yaml и secrets.yaml как Values Files:

ArgoCD теперь видит, что приложение рассинхронизировано с тем, что в репозитории:

Синхронизируем:

Проверяем новый под:

И напрямую в поде:

kubectl -n dev-1-devops-test-helm-chart-ns exec -ti test-helm-chart-5c777f9c9d-wkx6s -- printenv | grep SECRET
TEST_SECRET_PASSWORD=secretValue

Готово —  секрет на месте.


The post ArgoCD: деплой Helm-чарта и работа с Helm Secrets через AWS KMS first appeared on RTFM: Linux, DevOps и системное администрирование.

Источник: rtfm.co.ua

Amazon web services ArgoCD CI/CD HOWTO's Kubernetes Security Virtualization AWS AWS IAM GitOps

Читайте также


Lessons from Building the Dice Enhanced Recruiter Profile System

Разработка Dice Insights 25 ноября 2020 г. 12:49
Dice’s enhanced Recruiter Profile system, offering the ability for recruiters to link their profile to active job postings and highlight news and hiring needs within their profile, is live on […] The post Lessons from Buildi...... читать далее
Headline Programming Back-End Development Dice Recruiter Profiles Front-End Development

Weekend Roundup: Elon Musk Makes Bank, Xbox’s Future

Разработка Dice Insights 25 ноября 2020 г. 12:46
It’s not the weekend quite yet, but given how most of the U.S. will be taking the Thanksgiving holiday off, we decided to go ahead and […] The post Weekend Roundup: Elon Musk Makes Bank, Xbox’s Future appeared first on Dice Insights.... читать далее
Headline Working in Tech Apple Elon Musk Weekend Roundup Xbox

DevOps rtfm.co.ua 24 ноября 2020 г. 9:54

 В посте Kubernetes: обновление DNS в Route53 при создании Ingress выполнили ручную установку ExternalDNS, и посмотрели, как он работает — пора добавить автоматизацию его установки на кластера. В роли Configuration Management Tool у нас ис...... читать далее

Amazon web services Ansible CI/CD Configuration/Orchestration HOWTO's Jenkins Kubernetes Networking Virtualization AWS AWS IAM DNS Helm

Разработка DZone Web Dev 23 ноября 2020 г. 23:17

Digital innovations are not a know-how topic in 2020 because the processes of the Fourth Industrial Revolution have swept not just manufacturing and traditional industrial practices but literally every field of human life. The process took its time a...... читать далее

web dev digitalization digital transofrmation

Разработка Dice Insights 23 ноября 2020 г. 12:44

When Amazon Web Services (AWS), Amazon’s cloud division, announced its third quarter results for 2020 a few weeks ago, it disclosed a 37 percent year-on-year increase in revenues. […] The post Amazon Targeting Banks, Financial F...... читать далее

Cloud Headline Amazon AWS Job Hunting

DevOps DZone DevOps 20 ноября 2020 г. 16:40

What is Loki? Loki is an open-source, multi-tenant log aggregation system. It can be used with Grafana and Promtrail to collect and access logs, similar to the ELK/EFK stack. While one can use Kibana and Elasticsearch to make advanced data analysis a...... читать далее

devops

GameDev GCUP 27 ноября 2020 г. 18:04

Targem Games и Gaijin Entertainment объявляют о выходе обновления 0.12.20 “Чистый остров” в постапокалиптическом онлайн-экшне Crossout. В нем добавлена новая PvP-карта “Чистый остров”, запущен рейтинговый режим "Лига выживших" и открыта выставка тест...... читать далее

Игровые проекты

Популярные темы

ux (318) новости (300) design (278) новость (198) web dev (181) javascript (179) devops (176) ux-design (176) ubuntu (174) security (171) python (149) headline (144) seo (126) tutorial (120) ui (113) статьи (106) user-experience (99) testing roundup (85) programming (83) software testing (82) игровые проекты (77) java (76) api5 (76) дизайн (75) product-design (75) дайджесты вакансий от new.hr (73) design-thinking (70) google (68) primary (67) laravel (65) ui-design (64) working in tech (63) прочее (60) технологии (60) windows 10 (59) uncategorized (53) hardware (51) español (51) мероприятия (50) бизнес (50) движки и конструкторы игр (49) css (48) турбо-страницы (47) обучение (47) technology (47) работа (46) covid-19 (45) web design and applications (45) docker (44) case-study (44) навыки алисы (43) android (43) debian (42) publication (41) инструкции (40) cloud (39) angular (39) machine learning (38) inspiration (38) home page stories (38) chrome (38) wp (38) networking (37) ux-research (37) art (37) тестирование (36) kali linux (36) полезное (36) aspnet (36) web (36) vue.js (36) кейсы (35) .net (35) обзоры (35) events (35) google ads (34) arch linux (34) powershell (34) data (34) tutorials (33) навыки (33) wordpress (33) dotnet (32) linux mint (31) kubernetes (31) события (31) api (31) marketing (31) windows (31) алиса (31) автоматизация (31) job hunting (30) bash programming (30) интервью с экспертами (30) apple (30) creativity (29) ios (29) user-research (28) без рубрики (28) c# (28)