Русский
Русский
English
Статистика
Реклама

Кто читает тэги?

Ломаем и чиним Kubernetes

05.02.2021 20:16:55 | Автор: admin

Kubernetes отличная платформа как для оркестрации контейнеров так и для всего остального. За последнее время Kubernetes ушёл далеко вперёд как по части функциональности так и по вопросам безопасности и отказоустойчивости. Архитектура Kubernetes позволяет с лёгкостью переживать сбои различного характера и всегда оставаться на плаву.

Сегодня мы будем ломать кластер, удалять сертификаты, вживую реджойнить ноды и всё это, по возможности, без даунтайма для уже запущенных сервисов.


Итак приступим. Основной control-plane Kubernetes состоит всего из нескольких компонентов:

  • etcd - используется в качестве базы данных

  • kube-apiserver - API и сердце нашего кластера

  • kube-controller-manager - производит операции над Kubernetes-ресурсами

  • kube-scheduller - основной шедуллер

  • kubelet'ы - которые непосредственно и запускают контейнеры на хостах

Каждый из этих компонентов защищён набором TLS-сертификатов, клиентских и серверных, которые используются для аутентификации и авторизации компонентов между ссобой. Они не хранятся где-либо в базе данных Kuberentes, за исключением определенных случаев, а представлены в виде обычных файлов:

# tree /etc/kubernetes/pki//etc/kubernetes/pki/ apiserver.crt apiserver-etcd-client.crt apiserver-etcd-client.key apiserver.key apiserver-kubelet-client.crt apiserver-kubelet-client.key ca.crt ca.key CTNCA.pem etcd    ca.crt    ca.key    healthcheck-client.crt    healthcheck-client.key    peer.crt    peer.key    server.crt    server.key front-proxy-ca.crt front-proxy-ca.key front-proxy-client.crt front-proxy-client.key sa.key sa.pub

Сами компоненты описаны и запускаются на мастерах как static pods из директории /etc/kubernetes/manifests/

На этом месте не будем останавливаться подробно, т.к. это тема для отдельной статьи. В данном случае нас в первую очередь интересует как из этого всего добра получить рабочий кластер. Но для начала давайте немного абстрагируемся, и представим что у нас есть вышеперечисленные компоненты Kubernetes, которые как-то коммуницируют между ссобой.

Основная схема выглядит примерно так:

(стрелочки указывают на связи клиент --> сервер)(стрелочки указывают на связи клиент --> сервер)

Для коммуникации им нужны TLS-сертификаты, которые в принципе можно вынести на отдельный уровень абстракции и полностью довериться вашему инструменту деплоя, будь-то kubeadm, kubespray или что либо ещё. В этой статье мы разберём kubeadm т.к. это наиболее стандартный инструмент для развёртывания Kubernetes, а также он часто используется в составе других решений.


Предположим, что у нас уже есть задеплоенный кластер. Начнём с самого интересного:

rm -rf /etc/kubernetes/

На мастерах данная директория содержит:

  • Набор сертификатов и CA для etcd (в /etc/kubernetes/pki/etcd)

  • Набор сертификатов и CA для Kubernetes (в /etc/kubernetes/pki)

  • Kubeconfig для cluster-admin, kube-controller-manager, kube-scheduller и kubelet (каждый из них также имеет закодированный в base64 CA-сертификат для нашего кластера /etc/kubernetes/*.conf)

  • Набор статик-манифеств для etcd, kube-apiserver, kube-scheduller и kube-controller-manager (в /etc/kubernetes/manifests)

Чиним control-plane

Чтобы не было недоразумений, давайте также убедимся что все наши control-plane поды также остановлены:

crictl rm `crictl ps -aq`

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

Давайте начнём с восстановления etcd, так как если у нас был кворум (3 и более мастер-нод) etcd-кластер не запустится без присутствия большинства из них.

kubeadm init phase certs etcd-ca

- сгенерит новый CA для нашего etcd-кластера. Так как все остальные сертификаты должны быть им подписанны, скопируем его вместе с приватным ключём на остальные мастер-ноды:

/etc/kubernetes/pki/etcd/ca.{key,crt}

Теперь перегенерим остальные etcd-сертификаты и static-манифесты для него на всех control-plane нодах:

kubeadm init phase certs etcd-healthcheck-clientkubeadm init phase certs etcd-peerkubeadm init phase certs etcd-serverkubeadm init phase etcd local

На этом этапе у нас уже должен подняться работоспособный etcd-кластер:

# crictl psCONTAINER ID        IMAGE               CREATED             STATE               NAME                ATTEMPT             POD IDac82b4ed5d83a       0369cf4303ffd       2 seconds ago       Running             etcd                0                   bc8b4d568751b

Теперь давайте проделаем тоже самое, но для для Kubernetes, на одной из master-нод выполним:

kubeadm init phase certs allkubeadm init phase kubeconfig allkubeadm init phase control-plane allcp -f /etc/kubernetes/admin.conf ~/.kube/config

Вышеописанные команды сгенирируют все SSL-сертификаты нашего Kubernetes-кластера.

Если вы используете kubeadm для джойна кубелетов, вам также потребуется обновить конфиг cluster-info в kube-public неймспейсе т.к. он до сих пор содержит хэш вашего старого CA.

kubeadm init phase bootstrap-token

Так как все сертификаты на других инстансах также должны быть подписаны одним CA, скопируем его на остальные control-plane ноды, и повторим вышеописанные команды на каждой из них.

/etc/kubernetes/pki/{ca,front-proxy-ca}.{key,crt}/etc/kubernetes/pki/sa.{key,pub}

Кстати, в качестве альтернативы ручного копирования сертификатов теперь вы можете использовать интерфейс Kubernetes, например следующая команда:

kubeadm init phase upload-certs --upload-certs

Зашифрует и загрузит сертификаты в Kubernetes на 2 часа, таким образом вы сможете сделать реджойн мастеров следующим образом:

kubeadm join phase control-plane-prepare all kubernetes-apiserver:6443 --control-plane --token cs0etm.ua7fbmwuf1jz946l     --discovery-token-ca-cert-hash sha256:555f6ececd4721fed0269d27a5c7f1c6d7ef4614157a18e56ed9a1fd031a3ab8 --certificate-key 385655ee0ab98d2441ba8038b4e8d03184df1806733eac131511891d1096be73kubeadm join phase control-plane-join all

Стоит заметить, что в API Kubernetes есть ещё один конфиг, который хранит CA сертификат для front-proxy client, он используется для аутентификации запросов от apiserver в вебхуках и прочих aggregation layer сервисах. К счастью kube-apiserver обновляет его автоматически.

Однако возможно вы захотите почистить его от старых сертификатов вручную:

kubectl get cm -n kube-system extension-apiserver-authentication -o yaml

В любом случае на данном этапе мы уже имеем полностью рабочий control-plane.

Чиним воркеров

Эта компанда выведет список всех нод кластера, хотя сейчас все они будут в статусе NotReady:

kubectl get node

Это потому что они по прежнему используют старые сертификаты и с ожидают запросов apiserver, подписанных старым CA. Для того чтобы это исправить мы воспользуемся kubeadm, и сделаем реджойн нод в кластер.

Когда как мастера имеют доступ к CA и могут быть присоеденены локально:

systemctl stop kubeletrm -rf /var/lib/kubelet/pki/ /etc/kubernetes/kubelet.confkubeadm init phase kubeconfig kubeletkubeadm init phase kubelet-start

То для джойна воркеров мы сгенерируем новый токен:

kubeadm token create --print-join-command

и на каждом из них выполним:

systemctl stop kubeletrm -rf /var/lib/kubelet/pki/ /etc/kubernetes/pki/ /etc/kubernetes/kubelet.conf kubeadm join phase kubelet-start kubernetes-apiserver:6443  --token cs0etm.ua7fbmwuf1jz946l     --discovery-token-ca-cert-hash sha256:555f6ececd4721fed0269d27a5c7f1c6d7ef4614157a18e56ed9a1fd031a3ab8

Внимание, удалять директорию /etc/kubernetes/pki/ на мастерах не нужно, так как она уже содержит все необходимые сертификаты.

Вышеописанная процедура переподключит все ваши kubelet'ы обратно к кластеру, при этом никак не повлияет на уже запущенные на них контейнеры. Однако если у вас в кластере много нод и вы сделаете это неодновременно, у вас может возникнуть ситуация когда controller-manager начнёт пересоздавать контейнеры с NotReady-нод и пытаться их запустить на живых нодах кластера.

Чтобы это предотвратить мы можем временно остановить controller-manager, на мастерах:

rm /etc/kubernetes/manifests/kube-controller-manager.yamlcrictl rmp `crictl ps --name kube-controller-manager -q`

Последняя команда нужна просто для того, чтобы удостовериться что под с controller-manager действительно не запущен. Как только все ноды кластера будут присоединены мы можем сгенерировать static-manifest для controller-manager обратно.

Для этого на всех мастерах выполняем:

kubeadm init phase control-plane controller-manager

Учтите что делать это нужно на этапе когда вы уже сгенерировали join token, в противном случае операция подключения зависнет на попытке прочитать токен из cluser-info.

В случае если kubelet настроен на получение сертификата подписанного вашим CA (опция serverTLSBootstrap: true), вам также потребуется заново подтвердить csr от ваших kubelet'ов:

kubectl get csrkubectl certificate approve <csr>

Чиним ServiceAccounts

Есть ещё один момент. Так как мы потеряли /etc/kubernetes/pki/sa.key - это тот самый ключ которм были подписаны jwt-токены для всех наших ServiceAccounts, то мы должны пересоздать токены для каждого из них.

Сделать это можно достаточно просто, удалив все секреты типа kubernetes.io/service-account-token:

kubectl get secret --all-namespaces | awk '/kubernetes.io\/service-account-token/ { print "kubectl delete secret -n " $1 " " $2}' | sh -s

После чего kube-controller-manager автоматически сгенерирует новые, подписанные новым ключём.

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

kubectl get pod --field-selector 'spec.serviceAccountName!=default' --no-headers --all-namespaces | awk '{print "kubectl delete pod -n " $1 " " $2}'

Например эта команда выведет список команд для удаления всех подов использующих недефолтный serviceAccount. Рекомендую начать с неймспейса kube-system, т.к. там могут быть установлен kube-proxy и CNI-плагин, жизненно необходимые для настройки коммуникации ваших микросервисов.

На этом восстановление кластера можно считать оконченным. Спасибо за внимание! В следующей статье мы подробнее рассмотрим бэкап и восстановление etcd-кластера.

Подробнее..

Ломаем и чиним etcd-кластер

01.03.2021 20:22:05 | Автор: admin

etcd это быстрая, надёжная и устойчивая к сбоям key-value база данных. Она лежит в основе Kubernetes и является неотъемлемой частью его control-plane. Именно поэтому критически важно уметь бэкапить и восстанавливать работоспособность как отдельных нод, так и всего etcd-кластера.

В предыдущей статье мы подробно рассмотрели перегенерацию SSL-сертификатов и static-манифестов для Kubernetes, а также вопросы связанные восстановлением работоспособности Kubernetes. Эта статья будет посвящена целиком и полностью восстановлению etcd-кластера.


Для начала я сразу должен сделать оговорку, что рассматривать мы будем лишь определённый кейс, когда etcd задеплоен и используется непосредственно в составе Kubernetes. Приведённые в статье примеры подразумевают что ваш etcd-кластер развёрнут с помощью static-манифестов и запускается внутри контейнеров.

Для наглядности возьмём схему stacked control plane nodes из предыдущей статьи:

(стрелочки указывают на связи клиент --> сервер)(стрелочки указывают на связи клиент --> сервер)

Предложенные ниже команды можно выполнить и с помощью kubectl, но в данном случае мы постараемся абстрагироваться от плоскости управления Kubernetes и рассмотрим локальный вариант управления контейнеризированным etcd-кластером с помощью crictl.

Данное умение также поможет вам починить etcd даже в случае неработающего Kubernetes API.

Подготовка

По этому, первое что мы сделаем, это зайдём по ssh на одну из master-нод и найдём наш etcd-контейнер:

CONTAINER_ID=$(crictl ps -a --label io.kubernetes.container.name=etcd --label io.kubernetes.pod.namespace=kube-system | awk 'NR>1{a=$1} END{print a}')

Так же установим алиас, чтобы каждый раз не перечислять пути к сертификатам в командах:

alias etcdctl='crictl exec "$CONTAINER_ID" etcdctl --cert /etc/kubernetes/pki/etcd/peer.crt --key /etc/kubernetes/pki/etcd/peer.key --cacert /etc/kubernetes/pki/etcd/ca.crt'

Приведённые выше команды являются временными, вам потребуется выполнять их каждый раз перед тем как начать работать с etcd. Конечно, для своего удобства вы можете добавить их в .bashrc. Как это сделать выходит за рамки этой статьи.

Если что-то пошло не так и вы не можете сделать exec в запущенный контейнер, посмотрите логи etcd:

crictl logs "$CONTAINER_ID"

А также убедитесь в наличии static-манифеста и всех сертифиткатов в случае если контейнер даже не создался. Логи kubelet так же бывают весьма полезными.

Проверка состояния кластера

Здесь всё просто:

# etcdctl member list -w table+------------------+---------+-------+---------------------------+---------------------------+------------+|        ID        | STATUS  | NAME  |        PEER ADDRS         |       CLIENT ADDRS        | IS LEARNER |+------------------+---------+-------+---------------------------+---------------------------+------------+| 409dce3eb8a3c713 | started | node1 | https://10.20.30.101:2380 | https://10.20.30.101:2379 |      false || 74a6552ccfc541e5 | started | node2 | https://10.20.30.102:2380 | https://10.20.30.102:2379 |      false || d70c1c10cb4db26c | started | node3 | https://10.20.30.103:2380 | https://10.20.30.103:2379 |      false |+------------------+---------+-------+---------------------------+---------------------------+------------+

Каждый инстанс etcd знает всё о каждом. Информация о members хранится внутри самого etcd и по этому любое изменение в ней будет также обновленно и на остальных инстансах кластера.

Важное замечание, команда member list отображает только статус конфигурации, но не статус конкретного инстанса. Чтобы проверить статусы инстансов есть команда endpoint status, но она требует явного указания всех эндпоинтов кластера для проверки.

ENDPOINTS=$(etcdctl member list | grep -o '[^ ]\+:2379' | paste -s -d,)etcdctl endpoint status --endpoints=$ENDPOINTS -w table

в случае если какой-то из эндпоинтов окажется недоступным вы увидите такую ошибку:

Failed to get the status of endpoint https://10.20.30.103:2379 (context deadline exceeded)

Удаление неисправной ноды

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

Первым делом нам нужно удалить failed member:

etcdctl member remove d70c1c10cb4db26c

Прежде чем продолжить, давайте убедимся, что на упавшей ноде под с etcd больше не запущен, а нода больше не содержит никаких данных:

rm -rf /etc/kubernetes/manifests/etcd.yaml /var/lib/etcd/crictl rm "$CONTAINER_ID"

Команды выше удалят static-pod для etcd и дирректорию с данными /var/lib/etcd на ноде.

Разумеется в качестве альтернативы вы также можете воспользоваться командой kubeadm reset, которая удалит все Kubernetes-related ресурсы и сертифиткаты с вашей ноды.

Добавление новой ноды

Теперь у нас есть два пути:

В первом случае мы можем просто добавить новую control-plane ноду используя стандартный kubeadm join механизм:

kubeadm init phase upload-certs --upload-certskubeadm token create --print-join-command --certificate-key <certificate_key>

Вышеприведённые команды сгенерируют команду для джойна новой control-plane ноды в Kubernetes. Этот кейс довольно подробно описан в официальной документации Kubernetes и не нуждается в разъяснении.

Этот вариант наиболее удобен тогда, когда вы деплоите новую ноду с нуля или после выполнения kubeadm reset

Второй вариант более аккуратный, так как позволяет рассмотреть и выполнить изменения необходимые только для etcd не затрагивая при этом другие контейнеры на ноде.

Для начала убедимся что наша нода имеет валидный CA-сертификат для etcd:

/etc/kubernetes/pki/etcd/ca.{key,crt}

В случае его отсутсвия скопируейте его с других нод вашего кластера. Теперь сгенерируем остальные сертификаты для нашей ноды:

kubeadm init phase certs etcd-healthcheck-clientkubeadm init phase certs etcd-peerkubeadm init phase certs etcd-server

и выполним присоединение к кластеру:

kubeadm join phase control-plane-join etcd --control-plane

Для понимания, вышеописанная команда сделает следующее:

  1. Добавит новый member в существующий etcd-кластер:

    etcdctl member add node3 --endpoints=https://10.20.30.101:2380,https://10.20.30.102:2379 --peer-urls=https://10.20.30.103:2380
    
  2. Сгенерирует новый static-manifest для etcd /etc/kubernetes/manifests/etcd.yaml с опциями:

    --initial-cluster-state=existing--initial-cluster=node1=https://10.20.30.101:2380,node2=https://10.20.30.102:2380,node3=https://10.20.30.103:2380
    

    эти опции позволят нашей ноде автоматически добавиться в существующий etcd-кластер.

Создание снапшота etcd

Теперь рассмотрим вариант создания и восстановления etcd из резервной копии.

Создать бэкап можно довольно просто, выполнив на любой из нод:

etcdctl snapshot save /var/lib/etcd/snap1.db

Обратите внимание я намерянно использую /var/lib/etcd так как эта директория уже прокинута в etcd контейнер (смотрим в static-манифест /etc/kubernetes/manifests/etcd.yaml)

После выполнения этой команды по указанному пути вы найдёте снапшот с вашими данными, давайте сохраним его в надёжном месте и рассмотрим процедуру восстановления из бэкапа

Восстановление etcd из снапшота

Здесь мы рассмотрим кейс когда всё пошло не так и нам потребовалось восстановить кластер из резервной копии.

У нас есть снапшот snap1.db сделанный на предыдущем этапе. Теперь давайте полностью удалим static-pod для etcd и данные со всех наших нод:

rm -rf /etc/kubernetes/manifests/etcd.yaml /var/lib/etcd/member/crictl rm "$CONTAINER_ID"

Теперь у нас снова есть два пути:

Вариант первый создать etcd-кластер из одной ноды и присоединить к нему остальные ноды, по описанной выше процедуре.

kubeadm init phase etcd local

эта команда сгенерирует статик-манифест для etcd c опциями:

--initial-cluster-state=new--initial-cluster=node1=https://10.20.30.101:2380

таким образом мы получим девственно чистый etcd на одной ноде.

# etcdctl member list -w table+------------------+---------+-------+---------------------------+---------------------------+------------+|        ID        | STATUS  | NAME  |        PEER ADDRS         |       CLIENT ADDRS        | IS LEARNER |+------------------+---------+-------+---------------------------+---------------------------+------------+| 1afbe05ae8b5fbbe | started | node1 | https://10.20.30.101:2380 | https://10.20.30.101:2379 |      false |+------------------+---------+-------+---------------------------+---------------------------+------------+

Восстановим бэкап на первой ноде:

etcdctl snapshot restore /var/lib/etcd/snap1.db \  --data-dir=/var/lib/etcd/new  --name=node1 \  --initial-advertise-peer-urls=https://10.20.30.101:2380 \  --initial-cluster=node1=https://10.20.30.101:2380mv /var/lib/etcd/member /var/lib/etcd/member.oldmv /var/lib/etcd/new/member /var/lib/etcd/membercrictl rm "$CONTAINER_ID"rm -rf /var/lib/etcd/member.old/ /var/lib/etcd/new/

На остальных нодах выполним присоединение к кластеру:

kubeadm join phase control-plane-join etcd --control-plane

Вариант второй: восстановить бэкап сразу на всех нодах кластера. Для этого копируем файл снапшота на остальные ноды, и выполняем восстановление вышеописанным образом. В данном случае в опциях к etcdctl нам потребуется указать сразу все ноды нашего кластера, к примеру

для node1:

etcdctl snapshot restore /var/lib/etcd/snap1.db \  --data-dir=/var/lib/etcd/new \  --name=node1 \  --initial-advertise-peer-urls=https://10.20.30.101:2380 \  --initial-cluster=node1=https://10.20.30.101:2380,node2=https://10.20.30.102:2380,node3=https://10.20.30.103:2380

для node2:

etcdctl snapshot restore /var/lib/etcd/snap1.db \  --data-dir=/var/lib/etcd/new \  --name=node2 \  --initial-advertise-peer-urls=https://10.20.30.102:2380 \  --initial-cluster=node1=https://10.20.30.101:2380,node2=https://10.20.30.102:2380,node3=https://10.20.30.103:2380

для node3:

etcdctl snapshot restore /var/lib/etcd/snap1.db \  --data-dir=/var/lib/etcd/new \  --name=node3 \  --initial-advertise-peer-urls=https://10.20.30.103:2380 \  --initial-cluster=node1=https://10.20.30.101:2380,node2=https://10.20.30.102:2380,node3=https://10.20.30.103:2380
Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru