Thank you for reading this post, don't forget to subscribe!
рассмотрим как на bare metal поднять кубер со всей обвязкой - статья будет масштабной.
что нужно подготовить: несколько серверов на debian
устанавливаете себе ansible выкачиваете следующую репку:
git clone https://github.com/midnight47/ansible-playbook.ginerdctlt
я её разместил в /etc/ansible
cd /etc/ansible/
cat hosts
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
[all_servers] 192.168.1.100 # freeipa1 192.168.1.101 # freeipa2 192.168.1.102 # nexus 192.168.1.103 # vault1 192.168.1.104 # vault2 192.168.1.105 # vault3 192.168.1.106 # gitlab 192.168.1.107 # gitlab-runner 192.168.1.108 # nfs 192.168.1.109 # gluster-fs1 192.168.1.110 # gluster-fs2 192.168.1.118 # s3-minio1 192.168.1.119 # s3-minio2 |
первым делом я прогоняю роль по установке базовых пакетов / пользователей
[root@ansible ansible]# ansible-playbook -u root /etc/ansible/playbooks/roles_play/new_server.yml --ask-pass
1.Установка Freeipa
2.Установка Nexus
3.Установка Vault
4. Установка gitlab / gitlab-runner(host)
5. Установка NFS
6. Установка Glusterfs
7. Установка s3-minio
9.Интеграция Freeipa
9.1 Freeipa -> Nexus
9.2 Freeipa -> Gitlab
9.3 Freeipa -> Vault
9.4 Freeipa -> S3-Minio
10. Установка kubernetes (kubespray - official)
10.1 Доступ с локального PC до кластера
11 Установка дополнительных компонентов
11.1 Ingress
11.1.1 ingress helm installation (предпочтительный вариант)
11.2 Metallb
11.2.1 Metallb helm-installation (предпочтительный вариант)
11.3 NFS provisioner
11.4 GlusterFS provisioner
11.5 Seaweedfs provisioner (есть проблемы с fuse chown, sync victoria-metrics не поднялась)
11.6 monitoring - Prometheus, grafana, alertmanager
11.7 monitoring - Victoria-metrics, grafana, alertmanager (предпочтительный вариант)
11.7.1 Exporter on node not in the k8s
11.7.2 VMServiceScrape - for ingress controller
11.8 Metrics servers
11.9 Правим COREDNS и LOCALDNS чтобы работал resolv.
11.10 Log - elk
11.11 Log - loki
11.11.1 Log - loki (backend s3-minio без Freeipa - LDAP)
11.11.2 Log - loki (backend s3-minio c Freeipa - LDAP)
11.11.3 Log - Loki - s3 bucket (seaweedfs)
11.11.4 Promail (сборщик логов)
11.11.5 Интеграция grafana с loki
12 Vault - auto unseal
12.1.1 Установка vault в k8s
12.1.2 Установка autounseal для vault в k8s через cronjob
12.1.3 Настройка tranzit autounseal vault на физических серверах
12.1.4 обновить токен для распечатывая основного vault
12.2 Интеграция vault и k8s "Vault Secrets Operator"
12.3 Пример с autoreloader после изменения секрета в vault
13 Аутентификация, авторизация в k8s (SSO в kubernetes через Freeipa)
13.1 Loft (мне не очень понравилось, не рекомендую)
13.2 Dex - dexK8sAuthenticator (нормально работает - дёшево и сердито)
13.3 Rancher (лучше ставить на отдельной виртуалке а не в кластере, много функционала)
13.3.1 Rancher интеграция с Freeipa
13.3.2 Rancher подключение к k8s кластеру
13.3.3 Rancher проверка авторизации для пользователей k8s
13.4 Keycloak (сложная штука, у меня толком с ней ничего не завелось)
13.4.1 Интеграция keycloak c Freeipa
13.4.2 Настройка подключения к k8s
13.5 Teleport
13.5.1 установка teleport
13.5.2 Подключение кластера Kubernetes к Teleport
13.5.3 интеграция teleport - keycloak
14.0.0 Обновление кластера k8s
14.0.1 update 1.24->1.25
14.0.2 update 1.25->1.26
14.0.3 update 1.26->1.27
14.0.4 update 1.27->1.28
14.0.5 update 1.28->1.29
14.0.6 update 1.29->1.30
14.0.6 update 1.30->1.31
14.0.7 update 1.31->1.32
14.0.8 update 1.32.5 - > 1.32.9 (пока писал статью новые версии вышли)
14.0.9 update 1.32.9 - > 1.33.5 (install PLUTO)
14.0.10 update 1.33.5 -> 1.34.1
15.1 Gitlab in k8s (helm chart)
15.2 Gitlab runner in k8s
15.3 Gitlab helm chart с Freeipa
16.1 Резервное копирование etcd в hostPath
16.2 Резервное копирование etcd в s3-minio
16.3 Резервное копирование etcd в s3-minio (LDAP enabled)
17 Cert-manager (self-signed - самоподписанный)
18 Argocd
18.1 Argocd add user
18.2 Argocd интеграция с Freeipa
18.3.1 Argocd создание проекта, настройка деплоя
18.3.2 Argocd создание проекта, настройка деплоя Helm
18.3.3 Argocd создание проекта из конфиг файла
18.3.4 Argocd создание проекта. настройка деплоя Helm когда values и chart в разных репозиториях
19 Keda
20 Patrony (кластер для postgresql)
21 Helm-chart
21.1 Создание дефолтного чарта
21.2 Добавление секретов из vault
21.3 Добавление Topology Spread Constraints
21.4 Добавление RollingUpdate
21.5 Примеры использования affinity/anti-affinity, nodeSelector, taint/tolerations
21.6 Добавление PodDisruptionBudget
21.7 Ingress+ cert-manager+resources
21.8 Проверим HPA (HorizontalPodAutoscaler)
21.9 Добавим Keda (есть примеры с логикой скейлинга ИЛИ / И)
21.9.1 Скейлинг с логикой ИЛИ
21.9.2 Скейлинг с логикой И
21.10 Контейнеры в POD
21.10.1 обычные контейнеры (sidecar)
21.10.2 init контейнеры
21.10.3 ephemeral Containers (debug) контейнеры
21.11 Configmap (делаем связку nginx->php-fpm)
21.12 volume/ephemeral volume/emptyDir
21.13 job и cronjob
21.14 probe grpc tcp http
21.15 network policy
21.16 Canary/Blue-green deployment
21.16.1 Blue-Green
21.16.2 Canary
22 Pod priority class
22.1 add priorityClassName promtail
22.2 add priorityClassName vault-csi-provider
22.3 add priorityClassName ingress-nginx-controller
23 проверим VPA vertical-pod-autoscaler
23.1 изменим дефолтное значение реплик 2 на 1 для VPA updater
24 namespace limitrange (ограничения для ресурсов на уровне namespace)
25 istio+kiali (service mesh)
25.1 установка istio - используем sidecar
25.2 установка kiali
26.0.1 Замена nginx ingress controller(Envoy Gateway)
26.0.2 Установка Envoy Gateway
26.1 Helm chart - gateway вместо ingress
27 Переезд на новую операционку debian 13
27.1 проблема с переездом kub-master1 etcd
27.2 проблема с переездом kub-master1 сертификаты
27.3 проблемы с переездом kub-master1 локальный kubctl
27.4 проблемы с переездом kub-master1 scheduller
27.5 проблемы с переездом - worker node а именно - cluster-info
27.6 быстрая диагностика проблем
27.7 переезд worker-node
28.1 Резервное копирование etcd в hostPath (без bitnami)
28.2 Резервное копирование etcd в s3-minio (без bitnami)
29.0 Обновление кластера k8s после переезда на debian 13
29.1 update v1.34.1 -> 1.34.6
29.2 update v1.34.6 -> 1.35.4
29.3 update v1.35.4 -> 1.36.2
Установка Freeipa
У меня 2 сервера CENTOS 7 с параметрами
2 ядра 4 гб оперативки - этого хватает на процесс установки - меньше оперативки я бы не ставил так как может прийти OOMkill после того как вся установка пройдёт можно и понизить ресурсы до 1 ядра и 2гб оперативки
Подготавливаем для freeipa inventory файл
cd /etc/ansible/
cat hosts
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
[freeipa:children] ipa_servers ipa_replicas ipa_clients [ipa_servers] freeipa-1.test.local ansible_host=192.168.1.100 [ipa_replicas] freeipa-2.test.local ansible_host=192.168.1.101 [ipa_clients] nexus.test.local ansible_host=192.168.1.102 vault1.test.local ansible_host=192.168.1.103 vault2.test.local ansible_host=192.168.1.104 vault3.test.local ansible_host=192.168.1.105 gitlab.test.local ansible_host=192.168.1.106 gitlab-runner.test.local ansible_host=192.168.1.107 nfs.test.local ansible_host=192.168.1.108 gluster-fs1.test.local ansible_host=192.168.1.109 gluster-fs2.test.local ansible_host=192.168.1.110 |
у нас будет 2 сервера freeipa для отказоустойчивости на них будет dns сервер отвечающий за зону test.local
cat /etc/ansible/playbooks/roles_play/freeipa.yaml
|
1 2 3 4 5 6 7 8 9 10 11 12 |
--- - hosts: freeipa become: yes vars: domain: test.local realm: TEST.LOCAL admin_password: Secret123 ds_password: Secret123 master_password: Secret123 roles: - freeipa_setup |
в данном файле мы обязательно задаём домен test.local и пароль Secret123
запускаем установку:
[root@ansible ansible]# ansible-playbook -u root /etc/ansible/playbooks/roles_play/freeipa.yaml --ask-pass
дожидаемся окончания и проверяем работу:
https://freeipa-1.test.local/
после можем снизить параметры сервера до 1 ядра и 2гб оперативки
Установка Nexus
для ноды с nexus нужно минимум 1 ядро и 2гб оперативки
на сервере ansible ставим:
переходим в директорию:
cd /etc/ansible/
cat hosts
|
1 2 3 |
[nexus] nexus.test.local ansible_host=192.168.1.102 |
указываем пароль и домен:
- domain: nexus.test.local
- password: Secret123
cat /etc/ansible/playbooks/roles_play/nexus.yml
|
1 2 3 4 5 6 7 8 9 10 11 12 |
--- - hosts: nexus become: true ignore_errors: yes become_method: sudo gather_facts: yes vars: - domain: nexus.test.local - password: Secret123 roles: - nexus |
[root@ansible ansible]# ansible-playbook -u root /etc/ansible/playbooks/roles_play/nexus.yml --ask-pass
дожидаемся окончания установки и проверяем работу:
http://nexus.test.local:8081/
Установка vault
cd /etc/ansible/
cat hosts
|
1 2 3 4 5 6 7 8 |
[vault] vault1.test.local ansible_host=192.168.1.103 vault2.test.local ansible_host=192.168.1.104 vault3.test.local ansible_host=192.168.1.105 [vault:vars] virtual_hostname=vault.test.local virtual_address=192.168.1.111 |
в файле
/etc/ansible/roles/vault/defaults/main.yaml
выставляем параметры для сертификата который будет сгенерен и какие там будут домены
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
vault_certificate_vars: country_name: RU locality_name: Some Country organization_name: Test email_address: master@test.local common_name: test.local subject_alt_name: - DNS:*.test.local - DNS:*.dev.test.local - DNS:*.staging.test.local - DNS:*.prod.test.local - DNS:*.infra.test.local - DNS:vault.test.local - DNS:vault1.test.local - DNS:vault2.test.local - DNS:vault3.test.local - IP:127.0.0.1 |
запускаем установку:
[root@ansible ansible]# ansible-playbook -u root /etc/ansible/playbooks/roles_play/vault.yml --ask-pass
как и писал выше ключи вот тут:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
[root@ansible ansible]# cat roles/vault/unseal_tmp/rootkey hvs.UuG0QJvRRfwUHUTGxDTjFaAd [root@ansible ansible]# [root@ansible ansible]# cat roles/vault/unseal_tmp/unseal_key_0 fab3991ca411bf9a7bfef1d99131a983ce1906b10f6e47cbdc8da7e44f0b3a3a3e [root@ansible ansible]# [root@ansible ansible]# cat roles/vault/unseal_tmp/unseal_key_1 7540f4e2e6084813fd23ad98a00257501e6a8be27dbabf2664b1f807a71d1bf9a3 [root@ansible ansible]# [root@ansible ansible]# cat roles/vault/unseal_tmp/unseal_key_2 261be9452bc5abbe07942e00d5f3d1adfeb705e6a24733ae9fed514f22b81e6a82 [root@ansible ansible]# [root@ansible ansible]# cat roles/vault/unseal_tmp/unseal_key_3 c0f1027f89631c403814321e3ad764407e3c204a091f0a30ccc9d1e3bb373d2ca4 [root@ansible ansible]# [root@ansible ansible]# cat roles/vault/unseal_tmp/unseal_key_4 9d8faf0c33a784f7019ab24685e22f9f146c7091674bd868f730993bb02c6c7476 [root@ansible ansible]# |
Установка gitlab / gitlab-runner(host)
по ресурсам нужно для установки
4 ядра 4 оперативки
20 гб диска
/etc/ansible/hosts
|
1 2 3 4 5 6 7 8 |
[gitlab:children] gitlab-server gitlab-runner [gitlab-server] gitlab.test.local ansible_host=192.168.1.106 [gitlab-runner] gitlab-runner.test.local ansible_host=192.168.1.107 |
/etc/ansible/playbooks/roles_play/gitlab.yml
если хотим раннер хостовой то ставим переменную в true
|
1 2 |
vars: - gitlabrunner: true |
запускаем установку:
ansible-playbook -u root /etc/ansible/playbooks/roles_play/gitlab.yml --ask-pass
дожидаемся конца установки.
в самом конце ансибл покажет пароль:
|
1 2 3 4 5 6 |
TASK [gitlab-gitlab-runner : gitlab show PASSWORD] **************************************************************************************************************************************************************** ok: [gitlab.test.local] => { "gitlab_password.stdout_lines": [ "Password: Ukhr+Al9mi9SutCfItn05Q+MtsYLM9HZ7OkoxpUBxk8=" ] } |
проверяем:
http://gitlab.test.local/
логин: root
пароль: Ukhr+Al9mi9SutCfItn05Q+MtsYLM9HZ7OkoxpUBxk8=
заходим и меняем пароль. я меняю на Secret123
готово, после установки можем откатить ресурсы на 2 ядра и 3,5 гб оперативки
Установка NFS
по ресурсам хватит 1 ядро и 512 оперативки
можем поменять директорию в которой будут хранится данные на nfs сервере, а на клиенте к какой директории будет подключено хранилище, или вообще не создавать, если хотим использовать как провижинер на k8s:
/etc/ansible/playbooks/roles_play/nfs.yml
|
1 2 3 4 |
vars: - dir_nfs_master: /nfs - dir_nfs_client: /nfs-client - k8s_nfs_provision: false # if true nfs client directory will not create |
в моём примере на клиентах я буду создавать директории к которым подключено хранилище.
/etc/ansible/hosts
|
1 2 3 4 5 6 7 8 |
[nfs:children] nfsmaster nfsclient [nfsmaster] nfs.test.local ansible_host=192.168.1.108 [nfsclient] 192.168.1.100 192.168.1.101 |
ansible-playbook -u root /etc/ansible/playbooks/roles_play/nfs.yml --ask-pass
дожидаемся окончания и проверяем:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
[root@ansible ansible]# ssh 192.168.1.100 root@192.168.1.100's password: [root@freeipa-1 ~]# touch /nfs-client/test-file [root@freeipa-1 ~]# logout Connection to 192.168.1.100 closed. [root@ansible ansible]# ssh 192.168.1.108 root@192.168.1.108's password: root@nfs:~# ls -lah /nfs/ total 8.0K drwxr-xr-x 2 root root 4.0K Jun 22 10:00 . drwxr-xr-x 19 root root 4.0K Jun 22 09:53 .. -rw-r--r-- 1 root root 0 Jun 22 10:00 test-file root@nfs:~# |
Установка GLUSTERFS
можем задать директории для сервера и клиентов и имя для glusterfs tom:
/etc/ansible/playbooks/roles_play/glusterfs.yml
|
1 2 3 4 |
vars: - dir_gluster_master: /gluster - dir_gluster_client: /gluster-client - name_of_gluster_tom: gluster-tom |
/etc/ansible/hosts
|
1 2 3 4 5 6 7 8 9 10 |
[glusterfs:children] glustermaster glusterclient [glustermaster] gluster-fs1.test.local ansible_host=192.168.1.109 gluster-fs2.test.local ansible_host=192.168.1.110 [glusterclient] 192.168.1.100 192.168.1.101 |
если нужно добавить больше серверов то просто докидывайте в glustermaster. Репликафактор равен 2 всегда. Изменить можно тут:
/etc/ansible/roles/glusterfs/tasks/add-tom.yml
|
1 2 |
replicas: 2 |
запускаем установку:
ansible-playbook -u root /etc/ansible/playbooks/roles_play/glusterfs.yml --ask-pass
после первого прохождения запустите ещё раз, - возможны проблемы при первом проходе
Дальше проверяем:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
root@gluster-fs1:~# gluster peer status Number of Peers: 1 Hostname: gluster-fs2.test.local Uuid: 845b3a30-c48c-4048-b851-2c92c162f9fe State: Peer in Cluster (Connected) root@gluster-fs1:~# gluster volume info Volume Name: gluster-tom Type: Replicate Volume ID: ad32a409-1b0f-4bd6-9420-acd93b0e1a14 Status: Started Snapshot Count: 0 Number of Bricks: 1 x 2 = 2 Transport-type: tcp Bricks: Brick1: gluster-fs1.test.local:/gluster/gv01 Brick2: gluster-fs2.test.local:/gluster/gv01 Options Reconfigured: cluster.granular-entry-heal: on storage.fips-mode-rchecksum: on transport.address-family: inet nfs.disable: on performance.client-io-threads: off root@gluster-fs1:~# gluster volume status Status of volume: gluster-tom Gluster process TCP Port RDMA Port Online Pid ------------------------------------------------------------------------------ Brick gluster-fs1.test.local:/gluster/gv01 49450 0 Y 615 Brick gluster-fs2.test.local:/gluster/gv01 49701 0 Y 9420 Self-heal Daemon on localhost N/A N/A Y 708 Self-heal Daemon on gluster-fs2.test.local N/A N/A Y 9437 Task Status of Volume gluster-tom ------------------------------------------------------------------------------ There are no active volume tasks |
|
1 2 3 4 |
[root@ansible ansible]# ssh 192.168.1.100 root@192.168.1.100's password: [root@freeipa-1 ~]# touch /gluster-client/test_file |
и на сервере
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
root@gluster-fs1:~# ls -lah /gluster/gv01/ total 24K drwxr-xr-x 4 root root 4.0K Jun 22 12:46 . drwxr-xr-x 3 root root 4.0K Jun 22 12:28 .. drw------- 262 root root 4.0K Jun 22 12:31 .glusterfs drwxr-xr-x 2 root root 4.0K Jun 22 12:31 .glusterfs-anonymous-inode-ad32a409-1b0f-4bd6-9420-acd93b0e1a14 -rw-r--r-- 2 root root 0 Jun 22 12:46 test_file root@gluster-fs1:~# ls -lah /gluster-client/ total 8.0K drwxr-xr-x 4 root root 4.0K Jun 22 12:46 . drwxr-xr-x 20 root root 4.0K Jun 22 12:28 .. -rw-r--r-- 1 root root 0 Jun 22 12:46 test_file |
любой из серверов можно спокойно выключать, доступ настроен через fstab
|
1 2 |
root@gluster-fs1:~# cat /etc/fstab | grep gluster gluster-fs1.test.local:/gluster-tom,gluster-fs2.test.local:/gluster-tom /gluster-client glusterfs defaults,_netdev 0 0 |
Установка S3-minio
Установим хранилище S3 minio в виде кластера чтобы была репликация как бакетов так и пользователей с ролями, + у нас будет виртуальный IP чтобы мы ходили по одному и тому же адресу.
Приступим:
подготовим 2 сервера debian 12 1CPU 1RAM и нужно будет добавить туда по выделенную разделу , т.е. просто указать директорию не прокатит.
ip будут
192.168.1.119 # s3-minio2
/etc/ansible/hosts
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
[all_servers] 192.168.1.100 # freeipa1 192.168.1.101 # freeipa2 192.168.1.102 # nexus 192.168.1.103 # vault1 192.168.1.104 # vault2 192.168.1.105 # vault3 192.168.1.106 # gitlab 192.168.1.107 # gitlab-runner 192.168.1.108 # nfs 192.168.1.109 # gluster-fs1 192.168.1.110 # gluster-fs2 192.168.1.118 # s3-minio1 192.168.1.119 # s3-minio2 |
после этого настраиваем на этих серверах доп диск у меня он будет на LVM
|
1 2 3 4 5 6 7 8 9 10 |
root@debian:~# pvscan PV /dev/sda5 VG debian-vg lvm2 [<9.52 GiB / 24.00 MiB free] Total: 1 [<9.52 GiB] / in use: 1 [<9.52 GiB] / in no VG: 0 [0 ] root@debian:~# vgscan Found volume group "debian-vg" using metadata type lvm2 root@debian:~# lvscan ACTIVE '/dev/debian-vg/root' [8.54 GiB] inherit ACTIVE '/dev/debian-vg/swap_1' [976.00 MiB] inherit |
|
1 |
pvcreate /dev/sdb && vgextend debian-vg /dev/sdb && lvcreate --name minio -L 10g debian-vg && mkfs.ext4 /dev/mapper/debian--vg-minio && mkdir /minio && mount /dev/debian-vg/minio /minio |
Этой командой
создаём PV pvcreate /dev/sdb
расширяем группу vgextend debian-vg /dev/sdb
создаём том на 10GB lvcreate --name minio -L 10g debian-vg
форматриуем том в нужную файловую систему mkfs.ext4 /dev/mapper/debian--vg-minio
создаём директорию в которую будем монтировать том mkdir /minio
монтируем том mount /dev/debian-vg/minio /minio
далее добавим в fstab это
|
1 |
echo "/dev/mapper/debian--vg-minio /minio ext4 errors=remount-ro 0 1" >> /etc/fstab |
проверяем
root@debian:~# echo "/dev/mapper/debian--vg-minio /minio ext4 errors=remount-ro 0 1" >> /etc/fstab
root@debian:~# reboot
|
1 2 3 4 5 6 7 8 9 10 |
root@debian:~# df -h Filesystem Size Used Avail Use% Mounted on udev 445M 0 445M 0% /dev tmpfs 94M 568K 94M 1% /run /dev/mapper/debian--vg-root 8.4G 2.3G 5.7G 29% / tmpfs 469M 0 469M 0% /dev/shm tmpfs 5.0M 0 5.0M 0% /run/lock /dev/sda1 455M 147M 284M 35% /boot /dev/mapper/debian--vg-minio 9.8G 24K 9.3G 1% /minio tmpfs 94M 0 94M 0% /run/user/0 |
на втором сервере делаем тоже самое:
|
1 2 3 |
root@debian:~# pvcreate /dev/sdb && vgextend debian-vg /dev/sdb && lvcreate --name minio -L 10g debian-vg && mkfs.ext4 /dev/mapper/debian--vg-minio && mkdir /minio && mount /dev/debian-vg/minio /minio root@debian:~# echo "/dev/mapper/debian--vg-minio /minio ext4 errors=remount-ro 0 1" >> /etc/fstab |
ок сервера подготовили теперь настроим роль:
/etc/ansible/hosts
|
1 2 3 4 5 6 |
[minio] s3-minio-1.test.local ansible_host=192.168.1.118 s3-minio-2.test.local ansible_host=192.168.1.119 [minio:vars] virtual_address=192.168.1.120 |
/etc/ansible/roles/minio/defaults/main.yml
|
1 2 3 4 5 6 7 8 9 10 11 |
keepalived: true minio_server_datadirs: - /minio minio_server_cluster_nodes: - s3-minio-1.test.local - s3-minio-2.test.local minio_root_user: "admin" minio_root_password: "Secret123" |
указываем что мы будем использовать виртуальный IP keepalived: true
указываем наш LVM том minio_server_datadirs
указываем имена наших s3 нод minio_server_cluster_nodes отмечу что эти имена должны совпадать с теми что указаны в /etc/ansible/hosts
указываем рутовый логин minio_root_user
указываем рутовый пароль minio_root_password
готового можно запускать установку:
[root@ansible ansible]# ansible-playbook -u root /etc/ansible/playbooks/roles_play/s3-minio.yml --ask-pass
ждём окончания и проверяем:
сначала первый сервер:
http://s3-minio-1.test.local:9001

вводим наши
admin
Secret123
Создаём бакет

создаём пользователя:

проверяем на втором сервере:
http://s3-minio-2.test.local:9001/login

как видим на втором сервере и бакет и пользователь доступны - репликация работает, всё ок
Интеграция FREEIPA
Для начала создадим группу серверов группы пользователей для сервисов
создаём следующие группы пользователей:

добавим 2 пользователя
user1 - будет админом
user2 - будет обычным пользователем

теперь добавим их по соответствующим группам


так же раскидываем по остальным группам
gitlab, nexus-admins, vault-admins - user1
gitlab. nexus-ro-users, vault-ro-users - user2
Freeipa -> Nexus
[root@freeipa-1 ~]# ipa cert-show 1 --out=/tmp/ipa-ca.crt
[root@freeipa-1 ~]# scp /tmp/ipa-ca.crt root@192.168.1.102:/tmp/
root@nexus:~# find / -name jre
/usr/lib/jvm/jdk1.8.0_371/jre/bin
root@nexus:/usr/lib/jvm/jdk1.8.0_371/jre/bin# ./keytool -import -trustcacerts -alias freeipa-ca -file /tmp/ipa-ca.crt -keystore $JAVA_HOME/jre/lib/security/cacerts -storepass changeit
Trust this certificate? [no]: yes
Certificate was added to keystore
Далее нам нужно создать системного пользователя во freeipa с помощью которого nexus сможет ходить в api и читать имена пользователей, но у него не будет прав что то выполнить. для этого на сервере freeipa или его реплике заходим в директорию /etc/ipa и запускаем скрипт. или выкачиваем тут:
https://github.com/noahbliss/freeipa-sam
[root@freeipa-1 ipa]# cd /etc/ipa
[root@freeipa-1 ipa]# bash freeipa-sam.sh
нажимаем 1 и задаём имя нашего сервера:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
### FreeIPA - System Account Manager ### 1.) ldapserver= 2.) domain= (ldapdomain=) 3.) binduser= 4.) bindpass=UNSET! 5.) ssl=true Actions (conditions not yet met): add | rm | ls | info | passwd | save --- Results --- --- End Results --- > 1 ldapserver=freeipa-1.test.local |
нажимаем enter и получаем результат:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
### FreeIPA - System Account Manager ### 1.) ldapserver=freeipa-1.test.local 2.) domain=test.local(ldapdomain=dc=test,dc=local) 3.) binduser= 4.) bindpass=UNSET! 5.) ssl=true Actions (conditions not yet met): add | rm | ls | info | passwd | save --- Results --- --- End Results --- > 3 |
как видим далее мы нажали 3
выбираем любого пользователя с админскими правами - я выбрал admin
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
### FreeIPA - System Account Manager ### 1.) ldapserver=freeipa-1.test.local 2.) domain=test.local(ldapdomain=dc=test,dc=local) 3.) binduser= 4.) bindpass=UNSET! 5.) ssl=true Actions (conditions not yet met): add | rm | ls | info | passwd | save --- Results --- --- End Results --- > 3 Enter "mgr" for Directory Manager. Otherwise enter the username or full binddn (-D option in ldapsearch) binduser=admin |
Далее выбираем 4 и нужно будет ввести пароль
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
### FreeIPA - System Account Manager ### 1.) ldapserver=freeipa-1.test.local 2.) domain=test.local(ldapdomain=dc=test,dc=local) 3.) binduser=uid=admin,cn=users,cn=accounts,dc=test,dc=local 4.) bindpass=UNSET! 5.) ssl=true Actions (conditions not yet met): add | rm | ls | info | passwd | save --- Results --- --- End Results --- > 4 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
### FreeIPA - System Account Manager ### 1.) ldapserver=freeipa-1.test.local 2.) domain=test.local(ldapdomain=dc=test,dc=local) 3.) binduser=uid=admin,cn=users,cn=accounts,dc=test,dc=local 4.) bindpass=UNSET! 5.) ssl=true Actions (conditions not yet met): add | rm | ls | info | passwd | save --- Results --- --- End Results --- > 4 Enter password (will not echo): |
после выбираем 5 чтоб отключить SSL
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
### FreeIPA - System Account Manager ### 1.) ldapserver=freeipa-1.test.local 2.) domain=test.local (ldapdomain=dc=test,dc=local) 3.) binduser=uid=admin,cn=users,cn=accounts,dc=test,dc=local 4.) bindpass=SET! 5.) ssl=false Actions (ready): add | rm | ls | info | passwd | save --- Results --- --- End Results --- > |
как видим ssl=true поменялось на ssl=false
жмём enter и после можем создавать системного пользователя nexus для этого набираем команду add
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
### FreeIPA - System Account Manager ### 1.) ldapserver=freeipa-1.test.local 2.) domain=test.local(ldapdomain=dc=test,dc=local) 3.) binduser=uid=admin,cn=users,cn=accounts,dc=test,dc=local 4.) bindpass=SET! 5.) ssl=true Actions (ready): add | rm | ls | info | passwd | save --- Results --- --- End Results --- > add uid of new user=nexus |
далее вводим пароль и нас попросят указать дату окончания работы этого пароля, там ничего не указываем - просто жмём ENTER
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
### FreeIPA - System Account Manager ### 1.) ldapserver=freeipa-1.test.local 2.) domain=test.local(ldapdomain=dc=test,dc=local) 3.) binduser=uid=admin,cn=users,cn=accounts,dc=test,dc=local 4.) bindpass=SET! 5.) ssl=true Actions (ready): add | rm | ls | info | passwd | save --- Results --- --- End Results --- > add uid of new user=nexus password of new user (blank to generate a password)= password expiration date YYYYMMDD (blank for 20380119)= |
командой ls можем проверить созданного пользователя
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
### FreeIPA - System Account Manager ### 1.) ldapserver=freeipa-1.test.local 2.) domain=test.local (ldapdomain=dc=test,dc=local) 3.) binduser=uid=admin,cn=users,cn=accounts,dc=test,dc=local 4.) bindpass=SET! 5.) ssl=false Actions (ready): add | rm | ls | info | passwd | save --- Results --- dn: uid=sudo,cn=sysaccounts,cn=etc,dc=test,dc=local dn: uid=nexus,cn=sysaccounts,cn=etc,dc=test,dc=local --- End Results --- > |
пароль я кстати задал Secret123
логинимся в nexus и переходим в настройки LDAP

для заполнения используем данные
uid=nexus,cn=sysaccounts,cn=etc,dc=test,dc=local

проверяем и нажимаем NEXT

далее настраиваем
User relative DN: cn=users,cn=accounts
User subtree: устанавливаем галку.
Object class: inetOrgPerson
User filter: (memberOf=cn=nexus-admins,cn=groups,cn=accounts,dc=test,dc=local)
User ID attribute: uid
Real name attribute: cn
Email attribute: mail
Password attribute: оставляем пустым.
Map LDAP groups as roles: устанавливаем галку.
Group type: Static Groups
Group relative DN: cn=groups,cn=accounts
Group subtree: устанавливаем галку.
Group object class: groupOfNames
Group ID attribute: cn
Group member attribute: member
Group member format: uid=${username},cn=users,cn=accounts,dc=example,dc=org



всё нажимаем create
идём проверять:

Настраиваем тоже самое для второго сервера - для отказоустойчивости:

Создаём админскую роль чтоб пользователи подтягивались и становились сразу админами:


всё, теперь мотаем в самый низ и сохраняем роль

вот наша роль:

проверяем, для этого идём во freeipa и добавляем нового пользователя:

добавляем его в группу nexus-admins

проверяем в nexus
пользователь есть:

и он в нужными правами правами

read only группа
тут пока в настройках оставляем всё так же

а вот при настройке
User filter мы меняем группу с nexus-admins на nexus-ro-users
(memberOf=cn=nexus-ro-users,cn=groups,cn=accounts,dc=test,dc=local)

и создаём роль с привилегиями - тут я указал привелегии первые попавшиеся, вы можете задавать какие нужны именно вам.

проверяем права у пользователя user2

как видим всё ок
Freeipa -> Gitlab
сделаем интеграцию между freeipa и gitlab
к сожалению в бесплатной версии гитлаба нельзя привязывать пользователей к определённым группам в гитлабе, вот дока:
https://docs.gitlab.com/ee/administration/auth/ldap/ldap_synchronization.html
поэтому создадим 1 группу в FreeIPA:
gitlab
создадим системного пользователя gitlab у которого будет доступ на чтение групп и пользователей.
для этого на сервере freeipa или его реплике заходим в директорию /etc/ipa и запускаем скрипт. или выкачиваем тут:
https://github.com/noahbliss/freeipa-sam
[root@freeipa-1 ipa]# cd /etc/ipa
[root@freeipa-1 ipa]# bash freeipa-sam.sh
нажимаем 1 и задаём имя нашего сервера freeipa-1.test.local :
вот результат:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
### FreeIPA - System Account Manager ### 1.) ldapserver=freeipa-1.test.local 2.) domain=test.local (ldapdomain=dc=test,dc=local) 3.) binduser= 4.) bindpass=UNSET! 5.) ssl=true Actions (conditions not yet met): add | rm | ls | info | passwd | save --- Results --- --- End Results --- > 3 |
выбрали 3 указываем пользователя admin (любой пользователь с правами админа)в нашем freeipa далее выберем 4 и зададим пароль от этого пользователя, после выбираем 5 чтоб отключить SSL
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
### FreeIPA - System Account Manager ### 1.) ldapserver=freeipa-1.test.local 2.) domain=test.local (ldapdomain=dc=test,dc=local) 3.) binduser=uid=admin,cn=users,cn=accounts,dc=test,dc=local 4.) bindpass=SET! 5.) ssl=false Actions (ready): add | rm | ls | info | passwd | save --- Results --- --- End Results --- > |
после можем создавать системного пользователя gitlab для этого набираем команду add
нас попросят ввести имя пользователя мы вводим gitlab после нас попросят ввести пароль я указал Secret123 далее нас попросят указать дату истечения этого пароль - ничего не указываем, нажимаем ENTER
проверяем что пользователь создан для этого набираем ls
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
### FreeIPA - System Account Manager ### 1.) ldapserver=freeipa-1.test.local 2.) domain=test.local (ldapdomain=dc=test,dc=local) 3.) binduser=uid=admin,cn=users,cn=accounts,dc=test,dc=local 4.) bindpass=SET! 5.) ssl=false Actions (ready): add | rm | ls | info | passwd | save --- Results --- dn: uid=sudo,cn=sysaccounts,cn=etc,dc=test,dc=local dn: uid=nexus,cn=sysaccounts,cn=etc,dc=test,dc=local dn: uid=gitlab,cn=sysaccounts,cn=etc,dc=test,dc=local --- End Results --- > |
как видим пользователь создан:
dn: uid=gitlab,cn=sysaccounts,cn=etc,dc=test,dc=local
теперь идём на сервер gitlab
[root@ansible ansible]# ssh 192.168.1.106
root@192.168.1.106's password:
редактируем файл:
root@gitlab:~# nano /etc/gitlab/gitlab.rb
включаем LDAP
gitlab_rails['ldap_enabled'] = true
Затем укажите путь к файлу с настройками LDAP для FreeIPA.
gitlab_rails['ldap_servers'] = YAML.load_file('/etc/gitlab/freeipa_settings.yml')
Наконец, создайте файл YAML для хранения настроек подключения IPA.
cat /etc/gitlab/freeipa_settings.yml
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
main: label: 'FreeIPA' host: 'freeipa-1.test.local' port: 389 uid: 'uid' method: 'tls' bind_dn: 'uid=gitlab,cn=sysaccounts,cn=etc,dc=test,dc=local' password: 'Secret123' encryption: 'plain' base: 'cn=accounts,dc=test,dc=local' group_base: 'cn=groups,cn=accounts,DC=test,DC=local' user_filter: 'memberOf=cn=gitlab,cn=groups,cn=accounts,dc=test,dc=local' verify_certificates: false attributes: username: ['uid'] email: ['mail'] name: 'displayName' first_name: 'givenName' last_name: 'sn' |
вот тут:
user_filter: 'memberOf=cn=gitlab,cn=groups,cn=accounts,dc=test,dc=local'
мы ограничиваем пользователей группой gitlab
bind_dn: 'uid=gitlab,cn=sysaccounts,cn=etc,dc=test,dc=local'
а тут мы используем системный аккаунт который создали ранее
и запускаем реконфигурацию:
root@gitlab:~# gitlab-ctl reconfigure
проверяем:
http://gitlab.test.local/users/sign_in


как видим всё ок.
дальше можем настраивать группы и добавлять пользователей внутри гитлаба
Freeipa -> Vault
создадим системного пользователя vault у которого будет доступ на чтение групп и пользователей.
для этого на сервере freeipa или его реплике заходим в директорию /etc/ipa и запускаем скрипт. или выкачиваем тут:
https://github.com/noahbliss/freeipa-sam
[root@freeipa-1 ipa]# cd /etc/ipa
[root@freeipa-1 ipa]# bash freeipa-sam.sh
нажимаем 1 и задаём имя нашего сервера freeipa-1.test.local
нажимаем 3 и вводим имя пользователя admin
нажимаем 4 и вводим пароль Secret123
нажимаем 5 - выключаем ssl
далее нажимаем add предложат ввести имя системного пользователя, вводим vault предложат ввести пароль для него, я использую Secret123 далее попросят ввести дату истечения пароля, ничего не вводим, нажимаем Enter
пользователь создан, проверяем, нажимаем ls
вот результат:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
### FreeIPA - System Account Manager ### 1.) ldapserver=freeipa-1.test.local 2.) domain=test.local (ldapdomain=dc=test,dc=local) 3.) binduser=uid=admin,cn=users,cn=accounts,dc=test,dc=local 4.) bindpass=SET! 5.) ssl=false Actions (ready): add | rm | ls | info | passwd | save --- Results --- dn: uid=sudo,cn=sysaccounts,cn=etc,dc=test,dc=local dn: uid=nexus,cn=sysaccounts,cn=etc,dc=test,dc=local dn: uid=gitlab,cn=sysaccounts,cn=etc,dc=test,dc=local dn: uid=vault,cn=sysaccounts,cn=etc,dc=test,dc=local --- End Results --- > |
со стороны freeipa всё,
теперь настраиваем vault:
на всех тачках добавляем:
echo "192.168.1.100 freeipa-1.test.local" >> /etc/hosts
если DNS не настроен.
заходим в vault
https://vault.test.local:8200/
напомню что root_token hvs.UuG0QJvRRfwUHUTGxDTjFaAd
переходим:
Access -> Enable new Method -> LDAP -> Enable Method


теперь настраиваем
URL = ldap://freeipa-1.test.local:389

в разделе LDAP options: раскрываем и меняем User atrribut на uid

Следующий раздел customize user search — тут мы как раз и настраиваем адрес кем биндимся, а так же где искать пользователей, для freeipa это будут такие параметры
Name of Object to bind (binddn) = uid=vault,cn=sysaccounts,cn=etc,dc=test,dc=local
User DN = cn=users,cn=accounts,dc=test,dc=local
Bindpass = Secret123

Так же настроим поиск по группам
Group Filter = (|(member={{.UserDN}})(uniqueMember={{.UserDN}}))
Group Attribute = cn
Group DN = cn=groups,cn=accounts,dc=test,dc=local

теперь создадим policy для админов и пользователей

и ещё одну

вот сами policy:
vault-admins
|
1 2 3 |
path "*" { capabilities = ["create", "read", "update", "delete", "list", "sudo"] } |
vault-ro-users
|
1 2 3 |
path "*" { capabilities = ["read", "list"] } |
теперь группы в LDAP

группа vault-admins
policy vault-admins

добавляем вторую группу
группа vault-ro-users
policy vault-ro-users

проверяем:

как видим KV успешно создан
теперь зайдём под user2 который напомним что находится в группе vault-ro-users


как видим нам не хватает прав на создание нового KV
настройка закончена, далее можно изменять policy как нам требуется.
ниже указано как произвести всё тоже самое но консольными командами
заходим на vault и логинимся
vault login
вводим рут токен
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
root@vault1:~# vault login WARNING! VAULT_ADDR and -address unset. Defaulting to https://127.0.0.1:8200. Token (will be hidden): Success! You are now authenticated. The token information displayed below is already stored in the token helper. You do NOT need to run "vault login" again. Future Vault requests will automatically use this token. Key Value --- ----- token hvs.b1aFMPa9WenUgwIkUgqtFtIC token_accessor a87f7TLRK4mCRnrg5RDh4Pt4 token_duration ∞ token_renewable false token_policies ["root"] identity_policies [] policies ["root"] |
включаем LDAP
vault auth enable ldap
|
1 2 3 |
root@vault1:~# vault auth enable ldap WARNING! VAULT_ADDR and -address unset. Defaulting to https://127.0.0.1:8200. Success! Enabled ldap auth method at: ldap/ |
настраиваем подключение
vault write auth/ldap/config \
url="ldap://freeipa-1.test.local:389" \
binddn="uid=vault,cn=sysaccounts,cn=etc,dc=test,dc=local" \
bindpass='Secret123' \
userdn="cn=users,cn=accounts,dc=test,dc=local" \
userattr="uid" \
groupdn="cn=groups,cn=accounts,dc=test,dc=local" \
groupfilter="(|(member={{.UserDN}})(uniqueMember={{.UserDN}}))" \
groupattr="cn"
|
1 2 3 4 5 6 7 8 9 10 11 |
root@vault1:~# vault write auth/ldap/config \ url="ldap://freeipa-1.test.local:389" \ binddn="uid=vault,cn=sysaccounts,cn=etc,dc=test,dc=local" \ bindpass='Secret123' \ userdn="cn=users,cn=accounts,dc=test,dc=local" \ userattr="uid" \ groupdn="cn=groups,cn=accounts,dc=test,dc=local" \ groupfilter="(|(member={{.UserDN}})(uniqueMember={{.UserDN}}))" \ groupattr="cn" WARNING! VAULT_ADDR and -address unset. Defaulting to https://127.0.0.1:8200. Success! Data written to: auth/ldap/config |
создаём policy
vault-admins
vault policy write vault-admins - <<EOF path "*" { capabilities = ["create", "read", "update", "delete", "list", "sudo"] } EOF
и vault-ro-users
vault policy write vault-ro-users - <<EOF path "*" { capabilities = ["read", "list"] } EOF
|
1 2 3 4 5 6 |
root@vault1:~# vault policy write vault-admins - <<EOF path "*" { capabilities = ["create", "read", "update", "delete", "list", "sudo"] } EOF Success! Uploaded policy: vault-admins |
|
1 2 3 4 5 6 |
root@vault1:~# vault policy write vault-ro-users - <<EOF path "*" { capabilities = ["read", "list"] } EOF Success! Uploaded policy: vault-ro-users |
создаём группы в ldap и привязываем к ним policy
vault write auth/ldap/groups/vault-admins policies=vault-admins
vault write auth/ldap/groups/vault-ro-users policies=vault-ro-users
|
1 2 3 |
root@vault1:~# vault write auth/ldap/groups/vault-admins policies=vault-admins WARNING! VAULT_ADDR and -address unset. Defaulting to https://127.0.0.1:8200. Success! Data written to: auth/ldap/groups/vault-admins |
|
1 2 3 |
root@vault1:~# vault write auth/ldap/groups/vault-ro-users policies=vault-ro-users WARNING! VAULT_ADDR and -address unset. Defaulting to https://127.0.0.1:8200. Success! Data written to: auth/ldap/groups/vault-ro-users |
Freeipa -> S3-Minio
Настраиваем интеграцию Freeipa с S3-Minio
/etc/ansible/hosts
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
[freeipa:children] ipa_servers ipa_replicas ipa_clients [ipa_servers] freeipa-1.test.local ansible_host=192.168.1.100 [ipa_replicas] freeipa-2.test.local ansible_host=192.168.1.101 [ipa_clients] nexus.test.local ansible_host=192.168.1.102 vault1.test.local ansible_host=192.168.1.103 vault2.test.local ansible_host=192.168.1.104 vault3.test.local ansible_host=192.168.1.105 gitlab.test.local ansible_host=192.168.1.106 gitlab-runner.test.local ansible_host=192.168.1.107 nfs.test.local ansible_host=192.168.1.108 gluster-fs1.test.local ansible_host=192.168.1.109 gluster-fs2.test.local ansible_host=192.168.1.110 s3-minio-1.test.local ansible_host=192.168.1.118 s3-minio-2.test.local ansible_host=192.168.1.119 |
[root@ansible ansible]# ansible-playbook -u root /etc/ansible/playbooks/roles_play/freeipa.yaml --ask-pass
идём в freeipa
https://freeipa-1.test.local/
создаём группу узлов:

Добавляем туда наши сервера

создаём группы пользователей admin и read only


user1 у нас будет в группе админов
user2 у нас будет в группе read only пользователей
Далее нам нужно создать системного пользователя во freeipa с помощью которого s3-minio сможет ходить в api и читать имена пользователей, но у него не будет прав что то выполнить. для этого на сервере freeipa или его реплике заходим в директорию /etc/ipa и запускаем скрипт. или выкачиваем тут:
https://github.com/noahbliss/freeipa-sam
[root@freeipa-1 ipa]# cd /etc/ipa
[root@freeipa-1 ipa]# bash freeipa-sam.sh
нажимаем 1 и задаём имя нашего сервера freeipa-1.test.local :
вот результат:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
### FreeIPA - System Account Manager ### 1.) ldapserver=freeipa-1.test.local 2.) domain=test.local (ldapdomain=dc=test,dc=local) 3.) binduser= 4.) bindpass=UNSET! 5.) ssl=true Actions (conditions not yet met): add | rm | ls | info | passwd | save --- Results --- --- End Results --- > 3 |
выбрали 3 указываем пользователя admin (любой пользователь с правами админа)в нашем freeipa далее выберем 4 и зададим пароль от этого пользователя, после выбираем 5 чтоб отключить SSL
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
### FreeIPA - System Account Manager ### 1.) ldapserver=freeipa-1.test.local 2.) domain=test.local (ldapdomain=dc=test,dc=local) 3.) binduser=uid=admin,cn=users,cn=accounts,dc=test,dc=local 4.) bindpass=SET! 5.) ssl=false Actions (ready): add | rm | ls | info | passwd | save --- Results --- --- End Results --- > |
после можем создавать системного пользователя s3minio для этого набираем команду add
нас попросят ввести имя пользователя мы вводим s3minio после нас попросят ввести пароль я указал Secret123 далее нас попросят указать дату истечения этого пароль - ничего не указываем, нажимаем ENTER
проверяем что пользователь создан для этого набираем ls
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
### FreeIPA - System Account Manager ### 1.) ldapserver=freeipa-1.test.local 2.) domain=test.local (ldapdomain=dc=test,dc=local) 3.) binduser=uid=admin,cn=users,cn=accounts,dc=test,dc=local 4.) bindpass=SET! 5.) ssl=false Actions (ready): add | rm | ls | info | passwd | save --- Results --- dn: uid=sudo,cn=sysaccounts,cn=etc,dc=test,dc=local dn: uid=nexus,cn=sysaccounts,cn=etc,dc=test,dc=local dn: uid=gitlab,cn=sysaccounts,cn=etc,dc=test,dc=local dn: uid=vault,cn=sysaccounts,cn=etc,dc=test,dc=local dn: uid=s3minio,cn=sysaccounts,cn=etc,dc=test,dc=local --- End Results --- > |
как видим пользователь создан:
dn: uid=s3minio,cn=sysaccounts,cn=etc,dc=test,dc=local
дальше настройка из консоли:
1 создаём подключение:
|
1 2 3 4 5 6 |
root@s3-minio-1:~# mc alias set myminio http://s3-minio-1.test.local:9000 admin Secret123 mc: Configuration written to `/root/.mc/config.json`. Please update your access credentials. mc: Successfully created `/root/.mc/share`. mc: Initialized share uploads `/root/.mc/share/uploads.json` file. mc: Initialized share downloads `/root/.mc/share/downloads.json` file. Added `myminio` successfully. |
2. проверяем какие бакеты есть
|
1 2 |
root@s3-minio-1:~# mc ls myminio [2024-08-31 14:47:41 +06] 0B test-bucket/ |
3. настраиваем подключение к ldap
|
1 2 3 4 5 6 7 8 9 10 11 |
mc admin config set myminio identity_ldap \ enabled="true" \ server_addr="freeipa-1.test.local:389" \ lookup_bind_dn="uid=s3minio,cn=sysaccounts,cn=etc,dc=test,dc=local" \ lookup_bind_password="Secret123" \ user_dn_search_base_dn="cn=users,cn=accounts,dc=test,dc=local" \ user_dn_search_filter="(&(uid=%s)(objectClass=inetOrgPerson))" \ group_search_base_dn="cn=groups,cn=accounts,dc=test,dc=local" \ group_search_filter="(&(objectclass=groupOfNames)(member=%d))" \ server_insecure="true" \ tls_skip_verify="true" |
рестартуем
|
1 |
mc admin service restart myminio |
подвязываем политику consoleAdmin к роли админов s3-minio-admins
|
1 |
mc idp ldap policy attach myminio consoleAdmin --group='cn=s3-minio-admins,cn=groups,cn=accounts,dc=test,dc=local' |
подвязываем политики readwrite и diagnostics к роли пользователей s3-minio-ro-users я вижу что роль называется RO но мне лень сейчас и скрины переделывать и роль переименовывать.
|
1 2 3 |
mc idp ldap policy attach myminio readwrite --group='cn=s3-minio-ro-users,cn=groups,cn=accounts,dc=test,dc=local' mc idp ldap policy attach myminio diagnostics --group='cn=s3-minio-ro-users,cn=groups,cn=accounts,dc=test,dc=local' |
проверяем в админ панели minio что доступы есть:

проверяем авторизацию напомню что user1 у нас в группе админов а user2 в группе пользователей, + логин пароль используем из freeipa
y user1 админка не отличается от пользователя admin
вот админка пользователя user2

ну всё ок. логин проверили.
интеграция успешно настроена.
Install kubernetes (kubespray official)
Подготовим 6 серверов 3 под мастеров 3 под воркеры
операционка debian 12
для мастеров 2 ядра 4 оперативки
для воркеров 4 ядра 4 оперативки
так же подготовим диски минимум 30 гб
pvcreate /dev/sdb && vgextend debian-vg /dev/sdb && lvextend -L +15G /dev/debian-vg/root && resize2fs /dev/debian-vg/root
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
[root@ansible ansible]# ssh 192.168.1.112 root@192.168.1.112's password: Linux debian 5.10.0-30-amd64 #1 SMP Debian 5.10.218-1 (2024-06-01) x86_64 root@debian:~# pvscan PV /dev/sda5 VG debian-vg lvm2 [<14.52 GiB / 0 free] Total: 1 [<14.52 GiB] / in use: 1 [<14.52 GiB] / in no VG: 0 [0 ] root@debian:~# pvcreate /dev/sdb Physical volume "/dev/sdb" successfully created. root@debian:~# vgextend debian-vg /dev/sdb Volume group "debian-vg" successfully extended root@debian:~# pvscan PV /dev/sda5 VG debian-vg lvm2 [<14.52 GiB / 0 free] PV /dev/sdb VG debian-vg lvm2 [<30.00 GiB / <30.00 GiB free] Total: 2 [<44.52 GiB] / in use: 2 [<44.52 GiB] / in no VG: 0 [0 ] root@debian:~# lvextend -L +15G /dev/debian-vg/root Size of logical volume debian-vg/root changed from 13.56 GiB (3472 extents) to 28.56 GiB (7312 extents). Logical volume debian-vg/root successfully resized. root@debian:~# resize2fs /dev/debian-vg/root resize2fs 1.46.2 (28-Feb-2021) Filesystem at /dev/debian-vg/root is mounted on /; on-line resizing required old_desc_blocks = 2, new_desc_blocks = 4 The filesystem on /dev/debian-vg/root is now 7487488 (4k) blocks long. |
обновим и доустановим все необходимые пакеты:
/etc/ansible/hosts
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
[all_servers] 192.168.1.100 # freeipa1 192.168.1.101 # freeipa2 192.168.1.102 # nexus 192.168.1.103 # vault1 192.168.1.104 # vault2 192.168.1.105 # vault3 192.168.1.106 # gitlab 192.168.1.107 # gitlab-runner 192.168.1.108 # nfs 192.168.1.109 # gluster-fs1 192.168.1.110 # gluster-fs2 192.168.1.112 # kub-master1 192.168.1.113 # kub-master2 192.168.1.114 # kub-master3 192.168.1.115 # kub-worker1 192.168.1.116 # kub-worker2 192.168.1.117 # kub-worker3 |
[root@ansible ansible]# ansible-playbook -u root /etc/ansible/playbooks/roles_play/new_server.yml --ask-pass
ЭТО ОПЦИОНАЛЬНО добавим эти сервера сразу к freeipa. nexus
(можно и не добавлять если в вашем варианте это не требуется)
/etc/ansible/hosts
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
[freeipa:children] ipa_servers ipa_replicas ipa_clients [ipa_servers] freeipa-1.test.local ansible_host=192.168.1.100 [ipa_replicas] freeipa-2.test.local ansible_host=192.168.1.101 [ipa_clients] nexus.test.local ansible_host=192.168.1.102 vault1.test.local ansible_host=192.168.1.103 vault2.test.local ansible_host=192.168.1.104 vault3.test.local ansible_host=192.168.1.105 gitlab.test.local ansible_host=192.168.1.106 gitlab-runner.test.local ansible_host=192.168.1.107 nfs.test.local ansible_host=192.168.1.108 gluster-fs1.test.local ansible_host=192.168.1.109 gluster-fs2.test.local ansible_host=192.168.1.110 kub-master1.test.local ansible_host=192.168.1.112 kub-master2.test.local ansible_host=192.168.1.113 kub-master3.test.local ansible_host=192.168.1.114 kub-worker1.test.local ansible_host=192.168.1.115 kub-worker2.test.local ansible_host=192.168.1.116 kub-worker3.test.local ansible_host=192.168.1.117 [nexus:children] nexus_server nexus_clients [nexus_server] nexus.test.local ansible_host=192.168.1.102 [nexus_clients] freeipa-1.test.local ansible_host=192.168.1.100 freeipa-2.test.local ansible_host=192.168.1.101 vault1.test.local ansible_host=192.168.1.103 vault2.test.local ansible_host=192.168.1.104 vault3.test.local ansible_host=192.168.1.105 gitlab.test.local ansible_host=192.168.1.106 gitlab-runner.test.local ansible_host=192.168.1.107 nfs.test.local ansible_host=192.168.1.108 gluster-fs1.test.local ansible_host=192.168.1.109 gluster-fs2.test.local ansible_host=192.168.1.110 kub-master1.test.local ansible_host=192.168.1.112 kub-master2.test.local ansible_host=192.168.1.113 kub-master3.test.local ansible_host=192.168.1.114 kub-worker1.test.local ansible_host=192.168.1.115 kub-worker2.test.local ansible_host=192.168.1.116 kub-worker3.test.local ansible_host=192.168.1.117 |
[root@ansible ansible]# ansible-playbook -u root /etc/ansible/playbooks/roles_play/freeipa.yaml --ask-pass
теперь можно приступать к установке k8s
нам нужен будет python3.8 у меня на ansible используется операционка centos7 с python3.6 поэтому я ставлю так:
yum install -y gcc openssl-devel bzip2-devel libffi-devel zlib-devel
cd /usr/src
wget https://www.python.org/ftp/python/3.8.10/Python-3.8.10.tgz
tar xzf Python-3.8.10.tgz
cd Python-3.8.10
./configure --enable-optimizations
make altinstall
python3.8 -m venv ~/venv
source ~/venv/bin/activate
вот тут официальный чарт kubespray
https://github.com/kubernetes-incubator/kubespray.git
в моём репозитории тоже используется официальный kubespray но так в моём репозитории используется старый kubespray
Ставим все нужные зависимости в ansible:
root@ansible:/etc/ansible# cd /etc/ansible/kubespray-official/kubespray
root@ansible:/etc/ansible/kubespray-official/kubespray# apt install python3-pip -y
root@ansible:/etc/ansible/kubespray-official/kubespray# pip install -r requirements.txt
root@ansible:/etc/ansible/kubespray-official/kubespray# pip install -U jinja2
заполняем inventory
/etc/ansible/kubespray-official/kubespray/inventory/sample/inventory.ini
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
[all] kub-master1.test.local ansible_host=192.168.1.112 ip=192.168.1.112 kub-master2.test.local ansible_host=192.168.1.113 ip=192.168.1.113 kub-master3.test.local ansible_host=192.168.1.114 ip=192.168.1.114 kub-worker1.test.local ansible_host=192.168.1.115 ip=192.168.1.115 kub-worker2.test.local ansible_host=192.168.1.116 ip=192.168.1.116 kub-worker3.test.local ansible_host=192.168.1.117 ip=192.168.1.117 [kube_control_plane] kub-master1.test.local kub-master2.test.local kub-master3.test.local [etcd] kub-master1.test.local kub-master2.test.local kub-master3.test.local [kube_node] kub-worker1.test.local kub-worker2.test.local kub-worker3.test.local |
дополнительные аддоны мы можем включить тут:
inventory/sample/group_vars/k8s_cluster/addons.yml
|
1 2 3 |
[root@ansible kubespray]# cat inventory/sample/group_vars/k8s_cluster/addons.yml | grep -i true | grep -v '#' helm_enabled: true local_volume_provisioner_enabled: true |
я выключаю всё кроме helm_enabled и local_volume_provisioner_enabled
Далее правим
vim inventory/sample/group_vars/all/all.yml
|
1 2 3 4 5 6 |
## Upstream dns servers upstream_dns_servers: - 192.168.1.100 - 192.168.1.101 - 8.8.8.8 - 8.8.4.4 |
#тут указываем наши днс сервера, я использую гугловые, можно оставить по умолчанию. ниже указываем наш прокси если он используется http_proxy: "http://proxy_ip:3128" https_proxy: "http://proxy_ip:3128"
я добавляю ещё мои 2 dns сервера с freeipa это 192,168,1,100 и 192,168,1,101
vim inventory/sample/group_vars/k8s_cluster/k8s-cluster.yml
включим все варианты аутентификации (обратите внимание на отступы в yml файлах, модули должны быть без отступов)
kube_oidc_auth: true
kube_token_auth: true
|
1 2 3 |
[root@ansible kubespray]# cat inventory/sample/group_vars/k8s_cluster/k8s-cluster.yml | grep -E 'kube_oidc_auth|kube_basic_auth|kube_token_auth' kube_oidc_auth: true kube_token_auth: true |
в файле
/etc/ansible/kubespray-official/kubespray/inventory/sample/group_vars/k8s_cluster/k8s-cluster.yml
настраиваем:
версию кластера:
kube_version: v1.24.0
плагин сети
kube_network_plugin: calico
подсеть для service
kube_service_addresses: 10.233.0.0/18
подсеть для pod
kube_pods_subnet: 10.233.64.0/18
подсеть node
kube_network_node_prefix: 24
выключим поддержку IPv6
enable_dual_stack_networks: false
имя кластера
cluster_name: cluster.local
можно настроить авто обновление сертификатов controlplane, но я это буду делать в ручном режиме.
auto_renew_certificates: false
|
1 2 3 4 5 6 7 8 9 10 11 12 |
python3 -m venv .venv-ansible source .venv-ansible/bin/activate pip install --upgrade pip setuptools wheel sed -i '/ruamel.yaml.clib/d' requirements.txt pip install --upgrade pip setuptools wheel pip install -r requirements.txt pip install netaddr pip install cryptography pip install jmespath pip install jsonschema mkdir -p filter_plugins cp ~/.ansible/collections/ansible_collections/ansible/utils/plugins/filter/ipaddr.py filter_plugins/ |
запускаем установку:
ansible-playbook -u root -i inventory/sample/inventory.ini cluster.yml -b --ask-pass
ждём минут 30-40 зависит от сети и системы
проверяем:
[root@ansible ansible]# ssh 192.168.1.112
root@kub-master1:~# kubectl get nodes
|
1 2 3 4 5 6 7 8 |
root@kub-master1:~# kubectl get nodes NAME STATUS ROLES AGE VERSION kub-master1.test.local Ready control-plane 5d16h v1.24.0 kub-master2.test.local Ready control-plane 5d16h v1.24.0 kub-master3.test.local Ready control-plane 5d16h v1.24.0 kub-worker1.test.local Ready <none> 5d16h v1.24.0 kub-worker2.test.local Ready <none> 5d16h v1.24.0 kub-worker3.test.local Ready <none> 5d16h v1.24.0 |
Доступ с локального PC до кластера
допустим у нас PC с операционкой debian и ip адресом 192.168.1.120
root@debian:~# curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
root@debian:~# chmod +x kubectl
root@debian:~# cp ./kubectl /usr/bin/
копируем конфиг на наш клиент
root@kub-master1:~# scp /etc/kubernetes/admin.conf root@192.168.1.120:~/
|
1 2 3 4 5 6 7 8 |
root@kub-master1:~# scp /etc/kubernetes/admin.conf root@192.168.1.120:~/ The authenticity of host '192.168.1.120 (192.168.1.120)' can't be established. ED25519 key fingerprint is SHA256:soNRFix0hNHB30IqzBzqt9BV9LTjHRE6V5uCvOVNxm8. This key is not known by any other names. Are you sure you want to continue connecting (yes/no/[fingerprint])? yes Warning: Permanently added '192.168.1.120' (ED25519) to the list of known hosts. root@192.168.1.120's password: admin.conf |
root@debian:~# mkdir -p ~/.kube
root@debian:~# mv admin.conf ~/.kube/config
так как в этом конфиге указан 127.0.0.1 в качестве сервера, заменим на наш ip 192.168.1.112
root@debian:~# sed -i 's/127.0.0.1/192.168.1.112/' ~/.kube/config
можем проверять
|
1 2 3 4 5 6 7 8 9 |
root@debian:~# kubectl get nodes NAME STATUS ROLES AGE VERSION kub-master1.test.local Ready control-plane 6d v1.24.0 kub-master2.test.local Ready control-plane 6d v1.24.0 kub-master3.test.local Ready control-plane 6d v1.24.0 kub-worker1.test.local Ready <none> 5d23h v1.24.0 kub-worker2.test.local Ready <none> 6d v1.24.0 kub-worker3.test.local Ready <none> 5d23h v1.24.0 |
настроим ещё автодополнения
root@debian:~# apt-get install bash-completion -y
root@debian:~# echo 'source <(kubectl completion bash)' >>~/.bashrc
установим на наш локальный PC HELM
root@debian:~# apt-get install gpg -y
root@debian:~# curl https://baltocdn.com/helm/signing.asc | gpg --dearmor | tee /usr/share/keyrings/helm.gpg > /dev/null
root@debian:~# apt-get install apt-transport-https --yes
root@debian:~# echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/helm.gpg] https://baltocdn.com/helm/stable/debian/ all main" | tee /etc/apt/sources.list.d/helm-stable-debian.list
root@debian:~# apt-get update
root@debian:~# apt-get install helm -y
добавим ещё плагин diff - пригодится
helm plugin install https://github.com/databus23/helm-diff
Установка дополнительных компонентов
Ставим ingress controller
сразу включим и метрики и snippet-annotations
curl -sL https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.11.3/deploy/static/provider/cloud/deploy.yaml \
| sed 's/--enable-metrics=false/--enable-metrics=true/g; s/allow-snippet-annotations: "false"/allow-snippet-annotations: "true"/g' \
| kubectl apply -f -
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
root@kub-master1:~# curl -sL https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.11.3/deploy/static/provider/cloud/deploy.yaml \ | sed 's/--enable-metrics=false/--enable-metrics=true/g; s/allow-snippet-annotations: "false"/allow-snippet-annotations: "true"/g' \ | kubectl apply -f - namespace/ingress-nginx created serviceaccount/ingress-nginx created serviceaccount/ingress-nginx-admission created role.rbac.authorization.k8s.io/ingress-nginx created role.rbac.authorization.k8s.io/ingress-nginx-admission created clusterrole.rbac.authorization.k8s.io/ingress-nginx created clusterrole.rbac.authorization.k8s.io/ingress-nginx-admission created rolebinding.rbac.authorization.k8s.io/ingress-nginx created rolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx created clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created configmap/ingress-nginx-controller created service/ingress-nginx-controller created service/ingress-nginx-controller-admission created deployment.apps/ingress-nginx-controller created job.batch/ingress-nginx-admission-create created job.batch/ingress-nginx-admission-patch created ingressclass.networking.k8s.io/nginx created |
проверяем:
|
1 2 3 4 5 |
root@kub-master1:~# kubectl get pod -n ingress-nginx NAME READY STATUS RESTARTS AGE ingress-nginx-admission-create-v94wt 0/1 Completed 0 3m2s ingress-nginx-admission-patch-8zwp2 0/1 Completed 2 3m2s ingress-nginx-controller-686556747b-dqxdg 1/1 Running 0 3m2s |
Ingress controller helm install
или можем поставить через helm вот оф values
https://github.com/kubernetes/ingress-nginx/blob/helm-chart-4.12.2/charts/ingress-nginx/values.yaml
вот values
cat values.yaml
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
controller: enabled: true kind: Deployment # DaemonSet replicaCount: 2 # CPU/memory limits for the controller pods resources: limits: cpu: 1000m memory: 1Gi requests: cpu: 100m memory: 256Mi # CRD for the nginx ingress class ingressClassResource: default: false enabled: true name: nginx controllerValue: k8s.io/ingress-nginx # Enable metrics endpoint metrics: enabled: true extraArgs: "enable-ssl-passthrough": "true" # enable-ssl-passthrough: true # passthrough: # enabled: true # All valid ConfigMap keys must be in kebab-case config: disable-ipv6: "true" disable-ipv6-dns: "true" enable-access-log-for-default-backend: "false" http2-max-field-size: "8k" large-client-header-buffers: "16 64k" limit-conn-status-code: "429" limit-req-status-code: "429" load-balance: ewma keepalive: "65" client-max-body-size: "300m" proxy-body-size: "300m" error-log-level: error log-format-escape-json: "true" log-format-upstream: >- {"bytes_sent":"$bytes_sent","vhost":"$host"," request_proto":"$server_protocol","remote_addr":"$remote_addr", "proxy_add_x_forwarded_for":"$proxy_add_x_forwarded_for","remote_user":"$remote_user", "time_local":"$time_local","request_method":"$request_method", "request_uri":"$uri","request_args":"$args","request":"$request", "status":"$status","body_bytes_sent":"$body_bytes_sent", "http_referer":"$http_referer","http_user_agent":"$http_user_agent", "request_length":"$request_length","request_time":"$request_time", "upstream_addr":"$upstream_addr","upstream_response_length":"$upstream_response_length", "upstream_response_time":"$upstream_response_time","upstream_status":"$upstream_status", "X-Business-Error":"$upstream_http_x_business_error","upstream_header_time":"$upstream_header_time", "upstream_connect_time":"$upstream_connect_time","connections_waiting":"$connections_waiting", "connections_active":"$connections_active"} map-hash-bucket-size: "128" server-tokens: "false" ssl-protocols: "TLSv1.2 TLSv1.3" ssl-session-cache: "true" ssl-session-cache-size: "20m" ssl-session-timeout: "30m" use-forwarded-headers: "true" use-gzip: "true" use-proxy-protocol: "false" worker-cpu-affinity: auto worker-processes: "2" allow-snippet-annotations: "true" annotations-risk-level: Critical # If you need raw TCP/SNI pass-through, un-comment and adjust: # extraArgs: # enable-ssl-passthrough: "true" # tcp: # "443": "default/ssl-passthrough:443" ingressClass: nginx # Service definition for the controller service: type: LoadBalancer externalTrafficPolicy: Cluster # Turn on the admission webhook deployment admissionWebhooks: enabled: true tolerations: - key: "node-role.kubernetes.io/master" operator: "Exists" effect: "NoSchedule" - key: "node-role.kubernetes.io/control-plane" operator: "Exists" effect: "NoSchedule" defaultBackend: enabled: false |
ставим командой:
|
1 2 3 4 |
helm upgrade --install ingress-nginx ingress-nginx/ingress-nginx \ --namespace ingress-nginx --create-namespace \ --version 4.12.1 \ -f values.yaml |
Metalllb
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.13.9/config/manifests/metallb-native.yaml
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
root@kub-master1:~# kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.13.9/config/manifests/metallb-native.yaml namespace/metallb-system created customresourcedefinition.apiextensions.k8s.io/addresspools.metallb.io created customresourcedefinition.apiextensions.k8s.io/bfdprofiles.metallb.io created customresourcedefinition.apiextensions.k8s.io/bgpadvertisements.metallb.io created customresourcedefinition.apiextensions.k8s.io/bgppeers.metallb.io created customresourcedefinition.apiextensions.k8s.io/communities.metallb.io created customresourcedefinition.apiextensions.k8s.io/ipaddresspools.metallb.io created customresourcedefinition.apiextensions.k8s.io/l2advertisements.metallb.io created serviceaccount/controller created serviceaccount/speaker created role.rbac.authorization.k8s.io/controller created role.rbac.authorization.k8s.io/pod-lister created clusterrole.rbac.authorization.k8s.io/metallb-system:controller created clusterrole.rbac.authorization.k8s.io/metallb-system:speaker created rolebinding.rbac.authorization.k8s.io/controller created rolebinding.rbac.authorization.k8s.io/pod-lister created clusterrolebinding.rbac.authorization.k8s.io/metallb-system:controller created clusterrolebinding.rbac.authorization.k8s.io/metallb-system:speaker created secret/webhook-server-cert created service/webhook-service created deployment.apps/controller created daemonset.apps/speaker created validatingwebhookconfiguration.admissionregistration.k8s.io/metallb-webhook-configuration created root@kub-master1:~# |
правим конфигмапу
kubectl get configmap kube-proxy -n kube-system -o yaml | sed -e "s/strictARP: false/strictARP: true/" | kubectl apply -f - -n kube-system
|
1 2 3 |
root@kub-master1:~# kubectl get configmap kube-proxy -n kube-system -o yaml | sed -e "s/strictARP: false/strictARP: true/" | kubectl apply -f - -n kube-system Warning: resource configmaps/kube-proxy is missing the kubectl.kubernetes.io/last-applied-configuration annotation which is required by kubectl apply. kubectl apply should only be used on resources created declaratively by either kubectl create --save-config or kubectl apply. The missing annotation will be patched automatically. configmap/kube-proxy configured |
возвращаемся на ansible и идём в директорию: /etc/ansible/kubespray-official/
|
1 2 3 4 |
[root@ansible ansible]# cd /etc/ansible/kubespray-official/ [root@ansible kubespray-official]# pwd /etc/ansible/kubespray-official [root@ansible kubespray-official]# |
так как нам нужно отредактировать файлы и применить их а доступ на ансибл мы давать не хотим то скопируем эту директорию на наш master и уже оттуда применим.
rsync -avh --progress metallb root@192.168.1.112:/tmp/
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
[root@ansible kubespray-official]# rsync -avh --progress metallb root@192.168.1.112:/tmp/ root@192.168.1.112's password: sending incremental file list metallb/ metallb/README.md 2.44K 100% 0.00kB/s 0:00:00 (xfr#1, to-chk=2/4) metallb/ip-pool.yaml 267 100% 260.74kB/s 0:00:00 (xfr#2, to-chk=1/4) metallb/lb-ingress-controller-svc.yaml 537 100% 524.41kB/s 0:00:00 (xfr#3, to-chk=0/4) sent 3.52K bytes received 77 bytes 2.40K bytes/sec total size is 3.24K speedup is 0.90 |
переходим в мастер и заходим в директорию /tmp/metallb и редактируем файл ip-pool.yaml
root@kub-master1:~# cd /tmp/metallb/
root@kub-master1:/tmp/metallb# vim ip-pool.yaml
В файл ip-pool.yaml добавляем или диапазон IP адресов, или 1 IP указав его подсеть /32 (по этому адресу будет доступен кластер)
я добавил - 192.168.1.191/32 и 192.168.1.192/32
|
1 2 3 4 5 6 7 8 9 |
apiVersion: metallb.io/v1beta1 kind: IPAddressPool metadata: name: first-pool namespace: metallb-system spec: addresses: - 192.168.1.191/32 - 192.168.1.192/32 |
применяем
kubectl apply -f ip-pool.yaml
|
1 2 3 |
root@kub-master1:/tmp/metallb# kubectl apply -f ip-pool.yaml ipaddresspool.metallb.io/first-pool created l2advertisement.metallb.io/example created |
Добавляем сервис для ingres-controller типа LoadBalancer
kubectl apply -f lb-ingress-controller-svc.yaml
|
1 2 |
root@kub-master1:/tmp/metallb# kubectl apply -f lb-ingress-controller-svc.yaml service/ingress-nginx-lb created |
проверяем:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
root@kub-master1:/tmp/metallb# kubectl get pod -n metallb-system NAME READY STATUS RESTARTS AGE controller-c6c466d64-f79d8 1/1 Running 1 (18m ago) 19m speaker-4t9hf 1/1 Running 0 19m speaker-9p9sd 1/1 Running 0 19m speaker-b8m9m 1/1 Running 0 19m speaker-kfkfz 1/1 Running 0 19m speaker-rwcfg 1/1 Running 0 19m speaker-x2g8d 1/1 Running 0 19m root@kub-master1:/tmp/metallb# kubectl get svc -n ingress-nginx NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE ingress-nginx-controller LoadBalancer 10.233.32.234 192.168.1.191 80:31034/TCP,443:30100/TCP 27m ingress-nginx-controller-admission ClusterIP 10.233.6.121 <none> 443/TCP 27m ingress-nginx-lb LoadBalancer 10.233.14.234 192.168.1.192 80:31990/TCP,443:31405/TCP 41s |
как видим IP 192.168.1.191 и 192.168.1.192 являются EXTERNAL-IP
Metallb helm-installation
оффициальный репозиторий
https://github.com/metallb/metallb/tree/main/charts/metallb
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
controller: enabled: true resources: requests: cpu: "50m" memory: "80Mi" limits: cpu: 100m memory: 100Mi speaker: enabled: true logLevel: info resources: requests: cpu: "50m" memory: "50Mi" limits: cpu: 100m memory: 100Mi frr: enabled: true resources: requests: cpu: "10m" memory: "50Mi" limits: cpu: 50m memory: 100Mi crds: enabled: true |
helm install metallb metallb/metallb -n metallb-system --create-namespace -f values.yaml
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
apiVersion: metallb.io/v1beta1 kind: IPAddressPool metadata: name: first-pool namespace: metallb-system spec: addresses: - 192.168.1.191/32 --- apiVersion: metallb.io/v1beta1 kind: L2Advertisement metadata: name: first-l2-advertisement namespace: metallb-system spec: ipAddressPools: - first-pool |
kubectl apply -f ip-pool.yaml
так же включаем strictArp
|
1 2 3 |
kubectl get configmap kube-proxy -n kube-system -o yaml | \ sed -e "s/strictARP: false/strictARP: true/" | \ kubectl apply -f - -n kube-system |
проверить можем так
cat 1.yaml
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: test-ingress namespace: ingress-nginx annotations: nginx.ingress.kubernetes.io/rewrite-target: / spec: ingressClassName: nginx rules: - host: test.test.local http: paths: - path: / pathType: Prefix backend: service: name: ingress-nginx-defaultbackend port: number: 80 |
kubectl apply -f 1.yaml
|
1 2 3 |
kubectl get ingress -A NAMESPACE NAME CLASS HOSTS ADDRESS PORTS AGE ingress-nginx test-ingress nginx test.test.local 192.168.1.191 80 20s |
NFS provisioner
добавляем доступ для серверов k8s к nfs серверу
/etc/ansible/hosts
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
[nfs:children] nfsmaster nfsclient [nfsmaster] nfs.test.local ansible_host=192.168.1.108 [nfsclient] 192.168.1.100 192.168.1.101 192.168.1.112 192.168.1.113 192.168.1.114 192.168.1.115 192.168.1.116 192.168.1.117 |
[root@ansible ansible]# ansible-playbook -u root /etc/ansible/playbooks/roles_play/nfs.yml --ask-pass
копируем
[root@ansible ansible]# scp -r /etc/ansible/kubespray-official/nfs-provision root@192.168.1.120:~/
с клиента заходим и правим конфиг файл:
root@debian:~# cd ~/nfs-provision/
root@debian:~/nfs-provision# nano nfs_provision.yaml
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
apiVersion: apps/v1 kind: Deployment metadata: name: nfs-client-provisioner labels: app: nfs-client-provisioner # replace with namespace where provisioner is deployed namespace: kube-system spec: replicas: 1 strategy: type: Recreate selector: matchLabels: app: nfs-client-provisioner template: metadata: labels: app: nfs-client-provisioner spec: serviceAccountName: nfs-client-provisioner containers: - name: nfs-client-provisioner image: k8s.gcr.io/sig-storage/nfs-subdir-external-provisioner:v4.0.2 volumeMounts: - name: nfs-client-root mountPath: /persistentvolumes env: - name: PROVISIONER_NAME value: k8s-sigs.io/nfs-subdir-external-provisioner - name: NFS_SERVER value: 192.168.1.108 - name: NFS_PATH value: /nfs volumes: - name: nfs-client-root nfs: server: 192.168.1.108 path: /nfs |
устанавливаем ip адрес нашего nfs сервера
192.168.1.108
всё, можно ставить все компоненты
root@debian:~/nfs-provision# kubectl apply -f rbac.yaml -f nfs_class.yaml -f nfs_provision.yaml
если что, внутри этой директории есть README.md
проверяем:
|
1 2 3 4 5 |
root@debian:~# kubectl get storageclasses.storage.k8s.io NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE local-storage kubernetes.io/no-provisioner Delete WaitForFirstConsumer false 6d nfs-client k8s-sigs.io/nfs-subdir-external-provisioner Delete Immediate false 18m |
запросим тестовые volume
root@debian:~/nfs-provision# kubectl apply -f test-claim.yaml
|
1 2 3 4 5 6 7 8 |
root@debian:~/nfs-provision# kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE test-claim Bound pvc-2c3d964b-ceb5-482b-a8a0-902ff3117c51 50Mi RWX nfs-client 44s root@debian:~/nfs-provision# kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE pvc-2c3d964b-ceb5-482b-a8a0-902ff3117c51 50Mi RWX Delete Bound default/test-claim nfs-client 49s |
GlusterFS provisioner
Добавляем доступ для наших k8s серверов:
/etc/ansible/hosts
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
[glusterfs:children] glustermaster glusterclient [glustermaster] gluster-fs1.test.local ansible_host=192.168.1.109 gluster-fs2.test.local ansible_host=192.168.1.110 [glusterclient] 192.168.1.100 192.168.1.101 192.168.1.112 192.168.1.113 192.168.1.114 192.168.1.115 192.168.1.116 192.168.1.117 |
[root@ansible ansible]# ansible-playbook -u root /etc/ansible/playbooks/roles_play/glusterfs.yml --ask-pass
копируем файлы на клиента
[root@ansible ansible]# scp -r /etc/ansible/kubespray-official/glusterfs-provision root@192.168.1.120:~/
заходим на наш клиент и правим файл:
root@debian:~# cd glusterfs-provision/
root@debian:~/glusterfs-provision# nano endpont.yml
|
1 2 3 4 5 6 7 8 9 10 11 12 |
subsets: - addresses: #Тут указываем ip наших серверов - ip: 192.168.1.109 ports: #Тут просто можно оставить 1, порт роли не играет - port: 1 - addresses: - ip: 192.168.1.110 ports: - port: 1 |
добавляем IP наших glusterfs серверов
192.168.1.109
192.168.1.110
root@debian:~/glusterfs-provision# helm repo add olli-ai https://olli-ai.github.io/helm-charts/
root@debian:~/glusterfs-provision# helm repo update
проверяем:
|
1 2 3 4 |
root@debian:~/glusterfs-provision# helm search repo glusterfs-client NAME CHART VERSION APP VERSION DESCRIPTION olli-ai/glusterfs-client-provisioner 1.0.2 v1.0.2 glusterfs-client-provisioner is an automatic pr... |
ставим
|
1 2 3 4 5 |
helm install glusterfs-client olli-ai/glusterfs-client-provisioner \ --namespace kube-system \ --set glusterfs.server="{192.168.1.109,192.168.1.110}" \ --set glusterfs.volume=gluster-tom \ --set glusterfs.path=/ |
glusterfs.volume: gluster-tom - это том который мы задавали при настройке плейбука:
glusterfs.path / - это путь который в самом glusterfs будет смотреть в самый корень т.е. вот сюда:
|
1 2 3 4 5 6 7 8 9 10 |
root@gluster-fs2:~# ls -lah /gluster/gv01/ total 32K drwxr-xr-x 4 root root 4.0K Jun 22 13:06 . drwxr-xr-x 3 root root 4.0K Jun 22 12:28 .. -rw-r--r-- 2 root root 0 Jun 22 13:06 1111111111111 -rw-r--r-- 2 root root 0 Jun 22 12:58 111212 drw------- 262 root root 4.0K Jun 22 12:31 .glusterfs drwxr-xr-x 2 root root 4.0K Jun 22 12:31 .glusterfs-anonymous-inode-ad32a409-1b0f-4bd6-9420-acd93b0e1a14 -rw-r--r-- 2 root root 0 Jun 22 12:46 test_file |
/etc/ansible/playbooks/roles_play/glusterfs.yml
|
1 |
- name_of_gluster_tom: gluster-tom |
ответ примерно такой
|
1 2 3 4 5 6 7 8 9 10 11 |
root@debian:~/glusterfs-provision# helm install glusterfs-client olli-ai/glusterfs-client-provisioner \ --namespace kube-system \ --set glusterfs.server="{192.168.1.109,192.168.1.110}" \ --set glusterfs.volume=/ NAME: glusterfs-client LAST DEPLOYED: Sat Jul 6 13:00:39 2024 NAMESPACE: kube-system STATUS: deployed REVISION: 1 TEST SUITE: None |
проверяем
|
1 2 3 4 5 6 |
root@debian:~/glusterfs-provision# kubectl get storageclasses.storage.k8s.io NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE glusterfs-client cluster.local/glusterfs-client-glusterfs-client-provisioner Delete Immediate true 3m34s local-storage kubernetes.io/no-provisioner Delete WaitForFirstConsumer false 6d2h nfs-client k8s-sigs.io/nfs-subdir-external-provisioner Delete Immediate false 129m |
|
1 2 3 4 5 6 7 8 9 10 |
root@debian:~/glusterfs-provision# kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE test-claim Bound pvc-2c3d964b-ceb5-482b-a8a0-902ff3117c51 50Mi RWX nfs-client 112m test-claim-gluster Bound pvc-6ac72dee-d061-459b-8dc1-b9fb90e249ae 50Mi RWX glusterfs-client 9s root@debian:~/glusterfs-provision# kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE pvc-2c3d964b-ceb5-482b-a8a0-902ff3117c51 50Mi RWX Delete Bound default/test-claim nfs-client 112m pvc-6ac72dee-d061-459b-8dc1-b9fb90e249ae 50Mi RWX Delete Bound default/test-claim-gluster glusterfs-client 15s |
Seaweedfs provisioner
сразу скажу что есть проблемы с подключением по fuse
- не работает chown (пришлось отключать у grafana init контейнер)
- если делать репликацию 2 то тогда metadata у vmsingle victoria-metrics не работает
так что как то так - для victoria-metrics не подошло, возможно отстрелит ещё что то.
https://github.com/seaweedfs/seaweedfs-csi-driver/tree/master/deploy/helm/seaweedfs-csi-driver
ставим сам seaweedfs
/etc/ansible/hosts
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
[seaweed:children] seaweed_master seaweed_filer seaweed_volume [seaweed_master] 192.168.1.121 192.168.1.122 192.168.1.123 [seaweed_filer] 192.168.1.124 192.168.1.125 [seaweed_volume] 192.168.1.126 192.168.1.127 |
root@ansible:/etc/ansible# ansible-playbook playbooks/roles_play/seaweedfs.yml --ask-pass
теперь ставим provisioner
/etc/ansible/kubespray-official/seaweedfs-provision/values.yaml
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
# Значение seaweedfsFiler должно указывать на все доступные Filer-сервера # Формат: "<host1>:<port>,<host2>:<port>,…" seaweedfsFiler: "192.168.1.124:8888,192.168.1.125:8888" # Имя StorageClass, через который Pods будут брать PV storageClassName: "seaweedfs-storage" # Сделать ли этот StorageClass по-умолчанию в кластере isDefaultStorageClass: false # (опционально) Если у вас TLS между CSI-плагином и Filer-ами, # можно указать здесь имя Kubernetes Secret с сертификатом tlsSecret: "" # Настройки образов CSI-компонентов можно оставить по-умолчанию imagePullPolicy: "IfNotPresent" csiProvisioner: image: registry.k8s.io/sig-storage/csi-provisioner:v3.5.0 csiResizer: image: registry.k8s.io/sig-storage/csi-resizer:v1.8.0 csiAttacher: enabled: true image: registry.k8s.io/sig-storage/csi-attacher:v4.3.0 csiNodeDriverRegistrar: image: registry.k8s.io/sig-storage/csi-node-driver-registrar:v2.8.0 csiLivenessProbe: image: registry.k8s.io/sig-storage/livenessprobe:v2.10.0 seaweedfsCsiPlugin: image: chrislusf/seaweedfs-csi-driver:latest securityContext: privileged: true capabilities: add: ["SYS_ADMIN"] allowPrivilegeEscalation: true driverName: seaweedfs-csi-driver controller: replicas: 1 # Опция dataLocality можно оставить none, # если не требуется привязка к топологиям нод dataLocality: "none" node: enabled: true |
для проверки можем использовать:
/etc/ansible/kubespray-official/seaweedfs-provision/pvc-test.yaml
|
1 2 3 4 5 6 7 8 9 10 11 12 |
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: seaweedfs-csi-pvc spec: accessModes: - ReadWriteOnce resources: requests: storage: 5Gi storageClassName: seaweedfs-storage |
|
1 2 3 4 |
root@kub-master1:~/seaweedfs-provision# kubectl get pvc -n seaweedfs-csi-driver NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE seaweedfs-csi-pvc Bound pvc-d4435cb0-2469-45bc-9d4e-79507eda7dc6 5Gi RWO seaweedfs-storage <unset> 10s |
Prometheus, grafana, alertmanager
копируем values который будем использовать для развёртывания prom stack
[root@ansible ansible]# scp -r /etc/ansible/kubespray-official/prometheus root@192.168.1.120:~/
далее на нашем PC с которого есть доступ до кластера ставим всё необходимое:
namespace:
root@debian:~# kubectl create ns monitoring
репозиторий
root@debian:~# helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
root@debian:~# helm repo update
смотрим версии чартов
root@debian:~# helm search repo prometheus
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
NAME CHART VERSION APP VERSION DESCRIPTION prometheus-community/kube-prometheus-stack 61.2.0 v0.75.0 kube-prometheus-stack collects Kubernetes manif... prometheus-community/prometheus 25.22.1 v2.53.0 Prometheus is a monitoring system and time seri... prometheus-community/prometheus-adapter 4.10.0 v0.11.2 A Helm chart for k8s prometheus adapter prometheus-community/prometheus-blackbox-exporter 8.17.0 v0.25.0 Prometheus Blackbox Exporter prometheus-community/prometheus-cloudwatch-expo... 0.25.3 0.15.5 A Helm chart for prometheus cloudwatch-exporter prometheus-community/prometheus-conntrack-stats... 0.5.10 v0.4.18 A Helm chart for conntrack-stats-exporter prometheus-community/prometheus-consul-exporter 1.0.0 0.4.0 A Helm chart for the Prometheus Consul Exporter prometheus-community/prometheus-couchdb-exporter 1.0.0 1.0 A Helm chart to export the metrics from couchdb... prometheus-community/prometheus-druid-exporter 1.1.0 v0.11.0 Druid exporter to monitor druid metrics with Pr... prometheus-community/prometheus-elasticsearch-e... 6.0.0 v1.7.0 Elasticsearch stats exporter for Prometheus prometheus-community/prometheus-fastly-exporter 0.4.0 v8.1.0 A Helm chart for the Prometheus Fastly Exporter prometheus-community/prometheus-ipmi-exporter 0.4.0 v1.8.0 This is an IPMI exporter for Prometheus. prometheus-community/prometheus-json-exporter 0.13.0 v0.6.0 Install prometheus-json-exporter prometheus-community/prometheus-kafka-exporter 2.10.0 v1.7.0 A Helm chart to export the metrics from Kafka i... prometheus-community/prometheus-memcached-exporter 0.3.2 v0.14.3 Prometheus exporter for Memcached metrics prometheus-community/prometheus-modbus-exporter 0.1.2 0.4.1 A Helm chart for prometheus-modbus-exporter prometheus-community/prometheus-mongodb-exporter 3.5.0 0.40.0 A Prometheus exporter for MongoDB metrics prometheus-community/prometheus-mysql-exporter 2.5.3 v0.15.1 A Helm chart for prometheus mysql exporter with... prometheus-community/prometheus-nats-exporter 2.17.0 0.15.0 A Helm chart for prometheus-nats-exporter prometheus-community/prometheus-nginx-exporter 0.2.1 0.11.0 A Helm chart for the Prometheus NGINX Exporter prometheus-community/prometheus-node-exporter 4.37.0 1.8.1 A Helm chart for prometheus node-exporter prometheus-community/prometheus-opencost-exporter 0.1.1 1.108.0 Prometheus OpenCost Exporter prometheus-community/prometheus-operator 9.3.2 0.38.1 DEPRECATED - This chart will be renamed. See ht... prometheus-community/prometheus-operator-admiss... 0.14.0 0.75.1 Prometheus Operator Admission Webhook prometheus-community/prometheus-operator-crds 13.0.1 v0.75.0 A Helm chart that collects custom resource defi... prometheus-community/prometheus-pgbouncer-exporter 0.3.0 v0.8.0 A Helm chart for prometheus pgbouncer-exporter prometheus-community/prometheus-pingdom-exporter 2.5.0 20190610-1 A Helm chart for Prometheus Pingdom Exporter prometheus-community/prometheus-pingmesh-exporter 0.4.0 v1.2.1 Prometheus Pingmesh Exporter prometheus-community/prometheus-postgres-exporter 6.0.0 v0.15.0 A Helm chart for prometheus postgres-exporter prometheus-community/prometheus-pushgateway 2.14.0 v1.9.0 A Helm chart for prometheus pushgateway prometheus-community/prometheus-rabbitmq-exporter 1.12.0 v0.29.0 Rabbitmq metrics exporter for prometheus prometheus-community/prometheus-redis-exporter 6.3.0 v1.61.0 Prometheus exporter for Redis metrics prometheus-community/prometheus-smartctl-exporter 0.10.0 v0.12.0 A Helm chart for Kubernetes prometheus-community/prometheus-snmp-exporter 5.5.0 v0.26.0 Prometheus SNMP Exporter prometheus-community/prometheus-sql-exporter 0.1.0 v0.5.4 Prometheus SQL Exporter prometheus-community/prometheus-stackdriver-exp... 4.5.1 v0.15.1 Stackdriver exporter for Prometheus prometheus-community/prometheus-statsd-exporter 0.13.1 v0.26.1 A Helm chart for prometheus stats-exporter prometheus-community/prometheus-systemd-exporter 0.2.2 0.6.0 A Helm chart for prometheus systemd-exporter prometheus-community/prometheus-to-sd 0.4.2 0.5.2 Scrape metrics stored in prometheus format and ... prometheus-community/prometheus-windows-exporter 0.3.1 0.25.1 A Helm chart for prometheus windows-exporter prometheus-community/alertmanager 1.11.0 v0.27.0 The Alertmanager handles alerts sent by client ... prometheus-community/alertmanager-snmp-notifier 0.3.0 v1.5.0 The SNMP Notifier handles alerts coming from Pr... prometheus-community/jiralert 1.7.1 v1.3.0 A Helm chart for Kubernetes to install jiralert prometheus-community/kube-state-metrics 5.21.0 2.12.0 Install kube-state-metrics to generate and expo... prometheus-community/prom-label-proxy 0.9.0 v0.10.0 A proxy that enforces a given label in a given ... |
мы будем использовать prometheus-community/kube-prometheus-stack версии 61.2.0
root@debian:~# cd prometheus/
|
1 2 3 4 5 6 |
root@debian:~/prometheus# kubectl get storageclasses.storage.k8s.io NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE glusterfs-client cluster.local/glusterfs-client-glusterfs-client-provisioner Delete Immediate true 17h local-storage kubernetes.io/no-provisioner Delete WaitForFirstConsumer false 6d19h nfs-client k8s-sigs.io/nfs-subdir-external-provisioner Delete Immediate false 19h |
|
1 2 3 4 |
prometheusSpec: serviceMonitorNamespaceSelector: matchLabels: prometheus: enabled |
т.е. нужно будет добавлять label к тем неймспейсам с которых хотим собирать метрики
применим CRD
|
1 2 3 4 5 6 7 8 9 10 |
kubectl apply --server-side -f https://raw.githubusercontent.com/prometheus-operator/prometheus-operator/v0.75.0/example/prometheus-operator-crd/monitoring.coreos.com_alertmanagerconfigs.yaml --force-conflicts kubectl apply --server-side -f https://raw.githubusercontent.com/prometheus-operator/prometheus-operator/v0.75.0/example/prometheus-operator-crd/monitoring.coreos.com_alertmanagers.yaml --force-conflicts kubectl apply --server-side -f https://raw.githubusercontent.com/prometheus-operator/prometheus-operator/v0.75.0/example/prometheus-operator-crd/monitoring.coreos.com_podmonitors.yaml --force-conflicts kubectl apply --server-side -f https://raw.githubusercontent.com/prometheus-operator/prometheus-operator/v0.75.0/example/prometheus-operator-crd/monitoring.coreos.com_probes.yaml --force-conflicts kubectl apply --server-side -f https://raw.githubusercontent.com/prometheus-operator/prometheus-operator/v0.75.0/example/prometheus-operator-crd/monitoring.coreos.com_prometheusagents.yaml --force-conflicts kubectl apply --server-side -f https://raw.githubusercontent.com/prometheus-operator/prometheus-operator/v0.75.0/example/prometheus-operator-crd/monitoring.coreos.com_prometheuses.yaml --force-conflicts kubectl apply --server-side -f https://raw.githubusercontent.com/prometheus-operator/prometheus-operator/v0.75.0/example/prometheus-operator-crd/monitoring.coreos.com_prometheusrules.yaml --force-conflicts kubectl apply --server-side -f https://raw.githubusercontent.com/prometheus-operator/prometheus-operator/v0.75.0/example/prometheus-operator-crd/monitoring.coreos.com_scrapeconfigs.yaml --force-conflicts kubectl apply --server-side -f https://raw.githubusercontent.com/prometheus-operator/prometheus-operator/v0.75.0/example/prometheus-operator-crd/monitoring.coreos.com_servicemonitors.yaml --force-conflicts kubectl apply --server-side -f https://raw.githubusercontent.com/prometheus-operator/prometheus-operator/v0.75.0/example/prometheus-operator-crd/monitoring.coreos.com_thanosrulers.yaml --force-conflicts |
|
1 2 3 4 5 6 7 8 9 10 11 12 |
root@debian:~/prometheus# helm install prometheus prometheus-community/kube-prometheus-stack --version 61.2.0 -n monitoring -f my-values.yaml NAME: prometheus LAST DEPLOYED: Sun Jul 7 06:30:44 2024 NAMESPACE: monitoring STATUS: deployed REVISION: 1 NOTES: kube-prometheus-stack has been installed. Check its status by running: kubectl --namespace monitoring get pods -l "release=prometheus" Visit https://github.com/prometheus-operator/kube-prometheus for instructions on how to create & configure Alertmanager and Prometheus instances using the Operator. |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
root@debian:~/prometheus# kubectl label namespace --all "prometheus=enabled" namespace/cattle-fleet-system labeled namespace/cattle-system labeled namespace/cert-manager labeled namespace/default labeled namespace/fleet-default labeled namespace/fleet-local labeled namespace/ingress-nginx labeled namespace/kube-node-lease labeled namespace/kube-public labeled namespace/kube-system labeled namespace/local labeled namespace/metallb-system labeled namespace/monitoring labeled |
мы воспользуемся скриптом:
for ip in 192.168.1.112 192.168.1.113 192.168.1.114; do ssh root@$ip "sed -i 's/metricsBindAddress: 127.0.0.1:10249/metricsBindAddress: 0.0.0.0:10249/' /etc/kubernetes/kubeadm-config.yaml"; done
|
1 2 |
root@debian:~/prometheus# for ip in 192.168.1.112 192.168.1.113 192.168.1.114; do ssh root@$ip "sed -i 's/metricsBindAddress: 127.0.0.1:10249/metricsBindAddress: 0.0.0.0:10249/' /etc/kubernetes/kubeadm-config.yaml"; done |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
root@debian:~/prometheus# kubectl get pod -n monitoring NAME READY STATUS RESTARTS AGE alertmanager-prometheus-kube-prometheus-alertmanager-0 2/2 Running 0 153m prometheus-grafana-797c9f4574-2fgmk 3/3 Running 0 154m prometheus-kube-prometheus-operator-669b6cb6d6-ntl7t 1/1 Running 0 154m prometheus-kube-state-metrics-fdb4588c9-cxv6n 1/1 Running 0 154m prometheus-prometheus-kube-prometheus-prometheus-0 2/2 Running 0 2m51s prometheus-prometheus-node-exporter-2d9mf 1/1 Running 0 154m prometheus-prometheus-node-exporter-6wq9x 1/1 Running 0 154m prometheus-prometheus-node-exporter-7bddg 1/1 Running 4 154m prometheus-prometheus-node-exporter-8gdjv 1/1 Running 0 154m prometheus-prometheus-node-exporter-pjwcm 1/1 Running 2 (70m ago) 154m prometheus-prometheus-node-exporter-ztqts 1/1 Running 4 (26m ago) 154m root@debian:~/prometheus# kubectl get ingress -n monitoring NAME CLASS HOSTS ADDRESS PORTS AGE prometheus-grafana nginx grafana.test.local 192.168.1.191 80 154m prometheus-kube-prometheus-alertmanager nginx alertmanager.test.local 192.168.1.191 80 154m prometheus-kube-prometheus-prometheus nginx prometheus.test.local 192.168.1.191 80 154m |
http://grafana.test.local/

Victoria-metrics, grafana, alertmanager
|
1 2 3 4 5 6 7 8 9 |
NAME CHART VERSION APP VERSION DESCRIPTION vm/victoria-metrics-operator-crds 0.2.1 v0.59.2 VictoriaMetrics Operator CRDs vm/victoria-metrics-operator-crds 0.2.0 v0.59.0 VictoriaMetrics Operator CRDs vm/victoria-metrics-operator-crds 0.1.2 v0.58.0 Victoria Metrics Operator CRDs vm/victoria-metrics-operator-crds 0.1.1 v0.57.0 Victoria Metrics Operator CRDs vm/victoria-metrics-operator-crds 0.1.0 v0.56.0 Victoria Metrics Operator CRDs vm/victoria-metrics-operator-crds 0.0.3 v0.55.0 Victoria Metrics Operator CRDs vm/victoria-metrics-operator-crds 0.0.2 v0.55.0 Victoria Metrics Operator CRDs vm/victoria-metrics-operator-crds 0.0.1 v0.54.1 Victoria Metrics Operator CRDs |
|
1 2 3 4 5 6 7 8 |
helm search repo vm/victoria-metrics-k8s-stack -l NAME CHART VERSION APP VERSION DESCRIPTION vm/victoria-metrics-k8s-stack 0.52.0 v1.119.0 Kubernetes monitoring on VictoriaMetrics stack.... vm/victoria-metrics-k8s-stack 0.51.0 v1.119.0 Kubernetes monitoring on VictoriaMetrics stack.... vm/victoria-metrics-k8s-stack 0.50.1 v1.118.0 Kubernetes monitoring on VictoriaMetrics stack.... vm/victoria-metrics-k8s-stack 0.50.0 v1.118.0 Kubernetes monitoring on VictoriaMetrics stack.... vm/victoria-metrics-k8s-stack 0.49.0 v1.118.0 Kubernetes monitoring on VictoriaMetrics stack.... vm/victoria-metrics-k8s-stack 0.48.1 v1.117.1 Kubernetes monitoring on VictoriaMetrics stack.... |
создаём сертификат:
/etc/ansible/kubespray-official/victoria-metrics/certs/ca_openssl.cnf
|
1 2 3 4 5 6 7 8 9 |
[ v3_ca ] subjectAltName = @alt_names [ alt_names ] DNS.1 = vmsingle.test.local DNS.2 = alertmanager.test.local DNS.3 = grafana.test.local DNS.4 = vmalert.test.local |
/etc/ansible/kubespray-official/victoria-metrics/certs/vm_openssl.cnf
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
[ req ] default_bits = 4096 prompt = no default_md = sha256 distinguished_name = dn req_extensions = req_ext [ dn ] C = RU ST = YourState L = YourCity O = YourOrganization CN = vmsingle.test.local [ req_ext ] subjectAltName = @alt_names [ alt_names ] DNS.1 = vmsingle.test.local DNS.2 = alertmanager.test.local DNS.3 = grafana.test.local DNS.4 = vmalert.test.local |
|
1 2 3 4 5 |
kubectl create secret generic vm-tls \ --from-file=tls.crt=./vmsingle.crt \ --from-file=tls.key=./vmsingle.key \ --from-file=ca.crt=./ca.crt \ -n monitoring |
/etc/ansible/kubespray-official/victoria-metrics/values.yaml
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 |
defaultRules: create: true rules: general: false kubernetesSystem: false disabled: TooHighChurnRate: true TooManyScrapeErrors: true victoria-metrics-operator: enabled: true resources: limits: cpu: "500m" memory: "500Mi" requests: cpu: "100m" memory: "100Mi" crd: create: false cleanup: enabled: false operator: disable_prometheus_converter: false enable_converter_ownership: true useCustomConfigReloader: true psp_auto_creation_enabled: false vmsingle: enabled: true # spec for VMSingle crd rbac: create: true # https://github.com/VictoriaMetrics/operator/blob/master/docs/api.MD#vmsinglespec ingress: enabled: true ingressClassName: nginx annotations: nginx.ingress.kubernetes.io/force-ssl-redirect: "true" path: / hosts: - vmsingle.test.local tls: - secretName: vm-tls hosts: - vmsingle.test.local spec: retentionPeriod: "12d" replicaCount: 1 storage: storageClassName: nfs-client accessModes: - ReadWriteOnce resources: requests: storage: "10Gi" resources: limits: cpu: "2" memory: "1500Mi" requests: cpu: "1" memory: "1000Mi" extraArgs: maxLabelsPerTimeseries: "50" memory.allowedPercent: "90" search.maxUniqueTimeseries: "5000000" search.maxConcurrentRequests: "100" search.maxQueryDuration: "300s" search.maxSeries: "5000000" search.cacheTimestampOffset: "12m" search.logSlowQueryDuration: "25s" vmagent: enabled: true spec: selectAllByDefault: true extraArgs: promscrape.streamParse: "true" promscrape.dropOriginalLabels: "true" promscrape.maxScrapeSize: "3355443200" resources: limits: cpu: "500m" memory: "500Mi" requests: cpu: "100m" memory: "100Mi" kube-state-metrics: enabled: true resources: limits: cpu: "500m" memory: "500Mi" requests: cpu: "100m" memory: "100Mi" kubeScheduler: enabled: false kubeControllerManager: enabled: false kubeEtcd: enabled: false kubeProxy: enabled: false grafana: enabled: true adminPassword: "Secret123" deploymentStrategy: type: Recreate rbac: pspUseAppArmor: false ingress: enabled: true ingressClassName: nginx annotations: nginx.ingress.kubernetes.io/force-ssl-redirect: "true" path: / hosts: - grafana.test.local tls: - secretName: vm-tls hosts: - grafana.test.local env: GF_SERVER_ROOT_URL: https://grafana.test.local GF_USERS_ALLOW_SIGN_UP: false resources: limits: cpu: "500m" memory: "500Mi" requests: cpu: "100m" memory: "200Mi" persistence: enabled: true type: pvc size: "5Gi" storageClassName: nfs-client accessModes: - ReadWriteOnce # Alertmanager parameters alertmanager: enabled: true rbac: create: true externalURL: "https://alertmanager.test.local" ingress: enabled: true ingressClassName: nginx annotations: nginx.ingress.kubernetes.io/force-ssl-redirect: "true" path: / hosts: - alertmanager.test.local tls: - secretName: vm-tls hosts: - nginx spec: storage: volumeClaimTemplate: spec: storageClassName: nfs-client accessModes: - ReadWriteOnce resources: requests: storage: "1Gi" resources: limits: cpu: "500m" memory: "500Mi" requests: cpu: "100m" memory: "100Mi" #Vmalert configuration vmalert: enabled: true externalURL: "https://alertmanager.test.local" ingress: enabled: true ingressClassName: nginx annotations: nginx.ingress.kubernetes.io/force-ssl-redirect: "true" path: / hosts: - vmalert.test.local tls: - secretName: vm-tls hosts: - vmalert.test.local spec: resources: limits: cpu: "500m" memory: "500Mi" requests: cpu: "100m" memory: "100Mi" prometheus-node-exporter: enabled: true rbac: create: true resources: limits: cpu: "500m" memory: "500Mi" requests: cpu: "50m" memory: "50Mi" service: enabled: true type: ClusterIP port: 9100 targetPort: 9100 |
|
1 2 3 4 5 6 |
kubectl get ingress -n monitoring NAME CLASS HOSTS ADDRESS PORTS AGE vmalert-vmks-victoria-metrics-k8s-stack nginx vmalert.test.local 192.168.1.191 80, 443 26h vmalertmanager-vmks-victoria-metrics-k8s-stack nginx alertmanager.test.local 192.168.1.191 80, 443 26h vmks-grafana nginx grafana.test.local 192.168.1.191 80, 443 26h vmsingle-vmks-victoria-metrics-k8s-stack nginx vmsingle.test.local 192.168.1.191 80, 443 26h |
Exporter on node not in the k8s
|
1 2 3 4 5 6 7 8 9 10 11 12 |
global: # как часто опрашивать targets scrape_interval: 15s # сколько ждать ответа от target scrape_timeout: 10s scrape_configs: - job_name: 'node-vault' metrics_path: /metrics static_configs: - targets: ['192.168.1.103:9100'] |
тут указываем job_name: и группируем его по 'node-vault' , т.е. в списке мы будем получать все ноды под этой группой
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
version: '3.5' services: node-exporter: image: prom/node-exporter volumes: - /proc:/host/proc:ro - /sys:/host/sys:ro - /:/rootfs:ro command: - '--path.procfs=/host/proc' - '--path.sysfs=/host/sys' - '--path.rootfs=/rootfs' - '--collector.filesystem.mount-points-exclude' - "^/(sys|proc|dev|host|etc|rootfs/var/lib/docker/containers|rootfs/var/lib/docker/overlay2|rootfs/run/docker/netns|rootfs/var/lib/docker/aufs)($$|/)" expose: - 9100 restart: always network_mode: host vmagent: image: victoriametrics/vmagent volumes: - ./data/vmagentdata:/vmagentdata - ./prometheus.yml:/etc/prometheus/prometheus.yml command: - '--promscrape.config=/etc/prometheus/prometheus.yml' - '--remoteWrite.url=http://vmsingle.test.local/api/v1/write' - '--remoteWrite.showURL' - '--remoteWrite.tlsInsecureSkipVerify=true' restart: always network_mode: host |
тут указываем для vmagent '--remoteWrite.url=http://vmsingle.test.local/api/v1/write' это адрес нашего vmsingle.

|
1 2 3 4 5 6 7 8 |
metrics: enabled: true port: 10254 service: enabled: true annotations: prometheus.io/scrape: "true" prometheus.io/port: "10254" |
а чтобы метрики попали в victoria-metrics нужно использовать следующий конфиг:
/etc/ansible/kubespray-official/victoria-metrics/scrape-ingress-controller.yaml
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
apiVersion: operator.victoriametrics.com/v1beta1 kind: VMServiceScrape metadata: name: ingress-nginx namespace: ingress-nginx spec: selector: matchLabels: app.kubernetes.io/name: ingress-nginx namespaceSelector: matchNames: - ingress-nginx endpoints: - port: metrics path: /metrics interval: 15s |
применяем
kubectl apply -f scrape-ingress-controller.yaml
далее будут доступны такие метрики как:
nginx_ingress_controller_requests
nginx_ingress_controller_nginx_process_connections
и другие.
Metrics servers
root@debian:~/prometheus# helm upgrade --install metrics-server metrics-server/metrics-server --set args={"--kubelet-insecure-tls"} -n monitoring
проверяем:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
root@debian:~/prometheus# kubectl top pod -n monitoring NAME CPU(cores) MEMORY(bytes) alertmanager-prometheus-kube-prometheus-alertmanager-0 1m 43Mi metrics-server-dff86cf4f-t5g2h 7m 21Mi prometheus-grafana-797c9f4574-2fgmk 2m 239Mi prometheus-kube-prometheus-operator-669b6cb6d6-ntl7t 2m 29Mi prometheus-kube-state-metrics-fdb4588c9-cxv6n 4m 21Mi prometheus-prometheus-kube-prometheus-prometheus-0 46m 435Mi prometheus-prometheus-node-exporter-2d9mf 5m 13Mi prometheus-prometheus-node-exporter-6wq9x 2m 12Mi prometheus-prometheus-node-exporter-7bddg 2m 16Mi prometheus-prometheus-node-exporter-8gdjv 5m 12Mi prometheus-prometheus-node-exporter-pjwcm 2m 18Mi prometheus-prometheus-node-exporter-ztqts 2m 18Mi root@debian:~/prometheus# kubectl top node NAME CPU(cores) CPU% MEMORY(bytes) MEMORY% kub-master1.test.local 226m 7% 2332Mi 83% kub-master2.test.local 217m 7% 2530Mi 90% kub-master3.test.local 235m 7% 2321Mi 82% kub-worker1.test.local 158m 3% 2334Mi 62% kub-worker2.test.local 127m 3% 2186Mi 58% kub-worker3.test.local 125m 3% 2034Mi 54% |
Правим COREDNS и LOCALDNS чтобы работал resolv
cat > coredns.yaml
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
apiVersion: v1 kind: ConfigMap metadata: name: coredns namespace: kube-system data: Corefile: | test.local:53 { forward . 192.168.1.100:53 192.168.1.101:53 } .:53 { errors health { lameduck 5s } ready kubernetes cluster.local in-addr.arpa ip6.arpa { pods insecure fallthrough in-addr.arpa ip6.arpa } prometheus :9153 forward . 192.168.1.100 192.168.1.101 8.8.8.8 8.8.4.4 { prefer_udp max_concurrent 1000 } cache 30 loop reload loadbalance } |
kubectl apply -f coredns.yaml
тут мы указали test.local чтоб искал ip в нашем freeipa 192.168.1.100 192.168.1.101
ещё правил nodelocaldns
kubectl edit configmaps -n kube-system nodelocaldns
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
apiVersion: v1 data: Corefile: | test.local:53 { errors cache 30 bind 169.254.25.10 forward . 192.168.1.100 192.168.1.101 } cluster.local:53 { errors cache { success 9984 30 denial 9984 5 } reload loop bind 169.254.25.10 forward . 10.233.0.3 { force_tcp } prometheus :9253 health 169.254.25.10:9254 } in-addr.arpa:53 { errors cache 30 reload loop bind 169.254.25.10 forward . 10.233.0.3 { force_tcp } prometheus :9253 } ip6.arpa:53 { errors cache 30 reload loop bind 169.254.25.10 forward . 10.233.0.3 { force_tcp } prometheus :9253 } .:53 { errors cache 30 reload loop bind 169.254.25.10 forward . 8.8.8.8 8.8.4.4 prometheus :9253 } |
мы добавляем туда:
|
1 2 3 4 5 6 |
test.local:53 { errors cache 30 bind 169.254.25.10 forward . 192.168.1.100 192.168.1.101 } |
и рестартуем:
kubectl rollout restart -n kube-system daemonset nodelocaldns
Логирование - elk
apt-get update
apt-get install ca-certificates curl
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc
|
1 2 3 4 |
echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian \ $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \ tee /etc/apt/sources.list.d/docker.list > /dev/null |
apt-get update
|
1 2 3 4 5 6 7 8 9 10 11 12 |
docker run --name elastic-helm-charts-certs -i -w /tmp \ elasticsearch:8.5.1 \ /bin/sh -c " \ elasticsearch-certutil ca --out /tmp/elastic-stack-ca.p12 --pass '' && \ elasticsearch-certutil cert --name security-master --dns elasticsearch-master --dns kibana-kibana --dns kibana.test.local --dns elasticsearch-master* --dns logstash-logstash-headless --dns logstash* --days 7000 --ca /tmp/elastic-stack-ca.p12 --pass '' --ca-pass '' --out /tmp/elastic-certificates.p12" && \ docker cp elastic-helm-charts-certs:/tmp/elastic-certificates.p12 ./ && \ docker rm -f elastic-helm-charts-certs && \ openssl pkcs12 -nodes -passin pass:'' -in elastic-certificates.p12 -out elastic-certificate.pem && \ openssl x509 -outform der -in elastic-certificate.pem -out elastic-certificate.crt && \ kubectl create secret -n elk generic elastic-certificates --from-file=elastic-certificates.p12 && \ kubectl create secret -n elk generic elastic-certificate-pem --from-file=elastic-certificate.pem && \ kubectl create secret -n elk generic elastic-certificate-crt --from-file=elastic-certificate.crt |
проверяем что сертификаты созданы
|
1 2 3 4 5 6 |
root@debian:~/elk/certs# kubectl get secrets -n elk NAME TYPE DATA AGE elastic-certificate-crt Opaque 1 60s elastic-certificate-pem Opaque 1 60s elastic-certificates Opaque 1 60s |
root@debian:~/elk/certs# openssl pkey -in elastic-certificate.pem -out cert.key
root@debian:~/elk/certs# openssl crl2pkcs7 -nocrl -certfile elastic-certificate.pem | openssl pkcs7 -print_certs -out cert.crt
правим файл elastic.yaml
можем задать хранилище, я буду использовать
|
1 2 3 4 5 6 |
root@debian:~# kubectl get pod -n elk -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES elasticsearch-master-0 1/1 Running 0 56m 10.233.79.23 kub-worker3.test.local <none> <none> elasticsearch-master-1 1/1 Running 0 56m 10.233.67.240 kub-worker1.test.local <none> <none> elasticsearch-master-2 1/1 Running 0 56m 10.233.107.87 kub-worker2.test.local <none> <none> |
настраиваем kibana, для этого в файле kibana.yaml правим домен на наш kibana.test.local и ставим:
root@debian:~/elk# helm upgrade --install kibana elastic/kibana -n elk -f kibana.yaml
ставим logstash
|
1 2 3 4 5 6 7 8 9 10 11 |
root@debian:~/elk# kubectl get pod -n elk NAME READY STATUS RESTARTS AGE elasticsearch-master-0 1/1 Running 0 17m elasticsearch-master-1 1/1 Running 1 (18m ago) 86m elasticsearch-master-2 1/1 Running 1 (17m ago) 140m filebeat-filebeat-25grp 1/1 Running 3 (17m ago) 67m filebeat-filebeat-dvwkz 1/1 Running 2 (17m ago) 67m filebeat-filebeat-fd9q6 1/1 Running 1 (18m ago) 67m kibana-kibana-57c7f6b879-7vfm8 1/1 Running 1 (18m ago) 80m logstash-logstash-0 1/1 Running 0 8m25s |
|
1 2 3 4 |
root@debian:~# kubectl get ingress -n elk NAME CLASS HOSTS ADDRESS PORTS AGE kibana-kibana nginx kibana.test.local 192.168.1.191 80, 443 5m58s |
смотрим пароль:
root@debian:~/elk# kubectl get secrets --namespace=elk elasticsearch-master-credentials -ojsonpath='{.data.password}' | base64 -d
k57UN6bFQ01tLjhU
заходим
https://kibana.test.local/
в качестве логина используем elastic
в качестве пароля k57UN6bFQ01tLjhU


проверим что данные попадают в индекс:

чтобы посмотреть логи нужно создать data views

проверяем

создадим пользователя с правами админа

всё, можно логиниться под новыми кредами admin Secret123
Loki
в качестве альтернативы рассмотрим использование loki, в качестве хранилища у нас будет s3
Рассмотрим 2 варианта
1 когда s3-minio использует логин пароль (без ldap)
2 когда для s3-minio включён ldap
1 Вариант - у s3 minio НЕ включён ldap и мы создаём обычного пользователя.
создаём бакет

создаём policy:

|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:ListBucket", "s3:ListBucketMultipartUploads", "s3:GetBucketLocation" ], "Resource": [ "arn:aws:s3:::loki-bucket" ] }, { "Effect": "Allow", "Action": [ "s3:ListMultipartUploadParts", "s3:PutObject", "s3:AbortMultipartUpload", "s3:DeleteObject", "s3:GetObject" ], "Resource": [ "arn:aws:s3:::loki-bucket/*" ] } ] } |
создаём пользователя с нашими access-key и secret-key

создаём ключи доступа
- Access Key (Ключ доступа): Уникальный идентификатор пользователя.
- Secret Key (Секретный ключ): Пароль для доступа.

получаем
access key iXToAdDvt0gx0fEJ1bXl
secret key 8bpWxY7fNKi0zEL85UI9Sg22floLjqMhPdxs2kVS
проверяем что нужный к нашему бакету добавлен нужный пользователь

теперь приступим к настройке LOKI
вот наш values
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 |
loki: server: grpc_server_max_recv_msg_size: 8388608 grpc_server_max_send_msg_size: 8388608 useTestSchema: false auth_enabled: false structuredConfig: memberlist: cluster_label: "loki" schema_config: configs: - from: "2020-09-07" store: tsdb object_store: "s3" schema: "v13" index: period: "24h" prefix: "loki_index_" limits_config: retention_period: 14d ingestion_rate_mb: 4 ingestion_burst_size_mb: 6 # compactor: # shared_store: s3 # compaction_interval: 10m # retention_enabled: true # retention_delete_delay: 2h # retention_delete_worker_count: 150 storage: # filesystem: null # type: s3 bucketNames: chunks: loki-bucket ruler: loki-bucket admin: loki-bucket s3: endpoint: http://192.168.1.120:9000 # http://s3-minio.test.local:9000 accessKeyId: "ARMMLGAU2B2Q4K7H92OU" secretAccessKey: "4AkkOeozat6SRS7yZbdIwuWnND3wa1LD3q+4vtmm" insecure: true s3ForcePathStyle: true region: null sse_encryption: false http_config: idle_conn_timeout: 90s response_header_timeout: 0s insecure_skip_verify: true ingester: autoforget_unhealthy: true storage_config: memcached: chunk_cache: enabled: false results_cache: enabled: false commonConfig: replication_factor: 1 serviceAccount: create: true name: loki-service-account annotations: {} monitoring: dashboards: enabled: false rules: enabled: true alerting: true labels: release: kube-prometheus-stack serviceMonitor: enabled: true labels: release: kube-prometheus-stack interval: 15s lokiCanary: resources: requests: cpu: 100m memory: 256Mi limits: cpu: 200m memory: 512Mi selfMonitoring: grafanaAgent: resources: requests: cpu: 200m memory: 256Mi limits: cpu: 500m memory: 512Mi write: replicas: 1 extraArgs: - "-log.level=info" resources: requests: cpu: 100m memory: 300Mi limits: cpu: 500m memory: 800Mi persistence: enabled: true storageClass: nfs-client size: 10Gi read: replicas: 1 extraArgs: - "-log.level=info" resources: requests: cpu: 100m memory: 300Mi limits: cpu: 100m memory: 300Mi # persistence: # enabled: false # storageClass: nfs-client # size: 10Gi backend: replicas: 1 extraArgs: - "-log.level=info" resources: requests: cpu: 100m memory: 300Mi limits: cpu: 100m memory: 300Mi persistence: enabled: true storageClass: nfs-client size: 10Gi lokiCanary: enabled: false resources: requests: cpu: 100m memory: 300Mi limits: cpu: 100m memory: 300Mi gateway: replicas: 1 resources: requests: cpu: 100m memory: 300Mi limits: cpu: 100m memory: 300Mi nginxConfig: resolver: "coredns.kube-system.svc.cluster.local" # memberlist: # join_members: # - loki-memberlist.loki.svc.cluster.local memberlist: service: publishNotReadyAddresses: false join_members: - loki-memberlist.loki.svc.cluster.local:7946 #- loki-memberlist.loki.svc.cluster.local # Disable caches memcached: enabled: false # If specific sections are used memcached_chunks: enabled: false memcached_frontend: enabled: false chunksCache: enabled: false resultsCache: enabled: false sidecar: rules: enabled: false test: enabled: false enterprise: enabled: false # gateway: # enabled: false |
в этом values endpoint используем IP а не хостнейм потому что pod loki backend не резолвит домен - хотя все остальные pod-ы нормально резолвят домены с freeipa
root@debian:~# helm repo add grafana https://grafana.github.io/helm-charts
root@debian:~# helm repo update
kubectl create ns loki
2. Второй вариант когда у s3-minio включён ldap




|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:ListBucket", "s3:ListBucketMultipartUploads", "s3:GetBucketLocation" ], "Resource": [ "arn:aws:s3:::loki-bucket" ] }, { "Effect": "Allow", "Action": [ "s3:ListMultipartUploadParts", "s3:PutObject", "s3:AbortMultipartUpload", "s3:DeleteObject", "s3:GetObject" ], "Resource": [ "arn:aws:s3:::loki-bucket/*" ] } ] } |
|
1 2 3 |
root@s3-minio-1:~# mc idp ldap policy attach myminio loki-policy --user=user-loki Attached Policies: [loki-policy] To User: user-loki |
а теперь создаём access-key и secret-key
root@s3-minio-1:~# mc admin user svcacct add myminio user-loki
|
1 2 3 4 |
root@s3-minio-1:~# mc admin user svcacct add myminio user-loki Access Key: ARMMLGAU2B2Q4K7H92OU Secret Key: 4AkkOeozat6SRS7yZbdIwuWnND3wa1LD3q+4vtmm Expiration: no-expiry |
всё, теперь правим наш values c новыми access и secret key
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 |
loki: server: grpc_server_max_recv_msg_size: 8388608 grpc_server_max_send_msg_size: 8388608 useTestSchema: false auth_enabled: false structuredConfig: memberlist: cluster_label: "loki" schema_config: configs: - from: "2020-09-07" store: tsdb object_store: "s3" schema: "v13" index: period: "24h" prefix: "loki_index_" limits_config: retention_period: 14d ingestion_rate_mb: 4 ingestion_burst_size_mb: 6 # compactor: # shared_store: s3 # compaction_interval: 10m # retention_enabled: true # retention_delete_delay: 2h # retention_delete_worker_count: 150 storage: # filesystem: null # type: s3 bucketNames: chunks: loki-bucket ruler: loki-bucket admin: loki-bucket s3: endpoint: http://192.168.1.120:9000 # http://s3-minio.test.local:9000 accessKeyId: "ARMMLGAU2B2Q4K7H92OU" secretAccessKey: "4AkkOeozat6SRS7yZbdIwuWnND3wa1LD3q+4vtmm" insecure: true s3ForcePathStyle: true region: null sse_encryption: false http_config: idle_conn_timeout: 90s response_header_timeout: 0s insecure_skip_verify: true ingester: autoforget_unhealthy: true storage_config: memcached: chunk_cache: enabled: false results_cache: enabled: false commonConfig: replication_factor: 1 serviceAccount: create: true name: loki-service-account annotations: {} monitoring: dashboards: enabled: false rules: enabled: true alerting: true labels: release: kube-prometheus-stack serviceMonitor: enabled: true labels: release: kube-prometheus-stack interval: 15s lokiCanary: resources: requests: cpu: 100m memory: 256Mi limits: cpu: 200m memory: 512Mi selfMonitoring: grafanaAgent: resources: requests: cpu: 200m memory: 256Mi limits: cpu: 500m memory: 512Mi write: replicas: 1 extraArgs: - "-log.level=info" resources: requests: cpu: 100m memory: 300Mi limits: cpu: 100m memory: 300Mi persistence: enabled: true storageClass: nfs-client size: 10Gi read: replicas: 1 extraArgs: - "-log.level=info" resources: requests: cpu: 100m memory: 300Mi limits: cpu: 100m memory: 300Mi persistence: enabled: true storageClass: nfs-client size: 10Gi backend: replicas: 1 extraArgs: - "-log.level=info" resources: requests: cpu: 100m memory: 300Mi limits: cpu: 100m memory: 300Mi persistence: enabled: true storageClass: nfs-client size: 10Gi lokiCanary: enabled: false resources: requests: cpu: 100m memory: 300Mi limits: cpu: 100m memory: 300Mi gateway: replicas: 1 resources: requests: cpu: 100m memory: 300Mi limits: cpu: 100m memory: 300Mi nginxConfig: resolver: "coredns.kube-system.svc.cluster.local" # memberlist: # join_members: # - loki-memberlist.loki.svc.cluster.local memberlist: service: publishNotReadyAddresses: true join_members: - loki-memberlist.loki.svc.cluster.local # Disable caches memcached: enabled: false # If specific sections are used memcached_chunks: enabled: false memcached_frontend: enabled: false chunksCache: enabled: false resultsCache: enabled: false sidecar: rules: enabled: false test: enabled: false enterprise: enabled: false # gateway: # enabled: false |
и инсталлим:
root@client:~/LOKI# helm upgrade --install loki grafana/loki --namespace loki --version 6.30.1 -f loki.yaml
теперь ставим сборщик логов promtail
Loki - s3 bucket (seaweedfs)
заходим на мастер
root@ansible:/etc/ansible# ssh 192.168.1.121
подключаемся
root@debian:~# weed shell
проверяем что есть:
|
1 2 3 4 5 |
> s3.bucket.list pvc-5f6c6f0c-5f1a-44ec-be45-0542b83712a4 size:16 chunk:0 pvc-a52116d8-4e15-4f55-8413-5f3e0ba91e90 size:27674376 chunk:415 pvc-ab8f5b44-84d1-4224-b9d4-3a563824f748 size:7299778472 chunk:4061 test-bucket size:200680 chunk:1 |
создаём новый бакет loki-bucket:
> s3.bucket.create --name loki-bucket
|
1 2 3 4 5 6 7 8 9 10 |
> s3.bucket.create --name loki-bucket create bucket under /buckets created bucket loki-bucket > s3.bucket.list loki-bucket size:0 chunk:0 pvc-5f6c6f0c-5f1a-44ec-be45-0542b83712a4 size:16 chunk:0 pvc-a52116d8-4e15-4f55-8413-5f3e0ba91e90 size:27794664 chunk:415 pvc-ab8f5b44-84d1-4224-b9d4-3a563824f748 size:7355498792 chunk:4061 test-bucket size:200680 chunk:1 |
ок, настраиваем values
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 |
loki: server: grpc_server_max_recv_msg_size: 8388608 grpc_server_max_send_msg_size: 8388608 useTestSchema: false auth_enabled: false structuredConfig: memberlist: cluster_label: "loki" schema_config: configs: - from: "2020-09-07" store: tsdb object_store: "s3" schema: "v13" index: period: "24h" prefix: "loki_index_" limits_config: retention_period: 14d ingestion_rate_mb: 4 ingestion_burst_size_mb: 6 # compactor: # shared_store: s3 # compaction_interval: 10m # retention_enabled: true # retention_delete_delay: 2h # retention_delete_worker_count: 150 storage: # filesystem: null # type: s3 bucketNames: chunks: loki-bucket ruler: loki-bucket admin: loki-bucket s3: endpoint: http://192.168.1.124:8333 # http://s3-minio.test.local:9000 accessKeyId: "dummy" secretAccessKey: "dummy" insecure: true s3ForcePathStyle: true region: null sse_encryption: false http_config: idle_conn_timeout: 90s response_header_timeout: 0s insecure_skip_verify: true ingester: autoforget_unhealthy: true storage_config: memcached: chunk_cache: enabled: false results_cache: enabled: false commonConfig: replication_factor: 1 serviceAccount: create: true name: loki-service-account annotations: {} monitoring: dashboards: enabled: false rules: enabled: true alerting: true labels: release: kube-prometheus-stack serviceMonitor: enabled: true labels: release: kube-prometheus-stack interval: 15s lokiCanary: resources: requests: cpu: 100m memory: 256Mi limits: cpu: 200m memory: 512Mi selfMonitoring: grafanaAgent: resources: requests: cpu: 200m memory: 256Mi limits: cpu: 500m memory: 512Mi write: replicas: 1 extraArgs: - "-log.level=info" resources: requests: cpu: 100m memory: 300Mi limits: cpu: 100m memory: 300Mi persistence: enabled: true storageClass: seaweedfs-storage size: 5Gi read: replicas: 1 extraArgs: - "-log.level=info" resources: requests: cpu: 100m memory: 300Mi limits: cpu: 100m memory: 300Mi persistence: enabled: true storageClass: seaweedfs-storage size: 5Gi backend: replicas: 1 extraArgs: - "-log.level=info" resources: requests: cpu: 100m memory: 300Mi limits: cpu: 100m memory: 300Mi persistence: enabled: true storageClass: seaweedfs-storage size: 5Gi lokiCanary: enabled: false resources: requests: cpu: 100m memory: 300Mi limits: cpu: 100m memory: 300Mi gateway: replicas: 1 resources: requests: cpu: 100m memory: 300Mi limits: cpu: 100m memory: 300Mi nginxConfig: resolver: "coredns.kube-system.svc.cluster.local" # memberlist: # join_members: # - loki-memberlist.loki.svc.cluster.local memberlist: service: publishNotReadyAddresses: true join_members: - loki-memberlist.loki.svc.cluster.local # Disable caches memcached: enabled: false # If specific sections are used memcached_chunks: enabled: false memcached_frontend: enabled: false chunksCache: enabled: false resultsCache: enabled: false sidecar: rules: enabled: false test: enabled: false enterprise: enabled: false # gateway: # enabled: false |
Promtail
Promtail — агент, который читает и отправляет логи на сервер. Он устанавливается как daemonset так как логи собираются со всех нод в кластере.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
daemonset: enabled: true resources: limits: cpu: 200m memory: 128Mi requests: cpu: 100m memory: 128Mi serviceMonitor: enabled: true labels: release: kube-prometheus-stack prometheusRule: enabled: false config: logLevel: info clients: - url: http://loki-gateway.loki.svc.cluster.local/loki/api/v1/push tenant_id: 1 |
проверяем:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
root@client:~/LOKI# kubectl get pod -n loki -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES loki-backend-0 1/1 Running 1 (18h ago) 18h 10.233.79.43 kub-worker3.test.local <none> <none> loki-gateway-5456868ddc-kdddj 1/1 Running 2 (133m ago) 18h 10.233.79.1 kub-worker3.test.local <none> <none> loki-read-6f49c674b7-pzh69 1/1 Running 1 (18h ago) 18h 10.233.79.60 kub-worker3.test.local <none> <none> loki-write-0 1/1 Running 1 (18h ago) 18h 10.233.79.63 kub-worker3.test.local <none> <none> promtail-6r9g8 1/1 Running 0 103m 10.233.79.10 kub-worker3.test.local <none> <none> promtail-9ft9z 1/1 Running 0 102m 10.233.126.121 kub-master3.test.local <none> <none> promtail-j8wmw 1/1 Running 0 103m 10.233.107.120 kub-worker2.test.local <none> <none> promtail-mz487 1/1 Running 0 103m 10.233.66.194 kub-master1.test.local <none> <none> promtail-w8w52 1/1 Running 0 104m 10.233.122.200 kub-master2.test.local <none> <none> promtail-xrvfl 1/1 Running 0 104m 10.233.67.251 kub-worker1.test.local <none> <none> |
как видим promtail установлен на каждой ноде в кластере. Проверим так же наш бакет:

как видим логи собираются.
Grafana - Loki
Идём в нашу grafana
http://grafana.test.local/login
настраиваем datasource

для Connection URL указываем
http://loki-read.loki.svc.cluster.local:3100

всё этого хватает, дальше скролим вниз и нажимаем save & test

как видим получаем сообщение
Data source successfully connected.




Auto unseal для vault кластера
в чём идея, у нас уже есть кластер vault
идея в том что в k8s будет так же поднят vault который будет настроен для разблокировки нашего кластера, а vault в кластере будет разблокироваться cronjob.
добавляем репозиторй
устанавливаем vault, отмечу что я использую в качестве бэкенда raft а для него использую диски от провиженера
root@client:~/vault-autounseal# helm upgrade --install -n vault vault hashicorp/vault -f values.yaml
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
apiVersion: batch/v1 kind: CronJob metadata: name: vault-unseal-cronjob namespace: vault spec: schedule: "*/2 * * * *" # Запускать каждые 2 минуты successfulJobsHistoryLimit: 1 # Хранить только последнее успешное задание failedJobsHistoryLimit: 1 # Хранить только последнее неудачное задание jobTemplate: spec: template: spec: containers: - name: vault-unseal image: alpine:latest command: - /bin/sh - -c - | apk add --no-cache curl jq bind-tools netcat-openbsd KEYS=$(jq -r '.unseal_keys_b64[]' /config/cluster-keys.json) for POD in vault-0 vault-1 vault-2 do POD_ADDRESS="$POD.vault-internal.vault.svc.cluster.local" echo "Checking if $POD_ADDRESS is sealed…" SEALED=$(curl -s http://$POD_ADDRESS:8200/v1/sys/health | jq -r '.sealed') if [ "$SEALED" = "true" ]; then echo "$POD_ADDRESS is sealed. Proceeding to unseal." for KEY in $KEYS do RESPONSE=$(curl --silent --request POST --data "{\"key\":\"$KEY\"}" http://$POD_ADDRESS:8200/v1/sys/unseal) echo "Unseal response from $POD: $RESPONSE" SEALED=$(echo "$RESPONSE" | jq -r '.sealed') if [ "$SEALED" = "false" ]; then echo "$POD_ADDRESS is now unsealed." break fi done else echo "$POD_ADDRESS is already unsealed." fi done volumeMounts: - name: config mountPath: /config restartPolicy: OnFailure volumes: - name: config configMap: name: vault-unseal-keys |
теперь приступим к настройке autounseal с помощью tranzit
root@client:~/vault-autounseal# kubectl exec -it -n vault vault-0 -- sh
/ $ vault write -f transit/keys/vault-unseal-key
/ $ cd /tmp/
/tmp $ cat > transit-policy.yaml
|
1 2 3 4 5 6 7 8 9 10 11 |
path "transit/encrypt/vault-unseal-key" { capabilities = [ "update" ] } path "transit/decrypt/vault-unseal-key" { capabilities = [ "update" ] } path "transit/keys/vault-unseal-key" { capabilities = ["read"] } |
/tmp $ vault policy write unseal-policy transit-policy.yaml
/tmp $ vault token create -policy=unseal-policy
получаем token
|
1 2 3 4 5 6 7 8 9 10 11 |
/tmp $ vault token create -policy=unseal-policy Key Value --- ----- token s.UAiqZBv9lIwDS6ZzOpDdf3XW token_accessor kYcVArPi5nVu9o0lKRqdNbNE token_duration 768h token_renewable true token_policies ["default" "unseal-policy"] identity_policies [] policies ["default" "unseal-policy"] |
теперь идём в конфигурации наших vault на виртуалках и добавляем следующее:
|
1 2 3 4 5 6 7 8 9 |
seal "transit" { address = "http://vault-unseal.test.local:80" token = "s.UAiqZBv9lIwDS6ZzOpDdf3XW" key_name = "vault-unseal-key" mount_path = "transit/" disable_renewal = "false" tls_skip_verify = "true" } |
root@client:~/vault-autounseal# ssh 192.168.1.103
root@vault1:~# nano /etc/vault.d/vault.hcl
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
cluster_addr = "https://vault1.test.local:8201" api_addr = "https://vault1.test.local:8200" disable_mlock = true ui = true listener "tcp" { address = "0.0.0.0:8200" tls_ca_cert_file = "/opt/vault/tls/my_crt_file.pem" tls_cert_file = "/opt/vault/tls/my_crt_file.pem" tls_key_file = "/opt/vault/tls/my_crt_file.pem" } storage "raft" { path = "/opt/vault/data" node_id = "vault1.test.local" retry_join { leader_tls_servername = "vault1.test.local" leader_api_addr = "https://vault1.test.local:8200" leader_ca_cert_file = "/opt/vault/tls/my_crt_file.pem" leader_client_cert_file = "/opt/vault/tls/my_crt_file.pem" leader_client_key_file = "/opt/vault/tls/my_crt_file.pem" } retry_join { leader_tls_servername = "vault2.test.local" leader_api_addr = "https://vault2.test.local:8200" leader_ca_cert_file = "/opt/vault/tls/my_crt_file.pem" leader_client_cert_file = "/opt/vault/tls/my_crt_file.pem" leader_client_key_file = "/opt/vault/tls/my_crt_file.pem" } retry_join { leader_tls_servername = "vault3.test.local" leader_api_addr = "https://vault3.test.local:8200" leader_ca_cert_file = "/opt/vault/tls/my_crt_file.pem" leader_client_cert_file = "/opt/vault/tls/my_crt_file.pem" leader_client_key_file = "/opt/vault/tls/my_crt_file.pem" } } seal "transit" { address = "http://vault-unseal.test.local:80" token = "s.UAiqZBv9lIwDS6ZzOpDdf3XW" key_name = "vault-unseal-key" mount_path = "transit/" disable_renewal = "false" tls_skip_verify = "true" } |
root@client:~/vault-autounseal# ssh 192.168.1.104
root@vault2:~# nano /etc/vault.d/vault.hcl
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
cluster_addr = "https://vault2.test.local:8201" api_addr = "https://vault2.test.local:8200" disable_mlock = true ui = true listener "tcp" { address = "0.0.0.0:8200" tls_ca_cert_file = "/opt/vault/tls/my_crt_file.pem" tls_cert_file = "/opt/vault/tls/my_crt_file.pem" tls_key_file = "/opt/vault/tls/my_crt_file.pem" } storage "raft" { path = "/opt/vault/data" node_id = "vault2.test.local" retry_join { leader_tls_servername = "vault1.test.local" leader_api_addr = "https://vault1.test.local:8200" leader_ca_cert_file = "/opt/vault/tls/my_crt_file.pem" leader_client_cert_file = "/opt/vault/tls/my_crt_file.pem" leader_client_key_file = "/opt/vault/tls/my_crt_file.pem" } retry_join { leader_tls_servername = "vault2.test.local" leader_api_addr = "https://vault2.test.local:8200" leader_ca_cert_file = "/opt/vault/tls/my_crt_file.pem" leader_client_cert_file = "/opt/vault/tls/my_crt_file.pem" leader_client_key_file = "/opt/vault/tls/my_crt_file.pem" } retry_join { leader_tls_servername = "vault3.test.local" leader_api_addr = "https://vault3.test.local:8200" leader_ca_cert_file = "/opt/vault/tls/my_crt_file.pem" leader_client_cert_file = "/opt/vault/tls/my_crt_file.pem" leader_client_key_file = "/opt/vault/tls/my_crt_file.pem" } } seal "transit" { address = "http://vault-unseal.test.local:80" token = "s.UAiqZBv9lIwDS6ZzOpDdf3XW" key_name = "vault-unseal-key" mount_path = "transit/" disable_renewal = "false" tls_skip_verify = "true" } |
root@client:~/vault-autounseal# ssh 192.168.1.105
root@vault3:~# nano /etc/vault.d/vault.hcl
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
cluster_addr = "https://vault3.test.local:8201" api_addr = "https://vault3.test.local:8200" disable_mlock = true ui = true listener "tcp" { address = "0.0.0.0:8200" tls_ca_cert_file = "/opt/vault/tls/my_crt_file.pem" tls_cert_file = "/opt/vault/tls/my_crt_file.pem" tls_key_file = "/opt/vault/tls/my_crt_file.pem" } storage "raft" { path = "/opt/vault/data" node_id = "vault3.test.local" retry_join { leader_tls_servername = "vault1.test.local" leader_api_addr = "https://vault1.test.local:8200" leader_ca_cert_file = "/opt/vault/tls/my_crt_file.pem" leader_client_cert_file = "/opt/vault/tls/my_crt_file.pem" leader_client_key_file = "/opt/vault/tls/my_crt_file.pem" } retry_join { leader_tls_servername = "vault2.test.local" leader_api_addr = "https://vault2.test.local:8200" leader_ca_cert_file = "/opt/vault/tls/my_crt_file.pem" leader_client_cert_file = "/opt/vault/tls/my_crt_file.pem" leader_client_key_file = "/opt/vault/tls/my_crt_file.pem" } retry_join { leader_tls_servername = "vault3.test.local" leader_api_addr = "https://vault3.test.local:8200" leader_ca_cert_file = "/opt/vault/tls/my_crt_file.pem" leader_client_cert_file = "/opt/vault/tls/my_crt_file.pem" leader_client_key_file = "/opt/vault/tls/my_crt_file.pem" } } seal "transit" { address = "http://vault-unseal.test.local:80" token = "s.UAiqZBv9lIwDS6ZzOpDdf3XW" key_name = "vault-unseal-key" mount_path = "transit/" disable_renewal = "false" tls_skip_verify = "true" } |
Добавляем в хосты наш сервер(который в кубере)
root@vault1:~# echo "192.168.1.191 vault-unseal.test.local" >> /etc/hosts
root@vault2:~# echo "192.168.1.191 vault-unseal.test.local" >> /etc/hosts
root@vault3:~# echo "192.168.1.191 vault-unseal.test.local" >> /etc/hosts
[root@vault1 ~]# systemctl restart vault
[root@vault2 ~]# systemctl restart vault
[root@vault3 ~]# systemctl restart vault
теперь нужно распечатать наше хранилище с параметром:
'-migrate'
после каждой команды нужно ввести ключ для разблокировки
[root@vault1 ~]# vault operator unseal -migrate
[root@vault1 ~]# vault operator unseal -migrate
[root@vault1 ~]# vault operator unseal -migrate
[root@vault2 ~]# vault operator unseal -migrate
[root@vault2 ~]# vault operator unseal -migrate
[root@vault2 ~]# vault operator unseal -migrate
[root@vault3 ~]# vault operator unseal -migrate
[root@vault3 ~]# vault operator unseal -migrate
[root@vault3 ~]# vault operator unseal -migrate
напомню вот мои ключи
|
1 2 3 4 5 |
184bdc202f516bee92f449ec14a2e92ca22b05aea7032f74badb77303863cd8aeb c9726a282be011b8e21a62ab307d30104e1bea0701c7d9406e05d4ae68b57344af 8034b00602cc135c602025b9117815f0ea1a38d2ce7c812d3b740b0071b60e0696 1304a0e6cea51b5192465d6beccab9efaa7b856f9511c81f9ea6690a43cf9e3d38 4bb452f31133f4724df04db888611cb88b19014f71b32f3fb2ad58d0f7bdbb218a |
всё, можем проверять что наш кластер успешно распечатывается:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
root@vault2:~# vault status WARNING! VAULT_ADDR and -address unset. Defaulting to https://127.0.0.1:8200. Key Value --- ----- Seal Type transit Recovery Seal Type shamir Initialized true Sealed false Total Recovery Shares 5 Threshold 3 Version 1.17.0 Build Date 2024-06-10T10:11:34Z Storage Type raft Cluster Name vault-cluster-011de0c1 Cluster ID 69604c77-2116-9a8d-4c2e-fced06b9b578 HA Enabled true HA Cluster https://vault2.test.local:8201 HA Mode active Active Since 2024-11-18T19:58:34.395069102+06:00 Raft Committed Index 10335 Raft Applied Index 10335 |
главный сервер https://vault2.test.local:8201 проверяем что и виртуальный ip на этом сервере:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
root@vault2:~# ifconfig enp0s3: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 192.168.1.104 netmask 255.255.255.0 broadcast 192.168.1.255 inet6 fe80::a00:27ff:fe6d:1798 prefixlen 64 scopeid 0x20<link> ether 08:00:27:6d:17:98 txqueuelen 1000 (Ethernet) RX packets 418231 bytes 61354583 (58.5 MiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 192759 bytes 37057036 (35.3 MiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 enp0s3:vip: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 192.168.1.111 netmask 255.255.255.255 broadcast 0.0.0.0 ether 08:00:27:6d:17:98 txqueuelen 1000 (Ethernet) lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536 inet 127.0.0.1 netmask 255.0.0.0 inet6 ::1 prefixlen 128 scopeid 0x10<host> loop txqueuelen 1000 (Local Loopback) RX packets 242954 bytes 49866779 (47.5 MiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 242954 bytes 49866779 (47.5 MiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 |
рестартуем vault и проверяем статус:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
root@vault2:~# systemctl restart vault root@vault2:~# vault status WARNING! VAULT_ADDR and -address unset. Defaulting to https://127.0.0.1:8200. Key Value --- ----- Seal Type transit Recovery Seal Type shamir Initialized true Sealed false Total Recovery Shares 5 Threshold 3 Version 1.17.0 Build Date 2024-06-10T10:11:34Z Storage Type raft Cluster Name vault-cluster-011de0c1 Cluster ID 69604c77-2116-9a8d-4c2e-fced06b9b578 HA Enabled true HA Cluster https://vault1.test.local:8201 HA Mode standby Active Node Address https://vault1.test.local:8200 Raft Committed Index 10376 Raft Applied Index 10376 root@vault2:~# ifconfig enp0s3: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 192.168.1.104 netmask 255.255.255.0 broadcast 192.168.1.255 inet6 fe80::a00:27ff:fe6d:1798 prefixlen 64 scopeid 0x20<link> ether 08:00:27:6d:17:98 txqueuelen 1000 (Ethernet) RX packets 422662 bytes 62139374 (59.2 MiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 199484 bytes 38118335 (36.3 MiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536 inet 127.0.0.1 netmask 255.0.0.0 inet6 ::1 prefixlen 128 scopeid 0x10<host> loop txqueuelen 1000 (Local Loopback) RX packets 245286 bytes 50343368 (48.0 MiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 245286 bytes 50343368 (48.0 MiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 |
как видим после рестарта кластер успешно распечатан
Sealed false
основной нодой является https://vault1.test.local:8201
виртуальный адрес переехал на другую ноду.
как видим auto unseal успешно настроен.
напомню что все домены у нас заведены через freeipa

обновить токен для распечатывая основного vault
root@kub-master1:~# kubectl get configmaps -n vault vault-unseal-keys -o yaml | grep root
"root_token": "hvs.hOrdYvRpvieSUF1yuWDIeegB"
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
/ $ vault token create -policy=unseal-policy -period=90000h -orphan WARNING! The following warnings were returned from Vault: * period of "90000h" exceeded the effective max_ttl of "768h"; period value is capped accordingly Key Value --- ----- token hvs.CAESIJ4oY7ZiA7rRwJIBUmIafMpEa--RPzdZMjjFzJdfN0-3Gh4KHGh2cy4wOTduMTB5OG1SenIxNk9YdTJHZmhDOFM token_accessor Bl3c2lPN6OzkkAuhfKPZwk5h token_duration 768h token_renewable true token_policies ["default" "unseal-policy"] identity_policies [] policies ["default" "unseal-policy"] |
|
1 2 3 4 5 6 7 8 9 |
Key Value --- ----- token hvs.CAESIJGcJoJ3xhrepURua7Rvte2u3TCGJ7t1q3XIMaZFPkjwGh4KHGh2cy5nRHZUWVZ0elowYmE3REZpbFVkS2JCVGw token_accessor bkfa8CPHegDJbziHIOFWDdSs token_duration 90000h token_renewable true token_policies ["default" "unseal-policy"] identity_policies [] policies ["default" "unseal-policy"] |
/etc/vault.d/vault.hcl
|
1 2 3 4 5 6 7 8 |
seal "transit" { address = "http://vault-unseal.test.local:80" token = "hvs.CAESIJGcJoJ3xhrepURua7Rvte2u3TCGJ7t1q3XIMaZFPkjwGh4KHGh2cy5nRHZUWVZ0elowYmE3REZpbFVkS2JCVGw" key_name = "vault-unseal-key" mount_path = "transit/" disable_renewal = "false" tls_skip_verify = "true" } |
всё можно ребутать тачки волтов ну или рестартить волты
Добавим интеграцию vault(виртуалки) в k8s "Vault Secrets Operator"
вот официальная репка:
https://github.com/hashicorp/vault-secrets-operator
cat vault-secrets-operator.yaml
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
controller: replicas: 2 affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: app operator: In values: - vault-secrets-operator topologyKey: "kubernetes.io/hostname" kubeRbacProxy: resources: limits: cpu: 150m memory: 150Mi requests: cpu: 50m memory: 100Mi manager: resources: limits: cpu: 150m memory: 150Mi requests: cpu: 50m memory: 100Mi defaultVaultConnection: enabled: true skipTLSVerify: true address: "https://192.168.1.111:8200" |
адрес лучше конечно указать vault.test.local хоть оно и резолвит:
|
1 2 3 4 5 6 7 |
/ $ nslookup vault.test.local Server: 169.254.25.10 Address: 169.254.25.10:53 Name: vault.test.local Address: 192.168.1.111 |
но могут возникать проблемы:
|
1 2 |
Warning VaultClientConfigError 6s (x12 over 86s) VaultStaticSecret Failed to get Vault auth login: Put "https://vault.test.local:8200/v1/auth/kubernetes/login": dial tcp: lookup vault.test.local on 169.254.25.10:53: no such host |
поэтому я оставил ip address 192.168.1.111
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
root@client:~/vault-autounseal# kubectl get cm kube-root-ca.crt -o jsonpath="{['data']['ca\.crt']}" -----BEGIN CERTIFICATE----- MIIC/jCCAeagAwIBAgIBADANBgkqhkiG9w0BAQsFADAVMRMwEQYDVQQDEwprdWJl cm5ldGVzMB4XDTI0MDYzMDE0MjIxMFoXDTM0MDYyODE0MjIxMFowFTETMBEGA1UE AxMKa3ViZXJuZXRlczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPsS LBw3lg/4zrpycC1xJprK7XRIZK6AyL7DIClHPhAWn+h+m72r4PfZib9wu7/VS0e0 SkNhVhEhjTZx1yMkJfP4zwd6IUuFAptZfCgVHnJQnkPeadWUqj/zEIh2ubcgbRws jG1nh8yNRbtrxd/dpXPveBCFptOi5CqdUYodhoBDic5mYwbk8AbMf71FBhhdq8+8 y5TiFlXPglITHxe/0VHCXO3ANUvNJDbDoYUGU5I0Kv3AIwffxSrNlyVhgzX70b7e KaurkmGW577hXCEZapRjHlxl2wUwW+BCKZ7X8MCFbwUVVgVLP0nroKvEVUxQNima lip/RtaqiKrf3PypuUsCAwEAAaNZMFcwDgYDVR0PAQH/BAQDAgKkMA8GA1UdEwEB /wQFMAMBAf8wHQYDVR0OBBYEFB/OXFsCKolo5DCMnaFCxrXMBamSMBUGA1UdEQQO MAyCCmt1YmVybmV0ZXMwDQYJKoZIhvcNAQELBQADggEBAKQ3XdnYLdoS9OeETDMG j3nHXyjZsfc7TupYIpHwUH27Jh9LSzl0oxK1dWoRVizF6P1VKlKoZV7tzPPFEjqQ feE1cDXErY4acWq6v6ta3Og65TH6d9/rIqcUfr7UqZw+oMRi4UGkCP4PGoG8Kmcp mGmWBLmzBD/ChB0zE9gN2vsKgGWSYUdZoO3KbrMRE/bdH1jDABXpkXvcnQ+gPaqP w1ZNOcADFWwITbKaRAwWTCspgqKd7D1IAuPRR/kIDPIWCc0rLdvPPuKTmx4Ah3j0 TQvc8UW/gQX7OV2iReE0sZ1n/RRR4Fa+9mtUleaODao0MvqHVo+9IYFCTTi1fvRC ySE= -----END CERTIFICATE----- |
создаём сервис аккаунт
root@client:~/vault-autounseal# kubectl create serviceaccount vault-auth -n kube-system
на основе этого сервис аккаунта получаем jwt token устанавливаю время на 100 лет 876000h
root@client:~/vault-autounseal# kubectl create token vault-auth -n kube-system --duration 876000h
|
1 |
eyJhbGciOiJSUzI1NiIsImtpZCI6IkdrQlByYnlkSXBDczR6SUhjalZreDZibnlGX18wVGpqenU2SVRabXVZN1UifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiXSwiZXhwIjo0ODg1NzAzNDUwLCJpYXQiOjE3MzIxMDM0NTAsImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsInNlcnZpY2VhY2NvdW50Ijp7Im5hbWUiOiJ2YXVsdC1hdXRoIiwidWlkIjoiZDRhZmY3YzktY2Q4My00ODEwLWEyYzYtYWRjZmU5M2VlY2MwIn19LCJuYmYiOjE3MzIxMDM0NTAsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDprdWJlLXN5c3RlbTp2YXVsdC1hdXRoIn0.jwJulDCsUGDnTgQcMqcV00V_JW1OxfEJZleB_qggqjSlTWjlWW9y_4ijs8-o66IeZFi_tIx2lC80G1uAkxnpenGdqC36fEiOzMW3WosYWsXhdNVENBYLEaFUWHuJPNnDJsOg9bVPyLlyVigLfAgp5RdDltaheo2Voxw4P_JWCynYwNKNsdWmZ97CnB5SagHgccsYnr_eacL_P8Ewb82HFyqkcN6dk6lhzWkD2zI4Nqg1gNpkl95La8DX9-MckkMxyu5dh9foksWT4YjqJIsmHb76agTEbTF3r0N2DCDBLUQrVUQfT-a-8hXMCp1N68BjxANkyk-sDSmOp6xa4spzjg |
root@client:~/vault-autounseal# ssh 192.168.1.103
напоминаю root token
|
1 2 3 4 5 6 7 8 9 10 |
Key Value --- ----- token hvs.UuG0QJvRRfwUHUTGxDTjFaAd token_accessor a87f7TLRK4mCRnrg5RDh4Pt4 token_duration ∞ token_renewable false token_policies ["root"] identity_policies [] policies ["root"] |
создаём секрет который будем подкидывать:
root@vault1:~# vault secrets enable -path=test/secret/ kv
добавляем туда ключ значение:
root@vault1:~# vault kv put test/secret/namespace-test/first-app password="db-secret-password"
сертификат который получили ранее, кладём в файл /root/ca.crt
дальше присваиваем переменной TOKEN_REVIEWER_JWT наш jwt токен который мы создали выше
root@vault1:~#
|
1 2 |
export TOKEN_REVIEWER_JWT='eyJhbGciOiJSUzI1NiIsImtpZCI6IkdrQlByYnlkSXBDczR6SUhjalZreDZibnlGX18wVGpqenU2SVRabXVZN1UifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiXSwiZXhwIjo0ODg1NzAzNDUwLCJpYXQiOjE3MzIxMDM0NTAsImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsInNlcnZpY2VhY2NvdW50Ijp7Im5hbWUiOiJ2YXVsdC1hdXRoIiwidWlkIjoiZDRhZmY3YzktY2Q4My00ODEwLWEyYzYtYWRjZmU5M2VlY2MwIn19LCJuYmYiOjE3MzIxMDM0NTAsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDprdWJlLXN5c3RlbTp2YXVsdC1hdXRoIn0.jwJulDCsUGDnTgQcMqcV00V_JW1OxfEJZleB_qggqjSlTWjlWW9y_4ijs8-o66IeZFi_tIx2lC80G1uAkxnpenGdqC36fEiOzMW3WosYWsXhdNVENBYLEaFUWHuJPNnDJsOg9bVPyLlyVigLfAgp5RdDltaheo2Voxw4P_JWCynYwNKNsdWmZ97CnB5SagHgccsYnr_eacL_P8Ewb82HFyqkcN6dk6lhzWkD2zI4Nqg1gNpkl95La8DX9-MckkMxyu5dh9foksWT4YjqJIsmHb76agTEbTF3r0N2DCDBLUQrVUQfT-a-8hXMCp1N68BjxANkyk-sDSmOp6xa4spzjg' |
теперь настраиваем auth к кластеру
root@vault1:~#
|
1 2 3 4 5 |
vault write auth/kubernetes/config \ kubernetes_host="https://192.168.1.112:6443" \ kubernetes_ca_cert=@/root/ca.crt \ token_reviewer_jwt="$TOKEN_REVIEWER_JWT" \ disable_iss_validation="true" |
|
1 2 3 4 5 |
vault policy write test-policy - <<EOF path "test/secret/namespace-test/first-app" { capabilities = ["read"] } EOF |
создаём роль "test-role"
Роль связывает учетную запись службы Kubernetes(serviceaccaunt), которую назовём test-serviceaccaunt (но лучше использовать уже существующий сервис аккаунт нашего приложения ) в пространстве имен test с политикой Vault, test-policy Токены, возвращенные после аутентификации, действительны в течение 10 минут
|
1 2 3 4 5 |
vault write auth/kubernetes/role/test-role \ bound_service_account_names=test-serviceaccount \ bound_service_account_namespaces=test \ policies=test-policy \ ttl=10m |
bound_service_account_names: Имя сервисного аккаунта, которому разрешено аутентифицироваться.bound_service_account_namespaces: Пространство имён, в котором находится сервисный аккаунт.
создаём namespace test
root@client:~# kubectl create ns test
создаём serviceaccaunt
kubectl create serviceaccount test-serviceaccount -n test
создаём объект VaultConnection который будет использоваться для подключения к vault во всех неймспейсах
|
1 2 3 4 5 6 7 8 9 |
apiVersion: secrets.hashicorp.com/v1beta1 kind: VaultConnection metadata: name: vault-connection namespace: kube-system spec: address: "https://192.168.1.111:8200" skipTLSVerify: true |
как видим он использует namespace: kube-system
192.168.1.111 - это виртуальный ip vault кластера
теперь создаём объект VaultAuth
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
apiVersion: secrets.hashicorp.com/v1beta1 kind: VaultAuth metadata: name: vault-auth-test namespace: test spec: vaultConnectionRef: kube-system/vault-connection method: kubernetes mount: kubernetes kubernetes: role: test-role serviceAccount: test-serviceaccount |
как видим для подключения к VaultConnection используется запись: kube-system/vault-connection
так же тут указываем роль созданную в vault test-role и сервис аккаунт test-serviceoccount - который так же должен совпадать с тем что мы указали в vault и с тем что у нас уже создан в k8s
теперь создаём объект VaultStaticSecret
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
apiVersion: secrets.hashicorp.com/v1beta1 kind: VaultStaticSecret metadata: name: app-db-creds-test namespace: test spec: vaultAuthRef: vault-auth-test mount: test/secret type: kv-v1 path: namespace-test/first-app refreshAfter: 10s destination: create: true overwrite: true name: test-secret-k8s |
тут мы указываем как будет называться secret и как часто его обновлять.
теперь создаём несколько объектов ClusterRole и ClusterRoleBinding
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: vault-token-reviewer rules: - apiGroups: ["authentication.k8s.io"] resources: ["tokenreviews"] verbs: ["create"] - apiGroups: [""] resources: ["configmaps"] verbs: ["get"] - apiGroups: [""] resources: ["secrets"] verbs: ["get"] - apiGroups: [""] resources: ["serviceaccounts"] verbs: ["get"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: vault-token-reviewer-binding roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: vault-token-reviewer subjects: - kind: ServiceAccount name: vault-auth namespace: kube-system |
как мы видим ClusterRoleBinding смотрит на ServiceAccount vault-auth расположенный kube-system мы его создавали вручную и jwt token создавали на его основе.
[root@ansible ansible]# scp -r /etc/ansible/kubespray-official/vault-autounseal/ root@192.168.1.121:~/
root@client:~/vault-autounseal# kubectl apply -f vault-secrets-operator-VaultConnection.yaml
root@client:~/vault-autounseal# kubectl apply -f vault-secrets-operator-VaultAuth.yaml
root@client:~/vault-autounseal# kubectl apply -f vault-secrets-operator-VaultSecret.yaml
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
root@client:~/vault-autounseal# kubectl describe vaultconnections.secrets.hashicorp.com -n kube-system vault-connection Name: vault-connection Namespace: kube-system Labels: <none> Annotations: <none> API Version: secrets.hashicorp.com/v1beta1 Kind: VaultConnection Metadata: Creation Timestamp: 2024-11-20T11:53:23Z Finalizers: vaultconnection.secrets.hashicorp.com/finalizer Generation: 1 Resource Version: 2383949 UID: 0a8e370d-2f46-4069-8fb6-9d760f782643 Spec: Address: https://192.168.1.111:8200 Skip TLS Verify: true Status: Valid: true Events: Type Reason Age From Message ---- ------ ---- ---- ------- Warning VaultClientError 2m15s VaultConnection Failed to check Vault seal status: Get "https://192.168.1.111:8200/v1/sys/seal-status": dial tcp 192.168.1.111:8200: connect: no route to host Normal Accepted 2m13s VaultConnection VaultConnection accepted |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
root@client:~/vault-autounseal# kubectl describe vaultauths.secrets.hashicorp.com -n test vault-auth-test Name: vault-auth-test Namespace: test Labels: <none> Annotations: <none> API Version: secrets.hashicorp.com/v1beta1 Kind: VaultAuth Metadata: Creation Timestamp: 2024-11-20T11:53:23Z Finalizers: vaultauth.secrets.hashicorp.com/finalizer Generation: 1 Resource Version: 2383940 UID: db80cd4b-5cbc-4e7f-8bac-fc0099b205fe Spec: Kubernetes: Role: test-role Service Account: test-serviceaccount Token Expiration Seconds: 600 Method: kubernetes Mount: kubernetes Vault Connection Ref: kube-system/vault-connection Status: Spec Hash: e70e9199edd25d059ad9898fa1142805fdd5df041e490bf1ca1a735c65ee7fd5 Valid: true Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal Accepted 2m56s VaultAuth Successfully handled VaultAuth resource request |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
root@client:~/vault-autounseal# kubectl describe vaultstaticsecrets.secrets.hashicorp.com -n test app-db-creds-test Name: app-db-creds-test Namespace: test Labels: <none> Annotations: <none> API Version: secrets.hashicorp.com/v1beta1 Kind: VaultStaticSecret Metadata: Creation Timestamp: 2024-11-20T11:53:23Z Finalizers: vaultstaticsecret.secrets.hashicorp.com/finalizer Generation: 2 Resource Version: 2383969 UID: 67395e52-f0b0-464f-9622-93f3c68c3f16 Spec: Destination: Create: true Name: test-secret-k8s Overwrite: false Transformation: Hmac Secret Data: true Mount: test/secret Path: namespace-test/first-app Refresh After: 10s Type: kv-v1 Vault Auth Ref: vault-auth-test Status: Last Generation: 2 Secret MAC: vz7xmB0zNnANWvtdar3CZTYGB6Y+7SwkZJRqCfdAyxw= Events: Type Reason Age From Message ---- ------ ---- ---- ------- Warning VaultClientConfigError 4m12s VaultStaticSecret Failed to get Vault auth login: Put "https://192.168.1.111:8200/v1/auth/kubernetes/login": dial tcp 192.168.1.111:8200: connect: no route to host Normal SecretSynced 4m7s VaultStaticSecret Secret synced Normal SecretRotated 4m7s VaultStaticSecret Secret synced |
проверяем созданный секрет:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
root@client:~/vault-autounseal# kubectl get secret -n test test-secret-k8s -o yaml apiVersion: v1 data: _raw: eyJwYXNzd29yZCI6ImRiLXNlY3JldC1wYXNzd29yZCJ9 password: ZGItc2VjcmV0LXBhc3N3b3Jk kind: Secret metadata: creationTimestamp: "2024-11-20T11:53:39Z" labels: app.kubernetes.io/component: secret-sync app.kubernetes.io/managed-by: hashicorp-vso app.kubernetes.io/name: vault-secrets-operator secrets.hashicorp.com/vso-ownerRefUID: 67395e52-f0b0-464f-9622-93f3c68c3f16 name: test-secret-k8s namespace: test ownerReferences: - apiVersion: secrets.hashicorp.com/v1beta1 kind: VaultStaticSecret name: app-db-creds-test uid: 67395e52-f0b0-464f-9622-93f3c68c3f16 resourceVersion: "2383963" uid: eecfaae7-0275-4019-9898-a6f99aab2d35 type: Opaque |
а теперь добавим в vault секрет пару новый значений

|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
root@client:~/vault-autounseal# kubectl get secret -n test test-secret-k8s -o yaml apiVersion: v1 data: _raw: eyIxMjMiOiIxMjMiLCJwYXNzd29yZCI6ImRiLXNlY3JldC1wYXNzd29yZCIsInRlc3QiOiJ0ZXN0In0= "123": MTIz password: ZGItc2VjcmV0LXBhc3N3b3Jk test: dGVzdA== kind: Secret metadata: creationTimestamp: "2024-11-20T11:53:39Z" labels: app.kubernetes.io/component: secret-sync app.kubernetes.io/managed-by: hashicorp-vso app.kubernetes.io/name: vault-secrets-operator secrets.hashicorp.com/vso-ownerRefUID: 67395e52-f0b0-464f-9622-93f3c68c3f16 name: test-secret-k8s namespace: test ownerReferences: - apiVersion: secrets.hashicorp.com/v1beta1 kind: VaultStaticSecret name: app-db-creds-test uid: 67395e52-f0b0-464f-9622-93f3c68c3f16 resourceVersion: "2385699" uid: eecfaae7-0275-4019-9898-a6f99aab2d35 type: Opaque |
как видим дополнительные key - value 123 и test добавились в секрет.
Пример с autoreloader после изменения секрета в vault
/dev/service/app1(app2/app3)
/prod/service/app1(app2/app3)




сделаем одну полиси которая сможет читать все секреты в dev/service и вторую которая будет читать всё в prod/service
вот эти policy read-dev-service и read-prod-service
|
1 2 3 4 |
path "dev/service/*" { capabilities = ["read", "list"] } |
|
1 2 3 |
path "prod/service/*" { capabilities = ["read", "list"] } |

теперь создаём roles - нужно знать как будет называться serviceaccaunt у нашего приложения. если не использовать
|
1 2 3 4 5 |
vault write auth/kubernetes/role/dev-read-role-app1 \ bound_service_account_names=first-app-common-chart\ bound_service_account_namespaces=dev \ policies=read-dev-service \ ttl=10m |
сразу сделаем для второй апки в dev и апки в prod схема запуска будет такой же
|
1 2 3 4 5 |
vault write auth/kubernetes/role/dev-read-role-app2 \ bound_service_account_names=second-app-common-chart\ bound_service_account_namespaces=dev \ policies=read-dev-service \ ttl=10m |
|
1 2 3 4 5 |
vault write auth/kubernetes/role/prod-read-role-app1 \ bound_service_account_names=first-app-common-chart\ bound_service_account_namespaces=prod \ policies=read-prod-service \ ttl=10m |
проверить можно так:
vault list auth/kubernetes/role
|
1 2 3 4 5 6 |
vault list auth/kubernetes/role Keys dev-read-role-app1 dev-read-role-app2 prod-read-role-app1 test-role |
детально смотрим пару ролей
vault read auth/kubernetes/role/ИМЯ_РОЛИ
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
vault read auth/kubernetes/role/dev-read-role-app1 Key Value alias_name_source serviceaccount_uid bound_service_account_names ["first-app-common-chart"] bound_service_account_namespace_selector bound_service_account_namespaces ["dev"] policies ["read-dev-service"] token_bound_cidrs [] token_explicit_max_ttl 0 token_max_ttl 0 token_no_default_policy false token_num_uses 0 token_period 0 token_policies ["read-dev-service"] token_ttl 600 token_type default ttl 600 vault read auth/kubernetes/role/prod-read-role-app1 Key Value alias_name_source serviceaccount_uid bound_service_account_names ["first-app-common-chart"] bound_service_account_namespace_selector bound_service_account_namespaces ["prod"] policies ["read-prod-service"] token_bound_cidrs [] token_explicit_max_ttl 0 token_max_ttl 0 token_no_default_policy false token_num_uses 0 token_period 0 token_policies ["read-prod-service"] token_ttl 600 token_type default ttl 600 |
а вот values, я тут покажу только отличия в values для секретов:
/etc/ansible/kubespray-official/vault-autounseal/common-chart/values-dev-app1.yaml
|
1 2 3 4 5 6 7 8 9 10 |
vault_secret: mount: "dev/service" path: "app1" type: "kv-v1" refreshAfter: "10s" role: "dev-read-role-app1" vaultConnectionRef: "kube-system/vault-connection" secret_name: "first-app-secret" |
/etc/ansible/kubespray-official/vault-autounseal/common-chart/values-dev-app2.yaml
|
1 2 3 4 5 6 7 8 9 |
vault_secret: mount: "dev/service" path: "app2" type: "kv-v1" refreshAfter: "10s" role: "dev-read-role-app2" vaultConnectionRef: "kube-system/vault-connection" secret_name: "second-app-secret" |
/etc/ansible/kubespray-official/vault-autounseal/common-chart/values-prod-app1.yaml
|
1 2 3 4 5 6 7 8 9 |
vault_secret: mount: "prod/service" path: "app1" type: "kv-v1" refreshAfter: "10s" role: "prod-read-role-app1" vaultConnectionRef: "kube-system/vault-connection" secret_name: "first-app-secret" |
у нас будет 2 namespace dev и prod
root@client:~/vault-autounseal# kubectl create ns prod
root@client:~/vault-autounseal# helm upgrade --install --namespace dev first-app ./common-chart/ --values ./common-chart/values-dev-app1.yaml
root@client:~/vault-autounseal# helm upgrade --install --namespace dev second-app ./common-chart/ --values ./common-chart/values-dev-app2.yaml
root@client:~/vault-autounseal# helm upgrade --install --namespace prod first-app ./common-chart/ --values ./common-chart/values-prod-app1.yaml
|
1 2 3 4 5 6 7 8 9 10 11 12 |
apiVersion: secrets.hashicorp.com/v1beta1 kind: VaultAuth metadata: name: "vaultauth-{{ .Values.vault_secret.secret_name }}" spec: vaultConnectionRef: {{ .Values.vault_secret.vaultConnectionRef }} method: kubernetes mount: kubernetes kubernetes: role: {{ .Values.vault_secret.role }} serviceAccount: {{ include "common-chart.serviceAccountName" . }} |
/etc/ansible/kubespray-official/vault-autounseal/common-chart/templates/vault-secrets-operator-VaultSecret.yaml
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
apiVersion: secrets.hashicorp.com/v1beta1 kind: VaultStaticSecret metadata: name: "vaultsecret-{{ .Values.vault_secret.secret_name }}" spec: vaultAuthRef: "vaultauth-{{ .Values.vault_secret.secret_name }}" mount: {{ .Values.vault_secret.mount }} type: {{ .Values.vault_secret.type }} path: {{ .Values.vault_secret.path }} refreshAfter: {{ .Values.vault_secret.refreshAfter }} destination: create: true overwrite: true name: {{ .Values.vault_secret.secret_name }} rolloutRestartTargets: - kind: Deployment name: {{ include "common-chart.fullname" . }} |
rolloutRestartTargets - будет рестартовать деплоймент при изменении секрета
и нужно отредактировать deployment
/etc/ansible/kubespray-official/vault-autounseal/common-chart/templates/deployment.yaml
добавив:
|
1 2 3 |
envFrom: - secretRef: name: {{ .Values.vault_secret.secret_name }} |
всё теперь подкидывая новые или удаляя старые секреты - будет рестартоваться и деплоймент
Аутентификация, авторизация в k8s (SSO в kubernetes)
В Kubernetes есть два типа пользователей:
- Service Accounts — аккаунты, управляемые Kubernetes API;
- Users — «нормальные» пользователи, управляемые внешними, независимыми сервисами.
Основное отличие этих типов в том, что для Service Accounts существуют специальные объекты в Kubernetes API (они так и называются — ServiceAccounts), которые привязаны к пространству имён и набору авторизационных данных, хранящихся в кластере в объектах типа Secrets. Такие пользователи (Service Accounts) предназначены в основном для управления правами доступа к Kubernetes API процессов, работающих в кластере Kubernetes.
Обычные же Users не имеют записей в Kubernetes API: управление ими должно осуществляться внешними механизмами. Они предназначены для людей или процессов, живущих вне кластера.
Каждый запрос к API привязан либо к Service Account, либо к User, либо считается анонимным.
Аутентификационные данные пользователя включают в себя:
- Username — имя пользователя (зависит от регистра!);
- UID — машинно-читаемая строка идентификации пользователя, которая «более консистентна и уникальна, чем имя пользователя»;
- Groups — список групп, к которым принадлежит пользователь;
- Extra — дополнительные поля, которые могут быть использованы механизмом авторизации.
Kubernetes может использовать большое количество механизмов аутентификации: сертификаты X509, Bearer-токены, аутентифицирующий прокси, HTTP Basic Auth. При помощи этих механизмов можно реализовать большое количество схем авторизации: от статичного файла с паролями до OpenID OAuth2.
Более того, допускается использование нескольких схем авторизации одновременно. По умолчанию в кластере используются:
- service account tokens — для Service Accounts;
- X509 — для Users.
Loft
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
admin: create: true username: admin password: "Secret123" service: type: ClusterIP ingress: enabled: true name: loft-ingress host: loft.test.local ingressClass: nginx path: / tls: enabled: false # Установите true, если используете TLS secret: loft-tls persistence: enabled: true size: 10Gi storageClassName: nfs-client # Замените на ваш StorageClass accessModes: ["ReadWriteOnce"] serviceAccount: name: loft create: true clusterRole: cluster-admin |
тут мы указываем логин пароль с которым будем заходить
admin
Secret123
настраиваем persistant volume и ingress
loft.test.local
ставим
root@client:~# kubectl create ns loft
root@client:~# helm repo add loft https://charts.loft.sh
root@client:~# helm repo update
root@client:~# helm search repo loft
последний командой смотрим последнюю версию,
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
root@client:~# helm search repo loft NAME CHART VERSION APP VERSION DESCRIPTION loft/loft 4.1.0 Secure Cluster Sharing, Self-Service Namespace ... loft/loft-agent 3.2.4 Loft Cluster Agent loft/loft-direct-cluster-endpoint 1.14.0 Direct Secure Cluster Loft Access Point loft/loft-grafana-dashboards 0.1.2 Grafana dashboards for loft loft/central-hostpath-mapper 0.2.6 0.0.1 A centralized version of the hostpath mapper loft/cert-issuer 0.0.4 ClusterIssuer for cert-manager loft/component-chart 0.9.1 A general purpose helm chart for deploying comp... loft/devpod-pro 4.1.0 DevPod.Pro - Developer Environments as Code loft/devspace-cloud 0.3.3 The DevSpace Cloud control plane. loft/isolation-templates 0.1.0 Smart defaults for isolations including Network... loft/jspolicy 0.2.2 JavaScript Policies for Kubernetes loft/kiosk 0.2.11 Multi-Tenancy Extension For Kubernetes loft/vcluster 0.21.1 0.21.1 vcluster - Virtual Kubernetes Clusters loft/vcluster-control-plane 3.4.9 vCluster.Pro - Virtual Kubernetes Clusters loft/vcluster-eks 0.19.8 0.19.8 vcluster - Virtual Kubernetes Clusters (eks) loft/vcluster-hpm 0.1.2 1.20.0 A Helm chart to resolve the correct virtual pod... loft/vcluster-k0s 0.19.8 0.19.8 vcluster - Virtual Kubernetes Clusters (k0s) loft/vcluster-k8s 0.19.8 0.19.8 vcluster - Virtual Kubernetes Clusters (k8s) loft/vcluster-platform 4.1.0 vCluster Platform - Virtual Kubernetes Clusters loft/vcluster-pro 0.2.0 vcluster-pro - Virtual Kubernetes Clusters loft/vcluster-pro-eks 0.2.0 vcluster-pro - Virtual Kubernetes Clusters (eks) loft/vcluster-pro-k0s 0.2.0 vcluster-pro - Virtual Kubernetes Clusters (k0s) loft/vcluster-pro-k8s 0.2.0 vcluster-pro - Virtual Kubernetes Clusters (k8s) loft/vcluster-pro-rancher-plugin 0.0.2 0.0.2 vCluster.Pro plugin for Rancher loft/virtualcluster 0.0.28 A virtual kubernetes cluster |
всё теперь может устанавливать:
root@client:~/sso-loft# helm install loft loft/loft --namespace loft -f values.yaml --version 4.1.0
ждём окончания установки и заходим:
http://loft.test.local/login





и создаём для него ключ

Dex - dexK8sAuthenticator
Dex — сервер аутентификации, реализующий протоколы OpenID Connect (OIDC) и OAuth 2.0. Он выступает в качестве посредника между различными внешними провайдерами аутентификации и приложениями, которым требуется аутентификация через OIDC.
Функциональность Dex:
- Аутентификация пользователей: Dex позволяет интегрировать различные внешние источники аутентификации, такие как LDAP, SAML, GitHub, Google и другие.
- Провайдер OIDC: Предоставляет стандартный интерфейс OIDC для приложений, требующих аутентификации.
- (SSO): Позволяет реализовать единую точку входа для аутентификации пользователей в различных приложениях и сервисах.
В контексте Kubernetes:
- Аутентификация кластера: Dex может использоваться как OIDC-провайдер для Kubernetes, позволяя пользователям аутентифицироваться в кластере через внешний источник.
- Интеграция с kubectl: Пользователи могут получать токены доступа для взаимодействия с кластером через
kubectl.
dex-auth— это сторонний проект или инструмент, который позволяет настроить интеграцию Dex с Kubernetes для более удобного использования, например, для входа в Kubernetes с помощью Dex и управления OIDC токенами. Он обычно используется для автоматизации и упрощения аутентификации через Dex.
Основные возможности dexK8sAuthenticator:
- Упрощение процесса входа:
- Пользователь может войти в Kubernetes через Dex, не выполняя сложных операций с токенами OIDC вручную.
- Предоставляет интерфейс для ввода логина/пароля или использования внешнего браузера для аутентификации.
- Автоматическое управление токенами:
- После успешного входа автоматически получает OIDC токен (ID-токен, Access-токен).
- Токен добавляется в kubeconfig, чтобы пользователь мог работать с Kubernetes, используя стандартные инструменты (
kubectl,helmи т.д.).
- Интеграция с Dex:
- Работает как клиент для Dex.
- Использует OIDC протокол для получения токенов.
root@client:~# cd certs/
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
[ req ] default_bits = 4096 prompt = no default_md = sha256 distinguished_name = dn req_extensions = req_ext [ dn ] C = RU ST = YourState L = YourCity O = YourOrganization CN = dex.test.local # Основное доменное имя [ req_ext ] subjectAltName = @alt_names [ alt_names ] DNS.1 = dex.test.local DNS.2 = dex-auth.test.local |
root@client:~/certs# openssl req -new -key dex.key -out dex.csr -config dex_openssl.cnf
|
1 2 3 4 5 6 7 |
[ v3_ca ] subjectAltName = @alt_names [ alt_names ] DNS.1 = dex.test.local DNS.2 = dex-auth.test.local |
root@client:~/certs# openssl x509 -req -in dex.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out dex.crt -days 10000 -sha256 -extfile ca_openssl.cnf -extensions v3_ca
проверяем:
|
1 2 3 4 |
root@client:~/certs# openssl x509 -in dex.crt -text -noout | grep -i test Subject: C = RU, ST = YourState, L = YourCity, O = YourOrganization, CN = dex.test.local DNS:dex.test.local, DNS:dex-auth.test.local |
как видим оба домена есть в сертификатах
создаём неймспейсы:

[root@freeipa-1 ipa]# bash freeipa-sam.sh
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
### FreeIPA - System Account Manager ### 1.) ldapserver= 2.) domain= (ldapdomain=) 3.) binduser= 4.) bindpass=UNSET! 5.) ssl=true Actions (conditions not yet met): add | rm | ls | info | passwd | save --- Results --- --- End Results --- > 1 |
вводим домен ldap сервера freeipa-1.test.local
3 вводим admin
4 вводим пароль Secret123
5 выключаем ssl
теперь создадим пользователя k8s-access с паролем Secret123
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
### FreeIPA - System Account Manager ### 1.) ldapserver=freeipa-1.test.local 2.) domain=test.local (ldapdomain=dc=test,dc=local) 3.) binduser=uid=admin,cn=users,cn=accounts,dc=test,dc=local 4.) bindpass=SET! 5.) ssl=false Actions (ready): add | rm | ls | info | passwd | save > add uid of new user=k8s-access password of new user (blank to generate a password)= password expiration date YYYYMMDD (blank for 20380119)= |
теперь подготовим values
вот dex.yaml
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
replicaCount: 1 https: enabled: true configSecret: create: true name: "dex-config" config: issuer: https://dex.test.local storage: type: kubernetes config: inCluster: true web: https: 0.0.0.0:5556 tlsCert: /etc/dex/tls/tls.crt tlsKey: /etc/dex/tls/tls.key connectors: - type: ldap id: freeipa name: FreeIPA config: host: freeipa-1.test.local:389 insecureNoSSL: true startTLS: false bindDN: uid=k8s-access,cn=sysaccounts,cn=etc,dc=test,dc=local bindPW: Secret123 usernamePrompt: "Username" userSearch: baseDN: cn=users,cn=accounts,dc=test,dc=local filter: "(objectClass=person)" username: uid idAttr: uid emailAttr: mail nameAttr: cn groupSearch: baseDN: cn=groups,cn=accounts,dc=test,dc=local filter: "(objectClass=groupOfNames)" userMatchers: - userAttr: uid groupAttr: cn staticClients: - id: kubernetes redirectURIs: - 'https://dex-auth.test.local/callback' name: 'Kubernetes' secret: 'kubernetes-secret' oauth2: skipApprovalScreen: true ingress: enabled: true className: "nginx" annotations: cert-manager.io/cluster-issuer: "letsencrypt" hosts: - host: dex.test.local paths: - path: / pathType: Prefix tls: - secretName: dex-tls hosts: - dex.test.local rbac: create: true createClusterScoped: true volumes: - name: dex-tls secret: secretName: dex-tls volumeMounts: - name: dex-tls mountPath: /etc/dex/tls readOnly: true resources: requests: memory: "128Mi" cpu: "100m" limits: memory: "256Mi" cpu: "500m" |
и dex-auth.yaml
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
replicaCount: 1 image: repository: mintel/dex-k8s-authenticator tag: latest pullPolicy: IfNotPresent dex: url: https://dex.test.local # clientID: kubernetes # clientSecret: kubernetes-secret redirectURI: https://dex-auth.test.local/callback kubernetes: apiServerURL: https://192.168.1.112 caCert: "" # Если используется собственный CA, добавьте его сюда rbac: enabled: true roles: admin: groups: - k8s-devops edit: groups: - k8s-users-owners view: groups: - k8s-users-ro ingress: enabled: true annotations: kubernetes.io/ingress.class: "nginx" path: / hosts: - dex-auth.test.local tls: - secretName: dex-tls hosts: - dex-auth.test.local config: logLevel: debug resources: requests: memory: 128Mi cpu: 100m limits: memory: 256Mi cpu: 500m dexK8sAuthenticator: port: 5555 debug: true web_path_prefix: / tlsCert: /etc/dex/tls/tls.crt tlsKey: /etc/dex/tls/tls.key clusters: - name: cluster-local short_description: "My Cluster" description: "Example Cluster Long Description…" client_secret: kubernetes-secret issuer: https://dex.test.local k8s_master_uri: https://192.168.1.112 client_id: kubernetes redirect_uri: https://dex-auth.test.local/callback # k8s_ca_uri: https://url-to-your-ca.crt caCerts: enabled: true secrets: - name: dex-ca-cert filename: ca.crt value |
есть особенность для caCerts
в value добавляем наш ca.crt в base64
ставим:
root@client:~/certs# helm repo update
на
|
1 2 3 4 5 6 7 8 |
dnsPolicy: None dnsConfig: nameservers: - 10.233.0.3 searches: - cluster.local - svc.cluster.local - test.local |
|
1 2 |
root@client:~/autentification-dex-dex-auth# kubectl get svc -n kube-system | grep coredns coredns ClusterIP 10.233.0.3 <none> 53/UDP,53/TCP,9153/TCP 167d |
root@client:~/autentification-dex-dex-auth# kubectl edit deployments.apps -n dex dex
root@client:~/autentification-dex-dex-auth# kubectl edit deployments.apps -n dex dex-auth-dex-k8s-authenticator
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
root@client:~/autentification-dex-dex-auth# kubectl get deployments.apps -n dex dex -o yaml apiVersion: apps/v1 kind: Deployment metadata: annotations: deployment.kubernetes.io/revision: "2" meta.helm.sh/release-name: dex meta.helm.sh/release-namespace: dex creationTimestamp: "2024-12-15T10:28:37Z" generation: 2 labels: app.kubernetes.io/instance: dex app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: dex app.kubernetes.io/version: 2.41.1 helm.sh/chart: dex-0.19.1 name: dex namespace: dex resourceVersion: "2908992" uid: d23d3213-a2ba-4e88-a8a7-f117c28a03e0 spec: progressDeadlineSeconds: 600 replicas: 1 revisionHistoryLimit: 10 selector: matchLabels: app.kubernetes.io/instance: dex app.kubernetes.io/name: dex strategy: rollingUpdate: maxSurge: 25% maxUnavailable: 25% type: RollingUpdate template: metadata: annotations: checksum/config: 4f0ce13e695a73a4cce73dd6ce01a6b1852776f6eee0b6eb2e5325b7323112cc creationTimestamp: null labels: app.kubernetes.io/instance: dex app.kubernetes.io/name: dex spec: containers: - args: - dex - serve - --web-http-addr - 0.0.0.0:5556 - --web-https-addr - 0.0.0.0:5554 - --telemetry-addr - 0.0.0.0:5558 - /etc/dex/config.yaml image: ghcr.io/dexidp/dex:v2.41.1 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 3 httpGet: path: /healthz/live port: telemetry scheme: HTTP periodSeconds: 10 successThreshold: 1 timeoutSeconds: 1 name: dex ports: - containerPort: 5556 name: http protocol: TCP - containerPort: 5554 name: https protocol: TCP - containerPort: 5558 name: telemetry protocol: TCP readinessProbe: failureThreshold: 3 httpGet: path: /healthz/ready port: telemetry scheme: HTTP periodSeconds: 10 successThreshold: 1 timeoutSeconds: 1 resources: limits: cpu: 500m memory: 256Mi requests: cpu: 100m memory: 128Mi securityContext: {} terminationMessagePath: /dev/termination-log terminationMessagePolicy: File volumeMounts: - mountPath: /etc/dex name: config readOnly: true - mountPath: /etc/dex/tls name: dex-tls readOnly: true dnsConfig: nameservers: - 10.233.0.3 searches: - cluster.local - svc.cluster.local - test.local dnsPolicy: None restartPolicy: Always schedulerName: default-scheduler securityContext: {} serviceAccount: dex serviceAccountName: dex terminationGracePeriodSeconds: 30 volumes: - name: config secret: defaultMode: 420 secretName: dex-config - name: dex-tls secret: defaultMode: 420 secretName: dex-tls status: availableReplicas: 1 conditions: - lastTransitionTime: "2024-12-15T10:28:40Z" lastUpdateTime: "2024-12-15T10:28:40Z" message: Deployment has minimum availability. reason: MinimumReplicasAvailable status: "True" type: Available - lastTransitionTime: "2024-12-15T10:28:37Z" lastUpdateTime: "2024-12-15T10:51:02Z" message: ReplicaSet "dex-548c7df66c" has successfully progressed. reason: NewReplicaSetAvailable status: "True" type: Progressing observedGeneration: 2 readyReplicas: 1 replicas: 1 updatedReplicas: 1 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 |
root@client:~/autentification-dex-dex-auth# kubectl get deployments.apps -n dex dex-auth-dex-k8s-authenticator -o yaml apiVersion: apps/v1 kind: Deployment metadata: annotations: deployment.kubernetes.io/revision: "5" meta.helm.sh/release-name: dex-auth meta.helm.sh/release-namespace: dex creationTimestamp: "2024-12-15T10:29:04Z" generation: 5 labels: app: dex-k8s-authenticator app.kubernetes.io/managed-by: Helm chart: dex-k8s-authenticator-1.7.0 env: dev heritage: Helm release: dex-auth name: dex-auth-dex-k8s-authenticator namespace: dex resourceVersion: "2907244" uid: c6c3787a-9e24-465e-a734-1d4013158b4a spec: progressDeadlineSeconds: 600 replicas: 1 revisionHistoryLimit: 10 selector: matchLabels: app: dex-k8s-authenticator env: dev release: dex-auth strategy: rollingUpdate: maxSurge: 25% maxUnavailable: 25% type: RollingUpdate template: metadata: annotations: checksum/config: 2fb5ed5e0c4df2a6343b4dedc4af4f78e970a2a9219c1f888c784cbf2a30e994 creationTimestamp: null labels: app: dex-k8s-authenticator env: dev release: dex-auth spec: containers: - args: - --config - config.yaml image: mintel/dex-k8s-authenticator:latest imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 3 httpGet: path: /healthz port: http scheme: HTTP periodSeconds: 10 successThreshold: 1 timeoutSeconds: 1 name: dex-k8s-authenticator ports: - containerPort: 5555 name: http protocol: TCP readinessProbe: failureThreshold: 3 httpGet: path: /healthz port: http scheme: HTTP periodSeconds: 10 successThreshold: 1 timeoutSeconds: 1 resources: limits: cpu: 500m memory: 256Mi requests: cpu: 100m memory: 128Mi terminationMessagePath: /dev/termination-log terminationMessagePolicy: File volumeMounts: - mountPath: /app/config.yaml name: config subPath: config.yaml - mountPath: /certs/ca.crt name: dex-auth-dex-k8s-authenticator-dex-ca-cert subPath: dex-ca-cert dnsConfig: nameservers: - 10.233.0.3 searches: - cluster.local - svc.cluster.local - test.local dnsPolicy: None restartPolicy: Always schedulerName: default-scheduler securityContext: {} terminationGracePeriodSeconds: 30 volumes: - configMap: defaultMode: 420 name: dex-auth-dex-k8s-authenticator name: config - name: dex-auth-dex-k8s-authenticator-dex-ca-cert secret: defaultMode: 420 secretName: dex-auth-dex-k8s-authenticator-dex-ca-cert status: availableReplicas: 1 conditions: - lastTransitionTime: "2024-12-15T10:45:09Z" lastUpdateTime: "2024-12-15T10:45:09Z" message: Deployment has minimum availability. reason: MinimumReplicasAvailable status: "True" type: Available - lastTransitionTime: "2024-12-15T10:45:05Z" lastUpdateTime: "2024-12-15T10:45:10Z" message: ReplicaSet "dex-auth-dex-k8s-authenticator-7b787957f7" has successfully progressed. reason: NewReplicaSetAvailable status: "True" type: Progressing observedGeneration: 5 readyReplicas: 1 replicas: 1 updatedReplicas: 1 |
проверяем:
|
1 2 3 4 5 |
root@client:~/autentification-dex-dex-auth# kubectl get ingress -n dex NAME CLASS HOSTS ADDRESS PORTS AGE dex nginx dex.test.local 192.168.1.191 80, 443 73m dex-auth-dex-k8s-authenticator <none> dex-auth.test.local 192.168.1.191 80, 443 73m |
идём по адресу:
https://dex-auth.test.local
нас сразу перекинет на
https://dex.test.local/auth/freeipa/login?back=&state=c365acph2k6v35bqqcbhig72d


|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
-----BEGIN CERTIFICATE----- MIIC/jCCAeagAwIBAgIBADANBgkqhkiG9w0BAQsFADAVMRMwEQYDVQQDEwprdWJl cm5ldGVzMB4XDTI0MDYzMDE0MjIxMFoXDTM0MDYyODE0MjIxMFowFTETMBEGA1UE AxMKa3ViZXJuZXRlczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPsS LBw3lg/4zrpycC1xJprK7XRIZK6AyL7DIClHPhAWn+h+m72r4PfZib9wu7/VS0e0 SkNhVhEhjTZx1yMkJfP4zwd6IUuFAptZfCgVHnJQnkPeadWUqj/zEIh2ubcgbRws jG1nh8yNRbtrxd/dpXPveBCFptOi5CqdUYodhoBDic5mYwbk8AbMf71FBhhdq8+8 y5TiFlXPglITHxe/0VHCXO3ANUvNJDbDoYUGU5I0Kv3AIwffxSrNlyVhgzX70b7e KaurkmGW577hXCEZapRjHlxl2wUwW+BCKZ7X8MCFbwUVVgVLP0nroKvEVUxQNima lip/RtaqiKrf3PypuUsCAwEAAaNZMFcwDgYDVR0PAQH/BAQDAgKkMA8GA1UdEwEB /wQFMAMBAf8wHQYDVR0OBBYEFB/OXFsCKolo5DCMnaFCxrXMBamSMBUGA1UdEQQO MAyCCmt1YmVybmV0ZXMwDQYJKoZIhvcNAQELBQADggEBAKQ3XdnYLdoS9OeETDMG j3nHXyjZsfc7TupYIpHwUH27Jh9LSzl0oxK1dWoRVizF6P1VKlKoZV7tzPPFEjqQ feE1cDXErY4acWq6v6ta3Og65TH6d9/rIqcUfr7UqZw+oMRi4UGkCP4PGoG8Kmcp mGmWBLmzBD/ChB0zE9gN2vsKgGWSYUdZoO3KbrMRE/bdH1jDABXpkXvcnQ+gPaqP w1ZNOcADFWwITbKaRAwWTCspgqKd7D1IAuPRR/kIDPIWCc0rLdvPPuKTmx4Ah3j0 TQvc8UW/gQX7OV2iReE0sZ1n/RRR4Fa+9mtUleaODao0MvqHVo+9IYFCTTi1fvRC ySE= -----END CERTIFICATE----- |
добавляем его на нашем клиенте
root@client:~# cat > /usr/local/share/ca-certificates/myca.crt
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
-----BEGIN CERTIFICATE----- MIIC/jCCAeagAwIBAgIBADANBgkqhkiG9w0BAQsFADAVMRMwEQYDVQQDEwprdWJl cm5ldGVzMB4XDTI0MDYzMDE0MjIxMFoXDTM0MDYyODE0MjIxMFowFTETMBEGA1UE AxMKa3ViZXJuZXRlczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPsS LBw3lg/4zrpycC1xJprK7XRIZK6AyL7DIClHPhAWn+h+m72r4PfZib9wu7/VS0e0 SkNhVhEhjTZx1yMkJfP4zwd6IUuFAptZfCgVHnJQnkPeadWUqj/zEIh2ubcgbRws jG1nh8yNRbtrxd/dpXPveBCFptOi5CqdUYodhoBDic5mYwbk8AbMf71FBhhdq8+8 y5TiFlXPglITHxe/0VHCXO3ANUvNJDbDoYUGU5I0Kv3AIwffxSrNlyVhgzX70b7e KaurkmGW577hXCEZapRjHlxl2wUwW+BCKZ7X8MCFbwUVVgVLP0nroKvEVUxQNima lip/RtaqiKrf3PypuUsCAwEAAaNZMFcwDgYDVR0PAQH/BAQDAgKkMA8GA1UdEwEB /wQFMAMBAf8wHQYDVR0OBBYEFB/OXFsCKolo5DCMnaFCxrXMBamSMBUGA1UdEQQO MAyCCmt1YmVybmV0ZXMwDQYJKoZIhvcNAQELBQADggEBAKQ3XdnYLdoS9OeETDMG j3nHXyjZsfc7TupYIpHwUH27Jh9LSzl0oxK1dWoRVizF6P1VKlKoZV7tzPPFEjqQ feE1cDXErY4acWq6v6ta3Og65TH6d9/rIqcUfr7UqZw+oMRi4UGkCP4PGoG8Kmcp mGmWBLmzBD/ChB0zE9gN2vsKgGWSYUdZoO3KbrMRE/bdH1jDABXpkXvcnQ+gPaqP w1ZNOcADFWwITbKaRAwWTCspgqKd7D1IAuPRR/kIDPIWCc0rLdvPPuKTmx4Ah3j0 TQvc8UW/gQX7OV2iReE0sZ1n/RRR4Fa+9mtUleaODao0MvqHVo+9IYFCTTi1fvRC ySE= -----END CERTIFICATE----- |
обновляем
так же добавляем сертификат с dex
root@client:~# kubectl get secret -n dex dex-tls -o yaml
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
root@client:~# kubectl get secret -n dex dex-tls -o yaml apiVersion: v1 data: tls.crt tls.key kind: Secret metadata: annotations: cert-manager.io/alt-names: dex.test.local,dex-auth.test.local cert-manager.io/common-name: dex.test.local cert-manager.io/ip-sans: "" cert-manager.io/subject-countries: RU cert-manager.io/subject-localities: YourCity cert-manager.io/subject-organizations: YourOrganization cert-manager.io/subject-provinces: YourState cert-manager.io/uri-sans: "" creationTimestamp: "2024-12-15T08:55:51Z" labels: controller.cert-manager.io/fao: "true" name: dex-tls namespace: dex resourceVersion: "2902166" uid: 2d5bed1d-1eff-472c-aea2-03a6f1ef0ae9 type: kubernetes.io/tls |
берём только
tls.crt
|
1 2 |
root@client |
вот наш сертификат:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
-----BEGIN CERTIFICATE----- MIIFrzCCA5egAwIBAgIUCH/P7Zinjm1wVdFQhCkWDtqT0+AwDQYJKoZIhvcNAQEL BQAwRTELMAkGA1UEBhMCa2cxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAgFw0yNDEyMTUwODU1MThaGA8yMDUy MDUwMjA4NTUxOFowaDELMAkGA1UEBhMCUlUxEjAQBgNVBAgMCVlvdXJTdGF0ZTER MA8GA1UEBwwIWW91ckNpdHkxGTAXBgNVBAoMEFlvdXJPcmdhbml6YXRpb24xFzAV BgNVBAMMDmRleC50ZXN0LmxvY2FsMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC CgKCAgEA11xPhtHcJPjMApLBtYx2DjG0FwbB5QB87KrQE1DOc7TI6WeA8t51wPlE LbiqiZMD1R+R3ZohQVktwhpWDkrOvQl/pLbX3/GFOuW5Y4EXusVu/tvJEM9VUsSm CkVt4Xk1vpzgO1dnXh/nYBQHzru60jlgtp0CGN79mICMQbm+iiHj/4vku3Bk++4x SyQ07SX6LT8yPJnCAlV/Swp6Vu0MN2I0dmgl9Azz1OCZ/7PTZc+Uz8KBsi4RXfOB Cvc/+CF6rTKMJq4FjLfAPhqrlxfgoKq2XKBCFFQh/qAhplS9NUFb20oOPLOLqRwm CK78CA/gzWbIAVDUtIh6eQILn9/LAtffWYHkV7mvyAWO1TAwDeESsvXXiIfFQ23K nQ7gf/86OY3XWOsLGPd4GxNyzqYef427MQq9X37ebtQf61OyR4bSPjL/O1+bMTdi o7Mhg4bRaSnGG/hlS0tRdZdTANQMMgoDTeR+DIEus/kwIbnAJ6mD+imxb47urICf VOUADIjnC1RbAlBTLckjFpPpbdOg4FgTWOwjE91cFDpPL2832bcKy2AnCuWcjzJI Ir6rH48YjdWGXAt8w1Ov8VDDV2n3NMqb0mx/VDK9lfE2aSM/X8rMagpOICQTaBkd vjKTFaCV5nrNkmOpMA1GhADZ8V5GpJr1NHm5Wk2Kgkkwqksdk68CAwEAAaNyMHAw LgYDVR0RBCcwJYIOZGV4LnRlc3QubG9jYWyCE2RleC1hdXRoLnRlc3QubG9jYWww HQYDVR0OBBYEFKR85gNF3SLlxBiwOIYA0bCYj8hNMB8GA1UdIwQYMBaAFEHzZYwM ZOMjNeMQC21old/vFjvqMA0GCSqGSIb3DQEBCwUAA4ICAQBUJd3Yr1OYZgonjwjG piUumQ53cuNKWztX5fY7WXU+MBlgzRy+8UAE9ZNB3m07YXjUO3YIuDLLSiwlpCEr l4q6wnkCuzlwpbNQPJAsdYGrkRt+kTJMBTAfvt6bNcoLCixu43FJBviwxxp3xUwv 0xiM0oCfITuVPddKmse06sj88ZsemhOZH9SSSaSlt9cLGAThIiogZCwBdrNxFv8V bex4xlO+tVkUfNTIkyomzvY3/StACxKd9aUwuegsUilq1NE8S/DiyLO/7vN3Mh+K uy0zWtcFyLfB503vRZh9ScivaAEtIVDVG7FmSjODo9GghibAL8TaR10alcoqyp47 Jh/Yw4f9o2+uRkiEj+RGuo+wpUMMC0DqGF2hwF5Pc0ypGBBIRbkZGFNgvjahsY0i N3YhOA0ar75T8qD1EBt6CSyxtUdquFsfS/SAL8OWFzgxN8jkwxBa7lTMfmeOwz8h Ci17MY+3dfTYGW6pZdnAwwyl/8/vL02pr5n+qexoCuNN60Uc0afH+5fwhn90S1lu AkOgxz5BhKyMiMc66tOGuzDjDTjgThfCYmYEg7agjFf3lXH1PvO6IzXy2lcb9gV5 IRsaSNOwfiz6wgN5bQ8RA5RF7deZ+jYEvDSYKApuJ8AsoUSKpKr8ZLriDHQQpdc3 uY6JCc65wQl+wV+tGp6k51qtNQ== -----END CERTIFICATE----- |
root@client:~# sudo update-ca-certificates
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
{ "iss": "https://dex.test.local", "sub": "CgV1c2VyMRIHZnJlZWlwYQ", "aud": "kubernetes", "exp": 1734955376, "iat": 1734868976, "at_hash": "Qn83iEgoWVr-4GEyVtDhxw", "c_hash": "ihn0JLjxj4sAgWM8aSls5Q", "email": "user1@test.local", "email_verified": true, "groups": [ "ipausers", "nexus-admins", "vault-admins", "s3-minio-admins", "k8s-devops" ], "name": "user1 user1" } |
видимо что в группе подтянулись все группы из freeipa
|
1 2 3 4 5 6 7 8 |
"groups": [ "ipausers", "nexus-admins", "vault-admins", "s3-minio-admins", "k8s-devops" ], |
теперь надо поправить на мастерах api для oidc
root@kub-master1:~# cat /etc/kubernetes/manifests/kube-apiserver.yaml
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
root@kub-master1:~# cat /etc/kubernetes/manifests/kube-apiserver.yaml apiVersion: v1 kind: Pod metadata: annotations: kubeadm.kubernetes.io/kube-apiserver.advertise-address.endpoint: 192.168.1.112:6443 creationTimestamp: null labels: component: kube-apiserver tier: control-plane name: kube-apiserver namespace: kube-system spec: containers: - command: - kube-apiserver - --advertise-address=192.168.1.112 - --allow-privileged=true - --anonymous-auth=True - --apiserver-count=3 - --authorization-mode=Node,RBAC - --bind-address=0.0.0.0 - --client-ca-file=/etc/kubernetes/ssl/ca.crt - --default-not-ready-toleration-seconds=300 - --default-unreachable-toleration-seconds=300 - --enable-admission-plugins=NodeRestriction - --enable-aggregator-routing=False - --enable-bootstrap-token-auth=true - --endpoint-reconciler-type=lease - --etcd-cafile=/etc/ssl/etcd/ssl/ca.pem - --etcd-certfile=/etc/ssl/etcd/ssl/node-kub-master1.test.local.pem - --etcd-keyfile=/etc/ssl/etcd/ssl/node-kub-master1.test.local-key.pem - --etcd-servers=https://192.168.1.112:2379,https://192.168.1.113:2379,https://192.168.1.114:2379 - --event-ttl=1h0m0s - --kubelet-client-certificate=/etc/kubernetes/ssl/apiserver-kubelet-client.crt - --kubelet-client-key=/etc/kubernetes/ssl/apiserver-kubelet-client.key - --kubelet-preferred-address-types=InternalDNS,InternalIP,Hostname,ExternalDNS,ExternalIP - --profiling=False - --proxy-client-cert-file=/etc/kubernetes/ssl/front-proxy-client.crt - --proxy-client-key-file=/etc/kubernetes/ssl/front-proxy-client.key - --request-timeout=1m0s - --requestheader-allowed-names=front-proxy-client - --requestheader-client-ca-file=/etc/kubernetes/ssl/front-proxy-ca.crt - --requestheader-extra-headers-prefix=X-Remote-Extra- - --requestheader-group-headers=X-Remote-Group - --requestheader-username-headers=X-Remote-User - --secure-port=6443 - --service-account-issuer=https://kubernetes.default.svc.cluster.local - --service-account-key-file=/etc/kubernetes/ssl/sa.pub - --service-account-lookup=True - --service-account-signing-key-file=/etc/kubernetes/ssl/sa.key - --service-cluster-ip-range=10.233.0.0/18 - --service-node-port-range=30000-32767 - --storage-backend=etcd3 - --tls-cert-file=/etc/kubernetes/ssl/apiserver.crt - --tls-private-key-file=/etc/kubernetes/ssl/apiserver.key - --oidc-issuer-url=https://dex.test.local - --oidc-client-id=kubernetes # берём отсюда dexK8sAuthenticator -> clusters -> client_id - --oidc-ca-file=/etc/kubernetes/ssl/dex.crt - --oidc-username-claim=email - --oidc-groups-claim=groups image: registry.k8s.io/kube-apiserver:v1.24.0 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 8 httpGet: host: 192.168.1.112 path: /livez port: 6443 scheme: HTTPS initialDelaySeconds: 10 periodSeconds: 10 timeoutSeconds: 15 name: kube-apiserver readinessProbe: failureThreshold: 3 httpGet: host: 192.168.1.112 path: /readyz port: 6443 scheme: HTTPS periodSeconds: 1 timeoutSeconds: 15 resources: requests: cpu: 250m startupProbe: failureThreshold: 30 httpGet: host: 192.168.1.112 path: /livez port: 6443 scheme: HTTPS initialDelaySeconds: 10 periodSeconds: 10 timeoutSeconds: 15 volumeMounts: - mountPath: /etc/ssl/certs name: ca-certs readOnly: true - mountPath: /etc/ca-certificates name: etc-ca-certificates readOnly: true - mountPath: /etc/pki name: etc-pki readOnly: true - mountPath: /etc/ssl/etcd/ssl name: etcd-certs-0 readOnly: true - mountPath: /etc/kubernetes/ssl name: k8s-certs readOnly: true - mountPath: /usr/local/share/ca-certificates name: usr-local-share-ca-certificates readOnly: true - mountPath: /usr/share/ca-certificates name: usr-share-ca-certificates readOnly: true hostNetwork: true priorityClassName: system-node-critical securityContext: seccompProfile: type: RuntimeDefault volumes: - hostPath: path: /etc/ssl/certs type: DirectoryOrCreate name: ca-certs - hostPath: path: /etc/ca-certificates type: DirectoryOrCreate name: etc-ca-certificates - hostPath: path: /etc/pki type: DirectoryOrCreate name: etc-pki - hostPath: path: /etc/ssl/etcd/ssl type: DirectoryOrCreate name: etcd-certs-0 - hostPath: path: /etc/kubernetes/ssl type: DirectoryOrCreate name: k8s-certs - hostPath: path: /usr/local/share/ca-certificates type: DirectoryOrCreate name: usr-local-share-ca-certificates - hostPath: path: /usr/share/ca-certificates type: "" name: usr-share-ca-certificates status: {} |
мы же добавляем только oidc
root@kub-master1:~# cat /etc/kubernetes/manifests/kube-apiserver.yaml| grep -i oidc
|
1 2 3 4 5 6 |
- --oidc-issuer-url=https://dex.test.local - --oidc-client-id=kubernetes # берём отсюда dexK8sAuthenticator -> clusters -> client_id - --oidc-ca-file=/etc/kubernetes/ssl/dex.crt - --oidc-username-claim=email - --oidc-groups-claim=groups |
добавляем наш dex сертификат: /etc/kubernetes/ssl/dex.crt и данные изменения по oidc на все мастер сервера:
- --oidc-client-id=<client_id> # Должен совпадать с тем, что в Dex (например 'kubernetes')
- --oidc-username-claim=email # Или другой claim, который возвращает Dex
- --oidc-groups-claim=groups # Если хотите маппить группы
- --oidc-ca-file=/etc/kubernetes/ssl/dex.crt # или другой путь к CA, который выдало Dex (если Dex использует свой сертификат)
теперь нам нужно создать rbac роли:
- для админов/девопсов будем использовать уже существующий ClusterRole - cluster-admin
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
root@client:~# kubectl get clusterroles.rbac.authorization.k8s.io cluster-admin -o yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: annotations: rbac.authorization.kubernetes.io/autoupdate: "true" creationTimestamp: "2024-06-30T14:22:26Z" labels: kubernetes.io/bootstrapping: rbac-defaults name: cluster-admin resourceVersion: "77" uid: 34ce792f-9e65-4b05-aa48-2023e2e6adc9 rules: - apiGroups: - '*' resources: - '*' verbs: - '*' - nonResourceURLs: - '*' verbs: - '*' |
он уже есть в системе, поэтому для него нужно создать только ClusterRoleBinding
group-devops.yaml
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: k8s-devops-cluster-admin subjects: - kind: Group name: k8s-devops apiGroup: rbac.authorization.k8s.io roleRef: kind: ClusterRole name: cluster-admin apiGroup: rbac.authorization.k8s.io |
root@client:~/autentification-dex-dex-auth# kubectl apply -f group-devops.yaml
проверяем:
|
1 2 3 4 5 6 7 8 9 |
test1@client:~$ kubectl get nodes NAME STATUS ROLES AGE VERSION kub-master1.test.local Ready control-plane 181d v1.24.0 kub-master2.test.local Ready control-plane 181d v1.24.0 kub-master3.test.local Ready control-plane 181d v1.24.0 kub-worker1.test.local Ready <none> 181d v1.24.0 kub-worker2.test.local Ready <none> 76d v1.24.0 kub-worker3.test.local Ready <none> 181d v1.24.0 |
как видим под пользователем test1@client нам теперь доступен вывод серверов.
2. создадим теперь Role и RoleBinding для доступа пользователей в определённый namespace в нашем случае это dev. Вот файл group-k8s-users-ro.yaml
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: namespace: dev name: dev-read-only rules: - apiGroups: ["", "apps", "batch", "extensions", "networking.k8s.io"] resources: ["*"] verbs: ["get", "list", "watch"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: k8s-users-ro-read-only namespace: dev subjects: - kind: Group name: k8s-users-ro apiGroup: rbac.authorization.k8s.io roleRef: kind: Role name: dev-read-only apiGroup: rbac.authorization.k8s.io |
применим его:
root@client:~/autentification-dex-dex-auth# kubectl apply -f group-k8s-users-ro.yaml


|
1 2 |
kubectl config set-cluster cluster-local \ --server=https://192.168.1.112 |
|
1 2 3 4 5 6 7 |
kubectl config set-credentials user2-cluster-local \ --auth-provider=oidc \ --auth-provider-arg="idp-issuer-url=https://dex.test.local" \ --auth-provider-arg="client-id=kubernetes" \ --auth-provider-arg="client-secret=kubernetes-secret" \ --auth-provider-arg="refresh-token=ChlpamVxN2c2d3dkNGZhYWozNXF6dzNhN202EhlkZ2lnYXBndmNta29uNGg2d2V5N3R1ZWF6" \ --auth-provider-arg="id-token=eyJhbGciOiJSUzI1NiIsImtpZCI6IjdlNDdlNTNlMzg4Yzg1YzY1YjkzOGFiOGNiMWZiYjllYTA0ZjIyMzcifQ.eyJpc3MiOiJodHRwczovL2RleC50ZXN0LmxvY2FsIiwic3ViIjoiQ2dWMWMyVnlNaElIWm5KbFpXbHdZUSIsImF1ZCI6Imt1YmVybmV0ZXMiLCJleHAiOjE3MzU1NTY1MzYsImlhdCI6MTczNTQ3MDEzNiwiYXRfaGFzaCI6IlZYWG51ck5pVDVpcm5ZUzFITGE5WXciLCJjX2hhc2giOiIzX3ZwWlJQWUdMZVBScVVVVnJCQ053IiwiZW1haWwiOiJ1c2VyMkB0ZXN0LmxvY2FsIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImdyb3VwcyI6WyJpcGF1c2VycyIsIm5leHVzLXJvLXVzZXJzIiwidmF1bHQtcm8tdXNlcnMiLCJzMy1taW5pby1yby11c2VycyIsIms4cy11c2Vycy1ybyJdLCJuYW1lIjoidXNlcjIgdXNlcjIifQ.XY5tBN1JDow-6QhP1ir0TcIxOiyjtLSM4d_-K6Cm6qxDOmL1k7hhrUrI58ZdRoqcN55mmPLq99BoH9yO4be_H8PLMqaHhzsw0vr46jRiS-gGQLJUKJtKxSRwPuNjIu-Fz48jF2TFU7XfbbcnzNQwFiPw3_grqoWl0quf9InJozxVpHc0JkNLk9mpvknp7vp7qswfXCS1gSc54deY8g29c2OBKJeB46GlyNMVYLEEboLcjU7mjyby1M8K9lUqm76h6wI6uYP_MgZgkNN4a5zHJFi-pqOx1PX-Al5KZE90CeNc5rUByd1Tj7csd66FKsoi_ZEyxLjx55EC9CErKAdi-Q" |
|
1 2 3 |
kubectl config set-context user2-cluster-local \ --cluster=cluster-local \ --user=user2-cluster-local |
|
1 |
kubectl config use-context user2-cluster-local |
далее надо поправить порт для кластера потому что у меня используется 6443
test2@client:~$ nano ~/.kube/config
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
apiVersion: v1 clusters: - cluster: server: https://192.168.1.112:6443 name: cluster-local contexts: - context: cluster: cluster-local user: user2-cluster-local name: user2-cluster-local current-context: user2-cluster-local kind: Config preferences: {} users: - name: user2-cluster-local user: auth-provider: config: client-id: kubernetes client-secret: kubernetes-secret id-token: eyJhbGciOiJSUzI1NiIsImtpZCI6IjdlNDdlNTNlMzg4Yzg1YzY1YjkzOGFiOGNiMWZiYjllYTA0ZjIyMzcifQ.eyJpc3MiOiJodHRwczovL2RleC50ZXN0LmxvY2FsIiwic3ViIjoiQ2dWMWMyVnlNaElIWm5KbFpXbHdZUSIs> idp-issuer-url: https://dex.test.local refresh-token: ChlpamVxN2c2d3dkNGZhYWozNXF6dzNhN202EhlkZ2lnYXBndmNta29uNGg2d2V5N3R1ZWF6 name: oidc |
проверяем:
|
1 2 3 4 5 6 7 8 9 10 11 |
test2@client:~$ kubectl get nodes Error from server (Forbidden): nodes is forbidden: User "user2@test.local" cannot list resource "nodes" in API group "" at the cluster scope test2@client:~$ kubectl get pod -n dev NAME READY STATUS RESTARTS AGE first-app-common-chart-6cdf48bf88-5785g 1/1 Running 14 (21h ago) 36d second-app-common-chart-6b8457fbfb-sgmh8 1/1 Running 12 (21h ago) 36d test2@client:~$ kubectl get pod -n dex Error from server (Forbidden): pods is forbidden: User "user2@test.local" cannot list resource "pods" in API group "" in the namespace "dex" |
как видим мы не можем вывести ни ноды ни поды из неймспейса dex, но нам доступны поды в неймспейсе dev
rancher
kubectl create namespace cattle-system
helm repo add rancher-stable https://releases.rancher.com/server-charts/stable
helm repo update
[root@ansible ansible]# cd kubespray-official/autentification-keycloak/certs-rancher/
ca_openssl.cnf
|
1 2 3 4 5 6 7 |
[ v3_ca ] subjectAltName = @alt_names [ alt_names ] DNS.1 = rancher.test.local DNS.2 = *.test.local |
rancher_openssl.cnf
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
[ req ] default_bits = 4096 prompt = no default_md = sha256 distinguished_name = dn req_extensions = req_ext [ dn ] C = RU ST = YourState L = YourCity O = YourOrganization CN = rancher.test.local # Основное доменное имя [ req_ext ] subjectAltName = @alt_names [ alt_names ] DNS.1 = rancher.test.local DNS.2 = *.test.local |
[root@ansible kubespray-official]# scp -r /etc/ansible/kubespray-official/autentification-keycloak/ root@192.168.1.121:~/
создаём секрет
root@client:~/autentification-keycloak/certs-rancher# kubectl -n cattle-system create secret tls tls-rancher-ingress --cert=./rancher.crt --key=./rancher.key
запускаем установку:
values-rancher.yaml
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
# Основной домен для доступа к Rancher hostname: rancher.test.local replicas: 1 privateCA: true caCerts: secretName: tls-ca # ваш секрет с CA сертификатом API # extraEnv: # - name: CATTLE_K8S_API_ENDPOINT # value: "https://192.168.1.112:6443" # - name: KUBERNETES_SERVICE_HOST # value: "192.168.1.112" # - name: KUBERNETES_SERVICE_PORT # value: "6443" ingress: # If set to false, ingress will not be created # Defaults to true # options: true, false enabled: true includeDefaultExtraAnnotations: true extraAnnotations: {} ingressClassName: "nginx" # Certain ingress controllers will require the pathType or path to be set to a different value. pathType: ImplementationSpecific path: "/" # backend port number servicePort: 80 tls: # options: rancher, letsEncrypt, secret source: secret secretName: tls-rancher-ingress resources: requests: cpu: "100m" memory: "500Mi" limits: cpu: "1000m" memory: "2024Mi" persistence: enabled: true # Включаем использование persistent volume (если необходимо) storageClass: "nfs-client" # Укажите storageClass, подходящий для вашего кластера accessModes: - ReadWriteOnce size: 10Gi startupProbe: enabled: true initialDelaySeconds: 60 periodSeconds: 20 timeoutSeconds: 5 failureThreshold: 30 # Это даст Rancher до 10 минут на запуск readinessProbe: initialDelaySeconds: 30 periodSeconds: 20 timeoutSeconds: 5 failureThreshold: 10 livenessProbe: initialDelaySeconds: 300 periodSeconds: 30 timeoutSeconds: 5 failureThreshold: 5 |
root@client:~/autentification-keycloak# scp root@192.168.1.112:/etc/kubernetes/ssl/ca.crt ./ca.crt
root@client:~/autentification-keycloak# kubectl -n cattle-system create secret generic tls-ca --from-file=cacerts.pem=./ca.crt
root@client:~/autentification-keycloak# helm install rancher rancher-stable/rancher -n cattle-system --version 2.9.3 -f values-rancher.yaml --set bootstrapPassword=ddjjKKSSlldd345hhRRsd
kubectl get secret --namespace cattle-system bootstrap-secret -o go-template='{{.data.bootstrapPassword|base64decode}}{{ "\n" }}'
echo https://rancher.test.local/dashboard/?setup=$(kubectl get secret --namespace cattle-system bootstrap-secret -o go-template='{{.data.bootstrapPassword|base64decode}}')
получаем
https://rancher.test.local/dashboard/?setup=ddjjKKSSlldd345hhRRsd

================================================
так как rancher в кластере жрёт слишком много ресурсов и у меня тупо сетка отваливалась, я поднял его на отдельной виртуалке:
192.168.1.128
первоначальная установка 4 ядра 4 гб оперативки.
в докере
|
1 2 |
docker run -d --name=v2.11-head --restart=unless-stopped --privileged -p 80:80 -p 443:443 -v /root/rancher:/var/lib/rancher rancher/rancher:v2.11-head --debug |
смотрим пароль
root@debian:~# docker logs v2.11-head 2>&1 | grep "Bootstrap Password:"
2025/03/29 06:46:41 [INFO] Bootstrap Password: 9ppkpqdbmwv9txs9w8png5jc4h2btzhktdn6km6b2rp96gk9s9rngv
идём в панель
https://192.168.1.128/dashboard/auth/login

задаём пароль

Password must be at least 12 characters
после установки ресурсы можно порезать до 2 ядер и 2гб оперативки.
Rancher интеграция с Freeipa
создаём системного пользователя во freeipa
[root@freeipa-1 ~]# cd /etc/ipa
[root@freeipa-1 ipa]# bash freeipa-sam.sh
выбираем 1
вводим freeipa-1.test.local
выбираем 3
вводим admin
выбираем 4
вводим Secret123
дальше можем добавлять нового пользователя,
вводим add
имя пользователя rancher
пароль Secret123 (можно любой но я делаю везде одинаковый)
проверяем пользователей нажимаем ls
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
### FreeIPA - System Account Manager ### 1.) ldapserver=freeipa-1.test.local 2.) domain=test.local (ldapdomain=dc=test,dc=local) 3.) binduser=uid=admin,cn=users,cn=accounts,dc=test,dc=local 4.) bindpass=SET! 5.) ssl=true Actions (ready): add | rm | ls | info | passwd | save --- Results --- dn: uid=sudo,cn=sysaccounts,cn=etc,dc=test,dc=local dn: uid=nexus,cn=sysaccounts,cn=etc,dc=test,dc=local dn: uid=gitlab,cn=sysaccounts,cn=etc,dc=test,dc=local dn: uid=vault,cn=sysaccounts,cn=etc,dc=test,dc=local dn: uid=s3minio,cn=sysaccounts,cn=etc,dc=test,dc=local dn: uid=k8s-access,cn=sysaccounts,cn=etc,dc=test,dc=local dn: uid=keycloak,cn=sysaccounts,cn=etc,dc=test,dc=local dn: uid=rancher,cn=sysaccounts,cn=etc,dc=test,dc=local --- End Results --- > |

заполняем поля:
User Search Base cn=users,cn=accounts,dc=test,dc=local
Group Search Base cn=groups,cn=accounts,dc=test,dc=local
Object Class (Users) inetorgperson
Login Attribute uid
Username Attribute uid
User Member Attribute memberOf
Object Class (Groups) groupofnames
Group Name Attribute cn
Group Member Attribute member
Group DN Attribute entrydn
в самом низу user1 это из freeipa пользователь, под которого вы сразу переключитесь если всё правильно настроено.


проверяем:

как видим появилась возможность авторизации через freeipa
так же можем авторизовываться и через локального пользователя.

как видим подключение успешно произведено.
Rancher подключение к k8s кластеру
заходим в rancher под рутом далее
Import -> Generic

cluster-name: cluster-local

получаем список команд

так как у меня самоподписанный сертификат то я использую вторую команду:
curl --insecure -sfL https://192.168.1.128/v3/import/57pzg7t2dnxvtjqn6tj7w2bhwttkr7gddgzcwc9xvgxgsfzw65ml74_c-sv925.yaml | kubectl apply -f -
результат команды такой
|
1 2 3 4 5 6 7 8 9 10 11 |
clusterrole.rbac.authorization.k8s.io/proxy-clusterrole-kubeapiserver created clusterrolebinding.rbac.authorization.k8s.io/proxy-role-binding-kubernetes-master created namespace/cattle-system created serviceaccount/cattle created clusterrolebinding.rbac.authorization.k8s.io/cattle-admin-binding created secret/cattle-credentials-4b82eaf created clusterrole.rbac.authorization.k8s.io/cattle-admin created Warning: spec.template.spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[0].key: beta.kubernetes.io/os is deprecated since v1.14; use "kubernetes.io/os" instead deployment.apps/cattle-cluster-agent created service/cattle-cluster-agent created |
можем посмотреть состояние процесса:

тут можем посмотреть какие сервера добавились в кластер:

дальше можем посмотреть разные данные по кластеру сторадж классы, ноды и т.д.
одну ноду я выключил так как у меня не хватало ресурсов на моём компе

добавляем группы, заходим из под пользователя user1 так как из под рута будет недоступно - х.з. почему

группа k8s-devops будет с полными доступами
добавим группу k8s-users-ro для пользователей:

так же ограничиваем доступ группами

чтобы ограничить группу неймспейсами - создадим проект с названием limited-access-project

создали проект теперь добавим к нему группу k8s-users-ro и удалим пользователя user1


теперь переместим в этот проект нужный нам namespace

любому проекту можно назначать несколько групп с разными правами, например добавим группу k8s-devops как owner

можно кастомайзить доступы при добавлении группы:

так же всегда можно добавлять RBAC руками - rancher нормально это подтягивает.
Rancher проверка авторизации для пользователей k8s
во freeipa есть пользователь user-2 в группе k8s-users-ro

авторизуемся с этим пользователем в rancher

теперь нужно скачать kubeconfig
там на выбор можно файл скачать можно в буфер обмена скопировать

теперь идём в консоль
создаём директорию:
user2@client:~$ mkdir ~/.kube
создаём файл:
user2@client:~$ cat > .kube/config
и вставляем содержимое которое скопировали:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
apiVersion: v1 kind: Config clusters: - name: "cluster-local" cluster: server: "https://192.168.1.128/k8s/clusters/c-sv925" certificate-authority-data: "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJ2RENDQ\ VdPZ0F3SUJBZ0lCQURBS0JnZ3Foa2pPUFFRREFqQkdNUnd3R2dZRFZRUUtFeE5rZVc1aGJXbGoKY\ kdsemRHVnVaWEl0YjNKbk1TWXdKQVlEVlFRRERCMWtlVzVoYldsamJHbHpkR1Z1WlhJdFkyRkFNV\ GMwTXpJegpNRGd3TXpBZUZ3MHlOVEF6TWprd05qUTJORE5hRncwek5UQXpNamN3TmpRMk5ETmFNR\ Vl4SERBYUJnTlZCQW9UCkUyUjVibUZ0YVdOc2FYTjBaVzVsY2kxdmNtY3hKakFrQmdOVkJBTU1IV\ 1I1Ym1GdGFXTnNhWE4wWlc1bGNpMWoKWVVBeE56UXpNak13T0RBek1Ga3dFd1lIS29aSXpqMENBU\ VlJS29aSXpqMERBUWNEUWdBRVc0MitBTFYrMTB5NQpNb0h6SFpZRnljazlIcHdKVy9QK3h2SUYwd\ 2RkNmV6NnVGZ2N4cnVrM3lqelFKZEdBaTQ2S0NrcCtNaGlwTHhtCjBPZjEwVUZ6MzZOQ01FQXdEZ\ 1lEVlIwUEFRSC9CQVFEQWdLa01BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0hRWUQKVlIwT0JCWUVGR\ GVnZXBuK01lQmVJdWw1LzZ1cWh5alNvT2pBTUFvR0NDcUdTTTQ5QkFNQ0EwY0FNRVFDSUVkSApzT\ 2FJZEVTS0ZSZkhwNTdBek41SHhMeHkrdGJITWlXUGhHQkcrOVgyQWlBaGo2dmpiOUZjbVhtUjExS\ zhVSE52CnNmSDBmYk9KQklxYzVYTnY1WFJBVFE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0t" users: - name: "cluster-local" user: token: "kubeconfig-u-kvtgq3ddgxnt9qk:njmk6v2w8cq57gqnwzn8qq598vnb9vqmvxhwtr9mssgzrrl6bftqws" contexts: - name: "cluster-local" context: user: "cluster-local" cluster: "cluster-local" current-context: "cluster-local" |
пытаем отобразить все POD
user2@client:~$ kubectl get pod -A
получаем ответ
|
1 2 |
Error from server (Forbidden): pods is forbidden: User "u-kvtgq3ddgx" cannot list resource "pods" in API group "" at the cluster scope |
смотрим наш неймспейс dev там всё ок
|
1 2 3 4 5 |
user2@client:~$ kubectl get pod -n dev NAME READY STATUS RESTARTS AGE first-app-common-chart-6cdf48bf88-q9db6 1/1 Running 7 (174m ago) 71d second-app-common-chart-6b8457fbfb-fqm8w 1/1 Running 6 (174m ago) 71d |
как видим всё работает
удаление rancher из кластера
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# Get the resource types for both namespace and cluster-level NS_TYPES=$(kubectl api-resources --verbs=list -o name --namespaced=true | grep "cattle.io") CLUSTER_TYPES=$(kubectl api-resources --verbs=list -o name --namespaced=false | grep "cattle.io") echo "Removing finalizers from namespaced Rancher resources" for type in $NS_TYPES; do echo "Removing finalizers for $type" kubectl get $type --all-namespaces -o custom-columns='NAMESPACE:.metadata.namespace','NAME:.metadata.name' --no-headers | awk '{print $1 " " $2}' | xargs -L1 bash -c "kubectl patch --dry-run=client -n \$0 $type/\$1 --type=merge -p \$(kubectl get -n \$0 $type/\$1 -o json | jq -Mcr '.metadata.finalizers // [] | {metadata:{finalizers:map(select(. | (contains(\"controller.cattle.io/\") or contains(\"wrangler.cattle.io/\")) | not ))}}')" done echo "Removing finalizers from cluster Rancher resources" for type in $CLUSTER_TYPES; do echo "Removing finalizers for $type" kubectl get $type -o name --show-kind --no-headers | awk '{print $1 }' | xargs -L1 bash -c "kubectl patch --dry-run=client \$0 --type=merge -p \$(kubectl get \$0 -o json | jq -Mcr '.metadata.finalizers // [] | {metadata:{finalizers:map(select(. | (contains(\"controller.cattle.io/\") or contains(\"wrangler.cattle.io/\")) | not ))}}')" done |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
#!/bin/bash NAMESPACES=$(for ns in $(kubectl get ns -o jsonpath='{.items[*].metadata.name}'); do if kubectl get ns "$ns" -o json | grep -q 'wrangler.cattle.io'; then echo "$ns" fi done) echo "Removing finalizers from namespaces" for ns in $NAMESPACES; do echo "Removing finalizers from namespace $ns" PATCH=$(kubectl get namespace "$ns" -o json | jq -Mc ' { metadata: { finalizers: ( .metadata.finalizers // [] | map( select( (. | contains("controller.cattle.io/") | not) and (. | contains("wrangler.cattle.io/") | not) ) ) ) } } ') if [[ -n "$PATCH" && "$PATCH" != "{}" ]]; then echo "Patching: $PATCH" kubectl patch namespace "$ns" --type=merge -p "$PATCH" else echo "Nothing to patch for $ns" fi done |
для удаления crd используем:
|
1 |
for crd in $(kubectl get crd -o name | grep cattle.io | cut -d'/' -f2); do echo "Patching $crd to remove finalizers…"; kubectl patch crd "$crd" --type=json -p='[{"op": "remove", "path": "/metadata/finalizers"}]'; kubectl delete crd "$crd"; done |
как только процесс остановится отменяем процесс и перезапускаем и так до победного
Keycloak
Keycloak - это приложение для реализации единой точки аутентификации и авторизации. Данную технологию единого входа также называют Single Sign-On или, сокращенно, SSO. А подобные сервисы имеют общее название "Система управления идентификацией и доступом" или Identity and Access Management (IAM).
Keycloak может предоставить возможность пользователям получать права для различных приложений, пройдя один раз процесс аутентификации. Разработчикам не нужно для этого писать много кода. А инженеры DevOps могут настроить аутентификацию через общую базу пользователей для приложений, у которых нет дополнительных механизмов интеграции.
Среди функций и возможностей выделяют:
- SSO.
- Выдачу токенов.
- Двухфакторную аутентификацию.
- Авторизацию через социальные сети.
- Возможность интеграции со службами каталогов.
- Автоматическую аутентификацию с использованием тикетов Kerberos.
- Управление разными изолированными средами (Realm) со своими настройками.
- Свой интерфейс для регистрации и аутентификации пользователей с возможностью настройки внешнего вида.
Keycloak имеет клиент-серверную инфраструктуру. В качестве сервера используется готовый пакет, устанавливаемый на операционную систему (есть поддержка Linux, Windows) или docker-приложение. В качестве клиента используется адаптер — блок кода, который должен использовать разработчик для интеграции своего приложения с сервером.
Поддерживается два стандарта обмена данными аутентификации и авторизации: OpenID Connect и SAML. В зависимости от данного стандарта Keycloak предлагает готовые шаблоны адаптеров для языков программирования, платформ или приложений а также их фреймворков/расширений:
1. Для OpenID Connect:
- Java (Spring Boot, Wildfly Elytron OIDC).
- JavaScript.
- Node.js.
- C#.
- Python.
- Android/iOS.
- Apache Web Server (mod_auth_openidc).
2. Для SAML:
- Java.
- Apache Web Server (mod_auth_mellon).
Подробнее о поддерживаемых языках можно почитать в официальной документации.
ставим keycloak в k8s вот офф helm чарт
https://github.com/codecentric/helm-charts/tree/master
добавляем репозиторий
|
1 2 3 |
kubectl create secret generic keycloak-admin-settings -n keycloak \ --from-literal=admin-password=Secret123 \ --from-literal=admin-username=admin |
создаём сертификаты чтоб работы по https
|
1 2 3 4 5 6 7 |
[ v3_ca ] subjectAltName = @alt_names [ alt_names ] DNS.1 = keycloak.test.local |
root@client:~# cat keycloak_openssl.cnf
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
[ req ] default_bits = 4096 prompt = no default_md = sha256 distinguished_name = dn req_extensions = req_ext [ dn ] C = RU ST = YourState L = YourCity O = YourOrganization CN = keycloak.test.local # Основное доменное имя [ req_ext ] subjectAltName = @alt_names [ alt_names ] DNS.1 = keycloak.test.local |
root@client:~/autentification-keycloak/certs# openssl genrsa -out ca.key 4096
создаём секреты
вот values
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
extraEnv: | - name: KEYCLOAK_USER valueFrom: secretKeyRef: name: keycloak-admin-settings key: admin-username - name: KEYCLOAK_PASSWORD valueFrom: secretKeyRef: name: keycloak-admin-settings key: admin-password - name: PROXY_ADDRESS_FORWARDING value: "true" - name: KEYCLOAK_EXTRA_ARGS value: "-Dkeycloak.frontendUrl=https://keycloak.test.local/" - name: KEYCLOAK_ENABLE_HTTPS value: 'true' resources: requests: cpu: "500m" memory: "1024Mi" limits: cpu: "500m" memory: "1024Mi" ingress: enabled: true ingressClassName: "nginx" servicePort: http # annotations: # # Это чтобы nginx всегда редиректил http на https # nginx.ingress.kubernetes.io/force-ssl-redirect: "true" rules: - host: 'keycloak.test.local' paths: - path: / pathType: Prefix tls: - hosts: - 'keycloak.test.local' secretName: "keycloak-tls" console: enabled: true ingressClassName: "nginx" rules: - host: 'keycloak.test.local' paths: - path: /auth/admin/ pathType: Prefix tls: - hosts: - 'keycloak.test.local' secretName: "keycloak-tls" postgresql: enabled: true # PostgreSQL User to create postgresqlUsername: keycloak # PostgreSQL Password for the new user postgresqlPassword: keycloak # PostgreSQL Database to create postgresqlDatabase: keycloak resources: requests: cpu: 500m memory: 500Mi limits: cpu: "1" memory: 1000Mi persistence: enabled: true volumeName: "data" existingClaim: "" mountPath: /bitnami/postgresql subPath: "" storageClass: nfs-client accessModes: - ReadWriteOnce size: 5Gi |
тут я настраивал:
ingressClassName
ingress
postgresql.persistence.storageClass
postgresql.persistence.size
ставим:
root@client:~/ cd /etc/ansible/kubespray-official/autentification-keycloak/

пароль Secret123
интеграция c Freeipa
для начала создадим системного пользователя во freeipa
[root@freeipa-1 ~]# cd /etc/ipa
[root@freeipa-1 ipa]# bash freeipa-sam.sh
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
### FreeIPA - System Account Manager ### 1.) ldapserver= 2.) domain= (ldapdomain=) 3.) binduser= 4.) bindpass=UNSET! 5.) ssl=true Actions (conditions not yet met): add | rm | ls | info | passwd | save --- Results --- --- End Results --- > 1 ldapserver=freeipa-1.test.local |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
### FreeIPA - System Account Manager ### 1.) ldapserver=freeipa-1.test.local 2.) domain=test.local (ldapdomain=dc=test,dc=local) 3.) binduser= 4.) bindpass=UNSET! 5.) ssl=true Actions (conditions not yet met): add | rm | ls | info | passwd | save --- Results --- --- End Results --- > 3 Enter "mgr" for Directory Manager. Otherwise enter the username or full binddn (-D option in ldapsearch) binduser=admin |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
### FreeIPA - System Account Manager ### 1.) ldapserver=freeipa-1.test.local 2.) domain=test.local (ldapdomain=dc=test,dc=local) 3.) binduser=uid=admin,cn=users,cn=accounts,dc=test,dc=local 4.) bindpass=UNSET! 5.) ssl=true Actions (conditions not yet met): add | rm | ls | info | passwd | save --- Results --- --- End Results --- > 4 Enter password (will not echo): |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
### FreeIPA - System Account Manager ### 1.) ldapserver=freeipa-1.test.local 2.) domain=test.local (ldapdomain=dc=test,dc=local) 3.) binduser=uid=admin,cn=users,cn=accounts,dc=test,dc=local 4.) bindpass=SET! 5.) ssl=true Actions (ready): add | rm | ls | info | passwd | save --- Results --- --- End Results --- > 5 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
### FreeIPA - System Account Manager ### 1.) ldapserver=freeipa-1.test.local 2.) domain=test.local (ldapdomain=dc=test,dc=local) 3.) binduser=uid=admin,cn=users,cn=accounts,dc=test,dc=local 4.) bindpass=SET! 5.) ssl=false Actions (ready): add | rm | ls | info | passwd | save --- Results --- --- End Results --- > add uid of new user=keycloak password of new user (blank to generate a password)= password expiration date YYYYMMDD (blank for 20380119)= |
создал пользователя keycloak а пароль Secret123
проверим всем пользователей:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
### FreeIPA - System Account Manager ### 1.) ldapserver=freeipa-1.test.local 2.) domain=test.local (ldapdomain=dc=test,dc=local) 3.) binduser=uid=admin,cn=users,cn=accounts,dc=test,dc=local 4.) bindpass=SET! 5.) ssl=false Actions (ready): add | rm | ls | info | passwd | save --- Results --- dn: uid=sudo,cn=sysaccounts,cn=etc,dc=test,dc=local dn: uid=nexus,cn=sysaccounts,cn=etc,dc=test,dc=local dn: uid=gitlab,cn=sysaccounts,cn=etc,dc=test,dc=local dn: uid=vault,cn=sysaccounts,cn=etc,dc=test,dc=local dn: uid=s3minio,cn=sysaccounts,cn=etc,dc=test,dc=local dn: uid=k8s-access,cn=sysaccounts,cn=etc,dc=test,dc=local dn: uid=keycloak,cn=sysaccounts,cn=etc,dc=test,dc=local --- End Results --- > |
всё готово.
Настройка федерации
Первым делом создадим новый realm. Realm — это пространство нашего приложения. Каждое приложение может иметь свой realm с разными пользователями и настройками авторизации. Master realm используется самим Keycloak и использовать его для чего-нибудь еще неправильно.
Нажимаем Add realm

Kubernetes по умолчанию проверяет подтвержден у пользователя email или нет. Так как мы используем собственный LDAP-сервер, то тут эта проверка почти всегда будет возвращать false. Давайте отключим представление этого параметра в Kubernetes:
Client scopes --> Email --> Mappers --> Email verified (Delete)

Теперь настроим федерацию, для этого перейдем в:
User federation --> Add provider… --> ldap

Приведу пример настройки для FreeIPA:
Console Display Name - freeipa-1.test.local
Edit Mode - READ_ONLY
Vendor - Red Hat Directory Server
Connection URL - ldap://freeipa-1.test.local:389
UUID LDAP attribute - ipaUniqueID
Users DN - cn=users,cn=accounts,dc=test,dc=local
Bind DN - uid=keycloak ,cn=sysaccounts,cn=etc,dc=test,dc=local
Bind Credential - Secret123
Allow Kerberos authentication - on
Kerberos Realm - TEST.LOCAL
Server Principal - HTTP/freeipa-1.test.local@TEST.LOCAL
KeyTab - /etc/krb5.keytab


теперь перейдём:
User federation --> freeipa.test.local --> Mappers --> First Name

Ldap attribure - givenName

Теперь включим маппинг групп:
User federation --> freeipa.test.local --> Mappers --> Create

Name - groups
Mapper type - group-ldap-mapper
LDAP Groups DN - cn=groups,cn=accounts,dc=test,dc=local
User Groups Retrieve Strategy - GET_GROUPS_FROM_USER_MEMBEROF_ATTRIBUTE

На этом настройка федерации закончена, перейдем к настройке клиента.
Настройка клиента
Создадим нового клиента (приложение которое будет получать пользователей из Keycloak). Переходим:
Clients --> Create

Client ID - kubernetes
Client Protocol - openid-connect
Root URL - http://kubernetes.test.local/

Access Type - confidential
Valid Redirect URIs - http://kubernetes.test.local/*
Admin URL - http://kubernetes.test.local/

Так же создадим scope для групп:
Client Scopes --> Create
Name groups

И настроим mapper для них:
Client Scopes --> groups --> Mappers --> Create
Name - groups
Mapper Type - Group membership
Token Claim Name - groups

Теперь нам нужно включить маппинг груп в нашем client scope:
Clients --> kubernetes --> Client Scopes --> Default Client Scopes

Выбираем groups в Available Client Scopes, нажимаем Add selected


Теперь настроим аутентификацию нашего приложения, переходим:
Clients --> kubernetes
Authorization Enabled - ON

Нажимем save и на этом настройка клиента завершена,
проверим что в keycloak подтягиваются группы и пользователи с Freeipa

теперь на вкладке
Clients --> kubernetes --> Credentials
вы сможете получить Secret который мы будем использовать в дальнейшем.
Настройка Kubernetes
Настройка Kubernetes для OIDC-авторизации. Все что вам нужно это положить CA-сертификат вашего OIDC-сервера в /etc/kubernetes/ssl/keycloak.crt и добавить необходимые опции для kube-apiserver.
Для этого обновите /etc/kubernetes/manifests/kube-apiserver.yaml на всех ваших мастерах:
сначала раскидаем сертификаты по всем серверам:
root@client:~/autentification-keycloak/certs# scp keycloak.crt root@192.168.1.112:/etc/kubernetes/ssl/keycloak.crt
root@client:~/autentification-keycloak/certs# scp keycloak.crt root@192.168.1.113:/etc/kubernetes/ssl/keycloak.crt
root@client:~/autentification-keycloak/certs# scp keycloak.crt root@192.168.1.114:/etc/kubernetes/ssl/keycloak.crt
root@client:~/autentification-keycloak/certs# scp keycloak.crt root@192.168.1.115:/etc/kubernetes/ssl/keycloak.crt
root@client:~/autentification-keycloak/certs# scp keycloak.crt root@192.168.1.116:/etc/kubernetes/ssl/keycloak.crt
root@client:~/autentification-keycloak/certs# scp keycloak.crt root@192.168.1.117:/etc/kubernetes/ssl/keycloak.crt
теперь на мастерах правим файл /etc/kubernetes/manifests/kube-apiserver.yaml
добавляем следующее:
|
1 2 3 4 5 6 |
- --oidc-issuer-url=https://keycloak.test.local/auth/realms/kubernetes - --oidc-client-id=kubernetes - --oidc-ca-file=/etc/kubernetes/ssl/keycloak.crt - --oidc-username-claim=email - --oidc-groups-claim=groups |
root@kub-master1:~# nano /etc/kubernetes/manifests/kube-apiserver.yaml
весь файл выглядит вот так:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
apiVersion: v1 kind: Pod metadata: annotations: kubeadm.kubernetes.io/kube-apiserver.advertise-address.endpoint: 192.168.1.112:6443 creationTimestamp: null labels: component: kube-apiserver tier: control-plane name: kube-apiserver namespace: kube-system spec: containers: - command: - kube-apiserver - --advertise-address=192.168.1.112 - --allow-privileged=true - --anonymous-auth=True - --apiserver-count=3 - --authorization-mode=Node,RBAC - --bind-address=0.0.0.0 - --client-ca-file=/etc/kubernetes/ssl/ca.crt - --default-not-ready-toleration-seconds=300 - --default-unreachable-toleration-seconds=300 - --enable-admission-plugins=NodeRestriction - --enable-aggregator-routing=False - --enable-bootstrap-token-auth=true - --endpoint-reconciler-type=lease - --etcd-cafile=/etc/ssl/etcd/ssl/ca.pem - --etcd-certfile=/etc/ssl/etcd/ssl/node-kub-master1.test.local.pem - --etcd-keyfile=/etc/ssl/etcd/ssl/node-kub-master1.test.local-key.pem - --etcd-servers=https://192.168.1.112:2379,https://192.168.1.113:2379,https://192.168.1.114:2379 - --event-ttl=1h0m0s - --kubelet-client-certificate=/etc/kubernetes/ssl/apiserver-kubelet-client.crt - --kubelet-client-key=/etc/kubernetes/ssl/apiserver-kubelet-client.key - --kubelet-preferred-address-types=InternalDNS,InternalIP,Hostname,ExternalDNS,ExternalIP - --profiling=False - --proxy-client-cert-file=/etc/kubernetes/ssl/front-proxy-client.crt - --proxy-client-key-file=/etc/kubernetes/ssl/front-proxy-client.key - --request-timeout=1m0s - --requestheader-allowed-names=front-proxy-client - --requestheader-client-ca-file=/etc/kubernetes/ssl/front-proxy-ca.crt - --requestheader-extra-headers-prefix=X-Remote-Extra- - --requestheader-group-headers=X-Remote-Group - --requestheader-username-headers=X-Remote-User - --secure-port=6443 - --service-account-issuer=https://kubernetes.default.svc.cluster.local - --service-account-key-file=/etc/kubernetes/ssl/sa.pub - --service-account-lookup=True - --service-account-signing-key-file=/etc/kubernetes/ssl/sa.key - --service-cluster-ip-range=10.233.0.0/18 - --service-node-port-range=30000-32767 - --storage-backend=etcd3 - --tls-cert-file=/etc/kubernetes/ssl/apiserver.crt - --tls-private-key-file=/etc/kubernetes/ssl/apiserver.key - --oidc-issuer-url=https://keycloak.test.local/auth/realms/kubernetes - --oidc-client-id=kubernetes - --oidc-ca-file=/etc/kubernetes/ssl/keycloak.crt - --oidc-username-claim=email - --oidc-groups-claim=groups image: registry.k8s.io/kube-apiserver:v1.24.0 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 8 httpGet: host: 192.168.1.112 path: /livez port: 6443 scheme: HTTPS initialDelaySeconds: 10 periodSeconds: 10 timeoutSeconds: 15 name: kube-apiserver readinessProbe: failureThreshold: 3 httpGet: host: 192.168.1.112 path: /readyz port: 6443 scheme: HTTPS periodSeconds: 1 timeoutSeconds: 15 resources: requests: cpu: 250m startupProbe: failureThreshold: 30 httpGet: host: 192.168.1.112 path: /livez port: 6443 scheme: HTTPS initialDelaySeconds: 10 periodSeconds: 10 timeoutSeconds: 15 volumeMounts: - mountPath: /etc/ssl/certs name: ca-certs readOnly: true - mountPath: /etc/ca-certificates name: etc-ca-certificates readOnly: true - mountPath: /etc/pki name: etc-pki readOnly: true - mountPath: /etc/ssl/etcd/ssl name: etcd-certs-0 readOnly: true - mountPath: /etc/kubernetes/ssl name: k8s-certs readOnly: true - mountPath: /usr/local/share/ca-certificates name: usr-local-share-ca-certificates readOnly: true - mountPath: /usr/share/ca-certificates name: usr-share-ca-certificates readOnly: true hostNetwork: true priorityClassName: system-node-critical securityContext: seccompProfile: type: RuntimeDefault volumes: - hostPath: path: /etc/ssl/certs type: DirectoryOrCreate name: ca-certs - hostPath: path: /etc/ca-certificates type: DirectoryOrCreate name: etc-ca-certificates - hostPath: path: /etc/pki type: DirectoryOrCreate name: etc-pki - hostPath: path: /etc/ssl/etcd/ssl type: DirectoryOrCreate name: etcd-certs-0 - hostPath: path: /etc/kubernetes/ssl type: DirectoryOrCreate name: k8s-certs - hostPath: path: /usr/local/share/ca-certificates type: DirectoryOrCreate name: usr-local-share-ca-certificates - hostPath: path: /usr/share/ca-certificates type: "" name: usr-share-ca-certificates status: {} |
root@kub-master2:~# nano /etc/kubernetes/manifests/kube-apiserver.yaml
root@kub-master3:~# nano /etc/kubernetes/manifests/kube-apiserver.yaml
А так-же поправим kubeadm конфиг в кластере, что бы не потерять эти настройки при обновлении:
root@client:~# kubectl edit -n kube-system configmaps kubeadm-config
|
1 2 3 4 5 6 7 8 9 10 11 |
... data: ClusterConfiguration: | apiServer: extraArgs: oidc-ca-file: /etc/kubernetes/ssl/keycloak.crt oidc-client-id: kubernetes oidc-groups-claim: groups oidc-issuer-url: https://keycloak.test.local/auth/realms/kubernetes oidc-username-claim: email ... |
вот так выглядит весь конфиг:
root@client:~# kubectl get -n kube-system configmaps kubeadm-config -o yaml
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
apiVersion: v1 data: ClusterConfiguration: | apiServer: certSANs: - kubernetes - kubernetes.default - kubernetes.default.svc - kubernetes.default.svc.cluster.local - 10.233.0.1 - localhost - 127.0.0.1 - kub-master1.test.local - kub-master2.test.local - kub-master3.test.local - lb-apiserver.kubernetes.local - 192.168.1.112 - 192.168.1.113 - 192.168.1.114 - kub-master1 - kub-master2 - kub-master3 extraArgs: allow-privileged: "true" anonymous-auth: "True" apiserver-count: "3" authorization-mode: Node,RBAC bind-address: 0.0.0.0 default-not-ready-toleration-seconds: "300" default-unreachable-toleration-seconds: "300" enable-aggregator-routing: "False" endpoint-reconciler-type: lease event-ttl: 1h0m0s kubelet-preferred-address-types: InternalDNS,InternalIP,Hostname,ExternalDNS,ExternalIP profiling: "False" request-timeout: 1m0s service-account-lookup: "True" service-cluster-ip-range: 10.233.0.0/18 service-node-port-range: 30000-32767 storage-backend: etcd3 oidc-ca-file: /etc/kubernetes/ssl/keycloak.crt oidc-client-id: kubernetes oidc-groups-claim: groups oidc-issuer-url: https://keycloak.test.local/auth/realms/kubernetes oidc-username-claim: email extraVolumes: - hostPath: /usr/share/ca-certificates mountPath: /usr/share/ca-certificates name: usr-share-ca-certificates readOnly: true timeoutForControlPlane: 5m0s apiVersion: kubeadm.k8s.io/v1beta3 certificatesDir: /etc/kubernetes/ssl clusterName: cluster.local controlPlaneEndpoint: 192.168.1.112:6443 controllerManager: extraArgs: bind-address: 0.0.0.0 cluster-cidr: 10.233.64.0/18 configure-cloud-routes: "false" leader-elect-lease-duration: 15s leader-elect-renew-deadline: 10s node-cidr-mask-size: "24" node-monitor-grace-period: 40s node-monitor-period: 5s profiling: "False" service-cluster-ip-range: 10.233.0.0/18 terminated-pod-gc-threshold: "12500" dns: imageRepository: registry.k8s.io/coredns imageTag: v1.8.6 etcd: external: caFile: /etc/ssl/etcd/ssl/ca.pem certFile: /etc/ssl/etcd/ssl/node-kub-master1.test.local.pem endpoints: - https://192.168.1.112:2379 - https://192.168.1.113:2379 - https://192.168.1.114:2379 keyFile: /etc/ssl/etcd/ssl/node-kub-master1.test.local-key.pem imageRepository: registry.k8s.io kind: ClusterConfiguration kubernetesVersion: v1.24.0 networking: dnsDomain: cluster.local podSubnet: 10.233.64.0/18 serviceSubnet: 10.233.0.0/18 scheduler: extraArgs: bind-address: 0.0.0.0 config: /etc/kubernetes/kubescheduler-config.yaml extraVolumes: - hostPath: /etc/kubernetes/kubescheduler-config.yaml mountPath: /etc/kubernetes/kubescheduler-config.yaml name: kubescheduler-config readOnly: true kind: ConfigMap metadata: creationTimestamp: "2024-06-30T14:22:29Z" name: kubeadm-config namespace: kube-system resourceVersion: "3343644" uid: 8f3983c4-d932-4a02-8928-fd084b04b401 |
Teleport
Teleport – это инструмент для безопасного подключения к Kubernetes-кластерам, а также к другому ПО . Помимо Kubernetes, Teleport можно использовать для аутентификации с такими системами как:
- Облачные провайдеры – Amazon, Google Cloud, Microsoft Azure;
- Операционные системы – Windows, Linux;
- СУБД – Redis, CockroachDB, MongoDB;
- Системы для поиска и анализа данных – Elasticsearch
достоинства Teleport:
Способы подключения
Teleport позволяет удаленно подключаться к Kubernetes-кластерам с использованием протоколов SSH или TLS. Также присутствует встроенный веб-интерфейс.
Аудит
Teleport отслеживает все действия, которые пользователь совершает внутри системы (кластера), и предоставляет расширенные функции, такие как фильтры, хронология, уведомления, управление учетными записями и т. д.
Аутентификация
Teleport использует многофакторную аутентификацию, чтобы удостоверить личность пользователя.
Управление учетными записями
Teleport может управлять учетными записями для того, чтобы проверить, что пользователь имеет доступ только к разрешенным ресурсам.
Архитектура и принцип работы Teleport
Teleport написан на языке Go и состоит из трех независимых исполняемых файлов:
- tsh (клиент командной строки);
- tctl (инструмент администрирования);
- teleport (серверный демон).
Представляет собой прокси-сервер, предназначенный для доступа и аутентификации к требуемым системам (операционным системам, СУБД и т. д.). Управление доступом реализовано на основе ролей RBAC.
Клиент командной строки tsh предназначен для входа на конечные ресурсы и выполнения команд.
Инструмент администрирования tctl используется для создания пользователей, ключей сертификатов, а также может применяться для изменения динамической конфигурации кластеров Kubernetes, например, для создания новых ролей.
Демон сервера teleport может работать в трех режимах:
- Node. В этом режиме демон предоставляет SSH и Kubernetes доступ к серверу, на котором он работает;
- Прокси-сервер. В этом режиме демон действует как удостоверяющий личность прокси для всех протоколов, поддерживаемых Teleport (SSH, HTTPS, Kubernetes API);
- Сервер аутентификации. В этом режиме демон действует как центр сертификации, который выдает сертификаты для пользователей. Также хранит журнал аудита.

работает Teleport следующим образом:
- Пользователь выбирает один из нескольких способов подключения к кластеру Kubernetes (например tsh);
- Далее запрос переходит к серверному демону teleport, который, в свою очередь, отправляет запрос Identity Provider – системе, предназначенной для создания и хранения цифровых идентификационных данных (логин, пароль и т. д). В качестве провайдеров Teleport поддерживает следующие системы:
- Azure Active Directory;
- Active Directory;
- Google Workspace;
- GitHub;
- GitLab;
- OneLogin;
- OIDC;
- SSO
- После того как в Identity Provider найдены аутентификационные данные пользователя, запрос возвращается демону teleport, который обрабатывает поступивший запрос и разрешает доступ к кластеру Kubernetes или другой конечной системе.
установка teleport
офф документация
https://goteleport.com/docs/admin-guides/deploy-a-cluster/helm-deployments/custom/
вот мой values
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
clusterName: teleport.test.local proxyListenerMode: multiplex proxyProtocol: "off" log: level: DEBUG teleport: dataDir: /var/lib/teleport authService: enabled: true storage: type: dir proxyService: enabled: true publicAddr: teleport.test.local:443 web_listen_addr: 0.0.0.0:443 # монтируем CA, чтобы Teleport доверял своему же TLS-сертификату extraVolumes: - name: teleport-ca secret: secretName: teleport-ca-cert extraVolumeMounts: - name: teleport-ca mountPath: /etc/ssl/certs readOnly: true env: - name: SSL_CERT_FILE value: /etc/ssl/certs/ca.pem kubeService: enabled: true kubeClusterName: "cluster.local" sshService: enabled: false service: type: ClusterIP persistence: enabled: true accessModes: - ReadWriteMany size: 2Gi storageClassName: nfs-client ingress: enabled: true spec: ingressClassName: nginx rules: - host: teleport.test.local http: paths: - path: / pathType: Prefix backend: service: name: teleport-cluster port: number: 443 # Глобальные аннотации (chart автоматически подхватит annotations.ingress для metadata.ingress.annotations) annotations: ingress: nginx.ingress.kubernetes.io/backend-protocol: "HTTPS" # Секрет с TLS-сертификатом для Teleport proxy (chart смонтирует его в /etc/teleport/certs) tls: existingSecretName: teleport-tls existingCASecretName: teleport-ca-cert auth: tls: enabled: true certFile: /etc/teleport/certs/tls.crt keyFile: /etc/teleport/certs/tls.key caFile: /etc/teleport/certs/ca.crt extraVolumes: - name: teleport-tls secret: secretName: teleport-tls extraVolumeMounts: - name: teleport-tls mountPath: /etc/teleport/certs readOnly: true |
домен
teleport.test.local
|
1 2 3 4 5 6 7 8 |
[ v3_ca ] subjectAltName = @alt_names [ alt_names ] DNS.1 = teleport.test.local DNS.2 = *.teleport.test.local |
cat > teleport_openssl.cnf
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
[ req ] default_bits = 4096 prompt = no default_md = sha256 distinguished_name = dn req_extensions = req_ext [ dn ] C = RU ST = YourState L = YourCity O = YourOrganization CN = teleport.test.local [ req_ext ] subjectAltName = @alt_names [ alt_names ] DNS.1 = teleport.test.local DNS.2 = *.teleport.test.local |
создаём секрет в k8s
|
1 2 3 4 5 |
kubectl create secret generic teleport-tls \ --from-file=tls.crt=./teleport.crt \ --from-file=tls.key=./teleport.key \ --from-file=ca.crt=./ca.crt \ -n teleport |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
root@client:~/autentification-teleport# kubectl get ingress -n teleport NAME CLASS HOSTS ADDRESS PORTS AGE teleport-cluster-proxy nginx teleport.test.local,*.teleport.test.local 192.168.1.191 80, 443 6d19h root@client:~/autentification-teleport# kubectl get pod -n teleport NAME READY STATUS RESTARTS AGE teleport-cluster-auth-f45664b6b-kp5s8 1/1 Running 2 (151m ago) 6d19h teleport-cluster-proxy-f856f76d9-9gdgg 1/1 Running 1 (151m ago) 6d4h teleport-cluster-proxy-f856f76d9-xbgjj 1/1 Running 1 (151m ago) 6d4h root@client:~/autentification-teleport# kubectl get ingress -n teleport NAME CLASS HOSTS ADDRESS PORTS AGE teleport-cluster-proxy nginx teleport.test.local,*.teleport.test.local 192.168.1.191 80, 443 6d19h root@client:~/autentification-teleport# kubectl get pvc -n teleport NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE teleport-cluster Bound pvc-f3a8dadb-7a60-4009-b8de-2c5cad5c932b 10Gi RWO nfs-client 6d19h |
заходим
https://teleport.test.local/

создаём админа
kubectl exec -it -n teleport deploy/teleport-cluster-auth -- tctl users add admin --roles=editor,auditor,access
|
1 2 3 4 5 |
root@client:~/autentification-teleport# kubectl exec -it -n teleport deploy/teleport-cluster-auth -- tctl users add admin --roles=editor,auditor,access User "admin" has been created but requires a password. Share this URL with the user to complete user setup, link is valid for 1h: https://teleport.test.local:443/web/invite/01ea692b034d44afb0ea6e61a6c6b3a5 NOTE: Make sure teleport.test.local:443 points at a Teleport proxy which users can access. |
получаем токен, по которому заходим
https://teleport.test.local:443/web/invite/01ea692b034d44afb0ea6e61a6c6b3a5

пароль минимум 12 символов





Подключение кластера Kubernetes к Teleport
Для того чтобы использовать Teleport для входа в кластер Kubernetes, необходимо установить агент. Для начала проверим, работает ли аутентификация в Teleport при помощи следующей команды:

|
1 2 3 4 5 6 7 8 9 10 11 |
user1@client:~$ tsh login --proxy=teleport.test.local:443 --auth=local --user=admin teleport.test.local Enter password for Teleport user admin: Enter an OTP code from a device: > Profile URL: https://teleport.test.local:443 Logged in as: admin Cluster: teleport.test.local Roles: access, auditor, editor Kubernetes: enabled Valid until: 2025-05-03 23:46:47 +0600 +06 [valid for 12h0m0s] Extensions: login-ip, permit-agent-forwarding, permit-port-forwarding, permit-pty, private-key-policy |
нужно будет ввести пароль от нашего админа и OTP код с мобилы.
чтобы был доступ к кластеру нужно добавить прав,
Создадим роль k8s-admin

в группу добавим
system:masters


роль создана:


|
1 2 3 4 5 6 7 8 9 |
user1@client:~$ tsh login --proxy=teleport.test.local:443 --auth=local --user=admin teleport.test.local > Profile URL: https://teleport.test.local:443 Logged in as: admin Cluster: teleport.test.local Roles: access, auditor, editor Kubernetes: enabled Valid until: 2025-05-03 23:46:47 +0600 +06 [valid for 10h44m0s] Extensions: login-ip, permit-agent-forwarding, permit-port-forwarding, permit-pty, private-key-policy |
как видим при обычном логине Roles остаются старыми поэтому делаем логаут
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
user1@client:~$ tsh logout Logged out all users from all proxies. user1@client:~$ tsh login --proxy=teleport.test.local:443 --auth=local --user=admin teleport.test.local Enter password for Teleport user admin: Enter an OTP code from a device: > Profile URL: https://teleport.test.local:443 Logged in as: admin Cluster: teleport.test.local Roles: access, auditor, editor, k8s-admin Kubernetes: enabled Kubernetes users: admin Valid until: 2025-05-04 01:04:20 +0600 +06 [valid for 12h0m0s] Extensions: login-ip, permit-agent-forwarding, permit-port-forwarding, permit-pty, private-key-policy |
всё, теперь как видим в наших ролях есть k8s-admin

|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
user1@client:~$ tsh kube login teleport.test.local Logged into Kubernetes cluster "teleport.test.local". Your Teleport cluster runs behind a layer 7 load balancer or reverse proxy. To access the cluster, use "tsh kubectl" which is a fully featured "kubectl" command that works when the Teleport cluster is behind layer 7 load balancer or reverse proxy. To run the Kubernetes client, use: tsh kubectl version Or, start a local proxy with "tsh proxy kube" and use the kubeconfig provided by the local proxy with your native Kubernetes clients: tsh proxy kube -p 8443 |
это говорит о том что так как мы используем ingress т.е. 7 уровень модели osi (application layer - прикладной уровень)
использовать напрямую утилиту kubectl будет невозможно, поэтому есть 2 варианта 1 это добавлять tsh перед kubectl или запустить в одной консоли
tsh proxy kube -p 8443
а в другой использовать kubectl.
проверим:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
user1@client:~$ tsh kubectl get ns NAME STATUS AGE cattle-system Active 19d cattle-ui-plugin-system Active 42d cert-manager Active 300d cluster-fleet-local-local-1a3d67d0a899 Active 47d default Active 306d dev Active 161d dex Active 145d elk Active 294d fleet-default Active 300d fleet-local Active 300d ingress-nginx Active 19d keycloak Active 118d kube-node-lease Active 306d kube-public Active 306d kube-system Active 306d local Active 300d loki Active 166d metallb-system Active 301d monitoring Active 299d prod Active 162d teleport Active 19d test Active 163d vault Active 165d |
как видим работает.
Проверим ограниченный доступ только к одному из namespace например это будет пользователь user2
создадим пользователя
user2

https://teleport.test.local/web/invite/70e8b53c1468f5ed82efa35480dc13ef
получив ссылку проходим по ней:

привязываемся по OTP




|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: dev-admin-binding namespace: dev roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: admin subjects: - kind: Group name: dev-admins # ← Должно точно совпадать с тем, что указано в Teleport (kubernetes_groups) apiGroup: rbac.authorization.k8s.io |
root@client:~# kubectl apply -f dev-admins.yaml

|
1 2 3 4 5 6 7 8 9 10 11 12 |
user2@client:~$ tsh login --proxy=teleport.test.local:443 --auth=local --user=user2 teleport.test.local Enter password for Teleport user user2: Enter an OTP code from a device: > Profile URL: https://teleport.test.local:443 Logged in as: user2 Cluster: teleport.test.local Roles: access, auditor, dev Kubernetes: enabled Kubernetes groups: dev-admins Valid until: 2025-05-05 04:49:15 +0600 +06 [valid for 12h0m0s] Extensions: login-ip, permit-agent-forwarding, permit-port-forwarding, permit-pty, private-key-policy |
user2@client:~$ export KUBECONFIG=${HOME?}/teleport-kubeconfig.yaml
user2@client:~$ tsh kube login teleport.test.local
получаем уведомление:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
user2@client:~$ tsh kube login teleport.test.local Logged into Kubernetes cluster "teleport.test.local". Your Teleport cluster runs behind a layer 7 load balancer or reverse proxy. To access the cluster, use "tsh kubectl" which is a fully featured "kubectl" command that works when the Teleport cluster is behind layer 7 load balancer or reverse proxy. To run the Kubernetes client, use: tsh kubectl version Or, start a local proxy with "tsh proxy kube" and use the kubeconfig provided by the local proxy with your native Kubernetes clients: tsh proxy kube -p 8443 Learn more at https://goteleport.com/docs/architecture/tls-routing/#working-with-layer-7-load-balancers-or-reverse-proxies-preview |
вот результат запросов:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
user2@client:~$ tsh kubectl get pod -n dev NAME READY STATUS RESTARTS AGE first-app-common-chart-6cdf48bf88-kfwc2 1/1 Running 6 (26h ago) 20d second-app-common-chart-6b8457fbfb-fqm8w 1/1 Running 13 (26h ago) 92d user2@client:~$ tsh kubectl get pod -n prod Error from server (Forbidden): pods is forbidden: User "user2" cannot list resource "pods" in API group "" in the namespace "prod" user2@client:~$ tsh kubectl get ns Error from server (Forbidden): namespaces is forbidden: User "user2" cannot list resource "namespaces" in API group "" at the cluster scope |
как видим в dev всё ок, а посмотреть в другом namespace или отобразить все ns не возможно
Интеграция teleport - keycloak
Обновление кластера k8s
ранее я использовал:
https://github.com/kubernetes-incubator/kubespray.git
сейчас он переехал:
https://github.com/kubernetes-sigs/kubespray
обновление 1,24 на 1,25
чтобы обновиться нужная новая версия ansible
|
1 2 3 4 5 6 7 8 9 10 |
ansible-galaxy collection install \ community.general \ community.kubernetes \ kubernetes.core \ amazon.aws \ azure.azcollection \ ansible.posix \ ansible.utils \ community.crypto \ ansible.netcommon |
|
1 2 3 4 5 6 7 8 9 10 |
--- required_pkgs: - python3-apt #- aufs-tools - apt-transport-https - software-properties-common - conntrack - apparmor - libseccomp2 |
/etc/ansible/kubespray-official/kubespray-new/kubespray/inventory/sample/inventory.ini
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
[all] kub-master1.test.local ansible_host=192.168.1.112 ip=192.168.1.112 kub-master2.test.local ansible_host=192.168.1.113 ip=192.168.1.113 kub-master3.test.local ansible_host=192.168.1.114 ip=192.168.1.114 kub-worker1.test.local ansible_host=192.168.1.115 ip=192.168.1.115 kub-worker2.test.local ansible_host=192.168.1.116 ip=192.168.1.116 kub-worker3.test.local ansible_host=192.168.1.117 ip=192.168.1.117 # ## configure a bastion host if your nodes are not directly reachable # [bastion] # bastion ansible_host=x.x.x.x ansible_user=some_user [kube_control_plane] kub-master1.test.local kub-master2.test.local kub-master3.test.local [etcd] kub-master1.test.local kub-master2.test.local kub-master3.test.local [kube_node] kub-worker1.test.local kub-worker2.test.local kub-worker3.test.local [calico_rr] [k8s_cluster:children] kube_control_plane kube_node calico_rr |
/etc/ansible/kubespray-official/kubespray-new/kubespray/inventory/sample/group_vars/k8s_cluster/k8s-cluster.yml
|
1 2 3 4 |
kube_oidc_auth: true kube_token_auth: true kube_proxy_strict_arp: true |
/etc/ansible/kubespray-official/kubespray-new/kubespray/inventory/sample/group_vars/k8s_cluster/addons.yml
|
1 2 3 4 |
helm_enabled: true local_path_provisioner_enabled: true |
/etc/ansible/kubespray-official/kubespray-new/kubespray/inventory/sample/group_vars/all/all.yml
|
1 2 3 |
upstream_dns_servers: - 8.8.8.8 - 8.8.4.4 |
и запускаем установку:
(.venv-ansible) root@ansible:/etc/ansible/kubespray-official/kubespray-new/kubespray# ansible-playbook -i inventory/sample/inventory.ini upgrade-cluster.yml -b --become-user=root --ask-pass
|
1 2 3 4 5 6 7 8 |
root@kub-master1:~# kubectl get nodes NAME STATUS ROLES AGE VERSION kub-master1.test.local Ready control-plane 25h v1.24.6 kub-master2.test.local Ready control-plane 25h v1.24.6 kub-master3.test.local Ready control-plane 25h v1.24.6 kub-worker1.test.local Ready <none> 25h v1.24.6 kub-worker2.test.local Ready <none> 25h v1.24.6 kub-worker3.test.local Ready <none> 25h v1.24.6 |
как видим обновились до v1.24.6
обновляемся дальше
(.venv-ansible) root@ansible:/etc/ansible/kubespray-official/kubespray-new/kubespray# deactivate
root@ansible:/etc/ansible/kubespray-official/kubespray-new/kubespray# rm -rf .venv-ansible/
root@ansible:/etc/ansible/kubespray-official/kubespray-new/kubespray# git add .
root@ansible:/etc/ansible/kubespray-official/kubespray-new/kubespray# git commit -m "20"
root@ansible:/etc/ansible/kubespray-official/kubespray-new/kubespray# git checkout release-2.21
нужно сейчас так же поправить инвентори и будем обновлять дальше на v1.25.6
/etc/ansible/kubespray-official/kubespray-new/kubespray/inventory/sample/group_vars/k8s_cluster/k8s-cluster.yml
|
1 2 3 4 |
kube_oidc_auth: true kube_token_auth: true kube_proxy_strict_arp: true |
/etc/ansible/kubespray-official/kubespray-new/kubespray/inventory/sample/group_vars/k8s_cluster/addons.yml
|
1 2 3 |
helm_enabled: true local_volume_provisioner_enabled: true |
/etc/ansible/kubespray-official/kubespray-new/kubespray/inventory/sample/group_vars/all/all.yml
|
1 2 3 |
upstream_dns_servers: - 8.8.8.8 - 8.8.4.4 |
/etc/ansible/kubespray-official/kubespray-new/kubespray/inventory/sample/inventory.ini
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
[all] kub-master1.test.local ansible_host=192.168.1.112 ip=192.168.1.112 kub-master2.test.local ansible_host=192.168.1.113 ip=192.168.1.113 kub-master3.test.local ansible_host=192.168.1.114 ip=192.168.1.114 kub-worker1.test.local ansible_host=192.168.1.115 ip=192.168.1.115 kub-worker2.test.local ansible_host=192.168.1.116 ip=192.168.1.116 kub-worker3.test.local ansible_host=192.168.1.117 ip=192.168.1.117 # ## configure a bastion host if your nodes are not directly reachable # [bastion] # bastion ansible_host=x.x.x.x ansible_user=some_user [kube_control_plane] kub-master1.test.local kub-master2.test.local kub-master3.test.local [etcd] kub-master1.test.local kub-master2.test.local kub-master3.test.local [kube_node] kub-worker1.test.local kub-worker2.test.local kub-worker3.test.local [calico_rr] [k8s_cluster:children] kube_control_plane kube_node calico_rr |
|
1 2 3 4 5 6 7 8 9 10 |
--- required_pkgs: - python3-apt #- aufs-tools - apt-transport-https - software-properties-common - conntrack - apparmor - libseccomp2 |
|
1 2 3 4 5 6 7 8 9 10 |
ansible-galaxy collection install \ community.general \ community.kubernetes \ kubernetes.core \ amazon.aws \ azure.azcollection \ ansible.posix \ ansible.utils \ community.crypto \ ansible.netcommon |
mkdir -p filter_plugins
обновляемся дальше до 1,26
root@ansible:/etc/ansible/kubespray-official/kubespray-new/kubespray# git add .
root@ansible:/etc/ansible/kubespray-official/kubespray-new/kubespray# source .venv-ansible/bin/activate
(.venv-ansible) root@ansible:/etc/ansible/kubespray-official/kubespray-new/kubespray# pip install --upgrade pip setuptools wheel
|
1 2 3 4 5 6 7 8 9 10 |
ansible-galaxy collection install \ community.general \ community.kubernetes \ kubernetes.core \ amazon.aws \ azure.azcollection \ ansible.posix \ ansible.utils \ community.crypto \ ansible.netcommon |
mkdir -p filter_plugins
|
1 2 3 4 5 6 7 8 |
root@kub-master1:~# kubectl get nodes NAME STATUS ROLES AGE VERSION kub-master1.test.local Ready control-plane 2d v1.26.11 kub-master2.test.local Ready control-plane 2d v1.26.11 kub-master3.test.local Ready control-plane 2d v1.26.11 kub-worker1.test.local Ready <none> 2d v1.26.11 kub-worker2.test.local Ready <none> 2d v1.26.11 kub-worker3.test.local Ready <none> 2d v1.26.11 |
обновляем до 1,27
(.venv-ansible) root@ansible:/etc/ansible/kubespray-official/kubespray-new/kubespray# deactivate
root@ansible:/etc/ansible/kubespray-official/kubespray-new/kubespray# rm -rf .venv-ansible/
root@ansible:/etc/ansible/kubespray-official/kubespray-new/kubespray# git add .
root@ansible:/etc/ansible/kubespray-official/kubespray-new/kubespray# git branch
master
release-2.20
release-2.21
* release-2.22
root@ansible:/etc/ansible/kubespray-official/kubespray-new/kubespray# git commit -m "22"
root@ansible:/etc/ansible/kubespray-official/kubespray-new/kubespray# git checkout release-2.23
правим все те же файлы - заполняем inventory, но в версии release-2.23 есть отличие, там появился playbook для debian 12
/etc/ansible/kubespray-official/kubespray-new/kubespray/roles/kubernetes/preinstall/vars/debian-12.yml
поэтому можно не править файл debian.yaml
root@ansible:/etc/ansible/kubespray-official/kubespray-new/kubespray# source .venv-ansible/bin/activate
(.venv-ansible) root@ansible:/etc/ansible/kubespray-official/kubespray-new/kubespray# pip install --upgrade pip setuptools wheel
|
1 2 3 4 5 6 7 8 9 10 |
ansible-galaxy collection install \ community.general \ community.kubernetes \ kubernetes.core \ amazon.aws \ azure.azcollection \ ansible.posix \ ansible.utils \ community.crypto \ ansible.netcommon |
mkdir -p filter_plugins
запускаем upgrade
(.venv-ansible) root@ansible:/etc/ansible/kubespray-official/kubespray-new/kubespray# ansible-playbook -i inventory/sample/inventory.ini upgrade-cluster.yml -b --become-user=root --ask-pass
|
1 2 3 4 5 6 7 8 |
root@kub-master2:~# kubectl get nodes NAME STATUS ROLES AGE VERSION kub-master1.test.local Ready control-plane 2d22h v1.27.10 kub-master2.test.local Ready control-plane 2d22h v1.27.10 kub-master3.test.local Ready control-plane 2d22h v1.27.10 kub-worker1.test.local Ready <none> 2d22h v1.27.10 kub-worker2.test.local Ready <none> 2d22h v1.27.10 kub-worker3.test.local Ready <none> 2d22h v1.27.10 |
обновляем до 1,28
root@ansible:/etc/ansible/kubespray-official/kubespray-new/kubespray# rm -rf .venv-ansible/
root@ansible:/etc/ansible/kubespray-official/kubespray-new/kubespray# git add .
root@ansible:/etc/ansible/kubespray-official/kubespray-new/kubespray# git commit -m "23"
root@ansible:/etc/ansible/kubespray-official/kubespray-new/kubespray# git checkout release-2.24
/etc/ansible/kubespray-official/kubespray-new/kubespray/inventory/sample/group_vars/all/all.yml
/etc/ansible/kubespray-official/kubespray-new/kubespray/inventory/sample/group_vars/k8s_cluster/addons.yml
/etc/ansible/kubespray-official/kubespray-new/kubespray/inventory/sample/group_vars/k8s_cluster/k8s-cluster.yml
root@ansible:/etc/ansible/kubespray-official/kubespray-new/kubespray# source .venv-ansible/bin/activate
(.venv-ansible) root@ansible:/etc/ansible/kubespray-official/kubespray-new/kubespray# pip install --upgrade pip setuptools wheel
|
1 2 3 4 5 6 7 8 9 10 |
ansible-galaxy collection install \ community.general \ community.kubernetes \ kubernetes.core \ amazon.aws \ azure.azcollection \ ansible.posix \ ansible.utils \ community.crypto \ ansible.netcommon |
mkdir -p filter_plugins
запускаем upgrade
(.venv-ansible) root@ansible:/etc/ansible/kubespray-official/kubespray-new/kubespray# ansible-playbook -i inventory/sample/inventory.ini upgrade-cluster.yml -b --become-user=root --ask-pass
|
1 2 3 4 5 6 7 8 |
root@kub-master1:~# kubectl get nodes NAME STATUS ROLES AGE VERSION kub-master1.test.local Ready control-plane 3d6h v1.28.10 kub-master2.test.local Ready control-plane 3d6h v1.28.10 kub-master3.test.local Ready control-plane 3d6h v1.28.10 kub-worker1.test.local Ready <none> 3d6h v1.28.10 kub-worker2.test.local Ready <none> 3d6h v1.28.10 kub-worker3.test.local Ready <none> 3d6h v1.28.10 |
обновляем до 1,29
(.venv-ansible) root@ansible:/etc/ansible/kubespray-official/kubespray-new/kubespray# deactivate
rm -rf .venv-ansible/
git add .
git commit -m "24"
git checkout release-2.25
python3 -m venv .venv-ansible
source .venv-ansible/bin/activate
pip install --upgrade pip setuptools wheel
sed -i '/ruamel.yaml.clib/d' requirements.txt
pip install -r requirements.txt
|
1 2 3 4 5 6 7 8 9 10 |
ansible-galaxy collection install \ community.general \ community.kubernetes \ kubernetes.core \ amazon.aws \ azure.azcollection \ ansible.posix \ ansible.utils \ community.crypto \ ansible.netcommon |
mkdir -p filter_plugins
/etc/ansible/kubespray-official/kubespray-new/kubespray/inventory/sample/group_vars/all/all.yml
/etc/ansible/kubespray-official/kubespray-new/kubespray/inventory/sample/group_vars/k8s_cluster/addons.yml
/etc/ansible/kubespray-official/kubespray-new/kubespray/inventory/sample/group_vars/k8s_cluster/k8s-cluster.yml
|
1 2 3 4 5 6 7 8 |
root@kub-master1:~# kubectl get nodes NAME STATUS ROLES AGE VERSION kub-master1.test.local Ready control-plane 3d7h v1.29.10 kub-master2.test.local Ready control-plane 3d7h v1.29.10 kub-master3.test.local Ready control-plane 3d7h v1.29.10 kub-worker1.test.local Ready <none> 3d7h v1.29.10 kub-worker2.test.local Ready <none> 3d7h v1.29.10 kub-worker3.test.local Ready <none> 3d7h v1.29.10 |
обновляем до 1,30
deactivate
rm -rf .venv-ansible/
git add .
git commit -m "24"
git checkout release-2.26
python3 -m venv .venv-ansible
source .venv-ansible/bin/activate
pip install --upgrade pip setuptools wheel
sed -i '/ruamel.yaml.clib/d' requirements.txt
pip install -r requirements.txt
|
1 2 3 4 5 6 7 8 9 10 |
ansible-galaxy collection install \ community.general \ community.kubernetes \ kubernetes.core \ amazon.aws \ azure.azcollection \ ansible.posix \ ansible.utils \ community.crypto \ ansible.netcommon |
mkdir -p filter_plugins
/etc/ansible/kubespray-official/kubespray-new/kubespray/inventory/sample/group_vars/all/all.yml
/etc/ansible/kubespray-official/kubespray-new/kubespray/inventory/sample/group_vars/k8s_cluster/addons.yml
/etc/ansible/kubespray-official/kubespray-new/kubespray/inventory/sample/group_vars/k8s_cluster/k8s-cluster.yml
адейт прошёл не совсем по плану:
|
1 2 3 4 5 6 7 8 |
root@kub-master1:~# kubectl get nodes NAME STATUS ROLES AGE VERSION kub-master1.test.local Ready control-plane 3d21h v1.30.4 kub-master2.test.local Ready,SchedulingDisabled control-plane 3d21h v1.30.4 kub-master3.test.local Ready control-plane 3d21h v1.30.4 kub-worker1.test.local Ready <none> 3d21h v1.30.4 kub-worker2.test.local Ready <none> 3d21h v1.30.4 kub-worker3.test.local Ready <none> 3d21h v1.30.4 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
root@kub-master1:~# kubectl describe node kub-master2.test.local Name: kub-master2.test.local Roles: control-plane Labels: beta.kubernetes.io/arch=amd64 beta.kubernetes.io/os=linux kubernetes.io/arch=amd64 kubernetes.io/hostname=kub-master2.test.local kubernetes.io/os=linux node-role.kubernetes.io/control-plane= node.kubernetes.io/exclude-from-external-load-balancers= Annotations: kubeadm.alpha.kubernetes.io/cri-socket: unix:///var/run/containerd/containerd.sock node.alpha.kubernetes.io/ttl: 0 projectcalico.org/IPv4Address: 192.168.1.113/24 projectcalico.org/IPv4VXLANTunnelAddr: 10.233.122.192 volumes.kubernetes.io/controller-managed-attach-detach: true CreationTimestamp: Wed, 21 May 2025 12:53:37 +0600 Taints: node-role.kubernetes.io/control-plane:NoSchedule node.kubernetes.io/unschedulable:NoSchedule Unschedulable: true Lease: HolderIdentity: kub-master2.test.local AcquireTime: <unset> RenewTime: Sun, 25 May 2025 10:21:29 +0600 Conditions: Type Status LastHeartbeatTime LastTransitionTime Reason Message ---- ------ ----------------- ------------------ ------ ------- NetworkUnavailable False Sat, 24 May 2025 23:21:24 +0600 Sat, 24 May 2025 23:21:24 +0600 CalicoIsUp Calico is running on this node MemoryPressure False Sun, 25 May 2025 10:21:29 +0600 Sat, 24 May 2025 21:40:58 +0600 KubeletHasSufficientMemory kubelet has sufficient memory available DiskPressure False Sun, 25 May 2025 10:21:29 +0600 Sat, 24 May 2025 21:40:58 +0600 KubeletHasNoDiskPressure kubelet has no disk pressure PIDPressure False Sun, 25 May 2025 10:21:29 +0600 Sat, 24 May 2025 21:40:58 +0600 KubeletHasSufficientPID kubelet has sufficient PID available Ready True Sun, 25 May 2025 10:21:29 +0600 Sat, 24 May 2025 23:21:04 +0600 KubeletReady kubelet is posting ready status Addresses: InternalIP: 192.168.1.113 Hostname: kub-master2.test.local Capacity: cpu: 4 ephemeral-storage: 69549756Ki hugepages-2Mi: 0 memory: 3978024Ki pods: 110 Allocatable: cpu: 3800m ephemeral-storage: 64097055024 hugepages-2Mi: 0 memory: 3351336Ki pods: 110 System Info: Machine ID: 10e2ab9ef8014ab8b59603f47fdc2db7 System UUID: c68dffeb-951c-c943-a81c-0471fa140e09 Boot ID: b74446fc-ce4e-48b3-9e0d-e6f5900aee44 Kernel Version: 6.1.0-35-amd64 OS Image: Debian GNU/Linux 12 (bookworm) Operating System: linux Architecture: amd64 Container Runtime Version: containerd://1.7.23 Kubelet Version: v1.30.4 Kube-Proxy Version: v1.30.4 PodCIDR: 10.233.65.0/24 PodCIDRs: 10.233.65.0/24 Non-terminated Pods: (6 in total) Namespace Name CPU Requests CPU Limits Memory Requests Memory Limits Age --------- ---- ------------ ---------- --------------- ------------- --- kube-system calico-node-m5kvp 150m (3%) 300m (7%) 64M (1%) 500M (14%) 12h kube-system kube-apiserver-kub-master2.test.local 250m (6%) 0 (0%) 0 (0%) 0 (0%) 12h kube-system kube-controller-manager-kub-master2.test.local 200m (5%) 0 (0%) 0 (0%) 0 (0%) 12h kube-system kube-proxy-cnjkw 0 (0%) 0 (0%) 0 (0%) 0 (0%) 12h kube-system kube-scheduler-kub-master2.test.local 100m (2%) 0 (0%) 0 (0%) 0 (0%) 12h kube-system nodelocaldns-jvcdh 100m (2%) 0 (0%) 70Mi (2%) 200Mi (6%) 14h Allocated resources: (Total limits may be over 100 percent, i.e., overcommitted.) Resource Requests Limits -------- -------- ------ cpu 800m (21%) 300m (7%) memory 137400320 (4%) 709715200 (20%) ephemeral-storage 0 (0%) 0 (0%) hugepages-2Mi 0 (0%) 0 (0%) Events: <none> |
root@kub-master1:~# kubectl uncordon kub-master2.test.local
node/kub-master2.test.local uncordoned
|
1 2 3 4 5 6 7 8 |
root@kub-master1:~# kubectl get nodes NAME STATUS ROLES AGE VERSION kub-master1.test.local Ready control-plane 3d21h v1.30.4 kub-master2.test.local Ready control-plane 3d21h v1.30.4 kub-master3.test.local Ready control-plane 3d21h v1.30.4 kub-worker1.test.local Ready <none> 3d21h v1.30.4 kub-worker2.test.local Ready <none> 3d21h v1.30.4 kub-worker3.test.local Ready <none> 3d21h v1.30.4 |
обновляем до 1,31
deactivate
rm -rf .venv-ansible/
git add .
git commit -m "26"
git checkout release-2.27
python3 -m venv .venv-ansible
source .venv-ansible/bin/activate
pip install --upgrade pip setuptools wheel
sed -i '/ruamel.yaml.clib/d' requirements.txt
pip install -r requirements.txt
|
1 2 3 4 5 6 7 8 9 10 |
ansible-galaxy collection install \ community.general \ community.kubernetes \ kubernetes.core \ amazon.aws \ azure.azcollection \ ansible.posix \ ansible.utils \ community.crypto \ ansible.netcommon |
mkdir -p filter_plugins
cp ~/.ansible/collections/ansible_collections/ansible/utils/plugins/filter/ipaddr.py filter_plugins/
/etc/ansible/kubespray-official/kubespray-new/kubespray/inventory/sample/group_vars/all/all.yml
/etc/ansible/kubespray-official/kubespray-new/kubespray/inventory/sample/group_vars/k8s_cluster/addons.yml
/etc/ansible/kubespray-official/kubespray-new/kubespray/inventory/sample/group_vars/k8s_cluster/k8s-cluster.yml
|
1 2 3 4 5 6 7 8 |
root@kub-master1:~# kubectl get nodes NAME STATUS ROLES AGE VERSION kub-master1.test.local Ready control-plane 4d1h v1.31.7 kub-master2.test.local Ready control-plane 4d1h v1.31.7 kub-master3.test.local Ready control-plane 4d1h v1.31.7 kub-worker1.test.local Ready <none> 4d1h v1.31.7 kub-worker2.test.local Ready <none> 4d1h v1.31.7 kub-worker3.test.local Ready <none> 4d1h v1.31.7 |
обновляем до 1,32
deactivate
rm -rf .venv-ansible/
git add .
git commit -m "27"
git checkout release-2.28
python3 -m venv .venv-ansible
source .venv-ansible/bin/activate
pip install --upgrade pip setuptools wheel
sed -i '/ruamel.yaml.clib/d' requirements.txt
pip install -r requirements.txt
|
1 2 3 4 5 6 7 8 9 10 |
ansible-galaxy collection install \ community.general \ community.kubernetes \ kubernetes.core \ amazon.aws \ azure.azcollection \ ansible.posix \ ansible.utils \ community.crypto \ ansible.netcommon |
mkdir -p filter_plugins
cp ~/.ansible/collections/ansible_collections/ansible/utils/plugins/filter/ipaddr.py filter_plugins/
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
[all] kub-master1.test.local ansible_host=192.168.1.112 ip=192.168.1.112 kub-master2.test.local ansible_host=192.168.1.113 ip=192.168.1.113 kub-master3.test.local ansible_host=192.168.1.114 ip=192.168.1.114 kub-worker1.test.local ansible_host=192.168.1.115 ip=192.168.1.115 kub-worker2.test.local ansible_host=192.168.1.116 ip=192.168.1.116 kub-worker3.test.local ansible_host=192.168.1.117 ip=192.168.1.117 # ## configure a bastion host if your nodes are not directly reachable # [bastion] # bastion ansible_host=x.x.x.x ansible_user=some_user [kube_control_plane] kub-master1.test.local kub-master2.test.local kub-master3.test.local [etcd] kub-master1.test.local kub-master2.test.local kub-master3.test.local [kube_node] kub-worker1.test.local kub-worker2.test.local kub-worker3.test.local [calico_rr] [k8s_cluster:children] kube_control_plane kube_node calico_rr |
|
1 2 3 |
upstream_dns_servers: - 8.8.8.8 - 8.8.4.4 |
|
1 2 3 |
helm_enabled: true local_volume_provisioner_enabled: true |
|
1 2 3 4 |
kube_oidc_auth: true kube_token_auth: true kube_proxy_strict_arp: true |
|
1 2 3 4 5 6 7 8 |
root@kub-master1:~# kubectl get nodes NAME STATUS ROLES AGE VERSION kub-master1.test.local Ready control-plane 4d3h v1.32.5 kub-master2.test.local Ready control-plane 4d3h v1.32.5 kub-master3.test.local Ready control-plane 4d3h v1.32.5 kub-worker1.test.local Ready <none> 4d3h v1.32.5 kub-worker2.test.local Ready <none> 4d3h v1.32.5 kub-worker3.test.local Ready <none> 4d3h v1.32.5 |
обновляем до 1.32.9
пока писал всю статью прилетели ещё обновления поэтому сейчас нужно обновиться с 1.32.5 до 1.32.9
поэтому выполняем все действия что и на предыдущем шаге и запускаем апдейт
я выкачивал
https://github.com/kubernetes-sigs/kubespray/tree/release-2.28
в директорию:
/etc/ansible/kubespray-official/kub-new
потом переименую и верну как было
(.venv-ansible) root@ansible:/etc/ansible/kubespray-official/kub-new/kubespray/kubespray# ansible-playbook -i inventory/sample/inventory.ini upgrade-cluster.yml -b --become-user=root --ask-pass
ждём и проверяем:
|
1 2 3 4 5 6 7 8 |
root@kub-master1:~# kubectl get nodes NAME STATUS ROLES AGE VERSION kub-master1.test.local Ready control-plane 166d v1.32.9 kub-master2.test.local Ready control-plane 166d v1.32.9 kub-master3.test.local Ready control-plane 166d v1.32.9 kub-worker1.test.local Ready <none> 166d v1.32.9 kub-worker2.test.local Ready <none> 166d v1.32.9 kub-worker3.test.local Ready <none> 166d v1.32.9 |
готово.
обновляемся дальше:
обновляем до 1.33.5 (install PLUTO)
перед обновлением поставим ещё утилиту pluto чтобы проверить есть проблемы с совместимостью api
https://github.com/FairwindsOps/pluto/releases
на момент написания статьи последняя версия
https://github.com/FairwindsOps/pluto/releases/download/v5.22.6/pluto_5.22.6_linux_amd64.tar.gz
качаем её
wget https://github.com/FairwindsOps/pluto/releases/download/v5.22.6/pluto_5.22.6_linux_amd64.tar.gz
root@kub-master1:~# tar -xvf pluto_5.22.6_linux_amd64.tar.gz
root@kub-master1:~# chmod +x pluto
root@kub-master1:~# mv ./pluto /usr/bin/
можно проверить как api в кластере:
|
1 2 |
root@kub-master1:~# pluto detect-helm -owide There were no resources found with known deprecated apiVersions. |
так и все файлы:
|
1 2 3 4 |
root@kub-master1:~# pluto detect-files -d . NAME KIND VERSION REPLACEMENT REMOVED DEPRECATED REPL AVAIL nginx Deployment apps/v1beta1 apps/v1 true true true nginx Deployment apps/v1beta1 apps/v1 true true true |
как видим найдена старая версия api apps/v1beta1 и её нужно заменить на apps/v1, других проблем у меня не найдено поэтому продолжим обновление:
deactivate
rm -rf .venv-ansible/
git add .
git commit -m "28"
git checkout release-2.29
python3 -m venv .venv-ansible
source .venv-ansible/bin/activate
pip install --upgrade pip setuptools wheel
sed -i '/ruamel.yaml.clib/d' requirements.txt
pip install -r requirements.txt
|
1 2 3 4 5 6 7 8 9 10 |
ansible-galaxy collection install \ community.general \ community.kubernetes \ kubernetes.core \ amazon.aws \ azure.azcollection \ ansible.posix \ ansible.utils \ community.crypto \ ansible.netcommon |
mkdir -p filter_plugins
cp ~/.ansible/collections/ansible_collections/ansible/utils/plugins/filter/ipaddr.py filter_plugins/
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
[all] kub-master1.test.local ansible_host=192.168.1.112 ip=192.168.1.112 kub-master2.test.local ansible_host=192.168.1.113 ip=192.168.1.113 kub-master3.test.local ansible_host=192.168.1.114 ip=192.168.1.114 kub-worker1.test.local ansible_host=192.168.1.115 ip=192.168.1.115 kub-worker2.test.local ansible_host=192.168.1.116 ip=192.168.1.116 kub-worker3.test.local ansible_host=192.168.1.117 ip=192.168.1.117 # ## configure a bastion host if your nodes are not directly reachable # [bastion] # bastion ansible_host=x.x.x.x ansible_user=some_user [kube_control_plane] kub-master1.test.local kub-master2.test.local kub-master3.test.local [etcd] kub-master1.test.local kub-master2.test.local kub-master3.test.local [kube_node] kub-worker1.test.local kub-worker2.test.local kub-worker3.test.local [calico_rr] [k8s_cluster:children] kube_control_plane kube_node calico_rr |
|
1 2 3 |
upstream_dns_servers: - 8.8.8.8 - 8.8.4.4 |
|
1 2 3 |
helm_enabled: true local_volume_provisioner_enabled: true |
|
1 2 3 4 |
kube_oidc_auth: true kube_token_auth: true kube_proxy_strict_arp: true |
Проверяем:
|
1 2 3 4 5 6 7 8 |
root@kub-master1:~# kubectl get nodes NAME STATUS ROLES AGE VERSION kub-master1.test.local Ready control-plane 166d v1.33.5 kub-master2.test.local Ready control-plane 166d v1.33.5 kub-master3.test.local Ready control-plane 166d v1.33.5 kub-worker1.test.local Ready <none> 166d v1.33.5 kub-worker2.test.local Ready <none> 166d v1.33.5 kub-worker3.test.local Ready <none> 166d v1.33.5 |
обновляем до 1.34.1
deactivate
rm -rf .venv-ansible/
git add .
git commit -m "29"
так как ещё нет последнего апдейта минорного и новой версии то переключаемся на мастера
git checkout master
python3 -m venv .venv-ansible
source .venv-ansible/bin/activate
pip install --upgrade pip setuptools wheel
sed -i '/ruamel.yaml.clib/d' requirements.txt
pip install -r requirements.txt
|
1 2 3 4 5 6 7 8 9 10 |
ansible-galaxy collection install \ community.general \ community.kubernetes \ kubernetes.core \ amazon.aws \ azure.azcollection \ ansible.posix \ ansible.utils \ community.crypto \ ansible.netcommon |
mkdir -p filter_plugins
cp ~/.ansible/collections/ansible_collections/ansible/utils/plugins/filter/ipaddr.py filter_plugins/
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
[all] kub-master1.test.local ansible_host=192.168.1.112 ip=192.168.1.112 kub-master2.test.local ansible_host=192.168.1.113 ip=192.168.1.113 kub-master3.test.local ansible_host=192.168.1.114 ip=192.168.1.114 kub-worker1.test.local ansible_host=192.168.1.115 ip=192.168.1.115 kub-worker2.test.local ansible_host=192.168.1.116 ip=192.168.1.116 kub-worker3.test.local ansible_host=192.168.1.117 ip=192.168.1.117 # ## configure a bastion host if your nodes are not directly reachable # [bastion] # bastion ansible_host=x.x.x.x ansible_user=some_user [kube_control_plane] kub-master1.test.local kub-master2.test.local kub-master3.test.local [etcd] kub-master1.test.local kub-master2.test.local kub-master3.test.local [kube_node] kub-worker1.test.local kub-worker2.test.local kub-worker3.test.local [calico_rr] [k8s_cluster:children] kube_control_plane kube_node calico_rr |
|
1 2 3 |
upstream_dns_servers: - 8.8.8.8 - 8.8.4.4 |
|
1 2 3 |
helm_enabled: true local_volume_provisioner_enabled: true |
|
1 2 3 4 |
kube_oidc_auth: true kube_token_auth: true kube_proxy_strict_arp: true |
Проверяем:
|
1 2 3 4 5 6 7 8 |
root@kub-master1:~# kubectl get nodes NAME STATUS ROLES AGE VERSION kub-master1.test.local Ready control-plane 166d v1.34.1 kub-master2.test.local Ready control-plane 166d v1.34.1 kub-master3.test.local Ready control-plane 166d v1.34.1 kub-worker1.test.local Ready <none> 166d v1.34.1 kub-worker2.test.local Ready <none> 166d v1.34.1 kub-worker3.test.local Ready <none> 166d v1.34.1 |
Gitlab in k8s
берём оф чарт:
https://gitlab.com/gitlab-org/charts/gitlab/-/tree/master/charts/gitlab
используем самоподписанный сертификат:
/etc/ansible/kubespray-official/gitlab-gitlab-runner/certs/ca_openssl.cnf
|
1 2 3 4 5 6 7 |
[ v3_ca ] subjectAltName = @alt_names [ alt_names ] DNS.1 = gitlab.test.local DNS.2 = kas.test.local |
/etc/ansible/kubespray-official/gitlab-gitlab-runner/certs/gitlab_openssl.cnf
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
[ req ] default_bits = 4096 prompt = no default_md = sha256 distinguished_name = dn req_extensions = req_ext [ dn ] C = RU ST = YourState L = YourCity O = YourOrganization CN = gitlab.test.local [ req_ext ] subjectAltName = @alt_names [ alt_names ] DNS.1 = gitlab.test.local DNS.2 = kas.test.local |
создаём сертификат
|
1 2 3 4 5 |
openssl genrsa -out ca.key 4096 openssl req -x509 -sha256 -new -key ca.key -days 10000 -out ca.crt openssl genrsa -out gitlab.key 4096 openssl req -new -key gitlab.key -out gitlab.csr -config gitlab_openssl.cnf openssl x509 -req -in gitlab.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out gitlab.crt -days 10000 -sha256 -extfile ca_openssl.cnf -extensions v3_ca |
создаём namespace
|
1 2 3 4 5 |
kubectl create secret generic gitlab-tls \ --from-file=tls.crt=./gitlab.crt \ --from-file=tls.key=./gitlab.key \ --from-file=ca.crt=./ca.crt \ -n gitlab |
|
1 2 3 4 5 |
for node in $(kubectl get nodes -o wide | awk 'NR>1{print $6}' | grep -v INTERNAL); do scp gitlab.crt root@$node:/usr/local/share/ca-certificates/gitlab.crt \ && ssh root@$node update-ca-certificates done |
нам нужно несколько s3 бакетов (встроенный minio не будет использовать)
gitlab-lfs
gitlab-artifacts
gitlab-uploads
gitlab-packages
gitlab-backups


|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:ListBucket", "s3:GetBucketLocation" ], "Resource": [ "arn:aws:s3:::gitlab-lfs", "arn:aws:s3:::gitlab-packages", "arn:aws:s3:::gilab-uploads", "arn:aws:s3:::gitlab-artifacts", "arn:aws:s3:::gitlab-backups" ] }, { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject" ], "Resource": [ "arn:aws:s3:::gilab-uploads/*", "arn:aws:s3:::gitlab-artifacts/*", "arn:aws:s3:::gitlab-backups/*", "arn:aws:s3:::gitlab-lfs/*", "arn:aws:s3:::gitlab-packages/*" ] } ] } |
создаём пользователя
и асайним на него нашу policy

создаём ключи для него:

создаём секрет с доступами к бакету, можно делать отдельные секреты но я решил использовать 1 на все
/etc/ansible/kubespray-official/gitlab-gitlab-runner/s3-conf.yml
|
1 2 3 4 5 6 7 |
provider: AWS aws_access_key_id: "biZiP3COrMLYTzflHWH6" aws_secret_access_key: "LejhJ03w8wqsMJrDf8qeGgcHCPs3B8ml9s73GYg4" region: us-east-1 # обязательный, даже если MinIO, любое значение допустимо endpoint: "http://192.168.1.120:9000" path_style: true |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 |
nginx-ingress: enabled: false global: hosts: # Домен, по которому будет доступен GitLab domain: "test.local" ingress: enabled: true configureCertmanager: false provider: nginx class: "nginx" annotations: nginx.ingress.kubernetes.io/force-ssl-redirect: "true" tls: secretName: "gitlab-tls" hosts: - gitlab.test.local # psql: # main: # # Параметры подключения к внешней базе данных для основной части GitLab # host: "${var.rds_address[local.gitlab.rds_infra_name]}" # port: 5432 # username: "gitlab" # password: # secret: "${kubernetes_secret.gitlab_db_secret.metadata[0].name}" # key: "${keys(kubernetes_secret.gitlab_db_secret.data)[0]}" # database: "gitlab" # ci: # # Параметры подключения для базы данных CI # host: "${var.rds_address[local.gitlab.rds_infra_name]}" # port: 5432 # username: "gitlab" # password: # secret: "${kubernetes_secret.gitlab_db_secret.metadata[0].name}" # key: "${keys(kubernetes_secret.gitlab_db_secret.data)[0]}" # database: "gitlab" appConfig: object_store: enabled: true connection: secret: "gitlab-s3-config" key: "s3.yml" minio: enabled: false serviceAccount: enabled: true email: display_name: 'GitLab' from: "gitlab@test.local" reply_to: "noreply@test.local" smtp: enabled: false address: "" port: 587 domain: "gitlab@test.local" user_name: "" password: secret: "" # имя секрета, где хранится пароль key: "" #password authentication: "login" # обычно используется "login" или "plain" starttls_auto: true registry: enabled: false shell: port: 2222 # так же открываем порт на ingress controller tcp: 2222: "gitlab/gitlab-gitlab-shell:2222:PROXY" tcp: proxyProtocol: false # appConfig: # lfs: # bucket: "gitlab-lfs" # path: "lfs" # connection: # secret: "biZiP3COrMLYTzflHWH6" # key: "LejhJ03w8wqsMJrDf8qeGgcHCPs3B8ml9s73GYg4" # artifacts: # bucket: "gitlab-artifacts" # path: "artifacts" # connection: # secret: "biZiP3COrMLYTzflHWH6" # key: "LejhJ03w8wqsMJrDf8qeGgcHCPs3B8ml9s73GYg4" # uploads: # bucket: "gilab-uploads" # path: "uploads" # connection: # secret: "biZiP3COrMLYTzflHWH6" # key: "LejhJ03w8wqsMJrDf8qeGgcHCPs3B8ml9s73GYg4" # packages: # bucket: "gitlab-packages" # path: "packages" # connection: # secret: "biZiP3COrMLYTzflHWH6" # key: "LejhJ03w8wqsMJrDf8qeGgcHCPs3B8ml9s73GYg4" # backups: # bucket: "gitlab-backups" # path: "backups" # tmpBucket: "gitlab-backups" # tmpPath: "tmp" # гугловая авторизация # omniauth: # enabled: true # allowSingleSignOn: ['google_oauth2'] # syncProfileAttributes: ['email'] # autoLinkSamlUser: true # blockAutoCreatedUsers: false # autoLinkUser: ['google_oauth2'] # providers: # - secret: "${kubernetes_secret.gitlab_secret.metadata[0].name}" # key: "${keys(kubernetes_secret.gitlab_secret.data)[0]}" redis: install: true master: resources: requests: cpu: "100m" memory: "100Mi" limits: cpu: "500m" memory: "300Mi" persistence: enabled: true storageClass: nfs-client size: 5Gi # Отключаем certmanager certmanager: installCRDs: false install: false # ОтклюВключаем чаем установку встроенного PostgreSQL postgresql: install: true primary: resources: requests: cpu: "300m" memory: "300Mi" limits: cpu: "1000m" memory: "1Gi" persistence: enabled: true storageClass: nfs-client size: 10Gi # Registry – хранение Docker-образов (если используется) registry: enabled: false # Prometheus – мониторинг GitLab (если устанавливается вместе с GitLab) prometheus: install: false # Основные компоненты GitLab (Rails/Sidekiq/Webservice) gitlab: webservice: hpa: enabled: false replicaCount: 1 minReplicas: 1 maxReplicas: 1 ingress: proxyBodySize: "5000m" proxyConnectTimeout: "8000" proxyReadTimeout: "8000" resources: requests: cpu: "500m" memory: "1500Mi" limits: cpu: "1000m" memory: "3Gi" sidekiq: hpa: enabled: true cpu: targetType: Value targetAverageValue: 400m replicaCount: 1 minReplicas: 1 maxReplicas: "40" concurrency: "20" resources: requests: cpu: "400m" memory: "1Gi" limits: cpu: "1500m" memory: "2Gi" toolbox: resources: requests: cpu: "100m" memory: "200Mi" limits: cpu: "500m" memory: "1Gi" # backups: # cron: # enabled: "false" # schedule: "@daily" # resources: # requests: # cpu: "200m" # memory: "200Mi" # limits: # cpu: "1000m" # memory: "2Gi" # persistence: # enabled: "true" # accessMode: ReadWriteOnce # useGenericEphemeralVolume: false # storageClass: "nfs-client" # size: 40Gi # objectStorage: # config: # secret: "biZiP3COrMLYTzflHWH6" # key: "LejhJ03w8wqsMJrDf8qeGgcHCPs3B8ml9s73GYg4" # Gitaly – хранит Git-репозитории GitLab gitaly: resources: requests: cpu: "300m" memory: "300Mi" limits: cpu: "1000m" memory: "1Gi" persistence: enabled: true storageClass: nfs-client size: 20Gi gitlab-shell: hpa: enabled: false replicaCount: 1 minReplicas: 1 maxReplicas: 1 service: enabled: true type: ClusterIP port: 2222 resources: requests: cpu: "100m" memory: "100Mi" limits: cpu: "500m" memory: "300Mi" kas: minReplicas: 1 maxReplicas: 1 resources: requests: cpu: "100m" memory: "100Mi" limits: cpu: "200m" memory: "200Mi" gitlab-exporter: resources: requests: cpu: "100m" memory: "100Mi" limits: cpu: "500m" memory: "200Mi" appConfig: lfs: bucket: "gitlab-lfs" path: "lfs" connection: secret: "gitlab-s3-config" key: "s3.yml" artifacts: bucket: "gitlab-artifacts" path: "artifacts" connection: secret: "gitlab-s3-config" key: "s3.yml" uploads: bucket: "gitlab-uploads" path: "uploads" connection: secret: "gitlab-s3-config" key: "s3.yml" packages: bucket: "gitlab-packages" path: "packages" connection: secret: "gitlab-s3-config" key: "s3.yml" backups: bucket: "gitlab-backups" path: "backups" tmpBucket: "gitlab-backups" tmpPath: "tmp" gitlab-runner: install: false |
ставим:
ждём пока всё установится,
|
1 2 3 4 5 6 7 8 9 10 11 |
root@kub-master1:~# kubectl get pod -n gitlab NAME READY STATUS RESTARTS AGE gitlab-gitaly-0 1/1 Running 0 18h gitlab-gitlab-exporter-6f6d6598d-xbhgk 1/1 Running 0 18h gitlab-gitlab-shell-b985b95ff-j4xz2 1/1 Running 0 18h gitlab-kas-95b98b8b6-bs79r 1/1 Running 3 (18h ago) 18h gitlab-postgresql-0 2/2 Running 3 (5h36m ago) 18h gitlab-redis-master-0 2/2 Running 6 (5h38m ago) 18h gitlab-sidekiq-all-in-1-v2-8448db474-6ltn9 1/1 Running 0 18h gitlab-toolbox-56d756dc6c-fssp9 1/1 Running 0 18h gitlab-webservice-default-54b9669ddd-w84j2 2/2 Running 7 (5h36m ago) 18h |
и смотрим пароль от админки:
|
1 2 |
tcp: 2222: "gitlab/gitlab-gitlab-shell:2222" |
весь values для ingress выглядит так:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
controller: enabled: true kind: Deployment # DaemonSet replicaCount: 2 # CPU/memory limits for the controller pods resources: limits: cpu: 1000m memory: 1Gi requests: cpu: 100m memory: 256Mi # CRD for the nginx ingress class ingressClassResource: default: false enabled: true name: nginx controllerValue: k8s.io/ingress-nginx # Enable metrics endpoint metrics: enabled: true port: 10254 service: enabled: true annotations: prometheus.io/scrape: "true" prometheus.io/port: "10254" extraArgs: "enable-ssl-passthrough": "true" # enable-ssl-passthrough: true # passthrough: # enabled: true # All valid ConfigMap keys must be in kebab-case config: disable-ipv6: "true" disable-ipv6-dns: "true" enable-access-log-for-default-backend: "false" http2-max-field-size: "8k" large-client-header-buffers: "16 64k" limit-conn-status-code: "429" limit-req-status-code: "429" load-balance: ewma keepalive: "65" client-max-body-size: "300m" proxy-body-size: "300m" error-log-level: error log-format-escape-json: "true" log-format-upstream: >- {"bytes_sent":"$bytes_sent","vhost":"$host"," request_proto":"$server_protocol","remote_addr":"$remote_addr", "proxy_add_x_forwarded_for":"$proxy_add_x_forwarded_for","remote_user":"$remote_user", "time_local":"$time_local","request_method":"$request_method", "request_uri":"$uri","request_args":"$args","request":"$request", "status":"$status","body_bytes_sent":"$body_bytes_sent", "http_referer":"$http_referer","http_user_agent":"$http_user_agent", "request_length":"$request_length","request_time":"$request_time", "upstream_addr":"$upstream_addr","upstream_response_length":"$upstream_response_length", "upstream_response_time":"$upstream_response_time","upstream_status":"$upstream_status", "X-Business-Error":"$upstream_http_x_business_error","upstream_header_time":"$upstream_header_time", "upstream_connect_time":"$upstream_connect_time","connections_waiting":"$connections_waiting", "connections_active":"$connections_active"} map-hash-bucket-size: "128" server-tokens: "false" ssl-protocols: "TLSv1.2 TLSv1.3" ssl-session-cache: "true" ssl-session-cache-size: "20m" ssl-session-timeout: "30m" use-forwarded-headers: "true" use-gzip: "true" use-proxy-protocol: "false" worker-cpu-affinity: auto worker-processes: "2" allow-snippet-annotations: "true" annotations-risk-level: Critical ingressClass: nginx # Service definition for the controller service: type: LoadBalancer externalTrafficPolicy: Cluster # Turn on the admission webhook deployment admissionWebhooks: enabled: true tolerations: - key: "node-role.kubernetes.io/master" operator: "Exists" effect: "NoSchedule" - key: "node-role.kubernetes.io/control-plane" operator: "Exists" effect: "NoSchedule" defaultBackend: enabled: false tcp: 2222: "gitlab/gitlab-gitlab-shell:2222" |
обновляем новую конфигурацию values
проверяем что порт на ingress добавился:
|
1 2 3 4 5 |
kubectl get svc -n ingress-nginx NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE ingress-nginx-controller LoadBalancer 10.233.9.103 192.168.1.191 80:31891/TCP,443:30190/TCP,2222:31213/TCP 3d5h ingress-nginx-controller-admission ClusterIP 10.233.50.207 <none> 443/TCP 3d5h ingress-nginx-controller-metrics ClusterIP 10.233.40.60 <none> 10254/TCP 3d5h |
всё, дальше можно добавлять наш публичный ssh ключ и работать с репозиториями:
git clone ssh://git@gitlab.test.local:2222/test/test-project.git
Gitlab runner
создаём секрет с нашим самоподписанным сертификатом:
root@kub-master1:~/gitlab-gitlab-runner/certs# kubectl create secret generic gitlab-ssl --namespace gitlab --from-file=gitlab.test.local.crt=./gitlab.crt


|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
imagePullPolicy: "Always" gitlabUrl: "https://gitlab.test.local" runnerRegistrationToken: "glrt-t3_sFjXrYwuz-xDWPx_A-5a" # токен регистрации gitlab runner создаётся вручную в gitlab concurrent: "20" checkInterval: "1" terminationGracePeriodSeconds: "3600" certsSecretName: "gitlab-ssl" # тут сапольный сертификат для gitlab rbac: create: "true" rules: - resources: ["configmaps", "pods", "pods/attach", "secrets", "services"] verbs: ["get", "list", "watch", "create", "patch", "update", "delete"] - apiGroups: [""] resources: ["pods/exec"] verbs: ["create", "patch", "delete"] serviceAccount: name: runner-gitlab-runner metrics: enabled: "true" serviceMonitor: enabled: true interval: 15s labels: release: kube-prometheus-stack service: enabled: true #nodeSelector: serviceAccount: create: true name: runner-gitlab-runner runners: serviceAccountName: runner-gitlab-runner config: | [[runners]] [runners.kubernetes] namespace = "{{.Release.Namespace}}" image = "ubuntu:22.04" # build container cpu = "300m" memory = "300Mi" cpu_limit = "500m" memory_limit = "500Mi" cpu_limit_overwrite_max_allowed = "500m" memory_limit_overwrite_max_allowed = "500Mi" # service containers service_cpu_limit = "100m" service_memory_limit = "100Mi" service_cpu_limit_overwrite_max_allowed = "100m" service_memory_limit_overwrite_max_allowed = "100Mi" # helper container helper_cpu_limit = "100m" helper_memory_limit = "100Mi" helper_cpu_limit_overwrite_max_allowed = "100m" helper_memory_limit_overwrite_max_allowed = "100Mi" service_account = "runner-gitlab-runner" image: "ubuntu:22.04" imagePullPolicy: "always" privileged: true tags: "test-runner" runUntagged: true resources: requests: cpu: "200m" memory: "150Mi" ephemeral-storage: "300Mi" limits: cpu: "400m" memory: "350Mi" ephemeral-storage: "600Mi" |
root@kub-master1:~/gitlab-gitlab-runner# helm upgrade --install gitlab-runner gitlab/gitlab-runner -n gitlab --version 0.74.1 --values gitlab-runner.yaml

|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
workflow: rules: - if: '$CI_COMMIT_BRANCH == "main"' when: always - when: never stages: - create - print create_file: stage: create image: alpine:latest tags: - test-runner script: - echo "hello world" > hello.txt artifacts: paths: - hello.txt expire_in: 1 hour print_file: stage: print image: alpine:latest tags: - test-runner dependencies: - create_file script: - echo "Содержимое hello.txt:" - cat hello.txt |
2 этапа и создаётся артифакт.

проверяем что всё ок:

проверяем что артефакт создался


теперь проверим что данный файл добавился в s3-minio

как видим всё ок
Gitlab helm chart с Freeipa
для интеграции с freeipa используем ранее созданный sysaccount
uid=gitlab,cn=sysaccounts,cn=etc,dc=test,dc=local
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
### FreeIPA - System Account Manager ### 1.) ldapserver=freeipa-1.test.local 2.) domain=test.local (ldapdomain=dc=test,dc=local) 3.) binduser=uid=admin,cn=users,cn=accounts,dc=test,dc=local 4.) bindpass=SET! 5.) ssl=false Actions (ready): add | rm | ls | info | passwd | save --- Results --- dn: uid=sudo,cn=sysaccounts,cn=etc,dc=test,dc=local dn: uid=nexus,cn=sysaccounts,cn=etc,dc=test,dc=local dn: uid=gitlab,cn=sysaccounts,cn=etc,dc=test,dc=local --- End Results --- |
создаём секрет с паролем:
/etc/ansible/kubespray-official/gitlab-gitlab-runner/gitlab-ldap-secret.yaml
|
1 2 3 4 5 6 7 8 9 10 |
apiVersion: v1 kind: Secret metadata: name: gitlab-ldap-secret namespace: gitlab type: Opaque stringData: # bindPW — здесь указываете ваш пароль ldap.bindPW: "Secret123" |
kubectl apply -f gitlab-ldap-secret.yaml
вот наш values:
/etc/ansible/kubespray-official/gitlab-gitlab-runner/gitlab-ldap.yaml
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 |
nginx-ingress: enabled: false global: hosts: # Домен, по которому будет доступен GitLab domain: "test.local" ingress: enabled: true configureCertmanager: false provider: nginx class: "nginx" annotations: nginx.ingress.kubernetes.io/force-ssl-redirect: "true" tls: secretName: "gitlab-tls" hosts: - gitlab.test.local # psql: # main: # # Параметры подключения к внешней базе данных для основной части GitLab # host: "${var.rds_address[local.gitlab.rds_infra_name]}" # port: 5432 # username: "gitlab" # password: # secret: "${kubernetes_secret.gitlab_db_secret.metadata[0].name}" # key: "${keys(kubernetes_secret.gitlab_db_secret.data)[0]}" # database: "gitlab" # ci: # # Параметры подключения для базы данных CI # host: "${var.rds_address[local.gitlab.rds_infra_name]}" # port: 5432 # username: "gitlab" # password: # secret: "${kubernetes_secret.gitlab_db_secret.metadata[0].name}" # key: "${keys(kubernetes_secret.gitlab_db_secret.data)[0]}" # database: "gitlab" appConfig: object_store: enabled: true connection: secret: "gitlab-s3-config" key: "s3.yml" ldap: enabled: true servers: main: label: "FreeIPA LDAP" host: "192.168.1.100" port: 389 uid: "uid" # в FreeIPA имя юзера в атрибуте uid bind_dn: "uid=gitlab,cn=sysaccounts,cn=etc,dc=test,dc=local" password: secret: gitlab-ldap-secret key: ldap.bindPW # Шифрование: plain – без TLS; start_tls – через StartTLS; simple_tls – LDAPS encryption: "plain" verify_certificates: false # Базовая точка поиска пользователей в FreeIPA base: "cn=accounts,dc=test,dc=local" # Ограничиваем вход только членами группы gitlab user_filter: "(memberOf=cn=gitlab,cn=groups,cn=accounts,dc=test,dc=local)" # По логину используем uid (или поставьте allow_username_or_email_login: true) allow_username_or_email_login: false block_auto_created_users: false minio: enabled: false serviceAccount: enabled: true email: display_name: 'GitLab' from: "gitlab@test.local" reply_to: "noreply@test.local" smtp: enabled: false address: "" port: 587 domain: "gitlab@test.local" user_name: "" password: secret: "" # имя секрета, где хранится пароль key: "" #password authentication: "login" # обычно используется "login" или "plain" starttls_auto: true registry: enabled: false shell: port: 2222 # так же открываем порт на ingress controller tcp: 2222: "gitlab/gitlab-gitlab-shell:2222:PROXY" tcp: proxyProtocol: false # appConfig: # lfs: # bucket: "gitlab-lfs" # path: "lfs" # connection: # secret: "biZiP3COrMLYTzflHWH6" # key: "LejhJ03w8wqsMJrDf8qeGgcHCPs3B8ml9s73GYg4" # artifacts: # bucket: "gitlab-artifacts" # path: "artifacts" # connection: # secret: "biZiP3COrMLYTzflHWH6" # key: "LejhJ03w8wqsMJrDf8qeGgcHCPs3B8ml9s73GYg4" # uploads: # bucket: "gilab-uploads" # path: "uploads" # connection: # secret: "biZiP3COrMLYTzflHWH6" # key: "LejhJ03w8wqsMJrDf8qeGgcHCPs3B8ml9s73GYg4" # packages: # bucket: "gitlab-packages" # path: "packages" # connection: # secret: "biZiP3COrMLYTzflHWH6" # key: "LejhJ03w8wqsMJrDf8qeGgcHCPs3B8ml9s73GYg4" # backups: # bucket: "gitlab-backups" # path: "backups" # tmpBucket: "gitlab-backups" # tmpPath: "tmp" # гугловая авторизация # omniauth: # enabled: true # allowSingleSignOn: ['google_oauth2'] # syncProfileAttributes: ['email'] # autoLinkSamlUser: true # blockAutoCreatedUsers: false # autoLinkUser: ['google_oauth2'] # providers: # - secret: "${kubernetes_secret.gitlab_secret.metadata[0].name}" # key: "${keys(kubernetes_secret.gitlab_secret.data)[0]}" redis: install: true master: resources: requests: cpu: "100m" memory: "100Mi" limits: cpu: "500m" memory: "300Mi" persistence: enabled: true storageClass: nfs-client size: 5Gi # Отключаем certmanager certmanager: installCRDs: false install: false # ОтклюВключаем чаем установку встроенного PostgreSQL postgresql: install: true primary: resources: requests: cpu: "300m" memory: "300Mi" limits: cpu: "1000m" memory: "1Gi" persistence: enabled: true storageClass: nfs-client size: 10Gi # Registry – хранение Docker-образов (если используется) registry: enabled: false # Prometheus – мониторинг GitLab (если устанавливается вместе с GitLab) prometheus: install: false # Основные компоненты GitLab (Rails/Sidekiq/Webservice) gitlab: webservice: hpa: enabled: false replicaCount: 1 minReplicas: 1 maxReplicas: 1 ingress: proxyBodySize: "5000m" proxyConnectTimeout: "8000" proxyReadTimeout: "8000" resources: requests: cpu: "500m" memory: "800Mi" limits: cpu: "1000m" memory: "3Gi" sidekiq: hpa: enabled: true cpu: targetType: Value targetAverageValue: 400m replicaCount: 1 minReplicas: 1 maxReplicas: "40" concurrency: "20" resources: requests: cpu: "400m" memory: "900Mi" limits: cpu: "1500m" memory: "2Gi" toolbox: resources: requests: cpu: "100m" memory: "200Mi" limits: cpu: "500m" memory: "1Gi" # backups: # cron: # enabled: "false" # schedule: "@daily" # resources: # requests: # cpu: "200m" # memory: "200Mi" # limits: # cpu: "1000m" # memory: "2Gi" # persistence: # enabled: "true" # accessMode: ReadWriteOnce # useGenericEphemeralVolume: false # storageClass: "nfs-client" # size: 40Gi # objectStorage: # config: # secret: "biZiP3COrMLYTzflHWH6" # key: "LejhJ03w8wqsMJrDf8qeGgcHCPs3B8ml9s73GYg4" # Gitaly – хранит Git-репозитории GitLab gitaly: resources: requests: cpu: "300m" memory: "300Mi" limits: cpu: "1000m" memory: "1Gi" persistence: enabled: true storageClass: nfs-client size: 20Gi gitlab-shell: hpa: enabled: false replicaCount: 1 minReplicas: 1 maxReplicas: 1 service: enabled: true type: ClusterIP port: 2222 resources: requests: cpu: "100m" memory: "100Mi" limits: cpu: "500m" memory: "300Mi" kas: minReplicas: 1 maxReplicas: 1 resources: requests: cpu: "100m" memory: "100Mi" limits: cpu: "200m" memory: "200Mi" gitlab-exporter: resources: requests: cpu: "100m" memory: "100Mi" limits: cpu: "500m" memory: "200Mi" appConfig: lfs: bucket: "gitlab-lfs" path: "lfs" connection: secret: "gitlab-s3-config" key: "s3.yml" artifacts: bucket: "gitlab-artifacts" path: "artifacts" connection: secret: "gitlab-s3-config" key: "s3.yml" uploads: bucket: "gitlab-uploads" path: "uploads" connection: secret: "gitlab-s3-config" key: "s3.yml" packages: bucket: "gitlab-packages" path: "packages" connection: secret: "gitlab-s3-config" key: "s3.yml" backups: bucket: "gitlab-backups" path: "backups" tmpBucket: "gitlab-backups" tmpPath: "tmp" gitlab-runner: install: false |
от предыдущего отличается вот этой частью:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
global: appConfig: ldap: enabled: true servers: main: label: "FreeIPA LDAP" host: "192.168.1.100" port: 389 uid: "uid" # в FreeIPA имя юзера в атрибуте uid bind_dn: "uid=gitlab,cn=sysaccounts,cn=etc,dc=test,dc=local" password: secret: gitlab-ldap-secret key: ldap.bindPW # Шифрование: plain – без TLS; start_tls – через StartTLS; simple_tls – LDAPS encryption: "plain" verify_certificates: false # Базовая точка поиска пользователей в FreeIPA base: "cn=accounts,dc=test,dc=local" # Ограничиваем вход только членами группы gitlab user_filter: "(memberOf=cn=gitlab,cn=groups,cn=accounts,dc=test,dc=local)" # По логину используем uid (или поставьте allow_username_or_email_login: true) allow_username_or_email_login: false block_auto_created_users: false |
во freeipa у нас есть группа gitlab и пользовать user1

пробуем залогиниться с ним:

как видим всё ок.
Резервное копирование etcd в hostPath
так как мы на своём железе нам нужно делать бэкапы etcd, тут мы будет делать snapshot etcd и сохранять его локально на мастерах
для этого первым делом создадим на всем мастерах директорию под бэкапы:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
apiVersion: batch/v1 kind: CronJob metadata: name: etcd-backup namespace: kube-system spec: schedule: "0 2 * * *" # каждый день в 02:00 jobTemplate: spec: template: spec: hostNetwork: true dnsPolicy: ClusterFirstWithHostNet nodeSelector: node-role.kubernetes.io/control-plane: "" # только на мастерах tolerations: - key: node-role.kubernetes.io/control-plane operator: Exists effect: NoSchedule securityContext: runAsUser: 0 restartPolicy: OnFailure volumes: - name: etcd-ssl hostPath: path: /etc/ssl/etcd/ssl type: Directory - name: backup-dir hostPath: path: /var/backups/etcd type: DirectoryOrCreate containers: - name: etcd-backup image: bitnami/etcd:3 volumeMounts: - name: etcd-ssl mountPath: /ssl readOnly: true - name: backup-dir mountPath: /backup command: - /bin/sh - -c - | export ETCDCTL_API=3 etcdctl \ --endpoints=https://127.0.0.1:2379 \ --cacert=/ssl/ca.pem \ --cert=/ssl/admin-$(hostname -f).pem \ --key=/ssl/admin-$(hostname -f)-key.pem \ snapshot save /backup/etcd-$(date +%Y%m%dT%H%M%S).db |
kubectl apply -f backup.yaml
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
root@kub-master1:/var/backups/etcd# ls -lah total 56M drwxr-xr-x 2 root root 4.0K Jun 29 16:25 . drwxr-xr-x 12 root root 4.0K Jun 29 16:16 .. -rw------- 1 root root 56M Jun 29 16:25 etcd-20250629T102518.db root@kub-master1:/var/backups/etcd# kubectl logs job/etcd-backup-now1 -n kube-system {"level":"info","ts":1751192718.184084,"caller":"snapshot/v3_snapshot.go:68","msg":"created temporary db file","path":"/backup/etcd-20250629T102518.db.part"} {"level":"info","ts":1751192718.1902862,"logger":"client","caller":"v3/maintenance.go:211","msg":"opened snapshot stream; downloading"} {"level":"info","ts":1751192718.1904283,"caller":"snapshot/v3_snapshot.go:76","msg":"fetching snapshot","endpoint":"https://127.0.0.1:2379"} {"level":"info","ts":1751192718.6031828,"logger":"client","caller":"v3/maintenance.go:219","msg":"completed snapshot read; closing"} {"level":"info","ts":1751192718.6250348,"caller":"snapshot/v3_snapshot.go:91","msg":"fetched snapshot","endpoint":"https://127.0.0.1:2379","size":"58 MB","took":"now"} {"level":"info","ts":1751192718.6251087,"caller":"snapshot/v3_snapshot.go:100","msg":"saved","path":"/backup/etcd-20250629T102518.db"} Snapshot saved at /backup/etcd-20250629T102518.db |
ок снапшот создаётся.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:ListBucket", "s3:GetBucketLocation" ], "Resource": [ "arn:aws:s3:::etcd-snapshots", "arn:aws:s3:::etcd-snapshots/*" ] }, { "Effect": "Allow", "Action": [ "s3:AbortMultipartUpload", "s3:DeleteObject", "s3:GetObject", "s3:ListBucketMultipartUploads", "s3:ListMultipartUploadParts", "s3:PutObject" ], "Resource": [ "arn:aws:s3:::etcd-snapshots", "arn:aws:s3:::etcd-snapshots/*" ] } ] } |
пользователь etcd и вот креды пользователя:
access: YcGmA6wqztm5FX4Pfwnf
secret: t0TkdM2Om2P4ZX4mgXx6Sxy9E9SsYnYO9MkIJsCI

--from-literal=access-key=ВАШ_ACCESS_KEY \
--from-literal=secret-key=ВАШ_SECRET_KEY \
-n kube-system
|
1 2 3 4 |
kubectl create secret generic s3-credentials \ --from-literal=access-key=YcGmA6wqztm5FX4Pfwnf \ --from-literal=secret-key=t0TkdM2Om2P4ZX4mgXx6Sxy9E9SsYnYO9MkIJsCI \ -n kube-system |
а вот cronjob
/etc/ansible/kubespray-official/etcd-backup/backup-to-s3-minio/backup.yaml
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
apiVersion: batch/v1 kind: CronJob metadata: name: etcd-backup-to-s3 namespace: kube-system spec: schedule: "0 */12 * * *" # каждые 12 часов jobTemplate: spec: template: spec: hostNetwork: true dnsPolicy: ClusterFirstWithHostNet nodeSelector: node-role.kubernetes.io/control-plane: "" tolerations: - key: node-role.kubernetes.io/control-plane operator: Exists effect: NoSchedule securityContext: runAsUser: 0 restartPolicy: OnFailure volumes: - name: etcd-ssl hostPath: path: /etc/ssl/etcd/ssl type: Directory - name: temp-backup emptyDir: {} initContainers: - name: snapshot image: bitnami/etcd:3 volumeMounts: - name: etcd-ssl mountPath: /ssl readOnly: true - name: temp-backup mountPath: /snapshot command: - /bin/sh - -c - | export ETCDCTL_API=3 etcdctl \ --endpoints=https://127.0.0.1:2379 \ --cacert=/ssl/ca.pem \ --cert=/ssl/admin-$(hostname -f).pem \ --key=/ssl/admin-$(hostname -f)-key.pem \ snapshot save /snapshot/etcd-$(date +%Y%m%dT%H%M%S).db containers: - name: uploader image: amazon/aws-cli:latest env: - name: AWS_ACCESS_KEY_ID valueFrom: secretKeyRef: name: s3-credentials key: access-key - name: AWS_SECRET_ACCESS_KEY valueFrom: secretKeyRef: name: s3-credentials key: secret-key - name: AWS_REGION value: us-east-1 volumeMounts: - name: temp-backup mountPath: /snapshot command: - /bin/sh - -c - | LATEST=$(ls /snapshot/etcd-*.db | sort | tail -n1) echo "Uploading $LATEST to etcd-snapshots…" aws s3api put-object \ --bucket etcd-snapshots \ --key "$(basename $LATEST)" \ --body "$LATEST" \ --endpoint-url http://192.168.1.120:9000 |
тут нам нужно поправить
--bucket указываем наш созданный бакет
--endpoint-url указываем адрес нашего s3-minio
алпаим
kubectl create job etcd-backup-s3 --from=cronjob/etcd-backup-to-s3 -n kube-system
|
1 2 3 4 5 6 7 |
kubectl logs -n kube-system etcd-backup-s3-jj7zc Defaulted container "uploader" out of: uploader, snapshot (init) Uploading /snapshot/etcd-20250629T115320.db to etcd-snapshots... { "ETag": "\"2b47552c13a06a1d94e5ee8f02948ad4\"" } |
всё норм.
проверяем бакет

всё норм
Резервное копирование etcd в s3-minio (LDAP enabled)


Attached Policies: [etcd-snapshots]
To User: user-etcd
Access Key: O0TQHSCRWF7ZXQXGLUGG
Secret Key: ZBCUUUSl+xmOQRXK4KqczWfUKks6xzXm9sovPPIl
Expiration: no-expiry

|
1 2 3 4 |
kubectl create secret generic s3-credentials \ --from-literal=access-key=O0TQHSCRWF7ZXQXGLUGG \ --from-literal=secret-key=ZBCUUUSl+xmOQRXK4KqczWfUKks6xzXm9sovPPIl \ -n kube-system |
проверяем что cronjob создаётся и работает:
root@kub-master1:~# kubectl create job etcd-backup-s3 --from=cronjob/etcd-backup-to-s3 -n kube-system
|
1 2 3 4 5 6 |
root@kub-master1:~# kubectl logs -n kube-system etcd-backup-s3-vfc6d Defaulted container "uploader" out of: uploader, snapshot (init) Uploading /snapshot/etcd-20250704T081714.db to etcd-snapshots... { "ETag": "\"cae1f28b2b4e13cfb5d2e566dc9df774\"" } |
Cert-manager (self-signed - самоподписанный)
https://github.com/cert-manager/cert-manager/tree/master/deploy/charts/cert-manager
чтобы каждый раз не бегать и руками не создать сертификат и секреты - можно использовать cert-manager
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
resources: requests: cpu: 10m memory: 32Mi limits: cpu: 20m memory: 50Mi webhook: resources: requests: cpu: 10m memory: 32Mi limits: cpu: 20m memory: 50Mi cainjector: resources: requests: cpu: 10m memory: 32Mi limits: cpu: 20m memory: 50Mi |
|
1 2 3 4 5 6 7 8 9 |
apiVersion: cert-manager.io/v1 kind: Issuer metadata: name: ca-selfsign namespace: cert-manager spec: selfSigned: {} |
/etc/ansible/kubespray-official/cert-manager/certificate-root-ca.yaml
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
apiVersion: cert-manager.io/v1 kind: Certificate metadata: name: test-local-root-ca namespace: cert-manager spec: isCA: true commonName: "test.local" secretName: test-local-root-ca-secret duration: 8760h # 1 год renewBefore: 360h # обновлять за 15 дней до окончания issuerRef: name: ca-selfsign kind: Issuer |
/etc/ansible/kubespray-official/cert-manager/issuer-ca.yaml
|
1 2 3 4 5 6 7 8 9 |
apiVersion: cert-manager.io/v1 kind: ClusterIssuer metadata: name: ca-issuer spec: ca: secretName: test-local-root-ca-secret |
/etc/ansible/kubespray-official/cert-manager/certificate-wildcard.yaml
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
apiVersion: cert-manager.io/v1 kind: Certificate metadata: name: wildcard-test-local namespace: cert-manager spec: secretName: wildcard-test-local-tls commonName: "*.test.local" dnsNames: - "*.test.local" - "test.local" # если нужен и корневой домен duration: 720h # 30 дней renewBefore: 168h # за неделю до окончания issuerRef: name: ca-issuer kind: Issuer |
мы будем выписывать самоподписанный сертификат *.test.local
|
1 2 |
annotations: cert-manager.io/cluster-issuer: ca-issuer |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: argo-ingress namespace: argocd annotations: cert-manager.io/cluster-issuer: ca-issuer spec: tls: - hosts: - argo.test.local - www.argo.test.local secretName: argo-tls-secret rules: - host: argo.test.local http: paths: - path: / pathType: Prefix backend: service: name: argo-server port: number: 80 - host: www.argo.test.local http: paths: - path: / pathType: Prefix backend: service: name: argo-server port: number: 80 |
|
1 2 3 |
kubectl get secrets -n argocd NAME TYPE DATA AGE argo-tls-secret kubernetes.io/tls 3 16m |
Argocd
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
global: domain: argocd.test.local configs: params: server.insecure: true server: replicas: 1 ingress: enabled: true ingressClassName: nginx annotations: cert-manager.io/cluster-issuer: ca-issuer nginx.ingress.kubernetes.io/force-ssl-redirect: "true" nginx.ingress.kubernetes.io/backend-protocol: "HTTP" extraTls: - hosts: - argocd.test.local # Based on the ingress controller used secret might be optional secretName: argocd-tls resources: limits: cpu: 100m memory: 128Mi requests: cpu: 50m memory: 64Mi redis-ha: enabled: true haproxy: resources: limits: cpu: 100m memory: 128Mi requests: cpu: 50m memory: 64Mi redis: resources: limits: cpu: 100m memory: 128Mi requests: cpu: 50m memory: 64Mi sentinel: resources: requests: memory: 200Mi cpu: 100m limits: memory: 200Mi cpu: 500m controller: replicas: 1 resources: limits: cpu: 500m memory: 512Mi requests: cpu: 250m memory: 150Mi repoServer: replicas: 2 resources: limits: cpu: 50m memory: 128Mi requests: cpu: 10m memory: 64Mi applicationSet: replicas: 2 resources: limits: cpu: 100m memory: 128Mi requests: cpu: 100m memory: 128Mi dex: resources: limits: cpu: 50m memory: 64Mi requests: cpu: 10m memory: 32Mi |
ждём пока установится, потом смотрим пароль:

Argocd add user
|
1 2 3 4 5 6 7 8 9 10 11 12 |
kubectl patch secret argocd-secret -n argocd --type=json -p="[ { \"op\":\"add\", \"path\":\"/data/accounts.test.password\", \"value\":\"$HASH_B64\" }, { \"op\":\"add\", \"path\":\"/data/accounts.test.passwordMtime\", \"value\":\"$MTIME_B64\" } ]" |
|
1 2 3 4 5 6 |
kubectl patch configmap argocd-cm -n argocd --type=merge -p '{ "data": { "accounts.test":"login", "accounts.test.enabled":"true" } }' |
|
1 2 3 4 5 |
kubectl patch configmap argocd-rbac-cm -n argocd --type=merge -p '{ "data": { "policy.csv": "p, role:readonly, applications, get, *, allow\ng, alice, role:readonly\np, role:admin, *, *, *, allow\ng, test, role:admin\n" } }' |
|
1 2 3 4 5 6 7 8 9 10 11 |
root@kub-master1:~/argocd# kubectl -n argocd get configmap argocd-rbac-cm -o yaml apiVersion: v1 data: policy.csv: | p, role:readonly, applications, get, *, allow g, alice, role:readonly p, role:admin, *, *, *, allow g, test, role:admin policy.default: "" policy.matchMode: glob scopes: '[groups]' |


Argocd интеграция с Freeipa
для начала создаём sysaccount во freeipa
[root@freeipa-1 ]# cd /etc/ipa
[root@freeipa-1 ipa]# bash freeipa-sam.sh
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
### FreeIPA - System Account Manager ### 1.) ldapserver=freeipa-1.test.local 2.) domain=test.local (ldapdomain=dc=test,dc=local) 3.) binduser=uid=admin,cn=users,cn=accounts,dc=test,dc=local 4.) bindpass=SET! 5.) ssl=false Actions (ready): add | rm | ls | info | passwd | save --- Results --- dn: uid=sudo,cn=sysaccounts,cn=etc,dc=test,dc=local dn: uid=nexus,cn=sysaccounts,cn=etc,dc=test,dc=local dn: uid=gitlab,cn=sysaccounts,cn=etc,dc=test,dc=local dn: uid=vault,cn=sysaccounts,cn=etc,dc=test,dc=local dn: uid=s3minio,cn=sysaccounts,cn=etc,dc=test,dc=local dn: uid=k8s-access,cn=sysaccounts,cn=etc,dc=test,dc=local dn: uid=keycloak,cn=sysaccounts,cn=etc,dc=test,dc=local dn: uid=rancher,cn=sysaccounts,cn=etc,dc=test,dc=local dn: uid=argocd,cn=sysaccounts,cn=etc,dc=test,dc=local --- End Results --- |
создаём 2 группы

в админах у меня user1
в read only user2
проверить наличие пользователя в группах мы можем вот такой командой:
ldapsearch -H ldap://192.168.1.100 -D "uid=argocd,cn=sysaccounts,cn=etc,dc=test,dc=local" -W -b "uid=user1,cn=users,cn=accounts,dc=test,dc=local" "(objectClass=*)" memberOf
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
[root@freeipa-1 ipa]# ldapsearch -H ldap://192.168.1.100 -D "uid=argocd,cn=sysaccounts,cn=etc,dc=test,dc=local" -W -b "uid=user1,cn=users,cn=accounts,dc=test,dc=local" "(objectClass=*)" memberOf Enter LDAP Password: # extended LDIF # # LDAPv3 # base <uid=user1,cn=users,cn=accounts,dc=test,dc=local> with scope subtree # filter: (objectClass=*) # requesting: memberOf # # user1, users, accounts, test.local dn: uid=user1,cn=users,cn=accounts,dc=test,dc=local memberOf: cn=ipausers,cn=groups,cn=accounts,dc=test,dc=local memberOf: cn=nexus-admins,cn=groups,cn=accounts,dc=test,dc=local memberOf: cn=vault-admins,cn=groups,cn=accounts,dc=test,dc=local memberOf: cn=s3-minio-admins,cn=groups,cn=accounts,dc=test,dc=local memberOf: cn=k8s-devops,cn=groups,cn=accounts,dc=test,dc=local memberOf: cn=argocd-admin,cn=groups,cn=accounts,dc=test,dc=local # search result search: 2 result: 0 Success # numResponses: 2 # numEntries: 1 |
проверить какие пользователи есть в группе вот такой командой:
ldapsearch -H ldap://192.168.1.100 -D "uid=argocd,cn=sysaccounts,cn=etc,dc=test,dc=local" -W -b "cn=argocd-admin,cn=groups,cn=accounts,dc=test,dc=local" "(objectClass=*)" member
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
[root@freeipa-1 ipa]# ldapsearch -H ldap://192.168.1.100 -D "uid=argocd,cn=sysaccounts,cn=etc,dc=test,dc=local" -W -b "cn=argocd-admin,cn=groups,cn=accounts,dc=test,dc=local" "(objectClass=*)" member Enter LDAP Password: # extended LDIF # # LDAPv3 # base <cn=argocd-admin,cn=groups,cn=accounts,dc=test,dc=local> with scope subtree # filter: (objectClass=*) # requesting: member # # argocd-admin, groups, accounts, test.local dn: cn=argocd-admin,cn=groups,cn=accounts,dc=test,dc=local member: uid=user1,cn=users,cn=accounts,dc=test,dc=local # search result search: 2 result: 0 Success # numResponses: 2 # numEntries: 1 |
пароль для sysaccount будем хранить в secret
/etc/ansible/kubespray-official/argocd/argocd-ldap-secret.yaml
|
1 2 3 4 5 6 7 8 9 10 11 12 |
apiVersion: v1 kind: Secret metadata: name: argocd-ldap-secret namespace: argocd type: Opaque stringData: # bindDN вашего LDAP‑пользователя ldap.bindDN: "uid=argocd,cn=sysaccounts,cn=etc,dc=test,dc=local" # bindPW — здесь указываете ваш пароль ldap.bindPW: "Secret123" |
root@kub-master1:~/argocd# kubectl apply -f argocd-ldap-secret.yaml
вот values
/etc/ansible/kubespray-official/argocd/values-ldap.yaml
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 |
global: domain: argocd.test.local configs: params: server.insecure: true # 1) ConfigMap argocd-cm cm: create: true annotations: {} # по надобности url: https://argocd.test.local dex.config: | connectors: - type: ldap id: freeipa name: FreeIPA config: host: 192.168.1.100:389 insecureNoSSL: true insecureSkipVerify: true bindDN: $LDAP_BIND_DN bindPW: $LDAP_BIND_PW usernamePrompt: Username userSearch: baseDN: "cn=users,cn=accounts,dc=test,dc=local" filter: "(&(objectClass=posixAccount)(|(memberOf=cn=argocd-admin,cn=groups,cn=accounts,dc=test,dc=local)(memberOf=cn=argocd-ro-users,cn=groups,cn=accounts,dc=test,dc=local)))" username: uid idAttr: uid emailAttr: uid nameAttr: displayName groupSearch: baseDN: cn=groups,cn=accounts,dc=test,dc=local filter: "(objectClass=groupOfNames)" userMatchers: - userAttr: DN groupAttr: member nameAttr: cn rbac: create: true policy.csv: | p, role:admin, applications, *, *, allow g, "argocd-admin", role:admin p, role:readonly, applications, get, *, allow g, "argocd-ro-users", role:readonly policy.default: "" policy.matchMode: glob scopes: '[groups]' server: replicas: 1 ingress: enabled: true ingressClassName: nginx annotations: cert-manager.io/cluster-issuer: ca-issuer nginx.ingress.kubernetes.io/force-ssl-redirect: "true" nginx.ingress.kubernetes.io/backend-protocol: "HTTP" extraTls: - hosts: - argocd.test.local # Based on the ingress controller used secret might be optional secretName: argocd-tls resources: limits: cpu: 100m memory: 128Mi requests: cpu: 50m memory: 64Mi redis-ha: enabled: true haproxy: resources: limits: cpu: 100m memory: 128Mi requests: cpu: 50m memory: 64Mi redis: resources: limits: cpu: 100m memory: 128Mi requests: cpu: 50m memory: 64Mi sentinel: resources: requests: memory: 200Mi cpu: 100m limits: memory: 200Mi cpu: 500m controller: replicas: 1 resources: limits: cpu: 500m memory: 512Mi requests: cpu: 250m memory: 150Mi repoServer: replicas: 2 resources: limits: cpu: 50m memory: 128Mi requests: cpu: 10m memory: 64Mi applicationSet: replicas: 2 resources: limits: cpu: 100m memory: 128Mi requests: cpu: 100m memory: 128Mi dex: resources: limits: cpu: 50m memory: 64Mi requests: cpu: 10m memory: 32Mi env: - name: LDAP_BIND_DN valueFrom: secretKeyRef: name: argocd-ldap-secret key: ldap.bindDN - name: LDAP_BIND_PW valueFrom: secretKeyRef: name: argocd-ldap-secret key: ldap.bindPW |
тут:
host: 192.168.1.100:389 адрес freeipa сервера
bindDN: $LDAP_BIND_DN логин из секрета
bindPW: $LDAP_BIND_PW пароль из секрета
filter: "(&(objectClass=posixAccount)(|(memberOf=cn=argocd-admin,cn=groups,cn=accounts,dc=test,dc=local)(memberOf=cn=argocd-ro-users,cn=groups,cn=accounts,dc=test,dc=local)))" ограничиваем доступ 2мя группами argocd-admin и argocd-ro-users
|
1 2 3 4 5 |
policy.csv: | p, role:admin, applications, *, *, allow g, "argocd-admin", role:admin p, role:readonly, applications, get, *, allow g, "argocd-ro-users", role:readonly |
а тут выдаём rbac доступы для групп
dex:
env:
- name: LDAP_BIND_DN
проверяем:

как видим пользователь user3 не имеет доступа
проверим user2

как видим мы можем зайти но ничего создать не можем
проверим теперь user1

можем увидеть в каких группах состоит этот пользователь
создадим группу:

как видим всё ок - успешно создаётся, прав хватает.
Argocd создание проекта, настройка деплоя
в моей репке
https://gitlab.test.local/argo/test-app/app1.git
есть только 1 файл:
deployment.yaml
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment spec: replicas: 2 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:latest resources: requests: cpu: "50m" memory: "64Mi" limits: cpu: "100m" memory: "128Mi" livenessProbe: httpGet: path: / port: 80 initialDelaySeconds: 15 periodSeconds: 20 failureThreshold: 3 readinessProbe: httpGet: path: / port: 80 initialDelaySeconds: 5 periodSeconds: 10 failureThreshold: 3 |
которые запускает деплоймент в 2х репликах с минимальными ресурсами и liveness readiness пробами.
создаём новый проект

добавляем целевой кластер

указываем что проекту будет разрешено

для подключения репозитория будем использовать access token
токен делается на уровне группы:

создаю argo-token из доступов хватает read_repository но я ещё тыкнул на api лень было пересоздавать оставил так.
после создания будет показан пароль который показывается только 1 раз, сохраните его

возвращаемся в argocd


заполняем все указанные поля, и не забываем включить Skip server verification
кстати подключить репозиторий можно командой:
|
1 2 3 4 |
argocd@argo-cd-argocd-server-6c68fc4594-xhjn4:~$ argocd repo add https://gitlab.test.local/argo/test-app/app1.git \ --username argo-token \ --password glpat-SshzATU-yGpnWQF9_EtA \ --insecure-skip-server-verification |
как видим репозиторий подключен

теперь создадим application который будет автоматически синхронизироваться

выбираем наш репозиторий указываем что по branch должен синкаться, можно ещё выбрать по тэгам, и в path ставим точку (если прям в корне лежат файлы которые нужно аплаить, а если в какой то другой директории то указываем до неё путь, можно ещё включить и рекурсивную проходу по всем вложенными директориям в path)

сохраняем и получаем:

как видим сразу пошёл процесс синхронизации, так как мы указали Sync Policy автоматически.
как видим всё ок, и оба наших pod задеплоились

проверим:
|
1 2 3 4 |
root@kub-master1:~# kubectl get pod -n test NAME READY STATUS RESTARTS AGE nginx-deployment-fd447d4f5-nlkzv 1/1 Running 0 4m10s nginx-deployment-fd447d4f5-x74w4 1/1 Running 0 4m10s |
мы можем скейлить наш деплоймент в любое количество - argocd будет возвращать к исходному в 2 реплики.
для примера выключу SELF HEAL и деплоймент заскейлю в 1.
выбираю апку

прометываем ниже:

выключаем SELF HEAL

приводим к такому виду:

скейлим апку вниз:
|
1 2 3 4 5 6 |
root@kub-master1:~# kubectl scale deployment -n test nginx-deployment --replicas 1 deployment.apps/nginx-deployment scaled root@kub-master1:~# kubectl get pod -n test NAME READY STATUS RESTARTS AGE nginx-deployment-fd447d4f5-x74w4 1/1 Running 0 9m27s |
как видим только 1 pod
проверяем:

как видим SYNC STATUS = OutOfSync
если нажать на OutOfSync откроется diff

который покажет различие:

для восстановления может нажать sync

как видим всё ок - синканулось
Argocd создание проекта, настройка деплоя Helm
Теперь с помощью argocd установим апку которая использует helm.
у нас есть репка
https://gitlab.test.local/argo/test-helm/app1.git
я создал дефолтный chart
helm create helm-app1
и пушнул его в gitlab repo

добавляем этот репозиторий в argocd

напоминаю что для моего проекта project-dev можно деплоить в любой неймспейс

теперь создадим application

будем автоматически ставить без ручной синхронизации + включим создание namespace

указываем нашу репку имя неймспейса test2 и путь где лежит чарт

дальше появляется меню где можем выставить настройки для values которые НЕ закомменчены

как видим всё ок.

Argocd создание проекта из конфиг файла
Всё ок, мы натыкали application в панели, но есть проблема если мы удалим его в панели то с включённым финалайзером удалится и апка и потом надо будет вручную добавлять всё.
поэтому нужно хранить апликейшены в конфигах.
вытащить конфиг файл можем так:
|
1 2 3 4 5 6 7 8 9 10 |
kubectl get applications.argoproj.io app1 -n argocd -o json \ | yq -y 'del( .status, .metadata.managedFields, .metadata.annotations."kubectl.kubernetes.io/last-applied-configuration", .metadata.creationTimestamp, .metadata.resourceVersion, .metadata.uid, .metadata.generation )' |
конфиг получаем следующий:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
apiVersion: argoproj.io/v1alpha1 kind: Application metadata: finalizers: - resources-finalizer.argocd.argoproj.io name: app1 namespace: argocd spec: destination: namespace: test server: https://kubernetes.default.svc project: project-dev source: path: . repoURL: https://gitlab.test.local/argo/test-app/app1.git targetRevision: HEAD syncPolicy: automated: prune: true selfHeal: true |
helm app вот такой:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
apiVersion: argoproj.io/v1alpha1 kind: Application metadata: finalizers: - resources-finalizer.argocd.argoproj.io name: helm-app1 namespace: argocd spec: destination: namespace: test2 server: https://kubernetes.default.svc project: project-dev source: path: helm-app1 repoURL: https://gitlab.test.local/argo/test-helm/app1.git targetRevision: HEAD syncPolicy: automated: prune: true selfHeal: true syncOptions: - CreateNamespace=true |
сложим эти конфиги в репку
https://gitlab.test.local/argo/argo-config.git
файлы будут лежать тут:
argo-config/app1/app1.yaml
argo-config/helm-app1/helm-app1.yaml
далее удаляем наши applications

настроем теперь репку, так же как и остальные

теперь настроим application


обязательно отмечаем DIRECTORY RECURSE

проверяем что всё ок:

как видим ок - мы запускаем наши applications с помощью другого application
Argocd создание проекта. настройка деплоя Helm когда values и chart в разных репозиториях
у нас есть репка где лежит основной helm chart:
https://gitlab.test.local/argo/test-helm/app1.git
а вот в эту репку:
https://gitlab.test.local/argo/test-helm/app2.git
положим только values
helm/values.yaml
|
1 2 3 4 5 6 |
replicaCount: 2 nameOverride: "" fullnameOverride: "second-app" |
добавляем репку

дальше есть проблема так как UI у ArgoCD не умеет добавлять к application несколько репозиториев, мы будем настраивать это через конфиг test123.yaml
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
apiVersion: argoproj.io/v1alpha1 kind: Application metadata: name: test123 namespace: argocd spec: project: project-dev destination: server: https://kubernetes.default.svc namespace: test123 syncPolicy: automated: prune: true selfHeal: true syncOptions: - CreateNamespace=true sources: # 1) сам Helm‑чарт - repoURL: https://gitlab.test.local/argo/test-helm/app1.git targetRevision: HEAD path: helm-app1 helm: valueFiles: # -- здесь мы ссылаемся на внешний values-файл - $valuesRepo/helm/values.yaml # 2) репо только для values (без `path`) - repoURL: https://gitlab.test.local/argo/test-helm/app2.git targetRevision: HEAD ref: valuesRepo |
применим его
kubectl apply -f test123.yaml
проверяем:

как видим при такой конфигурации у нас появилась дополнительная вкладка SOURCES которой не было при запуске из панели

по сути всё, готово. у нас есть основной репозиторий в котором находится helm chart и у нас может быть множество других репозиториев в которых может быть только values.
всё разруливается на уровне конфига в sources
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
sources: # 1) сам Helm‑чарт - repoURL: https://gitlab.test.local/argo/test-helm/app1.git targetRevision: HEAD path: helm-app1 helm: valueFiles: # -- здесь мы ссылаемся на внешний values-файл - $valuesRepo/helm/values.yaml # 2) репо только для values (без `path`) - repoURL: https://gitlab.test.local/argo/test-helm/app2.git targetRevision: HEAD ref: valuesRepo |
вот официальная дока:
https://argo-cd.readthedocs.io/en/latest/user-guide/multiple_sources/
Keda
кеда позволяет скейлить приложения на основе разных метрик из promethtus или victoria-metrics
офф чарт
https://github.com/kedacore/charts/tree/main/keda
|
1 2 3 |
root@kub-master1:~/keda/example# kubectl get pod -n app1 NAME READY STATUS RESTARTS AGE app1-test-app-keda-65cbf4f4c7-c5zcx 1/1 Running 0 14s |
создаём тестовый домен
|
1 2 3 |
root@kub-master1:~/keda/example# kubectl get ingress -n app1 NAME CLASS HOSTS ADDRESS PORTS AGE app1-test-app-keda nginx test.test.local 192.168.1.191 80 69s |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
apiVersion: keda.sh/v1alpha1 kind: ScaledObject metadata: name: v2-ingress-requests namespace: app1 spec: scaleTargetRef: kind: Deployment name: app1-test-app-keda pollingInterval: 15 cooldownPeriod: 30 minReplicaCount: 1 maxReplicaCount: 5 triggers: - type: prometheus metadata: serverAddress: http://vmsingle-vmks-victoria-metrics-k8s-stack.monitoring.svc.cluster.local:8428 metricName: ingress_requests query: >- sum( increase( nginx_ingress_controller_requests{ingress="app1-test-app-keda"}[2m] ) ) threshold: "100" |
он будет смотреть в викторию и в случае если за 2 минуты будет больше 100 запросов он будет скейлить апку вверх
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
root@kub-master1:~# kubectl get hpa -n app1 -w NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE keda-hpa-v2-ingress-requests Deployment/app1-test-app-keda 0/100 (avg) 1 5 1 13m keda-hpa-v2-ingress-requests Deployment/app1-test-app-keda 797/100 (avg) 1 5 1 14m keda-hpa-v2-ingress-requests Deployment/app1-test-app-keda 590500m/100 (avg) 1 5 4 15m root@kub-master1:~# kubectl get pod -n app1 NAME READY STATUS RESTARTS AGE app1-test-app-keda-685c6c455c-mw2nf 1/1 Running 0 38s app1-test-app-keda-685c6c455c-mzn27 1/1 Running 0 38s app1-test-app-keda-685c6c455c-rgf2h 1/1 Running 0 23s app1-test-app-keda-685c6c455c-wqhcg 1/1 Running 0 39m app1-test-app-keda-685c6c455c-wrt7s 1/1 Running 0 38s |
|
1 2 3 4 5 |
keda-hpa-v2-ingress-requests Deployment/app1-test-app-keda 9/100 (avg) 1 5 2 30m keda-hpa-v2-ingress-requests Deployment/app1-test-app-keda 2/100 (avg) 1 5 2 30m keda-hpa-v2-ingress-requests Deployment/app1-test-app-keda 0/100 (avg) 1 5 2 31m keda-hpa-v2-ingress-requests Deployment/app1-test-app-keda 0/100 (avg) 1 5 2 34m keda-hpa-v2-ingress-requests Deployment/app1-test-app-keda 0/100 (avg) 1 5 1 34m |
Patrony
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
--- # Default variables for Patroni role patroni_cluster_name: "my_patroni_cluster" replication_password: replication_password superuser_password: superuser_password haproxy_port: 5432 # PostgreSQL listener port postgresql_port: 5430 # Subnet for replication in pg_hba pg_hba_subnet: 192.168.1.0/24 # Patroni REST API port patroni_restapi_port: 8008 # Watchdog mode: off | automatic | required watchdog_mode: "off" # Major version of PostgreSQL postgresql_major_version: 17 # Virtual IP managed by Keepalived / HAProxy binds here virtual_ip: 192.168.1.155 # Keepalived instance ID per node keepalived_id: 1 |
тут мы можем задать порты для postgresql/haproxy/patroni
подсеть в которой будет расположен кластер
версия postgres
виртуальный IP
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
--- - name: restart ntpd service: name=ntpd state=restarted enabled=yes - name: restart keepalived service: name=keepalived state=restarted enabled=yes - name: restart haproxy service: name=haproxy state=restarted enabled=yes - name: restart etcd service: name=etcd state=restarted enabled=yes - name: Restart Patroni become: true service: name: patroni state: restarted enabled: yes |
/etc/ansible/roles/patroni/tasks/install_etcd.yml
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
- name: Install etcd and dependencies on Debian/Ubuntu apt: name: - etcd-server - etcd-client - gcc - python3-dev - python3-requests - python3-urllib3 state: present update_cache: yes when: ansible_os_family == 'Debian' - name: Install etcd and dependencies on RedHat yum: name: [ 'gcc', 'python-devel', 'etcd', 'python-requests', 'python-urllib3' ] state: present when: ansible_os_family == 'RedHat' ################################ configure etcd ################################ - name: set etcd node name set_fact: etcd_check: "{{ inventory_hostname }}" - name: ensure etcd configuration directory exists on RedHat file: path: /etc/etcd state: directory owner: root group: root mode: '0755' when: - ansible_os_family == 'RedHat' - name: copy etcd configuration template: src: ../templates/etcd.conf dest: /etc/default/etcd backup: yes notify: - restart etcd register: etcd_conf - name: Перезапустить etcd, если конфиг изменился service: name: etcd state: restarted enabled: yes when: - etcd_conf is changed - name: Wait for ETCD to accept connections wait_for: host: "{{ ansible_host }}" port: 2379 state: started delay: 5 timeout: 60 when: inventory_hostname in groups['etcd'] |
/etc/ansible/roles/patroni/tasks/install_haproxy.yml
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
--- # Install HAProxy on both Debian/Ubuntu and RedHat systems. HAProxy is used # as a TCP load balancer for the Patroni-managed PostgreSQL cluster. We # differentiate package managers based on the OS family. - name: Install haproxy on Debian/Ubuntu apt: name: haproxy state: present update_cache: yes notify: - restart haproxy when: ansible_os_family == 'Debian' - name: Install haproxy on RedHat yum: name: haproxy state: present notify: - restart haproxy when: ansible_os_family == 'RedHat' - name: Render HAProxy configuration become: true template: src: haproxy.cfg.j2 dest: /etc/haproxy/haproxy.cfg owner: root group: root mode: '0644' notify: restart haproxy |
/etc/ansible/roles/patroni/tasks/install_keepalived.yml
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
--- # Install Keepalived, which provides VRRP-based failover between HAProxy instances. - name: Install keepalived on Debian/Ubuntu apt: name: keepalived state: present update_cache: yes notify: - restart keepalived when: ansible_os_family == 'Debian' - name: Install keepalived on RedHat yum: name: keepalived state: present notify: - restart keepalived when: ansible_os_family == 'RedHat' - name: put keepalived service version 2 copy: src: ../templates/keepalived dest: /usr/sbin/ backup: yes # Only overwrite the keepalived binary on RedHat-based systems. when: ansible_os_family == 'RedHat' - name: assign keepalived role # Determine whether the current host should act as MASTER or BACKUP. # The first host in the `patroni` group is elected MASTER; all others become BACKUP. set_fact: keepalived_check: "{{ 'MASTER' if inventory_hostname == groups['patroni'][0] else 'BACKUP' }}" keepalived_priority_check: "{{ 100 if inventory_hostname == groups['patroni'][0] else 90 }}" - name: put keepalived_config template: src: ../templates/keepalived.conf dest: /etc/keepalived/keepalived.conf backup: yes notify: - restart keepalived |
/etc/ansible/roles/patroni/tasks/install_patroni.yml
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
- name: install pip on Debian/Ubuntu apt: name: python3-pip state: present update_cache: yes environment: # Разрешаем pip «ломать» системные пакеты (PEP 668) PIP_BREAK_SYSTEM_PACKAGES: '1' when: ansible_os_family == 'Debian' - name: install pip on RedHat yum: name: python-pip state: present environment: PIP_BREAK_SYSTEM_PACKAGES: '1' when: ansible_os_family == 'RedHat' - name: set pip executable fact set_fact: pip_executable: "{{ 'pip' if ansible_os_family == 'RedHat' else 'pip3' }}" - name: upgrade pip to latest pip: name: pip executable: "{{ pip_executable }}" state: latest environment: PIP_BREAK_SYSTEM_PACKAGES: '1' - name: install setuptools pip: name: setuptools executable: "{{ pip_executable }}" environment: PIP_BREAK_SYSTEM_PACKAGES: '1' - name: upgrade setuptools pip: name: setuptools extra_args: --upgrade executable: "{{ pip_executable }}" environment: PIP_BREAK_SYSTEM_PACKAGES: '1' - name: install python packages flake8 pip: name: "flake8" executable: "{{ pip_executable }}" environment: PIP_BREAK_SYSTEM_PACKAGES: '1' - name: install python packages psycopg2-binary pip: name: "psycopg2-binary" executable: "{{ pip_executable }}" environment: PIP_BREAK_SYSTEM_PACKAGES: '1' - name: install python packages urllib3 patroni[etcd] pip: name: - 'urllib3>=1.19.1' - 'patroni[etcd]' - 'cryptography' - 'certifi' - 'idna' - 'pyOpenSSL' executable: "{{ pip_executable }}" environment: PIP_BREAK_SYSTEM_PACKAGES: '1' - name: Render Patroni configuration become: true template: src: patroni.yml.j2 dest: /etc/patroni.yml owner: postgres group: postgres mode: '0644' - name: put patroni.service systemd unit template: src: ../templates/patroni.service dest: /etc/systemd/system/patroni.service backup: yes register: patroni_service - name: Reload daemon definitions command: /usr/bin/systemctl daemon-reload tags: patroni when: - patroni_service is changed - name: Start Patroni on primary node service: name: patroni state: started enabled: yes when: inventory_hostname == groups['patroni'][0] - name: Wait for primary Postgres to accept connections wait_for: host: "{{ ansible_host }}" port: "{{ postgresql_port }}" state: started delay: 5 timeout: 60 when: inventory_hostname == groups['patroni'][0] # 2) Then start Patroni on the replica nodes - name: Start Patroni on replica nodes service: name: patroni state: started enabled: yes when: inventory_hostname != groups['patroni'][0] # 3) Finally, ensure Patroni is running everywhere - name: Ensure Patroni is running service: name: patroni state: started enabled: yes when: inventory_hostname in groups['patroni'] |
/etc/ansible/roles/patroni/tasks/install_postgres.yml
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
- name: install PGDG repository on RedHat/CentOS yum: name: https://download.postgresql.org/pub/repos/yum/pgdg-redhat-repo-latest.noarch.rpm state: present when: ansible_os_family == 'RedHat' - name: Install PostgreSQL {{ postgresql_major_version }} and dependencies on RedHat/CentOS" yum: name: "{{ packages }}" state: present vars: packages: - "postgresql{{ postgresql_major_version }}" - "postgresql{{ postgresql_major_version }}-contrib" - "postgresql{{ postgresql_major_version }}-server" - python-psycopg2 - "repmgr{{ postgresql_major_version }}" when: ansible_os_family == 'RedHat' - name: Stop and disable default PostgreSQL on RedHat/CentOS systemd: name: postgresql-{{ postgresql_major_version }} state: stopped enabled: no masked: yes # чтобы PostgreSQL гарантированно не запустился сам when: ansible_os_family == "RedHat" ########################################################## debian ########################################################## - name: Install repository prerequisites on Debian/Ubuntu apt: name: - curl - ca-certificates - gnupg - lsb-release state: present update_cache: yes when: ansible_os_family == 'Debian' - name: Add PostgreSQL apt repository key on Debian/Ubuntu apt_key: url: https://www.postgresql.org/media/keys/ACCC4CF8.asc state: present when: ansible_os_family == 'Debian' - name: Add PostgreSQL apt repository on Debian/Ubuntu apt_repository: repo: "deb https://apt.postgresql.org/pub/repos/apt {{ ansible_distribution_release }}-pgdg main" state: present filename: "pgdg" when: ansible_os_family == 'Debian' - name: Update apt cache after adding PGDG repository on Debian/Ubuntu apt: update_cache: yes when: ansible_os_family == 'Debian' - name: Install PostgreSQL {{ postgresql_major_version }} on Debian/Ubuntu apt: name: - "postgresql-{{ postgresql_major_version }}" - "postgresql-client-{{ postgresql_major_version }}" - postgresql-contrib - python3-psycopg2 state: present when: ansible_os_family == 'Debian' - name: Stop and disable default PostgreSQL (Debian/Ubuntu) systemd: name: postgresql state: stopped enabled: no masked: yes # чтобы PostgreSQL гарантированно не запустился сам when: ansible_os_family == "Debian" |
/etc/ansible/roles/patroni/tasks/ntp.yml
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
- name: update time command: "ntpdate 0.debian.pool.ntp.org" when: ansible_distribution in ['Debian', 'Ubuntu'] - name: ntpd stop service: name: ntpd state: stopped enabled: yes when: ansible_distribution in ['RedHat', 'CentOS'] - name: update time command: "ntpd -gq" when: ansible_distribution in ['RedHat', 'CentOS'] - name: ntpd start service: name: ntpd state: started enabled: yes when: ansible_distribution in ['RedHat', 'CentOS'] |
/etc/ansible/roles/patroni/tasks/main.yml
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
--- - import_tasks: ntp.yml tags: update time - import_tasks: install_etcd.yml tags: install etcd package when: inventory_hostname in groups['etcd'] - import_tasks: install_postgres.yml tags: install postgres package when: - inventory_hostname in groups['patroni'] - import_tasks: install_patroni.yml tags: install patroni when: - inventory_hostname in groups['patroni'] - import_tasks: install_haproxy.yml tags: install haproxy when: inventory_hostname in groups['patroni'] - import_tasks: install_keepalived.yml tags: install keepalived when: inventory_hostname in groups['patroni'] |
/etc/ansible/roles/patroni/templates/etcd.conf
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
ETCD_NAME="{{ etcd_check }}" ETCD_LISTEN_CLIENT_URLS="http://0.0.0.0:2379" ETCD_ADVERTISE_CLIENT_URLS="http://{{ ansible_default_ipv4.address }}:2379" ETCD_LISTEN_PEER_URLS="http://0.0.0.0:2380" ETCD_INITIAL_ADVERTISE_PEER_URLS="http://{{ ansible_default_ipv4.address }}:2380" ETCD_INITIAL_CLUSTER_TOKEN="etcdPatroniCluster" ETCD_INITIAL_CLUSTER="{% for host in groups['etcd'] | sort %}{{ host }}=http://{{ hostvars[host]['ansible_host'] }}:2380{% if not loop.last %},{% endif %}{% endfor %}" ETCD_INITIAL_CLUSTER_STATE="new" ETCD_DATA_DIR="/var/lib/etcd" ETCD_ELECTION_TIMEOUT="5000" ETCD_HEARTBEAT_INTERVAL="1000" ETCD_ENABLE_V2="true" |
/etc/ansible/roles/patroni/templates/haproxy.cfg.j2
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
global log /dev/log local0 maxconn 4096 user haproxy group haproxy defaults log global mode tcp option tcplog retries 3 timeout connect 5s timeout client 60s timeout server 60s frontend postgres bind *:{{ haproxy_port }} default_backend postgres_backend backend postgres_backend mode tcp balance roundrobin # шлём только строку запроса option httpchk GET /master HTTP/1.1 # отдельно даём заголовок Host http-check send hdr Host localhost\r\n # проверяем, что код 200 http-check expect status 200 {% for host in groups['patroni'] %} server {{ host }} {{ hostvars[host].ansible_host }}:{{ postgresql_port }} check port {{ patroni_restapi_port }} inter 3s rise 2 fall 2 {% endfor %} |
/etc/ansible/roles/patroni/templates/keepalived.conf
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
! Configuration File for keepalived global_defs { script_user root script_security on !notification_email { ! an@test.ru !} !notification_email_from keepalived@{{ ansible_hostname }}.test.ru ! smtp_server 192.168.1.56 ! smtp_connect_timeout 30 } # 1) Проверяем haproxy vrrp_script chk_haproxy { script "/usr/bin/pgrep haproxy" interval 2 fall 2 rise 1 weight 20 # при успехе добавляем +20 к priority } vrrp_instance VI_1 { state {{ keepalived_check }} # Use the primary network interface detected by Ansible. This avoids # hard‑coding interface names like enp0s3 and makes the configuration # portable across different distributions and cloud providers. interface {{ ansible_default_ipv4.interface }} garp_master_refresh 15 virtual_router_id {{ keepalived_id }} priority {{ keepalived_priority_check }} advert_int 5 smtp_alert authentication { auth_type PASS auth_pass 111werfgfgqwer3gfh567 } virtual_ipaddress { {{ virtual_ip }} dev {{ ansible_default_ipv4.interface }} label {{ ansible_default_ipv4.interface }}:vip } track_script { chk_haproxy } } |
/etc/ansible/roles/patroni/templates/patroni.service
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
[Unit] Description=Patroni PostgreSQL High-Availability After=syslog.target network.target [Service] Type=simple User=postgres Group=postgres ExecStart=/usr/local/bin/patroni /etc/patroni.yml ExecReload=/bin/kill -s HUP $MAINPID KillMode=process TimeoutSec=30 Restart=on-failure [Install] WantedBy=multi-user.target |
/etc/ansible/roles/patroni/templates/patroni.yml.j2
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
--- scope: "{{ patroni_cluster_name }}" name: "{{ inventory_hostname }}" watchdog: mode: "{{ watchdog_mode }}" restapi: listen: "0.0.0.0:{{ patroni_restapi_port }}" connect_address: "{{ ansible_host }}:{{ patroni_restapi_port }}" etcd: hosts: {% for host in groups['etcd'] | sort %} - "{{ hostvars[host]['ansible_host'] | default(host) }}:2379" {% endfor %} protocol: http use_proxies: false bootstrap: dcs: ttl: 30 loop_wait: 10 retry_timeout: 10 maximum_lag_on_failover: 1048576 postgresql: use_pg_rewind: true use_slots: true parameters: archive_mode: "on" wal_level: hot_standby max_wal_senders: 10 wal_keep_segments: 8 archive_timeout: "1800s" max_replication_slots: 5 hot_standby: "on" wal_log_hints: "on" initdb: - encoding: UTF8 - data-checksums pg_hba: - local all postgres peer - host replication repl {{ pg_hba_subnet }} md5 - host replication repl 127.0.0.1/32 trust - host all all 0.0.0.0/0 md5 authentication: replication: username: repl password: "{{ replication_password }}" superuser: username: postgres password: "{{ superuser_password }}" postgresql: pgpass: /var/lib/postgresql/{{ postgresql_major_version }}/.pgpass listen: "0.0.0.0:{{ postgresql_port }}" connect_address: "{{ ansible_host }}:{{ postgresql_port }}" data_dir: "/var/lib/postgresql/{{ postgresql_major_version }}/data/" bin_dir: "/usr/lib/postgresql/{{ postgresql_major_version }}/bin/" create_replica_methods: - basebackup use_unix_socket: true parameters: unix_socket_directories: '/var/run/postgresql' pg_rewind: username: postgres password: "{{ superuser_password }}" pg_hba: - local all postgres peer - host replication repl {{ pg_hba_subnet }} md5 - host replication repl 127.0.0.1/32 trust - host all all 0.0.0.0/0 md5 replication: username: repl password: "{{ replication_password }}" superuser: username: postgres password: "{{ superuser_password }}" |
/etc/ansible/playbooks/roles_play/patroni.yml
|
1 2 3 4 5 6 |
- name: Deploy highly available PostgreSQL cluster hosts: patronicluster become: yes roles: - patroni |
/etc/ansible/hosts
|
1 2 3 4 5 6 7 8 9 10 11 12 |
[patronicluster:children] etcd patroni [etcd] etcd1 ansible_host=192.168.1.152 etcd2 ansible_host=192.168.1.153 etcd3 ansible_host=192.168.1.154 [patroni] db1 ansible_host=192.168.1.152 db2 ansible_host=192.168.1.153 # db3 ansible_host=10.0.0.13 # если добавите ещё реплики |
установку запускаем так:
root@ansible:/etc/ansible# ansible-playbook playbooks/roles_play/patroni.yml --ask-pass
|
1 2 3 4 5 6 7 |
root@debian:~# patronictl -c /etc/patroni.yml list + Cluster: my_patroni_cluster (7534615147125032841) +----+-----------+ | Member | Host | Role | State | TL | Lag in MB | +--------+--------------------+---------+-----------+----+-----------+ | db1 | 192.168.1.152:5430 | Leader | running | 9 | | | db2 | 192.168.1.153:5430 | Replica | streaming | 9 | 0 | +--------+--------------------+---------+-----------+----+-----------+ |
|
1 2 3 4 |
patronictl -c /etc/patroni.yml switchover \ --leader db2 \ --candidate db1 \ --force |
в интерактивном режиме команда
patronictl -c /etc/patroni.yml failover
он там сам предложит варианты
Helm-chart
Helm - это менеджер пакетов для Kubernetes. Этот инструмент позволяет нам обернуть Kubernetes приложения в удобные пакеты, называемые чартами, которые можно легко развертывать, обновлять и управлять ими в любой момент времени.
Чарты – это пакеты, которые могут включать в себя все для запуска приложения в Kubernetes, от deployments до services. Все это дает возможность работать с приложениями как с единой сущностью, а не как с набором отдельных ресурсов, которые еще и в ручную нужно настраивать…
Так же Helm упрощает управление зависимостями между приложениями, позволяет легко параметризировать настройки приложений через файлы values.yaml и дает возможность повторного использования чартов с помощью шаблонизации.
Создание дефолтного чарта
|
1 2 3 4 5 6 7 |
root@kub-master1:~/helm-charts/1_default# kubectl get pod -n app NAME READY STATUS RESTARTS AGE app-common-chart-749cf6bc54-h5jnn 1/1 Running 0 8s root@kub-master1:~/helm-charts/1_default# helm list -n app NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION app app 1 2025-08-07 12:55:32.737050736 +0600 +06 deployed common-chart-0.1.0 1.16.0 |
|
1 2 3 4 5 |
root@kub-master1:~/helm-charts/1_default# helm upgrade --install app -n app ./common-chart/ --values ./common-chart/values.yaml root@kub-master1:~/helm-charts/1_default# kubectl get pod -n app NAME READY STATUS RESTARTS AGE app-5d588f6bc8-qvwqz 1/1 Running 0 4s |
как видим имя сменилось.
Добавление секретов из vault
настроим подключение vault secret к нашему чарту
не забудем что у нас должен быть установлен
"Vault Secrets Operator"
ну настроем ещё разок
https://github.com/hashicorp/vault-secrets-operator/blob/main/chart/Chart.yaml
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
root@kub-master1:~/vault-autounseal# cat vault-secrets-operator.yaml controller: replicas: 2 affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: app operator: In values: - vault-secrets-operator topologyKey: "kubernetes.io/hostname" kubeRbacProxy: resources: limits: cpu: 150m memory: 150Mi requests: cpu: 50m memory: 100Mi manager: resources: limits: cpu: 150m memory: 150Mi requests: cpu: 50m memory: 100Mi defaultVaultConnection: enabled: true skipTLSVerify: true address: "https://192.168.1.111:8200" |
дальше нам надо настроить интеграцию с нашим vault кластером, который на виртуалках, для этого нам понадобится сертификат поэтому выполняем команду:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
root@kub-master1:~# kubectl get cm kube-root-ca.crt -o jsonpath="{['data']['ca\.crt']}" -----BEGIN CERTIFICATE----- MIIC/jCCAeagAwIBAgIBADANBgkqhkiG9w0BAQsFADAVMRMwEQYDVQQDEwprdWJl cm5ldGVzMB4XDTI1MDUyMTA2NTMwOFoXDTM1MDUxOTA2NTMwOFowFTETMBEGA1UE AxMKa3ViZXJuZXRlczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOK8 T4rAvr9V4TcXsOfuuT0Jew14EgAekHNHOuqnkb9E2O9dTMfcAZK+jHTJx7NNFtHd JxcVQeoQGDQxLI21PW50cr48Tw24tvpYEWNeJJnMeo59kcn00knegAgIP5EqyTfj FIuozkQWXtT0BE0791nRJt82U6wh35pxN8UJA31in7Qo5Ga/VV9yp7F4yy4ZeLTc BVYssK1vdHWd8mb43pGqZs4vSP16oVr4JLJMImlqTuZQqJs6eNHhuBOR1Sxq5gWe TQlfSmSMRAg5ctmYmgG8QvW6phkHMH5DaBz1ZKlpHy0bGtEDoWqShSrdo08+3GNH ATRVh6rFGX7vI6ATOZcCAwEAAaNZMFcwDgYDVR0PAQH/BAQDAgKkMA8GA1UdEwEB /wQFMAMBAf8wHQYDVR0OBBYEFLIi6aXVZDS6deO1oMXEPzX0gc/bMBUGA1UdEQQO MAyCCmt1YmVybmV0ZXMwDQYJKoZIhvcNAQELBQADggEBANVO3epULTmHP09zQxkM R984HWpqYSpCd+zho3fzIpXfWgGbk8Kx6l2sBw3PKQB9MA9y/cE5t8mKTkZcO2sE gtpCLRC+TA3652HrSWyHgbCGPh9+SZvctLkPeZSz8Z22D6Nq1rZ5lf8RXsbm9vCw lVtS/K3LRsuk/mO7ZTLPQ2Fp8pkkQ00pW42Y8BifFSrzPewc6LO9jSxe2Ulbthwt oFj02IxKY4PSmcORZ5qAcYz9DbowjzXxmHZOUZTIQaEruVtqObbFy5BJFsfTYhsj lILMas+xb5Oibpx2R+LT6L1jm4kT/GkQoi1PnFr8wAB5Nhs/INC1B94AolonlX/A i5o= -----END CERTIFICATE----- |
|
1 2 |
root@kub-master1:~# kubectl create token vault-auth -n kube-system --duration 876000h eyJhbGciOiJSUzI1NiIsImtpZCI6Im8wVXVCeFB2ZHpscW1IcWpOeUh6M0NHbjRDcW8xOVhicm1XVHF5VUx2blUifQ..X3DXNViu8lumFeEwF13ESyQpXCDGvrK9cVtMd_DIE-EZteeHmfyyQi7D_KO95NpTYLbeLzDhQFV9v9PUbMxBaXtLMkBrJMpcx5txMXYYdQrJHRR6Ft7jXPHdCI73HEPfpbcnzfLoyFloISWzDP8gd62F5WMMGEi18ubIBWLNIn04X8T7bpPSTUdrFOnJPwu5rtvp15yYMLcgrzlGOaE_8AKkkozLuOGcpQL8zH1CIdhz6u7Ed-tjOMPZUmDQPI9tHq2ZZXMcvkaGybSWYMRVeMcGAM6bMqKY4JQG7ZDF6_k-_her_Ci1-baNHmOfe-_O_-lM3j8AE8wki1HAuNQFwA |
|
1 |
root@vault1 |
|
1 2 3 4 5 |
vault write auth/kubernetes/config \ kubernetes_host="https://192.168.1.112:6443" \ kubernetes_ca_cert=@/root/ca.crt \ token_reviewer_jwt="$TOKEN_REVIEWER_JWT" \ disable_iss_validation="true" |
|
1 2 3 4 5 |
vault policy write test-policy - <<EOF path "test/secret/app/first-app" { capabilities = ["read"] } EOF |
|
1 2 3 4 |
root@kub-master1:~# kubectl get serviceaccounts -n app NAME SECRETS AGE app 0 16d default 0 16d |
|
1 2 3 4 5 6 |
vault write auth/kubernetes/role/test-role \ bound_service_account_names=app \ bound_service_account_namespaces=app \ policies=test-policy \ audience="vault" \ ttl=10m |
|
1 2 3 4 5 6 7 8 9 10 11 |
# фрагмент Pod/Deployment spec: serviceAccountName: app volumes: - name: sa-token projected: sources: - serviceAccountToken: path: token audience: vault # <— то же значение укажете в роли expirationSeconds: 3600 |
|
1 2 3 4 5 6 7 8 9 |
apiVersion: secrets.hashicorp.com/v1beta1 kind: VaultConnection metadata: name: vault-connection namespace: kube-system spec: address: "https://192.168.1.111:8200" skipTLSVerify: true |
kubectl apply -f vault-auth.yaml
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
apiVersion: secrets.hashicorp.com/v1beta1 kind: VaultAuth metadata: name: vault-auth-test namespace: app spec: vaultConnectionRef: kube-system/vault-connection method: kubernetes mount: kubernetes kubernetes: role: test-role serviceAccount: app audiences: - vault # <-- та же строка должна быть в роли Vault |