Деплой инфраструктуры по модели gitops
Данная статья является описанием прохождения мной одноимённого курса на Практикуме. Курс хорошо составлен и продуман.
Перед началом прохождения данного курса необходимо выполнить некоторую подготовку.
- Зарегистрировать свой домен;
- Делегировать управление доменом Яндексу;
- Создать сеть для работы в облаке;
- Создать SA для кластера k8s;
- Создать ключ KMS для кластера k8s;
- Создать необходимые для работы переменные;
- Зарезервировать статический адрес для домена;
- Прописать необходимые записи в Cloud DNS;
- Выпустить для данного домена сертификат;
- Настроить SG для работы кластера k8s;
- Создать кластер k8s.
- Добавить из Marketplace k8s несколько дополнений.
Зарегистрируем свой домен на сайте https://www.freenom.com/ru/index.html?lang=ru Домен здесь можно зарегистрировать совершенно бесплатно. На сайте необходимо зарегистрироваться и добавить в корзину домен/домены.
Я буду использовать для выполнения работ домен cameda.ga.
Делегируем управление доменом Яндекс Облако.
Для делегирования домена в Яндекс необходимо прописать на сайте freenom NS записи облака.
Services-->My Domains-->Manage Domain (кнопка напротив выбранного домена)-->Management Tools-->Nameservers-->Use custom nameservers (enter below)
И вставляем адреса DNS серверов Яндекс Облака:
Сеть для облака создали здесь:
https://teletype.in/@cameda/ZbTkF70KWK9
SA для работы с кластером k8s создали здесь: https://teletype.in/@cameda/w93lPMPQWS4
Ключ KMS для шифрования секретов в кластере k8s создали здесь:
https://teletype.in/@cameda/vBIMDOZi7vV
Неоходимые для работы переменные.
export FOLDER=$(yc config get folder-id) export ZONE=ru-central1-a export NETWORK=$(yc vpc network get cameda-test --format json | jq -r '.id') export SUBNET=$(yc vpc subnet get subnet-a --format json | jq -r '.id') export SA=$(yc iam service-account get cameda-service --format json | jq -r '.id') export KMS=$(yc kms symmetric-key get k8s-key --format json | jq -r ".id")
Зарезервируем статический адрес для домена в подсети.
yc vpc address create \ --folder-id $FOLDER \ --name deploy_gitops \ --description "deploy gitops model" \ --labels test=deploy_gitops \ --external-ipv4 zone=$ZONE \ --async
export IP=$(yc vpc address get deploy_gitops --format=json | jq -r ".external_ipv4_address" | jq -r ".address")
Пропишем необходимые записи в Cloud DNS.
1. Но для начала надо создать публичную зону.
yc dns zone create \ --folder-id $FOLDER \ --name k8s-ingress \ --description "deploy gitops model" \ --zone cameda.ga. \ --public-visibility \ --async
2. Создадим А запись для домена. В качестве IP адреса будем использовать созданный ранее адрес.
yc dns zone add-records --name k8s-ingress \ --record "*.infra.cameda.ga. 600 A $IP"
3. Проверим, что всё правильно резолвит.
host test.infra.cameda.ga. test.infra.cameda.ga has address 158.160.37.229
yc certificate-manager certificate request \ --folder-id $FOLDER \ --name kube-infra \ --domains "*.infra.cameda.ga" \ --challenge dns
export CERT=$(yc cm certificate get kube-infra --format=json | jq -r ".id")
Добавим CNAME запись в домен для прохождения валидации.
yc dns zone add-records --name k8s-ingress --record \ "_acme-challenge.infra.cameda.ga. 600 CNAME $CERT.cm.yandexcloud.net."
Выпуск Wildcard сертификата может занять несколько часов!!!
Создадим правила SG для корректной работы кластера k8s.
yc vpc sg create --name k8s-sg \ "--rule" "description=access all egress port,direction=egress,from-port=1,to-port=65535,protocol=any,v4-cidrs=[0.0.0.0/0]" \ "--rule" "description=access 22 port,direction=ingress,port=22,protocol=tcp,v4-cidrs=[0.0.0.0/0]" \ "--rule" "description=access 443 port,direction=ingress,port=443,protocol=tcp,v4-cidrs=[0.0.0.0/0]" \ "--rule" "description=access 6443 port,direction=ingress,port=6443,protocol=tcp,v4-cidrs=[0.0.0.0/0]" \ "--rule" "description=balancer port,direction=ingress,from-port=1,to-port=65535,protocol=tcp,v4-cidrs=[198.18.235.0/24],v4-cidrs=[198.18.248.0/24]" \ "--rule" "description=service port,direction=ingress,from-port=30000,to-port=32767,protocol=tcp,v4-cidrs=[0.0.0.0/0]" \ "--rule" "description=ICMP,direction=ingress,protocol=icmp,v4-cidrs=[10.0.0.0/8],v4-cidrs=[192.168.0.0/16]" \ "--rule" "description=PODtoService,direction=ingress,from-port=1,to-port=65535,protocol=any,v4-cidrs=[10.95.0.0/16],v4-cidrs=[10.96.0.0/16]" \ "--rule" "description=Self,direction=ingress,from-port=1,to-port=65535,protocol=any,predefined=self_security_group" \ --network-id $NETWORK --description "k8s access" --folder-id $FOLDER --async
Здесь мы указываем сидры для работы подов: [10.97.0.0/16],v4-cidrs=[10.98.0.0/16]
При создании кластера укажем такие же.
export SG=$(yc vpc sg get k8s-sg --format json | jq -r '.id')
Создадим кластер k8s с CNI Calico, KMS ключем для шифрования данных и фиксированной нод группой. Версия кластера 1.21. Без обновлений.
yc k8s cluster create \ --folder-id $FOLDER \ --name cam-praktikum \ --description "cameda praktikum" \ --network-id $NETWORK \ --zone $ZONE \ --subnet-id $SUBNET \ --public-ip \ --release-channel stable \ --version 1.21 \ --cluster-ipv4-range 10.95.0.0/16 \ --service-ipv4-range 10.96.0.0/16 \ --auto-upgrade=false \ --security-group-ids $SG \ --enable-network-policy \ --node-service-account-id $SA \ --service-account-id $SA \ --kms-key-id $KMS \ --daily-maintenance-window start=22:00,duration=10h \ --async
Важно! Создание кластера может занимать до часа. После того как кластер будет создан, создаём нод группу.
Важно! Копируем публичный ключ id_rsa.pub в файл /Users/cameda/ssh-pairs.txt.
Создание динамической нод группы для кластера.
yc k8s node-group create \ --folder-id $FOLDER \ --name cam-praktikum-group \ --cluster-name cam-praktikum \ --description "praktikum" \ --metadata serial-port-enable=1 \ --metadata-from-file=ssh-keys=/Users/cameda/ssh-pairs.txt \ --location zone=$ZONE \ --platform standard-v3 \ --memory 8 \ --cores 4 \ --core-fraction 100 \ --disk-type network-ssd \ --disk-size 96 \ --network-acceleration-type standard \ --network-interface security-group-ids=$SG,subnets=$SUBNET,ipv4-address=nat \ --version 1.21 \ --container-runtime containerd \ --auto-scale min=0,max=2,initial=1 \ --auto-upgrade \ --auto-repair \ --max-expansion 1 \ --max-unavailable 1 \ --daily-maintenance-window start=22:00,duration=5h \ --allowed-unsafe-sysctls net.ipv4.tcp_timestamps \ --async
Важно! Создание нод группы также может занимать до часа.
Добавляем доп. приложения из Merketplace.
- NodeLocal DNS. По мануалу: https://teletype.in/@cameda/1GKt9Af0tBR
Или https://cloud.yandex.ru/docs/managed-kubernetes/operations/applications/node-local-dns#marketplace-install - ALB контроллер: https://teletype.in/@cameda/3n75CiuRGB-
Или https://cloud.yandex.ru/docs/managed-kubernetes/operations/applications/alb-ingress-controller#install-alb-marketplace
Заключительная часть подготовительного этапа.
Установим kubectl по мануалу: https://kubernetes.io/ru/docs/tasks/tools/install-kubectl/
yc managed-kubernetes cluster get-credentials --id Cluster_ID --external
Готово! Подготовка закончилась.
Создадим приложение в Deployment и получим к нему доступ извне.
kubectl create namespace httpbin
Добавим следующую спецификацию.
cat <<EOF | kubectl apply -f - apiVersion: apps/v1 kind: Deployment metadata: name: httpbin namespace: httpbin labels: app: httpbin spec: replicas: 1 selector: matchLabels: app: httpbin template: metadata: labels: app: httpbin spec: containers: - name: httpbin image: kennethreitz/httpbin:latest ports: - name: http containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: httpbin namespace: httpbin spec: type: NodePort selector: app: httpbin ports: - name: http port: 80 targetPort: 80 protocol: TCP nodePort: 30081 --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: httpbin namespace: httpbin annotations: ingress.alb.yc.io/subnets: e9b16nenl5a52h6o9djb ingress.alb.yc.io/external-ipv4-address: 158.160.37.229 ingress.alb.yc.io/group-name: infra-alb ingress.alb.yc.io/security-groups: enpauibqifvmvss6balo spec: rules: - host: httpbin.infra.cameda.ga http: paths: - path: / pathType: Prefix backend: service: name: httpbin port: number: 80 EOF
При применении данного манифеста будет создан балансировщик на базе ALB. Это мжет занять минут 10-15. Далее добавим поддержку TLS.
cat <<EOF | kubectl apply -f - apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: httpbin namespace: httpbin annotations: ingress.alb.yc.io/subnets: e9b16nenl5a52h6o9djb ingress.alb.yc.io/external-ipv4-address: 158.160.37.229 ingress.alb.yc.io/group-name: infra-alb ingress.alb.yc.io/security-groups: enpauibqifvmvss6balo spec: tls: - hosts: - "httpbin.infra.cameda.ga" secretName: yc-certmgr-cert-id-fpqepq5cbt3q7548a2nj rules: - host: httpbin.infra.cameda.ga http: paths: - path: / pathType: Prefix backend: service: name: httpbin port: number: 80 EOF
При применении данного манифеста может не добавиться http роутер в балансировщик. В этом случае его необходимо вручную модифицировать обработчик. Надо прописать в одном из двух обработчиков поддержку HTTPS, сертификат и роутер с поддержкой TLS. После этого httpbin.infra.cameda.ga должен будет открыться.
Также ещё сталкивался с тем, что при применении манифеста не создался балансировщик. Я просто удалил ingress и создал его заново.
Важно! Сначала применяем ingress без TLS, потом с ним. Это уменьшает риск ошибок.
curl -I httpbin.infra.cameda.ga HTTP/1.1 301 Moved Permanently location: https://httpbin.infra.cameda.ga:443/ date: Thu, 17 Nov 2022 17:01:23 GMT server: ycalb transfer-encoding: chunked
Установка GitLab, GitLab Runner и интеграция их с k8s.
1. Установить GitLab можно только через UI облака. Это можно сделать по инструкции:
https://cloud.yandex.ru/docs/managed-gitlab/operations/instance/instance-create
2. После создания подключимся к инстансу:
https://cloud.yandex.ru/docs/managed-gitlab/operations/connect
4. Settings→CI/CD→Runners->Expand. Здесь нам потребуются URL, registration token.
5. Установим GitLab Runner в кластер k8s.
В поле "Имя домена GitLab" вставляем URL нашего GitLab, полученный на 4 этапе.
В поле "Регистрационный токен" вставляем token нашего GitLab, полученный на 4 этапе.
В поле "Пространство имён" выбираем kube-system.
6. Repository->Files. Создаём новый файл (+->New File). Вставляем имя - .gitlab-ci.yml
stages: - echo echo job: stage: echo script: - echo "Hello world!"
Применяем изменения. Commit changes. Для этого мотаем экран вниз.
7. CI/CD->Pipelines. Смотрим статус нашего пацплайна. Если passed, то всё корректно отработало.
Проверим. Нажимаем на passed->Jobs->passed.
8. Создадим две директории в меню File: charts, values. Также создадим ещё два файла:
8.1 .gitignore
— игнорируемые Git файлы
8.2 commands.sh
— файл для сохранения команд, которые вводим вручную, необходимых для развертывания
Развернём ArgoCD.
export HELM_EXPERIMENTAL_OCI=1 helm pull oci://cr.yandex/yc-marketplace/yandex-cloud/argo/chart/argo-cd \ --untar --untardir=charts --version=4.5.3-1
helm install -n argocd \ --create-namespace \ argocd charts/argo-cd
kubectl port-forward svc/argo-cd-argocd-server -n kube-system 8080:443
После установки заходим в приложение: http://localhost:8080
Логин: admin
Password получаем с помощью команды:
kubectl -n kube-system get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d
Настраиваем связку GitLab+ArgoCD.
Заходим в GitLab. Settings->Access Tokens. Ввоим данные:
- Имя токена (argo-cd);
- Если не указывать срок жизни токена, то он выпустится на бессрочной основе;
- Роль - Developer;
- Scopes - read_repository.
touch values/argocd.yaml vi values/argocd.yaml
configs: repositories: infra: password: <AccessToken> project: default type: git url: https://cameda3.gitlab.yandexcloud.net/gitlab_test/infra.git username: gitlab-ci-token
<AccessToken> - находится в профиле пользователя в разделе AccessToken.
helm -n argocd upgrade --install \ argocd \ charts/argo-cd \ -f values/argocd.yaml
Проверяем, что апгрейд применился:
kubectl -n argocd get secret argocd-repo-infra NAME TYPE DATA AGE argocd-repo-infra Opaque 5 4m34s
Да, всё ОК. Раз секрет есть, значит ОК.
Управление секретами в Git репозитории.
Чтобы скрывать пароли из Git-репозитория, подключим инструмент Helm Secrets. Он позволяет зашифровывать чувствительную информацию с помощью различных методов и расшифровывать при применении.
helm plugin install https://github.com/jkroepke/helm-secrets --version v3.12.0 brew install age
{apt,yum,brew} install golang echo 'export GOPATH=~/go' >> ~/.bashrc source ~/.bashrc mkdir $GOPATH/src/go.mozilla.org/sops/
git clone https://github.com/mozilla/sops.git $GOPATH/src/go.mozilla.org/sops/ cd $GOPATH/src/go.mozilla.org/sops/ git checkout develop make install
age-keygen -o key.txt
helm secrets enc values/argocd.yaml