ProHoster > Блог > адміністрування > Наш досвід роботи з даними в etcd Kubernetes кластера безпосередньо (без K8s API)
Наш досвід роботи з даними в etcd Kubernetes кластера безпосередньо (без K8s API)
Все частіше до нас звертаються клієнти з проханням забезпечити доступ у Kubernetes-кластер для можливості звернення до сервісів усередині кластера: щоб можна було безпосередньо підключитися до якоїсь бази даних або сервісу, для зв'язку локальної програми з додатками всередині кластера.
Наприклад, виникає потреба підключитися зі своєї локальної машини до сервісу memcached.staging.svc.cluster.local. Ми надаємо таку можливість за допомогою VPN усередині кластера, до якого підключається клієнт. Для цього анонсуємо підмережі pod'ів, сервісів та push'им кластерні DNS клієнту. Таким чином, коли клієнт намагається підключитись до сервісу memcached.staging.svc.cluster.local, запит йде в DNS кластера і у відповідь отримує адресу даного сервісу з мережі кластера або адресу pod'а.
K8s-кластери ми налаштовуємо за допомогою kubeadm, де за замовчуванням сервісна підмережа 192.168.0.0/16, а мережа pod'ів - 10.244.0.0/16. Зазвичай все добре працює, але є кілька моментів:
підмережа 192.168.*.* часто використовується в офісних мережах клієнтів, а ще частіше – у домашніх мережах розробників. І тоді у нас виходять конфлікти: домашні роутери працюють у цій підмережі та VPN push'іт ці підмережі з кластера клієнту.
У нас є кілька кластерів (кластери production, stage та/або декілька dev-кластерів). Тоді у всіх них за умовчанням будуть однакові підмережі для pod'ів та сервісів, що створює великі складнощі для одночасної роботи з сервісами у кількох кластерах.
Ми вже давно прийняли практику використання різних підмереж для сервісів і pod'ів у рамках одного проекту — загалом, щоб усі кластери були з різними мережами. Однак є велика кількість кластерів у роботі, які не хотілося б перекочувати з нуля, тому що в них запущено багато сервісів, stateful-додатків тощо.
І тоді ми запитали себе: як би поміняти підсіти в існуючому кластері?
Пошук рішень
Найпоширеніша практика – перестворити всі сервіси із типом ClusterIP. Як варіант, можуть порадити і таке:
Наступні процеси мають проблему: після того, як configured, підси керуються з old IP як DNS nameserver в /etc/resolv.conf.
Since I still did not find the solution, і had to reset the entire cluster with kubeadm reset and init it again.
Але не всім це підходить… Ось детальніші вступні для нашого випадку:
Використовується Flannel;
Є кластери як у хмарах, і на залозі;
Хотілося б уникнути повторного деплою всіх сервісів у кластері;
Є потреба взагалі зробити все із мінімальною кількістю проблем;
Версія Kubernetes - 1.16.6 (втім, подальші дії будуть аналогічні і для інших версій);
Основне завдання зводиться до того, щоб у кластері, розгорнутому за допомогою kubeadm із сервісною підмережею 192.168.0.0/16, замінити її на 172.24.0.0/16.
І так уже збіглося, що нам давно було цікаво подивитися, що і як у Kubernetes зберігається в etcd, що взагалі з цим можна зробити… Ось і подумали: «Чому б просто не оновити дані в etcd, замінивши старі IP-адреси (підмережі) на нові? »
Пошукавши готові інструменти для роботи з даними в etcd, ми не знайшли нічого вирішального поставленого завдання. (До речі, якщо ви знаєте про будь-які утиліти для роботи з даними безпосередньо в etcd - будемо вдячні за посилання.) Однак гарною відправною точкою стала etcdhelper від OpenShift(дякую його авторам!).
Ця утиліта вміє підключатися до etcd за допомогою сертифікатів та читати звідти дані за допомогою команд ls, get, dump.
Дописуємо etcdhelper
Наступна думка закономірна: «Що заважає дописати цю утиліту, додавши можливість запису даних etcd?»
Вона втілилася в модифіковану версію etcdhelper із двома новими функціями changeServiceCIDR и changePodCIDR. На її код можна переглянути тут.
Що виконують нові функції? Алгоритм changeServiceCIDR:
створюємо десеріалізатор;
компілюємо регулярний вираз для заміни CIDR;
проходимо по всіх сервісах з типом ClusterIP у кластері:
декодуємо значення з etcd Go-об'єкт;
за допомогою регулярного виразу замінюємо перші два байти адреси;
привласнюємо сервісу IP-адресу з нової підмережі;
створюємо серіалізатор, перетворимо Go-об'єкт у protobuf, записуємо нові дані etcd.
Функція changePodCIDR по суті аналогічна changeServiceCIDR — тільки замість редагування специфікації сервісів ми це робимо для вузла і змінюємо .spec.PodCIDR на нову підмережу.
Практика
Зміна serviceCIDR
План із реалізації поставленого завдання — дуже простий, але має на увазі даунтайм на момент перестворення всіх pod'ів у кластері. Після опису основних кроків ми також поділимося думками, як теоретично можна мінімізувати цей простий.
Підготовчі дії:
установка необхідного ПЗ та складання пропатченого etcdhelper;
бекап etcd і /etc/kubernetes.
Короткий план дій щодо зміни serviceCIDR:
зміна маніфестів apiserver'а та controller-manager'а;
перевипуск сертифікатів;
зміна ClusterIP сервісів etcd;
рестарт всіх pod'ів у кластері.
Далі представлена повна послідовність дій у деталях.
Зберігаємо собі etcdhelper.go, завантажуємо залежності, збираємо:
wget https://raw.githubusercontent.com/flant/examples/master/2020/04-etcdhelper/etcdhelper.go
go get go.etcd.io/etcd/clientv3 k8s.io/kubectl/pkg/scheme k8s.io/apimachinery/pkg/runtime
go build -o etcdhelper etcdhelper.go
4. Змінюємо сервісну мережу в маніфестах Kubernetes control plane. У файлах /etc/kubernetes/manifests/kube-apiserver.yaml и /etc/kubernetes/manifests/kube-controller-manager.yaml змінюємо параметр --service-cluster-ip-range на нову підмережу: 172.24.0.0/16 замість 192.168.0.0/16.
5. Оскільки ми змінюємо сервісну мережу, на яку kubeadm випускає сертифікати для apiserver'а (у тому числі), їх необхідно перевипустити:
Подивимося, на які домени та IP-адреси випущено поточний сертифікат:
openssl x509 -noout -ext subjectAltName </etc/kubernetes/pki/apiserver.crt
X509v3 Subject Alternative Name:
DNS:dev-1-master, DNS:kubernetes, DNS:kubernetes.default, DNS:kubernetes.default.svc, DNS:kubernetes.default.svc.cluster.local, DNS:apiserver, IP Address:192.168.0.1, IP Address:10.0.0.163, IP Address:192.168.199.100
Увага! У цей момент у кластері перестає працювати резолвінг доменів, так як у вже існуючих pod'ах /etc/resolv.conf прописана стара адреса CoreDNS (kube-dns), а kube-proxy змінив правила iptables зі старої підмережі на нову. Далі у статті написано про можливі варіанти мінімізувати простий.
Поправимо ConfigMap'и у просторі імен kube-system:
kubectl -n kube-system edit cm kubelet-config-1.16
- Тут замінимо clusterDNS на нову IP-адресу сервісу kube-dns: kubectl -n kube-system get svc kube-dns.
kubectl -n kube-system edit cm kubeadm-config
- Виправимо data.ClusterConfiguration.networking.serviceSubnet на нову підмережу.
Оскільки змінилася адреса kube-dns, необхідно оновити конфіг kubelet на всіх вузлах:
7. Якщо хоча б у одного вузла залишити старий podCIDR, то kube-controller-manager не зможе запуститися, а pod'и в кластері не плануватимуться.
Насправді, зміна podCIDR можна зробити і простіше (наприклад, так). Але нам хотілося навчитися працювати з etcd безпосередньо, тому що існують випадки, коли виправлення об'єктів Kubernetes в etcd єдиний Можливий варіант. (Наприклад, не можна просто так без простою змінити у Service поле spec.clusterIP.)
Підсумок
У статті розглянуто можливість роботи з даними etcd безпосередньо, тобто. в обхід Kubernetes API. Іноді такий підхід дозволяє робити хитрі штуки. Наведені в тексті операції ми тестували на реальних класах K8s. Однак їх статус готовності до широкого застосування PoC (proof of concept). Тому, якщо ви хочете використовувати модифіковану версію утиліти etcdhelper на своїх кластерах, робіть це на свій страх та ризик.