Как заменить etcd — мозг кластера Kubernetes — на PostgreSQL или любую другую СУБД, которую вы хотите
etcd
— это мозг каждого кластера Kubernetes, хранилище ключей и значений, отслеживающее все объекты в кластере. Он переплетен и тесно связан с Kubernetes, и может показаться, что это неотъемлемая часть кластера, или это так?
В этой статье мы рассмотрим, как мы можем заменить etcd
базой данных PostgreSQL, а также почему и когда это может иметь смысл.
Почему?
Если вы используете свой собственный кластер Kubernetes, то вам известны трудности управления etcd
. Помимо точки зрения пользователя, etcd
мало используется за пределами Kubernetes и находится в состоянии упадка, потому что никто не хочет его поддерживать. Из-за этого исправление критических ошибок занимает много времени.
Кроме того, почему бы и нет? Почему мы не должны использовать другие серверные части хранилища с Kubernetes? Наличие большего количества опций — это хорошо, и у запуска Kubernetes с РСУБД действительно нет недостатков, будь то PostgreSQL, MySQL или что-то еще, что вам может быть удобно.
Кроме того, запуск Kubernetes с РСУБД — не такая уж новая идея, k3s, дистрибутив Kubernetes производственного уровня может работать с реляционной БД вместо etcd
. Если это работает для k3s, почему это не работает для любого другого кластера?
Как?
Как упоминалось в начале, Kubernetes и etcd
тесно связаны. Компоненты кластера (сервер API) ожидают etcd-like
интерфейса, в который они могут писать и из которого они могут читать. Поэтому, чтобы использовать базу данных SQL в качестве хранилища, нам нужно предоставить etcd-to-SQL
уровень перевода, который называется Kine. Kine — это компонент k3s, позволяющий использовать различные СУБД в качестве замены etcd
. Он обеспечивает реализацию функций GRPC, на которые опирается Kubernetes. Что касается Kubernetes, то он разговаривает с сервером etcd
.
Прежде чем мы приступим к запуску Kine, если мы хотим запустить Kubernetes с PostgreSQL, нам, очевидно, понадобится экземпляр PostgreSQL:
Примечание. Если вы хотите продолжить или быстро развернуть кластер на виртуальной машине с поддержкой PostgreSQL, вы можете проверить мой репозиторий (ветвь k8s-without-etcd
).
apt -y install postgresql postgresql-contrib systemctl start postgresql.service
В этом руководстве мы будем использовать PostgreSQL, работающий как системный сервис, мы также настроим SSL для базы данных:
# Generate self signed root CA cert openssl req -addext "subjectAltName = DNS:localhost" -nodes \ -x509 -newkey rsa:2048 -keyout ca.key -out ca.crt -subj "/CN=localhost" # Generate server cert to be signed openssl req -addext "subjectAltName = DNS:localhost" -nodes \ -newkey rsa:2048 -keyout server.key -out server.csr -subj "/CN=localhost" # Sign the server cert openssl x509 -extfile <(printf "subjectAltName=DNS:localhost") -req \ -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt chmod og-rwx ca.key chmod og-rwx server.key cp {server.crt,server.key,ca.crt} /var/lib/postgresql/ chown postgres.postgres /var/lib/postgresql/server.key sed -i -e "s|ssl_cert_file.*|ssl_cert_file = '/var/lib/postgresql/server.crt'|g" /etc/postgresql/14/main/postgresql.conf sed -i -e "s|ssl_key_file.*|ssl_key_file = '/var/lib/postgresql/server.key'|g" /etc/postgresql/14/main/postgresql.conf sed -i -e "s|#ssl_ca_file.*|ssl_ca_file = '/var/lib/postgresql/ca.crt'|g" /etc/postgresql/14/main/postgresql.conf systemctl restart postgresql.service
Мы используем openssl
для создания корневого сертификата ЦС, запроса на подпись, а также сертификата и ключа сервера. Затем мы ограничиваем права доступа к ключам, чтобы их мог прочитать только их владелец. Копируем сертификаты и ключ в /var/lib/postgresql/
и делаем пользователя postgres
владельцем ключа сервера. Наконец, мы модифицируем postgresql.conf
, чтобы указать PostgreSQL использовать наши новые SSL-сертификаты и ключ.
Когда база данных запущена, мы можем перейти к созданию кластера. Мы сделаем это, используя конфигурацию kubeadm
и ниже:
# kubeadm-config.yaml apiVersion: kubeadm.k8s.io/v1beta3 kind: InitConfiguration localAPIEndpoint: advertiseAddress: 192.168.56.2 bindPort: 6443 nodeRegistration: criSocket: "unix:///var/run/crio/crio.sock" --- apiVersion: kubeadm.k8s.io/v1beta3 kind: ClusterConfiguration kubernetesVersion: v1.25.0 networking: podSubnet: 10.244.0.0/16 etcd: external: endpoints: - http://127.0.0.1:2379 apiServer: timeoutForControlPlane: 1m0s
Вы можете настроить это под свои нужды. Единственная важная часть здесь — это массив etcd.external.endpoints
, который сообщает Kubernetes, где находится etcd
(или etcd-compatible-interface
) — в нашем случае это место, где Kine будет слушать.
Чтобы построить кластер с использованием этой конфигурации, запустите:
kubeadm init --config=/.../kubeadm-config.yaml --upload-certs \ --ignore-preflight-errors ExternalEtcdVersion 2>&1 || true kubectl taint nodes --all node-role.kubernetes.io/control-plane-
В дополнение к передаче файла конфигурации мы также указываем, что хотим игнорировать ошибки, связанные с etcd
, во время запуска, и, учитывая, что этот кластер игровой площадки имеет только один узел, мы также очищаем главный узел, чтобы мы могли запускать на нем рабочие нагрузки. .
Теперь самое интересное — настройка Kine. Есть несколько способов развернуть его — как базовый процесс, как сервис systemd
, как под в пространстве имен kube-system
или мой предпочтительный вариант — как вспомогательный сервер API.
Когда мы развернули кластер с kubeadm
, он уже сгенерировал для нас статические манифесты Pod, в том числе один для сервера API ( kube-apiserver.yaml
), поэтому нам нужно будет пропатчить его, чтобы включить контейнер с запущенным Kine:
# vim /etc/kubernetes/manifests/kube-apiserver.yaml # ... containers: - name: kube-apiserver # ... Existing API server container # ------------------------------------- - image: rancher/kine:v0.10.1-amd64 name: kine securityContext: # Don't do this in real deployment... runAsUser: 0 runAsGroup: 0 command: [ "/bin/sh", "-c", "--" ] args: [ 'kine --endpoint="postgres://$(POSTGRES_USERNAME):$(POSTGRES_PASSWORD)@localhost:5432/postgres" --ca-file=/var/lib/postgresql/ca.crt --cert-file=/var/lib/postgresql/server.crt --key-file=/var/lib/postgresql/server.key' ] env: - name: POSTGRES_USERNAME # This should be a secret value: "postgres" - name: POSTGRES_PASSWORD # This should be a secret value: "somepass" volumeMounts: - mountPath: /var/lib/postgresql/ name: kine-ssl readOnly: true volumes: # ------------------------------------- # ... Existing volumes used by API Server container # ------------------------------------- - hostPath: path: /var/lib/postgresql type: DirectoryOrCreate name: kine-ssl
Выше показан контейнер, который нам нужно добавить в статический под API-сервера, он использует образ Kine из Docker Hub по адресу rancher/kine:...
. Он указывает точку входа, которая указывает на базу данных PostgreSQL, а также сертификаты SSL и ключ, которые мы сгенерировали ранее.
После применения этих изменений к серверу API мы увидим, что Kubelet успешно запустит как kube-apiserver
, так и kine
контейнер:
crictl ps CONTAINER IMAGE CREATED STATE NAME ATTEMPT POD ID POD 09172381b42f1 4d2edfd10d3e3f... 29 seconds ago Running kube-apiserver 0 3187286722eba kube-apiserver-kubemaster 55ac5108ae677 ccdd8a15f4ca3e... 29 seconds ago Running kine 0 3187286722eba kube-apiserver-kubemaster
И просто чтобы доказать, что мы работаем без etcd
, мы можем проверить пространство имен kube-system
и увидеть, что etcd
pod'ов нет:
kubectl get pods -n kube-system NAME READY STATUS RESTARTS AGE kube-controller-manager-kubemaster 1/1 Running 0 5d19h kube-scheduler-kubemaster 1/1 Running 0 5d19h kube-apiserver-kubemaster 1/1 Running 0 5d19h kube-proxy-mrfs5 1/1 Running 0 5d19h coredns-565d847f94-wrn82 1/1 Running 0 5d19h coredns-565d847f94-7zvwr 1/1 Running 0 5d19h
Или в качестве дополнительного теста мы можем развернуть в кластере какие-то ресурсы, например, эти.
kubectl get pods -n default NAME READY STATUS RESTARTS AGE example-deployment-78d75878cc-b56kl 1/1 Running 0 22h example-deployment-78d75878cc-4ftj5 1/1 Running 0 22h example-deployment-78d75878cc-bvpx6 1/1 Running 0 22h kubectl get all -n default NAME READY STATUS RESTARTS AGE pod/example-deployment-78d75878cc-b56kl 1/1 Running 0 22h pod/example-deployment-78d75878cc-4ftj5 1/1 Running 0 22h pod/example-deployment-78d75878cc-bvpx6 1/1 Running 0 22h NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 6d17h service/example-service ClusterIP 10.98.111.70 <none> 80/TCP 22h NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/example-deployment 3/3 3 3 22h NAME DESIRED CURRENT READY AGE replicaset.apps/example-deployment-78d75878cc 3 3 3 22h
И ура, мы видим, что все работает, что было бы невозможно без работающего резервного хранилища.
Изучение/ковыряние
С полностью запущенным кластером мы можем копаться и исследовать базу данных PostgreSQL, сначала мы войдем в систему:
psql -U postgres -p 5432 -h 127.0.0.1 # somepass
И тогда мы можем просмотреть схему и таблицы:
-- List tables: \dt public.* List of relations Schema | Name | Type | Owner --------+------+-------+---------- public | kine | table | postgres (1 row) -- Describe Kine table \d public.kine; Table "public.kine" Column | Type | Collation | Nullable | Default -----------------+------------------------+-----------+----------+---------------------------------- id | integer | | not null | nextval('kine_id_seq'::regclass) name | character varying(630) | | | created | integer | | | deleted | integer | | | create_revision | integer | | | prev_revision | integer | | | lease | integer | | | value | bytea | | | old_value | bytea | | | Indexes: "kine_pkey" PRIMARY KEY, btree (id) "kine_id_deleted_index" btree (id, deleted) "kine_name_id_index" btree (name, id) "kine_name_index" btree (name) "kine_name_prev_revision_uindex" UNIQUE, btree (name, prev_revision) "kine_prev_revision_index" btree (prev_revision)
Как вы можете видеть выше, есть единственная таблица с именем kine
, которая содержит все данные. Kine использует базу данных как хранилище с журнальной структурой, поэтому при каждой записи с сервера API создается новая строка, в которой хранится созданный или обновленный объект Kubernetes.
Давайте посмотрим на данные:
-- Some 1000+ rows select count(*) from public.kine; count ------- 1319 (1 row) select name, encode(public.kine.value, 'escape') as value_plain from public.kine where name like '/registry/pods/default/example%' limit 1; select name, encode(public.kine.value, 'escape') as value_plain from public.kine where name like '/registry/configmaps/default/example%' limit 1;
Столбец name
использует ту же структуру, что и etcd
— он указывает путь к объекту в кластере — /registry/RESOURCE_TYPE/NAMESPACE/NAME
. Столбец value
содержит фактический манифест в виде массива байтов. Здесь я опускаю фактический результат запроса, потому что декодированные данные довольно уродливы из-за декодирования пробелов и наличия данных "управляемых полей", но если вы попробуете это сами, вы сможете расшифровать это.
Масштабирование/производительность
Мы выяснили, как запустить Kubernetes без etcd
, но стоит ли? Какова производительность такого кластера и масштабируется ли он?
Как уже упоминалось, Kine и, следовательно, бэкенды RDBMS используются k3s, поэтому мы можем проверить документы по профилированию ресурсов k3s, чтобы сравнить, как работают базы данных SQL по сравнению с etcd.
k3s также имеет набор тестов с настраиваемой базой данных/серверной частью, так что вы можете запускать тесты и сравнивать, если хотите.
Для масштабирования и, в частности, PostgreSQL, вы также можете последовать совету в этой проблеме GitHub и хранить разные типы данных в разных таблицах, используя секционированные таблицы PostgreSQL.
Заключительные мысли
Я считаю, что нет никаких недостатков в использовании РСУБД вместо etcd
для кластера Kubernetes, но избавление от etcd
не решит всех ваших проблем. Каждый инструмент приносит свои собственные проблемы и проблемы, и то же самое относится к PostgreSQL или любой другой базе данных SQL.
Вам придется взвесить все за и против запуска вашего кластера с базой данных SQL для вашего конкретного случая использования, возможно, например, знакомый интерфейс SQL и ваш опыт в управлении СУБД перевешивают накладные расходы, хлопоты или любые возможные проблемы, которые могут возникнуть. приходят с заменой etcd
.
…или, может быть, просто используйте k3s 😉.
Want to Connect? This article was originally posted at martinheinz.dev.