DevOps-им по-взрослому
167 subscribers
79 photos
32 files
58 links
Download Telegram
Хотели обновить версию своего кластера Kubernetes, но никак не доходили до этого руки?
"Самый милый релиз" UwUBernetes позволит вам сделать ваш кластер "симпатичнее"!
👍4🍌21
DevOps-им по-взрослому
2024-04-20_23-06.png
#devops, #monitoring, #k8s

🕵️‍♂️ Не отслеживаете метрики Kubernetes? Фатальная ошибка!

💡В одной из своих статей я писал о том, что мониторинг и логирование запущенных сервисов является одной из задачей в сфере DevOps. Я лишь частично прошёлся по самим метрикам, экспортировав готовый дашборд и уделив большее внимание установке нужных компонентов. Ну что же, на этот раз я решил больше углубиться в создание дашборда с нужными мне метриками. Начнём с полезных фишек и плавно перейдем к самому содержимому дашборда.

1) Используйте переменные.
И это касается не только программирования или GitLab CI. В Grafana для дашборда есть возможность создавать переменные, которые затем могут использоваться в запросах панелей. Зачем это нужно?

☝️ Для гибкости! Самый простой пример: переменные $prom_datasource и $namespace. С помощью первого можно задать источник данных, это полезно, если у вас их несколько (допустим, Prometheus в разных кластерах). $namespace поможет произвести фильтр по пространству имён. В моём дашборде есть и другие переменные, прошу к знакомству! Их практическое применение далее...

2) Используйте Rows.
Row это отдельная сущность дашборда, позволяющая группировать панели. Помимо этого, она позволит сделать ваш дашборд оптимизированным и быстрым!

🏃‍♂️ Если вы когда нибудь пробовали посмотреть кучу панелей одновременно, ваш браузер с большей вероятностью испытывал огромный стресс. Панели, принадлежащие "строкам", будут загружаться только после их раскрытия.

3) Настраивайте алерты
Я собрал именно те метрики, для которых собираюсь настраивать алертинг. Как показала "практика", всегда удобнее создавать алерт на базе панели. Плюсом вы получите сердечко в правом верхнем углу, показывающее статус алерта. ❤️‍🩹

Список Rows
Распределить панели я решил по следующим строкам:
- Logging
- Cluster (Nodes)
- Storage (Volumes)
- Replicas
- Requests/Limits
- Resource Real Usage (RAM/CPU/Network)

Расскажу о каждой по отдельности.

Row I: Logging
Одна панель с источником данных Loki:
{namespace=~"$namespace", container=~"$container"}

Ранее для логов я использовал отдельный дашборд с кучей панелей, где на каждой панели отображались логи для одного конкретного сервиса. Это было крайне неудобно, особенно для разработчиков, которым нужен регулярный доступ к логам. Колхоз! Гораздо удобнее делать это через переменные, как указано в запросе выше и с помощью одной панели.

Row II: Cluster
"Сырая" строка, содержащая совсем небольшую информацию о рабочих узлах, а именно количество узлов и использование CPU/RAM с возможностью фильтра по $namespace.

Row III: Storage
Просто список PVC. В своей работе я их не использую, но есть несколько томов для Loki и Prometheus.

Row IV: Replicas
Строка, содержащая панели, связанные с репликами и контейнерами. Тут целых пять панелей, которые позволят полностью отследить падение сервиса и его причину. Самый простой способ - повесить алерт на Container Restarts Total, тем самым отслеживая количество рестартов, а затем уже просматривать остальные панели. Но можно сделать алерт по Container Phase, чтобы отслеживать контейнеры со статусом "Failed" или "Pending" (об этом чуть дальше). Container Last Terminated Reason помимо проблемного контейнера отобразит метрику reason (например OOMKilled), где будет указана причина падения. Свободный простор для алертов!
P.S. NoData - нормальное состояние для панелей из этой строки. Это показывает, что всё ок

Row V: Requests/Limits
Очень важно отслеживать доступные для запросов и лимитов ресурсы, чтобы избежать неприятностей с запуском и работоспособностью подов! В строке Replicas есть панель, позволяющая отслеживать поды в статусе Pending, но лучше держать отметку свободного Memory на безопасной отметке. (на личном опыте).

Row VI: Resource Real Usage
Отслеживание потребления CPU/Memory подами и пропускной способность сети. Можно оформить в виде таблицы, что я сначала и сделал. Результат убил в прямом смысле слова, но не меня, а графану (до 10 раз возросло потребление RAM, OOMKilled из-за больших нагрузок 😁). Советую оставить в виде Time Series.

Ссылка на Dashboard: тык
👍1
DevOps-им по-взрослому
Photo
#devops, #security

👩‍💻 Checkov - статический анализатор кода

🧑‍💻 Я достаточно много пишу про подход IaC (инфраструктура как код). Особое отношение у меня к Terraform 👩‍💻, который используется чуть ли не в каждой моей статье на Хабре. А сейчас я стараюсь больше погружаться в безопасность инфраструктуры и сервисов.

Недавно я открыл для себя утилиту checkov, позволяющую находить уязвимости в коде. Он поддерживает множество расширении файлов. (так называемые фреймворки). Я сразу же пошёл проверять репозитории с Helm-чартом и инфраструктурой, описанной в Terraform.

😎 Ещё одна причина перевести свою инфраструктуру в код - анализ на уязвимости!

🖼️ Установить checkov проще простого: через pip:

pip3 install checkov


Теперь можно запустить анализатор. Если не указать фрейморк, то checkov сам поймёт для каких файлов нужно выполнить проверку. Выполним для Terraform репозитория. Укажем дополнительный параметр --download-external-modules true, который при необходимости сам установит нужные модули:

checkov --download-external-modules true -d .


Взглянем на Failed check-и и пофиксим их! Пока только для Terraform...

Check: CKV_SECRET_6: "Base64 High Entropy String"


🚫 Не храните секреты в коде, даже если репозитории приватный! Допустим, у нас в terraform есть переменная с параметром sensitive = true:

variable "redis_password" {
sensitive = true
}


👀 Не стоит указывать значения переменных в коде! Воспользуйтесь переменными окружениями. Создайте .env файл и поместите его в .gitignore:
export TF_VAR_redis_password="your_password"

Обратите внимание, что перед названием переменной необходимо добавить префикс, чтобы Terraform его смог распознать и использовать.

☺️ Fixed


Check: CKV_YC_11: "Ensure security group is assigned to network interface."


😭 checkov не очень любит, когда вы забываете о сетевой безопасности, поэтому просит настраивать для каждого ресурса облака группы безопасности. И это правильный подход: таким образом вы сможете защитить порты вашего приложения от злоумышленников.

✔️ Пофиксим уязвимость: добавим группу безопасности для ВМ, разрешающую входящий трафик 22 порт (TCP) из приватной сети, а также доступ из любого устройства для 443 (TCP):

resource "yandex_vpc_security_group" "vm-access" {
<...>
ingress {
protocol = "TCP"
description = "Правило разрешает доступ к VM по SSH из приватной сети."
v4_cidr_blocks = ["172.16.0.0/12", "10.0.0.0/8", "192.168.0.0/16"]
port = 22
}

ingress {
protocol = "TCP"
description = "Правило разрешает доступ к VM по HTTPS для любых адресов."
v4_cidr_blocks = ["0.0.0.0/0"]
port = 443
}
}


Теперь назначим созданную группу безопасности виртуальной машине:

resource "yandex_compute_instance" "vm" {
<...>
network_interface {
<...>
security_group_ids = [
yandex_vpc_security_group.vm-access.id
]
}
}


🍷 Fixed

Check: CKV_YC_23: "Ensure folder member does not have elevated access."


😈 Примитивные роли для пользовательских/сервисных аккаунтов представляют большую угрозу, если вы за ними не следите. Но бывают моменты, когда они просто необходимы. Например, Yandex.Cloud просит указать сервисный аккаунт для Managed K8s с ролью editor:

resource "yandex_resourcemanager_folder_iam_binding" "editor" {
folder_id = var.folder_id
members = [
"serviceAccount:${yandex_iam_service_account.k8s-resource.id}"
]
role = "editor"
}


Что делать в таком случае? Можно воспользоваться параметром, который позволит пропустить проверку:
checkov --skip-check CKV_YC_23 -d .


У такого подхода есть свой явный минус - человеческий фактор. Мы скипаем ВСЕ проверки на примитивные роли, что может повлечь к назначению таких ролей для других аккаунтов. Поэтому укажем скип проверки для конкретного ресурса прямо в коде:

<...>
# checkov:skip=CKV_YC_23:These service account should have primitive roles
role = "editor"


😄 Fixed


Это, конечно же, не все уязвимости, которые обнаружил checkov. В следующий раз рассмотрим Helm чарт 🖼️ и безопасность в Kubernetes 🖼️. Всем добра 👋
Please open Telegram to view this post
VIEW IN TELEGRAM
7🥱1
#devops, #k8s, #postgresql

👩‍💻 Базы данных в K8s

👀 За всё время работы с Kubernetes я имел дело только со Stateless приложениями. Я привык к такому мнению, что Docker контейнеры не предназначены для хранения данных.

🫣 Но в K8s немного иначе... Я успел посетить несколько тех. собеседовании, и одним из самых популярных вопросов про кубер оказался "Разница между Deployment и StatefulSet". Ответ вроде "первый не предназначен для хранения состояния" не совсем устраивал интервьюеров, они просили объяснить разницу на уровне реализации. И тут я начинал плыть...

⚙️ Ведь если так посмотреть, то K8s даёт всё, чтобы запускать те же самые базы данных... Сущности PV/PVC, возможности монтирования, абстракция StatefulSet (название говорит само за себя). Летс го запускать свой постгрес в кубере!

Для локального запуска Kubernetes советую к использованию k3s: он намного быстрее разворачивается и имеет больше возможностей, чем minikube. В случае с запуском PostgreSQL не будем изобретать велосипед: воспользуемся оператором CNPG (также частый вопрос на собеседованиях: CRD и Helm-чарты)

Итак, установим оператор:

helm repo add cnpg https://cloudnative-pg.github.io/charts
helm --kubeconfig ~/.kube/k3s upgrade --install cnpg \
--namespace cnpg-system \
--create-namespace \
cnpg/cloudnative-pg


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

apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: postgresql
namespace: default
spec:
instances: 3
imageName: ghcr.io/cloudnative-pg/postgresql:16.2-14
storage:
pvcTemplate:
resources:
requests:
storage: 5Gi
resources:
requests:
memory: "1Gi"
cpu: 1
limits:
memory: "1Gi"
cpu: 1


🤓 После принятия манифеста, кубер развернёт три пода с инстансами PostgreSQL (1 master, 2 replicas), для каждого пода будет создан Persistence Volume и свой ClusterIP сервис для обращения.

Так, чтобы использовать Master реплику (Write+Read) используется postgresql-rw сервис, тогда как для Replica (Read) - postgresql-ro

Узнаем пароль от базы данных, имя пользователя и название базы данных:
# k_k3s get secret/postgresql-app -o json | jq -r .data.password | base64 -d
UVWVz78DLD5Nc0M0nq6HKFyUUYjoN9Z2KdKjzxKp8adnzFVyUiA8lxeqGANVhpmz
# k_k3s get secret/postgresql-app -o json | jq -r .data.dbname | base64 -d
app
# k_k3s get secret/postgresql-app -o json | jq -r .data.user | base64 -d
app


Запустим под с psql:

k_k3s run -it --rm psql-client --image=postgres:16.3-alpine --restart=Never --command bash


Создадим таблицу и добавим в неё данные: https://pastebin.com/sC2qSgzu

Подключимся к реплике:

# psql \
--host=postgresql-ro \
--port=5432 \
--username=app \
--password \
app
app=> select count(*) from users;
count
-------
14
(1 row)

app=> insert into users (username, age) values ('A', 5);
ERROR: cannot execute INSERT in a read-only transaction

```

✔️ Все данные были успешно реплицированы с мастера на реплики. При этом при попытке сделать insert, реплика выдаст ошибку.



☺️ Это самая базовая конфигурация PostgreSQL в Kubernetes. CNPG предоставляет огромные возможности из коробки (бекапы, репликация, мониторинг&логирование и прочее), что избавляет от ручной настройки.

Такой кластер весьма легко развернуть. Но стоит учитывать, что в k8s шанс наступить на грабли в разы больше, чем при развертывании на классической виртуальной машине, особенно в прод средах. Всем добра 🍌
Please open Telegram to view this post
VIEW IN TELEGRAM
👏3
#postgresql, #backups

👩‍💻 Администрируем PostgreSQL. Часть I: Бэкапы via built-in tools

Я люблю DBaaS. Они просты в настройке и эксплуатации, так как облачный провайдер берёт на себя большую часть ответственности, например задачи по репликации на Replica-хосты, созданию резервных копий или соответствию законам.

🤔 Но представьте, что вам неожиданно дадут чистую ВМ и скажут: "Выкатывай в продакшен!" Страшно? Ну что же, го.

Запускаем слоника через docker compose (на проде так не делаем, т.к запускать StateFull приложения в контейнере - плохая затея)

https://pastebin.com/tMAH1pQ5

docker compose up -d


Подключаемся, создаём таблицу и заполняем её:
https://pastebin.com/tp6rVjPV

Теперь рассмотрим возможности для создания резервных копии.

1) SQL-дампы: pg_dump & pg_dumpall

При помощи pg_dump мы можем создать SQL-дамп для конкретной БД, тогда как с pg_dumpall - для всего кластера:

pg_dump \   
--host=localhost \
--port=5432 \
--username=dev_user \
--password \
users > users_dump.sql


users_dump.sql будет содержать все команды, необходимые для восстановления базы данных.

❗️ Но тут не всё так идеально. Хотя база данных в момент создания дампа не блокируется, для достаточно "жирных" баз операции создания и восстановления из такого дампа могут занять достаточно большое время.

Восстановим базу данных на другом кластере:

psql \
--host=localhost \
--port=5433 \
--username=dev_user \
--password \
users < users_dump.sql

users=# select count(*) from users;
count
-------
14
(1 row)


⚙️ 2) Физический бэкап: pg_basebackup

🤓 pg_dump называют логическим бэкапом, так как он содержит SQL-команды. Для физического бэкапирования используется утилита pg_basebackup. Преимущество таких бэкапов над дампами в том, что создание через pg_basebackup будет в разы быстрее для большого кластера, чем через pg_dumpall.

🤠 Добавим базу данных replication и назначим dev_user все права:

users=# create database replication;
CREATE DATABASE
users=# grant all privileges on database replication to dev_user;
GRANT


🧐 Добавим в pg_hba.conf строчку, которая позволит подключаться к базе данных для создания резервной копии:

host    replication           dev_user        0.0.0.0/0               password


Создадим физический бэкап:

pg_basebackup --host=localhost --port=5432 --username=dev_user -D ./main-postgres-data


👣 Теперь пробросим только что созданный бэкап в restored контейнер:

services:
main:
[...]

restored:
[...]
volumes:
- ./main-postgres-data:/var/lib/postgresql/data


🎩 Подключимся по psql к restored контейнеру и убедимся, что данные были восстановлены успешно:

psql \
--host=localhost \
--port=5433 \
--username=dev_user \
--password \
users
Password:
psql (14.11 (Ubuntu 14.11-0ubuntu0.22.04.1), server 14.1)
Type "help" for help.

users=# select count(*) from users;
count
-------
14
(1 row)


Таким образом, PostgreSQL обладает огромным количеством утилит для создания резервных копии. Мы рассмотрели команды, идущие "из коробки". Но их хватает не всегда: про другие инструменты бэкапирования я напишу в следующем посте. Замечательных выходных! 🤟
Please open Telegram to view this post
VIEW IN TELEGRAM
🐳3👍1
#cloud, #s3, #linux, #data

🤓 А вы знали, что S3 можно использовать как файловую систему или замену классическим облачным хранилищам? Да-да, это чистая правда!

🫥 S3 это отличное решение для хранения больших данных. Оно дешёвое, надёжное (облачный провайдер обеспечивает сохранность данных за счёт хранения данных в нескольких ДЦ) и имеет простой API. Обычно взаимодействие с ним происходит через SDK, который есть почти на каждом популярном языке программирования. Но в этом посте ЯП использоваться не будут.

S3 уже давно стал стандартом и относится не только к объектному хранилищу, разработанного AWS. Многие облачные провайдеры предоставляют свои сервисы, совместимые с S3, например Yandex Object Storage.

💬 Если вы создали бакет, то можете начать использовать хранилище по его прямому назначению. Например, загрузим некоторые фотографии или видео (1 скриншот, после выпускного их очень много :D )

Но это не совсем удобно. Каждый раз заходить на сайт и загружать нужные файлы ручками...

Давайте примонтируем бакет к устройству! Итак, я уже создал бакет и получил ACCESS_KEY/SECRET_KEY (тык и в документацию). Установим s3fs утилиту, которая позволит нашим планам сбыться, сохраним пару ключей в ~/.passwd-s3fs и сразу же попробуем примонтировать бакет:

sudo apt install s3fs
export ACCESS_KEY=YCAJENkZCs1eLXRWlkUzWcNpE
export SECRET_KEY=YCNVDcOukw3aIFvobPe_qnFOVv-P65EBrxn3x671
echo "$ACCESS_KEY:$SECRET_KEY" > ~/.passwd-s3fs && chmod 600 ~/.passwd-s3fs
s3fs azamat-komaev-file-system /mnt/ -o passwd_file=$HOME/.passwd-s3fs \
-o url=https://storage.yandexcloud.net -o use_path_request_style


👌 Теперь если перейти в /mnt и выполнить ls, то можно увидеть список всех файлов. Но можно не только считывать данные, но и записывать!

🖥 Помимо этого, есть мобильные клиенты, например, я нашёл S3Drive. Можно аналогично файлу при монтировании указать нужные доступы и вы без проблем сможете совершать те же самые действия с мобильного устройства! (скриншот 2 и скриншот 3)

💸 Это всё замечательно, но сколько всё это стоит? Всё просто! Посмотреть правила тарификации можно тут.

Оплата производится за:
Хранение данных: 2,01 рубля/ГБ, 1,07 рублей/ГБ, 0.5350 рублей/ГБ (зависит от класса хранилища)
Операции с данными: за PUT/POST/GET/etc (проверьте таблицу по ссылке сверху)
Исходящий трафик: за ГБ

🤑 При этом у Yandex Object Storage есть жирный Free tier, который позволяет бесплатно использовать 1ГБ хранилища, первые N операции и до 100ГБ исходящего трафика.

Всем добра 😄
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
#kafka, #education

💸 Взял платный практикум по Kafka. Но зачем?

😐 Везде, где используют микросервисы, с огромной вероятностью вам придется иметь дело с Kafka. Для меня данный инструмент является чем-то неизведанным. Я имел ограниченный опыт взаимодействия с ним, нажать кнопку в GUI облака и получить сконфигурированный кластер - на этом всё.

👍 Так не могло продолжаться дальше, и в один момент я решил выйти из зоны комфорта.... Вернее основным поводом стала частота появления в вакансиях на позицию DevOps инженера

Я не знал с чего начать изучение Kafka, поэтому решил взять практикум от Rebrain. До этого я никогда не платил за какие-либо курсы, но тяжелые времена требуют тяжелых решений.

Перед тем, как приобрести курс, я конечно же попробовал сам... Но сразу же запутался во внутреннем устройстве Kafka. Топики, партиции, фактор репликации... Это всё сводит с ума 😩

Но это было только начало... Люди, имеющие дело с разработкой на Java, знают боль, которая происходит при работе с TLS-сертификатами. Если ваше приложение ходит к защищенным сервисам (например, Sonatype Nexus), то от страдании с keytool и truststore вам не убежать.

🖼️ Kafka написана на Java. Поэтому если вы хотите, чтобы общение кластер-кластер/клиент-кластер было зашифровано, то у вас должно быть два хранилища - Trust store и Key store. В первом хранятся сертификаты, которым мы доверяем, во втором - необходимые для конфигурирования на стороне сервера. И у каждого Kafka-брокера должен быть свой Trust store и Key store...

Конечно, Kafka позволяет использовать разные порты и протоколы путём настройки слушателей. Например:

listeners=SSL://0.0.0.0:9092,PLAINTEXT://192.168.135.1:9093


🧑‍💻 Так для доступа из интернета мы используем защищенное соединение, тогда как из приватной сети (192.168.x.x) - без шифрования. Затем мы можем указать какой протокол использовать для общения между брокерами:

security.inter.broker.protocol=PLAINTEXT


Но погодите... Если мы запускаем кластер из нескольких Kafka-брокеров, то как они тогда будут синхронизировать свое состояние? Тут нам приходит на помощь Zookeeper или модный-молодёжный Kraft.

😐 Если мы используем Zookeeper, то нам необходимо развернуть в нашем контуре отдельный компонент, при этом их количество советуют приводить к количеству брокеров. А это ведёт к тому, что этот компонент нужно поддерживать, мониторить и дополнительно производить конфигурацию на стороне брокеров.

Поэтому Zookeeper был помечен Deprecated, и в новых версиях Kafka советуют использовать KRaft. Он позволяет хранить всю информацию о кластере в самих брокерах. В кластере путём голосования выбирается лидер. Затем Follower'ы реплицируют данные с лидера. Подробнее читайте тут.

🔜 Что дальше? Раздел администрирования я почти завершил, дальше - использование на стороне разработки.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
#linux

🤬 Забыли пароль от root? Не проблема!

Не думал, что столкнусь с этим, но это случилось. Две недели назад я ставил Kubernetes 🖼️ кластер через VirtualBox, в качестве ОС использовался Debian 👩‍💻. И вот я захотел вернуться к кластеру, но не тут то было.... Я перепробовал пароли, которые обычно ставлю на локальные машины (admin, 1234, etc), но ничего из этого не подошло. Благо я настроил доступ по SSH-ключу, но sudo без пароля не работает. 🤡

Т.к виртуальный диск не зашифрован, то сбросить пароль можно с помощью утилиты System Rescue.

Можно сделать загрузочную флешку, если ОС у вас поставлена на ПК. Так как мне нужно восстановить доступ к ВМ, то делать буду через VirtualBox.

(1) Зайдем в настройки ВМ -> Storage -> Controller: IDE. Тут снизу нажмем на диск, а затем на "Choose a virtual optical disk". Укажем iso образ. Затем перейдем в раздел System и поменяем Boot order (2). 🤓

Загрузимся и увидим командую оболочку. Получим список всех разделов и смонтируем тот, где хранится ОС. Сменим корневую директорию и выполним passwd (3). DONE!

fdisk -l
mount /dev/sda1 /mnt
arch-chroot /mnt
passwd azamat


Снова сменим порядок загрузки и попробуем ввести новый пароль (3). DONE! 😮
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5❤‍🔥1🔥1