DevOps-им по-взрослому
167 subscribers
79 photos
32 files
58 links
Download Telegram
#devops, #gitlab, #cicd

🦊 GitLab CI/CD: облегчаем работу с кодом для пайплайнов

☝️ GitLab это отличное решение при выборе системы управления репозиториями. Кроме этого, GitLab также позиционирует себя, как инструмент DevOps, имеющий в своём арсенале CI/CD, Container Registry и много других полезных инструментов. Остановимся на GitLab CI/CD. А именно на полезных штуках, которые могут облегчить вашу жизнь.

🏕 1) Окружения (environments)
Часто случается так, что у вас может быть несколько окружении. Для каждого, соответственно, используется своя ветка (develop, stage, main, etc).

🤓 С помощью GitLab окружении можно указать переменные с одинаковыми ключами, но при этом с разными значениями. Окружение для переменной назначается при её созданий, затем в Job-е необходимо указать, какое окружение вы будете использовать:

stages:
- build

Build For Development:
stage: setup
environment: development # !
image: ubuntu:22.04
only:
- develop
script:
- echo "Building project for development...."
- echo $KUBE_TOKEN # !

Build For Production:
stage: setup
environment: production # !
image: ubuntu:22.04
only:
- main
script:
- echo "Building project for production...."
- echo $KUBE_TOKEN # !


🙋‍♂️ Для Build Develop используется KUBE_TOKEN для подключения к девовскому кластеру, для Build Prod - продовскому. Но не всё так идеально...


👥 2) Переменные для группы (Group Variables)

Одна и та же переменная нужна для нескольких репозиториев. Что делать? Определить переменную на уровне группы!
Тут также есть окружения, но если у вас Free версия GitLab, то доступа к ним на уровне групп у вас не будет 🙁.

🤠 Но никто не мешает вам использовать префиксы! PROD_, STAGE_, DEVELOP_. При этом вы можете совмещать переменные на уровне репозиториев с переменными на уровне групп!


3) include и скрытые джобы

💭 Вы любитель разносить повторяющийся код по классам и обожаете DRY, но при этом вам приходится регулярно смотреть на ужасы, которые творятся в .gitlab-ci.yml файлах?

😵‍💫 Всё замечательно, пока у вас два-три репозитория. Но когда у вас их больше, при этом содержимое .gitlab-ci.yml повторяется, то приходят проблемы: в случае необходимости, нужно редактировать все файлы, чтение таких ямлов становится невыносимой задачей, код растягивается на тысячи строк. И тут мы можем воспользоваться include!

🔨 Он позволяет импортировать джобы для CI/CD в ваш текущий файл. Таким образом, вы можете переиспользовать одни и те же куски кода!

Сразу к примеру! В репозиторий 'my-templates' есть файл base-build.yml со следующим содержимым:

.build:
image: ubuntu:22.04

variables:
KUBE_URL: ""
KUBE_TOKEN: ""
script:
- echo "Set up connection to cluster $KUBE_URL with token $KUBE_TOKEN"


💡 Обратите внимание на точку перед названием джобы: это означает, что джоба является скрытой и служит шаблоном, поэтому запускаться не будет.

Теперь мы можем использовать эту скрытую джобу в других файлах или даже репозиториях!

🧑🏻‍💻 К примеру, есть репозиторий my-app-repo-1. "Заинклюдим" джобы из ранее созданного файла, при этом укажем путь до репозитория (группу/репо):

include:
- project: '<group_name>/my-templates'
ref: main
file: '/base-build.yml'


🙋 Теперь создадим таски для текущего проекта. Переопределим переменные и при помощи extends укажем в качестве шаблона .build джобу:

Build For Development:
extends: .build
stage: build
only:
- develop
variables:
KUBE_URL: DEVELOP_KUBE_URL
KUBE_TOKEN: DEVELOP_KUBE_TOKEN

Build For Production:
extends: .build
stage: build
only:
- main
variables:
KUBE_URL: PROD_KUBE_URL
KUBE_TOKEN: PROD_KUBE_TOKEN


Обратите внимание на переменные: мы можем их переопределять. Похоже на аргументы функций, не так ли?)

📌 Вы можете переопределить любое поле. Если вам нужно добавить какой-то дополнительный скрипт, то вы можете воспользоваться полями before_script и after_script, что также очень удобно!
4👎2🍌1🤓1
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
#devops, #aws, #cloud

В России 🇷🇺 уже давно душат зарубежные облачные провайдеры. Начиналось это с требования переноса личных данных граждан на территорию страны. Теперь взялись и за блокировку ресурсов, которые разворачиваются на серверах облаков таких как AWS. Но это не означает, что теперь мы не можем использовать ресурсы этих компании! Конечно, Amazon, Google, DigitalOcean следуют санкциям США, но физ.лица под них не попадают. Зарегистрировать аккаунт все еще можно, но указать Россию или Беларусь в качестве страны - нет.

🤓 Для подтверждения номера можно использовать онлайн сервисы для приема одноразовых СМС или же указать Казахстан и ввести российский номер. Это сработает, потому что у обеих стран код начинается с +7. С привязкой карты сложнее. Легче всего для россиян открывают счета в странах Центральной Азии (Кыргызстан, Узбекистан, Таджикистан). Можно открыть в Грузии, но с личным посещением. В любом случае придется платить за обслуживание $5-$10 в месяц 💸

Адрес можно указать любой, но будьте готовы, что AWS может запросить подтверждение. Так и случилось со мной после переноса сервера из одной зоны в другую. Поддержка облака запросила у меня доказательства моего проживания в Стамбуле 😬. Я получил копию договора с мобильным оператором, где был указан номер телефона и российский адрес, в настройках профиля AWS поменял место на этот адрес. И все сработало!

- А зачем мне это?
Опыт с российскими облаками ценится только внутри страны и в некоторых регионах СНГ. Конечно, имея опыт с условным Yandex.Cloud можно легко "вкатиться" в любое другое, но все же везде свои нюансы.

P.S. А еще AWS дает жирный Free tier, например целую ВМ бесплатно на год. Она отлично подходит для трёх букв 🤫
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
👨‍💻1