Всем привет. Меня зовут Добрый Кот Telegram.
В этой статье расскажем, как развернуть кластер в Yandex Cloud нашим модулем terraform.
От коллектива FR-Solutions и при поддержке @irbgeo Telegram : Продолжаем серию статей о K8S.
Освежим воспоминания
В предыдущей статье мы рассписали процесс развертывания Kubernetes с использованием голых конфигов и базовых бинарных файлов - это рабочий вариант, но в наших реалиях этого недостаточно, мы хотим автоматизацию.
* Пока писали автоматизацию, испытали море эмоций как от специфики шаблонизации, так и наследования ресурсов, но об это позже.
Отрицание
Первая автоматизация с моей стороны была написана на Ansible. Все начиналось лампово, пара тасок там, пара тут и через месяц родилась первая версия IaC кубера на Ansible, но чем больше я писал на Ansible, тем больше понимал, что его недостаточно - нужно постоянно удалять и создавать окружения в облаке.
Для создания окружения в облаке прекрасно подходит terraform, и первая мысль заключалась в том, чтобы скрестить ужа с ежом и получить первые 5 метров колючей проволки.
Получилось весьма неплохо, но в ходе эксплуатации я понял, что от Ansible можно избавиться полностью и написать все на Terraform. Связано это было с тем, что самое важное для сборки kubernetes моим методом - требуется всего лишь один правильный Cloud-INIT и вот для него расчехлять Ansible не хотелось. Ну что, сказано - сделано! Пошли писать на Terraform.
Гнев
Больше всего нервов скушал модуль для Vault, именно его кодовая база является самой большой в нашей автоматизации. Самые трудоемкие части:
Спецификация для будущих сертификатов. В ней содержится вся информация:
Описание для ролей.
Описание для заказа Key-keeper.
Описание сейфов и их CA.
Мета применимости.
Описание политик.
Описание токенов и аппролей.
В попытке решить базовую проблему безопасности - как передать в ВМ мастера временный токен через Cloud-Init - мы сотворили монстра. (На момент написания статьи Yandex Cloud наконец выпустил terraform provider для внутреннего сервиса LockBox, который позволяет решить проблему через IAM) - скоро пофиксим.
Торг
Дальше пытались задизайнить максимально простую, но достаточно высокодоступную конфигурацию и пришли к вот такой схеме.
Концепт простой, сделать так, чтобы виртуальные машины стали Stateless и не хранили никакой полезной нагрузки, и чтобы, в случае чего, их можно было без труда пересобрать (даже на другой тип операционной системы).
Чтобы такое провернуть, требутся несколько моментов:
База Etcd должна быть вынесена на внешние диски.
Сертификаты должны заезжать на ноду сами (сертификаты заказывает key-keeper).
Весь конфиг должен быть описан в Cloud-init.
У ВМ должны быть статичные A записи.
Мы решили все эти задачи и теперь можем работать с узлами мастеров достаточно гибко.
Депрессия
Первая версия на терраформе выглядела как одна большая репка
с ресурсами из-за чего часто были проблемы с добавлением нового или дебага старого. Было принято решение распилить на модули все ресурсы, что вызывало много вопросов "а как лучше?".
В основе лежит первый модуль yandex-cluster, в нем описывается спецификация будущего кластера и кастомизация входных аргументов модуля.
init
module "k8s-yandex-cluster" {
source = "../modules/k8s-yandex-cluster"
cluster_name = var.cluster_name
base_domain = "dobry-kot.ru"
vault_server = "http://193.32.219.99:9200/"
service_cidr = "29.64.0.0/16"
master_group = {
name = "master" # Разрешенный префикс для сертификатов.
count = 1
vpc_id = yandex_vpc_network.cluster-vpc.id
default_subnet_id = yandex_vpc_subnet.master-subnets["ru-central1-a"].id
default_zone = "ru-central1-a"
subnet_id_overwrite = {
master-1 = {
subnet = yandex_vpc_subnet.master-subnets["ru-central1-a"].id
zone = "ru-central1-a"
}
master-2 = {
subnet = yandex_vpc_subnet.master-subnets["ru-central1-b"].id
zone = "ru-central1-b"
}
master-3 = {
subnet = yandex_vpc_subnet.master-subnets["ru-central1-c"].id
zone = "ru-central1-c"
}
}
resources = {
core = 6
memory = 12
core_fraction = 100
etcd_disk = 60
first_disk = 30
}
os_image = "fd8kdq6d0p8sij7h5qe3"
ssh_username = "dkot"
ssh_rsa_path = "~/.ssh/id_rsa.pub"
}
}
vault_server - обязательный параметр, требуется для заказа сертификатов с ноды
subnet_id_overwrite - необязательный, применяется если требуется мастера разнести по разным подсетям и зонам
master_group - спецификация будущих мастеров
Для инициализации Control Plane достаточно создать VPC и подсети для мастеров, а в Vault должен быть создан корневой сертификат компании.
Дальше кастомные параметры попадают в модуль k8s-config-vars - в этом модуле формируются и растекаются по всем остальным модулям все переменные окружения кластера.
После того, как переменные окружения сформированы, мы собираем для каждой будущей ноды Cloud-init окружение в Vault. От k8s-vault-master мы получаем токен и подкидываем в Coud-init.
Последним действием мы создаем нужную инфраструктуру и узлы с нашими Cloud-Init конфигами.
Принятие
Как итог работы мы получили рабочий модуль для Yandex Cloud по созданию Control Plane Kubernetes. Когда я дописывал последние строки, я прям почувствовал себя Риком Огурчиком!
Эмоций это приключение породило очень много и не все позитивные. Самой большой проблемой было использование модулей внутри модуля, использование наследования переменных и шаблонизация самого терраформа. В перспективе, надеюсь, упростим конфигурацию.
Что дальше?
В принципе, уже на данный момент кластер можно собрать по примеру этого флоу
Вы получите:
Контрол Плейн K8S в HA режиме
Развернутый ворклоуд Cilium, CoreDNS, YandexCSIDriver, YandexCloudController, Gatekeeper, Certmanager, ClusterMachineApprover
В кластер добавится Worker узел - с сертификатами от волта, но выписанный кубом (Немного магии - расскажем позже)
Перед собой ставим цели собрать минимальную коробку с бестпрактисами:
Дописать модуль для Data-Plane (заказ workers).
Добавить интеграцию с etcd-backup-restore и yandex s3.
Настроить базовый мониторинг.
Настроить SSO на базе Keycloak.
Добавить Автоскейлер.
Переписать на GO рендер cloud-init.
Отрефакторить RBAC для мастеров.
Переделать мастера c instances на group-instances
Вишенка на торте
Статус
root@master-3-cluster-2:/home/dkot# kg no -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
master-1-cluster-2 Ready control-plane,master 2m50s v1.23.12 10.1.0.11 51.250.66.122 Ubuntu 20.04.4 LTS 5.4.0-124-generic containerd://1.6.8
master-2-cluster-2 Ready control-plane,master 2m53s v1.23.12 10.2.0.33 84.201.139.95 Ubuntu 20.04.4 LTS 5.4.0-124-generic containerd://1.6.8
master-3-cluster-2 Ready control-plane,master 2m55s v1.23.12 10.3.0.21 51.250.40.244 Ubuntu 20.04.4 LTS 5.4.0-124-generic containerd://1.6.8
После пересборки
root@master-2-cluster-2:/home/dkot# kg no -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
master-1-cluster-2 Ready <none> 37s v1.23.12 10.1.0.12 62.84.119.244 Debian GNU/Linux 10 (buster) 4.19.0-18-amd64 containerd://1.6.8
master-2-cluster-2 Ready control-plane,master 12m v1.23.12 10.2.0.16 51.250.27.187 Debian GNU/Linux 10 (buster) 4.19.0-18-amd64 containerd://1.6.8
master-3-cluster-2 Ready control-plane,master 12m v1.23.12 10.3.0.13 51.250.45.49 Debian GNU/Linux 10 (buster) 4.19.0-18-amd64 containerd://1.6.8
root@master-2-cluster-2:/home/dkot# kg po -A
NAMESPACE NAME READY STATUS RESTARTS AGE
fraima-ccm cluster-machine-approver-679bdb498-rv4fs 1/1 Running 0 11m
fraima-ccm yandex-cloud-controller-manager-vzxm8 1/1 Running 2 (2m2s ago) 11m
fraima-ccm yandex-cloud-controller-manager-z8694 1/1 Running 2 (2m21s ago) 11m
fraima-certmanager cert-manager-7b7db46cc-q7qfs 1/1 Running 0 11m
fraima-certmanager cert-manager-cainjector-5bc7858445-s29pr 1/1 Running 0 11m
fraima-certmanager cert-manager-webhook-5f9bc5fd88-qrqss 1/1 Running 0 11m
fraima-cni cilium-g6pj2 1/1 Running 0 13m
fraima-cni cilium-k4gqk 1/1 Running 0 13m
fraima-cni cilium-operator-77d7d656d9-4t29t 1/1 Running 0 13m
fraima-cni cilium-operator-77d7d656d9-tcpn5 1/1 Running 0 13m
fraima-cni cilium-xw7tm 1/1 Running 0 13m
fraima-csi csi-controller-656b87cd-ll7nn 6/6 Running 1 (8s ago) 11m
fraima-csi csi-node-fz4fd 3/3 Running 0 11m
fraima-csi csi-node-n4wgg 3/3 Running 1 (2m13s ago) 11m
fraima-csi csi-node-vtnwv 3/3 Running 1 (2m37s ago) 11m
fraima-dns coredns-6f67cdb5c-5x82v 1/1 Running 0 12m
fraima-dns coredns-6f67cdb5c-8nx9d 1/1 Running 0 12m
fraima-dns coredns-6f67cdb5c-jfsvx 0/1 Running 0 12m
fraima-gatekeeper gatekeeper-audit-6b8d6d54d7-xc57p 1/1 Running 0 11m
fraima-gatekeeper gatekeeper-controller-manager-597b75cb8b-8qkqj 0/1 Running 0 11m
fraima-gatekeeper gatekeeper-controller-manager-597b75cb8b-q7dzg 1/1 Running 0 11m
fraima-gatekeeper gatekeeper-controller-manager-597b75cb8b-qp9g4 1/1 Running 0 11m
kube-system etcd-master-1-cluster-2 1/1 Running 0 53s
kube-system etcd-master-2-cluster-2 1/1 Running 0 12m
kube-system etcd-master-3-cluster-2 1/1 Running 0 12m
kube-system kube-apiserver-master-1-cluster-2 1/1 Running 0 53s
kube-system kube-apiserver-master-2-cluster-2 1/1 Running 0 2m58s
kube-system kube-apiserver-master-3-cluster-2 1/1 Running 0 2m56s
kube-system kube-controller-manager-master-1-cluster-2 1/1 Running 0 53s
kube-system kube-controller-manager-master-2-cluster-2 1/1 Running 0 12m
kube-system kube-controller-manager-master-3-cluster-2 1/1 Running 0 11m
kube-system kube-scheduler-master-1-cluster-2 1/1 Running 0 53s
kube-system kube-scheduler-master-2-cluster-2 1/1 Running 0 12m
kube-system kube-scheduler-master-3-cluster-2 1/1 Running 0 11m
Как обещал, налету можно сменить даже тип операционной системы.
Ждем вас на обсуждения нашей работы в https://t.me/fr_solution_ru
Полезное чтиво
Kubernetes The Hard Way
https://github.com/fraima/kubernetes
https://github.com/fraima/key-keeper