DevOps
Заметки по инструментам направления DevOps.
Git
Config
# Настройка автора для коммитов
# Изменить редактор по умолчанию для сообщений коммитов и интерактивного rebase
# Выпускаем ключ и копируем в https://github.com/settings/keys
# Добавляем ключ в ssh-agent для сохранения passphrase в оперативной памяти для последующих команд
Автоматически добавлять ключ в агента при перезагрузки системы через файл ~/.ssh/config
# Host *
# AddKeysToAgent yes
# IdentityFile ~/.ssh/id_rsa
Базовые команды:
# Глобальный поиск текста во всех файлах проекта
# Показать список всех файлов, которые отслеживает Git
# Удалить файл из индекса и при этом оставить его физически на диске (например, если файл был добавлен в файл .gitignore)
# Применить .gitignore ко всем файлам заново
# Удалить все неотслеживаемые файлы на диске (которые добавлены в файл .gitinore)
# Показать, что будет удалено (режим dry-run)
Clone/WorkTree
# Клонируем репозиторий по протоколу https
# Создаем новую рабочую область из ветки dev (при этом будет синхронизация изменений между директориями и одна директория .git)
# Удалить рабочаю область или очистить список при удаление директории
Submodules
Submodules - это механизм встраивания одного Git-репозитория внутрь другого как дочернюю директорию. Например, если есть библиотека или тема оформления, которая используется в множестве разных проектах, и чтобы не копировать файлы проекта вручную (и не обновлять их потом везде), подключается подмодуль.
# Создать и инициализировать новый сайт
# Инициализировать новый Git репозиторий в текущем каталоге
# Добавляем новый репозиторий как модуль
# Скопировать markdown документацию в директорию content и запустить сервер
При подключение первого submodule, в репозитории создается файл .gitmodules, который хранит пути к подмодулям. При обычном клонирование, директории подмодулей будут пустыми.
# Загрузить все подмодули при клонирование репозитория
# Если проект уже клонирован, но подмодули не были загружены
# Обновить изменения для подмодулей
Когда вы меняете версию подмодуля, Git видит это не как изменение файлов, а как изменение ссылки на коммит:Subproject commit a1b2c3d4…
Fetch/Pull
# Загружаем изменения из удаленного репозитория, не затрагивая локальные изменения
# Выполняет git fetch и git merge (если был локальный commit, создается новый merge commit) для объединения изменений с локальной копией репозитория
# Вытягиваем изменения из удаленного репозитория и накладывает поверх локальные изменений (избегая git merge commit)
# Включить rebase по умолчанию
Commits
# Отобразить статус изменений в файлах локального репозитория
# Отобразить историю изменений с последнем коммитом в указанной ветке
# Добавить (проиндексировать) изменения во всех файлах
# Сохранить изменения с комментарием
# Изменить комментарий (до push) или дату последнего коммита
# Синхронизировать локальные изменения с удаленным репозиторием на сервере
# Отправить изменения в конкретную ветку
Branches/Tags
# Отобразить все локальные и удаленные (-r) ветки
# Создать новую ветку
# Переключиться на другую ветку
# Переименовать ветку, отправить в удаленный репозиторий и удалить ее локально
# Вывести список тегов
# Создать легковесный тег с ссылкой на коммит
# Создать аннотированный тег
# Запушить указанный тег или все в удаленный репозиторий
# Получить подробную информацию по тегу и коммиту под ним
# Переключиться на коммит по тегу
# Создать новую ветку feature/1.0 на основе тега 1.0
# Удалить тег локально и в удаленном репозитории
Merge/Cherry-Pick
# Слияние изменений из указанной ветки (zola-duckquill) в текущую ветку (на которой стоим)
# Влить изменения указанного коммита в текущую ветку, что позволяет не дублировать код и избегать конфликтов при последующем git merge
# Добавляет комментарий к коммиту в формате - cherry picked from commit a1b2c3d...
# Перенести несколько коммитов
Stash
Stash - это временное локальное хранилище, которое сохраняет текущие непроиндексированные изменения (до git commit), возвращая к состоянию последнего коммита. Например, на время смены ветки (git switch) или выполнения git pull, что пользволяет избежать временных коммитов (для возможности смены ветки без потери данных) и коммитов слияния (git merge) перед или во время git push.
# Сохранить текущее состояние
# Сохранить все измененные файлы, включая те, что еще не были проиндексированны через git add
# Применяет изменения из stash к текущей ветке (вернутся только измененные строки в файлах, при этом будут сохранены новые добавленные строки)
# Удалить сохранку по номеру
# Создать новую ветку на момент состояния создания stash с содержимым сохранения (полезно при возможных конфликтах слияния)
# Применяет и удаляет изменения из stash
Logs
# Отключить прокрутку (включить --no-pager для всех команд по умолчанию)
# Отобразить историю коммитов для всех веток (--all) в сжатом формате (--oneline) или с изменениями в файлах (--patch)
# Отобразить историю коммитов указанного пользователя
# Найти все коммиты, где в коде указанный текст в строке добавлися иди удалился
# Отобразить историю локальных изменений с момента клонирования репозитория и удаленные коммиты, которых уже нет в истории
# Отобразить изменения в файлах (diff) по номеру коммита
# Отобразить кто и когда внес изменения в каждую строку указанного файла
# Узнать какой коммит принес изменения в указанные строки файла
Reset
# Отменить не проиндексированные (до git add) изменения содержимого в файле, возвращая к состоянию на момент последнего коммита
# Отменить индекацию (git add README.md) без изменения содержимого в файле
# Восстановить файл на указанную версию по хэшу коммита
# Аналог команды git restore --staged README.md (если был git add но не было git commit)
# Отменяет последний (^ или ~1) коммит, сохраняя изменения из этого коммита в рабочем каталоге и индексе, позволяя внести изменения в файлы и повторно их зафиксировать
# Отменяет последние 5 коммитов, сохраняя все локальные изменения, для последующего их объединения в один коммит (like squash from rebase)
# Откатывает изменения к указанному коммиту и удаляет все коммиты, которые были сделаны после него
# Отменяет последний коммит, удаляя все его изменения из рабочего каталога и индекса до состояния последнего коммита
# Удалить последний коммит в удаленном репозитории (после git reset --hard)
# Создает новый коммит, который отменяет изменения последнего коммита (тем самым сохраняя историю коммитов, в отличии от reset, который удаляет коммиты)
# Создает новый коммит, который отменяет изменения, внесенные в указанный коммит (удалит то, что было добавлено, и вернет то, что было удалено) - возможны конфликты
Rebase
Rebase - интерактивный интерфейс для изменения истории коммитов.
# Открыть последние пять коммитов
pick- оставить коммит без измененийreword/r- изменить только сообщение коммитаsquash/s- склеить этот коммит с предыдущим (сообщения объединятся)fixup/f- склеить коммит с предыдущим и удалить его сообщениеedit/e- остановиться на этом коммите, чтобы изменить кодdrop/d- полностью удалить указанный коммит из истории
Пример:
drop a1b2c3d Доработал кнопку обновления
r e5f6g7h Добавил новую кнопку для повторного обновления
f i9j0k1l Исправил цвет кнопки
Если во время rebase возник конфликт, после правок делается git add . и вместо коммита git rebase --continue.
# Откатить все действия до начала rebase
# Запушить изменения с проверкой новых коммитов в удаленном репозитории перед изменением истории
Bisect
Bisect - механизм поиска виновного коммита. Гит всегда прыгает в середину, по этому, даже если прошло 1000 коммитов, можно найти виновного максимум за 10 шагов.
# Запускает процесс поиска
git bisect start
# Пометить текущий коммит как плохой
git bisect bad HEAD
# Пометить старый известный коммит как плохой (по тегу или хешу коммита)
git bisect good 0.8.0 # f6034e9
# Git сам переключает коммиты, можно проверять код и помечать коммиты
# Если баг не воспроизводится
# Если баг есть
git bisect good
git bisect bad
# По завершению Git напишет "... is the first bad commit", смотрим изменения:
git show
# Завершить bisect и вернуться на свою ветку
git bisect reset
# Автоматический поиск с использованием теста, который возврощает код возврата
git bisect start HEAD v1.0.0
git bisect run go test ./...
Remote
Добавление второго сервера Git:
# Посмотреть текущий URL удаленного репозитория (для origin - где стоим)
# Загружаем все ветки из текущего репозитория
# Добавляем новый удаленный репозиторий на другом сервере или изменяем его url
||
# Загружаем все ветки из нового репозитория
# Переключаемся на ветку нового репозитория (куда вливаем)
# git checkout -B main gitlab/main
# Выводим изменения перед объединением
# Вливаем изменения из локального репозитория GitHub (по названию указанной ветке) в удаленный (на которой стоим)
# Отправляем изменения в удаленный репозиторий
SQL
Базовые инструкции
CREATE- создает новые объекты, такие как базы, таблицы, индексы (CREATE TABLE users (id INT, name TEXT, status TEXT)).SELECT- выборка (чтение) данных ИЗ таблицы по столбцам (SELECT * FROM usersилиSELECT name FROM users).INSERT- добавление новых строк В таблицу (INSERT INTO users (id, name, status) VALUES (10, 'alex', 'active'),(11, 'jack', 'active')).UPDATE- обновление существующие данных (UPDATE users SET status = 'inactive' WHERE id = 10). Сначала находим нужные строки с помощью фильтра (SELECT * FROM users WHERE id = 10) и потом обновляем.DELETE- удаление конкретных строк (DELETE FROM users WHERE status = 'inactive').ALTER- изменение структуры - добавление или удаление полей/столбцов (ALTER TABLE users ADD age INT).DROP- полностью удаляет таблицу или базу данных (DROP TABLE users).TRUNCATE- мгновенно очищает таблицу от всех данных, оставляя пустую таблицу (TRUNCATE TABLE users).
Ключевые слова
FROM- указывает таблицу-источник (SELECT name FROM users).JOIN- соединяет несколько таблиц по общему полю (JOIN users_orders ON users.id = orders.user_id).WHERE- фильтрует строки по условию (WHERE age >= 18).GROUP BY- группировка строк по значению поля (например, узнать количество пользователей в состояние каждого отдельного статуса с помощью функцииCOUNT(*)-SELECT status, COUNT(*) FROM users GROUP BY status)HAVING- фильтрует уже сгруппированные данные (SELECT status, COUNT(*) FROM users GROUP BY status HAVING COUNT(*) > 5).ORDER BY- сортирует результат (по возрастанию/убыванию) (SELECT * FROM users ORDER BY name ASCилиDESC).LIMIT/TOP- ограничивает количество выводимых строк (например, только первые 10) (SELECT * FROM users LIMIT 10).
Логические операторы
AND- логическое И (SELECT * FROM users WHERE status = 'active' AND age >= 18).OR- логическое ИЛИ (SELECT * FROM users WHERE status = 'active' OR status = 'inactive').NOT- отрицание (SELECT * FROM users WHERE NOT status = 'inactive').IN- проверка по списку (SELECT * FROM users WHERE age IN (18, 20, 25, 30)).BETWEEN- проверка диапазона (SELECT * FROM users WHERE age BETWEEN 18 AND 30).LIKE- поиск по шаблону (SELECT * FROM users WHERE name LIKE 'alex%', найдетalexиalexander).IS NULL- поиск пустых значений (SELECT * FROM users WHERE email IS NULL).
Функции вычисления
COUNT()- считает количество строк (например, общее число пользователей -SELECT COUNT(id) FROM users).SUM()- считает сумму чисел (например, общая сумма баланса всех пользователей -SELECT SUM(balance) FROM usersили сумма баланса пользователей для всех статусовSELECT status, SUM(balance) AS total_balance FROM users GROUP BY status).AVG()- вычисляет среднее значение (например, средний возраст -SELECT AVG(age) FROM users).MAX()/MIN()- находит максимальное или минимальное значение (например, самого взрослого пользователя -SELECT MAX(age) FROM users).DISTINCT- получить список уникальных значений (SELECT DISTINCT name FROM users)
Операторы объединения
UNION- склеивает результаты двух запросов в один список, убирая дубликаты (SELECT name FROM users UNION SELECT name FROM admins).UNION ALL- склеивает результаты двух запросов, сохраняя все дубликаты (SELECT name FROM users UNION ALL SELECT name FROM admins).INTERSECT- оставляет только те строки людей, которые есть в обоих списках (SELECT name FROM users INTERSECT SELECT name FROM admins).EXCEPT/MINUS- вычитает данные одного запроса из другого (например, оставить только список пользователей не являющихся админамиSELECT name FROM users EXCEPT SELECT name FROM admins).
Настройка ролей
В PostgreSQL пользователь и роль - это одно почти и то же, разница заключается только в праве на вход в систему. По умолчанию команда CREATE ROLE создает роль без права входа (NOLOGIN), а команда CREATE USER — это удобный псевдоним для CREATE ROLE … LOGIN.
-- Создать пользователя с паролем
;
-- Создать только роль
CREATE ROLE gatusdb_admin;
-- Дать права на логин (создать пользователя из роли)
ALTER ROLE gatusdb_admin WITH LOGIN PASSWORD '123098';
-- Предоставить разрешения на создание базы данных для роли
ALTER ROLE gatusdb_admin CREATEDB;
-- Разрешить создание других пользователей
ALTER ROLE gatusdb_admin CREATEROLE;
-- Дать права суперпользователя (полный доступ)
-- ALTER ROLE gatusdb_admin SUPERUSER;
-- Создать базу данных
;
-- Дать права роли на подключение к базе данных
CONNECT ON DATABASE gatusdb TO gatusdb_read,gatusdb_admin;
-- Дать права роли на просмотр схемы
USAGE ON SCHEMA gatusdb TO gatusdb_read,gatusdb_admin;
-- Разрешить указанные действия (SELECT,INSERT или ALL) с всеми таблицами (ALL TABLES) в схеме gatusdb для роли gatusdb_admin
SELECT,INSERT ON ALL TABLES IN SCHEMA gatusdb TO gatusdb_admin;
Настройка схем
В PostgreSQL схема - это именованное пространство имен (namespace), содержащее независимые объекты базы данных (таблицы, представления, типы, функции) от других namespaces.
-- Удаляем схему gatusdb со всеми вложенными объектами, если она уже существует
IF EXISTS gatusdb CASCADE;
-- Создаем схему gatusdb (только если она еще не существует) и назначаем ее владельцем роль gatusdb_admin
;
-- Предоставляем полные права (USAGE + CREATE) роли gatusdb_admin на схему
ALL ON SCHEMA gatusdb TO gatusdb_admin;
-- Предоставляем права только на чтение для роли gatusdb_read
USAGE ON SCHEMA gatusdb TO gatusdb_read;
-- Предоставляет полные права по умолчанию для всех будущих создаваемых таблиц внутри схемы
ALTER DEFAULT PRIVILEGES IN SCHEMA gatusdb ALL ON TABLES TO gatusdb_admin;
-- Предоставляет права только на выполнение команд SELECT (или к примеру SELECT,UPDATE,INSERT)
ALTER DEFAULT PRIVILEGES IN SCHEMA gatusdb SELECT ON TABLES TO gatusdb_read;
-- Предоставить права на текущие таблицы
ALL PRIVILEGES ON ALL TABLES IN SCHEMA gatusdb TO gatusdb_admin;
SELECT ON ALL TABLES IN SCHEMA gatusdb TO gatusdb_read;
-- Предоставить права на последовательность (порядковый номер при INSERT)
ALTER DEFAULT PRIVILEGES IN SCHEMA gatusdb ALL ON SEQUENCES TO gatusdb_admin;
ALTER DEFAULT PRIVILEGES IN SCHEMA gatusdb SELECT, USAGE ON SEQUENCES TO gatusdb_read;
ALL ON ALL SEQUENCES IN SCHEMA gatusdb TO gatusdb_admin;
SELECT, USAGE ON ALL SEQUENCES IN SCHEMA gatusdb TO gatusdb_read;
Таблицы связей
Таблица Actors содержит только личные данные:
id name
- -
1 Киану Ривз
2 Лоренс Фишберн
Таблица Movies содержит только данные о фильмах:
id title
- -
101 Матрица
102 Джон Уик
Промежуточная таблица Movie_Actors содержит только пары для связи идентификаторов:
actor_id movie_id
- -
1 101
2 101
1 102
Получить список всех актеров фильма Матрица с помощью JOIN:
SELECT Actors.name
FROM Actors
JOIN Movie_Actors ON Actors.id = Movie_Actors.actor_id
JOIN Movies ON Movies.id = Movie_Actors.movie_id
WHERE Movies.title = 'Матрица';
-- или используя псевдонимы для сокращения
SELECT a.name
FROM Actors AS a
JOIN Movie_Actors AS ma ON a.id = ma.actor_id
JOIN Movies AS m ON m.id = ma.movie_id
WHERE m.title = 'Матрица';
Вывод:
name
Киану Ривз
Лоренс Фишберн
Docker
Docker - это платформа для контейнеризации, которая упаковывает приложение и все его зависимости (библиотеки, настройки) в изолированный контейнер. Контейнеры делят ядро ОС хоста, тогда как ВМ используют гипервизор для эмуляции оборудования и запускают полноценную гостевую ОС. Контейнеры изолированы на уровне процессов, быстро разворачиваются и позволяют экономить ресурсы по сравнению с ВМ.
&& &&
# Добавить пльзователя в группу docker
Компоненты Docker:
Docker Daemon(docker.service/dockerd) - процесс, который работает в фоне и управляет образами, сетями и томами черезsocket. Если сервис остановлен, управление контейнерами невозможно. Самdockerdне запускает контейнеры напрямую, а делегирует процессcontainerd.containerd- процесс, который отвечает за запуск контейнеров - скачивает образы из реестра, управляет хранилищем (все данные хранятся в/var/lib/docker/) и следит за состоянием работающих контейнеров, это позволяет перезагружать или обновлятьDocker Daemon, не останавливая при этом запущенные контейнеры. Еслиdockerdупадет из-за ошибки или нехватки памяти, контейнеры не выключатся за счет работы дочерних процессовshim-runc.runc- это процесс, который подготавливает изоляцию.docker-cli- утилита командной строки, через которую передаются команды управления через REST API локально на Unix-сокет (/var/run/docker.sock) или удаленно по TCP.
Для запуска нового контейнера, сначала запускается промежуточный процесс containerd-shim-runc-v2, он живет все время, пока работает контейнер и является родителем для процесса внутри контейнера (он забирает код возврата, если приложение упадет и держит каналы stdin/stdout открытыми для доступа к логам). Процесс containerd-shim создает дочерний процесс runc (создает новый процесс с помощью fork() и exec()), который напрямую взаимодействует с ядром Linux через системные вызовы (разрывает связь с родительскими пространствами имен с помощью unshare(), записывает ограничения в файл /sys/fs/cgroup/, меняет корневую файловую систему на путь из /var/lib/docker/overlay2/<IMAGE_ID>/merged с помощью pivot_root() и umount для старого кореня хоста). После настройки изоляции, runc заменяет себя процессом приложения в контейнере (командой ENTRYPOINT или CMD в Dockerfile).
ps axf | grep -A 3 containerd-shim - отобразить дерево процессов контейнеров
Namespaces/Cgroups
Технологии ядра Linux для настройки изоляции:
Namespaces(пространства имен) - изоляции видимости системных ресурсов.Cgroups(Control Groups) - ограничения потребления ресурсов процессом (лимиты на CPU, RAM, скорость диска). Без них один контейнер мог бы потреблять всю память хоста.
Пространства имен:
PID Namespace- изолирует дерево процессов. Процесс в контейнере может иметьPID 1, не конфликтуя сPID 1(init) основной системы.Network Namespace- создает собственные сетевые интерфейсы (IP-адреса и таблицы маршрутизации).Mount Namespace- изолирует точки монтирования файловой системы.UTS Namespace- позволяет задавать отдельное имя хоста (hostname) и домен.IPC Namespace- ограничивает межпроцессное взаимодействие (shared memory, очереди сообщений).User Namespace- сопоставляет идентификаторы пользователей (UID/GID) внутри контейнера с другими ID на хосте (например,rootв контейнере может быть обычным пользователем на хосте).
unshare (разъединитель) - запускает процесс в новых изолированных пространствах имен (namespaces), отделяя его от родительских ресурсов.
netns (Network Namespace) - создает и настраивает сетевое пространство имен, которое позволяет иметь в одной операционной системе несколько полностью независимых сетевых стеков. В отличии от unshare --net дает возможность настроить сеть до ее изоляции (связать с хостом через veth).
Скрипт для создания изолированной и ограниченной по ресурсам операционной системы Alpine Linux:
# Идентификатор изоляции
UUID=
# Подготавливаем хранилище btrfs
VOLUMES="/tmp/volumes"
IMG="/tmp/volumes/.img"
VOLUME="/tmp/volumes/"
# Загружаем базовый образ Alpine в btrfs volume
ALPINE="/base_image_alpine"
|
# Подготавливаем файловую систему
ROOTFS="/container_rootfs"
# Настраиваем DNS
# Функция для очистки
# Перехватывает выход из скрипта (EXIT) или прерывание (Ctrl+C)
# Создаем виртуальный интерфейс на хосте в режиме моста (L2 коммутатор) для всех контейнеров
# Настраиваем NAT для маршрутизации пакетов в Интернет чере хост
# Создаем интерфейс в режиме veth (виртуальный сетевой кабель для двусторонней связи)
# Подключаем один конец к бриджу (позволяет общаться между контейнерами в подсети и выходить в Интернет через хост)
# Создаем сетевое пространство (Network Namespace)
# Подключаем второй конец к изолированному стеку (для связи с хостом в режиме Point-to-Point и bridge)
# Настройка сети внутри внутри изолированного стека
# Поднимаем интерфейс loopback, чтобы программы могли обращаться к localhost
# Назначаем адрес и поднимает интерфейс
# Настраиваем маршрут по умолчанию (любой пакет не предназначенный для подсети 172.172.0.0/24 отправится на хост)
# sudo apt install cgroup-tools
# sudo cgcreate -g memory:/$UUID
# sudo cgset -r memory.limit_in_bytes=$((512 * 1024 * 1024)) $UUID
# Включаем лимиты перед netns
# sudo cgexec -g memory:$UUID
# Включаем управление памятью с помощью групп
|
# Создаем новую контрольную группу
# Устанавливаем лимит на память в 512 МБайт
|
# Записываем PID текущей оболочки в cgroup
|
# 1. Заходим в настроенную изолированную сеть (или nsenter --net=/var/run/netns/ns$UUID)
# 2. Создаем изоляцию для процессов, файловой системы, UTS и IPC
# 3. Мменяет корневой каталог с помощью chroot
# 4. Очищаем и заново определяем переменные окружения
# 5. Монтируем новую файловую систему процессов и заменяем текущий процесс на новый
Namespace Enter
nsenter (Namespace Enter) - это инструмент командной строки, который позволяет запускать процессы в контексте пространств имен других процессов.
# Целевой контейнер
containerName=dozzle
# Получить PID 1 контейнера внутри хостовой системе
PID=
# PID=$(docker inspect -f {{.State.Pid}} $containerName)
# PID=$(docker top $containerName -o pid | sed 1d)
# Отобразить содержимое файловой системы контейнера
# Отобразить переменные окружения
|
# Файловые дескрипторы, занятые процессом (обычно 1w и 2w используются через FIFO/pipe)
# Доступ к стандартным дескрипторам вывода процесса (stdout и stderr)
# Системные вызовы для всех дочерних процессов с фильтрацией на запись
|
# Получить прямой доступ к файловой системе контейнера
# При использование образа scratch не будут доступны системные команды из каталога /bin
# Подключиться к пространству имен процесса контейнера (доступ к сетевому стеку и процессам)
# Примонтировать виртуальную файловую систему процессов (procfs) контейнера
# Альтернатива nsenter -t $PID -n -p
OCI
OCI (Open Container Initiative) - это стандарт для упаковки, сбоки, загрузки в реестр и запуска контейнеров (например, через docker, podman или containerd).
Образы контейнеров состоят из слоев. Каждая из команд FROM, RUN, COPY и ADD в Dockerfile создает новый неизменяемый слой (Read-Only). Если в одном слое скачать архив, а в следующем его удалить, этот размер все равно останется в памяти нижнего слоя и будут занимать место в финальном образе. При запуске контейнера (экземпляра образа), Docker добавляет сверху один записываемый слой, который стирается после удаления контейнера. Если используется 10 образов на базе alpine, базовый образ хранится на диске в одном экземпляре.
Назначение слоев:
- Кэширование - при повторной сборке образа, Docker не выполняет заново те команды, которые не менялись, а просто берет готовые слои из кэша. Это делает сборку очень быстрой.
- Экономия места - если используется 10 образов на базе
alpine, базовый образ хранится на диске в одном экземпляре, и все они используют одни и те же неизменяемые слои, храня отдельно только свои уникальные изменения в верхнем слое. - Переиспользование - разные образы могут делить между собой общие слои, что ускоряет их скачивание и экономит дисковое пространство.
- Скорость - при загрузки образа из registry по сети, каждый слой загружается параллельно как отдельные архив в формате
.tar.gz.
Сохранить внесенные изменения в новый временный слой запущенного контейнера и сохранить в новый образ:
# docker commit <container_name> <image_name>:<new_tag>
Вывести историю слоев (используемая команда и размер слоя):
Команды FROM отображается как загрузка архива, например ADD alpine-minirootfs-3.23.3-aarch64.tar.gz.
Список директорий со слоями образа в системе:
|
Dive - интерактивный терминальный инструмент для анализа содержимого слоев с целью поиска способов уменьшения размера финального образа Docker.
LATEST_VERSION=
Изменение файла и переупаковка образа:
# Переменные: название образа и файла для внесения изменений
imageName=lifailon/docker-socket-proxy:arm64
fileName=haproxy.cfg
# Экспортируем и распаковываем образ
&&
# blobs index.json manifest.json oci-layout repositories
# Ищем файл в слоях
for; do
if | ; then
if | ; then
filePath=
break
fi
fi
done
# Распаковываем слой и вносим изменения в файл
hashOld=
sizeOld=
# Упаковываем слой обратно в архив (получаем новый hash и размер)
hashNew=
sizeNew=
# Заменяем старый слой на новый
|
# Обновляем хеш и размер слоя в манифесте
|
# Находим хеш конфигурации индекса и обновляем его в blobs
indexHash=
indexHashOld=
indexPath=
indexSizeOld=
# cat $indexPath | jq
# cat $indexPath | jq
indexHashNew=
indexSizeNew=
# Обновляем хеш конфигурации в файле индекса
# cat index.json | jq
# cat index.json | jq
# Находим хеш конфигурации манифеста и обновляем его в blobs
manifestConfigHashPath=
manifestConfigHash=
manifestConfigSize=
# Внутри конфигурации манифеста можно изменить Env и отобразить history (все команды при сборке Dockerfile)
|
# Обновляем хеш слоя на новый в директиве порядка сборки
|
# Обновляем хеш конфигурации в файле манифесте
manifestConfigHashNew=
manifestConfigSizeNew=
# Упаковываем содержимое образа в архив
# Загружаем образ в систему
Dockerfile
FROM- базовый образ, на основе которого будет создаваться новый (текущий) образ (например,FROM alpine:latestилиFROM node:alpine AS buildдля указания метки при использование нескольких образов).LABEL- добавляет метаданные к образу в формате ключ-значение (например,LABEL traefik.enable=trueилиLABEL stand=test, может использоваться другими сервисами для своей работы или фильтрации, например,docker ps --filter "stand=test").ARG- определяет переменные, которые будут доступны только на этапе сборки образа и недоступны в контейнере (например, объявление с помощьюARG TARGETARCHи изменениеdocker build --build-arg TARGETARCH=arm64).ENV- устанавливает переменные окружения, которые будут доступны внутри контейнера со значениями по умолчанию (например,ENV PORT=80, можно переопределить через-e PORT=8080, который имеет повышенный приоритет).WORKDIR- устанавливает рабочий каталог внутри контейнера для последующих команд (например,WORKDIR /app).SHELL- задает командную оболочку, которая будет использоваться для выполнения командRUN,CMDиENTRYPOINT(например,SHELL ["/bin/bash", "-c"], по умолчаниюSHELL ["/bin/sh", "-c"]).RUN- выполняет команды в контейнере во время сборки образа (например,RUN apk add --progress --no-cache util-linux bash curlилиRUN useradd -m node).USER- устанавливает пользователя, от имени которого будут выполняться следующие команды (например,USER node, по умолчаниюUSER root).COPY- копирует файлы (например,COPY . .для копирования всего содержимого из текущей директории в контейнер илиCOPY --from=buildдля указания метки при копирование файлов из другого образа).ADD- загружает файлы изURL(например,ADD alpine-minirootfs-3.23.3-aarch64.tar.gz /img/) с распаковкой архивов в форматеtar.gz(актуально для базовых образов).CMD- определяет параметры команды, которые будут выполняться при запуске контейнера (может быть переопределена при запуске контейнера).ENTRYPOINT- определяет основную команду, которая будет выполняться при запуске контейнера (можно переопределить с помощью флага--entrypoint).VOLUME- создает точку монтирования для хранения данных в хостовой системе, вместо слоев контейнера (например,VOLUME /var/log/app).EXPOSE- документирует порты без их проброса (например,EXPOSE 8080).HEALTHCHECK- определяет команду для проверки состояния работающего контейнера (например,HEALTHCHECK CMD curl -f http://localhost:8080 || exit 1).STOPSIGNAL- определяет сигнал, который будет отправлен контейнеру для его остановки (например,STOPSIGNAL SIGQUIT).ONBUILD- задает команды, которые будут автоматически выполнены при сборке дочерних образов.
Пример сборки контейнера для выполнения команды ping:
FROM alpine:latest
ENTRYPOINT ["ping"]
CMD ["localhost"]
docker build -t ping-image .
При запуске команды: docker run ping-image, запустится контейнер, который запустит команду: ping localhost.
При запуске команды: docker run ping-image google.com, аргумент google.com целиком переопределяет команду в CMD и контейнер запустит команду: ping google.com.
Можно изменить основную команду: docker run -v /var/log/syslog:/syslog --entrypoint tail ping-image -f /syslog
Пример загрузки и распаковки архива:
FROM alpine:latest
RUN apk add --progress --no-cache curl unzip && \
curl -sSL https://github.com/Lifailon/lazyjournal/archive/refs/heads/main.zip -o /tmp/main.zip && \
unzip /tmp/main.zip -d /app/ && \
rm /tmp/main.zip && \
apk del curl unzip
WORKDIR /app/lazyjournal-main
RUN ls -lh
Пример использования аргументов:
FROM alpine:latest
# Объявляем переменную для текущей сборки (обязательно) и присваиваем значение (опционально)
ARG HTTPS_PROXY=http://192.168.3.105:20171
RUN apk update && apk add curl
docker build --build-arg HTTPS_PROXY=http://192.168.3.105:20171 -t curl-image .
Scratch
Scratch - это пустой образ, который не содержит операционной системы, системных библиотек (как glibc в Ubuntu или musl в Alpine) оболочек (sh или bash) и файлов, что исключает возможные уязвимости в безопасности и требует использовать для запуска статические бинарные файлы (например, написанные на Go, Rust, C++).
Сборка Go приложения c нуля с помощью scratch:
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 go build -o app-0.0.1 main.go
FROM scratch
# Копируем файл из другого образа
# COPY --from=busybox:musl /bin/busybox /busybox
# Копируем приложение из промежуточного слоя
COPY --from=builder /app/app-0.0.1 /app
# Копируем корневые сертификаты для доступа по HTTPS из приложения
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
ENTRYPOINT ["/app"]
Загружаем busybox и копируем в scratch контейнер:
&&
Buildx
sudo apt install docker-buildx -y установить систему для мультиплатформенной сборки
docker buildx create --use --name multiarch-builder --driver docker-container создать и запустить сборщик в контейнере
docker buildx ls
docker buildx rm multiarch-builder
go list -u -m all && go get -u ./... обновить пакеты приложения на Go
Добавить аргументы в Dockerfile и передать их в переменные для сборки:
ARG TARGETOS TARGETARCH
RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build
docker buildx build --platform linux/amd64,linux/arm64 . собрать
docker buildx build --platform linux/amd64,linux/arm64 -t lifailon/logporter --push . собрать и опубликовать
npm outdated && npm update --save обновить пакеты node.jd приложения
Передаем аргументы в параметры платформы для образа:
ARG TARGETOS TARGETARCH
FROM --platform=${TARGETOS}/${TARGETARCH} node:alpine AS build
Docker Registry
Docker Hub
curl https://registry-1.docker.io/v2/ проверить доступ к Docker Hub
curl -s -X POST -H "Content-Type: application/json" -d '{"username": "lifailon", "password": "password"}' https://hub.docker.com/v2/users/login | jq -r .token > dockerToken.txt получить временный токен доступа для авторизации
docker login вход в реестр репозитория hub.docker.com
cat dockerToken.txt | sudo docker login --username lifailon --password-stdin передать токен авторизации (https://hub.docker.com/settings/security) из файла через stdin
cat /root/.docker/config.json | jq -r .auths[].auth место хранения токена авторизации в системе
Push/Pull
git clone https://github.com/Lifailon/TorAPI
cd TorAPI
docker build -t lifailon/torapi:latest . собрать образ для публикации на Docker Hub
docker push lifailon/torapi:latest загрузить образ на Docker Hub
docker pull lifailon/torapi:latest загрузить образ из Docker Hub
docker run -d --name TorAPI -p 8443:8443 lifailon/torapi:latest загрузить образ и создать контейнер
Nexus
Настройка HTTP-соединения с Nexus сервером (если не использует HTTPS):
|
docker login 192.168.3.105:8882 авторизируемся в репозитории Docker Registry на сервере Nexus
docker tag lifailon/docker-web-manager:latest 192.168.3.105:8882/docker-web-manager:latest создаем тег с прявязкой сервера
docker push 192.168.3.105:8882/docker-web-manager:latest загружаем образ на сервер Nexus
curl -sX GET http://192.168.3.105:8882/v2/docker-web-manager/tags/list | jq отобразить список доступных тегов
docker pull 192.168.3.105:8882/docker-web-manager:latest загрузить образ из Nexus
Mirrors
Зеркала необходимы, если прямой доступ к основному хранилищу Docker Hub ограничен или невозможен. Docker будет перебирать их по очереди при загрузке образа, пока не найдет доступный.
|
Proxy
Создаем дополнительную конфигурацию для службы Docker в файле /etc/systemd/system/docker.service.d/http-proxy.conf
[Service]
"HTTPS_PROXY=http://192.168.3.105:20171"
systemctl daemon-reload && systemctl restart docker
Docker API
UNIX Socket
curl --silent -XGET --unix-socket /run/docker.sock http://localhost/version | jq . использовать локальный сокет (/run/docker.sock) для взаимодействия с Docker daemon через его API
curl --silent -XGET --unix-socket /run/docker.sock http://localhost/info | jq . количество образов, запущенных и остановленных контейнеров и остальные метрики ОС
curl --silent -XGET --unix-socket /run/docker.sock http://localhost/events логи Docker daemon
curl --silent -XGET --unix-socket /run/docker.sock -H "Content-Type: application/json" http://localhost/containers/json | jq . список работающих контейнеров и их параметры конфигурации
curl --silent -XGET --unix-socket /run/docker.sock http://localhost/containers/uptime-kuma/json | jq . подробные сведения (конфигурация) контейнера
curl --silent -XPOST --unix-socket /run/docker.sock -d "{"Image":"nginx:latest"}" http://localhost/containers/create?name=nginx создать контейнер с указанным образом в теле запроса (должен уже присутствовать образ)
curl --silent -XPOST --unix-socket /run/docker.sock http://localhost/containers/17fab06a820debf452fe685d1522a9dd1611daa3a5087ff006c2dabbe25e52a1/start запустить контейнер по Id
curl --silent -XPOST --unix-socket /run/docker.sock http://localhost/containers/17fab06a820debf452fe685d1522a9dd1611daa3a5087ff006c2dabbe25e52a1/stop остановить контейнер
curl --silent -XDELETE --unix-socket /run/docker.sock http://localhost/containers/17fab06a820debf452fe685d1522a9dd1611daa3a5087ff006c2dabbe25e52a1 удалить контейнер
TCP Socket
service=
curl -sS -X GET http://192.168.3.102:2375/version | jq .
Metrics
Включаем встроенный экспортер на конечной точке /metrics в файле /etc/docker/daemon.json для Prometheus:
curl http://192.168.3.102:9323/metrics
Docker.DotNet
# Импорт библиотеки Docker.DotNet (https://nuget.info/packages/Docker.DotNet/3.125.15)
Add-Type -Path "$home\Documents\Docker.DotNet-3.125.15\lib\netstandard2.1\Docker.DotNet.dll"
# Указываем адрес удаленного сервера Docker, на котором слушает сокет Docker API
$config = [Docker.DotNet.DockerClientConfiguration]::new("http://192.168.3.102:2375")
# Подключаемся клиентом
$client = $config.CreateClient()
# Получить список методов класса клиента
$client | Get-Member
# Выводим список контейнеров
$containers = $client.Containers.ListContainersAsync([Docker.DotNet.Models.ContainersListParameters]::new()).GetAwaiter().GetResult()
# Забираем id по имени
$kuma_id = .id
# Получить список дочерних методов
$client.Containers | Get-Member
# Остановить контейнер по его id
$StopParameters = [Docker.DotNet.Models.ContainerStopParameters]::new()
$client.Containers.StopContainerAsync($kuma_id, $StopParameters)
# Запустить контейнер
$StartParameters = [Docker.DotNet.Models.ContainerStartParameters]::new()
$client.Containers.StartContainerAsync($kuma_id, $StartParameters)
Docker cli
docker search lazydocker поиск образа в реестре
docker pull lazyteam/lazydocker скачать образ из реестра Docker Hub
docker images/docker image ls отобразить все локальные (уже загруженные) образы docker (image ls)
docker images --format "table {{.ID}}\t{{.Repository}}\t{{.Tag}}" отфильтровать вывод
docker run -it --rm -v /var/run/docker.sock:/var/run/docker.sock lazyteam/lazydocker запустить контейнер из образа c последующим удалением (--rm) в интерактивном режиме (открыть STDIN) и пробросом tty
docker run -d --name lazydocker -it -v /var/run/docker.sock:/var/run/docker.sock lazyteam/lazydocker запустить контейнер в фоне (-d/--deamon)
docker logs prometheus вывести логи контейнера (содержимое STDOUT и STDERR)
docker attach lazydocker подключиться к терминалу работающего контейнера
docker exec -it lazydocker sh -c bash подключиться терминалу контейнера с помощью sh/bash
docker run -it --rm --entrypoint sh lazyteam/lazydocker запустить контейнер и подключиться к нему (даже если контейнер уходит в ошибку при запуске)
docker stats посмотреть статистику потребляемых ресурсов запущенными контейнерами (top)
docker top lazydocker/docker exec lazycompose top -n 1 отобразить список всех работающих процессов внутри контейнера
docker ps отобразить все запущенные контейнеры
docker ps -a отобразить все запущенные и остановленные контейнеры
docker ps -as добавляет размер (--size)
docker stop lazydocker остановить существующий (созданный ранее) контейнер (отправляет процессу с PID 1 сигнал SIGTERM и через 10 секунд SIGKILL)
docker kill -s SIGHUP prometheus отправить указанный сигнал контейнеру (например, перечитать конфигурацию, по умолчанию SIGKILL)
docker kill $(docker ps -q) принудительно остановить все контейнеры
docker restart lazydocker перезапустить контейнер
docker start lazydocker запустить контейнер
docker pause lazydocker поставить контейнер на паузу
docker unpause lazydocker возобновить работу контейнера
docker rename lazydocker lazydocker-tui переименоввать контейнер
docker rm lazydocker-tui удалить контейнер
docker rmi lazyteam/lazydocker удалить образ
docker image prune -a удалить все образы, которые не используются хотя бы одним контейнером
docker images -q | xargs docker rmi удалить все образы, которые не заняты контейнерами
update
docker update --restart unless-stopped uptime-kuma изменить режим перезапуска контейнера после его остановки
docker update --restart on-failure:3 uptime-kuma контейнер будет перезапущен только в случае его завершения с ошибкой (когда код завершения отличается от 0), используя 3 попытки
docker update --cpu-shares 512 --memory 500M uptime-kuma задать ограничения по CPU (доступ к указанной доле процессорного времени в диапазоне от 2 до 262,144 или --cpus - количество процессоров) и памяти
volume
docker volume ls вывести список томов и место хранения (механизмы хранения постояннымх данных контейнера на хостовой машине, которые сохраняются между перезапусками и при пересоздание контейнеров)
docker volume inspect uptime-kuma подробная информация о конфигурации тома (отображает локальный путь хранения данных в системе, Mountpoint: /var/lib/docker/volumes/uptime-kuma/_data)
docker volume create test создать том
docker volume rm test удалить том
network
docker network ls вывести список сетей
docker network inspect bridge подробная информация о сети bridge
docker inspect uptime-kuma | jq .[].NetworkSettings.Networks узнать наименование сетевого адаптера указанного контейнера
docker run -d --name uptime-kuma --network host nginx louislam/uptime-kuma:1 запуск контейнера с использованием сети host, которая позволяет контейнеру использовать сеть хостовой машины (like NAT)
docker network create network_test создать новую сеть
docker network connect network_test uptime-kuma подключить работающий контейнер к указанной сети (используется для связи контейнеров)
docker network disconnect network_test uptime-kuma отключить от сети
docker network rm network_test удалить сеть
inspect
docker inspect prometheus подробная информация о контейнере (например, конфигурация NetworkSettings)
docker inspect prometheus --format='{{.LogPath}}' отобразить, где хранятся логи для конкретного контейнера в локальной системе
docker port prometheus отобразить проброшенные порты контейнера
docker inspect $(docker ps -q) --format='{{.NetworkSettings.Ports}}' отобразить TCP порты всех запущенных контейнеров
cat /var/lib/docker/containers/$id/config.v2.json | jq . прочитать конфигурационный файл контейнера
system
docker system df отобразить сводную информацию занятого пространства образами, контейнерами и хранилищами
docker system events выводить события от демона dockerd в реальном времени
docker system prune –volumes заменяет все четыре команды очистки и дополнительно очищает кеш сборки
diff
docker diff <container_id_or_name> отображает изменения, внесенные в файловую систему контейнера по сравнению с исходным образом
A - добавленные файлы
C - измененные файлы
D - удаленные файлы
copy
Копируем базу данных sqlite3, обновляем пароль и разблокируем пользователя Grafana:
context
docker context create rpi-106 --docker "host=tcp://192.168.3.106:2375" добавить подключение к удаленному хосту через протокол TCP
docker context create rpi-106 --docker "host=ssh://lifailon@192.168.3.106:2121" добавить подключение к удаленному хосту через протокол SSH
docker context ls список всех доступных контекстов (* отображается текущий)
docker context inspect rpi-106 конфигурация указанного контекста
docker context use rpi-106 переключиться на выбранный контекст (возможно на прямую взаимосдействовать с удаленным Docker Engine через cli, за исключением взаимодействия через Socket)
docker context rm rpi-106 удалить контекст
alias
# Параллельный запуск всех остановленных контейнеров со статус выхода exited
# Остановка всех работающих контейнеров
# Перезапуск всех контейнеров
Docker Compose
version=
Dozzle
Dozzle - веб-интерфейс для просмотра и фильтрации журналов Docker (без хранения), с поддержкой базового управления, подключения удаленных хостов и кластеров Kubernetes.
mkdir -p $HOME/docker/dozzle/dozzle_data && cd $HOME/dozzle
Сгенерировать пароль в формате sha-256:
echo -n DozzleAdmin | shasum -a 256
Передать в конфигурацию ./dozzle_data/users.yml:
users:
admin:
name: "admin"
password: "a800c3ee4dac5102ed13ba673589077cf0a87a7ddaff59882bb3c08f275a516e"
или сгенерировать пользователя в формате yaml конфигурации:
Запускаем контейнер:
services:
dozzle:
image: amir20/dozzle:latest
container_name: dozzle
restart: always
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./dozzle_data:/data
environment:
# Отключить сбор и отправку аналитики
- DOZZLE_NO_ANALYTICS=true
# Включить действия (start/stop/restart)
- DOZZLE_ENABLE_ACTIONS=true
# Добавить возможность подключения к работающим контейнерам
- DOZZLE_ENABLE_SHELL=true
# Включить базовую авторизацию из файла /data/users.yml
- DOZZLE_AUTH_PROVIDER=simple
# Подключиться к удаленному хосту через Docker Socket API
# - DOZZLE_REMOTE_HOST=tcp://192.168.3.101:2375|us-101
# Подключиться к удаленному хосту через Dozzle Agent
# - DOZZLE_REMOTE_AGENT=192.168.3.105:7007,192.168.3.106:7007
ports:
- 9090:8080
# Контейнер для мониторинга файла syslog на хостовой системе
dozzle-syslog:
image: alpine
container_name: dozzle-syslog
restart: always
volumes:
- /var/log/syslog:/var/log/custom.log
command:
- tail
- -f
- /var/log/custom.log
docker-compose up -d
Контейнер для агента (альтернатива Docker TCP API):
services:
dozzle-agent:
image: amir20/dozzle:latest
container_name: dozzle-agent
restart: always
command: agent
# environment:
# - DOZZLE_HOSTNAME=dozzle-agent-01
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
ports:
- 7007:7007
Socket Proxy
Проксирование локального сокета Docker на базе HAProxy (не требуется внесение изменений в системные файлы, такие как daemon.json и docker.service) с контролем прав доступа к конечным точкам через переменные среды.
services:
docker-socket-proxy:
# image: lifailon/docker-socket-proxy:arm64
image: lifailon/docker-socket-proxy:amd64
container_name: docker-socket-proxy
restart: always
volumes:
- /var/run/docker.sock:/var/run/docker.sock
ports:
- 2375:2375 # Docker API
- 2376:2376 # HAProxy статистика
environment:
- SOCKET_PATH=/var/run/docker.sock # Путь к Docker сокету внутри контейнера
- LOG_LEVEL=info # Уровень логирования HAProxy-прокси (debug|info|warn|error)
# Включено по умолчанию
- INFO=1 # /info - общая информация о Docker демоне, версия, плагины, лимиты
- PING=1 # /_ping - проверку доступности Docker API
- VERSION=1 # /version - получение версии API и информации о сервере
# Отключено по умолчанию
- POST=1 # HTTP POST-запросы (например, для создания контейнеров)
- GRPC=1 # /grpc - gRPC интерфейс Docker (экспериментальный)
- EXEC=1 # /exec - запуск команд внутри контейнеров
- ALLOW_RESTARTS=1 # /containers/.../(restart|kill) - перезапуск или остановку контейнера
- ALLOW_START=1 # /containers/.../start - запуск остановленных контейнеров
- ALLOW_STOP=1 # /containers/.../stop - остановку запущенных контейнеров
- AUTH=1 # /auth - отвечает за логин к registry через Docker API
- CONTAINERS=1 # /containers - список контейнеров, их создание, удаление, inspect и т.п.
- IMAGES=1 # /images - просмотр, загрузка и удаление Docker-образов
- NETWORKS=1 # /networks - просмотр, создание и удаление сетей Docker
- BUILD=1 # /build - сборка образов через API
- COMMIT=1 # /commit - создание образа из контейнера (docker commit)
- DISTRIBUTION=1 # /distribution - доступ к registry API (например, метаданные образов)
- EVENTS=1 # /events - поток событий Docker (создание, запуск, удаление контейнеров)
- PLUGINS=1 # /plugins - управление Docker плагинами
- VOLUMES=1 # /volumes - управление Docker томами (создание, удаление, просмотр)
- SESSION=1 # /session - сессии терминалов и интерактивные API
# Swarm
- SWARM=0 # /swarm - настройки и статус Swarm кластера
- NODES=0 # /nodes - информация о нодах в Swarm
- CONFIGS=0 # /configs - используется в Swarm для конфигураций
- SECRETS=0 # /secrets - секреты Docker Swarm
- SERVICES=0 # /services - управление сервисами Docker Swarm
- SYSTEM=0 # /system - общая системная информация Docker (ресурсы, usage)
- TASKS=0 # /tasks - задачи Swarm (контейнеры внутри сервисов)
# HAProxy stats
- STATS_URI=/
- STATS_USER=admin
- STATS_PASS=admin
File Watch
Атрибут watch автоматически обновляет файлы измененные на хосте в контейнере (sync), перезагружает контейнеры (sync+restart) и пересобирает образ (rebuild) по мере редактирования и сохранения кода.
services:
web:
build: .
command: npm start
develop:
watch:
- action: sync+restart
path: ./web
target: /src/web
initial_sync: true
ignore:
- node_modules/
- action: rebuild
path: package.json
docker compose up --watch
Logging
Определить размер для одного лог-файла и огриничить количество лог-файлов:
Настройка логирования в compose, используя стандартный драйвер Docker в формате json:
logging:
driver: json-file
options:
max-size: 10m
max-file: 3
journald
Доступа к логам через journalctl используя systemd/journald
logging:
driver: journald
options:
tag: "{{.Name}}"
syslog
Изменить режим логирование всех контейнеров по умолчанию через файл /etc/docker/daemon.json для отправки логов на сервер rsyslog-collector или graylog в режиме inputs syslog:
Отправка логов на сервер сервера Sloggo с использованием формата rfc5424:
logging:
driver: syslog
options:
syslog-address: udp://localhost:1514
# Формат логов для Sloggo
syslog-format: rfc5424
gelf
Отправка логов на сервер Graylog, Logstash или другие GELF совместимые системы:
logging:
driver: gelf
options:
gelf-address: udp://1.2.3.4:12201
tag: "{{.Name}}"
fluentd
Отправка данных на агент fluen-bit через драйвер fluentd:
logging:
driver: fluentd
options:
fluentd-address: "fluentd-server:24224"
tag: "docker.{{.Name}}"
rsyslog
Пример запуска централизованного сервера rsyslog и агента для переадресации логов из контейнеров без настройки логирования:
services:
# Сервер для приема логов
# rsyslog-collector:
# image: rsyslog/rsyslog-collector:latest
# container_name: rsyslog-collector
# restart: unless-stopped
# volumes:
# - ./log_data:/var/log
# - /etc/hostname:/etc/hostname:ro
# environment:
# - ENABLE_UDP=on
# - ENABLE_TCP=on
# - ENABLE_RELP=off
# - WRITE_ALL_FILE=on # write all messages to /var/log/all.log
# - WRITE_JSON_FILE=off # write JSON formatted messages to /var/log/all-json.log
# - RSYSLOG_HOSTNAME=/etc/hostname
# - RSYSLOG_ROLE=collector
# ports:
# - 10514:514/udp
# - 10514:514/tcp
# Сервер rsyslog для приема лого и веб-интефрейс на базе PimpMyLog для просмотра логов
rsyslog-gui:
image: aguyonnet/rsyslog-gui
container_name: rsyslog-gui
restart: unless-stopped
volumes:
- ./rsyslog_data:/var/log/net
ports:
- 10080:80
- 10514:514/udp
- 10514:514/tcp
environment:
- SYSLOG_USERNAME=admin
- SYSLOG_PASSWORD=admin
# Агент для сбора логов и сокета Docker
rsyslog-dockerlogs:
image: rsyslog/rsyslog-dockerlogs:latest
container_name: rsyslog-dockerlogs
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- /etc/hostname:/etc/hostname:ro
environment:
- REMOTE_SERVER_NAME=rsyslog-collector
- REMOTE_SERVER_PORT=514
- RSYSLOG_HOSTNAME=/etc/hostname
- RSYSLOG_ROLE=docker
Volumes
tmpfs
Временная файловая система для хранения данных в оперативной памяти (исчезают после остановки контейнера):
volumes:
ram_disk:
driver_opts:
type: tmpfs
device: tmpfs
o: "size=512m,uid=1000"
nfs
Монтирование NFS (без необходимости предварительного монтирования на хосте):
volumes:
nfs_volume:
driver_opts:
type: nfs
o: "addr=192.168.3.101,nolock,soft,nfsvers=4"
device: ":/backup"
smb
Монтирование удаленного Windows хранилища через протокол SMB:
services:
nginx:
image: nginx
container_name: nginx
volumes:
- smb_volume:/data
volumes:
smb_volume:
driver_opts:
type: cifs
o: username=guest,password=,uid=1000,gid=1000
device: //192.168.3.100/docker-data/nginx
Ручное монтирование SMB:
sudo apt install cifs-utils smbclient -y установка зависимостей
smbclient //192.168.3.100/backup -U guest% проверить гостевой доступ к удаленной шаре
sudo mkdir /mnt/smb_backup && sudo chown -R 1000:1000 /mnt/smb_backup создать директорию для монтирования
mount -t cifs //192.168.3.100/backup /mnt/smb_backup -o user=guest примонтировать (до перезагрузки)
echo "//192.168.3.100/backup /mnt/smb_backup cifs username=guest,password=,uid=1000,gid=1000,rw,vers=3.0 0 0" | sudo tee -a /etc/fstab добавить настройки при загрузки системы
mount -a && systemctl daemon-reload && df -h примонтировать (прочитать и применить все записи из fstab)
volumes:
- /mnt/smb_backup:/data
Network
config
Определяем подсеть и фиксируем адрес хоста:
services:
alpine:
image: alpine
# Обновляем файл /etc/resolv.conf
dns:
# Внешний DNS сервер запущенный на хосте
- 10.5.0.1
# Встроенный DNS сервер Docker
# - 127.0.0.11
# Обновляем файл /etc/hosts
extra_hosts:
- host.docker.internal:host-gateway
# - host.docker.internal:10.5.0.1
networks:
alpine-net:
ipv4_address: 10.5.0.2
networks:
alpine-net:
driver: bridge
ipam:
config:
- subnet: 10.5.0.0/24
gateway: 10.5.0.1
external
Контейнеры в одном стеке взаимодействуют между собой через виртуальный мост (bridge) по container_name без проброса портов, а также с контейнерами в других стеках при подключение их сети с помощью external: true.
services:
alpine:
image: alpine
container_name: alpine
ports:
- 80:8080 # container:host
networks:
- alpine_net
- dns-stack_default
command: ping postgres
networks:
alpine_net:
driver: bridge
pg-stack_default:
external: true
host
В сетевом режиме host используется сеть хоста напрямую (порты через секцию ports не пробрасываются).
services:
nginx:
image: nginx
container_name: nginx
network_mode: host
macvlan
macvlan - это сетевой драйвер, который работает на уровне L2, где контейнеры получают свои MAC и IP адреса во внешней сети хоста (линкуется по названию интерфейса).
sudo ip link set eth0 promisc on включить режим promisc на интерфейсе хоста, что бы иметь возможность принимать все пакеты, проходящие через хост, независимо от MAC-адреса.
Set-VMNetworkAdapter -VMName hv-us-101 -MacAddressSpoofing On включить режим promisc на виртуальной машине Hyper-V
services:
nginx:
image: nginx
container_name: nginx
networks:
macvlan_net:
ipv4_address: 192.168.3.110
networks:
macvlan_net:
driver: macvlan
driver_opts:
parent: eth0
ipam:
config:
- subnet: 192.168.3.0/24
gateway: 192.168.3.1
ipvlan
ipvlan не создает отдельные MAC-адреса, поэтому может работать на wlan (Wi-Fi) интерфейсах хоста.
services:
nginx:
image: nginx
container_name: nginx
networks:
ipvlan_net:
ipv4_address: 192.168.3.110
networks:
ipvlan_net:
driver: ipvlan
driver_opts:
parent: wlan0
mode: l2
ipam:
config:
- subnet: 192.168.3.0/24
gateway: 192.168.3.1
Depends on
Depends on - механизм управление порядком запуска с проверкой готовности.
services:
pg-migrate:
image: app
command:
environment:
- DATABASE_URL=postgres://user:pass@pg:5432/dbname
# Дожидаемся успешного запуска контейнера с базой данных
depends_on:
pg:
condition: service_healthy
# Основное приложение
app:
image: app
command:
# Ждет успешного завершения миграции
depends_on:
pg-migrate:
condition: service_completed_successfully
Swarm
docker swarm init инициализировать manager node и получить токен для подключения worker node (на сервере)
docker swarm join-token worker получить токен для подключения worker или manager
docker swarm join --token SWMTKN-1-1a078rm7vuenefp6me84t4swqtvdoveu6dh2pw34xjcf2gyw33-81f8r32jt3kkpk4dqnt0oort9 192.168.3.101:2377 подключение на worker node (на клиенте)
docker node ls отобразить список node на manager node
docker node inspect u4u897mxb1oo39pbj5oezd3um подробная информация (конфигурация) о node по id
docker swarm leave --force выйти из кластера на worker node (на manager node изменится статус с Ready на Down)
docker node rm u4u897mxb1oo39pbj5oezd3um удалить node (со статусом Down) на manager node
docker swarm init --force-new-cluster заново инициализировать кластер (если упал, при наличии одного менеджера)
docker pull lifailon/torapi:latest
docker-stack.yml
services:
torapi:
image: lifailon/torapi:latest
labels:
- com.centurylinklabs.watchtower.enable=false
deploy:
# Режим развертывания
mode: replicated # Фиксированное число реплик (по умолчанию)
# mode: global # Одна копия на каждой ноде
replicas: 2 # Суммарное количество реплик на всех нодах (только в режиме replicated)
# Политика перезапуска
restart_policy:
condition: on-failure # Перезапускать только при ошибках (ненулевой код выхода)
# condition: any # Всегда перезапускать (аналог always в docker-compose)
delay: 5s # Задержка перед перезапуском (по умолчанию, 5 секунд)
max_attempts: 3 # Максимум попыток перезапуска (по умолчанию, бесконечно)
window: 30s # Время для оценки успешности перезапуска (по умолчанию, 0)
# Политика обновления (старые контейнеры не удаляются сразу, а только останавливаются и создаются новые с обновленными образами)
update_config:
parallelism: 1 # Количество реплик для одновременного обновения (по умолчанию, 1)
delay: 10s # Задержка между обновлениями (по умолчанию, 0 секунд)
order: start-first # Порядок: start-first (сначала новый) или stop-first (сначала старый, по умолчанию)
failure_action: rollback # Действие при ошибке: continue, rollback, pause (по умолчанию, pause)
monitor: 60s # Время мониторинга после обновления (по умолчанию, 0)
# Политика отката (конфигурация аналогична update_config) при статусе unhealthy на новых контейнерах после update_config
rollback_config:
parallelism: 1
delay: 10s
order: stop-first
failure_action: pause
monitor: 60s
# Ограничения размещения
# placement:
# constraints:
# - "node.role==worker" # Только на worker-нодах
# - "node.labels.env==dev" # Только на нодах с указаной меткой
# Ограничения ресурсов
resources:
limits:
cpus: "0.5" # Лимит CPU (0.5 = 50%)
memory: 256M # Лимит RAM
reservations:
cpus: "0.1" # Гарантированные CPU
memory: 128M # Гарантированная RAM
# Режим балансировки (конечной точки)
endpoint_mode: vip # Балансировка через виртуальный IP внутри сети swarm
# endpoint_mode: dnsrr # Балансировка через DNS в режиме Round-Robin
# Проверка здоровья (задается вне deploy)
# Необходимо для работы:
# 1. endpoint_mode - при статусе unhealthy исключает контейнер из балансировки
# 2. restart_policy - пытается перезапустить контейнер
# 3. update_config - ждет успешного прохождения healthcheck (статус healthy) перед обновлением следующей реплики или запускает rollback_config
healthcheck:
# HTTP-запрос для проверки кода возврата (0 = успех, 1 = ошибка)
test:
# Проверка TCP-порта
# test: ["CMD", "nc", "-z", "127.0.0.1 8443"]
interval: 30s # Интервал между проверками (по умолчанию, 30 секунд)
timeout: 10s # Время ожидания ответа (по умолчанию, 30 секунд)
retries: 3 # Количество попыток перед объявлением статуса unhealthy
start_period: 15s # Время на инициализацию перед проверками (по умолчанию, 0 секунд)
ports:
- target: 8443 # Порт контейнера
published: 8443 # Порт на хосте
protocol: tcp # Протокол (tcp/udp)
# Режим балансировки
mode: ingress # Балансировка через Swarm (только в режиме vip)
# mode: host # Балансировка через хостовую систему (прямой проброс, только в режиме dnsrr)
volumes:
# - type: config # Swarm Configs (статические конфиги, права только на чтение)
# - type: secret # Swarm Secrets (пароли, TLS-ключи. и т.п.)
# - type: nfs # Удаленный NFS-сервер для общих данных в кластере
# - type: tmpfs # RAM Временные файлы (/tmp)
# - type: bind # Файлы на хосте (только если файлы есть на всех нодах)
- type: volume # Управляется Docker (данные БД, кеш)
source: torapi
target: /rotapi
volumes:
torapi:
docker stack deploy -c docker-stack.yml TorAPI собрать стек сервисов (на worker node появится контейнер TorAPI_torapi.1.ug5ngdlqkl76dt)
docker stack ls отобразить список стеков (название стека и количество в нем сервисов, без учета реплик)
docker stack services TorAPI аналог docker service ls, но для отображения списока сервисов указанного стека
docker service ls отобразить список всех сервисов для всех стеков (имя формате <stackName_serviceName>, с количеством и статусом реплик)
docker stack ps TorAPI статистика работы всех сервисов внутри стека (аналог docker ps)
docker service ps TorAPI_torapi аналог docker stack ps, но для отображения статистики указанного сервиса
docker service logs TorAPI_torapi -fn 0 просмотреть логи сервиса по всех репликам кластера одновременно
docker node update --label-add dev=true iebj3itgan6xso8px00i3nizc добавить ноду в группу по метке для линковки при запуске
docker service update --image lifailon/torapi:fake TorAPI_torapi запустить обновление образа для сервиса
docker service scale TorAPI_torapi=3 масштабировать сервис до указанного числа реплик
docker service inspect --pretty TorAPI_torapi отобразить конфигурацию сервиса
docker service inspect TorAPI_torapi отобразить подробную конфигурацию сервиса в формате json
docker stack rm TorAPI удалить стек (не требует остановки контейнеров)
Kubernetes
Kubernetes (k8s) - это открытая платформа для оркестрации контейнеров, которая поддерживает автоматическое развертывание (склирование желаемого количества реплик на разных нодах), самовосстановлением (перезапуск контейнеров на рабочих нодах), балансировку, управление трафиком, а также горизонтальное и вертикальным масштабированием (HPA/VPA). Кластер всегда стремится к тому состоянию, которое описано в конфигурации.
Компоненты кластера:
etcd- распределенная база данных типа ключ-значение (key-value), в которой хранится состояние всего кластера (конфигурации, секреты, состояния подов). Работает на алгоритмеRaftдля консенсуса. Использует порты2379для общения с API-сервером Kubernetes и2380для синхронизация копийetcdмежду собой.kube-apiserver- единственный компонент, который имеет право напрямую читать и записывать данные вetcdи работает на Master-нодах в кластере. Например, при использование командыkubectl get pods, запрос идет на порт6443Master-ноды.kubelet- агент, запущенный на каждой Worker-ноде. Он получает команды (PodSpecs) от API-сервера и следит за запуском и здоровьем контейнеров. Порт10250используется API-сервером для связи с агентом, например, при использование командыkubectl logsилиkubectl exec.Container Runtime- программное обеспечение (например,containerdилиCRI-O), которое непосредственно запускает контейнеры.Kubeletобщается с ним через протоколCRI(Container Runtime Interface).kube-scheduler- диспетчер задач, который следит за появлением новых подов, у которых не назначен узел. Он выбирает подходящую Worker-ноду, основываясь на ресурсах (CPU/RAM), политиках и ограничениях (affinity/taints).kube-controller-manager- состав контроллеров, которые следят за состоянием кластера для приведения его к желаемому виду (например,Node Controllerследит за доступностью узлов, аReplication Controllerподдерживает желаемое количество копий пода).kube-proxy- сетевой агент на каждой ноде. Он реализует правила сети Kubernetes черезIPtablesилиIPVS, позволяя подам общаться друг с другом и обеспечивая работу сервисов. ОбъектService, который объединяет группу подов в логическую единицу получает один общий виртуальный IP-адрес. Когда на этот адрес приходит запрос, кластер черезkube-proxyсам балансирует трафик, перенаправляя его на один из живых подов этой группы, независимо от того, на каких нодах они физически находятся.
Краткий сценарий работы:
- Администратор кластера отправляет
YAMLфайл черезkubectlна API-Server, например, с содержимым деплоймента. kube-apiserverсохраняет план вetcdи отправляет события всем подписанным компонентам кластера.kube-schedulerвидит новый под без узла, выбирает подходящую Worker-ноду и пишет ее имя вetcd.kubeletвидит вetcd, что на его ноду назначен новый под и передает команду вruntime(например,containerd) запустить контейнеры.kube-proxyнастраивает правила сети, чтобы этот под стал доступен.
Kompose
Kompose - инструмент, который конвертируемт спецификацию docker-compose в манифесты Kubernetes.
arch=
version=
kompose --file docker-compose.yaml convert конвертация
docker-compose bridge convert встроенный конвертер в docker compose на базе шаблонов Helm.
Kubeadm
Kubeadm - это инструмент командной строки для сборки и настройки кластера Kubernetes.
Настройка кластера:
# Включаем использования Overlay FS и проброс трафика через bridge
# Настройка системных параметров для работы сети
# Устанавливаем контейнерную среду выполнения containerd
|
# Устанавливаем зависимые пакеты
# curl -sSLf https://pkgs.k8s.io/core:/stable:/v1.35/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
# echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.35/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list
|
|
# Инициализируем кластер (стандартная подсеть для Flannel/Calico)
# Список активных токенов и время их жизни
# Генерация нового токена для подключения Worker Node
# kubeadm token create
# Команда для присоединение Worker нод
# Настраиваем конфигурацию, чтобы работать с кластером через kubectl
# Удалить taint метку на всех Master нодах
Cluster Configuration
Cluster Configuration - это основной конфигурационный ресурс утилиты kubeadm, который определяет глобальные настройки для всего кластера Kubernetes. В отличие от настроек конкретной ноды в момент инициализации с помощь InitConfiguration, этот объект описывает общие параметры для всех компонентов Control Plane (API server, scheduler, controller manager) и etcd.
Конфигурация кластера определяется в момент инициализации кластера с помощью команды kubeadm init --config configuration.yaml
EDITOR=nano kubectl edit cm -n kube-system kubeadm-config
kind: ConfigMap
metadata:
name: kubeadm-config
namespace: kube-system
apiVersion: v1
data:
ClusterConfiguration: |
kind: ClusterConfiguration
kubernetesVersion: v1.35.4
imageRepository: registry.k8s.io
apiVersion: kubeadm.k8s.io/v1beta4
clusterName: kubernetes
# Директория с сертификатами (.crt, .key)
certificatesDir: /etc/kubernetes/pki
# Срок действия корневого сертификата (CA) 10 лет
caCertificateValidityPeriod: 87600h0m0s
# Срок действия сертификатов компонентов (API, Scheduler и др.) 1 год
certificateValidityPeriod: 8760h0m0s
encryptionAlgorithm: RSA-2048
etcd:
local:
dataDir: /var/lib/etcd
networking:
dnsDomain: cluster.local
podSubnet: 10.244.0.0/16
serviceSubnet: 10.96.0.0/12
apiServer: {}
dns: {}
proxy: {}
scheduler: {}
controllerManager: {}
Kubelet Configuration
Kubelet Configuration - глобальная конфигурация Kubelet агентов.
EDITOR=nano kubectl edit cm -n kube-system kubelet-config
apiVersion: v1
kind: ConfigMap
metadata:
name: kubelet-config
namespace: kube-system
data:
kubelet: |
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
# Настройки аутентификации запросов к API kubelet
authentication:
anonymous:
enabled: false
# Настройки проверки через Webhook API-сервера
webhook:
# Время кэширования ответов об аутентификации (отключено)
cacheTTL: 0s
# Включение проверки прав доступа через API-сервер
enabled: true
# Настройки аутентификации по клиентским сертификатам
x509:
# Путь к CA-сертификату для проверки подписей клиентов
clientCAFile: /etc/kubernetes/pki/ca.crt
# Конфигурация авторизации (проверка прав)
authorization:
# Режим авторизации через Webhook (запросы прав у API-сервера)
mode: Webhook
# Параметры кэширования ответов авторизации
webhook:
cacheAuthorizedTTL: 0s
cacheUnauthorizedTTL: 0s
# Драйвер управления группами процессов (должен совпадать с runtime)
cgroupDriver: systemd
# Список IP-адресов внутренних DNS-серверов кластера
clusterDNS:
- 10.96.0.10
# Внутренний домен кластера
clusterDomain: cluster.local
# Путь к сокету среды выполнения контейнеров (CRI)
containerRuntimeEndpoint: ""
# Интервал согласования ресурсов для CPU Manager
cpuManagerReconcilePeriod: 0s
# Настройки поведения при циклической ошибке запуска контейнеров
crashLoopBackOff: {}
# Время ожидания перед переходом узла из состояния нехватки ресурсов
evictionPressureTransitionPeriod: 0s
# Частота проверки конфигурационных файлов на диске
fileCheckFrequency: 0s
# IP-адрес для отдачи данных о состоянии здоровья kubelet
healthzBindAddress: 127.0.0.1
# Порт для эндпоинта /healthz
healthzPort: 10248
# Частота выполнения HTTP-проб состояния
httpCheckFrequency: 0s
# Максимальный возраст образа перед его очисткой (GC)
imageMaximumGCAge: 0s
# Минимальный возраст образа перед его очисткой (GC)
imageMinimumGCAge: 0s
# Настройки использования Swap (подкачки) на узле
memorySwap: {}
# Частота отправки полного отчета о статусе узла в API
nodeStatusReportFrequency: 0s
# Частота обновления статуса узла внутри системы
nodeStatusUpdateFrequency: 0s
# Включение автоматического обновления сертификатов узла
rotateCertificates: true
# Таймаут для запросов к среде выполнения контейнеров
runtimeRequestTimeout: 0s
# Общий период задержки перед выключением узла
shutdownGracePeriod: 0s
# Период задержки перед выключением для критических подов
shutdownGracePeriodCriticalPods: 0s
# Путь к директории с манифестами статических подов
staticPodPath: /etc/kubernetes/manifests
# Таймаут простоя для потоковых соединений (exec/logs)
streamingConnectionIdleTimeout: 0s
# Частота синхронизации состояния подов с заданным конфигом
syncFrequency: 0s
# Интервал сбора статистики использования дисковых томов
volumeStatsAggPeriod: 0s
# Настройки системы логирования
logging:
# Уровень логирования
verbosity: 0
# Частота записи логов из буфера на диск
flushFrequency: 0
options:
json:
# Размер буфера для логов в формате JSON
infoBufferSize: "0"
text:
# Размер буфера для текстовых логов
infoBufferSize: "0"
# Тюнинг
# Максимальное кол-во подов на одной ноде
maxPods: 50
# Резервирование ресурсов для системы
systemReserved:
cpu: "500m"
memory: "1Gi"
kubeReserved:
cpu: "500m"
memory: "1Gi"
# Порог вытеснения (Eviction) при нехватке ресурсов
# Когда на диске останется меньше 5%, Kubelet начнет удалять поды
evictionHard:
memory.available: "500Mi"
nodefs.available: "5%"
imagefs.available: "5%"
# Хранение логов
containerLogMaxSize: "10Mi"
containerLogMaxFiles: 5
# Настройка Garbage Collection (очистка старых образов)
imageGCHighThresholdPercent: 85 # Начинать чистку, если диск забит на 85%
imageGCLowThresholdPercent: 80 # Чистить, пока не станет 80%
Metrics Server
Metrics Server собирает метрики ресурсов из Kubelet агентов на нодах и предоставляет их в Kubernetes apiserver через Metrics API для использования в HPA и VPA.
# Установить metrics-server в кластер
# Статус запуска
# Проверить логи metrics-server
# Отключить проверку TLS сертификатов, т.к. требует поле SAN (Subject Alternative Name)
# Отобразить метрики ресурсов для всех узлов в кластере
Flannel
Flannel - это сетевой плагин (CNI) для создания overlay сети для связи между подами на разных нодах кластера.
kubectl apply -f https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml
# Выводим список всех ресурсов в новом пространстве имен kube-flannel
kubectl get all -n kube-flannel
# Список DaemonSet и подов
kubectl get ds -n kube-flannel kube-flannel-ds
kubectl get pods -n kube-flannel -l app=flannel
Local Path Provisioner
Local Path Provisioner - это инструмент от Rancher, позволяющий использовать локальные диски нодов для постоянного хранения данных (Persistent Volumes). Автоматически создает директории на ноде при создании запросов на хранение (PVC).
# Список всех доступных StorageClass
# Сделать хранилищем по умолчанию (бьз необходимости прописывать storageClassName: local-path в PVC)
Headlamp
Headlamp - это современная альтернатива Kubernetes Dashboard c расширенным функционалом, созданная сообществом SIGs (Kubernetes Special Interest Groups).
# Установить в кластер
# Создать сервисный аккаунт
# Назначить роль администратора кластера для сервисного аккаунта
# Сгенерировать временный JWT-токен, который не хранится в базе данных Kubernetes как объект
# kubectl create token headlamp-admin -n kube-system --duration=43800h
# Создать токен для авторизации
# Получить токен авторизации из секрета
|
# Пробросить порт через ноду (изменяем режим сервиса на NodePort) и узнаем порты
# Обновляем порт
# kubectl patch svc headlamp -n kube-system -p '{"spec": {"ports": [{"port": 80, "nodePort": 30001}]}}'
Kubectl Config
Kubectl - это инструмент командной строки для управления кластерами Kubernetes.
# Загрузить kubectl
# Включить автодополнение для kubectl в bash
Пример конфигурации для подключения к кластеру:
apiVersion: v1
kind: Config
current-context: ${contextName}
preferences:
# Адрес кластера
clusters:
- name: ${contextName}-url
cluster:
server: https://192.168.3.101:6443
# Токен доступа
users:
- name: ${contextName}-usr
user:
token: ${token}
# Название контекста (связь адреса с токеном)
contexts:
- name: ${contextName}
context:
cluster: ${contextName}-url
# Название пространтсва имен по умолчанию при переключение кластера
namespace: ${namespace}
user: ${contextName}-usr
Базовые команды для управления конфигурациями и получения информации:
# Использовать несколько файлов kubeconfig одновременно (в выводе объеденяет конфигурацию)
KUBECONFIG=/.kube/config:/.kube/config_new
# Отобразить текущую конфигурацию (настройка подключения kubectl к кластеру)
# Отобразить список всех доступных контекстов, кластеров и пользователей
# Отобразить текущий контекст
# Переключить контекст (установить контекст default как контекст по умолчанию)
# Изменить контекст по умолчанию в файле kubeconfig (параметр current-context)
# Отобразить адреса главного узла и сервисов
# Вывести состояние текущего кластера
# выгрузить состояние текущего кластера в директорию
# Вывести все ресурсы кластера
# Сортировка по дате создания/изменения
# Отобразить все поддерживаемые типы ресурсов
# Отобразить список всех ресурсов и действий, которые текущему пользователю разрешено совершать с этими ресурсами (права роли RBAC)
Уровни детального вывода для отладки в командах kubectl:
--v=3- расширенная информация об изменениях--v=6- показать запрашиваемые ресурсы (например, загрузка файлаkubeconfigи url для GET-запроса при вызовеkubectl get pods)--v=9- показать содержимого HTTP-запроса в полном виде (включая заголовки)
K9s
K9s - это терминальный интерфейс (TUI) для взаимодействия с кластерами Kubernetes (базовое управление и просмотр логов) с поддержкой плагинов.
EDITOR=nano
Настраиваем плагин kubectl-node-shell в файле ~/.config/k9s/plugins.yaml для подключения к терминалу ноды:
plugins:
kubectl-node-shell:
shortCut: s
description: Open a root shell on a node using the node-shell plugin
scopes:
- nodes
command: kubectl
args:
- node-shell
- $NAME
- --context
- $CONTEXT
background: false
confirm: false
kubectl-node-shell-root:
shortCut: a
description: Запуск временного пода для root-доступа к термину ноды
dangerous: true
scopes:
- nodes
command: kubectl
background: false
confirm: true
args:
- debug
- node/$NAME
- -it
- --image=alpine
# Привелигированный доступ к хостовой системе (hostNetwork: true и hostPID: true)
# Монтирует файловую систему ноды по пути /host
- --profile=sysadmin
Встроенные переменные:
$RESOURCE_GROUP- группа API текущего ресурса изapiVersion(например,apps)$RESOURCE_VERSION- версия API выбранного ресурса (например,v1)$RESOURCE_NAME- тип выбранного ресурса (например,deployments)$NAMESPACE- имя пространства имен выбранного ресурса$NAME- имя выбранного ресурса изmetadata.name$POD- имя пода (доступно при нахождении в режиме просмотра контейнеров)$CONTAINER- имя выбранного контейнера$FILTER- текущая строка фильтрации (введенная через/)$KUBECONFIG- путь к файлу конфигурацииKubeConfig$CLUSTER- имя выбранного (используемого) кластера$CONTEXT- имя выбранного (используемого) контекста$USER- имя выбранного (используемого) пользователя$GROUPS- активные группы выбранного (используемого) пользователя$COL-<COLUMN_NAME>- значение из выбранной колонки таблицы (например,$COL-STATUSили$COL-IP).
Krew
Krew - менеджер плагинов для kubectl.
(
; &&
OS="" &&
ARCH="" &&
KREW="krew-_" &&
&&
&&
) &&
&&
| Плагин | Описание |
|---|---|
| kubectx & kubens | Быстрое переключение между контекстами (кластерамси) и пространствами имен (требует установку fzf). |
| ktop | Мониторинг нагрузки всех node и pods в реальном времени. |
| ketall/get-all | Отображает все ресурсы Kubernetes. |
| kubectl-tree | Отображает зависимости ресурсов в древовидном формате. |
| kubectl-node-shell | Bash скрипт для подключения к оболочке операционной системы хоста (node, монтирует pode на базе Alpine). |
| kubetail | Bash скрипт, позволяющий объединять логи из нескольких подов в один поток. |
| kubetail & Dashboard | Панель управления для просмотра логов в терминале или браузер. |
| stern | Одновременный просмотр логов из нескольких подов в одном потоке. |
| outdated | Отображает устаревшие образы, которые доступны к обновлению. |
Nodes
# Отображает список всех узлов и их текущий статус (`Ready`, `SchedulingDisabled` и т.д.) \
# отображает подробную информацию об узле (ресурсы, события и запущенные поды)
# Отображает нагрузку на ноды (CPU и память) от Metrics Server
# Подготовка нод к техническому обслуживанию
# Отключает возможность создания новых нод на поде (не удаляет поды, которые уже запущены)
# Безопасно вытесняет все запущенные на нем поды
# Возвращяет в рабочее состояние после drain и uncordon
Labels
Связи в Kubernetes работают с помощью селекторов (selectors) и меток (labels) по принципу фильтров. Например, в шаблоне Deployment по пути spec.template.metadata.labels указана метки app: torapi, а Deployment управляет только теми подами, чьи метки совпадают с его селектором в spec.selector.matchLabels.
# Отобразить все доступные лейблы на нодах
# Создать новый лейбл на ноде
# Изменить значение лейбла
# Удалить лейбл
Namespaces
Namespaces - это виртуальные кластера в одном физическом кластере. Имена ресурсов в metadata.name являются идентификатором объекта в кластере и должны быть уникальными в пределах одного пространства имен (именно это имя используется в командах kubectl при обращение к ресурсам кластера).
apiVersion: v1
kind: Namespace
metadata:
name: rest-api
kubectl apply -f namespace.yaml или kubectl create namespace rest-api
kubectl get ns вывести список всех пространств имен в кластере
kubectl describe ns rest-api вывести статус, labels, наличие активных квот (ResourceQuota) и ограничений (LimitRange)
Resource Quotas
ResourceQuotas - используются в пространствах имен для разделения ресурсов (CPU, memory и количество создаваемых объектов) одного физического кластера между несколькими проектами.
apiVersion: v1
kind: ResourceQuota
metadata:
name: rest-api-quotas
namespace: rest-api
spec:
hard:
requests.cpu: "1"
requests.memory: "1Gi"
limits.cpu: "2"
limits.memory: "2Gi"
pods: "3"
replicationcontrollers: "20"
services: "10"
services.loadbalancers: "2"
configmaps: "10"
secrets: "10"
persistentvolumeclaims: "5"
kubectl create -f ./quotas.yaml --namespace=rest-api применить квоты к указанному пространству имен
kubectl get quota --namespace=rest-api вывести список всех квот в пространстве имен
kubectl describe quota rest-api-quotas --namespace=rest-api вывести ограничения выбранной квоты
Limit Range
LimitRange - это политика, ограничивающая выделение ресурсов (limits и requests), которые можно указать для каждого применимого типа объекта, например, Container, Pod (суммарное ресурсы всех контейнеров внутри одного пода) или PersistentVolumeClaim в пространстве имен.
apiVersion: v1
kind: LimitRange
metadata:
name: rest-api-limits
namespace: rest-api
spec:
limits:
- type: Container
# Реквесты по умолчанию (если не будет определено в containers.resources.limits)
defaultRequest:
cpu: 200m
memory: "1Gi"
# Лимиты по умолчанию
default:
cpu: 500m
memory: "2Gi"
# Предельный минимальный и максимальный диапазон
min:
cpu: 100m
max:
cpu: "1"
- type: PersistentVolumeClaim
min:
storage: "1Gi"
max:
storage: "10Gi"
kubectl apply -f ./limits.yaml --namespace=rest-api применить лимиты к указанному пространству имен
kubectl get limits --namespace=rest-api вывести список всех лимитов в пространстве имен
kubectl describe limitrange rest-api-limits --namespace=rest-api вывести значения ограничений и значений по умолчанию
Deployment
Deployment предназначен для описания шаблона одного уникального пода без сохранения stateless (состояния - локальных данных) и использует не уникальные имена для подов в формате [metadata.name]-[replicaSet_id]-[pod_id].
apiVersion: apps/v1
kind: Deployment
metadata:
name: torapi # Имя Deployment, который управляет созданием подов
namespace: rest-api # Новое пространство имен
spec:
replicas: 2 # Количество реплик (2 пода с одинаковыми настройками)
revisionHistoryLimit: 5 # Количество ReplicaSet в истории хранения для отката
selector:
matchLabels:
app: torapi # Определяет, какие поды будут управляться этим Deployment
template:
metadata:
labels:
app: torapi # Метка, которая связывает этот шаблон (template) с селектором выше
spec:
containers:
- name: torapi # Имя контейнера внутри пода
image: lifailon/torapi:latest # Используемый образ контейнера
imagePullPolicy: Always # Всегда скачивать образ из реестра
# imagePullPolicy: IfNotPresent # Использовать локальный (уже скачанный) образ контейнера
ports:
- containerPort: 8443 # Порт, который будет открыт внутри контейнера
resources: # Ограничения и гарантируемые ресурсы
requests:
cpu: "100m" # Минимальный запрашиваемый процессор (0.1 ядра = 100 милли-ядер)
memory: "128Mi" # Минимальный запрашиваемый объем оперативной памяти (128 МБайт)
limits:
cpu: "200m" # Максимально доступное процессорное время
memory: "256Mi" # Максимальный объем памяти
Сравнить текущее состояние кластера с состоянием в файле (в котором находился бы кластер в случае применения манифеста):
kubectl diff -f ./deployment.yaml
Применить манифест к кластеру:
kubectl apply -f deployment.yaml
# Создать ресурс Deployment без описания манифеста
# Удалить деплоймент вместе с его `ReplicaSet` и всеми подами
# Открыть манифест, который уже установленный в кластер на редактирование в терминале,
# с сохранением изменений, которые будут применяны в кластере
KUBE_EDITOR=nano
# Вывести список всех деплойментов, количество их реплик и состояние
# Вывести список всех подов, которые создал деплоймент по лейблу (через его селектор)
# Подробная информация (события, стратегия обновления, селекторы и ошибки)
# Вывести только события для деплоймента
# Вывести события всех деплойментов
Pods
# Отобразить статус всех подов
# Выводит дополнительную информации (для подов это внутренний ip-адрес и название ноды, на которой он работает)
# Отобразить только имена в формате pod/<podName>
# Отобразить нужные поля таблицы вывода в пользовательском формате
# Отобразить все заданные лейблы в подах
# Отобразить все запущенные поды с фильтрацией по лейблу
# Отобразить все запущенные поды с фильтрацией по статусу
# Отобразить нагрузку на подах из Metrics Server
# Отобразить метрики вместе с используемыми в подах контейнерами
# Состояние всех реплик для всех подов и время их работы
# DESIRED - желаемое количество реплик
# CURRENT - текущее количество реплик
# READY - успешно прошли проверки готовности (probes)
# Увеличить (масштабировать) или уменьшить количество подов в Deployment до указанного числа реплик
# Обновить текущую конфигурацию
# Изменения фиксируется в логах ReplicaSet (Scaled up replica set torapi-54775d94b8 from 2 to 3)
# Отобразить подробную конфигурацию развертвывания (шаблон и логи)
# Отобразить логи выбранного пода (сообщения, которые приложение отправляет в stdout)
# Выводить лог для всех запущенных репликах подов (фильтрация по лейблу) в реальном времени (--follow) с отображением временной метки (--timestamps)
# Вывести лог за последние 30 минут
# Отобразить лог предыдущего пода, если контейнер был перезапущен при падение
# Вывести логи выбранного контейнера
# Вывести логи для всех контейнеров внутри подах
# Отобразить логи во всех найденных подах и их репликах по селекторам
# Выполнить команду в указанноv контейнере внутри указанного пода (отобразить список переменных окружения)
# Проверить доступность приложения внутри контейнера
# Запустить sh или bash сессию в контейнере пода
# Подключиться к запущенному процессу пода (PID 1)
ReplicaSet Rollout
Deployment автоматически управляет дочерними ReplicaSet, который в свою очередь управляет желаемым количеством реплик этого пода.
Deployment самостоятельно создает новый ReplicaSet только в том случае, когда меняется шаблон конфигурации будущего пода в spec.template.
kubectl rollout restart deployment/torapi перезапуск всех подов, например, для применения обновленной конфигурации или секретов без изменения образа (деплоймент по очереди пересоздаст поды в новом ReplicaSet, не прерывая работу сервиса)
Новый ReplicaSet создается только в том случае, если изменилась конфигурация самих подов. Значение в revisionHistoryLimit, это количество версий в журнале ReplicaSet в котором хранятся старые версии Deployment, что позволяет сделать быстрый rollback с помощью команды undo.
# Выполнить обновление образа работающего контейнера (будет создан новый ReplicaSet)
# Формат: containerName=imageName:newTag
# Выводить результат процесса обновления в реальном времени
# Отобразить список доступных версий в истории
# Вывести содержимое манифеста по номеру ревизии
# Произвести откат на предыдущую или указанную ревизию
# Выводить результат процесса отката в реальном времени
Service
Service - предоставляет доступ к приложению, работающему в кластере, через единую внешнюю точку доступа, даже если рабочая нагрузка распределена между несколькими контейнерами в подах.
Endpoint Controller постоянно опрашивает apiserver для получения списка всех подов в кластере и фильтрует нужные ему поды по лейблам сервиса в spec.selector (app: torapi), а также проверяет, что прошли проверки здоровья (статус Ready в livenessProbe и readinessProbe). Найденные IP-адреса подов он записывает в специальный скрытый ресурс Endpoints и именно туда он перенаправляет трафик.
apiVersion: v1
kind: Service
metadata:
name: torapi-service
namespace: rest-api
spec:
selector:
app: torapi
ports:
- protocol: TCP
targetPort: 8443 # Порт, который слушают контейнеры в подах
port: 8444 # Порт, на котором сервис доступен внутри кластера
type: LoadBalancer
kubectl apply -f service.yaml
Типы сервисов:
ClusterIP- используется для общения сервисов внутри кластера (значение по умолчанию, чтобы другой под мог пойти к нему по адресуhttp://torapi-service:8444)NodePort- пробрасывает сервис на указанный вports.nodePort(в диапазоне30000-32767) порт каждой ноды в кластере (используется временно, если нет внешнего балансировщика).LoadBalancer- открывает порт, указанный вports.port, на внешнем IP-адресе в облаке.ExternalName- сопоставляют сервис с DNS-именем указанным вspec.externalNameвместо селектора (например, при поиске хостаtorapi-service.rest-api.svc.cluster.localслужба DNS в кластере возвращаетCNAMEзапись типаtorapi.k8s.local).
Команды для создания сервиса вручную:
# Создать сервис, автоматически привязав его к Deployment с помощью Label Selector
kubectl expose deployment torapi --target-port=8443 --port=8444 --name=torapi-service
# Изменить тип сервиса
kubectl patch svc torapi-service -p '{"spec": }'
# Создать внутренний сервис в формате <port>:<targetPort> без привязки к Deployment
kubectl create service clusterip torapi-service --tcp=8443:80
# Создать сервис типа NodePort (открывает порт на всех узлах кластера)
kubectl create service nodeport torapi-service --tcp=8443:80
# Изменить NodePort
kubectl patch svc torapi-service --type='json' -p='[{"op": "replace", "path": "/spec/ports/0/nodePort", "value": 30443}]'
# Удалить сервис
kubectl delete svc torapi-service
Проверить распредиление нагрузки в режиме LoadBalancer (с использованием ServiceLB в кластере k3s) между репликами подов:
for; do
done
Команды для управления сервисами:
# Список всех сервисов в текущем неймспейсе
# Показать расширенную информацию (включая External IP и селекторы)
# Детальное описание сервиса (IP, порты, эндпоинты, селекторы)
# Список конечных точек (на какие IP подов сервис перенаправляет трафик)
DNS
В каждом кластере Kubernetes есть внутренний сервис DNS (чаще CoreDNS) в пространстве имен kube-system. У него есть фиксированный ClusterIP (например, 10.96.0.10), который постоянен на протяжении всей жизни кластера.
На этапе создания пода, планировщик (kube-scheduler) назначает под на конкретную ноду, на котором агент Kubelet смотрит в свои настройки по флагу --cluster-dns, где прописан IP-адрес DNS-сервиса (10.96.0.10). В момент создания контейнера, Kubelet генерирует файл /etc/resolv.conf внутри файловой системы контейнера с содержимым:
namespace_name.svc.cluster.local svc.cluster.local cluster.local
ndots:5
Название в metadata.name объекта Service становится доменным именем для всех подов внутри кластера, чтобы любой другой под в этом же пространстве имен мог обращаться к нему по имени.
Формат имени для обращения в другое пространство имен: <metadata.name>.<namespace_name>.svc.cluster.local
Пример:
curl http://torapi-service.rest-api.svc.cluster.local:8444/api/provider/list
Proxy
# Запустить прокси сервер для локального взаимодействия с частной сетью кластера через API
# Вывести список всех доступных конечных точек
# Dывести список всех имен подов в указанном namespace
|
# Напрямую отправить запрос на конечную точку приложения в контейнере
# Запустить временный проброс порта из пода
Probes
Probes - это проверки, которые использует kubelet для перезапуска контейнера.
Режимы проверки доступности приложения:
livenessProbe- проверяет, живо ли приложение внутри контейнера (httpGetждет код ответа 200 до 399). Если проба провалена, Kubernetes убивает контейнер и запускает новый (перезагрузка).readinessProbe- проверяет, готово ли приложение принимать входящий трафик. Если проба провалена (например, приложение еще загружает кэш или подключается к БД), Kubernetes временно исключает этот под из балансировки вService, и на него не идет трафик.startupProbe- используется для проверки инициализации (например, выполнения скриптов) перед запуском приложения. Сначала отрабатываетStartup, а потом ужеLivenessиReadinessпараллельно, позволяя не убить приложение во время его инициализации.
livenessProbe:
# tcpSocket:
# port: 6379
# grpc:
# port: 9000
# exec:
# command: ["/bin/sh", "-c", "curl -sf http://localhost:8443/api/provider/list"]
httpGet:
path: /api/provider/list # Конечная точка в контейнере, по которому будет проверяться работоспособность
port: 8443 # Порт, на котором доступен этот endpoint внутри контейнера
initialDelaySeconds: 30 # Время ожидания до начала проверок (30 секунд)
periodSeconds: 10 # Интервал проверок (каждые 10 секунд)
timeoutSeconds: 5 # Максимальное время ожидания ответа в секундах
failureThreshold: 3 # Количество неудачных попыток перед рестартом контейнера
Container Hooks
Container Lifecycle Hooks - позволяют выполнять заданные действия в определенный момент времени жизненного цикла контейнера.
PostStart- выполняется сразу после создания контейнера, например, для инициализации БД или регистрации сервиса в сторонних системах. Хук не гарантирует, что выполнится до того, как сработаетENTRYPOINTконтейнера. Если хук завершится с ошибкой, контейнер будет перезапущен.PreStop- выполняется непосредственно перед тем, как контейнеру будет отправлен сигнал завершенияSIGTERM, например, при масштабировании вниз или обновлении для сохранения данных на диск, завершения активных HTTP-запросов или закрытия сетевых соединений. При вызове этого хука Kubernetes подождет время, указанное вterminationGracePeriodSeconds(по умолчанию 30 секунд) и только потом принудительно убьет процесс.
spec:
terminationGracePeriodSeconds: 60
containers:
- name: nginx
image: nginx:1.28-alpine
lifecycle:
postStart:
# Отправка GET-запроса на указанный endpoint приложения
# httpGet:
# path: /init
# port: 80
# scheme: HTTP
exec:
command:
preStop:
exec:
command:
Strategy Update
Стратегии обновления приложения:
Recreate(пересоздание)- агрессивная стратегия с простоем. Kubernetes сначала убивает все старые поды, и только когда они полностью удалены, начинает запускать новые (гарантирует, что две разные версии приложения не будут работать одновременно).RollingUpdate(плавное обновление) - стратегия, при которой Kubernetes постепенно заменяет старые поды на новые. Он запускает новую версию, ждет, пока она станет готова (пройдетreadinessProbe), и только потом убивает старую. Регулируется параметрамиmaxSurge(сколько лишних подов создать) иmaxUnavailable(сколько старых можно убить разом).
spec:
replicas: 2
revisionHistoryLimit: 5
strategy:
# type: Recreate
type: RollingUpdate
rollingUpdate:
maxSurge: 50% # 50% от 2 = 1 дополнительный под за раз (будет 3 в моменте, без учета maxUnavailable)
maxUnavailable: 50% # 50% от 2 = 1 под можно убить сразу (останется 1 старый рабочий)
Blue-Green- внешняя стратегия, когда запускается копию новой версии рядом со старой (ужно иметь два отдельных манифестаDeploymentс разнымиlabel), после проверки, производится переключение траффика вServiceилиIngressна новые поды.
Ограничивает трафик селектором для подов в Deployment с меткой color: blue:
kind: Service
metadata:
name: app-service
spec:
selector:
app: app
color: blue
После запуска копии нового приложения в поде с меткой color: green и проверки работы (например, через временный порт или логи), переключаем цвет в сервисе:
Canary(канареечный деплой) - стратегия на сторонеIstioчерезVirtualServiceиDestinationRule, при которой ограничивается количество трафика (например, 10%) на новую версию, чтобы проверить ее на реальных пользователях, прежде чем обновлять все остальное.
kind: DestinationRule
metadata:
name: app-dr
spec:
host: app-service
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
Разделяем трафик на сервис по subset с помощью весов:
kind: VirtualService
metadata:
name: app-vs-canary
spec:
hosts:
- app-service
http:
- route:
- destination:
host: app-service
subset: v1
weight: 90 # 90% трафика на старую версию
- destination:
host: app-service
subset: v2
weight: 10 # 10% трафика на новую версию (канарейку)
Security Context
Security Context - определяет параметры привилегий и контроля доступа для пода или контейнера.
spec:
# Настройка на уровне ПОДА
securityContext:
# Запускать все процессы под UID/GID 1000
runAsUser: 1000
runAsGroup: 1000
fsGroup: 2000
# Под не запустится, если образ настроен на запуск от root (UID 0)
runAsNonRoot: true
# Ограничение системных вызовов
seccompProfile:
type: RuntimeDefault
containers:
- name: secure-container
image: alpine:3.23
command:
# Настройка на уровне контейнера
securityContext:
# Запрещает процессам получать больше прав, чем у родителя (защита от SUID)
allowPrivilegeEscalation: false
# Если true, дает контейнеру почти полный доступ к хосту
privileged: false
# Запрещает запись в корень образа. Все временные файлы должны идти в EmptyDir
readOnlyRootFilesystem: false
# Тонкая настройка прав ядра
capabilities:
# Разрешить биндить порты < 1024
add:
Volumes
Empty dir
emptyDir - это временное хранилище, которое создается в момент запуска поды и удаляется вместе с ним, а также идеально подходит для передачи данных между контейнерами (включая init containers).
Загрузка оперативной памяти контейнера:
spec:
containers:
- name: mem-util
image: alpine:3.23
command:
args:
- |
# fallocate -l 1G /data/cache
dd if=/dev/zero of=/data/cache bs=1M count=1024
sleep infinity
volumeMounts:
- name: mem-data
mountPath: /data
volumes:
- name: mem-data
emptyDir:
medium: Memory
sizeLimit: "1500Mi"
Host path
hostPath - монтирует файл или каталог из файловой системы ноды. Чтобы при перезапуске поды данные не были потеряны, используется привязка к ноде по лейблу с помощью nodeSelector.
spec:
containers:
- name: check-node-data
image: alpine:3.23
command:
args:
- |
mkdir -p /data/test
echo "test" > /data/test/test.file
sleep infinity
volumeMounts:
- name: alpine-data
mountPath: /data
volumes:
- name: alpine-data
hostPath:
path: /k8s_data/alpine
type: DirectoryOrCreate
nodeSelector:
kubernetes.io/hostname: "hv-us-101"
Tolerations
Taints и Tolerations - правила отвержения подов самой нодой.
Отобразить заданные taint на всех нодах:
kubectl get nodes -o custom-columns=NAME:.metadata.name,TAINTS:.spec.taints[*].effect
Доступные эффекты для taint:
NoSchedule- планировщик (kube-scheduler) не будет рассматривать ноду для запуска новых подов, если у них нет подходящегоToleration.PreferNoSchedule- мягкое условиеNoSchedule, если во всем кластере другие ноды не подходят по ресурсам, поды будут запущены на этой ноде.NoExecute- жесткое правилоNoSchedule, которое при установки меткиtaintзаставляет сразу удалить поды безTolerationи попытается перезапустить их на других нодах.
Создание метки taint на поде в формате <Key>:<Value>:<Effect>:
kubectl taint nodes node-02 nodeType=backup:NoSchedule
На ноде с указанной меткой без правила Toleration никогда не будут запускаться новые поды.
Добавление Toleration в манифест пода:
spec:
tolerations:
- key: "key"
operator: "Equal"
value: "backup"
effect: "NoSchedule"
# Разрешить запуск на master нодах
- key: "node-role.kubernetes.io/control-plane"
operator: "Exists"
effect: "NoSchedule"
Добавить tolerations в деплоймент:
Affinity
Affinity и Anti Affinity - это правила для привязки пода к нодам, которые настраиваются через шаблон подов в Deployment.
Доступные параметры для определения типа правил:
requiredDuringSchedulingIgnoredDuringExecution- обязательное условие. Если подходящей ноды нет, то под не запустится (будет в статусеPending)preferredDuringSchedulingIgnoredDuringExecution- желательное условие, которое работает по принципу бальной системы, суммируя вес (weight) всех меток на нодах.
Доступные операторы в matchExpressions для проверки условий (фильтрация лейблов):
In- значение лейбла (указанное в values) на ноде должно быть одним из спискаNotIn- значение лейбла не должно входить в список (для исключения нод)Exists- проверяет только наличие ключа (список values не указывается)DoesNotExist- проверяет отсутствие ключаGt- значение лейбла должно быть больше (только для числовых значений)Lt- значение лейбла должно быть меньше
Node Affinity
nodeAffinity - определяют правила притяжения подов к нодам.
spec:
affinity:
nodeAffinity:
# Обязательное условие (Hard)
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: diskType
operator: In
values:
- "ssd"
- key: diskType
operator: NotIn
values:
- "hdd"
- key: cpuCount
operator: Gt
values:
- "7"
# Желательное условие (Soft) с весами
preferredDuringSchedulingIgnoredDuringExecution:
- preference:
matchExpressions:
- key: kubernetes.io/arch
operator: In
values:
- arm64
weight: 50
Pod Anti Affinity
podAntiAffinity - определяют правила отвержения (обратное условию nodeAffinity), например, чтобы две копии одного приложения не запускались на одной ноде.
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- torapi
topologyKey: "kubernetes.io/hostname"
weight: 100
Pod Topology Spread
Pod Topology Spread Constraints (TSC) - это механизм, который позволяет гибко и равномерно распределять поды по разным нодам в кластере или любым другим логическим группам (например, не размещать на одном хосте или зонам доступности).
В отличие от podAntiAffinity, который работает по принципу либо разрешить, либо запретить, topologySpreadConstraints фокусируется на балансе. Планировщик (kube-scheduler) при размещении нового пода считает количество уже запущенных подов в каждой логической группе по лейблу и выбирает ту, где размещение пода не нарушит значение maxSkew.
Как и affinity, настройка определяется на уровне спецификации шаблона пода:
spec:
topologySpreadConstraints:
# Максимально допустимая разница в количестве подов
- maxSkew: 1
# Метка на ноде, по которому определяются границы
topologyKey: kubernetes.io/hostname
# Что делать, если идеальное распределение невозможно
# Soft (все равно запланировать под, стараясь соблюсти баланс)
whenUnsatisfiable: ScheduleAnyway
# Hard (оставить под в статусе Pending, пока не появится подходящая пода)
# whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: torapi
Pod Disruption Budget
Pod Disruption Budget (PDB) - гарантирует, что если будут выводиться ноды на обслуживание (с помощью команды kubectl drain), поды на ноде не будут удалены, пока количество подов, указанных в minAvailable не будет запущены на всех доступных нодах.
Если в кластере 2 ноды и всего 2 реплики по 1 на каждой ноде, процесс drain зависнет, поэтому нужно сначала вывести ноду в состояние kubectl cordon, увеличить количество реплик до трех kubectl scale deployment torapi --replicas=3 на оставшихся доступных нодах и уже после этого запустить kubectl drain.
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: rest-api-pdb
namespace: rest-api
spec:
# Минимум доступных подов (число или процент)
minAvailable: 2
# Привяка к подам (должно совпадать с label в Deployment)
selector:
matchLabels:
app: torapi
# pdbGroup: high-priority
kubectl apply -f ./pdb.yaml --namespace=rest-api
kubectl get pdb --namespace=rest-api список всех PDB и их текущий статус
kubectl describe pdb rest-api-pdb --namespace=rest-api - отобразить, какие поды попадают под селектор
Pod Priority Class
Pod PriorityClass (PC) - это глобальный объект (не привязанный к namespace), который сообщает планировщику Kubernetes, какие поды важнее для запуска. Если в кластере закончатся ресурсы, будут удалены менее важные поды (с самым низким приоритетом), чтобы запустить более приоритетные.
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: high-priority-apps
# Чем выше число, тем выше приоритет
value: 1000000
# Если true, этот приоритет будет у всех подов без указанного класса
globalDefault: false
# Разрешает вытеснять поды с меньшим приоритетом
preemptionPolicy: PreemptLowerPriority
Чтобы под получил приоритет, нужно добавить параметр spec.template.spec.priorityClassName в Deployment:
# Спецификация Deployment
spec:
# Шаблон пода, которым управляет Deployment
template:
# Спецификация пода
spec:
priorityClassName: high-priority-apps
kubectl apply -f ./priority.yaml
kubectl get priorityclass или kubectl get pc вывести список всех доступных классов приоритета в кластере
kubectl describe priorityclass high-priority-apps посмотреть описание и числовое значение выбранного класса
kubectl get pods -A -o custom-columns=NAME:.metadata.name,PRIORITY:.spec.priorityClassName отобразить назначенные классы приоритета для всех подов в кластере
StatefulSet
StatefulSet (STS) используется для приложений, которым необходимо сохранять свое состояние и уникальность.
Основные приемущества StatefulSet:
- Поддерживает уникальное и статическое имя в формате
[metadata.name]-[replica_number]и имеет строгий порядок запуска (от 0 и по возрастанию). Если подpg-0упал и поднялся на другой ноде, он все равно получит имяpg-0, поэтому другие сервисы всегда знают, по какому адресу его искать. Headless Service- это специальный тип сервиса, у которого отсутствует виртуальный IP-адрес (spec.clusterIP: None), который требуется для работыStatefulSetи позволяет обращаться напрямую к конкретной реплике пода поDNSимени, например,pg-0.pg-svcбудет вести строго на под с именемpg-0.Volume Claim Templates- для каждой новой реплики пода автоматически создает отдельный запрос на диск (PVC), и если под переедет на новую ноду, его диск с данными переедет вместе с ним.
apiVersion: apps/v1
kind: StatefulSet
metadata:
# Имя объекта в кластере (от него будут строиться имена подов в формате pg-0)
name: pg
spec:
# Имя Headless Service, с помощью которого поды получат стабильные DNS-имена в формате pg-0.pg-svc
serviceName: "pg-svc"
replicas: 1
# Разрешить параллельный запуск с сохранением имени (не дожидаясь очередности запуска)
# podManagementPolicy: Parallel
# Привязка подов (должно строго совпадать с labels в секции template)
selector:
matchLabels:
app: pg
template:
metadata:
# Метки, по которым selector ищет свои поды
labels:
app: pg
spec:
containers:
- name: pg
image: postgres:15
ports:
- containerPort: 5432
# Имя позволяет сервису обращаться к порту по имени, а не по номеру
name: db-port
env:
- name: PGPORT
value: "5432"
- name: POSTGRES_PASSWORD
value: "SecretPassword"
# Определить путь, куда внутри контейнера подключить постоянное хранилище
volumeMounts:
# Должен совпадать с именем, указанным в volumeClaimTemplates.metadata.name
- name: pg_data
mountPath: /var/lib/postgresql/data
volumeClaimTemplates:
- metadata:
# Имя шаблона, на которое ссылается volumeMounts
name: pg_data
spec:
# Режим ReadWriteOnce, где диск может быть подключен только к одному поду одновременно
accessModes:
resources:
requests:
storage: 50Gi
Вывести список всех StatefulSet (набор команд идентичен Deployment):
kubectl get sts
Headless Service
Главное отличие Headless Service от обычного Service заключается в отсутствие виртуального IP-адреса (ClusterIP: none).
Конфигурация Headless Service для прямой связи с подом по имени:
apiVersion: v1
kind: Service
metadata:
name: pg-svc
spec:
clusterIP: None
selector:
app: pg
ports:
# Внешний порт сервиса
- port: 5432
# Ссылаемся на порта в контейнере по его имени
targetPort: db-port
DaemonSet
DaemonSet (DS) используется для запуска приложений для взаимодействия с ресурсами node. Он гарантирует, что запустит количество подов, равное количесту воркер нод (отсутствует параметр replicas), в случае добавления новых рабочих серверов в кластер, DaemonSet автоматически обнаружит их и поднимит на них свои поды.
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd-logs
labels:
app: fluentd
spec:
selector:
matchLabels:
app: fluentd
template:
metadata:
labels:
app: fluentd
spec:
containers:
- name: fluentd
image: fluentd:latest
# Пробрасываем директорию с логами внутрь контейнера
volumeMounts:
- name: varlog
mountPath: /var/log
volumes:
- name: varlog
# Путь на физическом узле (ноде)
hostPath:
path: /var/log
Вывести список всех DaemonSet (набор команд идентичен Deployment):
kubectl get ds
Job
Job используются для единовременного выполнения скриптов без дальнейше работы приложения, (пример, при миграции баз данных, когда добавляет новое поле в существующей таблице с помощью SQL-скрипта или кода на языке фреймворка). Задача создает один или несколько подов, и будет продолжать попытки выполнения кода до тех пор, пока указанное количество из них не завершится успешно. По мере успешного завершения подов, задача отслеживает эти завершения и когда достигается указанное количество успешных завершений, все задачи завершаеются (удаление задачи приведет к очистке созданных ею подов).
Если при обновление приложения, нужно добавить новую колонку в таблицу БД, это нельзя делать внутри обычного Deployment, т.к. если будет запущено 5 подов, они все одновременно попытаются применить одну и ту же миграцию и сломают базу.
apiVersion: batch/v1
kind: Job
metadata:
name: db-migrate
spec:
# Cколько раз требуется успешно выполнить задачу
completions: 1
# Сколько подов могут работать над этой задачей одновременно
parallelism: 1
# Сколько раз пытаться перезапустить под, если скрипт упал с ошибкой (Fail -> Retry)
backoffLimit: 3
# Лимит на выполнение в секундах до завершения поды (включая время на скачивание образа и все попытки перезапуска в backoffLimit)
activeDeadlineSeconds: 600
# Удалить Job и созданные им поды через 1 минуту после успешного завершения
ttlSecondsAfterFinished: 60
template:
spec:
containers:
- name: migrate-container
# Используем тот же образ, что и для основного приложения
image: app:0.2.0
# Переопределяем команду запуска
command:
args:
# Передача переменных окружения
env:
- name: DB_HOST
value: "pg-0.pg-svc"
# Передача переменной окружения DB_PASSWORD из Secret в приложение
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
# Имя объекта Secret
name: pg-creds
# Название ключа внутри Secret
key: PASSWORD
# Загрузить все ключи из Secret как переменные окружения
# envFrom:
# - secretRef:
# name: pg-creds
# Если скрипт упал с ошибкой, будет перезапущен тот же Pod
restartPolicy: OnFailure
# Если скрипт упал, будет перезапущена новая пода до достижения backoffLimit
# restartPolicy: Never
kubectl apply -f db-migrate-job.yaml
Нельзя запустить одну и ту же Job второй раз с тем же именем. Чтобы повторить миграцию, нужно либо удалить старую с помощью kubectl delete job db-migrate и создать ее заново, либо использовать replace:
kubectl get job db-migrate -o yaml | kubectl replace --force -f -
# Отобразить список всех задач
# получить подробную информацию о работе, включая Events
# Список подов, созданных этой задачей
# Отобразить логи задачи
Cron Job
Чтобы превратить Job в CronJob, нужно добавить расписание, а все содержимое задачи из блока spec перенести в spec.jobTemplate.spec:
apiVersion: batch/v1
kind: CronJob
metadata:
name: image-checker-daily
spec:
# Расписание в формате Unix-cron (минута час день месяц день-недели)
schedule: "0 0 * * *"
# Что делать, если предыдущая задача еще не завершилась
# Разрешает параллельный запуск (по умолчанию)
# concurrencyPolicy: Allow
# Строго запрещает параллельный запуск
concurrencyPolicy: Forbid
# Завершает текущую работающую задачу и запускает новую вместо нее
# concurrencyPolicy: Replace
# Сколько успешных/проваленных запусков хранить в истории
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 1
jobTemplate:
spec:
template:
spec:
serviceAccountName: image-checker-sa
restartPolicy: OnFailure
containers:
- name: image-checker
image: alpine:3.23
command:
args:
- |
apk add --no-cache kubectl skopeo jq
IMAGES=$(kubectl get pods --all-namespaces -o jsonpath='{range .items[*].status.containerStatuses[*]}{.image}{" "}{.imageID}{"\n"}{end}' | sort -u)
echo "$IMAGES" | while read -r line; do
[ -z "$line" ] && continue
IMAGE=$(echo $line | cut -d' ' -f1)
LOCAL_DIGEST=$(echo $line | cut -d'@' -f2)
if [[ "$IMAGE" == *":"* ]]; then
echo "- Image: ${IMAGE}"
REMOTE_DIGEST=$(skopeo inspect docker://$IMAGE --format "{{.Digest}}" 2>/dev/null)
if [ "$?" -eq 0 ] && [ "$LOCAL_DIGEST" != "$REMOTE_DIGEST" ]; then
echo " --- Local sha: ${LOCAL_DIGEST}"
echo " +++ Remote sha: ${REMOTE_DIGEST}"
else
echo " Local sha: ${LOCAL_DIGEST}"
echo " Remote sha: ${REMOTE_DIGEST}"
fi
IMAGE_CLEAR=$(echo $IMAGE | sed -r "s/\s.+//")
IMAGE_URL=$(echo $IMAGE_CLEAR | cut -d':' -f1)
LOCAL_TAG=$(echo $IMAGE_CLEAR | cut -d':' -f2)
REMOTE_TAGS=$(skopeo list-tags docker://$IMAGE_URL | jq -r '.Tags[]')
LATEST_TAG=$(echo "$REMOTE_TAGS" | sort -V | tail -n 1)
if [ "$LOCAL_TAG" != "$LATEST_TAG" ]; then
echo " --- Current local tag: $LOCAL_TAG"
echo " +++ Latest remote tag: $LATEST_TAG"
else
echo " Current local tag: $LOCAL_TAG"
echo " Latest remote tag: $LATEST_TAG"
fi
echo
fi
done
kubectl apply -f job-daily.yaml
Запустить задачу, не дожидаясь ее времени запуска:
kubectl create job --from=cronjob/image-checker-daily image-checker-job
Init Containers
Init Containers - это контейнеры, которые запускаются перед запуском рабочих контейнеров, например, для проверки доступности файла с секретами, который должен быть доступен на основном контейнере.
apiVersion: apps/v1
kind: Deployment
metadata:
name: alpine
spec:
replicas: 1
revisionHistoryLimit: 1
selector:
matchLabels:
app: alpine
template:
metadata:
labels:
app: alpine
spec:
initContainers:
- name: config-init
image: alpine:3.23
command:
args:
- |
apk add --no-cache curl
curl -L https://raw.githubusercontent.com/Lifailon/rudocs/refs/heads/main/Docker-Compose/gatus/config/config.yaml -o /data/gatus.config
volumeMounts:
- name: shared-data
mountPath: /data
containers:
- name: config-checker
image: alpine:3.23
command:
volumeMounts:
- name: shared-data
mountPath: /config
volumes:
- name: shared-data
emptyDir:
Image Pull Secrets
Pull Image Private Registry используется для загрузки образ из частного реестра образов контейнеров.
Создаем секрет:
# kubectl create secret docker-registry "image-pull-nexus" \
# --docker-server="$nexusUrl" \
# --docker-username="$nexusUser" \
# --docker-password="$nexusPass" \
# --dry-run=client -o yaml | kubectl apply -f -
# Создаем секрет вручную с доступом в несколько репозиториев
AUTH_BASE64=
|
# Обновляем imagePullSecrets для SA
Подключаем к контейнеру токен доступа:
spec:
# Назначем SA для всех контейнеров с привязанным imagePullSecrets
serviceAccountName: nexus
containers:
- name: vm-agent
image: nexusUrl/image:version
imagePullPolicy: IfNotPresent
# Вручную прописываем imagePullSecrets для контейнера (вместо SA)
imagePullSecrets:
- name: image-pull-nexus
ConfigMap
ConfigMap - это объект для хранения неконфиденциальных данных.
Существует 3 варианта объявления значений в конфигурации:
- Переменные окружения в формате:
key: value - Cодержимое многострочных файлов
- Бинарные данные для картиновк или сертификатов в формате
Base64
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-config
data:
PORT: 80
config: |
server {
listen 80;
server_name localhost;
}
binaryData:
favicon.ico: iVBORw0KGgoAAAANSUhEUgAA...
Работа с конфигурациями:
# Создать ConfigMap с данными из файла
# Изменить конфигурацию
# Получить список всех ConfigMap, количество данных и дату их создания
# Отобразить содержимое ConfigMap (на примере корневого сертифика)
# Вывести метаданные (лейблы и аннотации манифеста)
# Выведет только содержимое ключа ca.crt
Secrets
Secrets - это объект для хранения конфиденциальных данных, таких как пароли, токены или ключи.
Секреты принято хранить в формате Base64:
|
|
Формат передачи данных аналогичен ConfigMap:
apiVersion: v1
kind: Secret
type: Opaque
metadata:
name: telegram-secrets
namespace: monitoring
data:
TELEGRAM-API-KEY: MTIzNDU2Nzg5OlRPS0VO
TELEGRAM-CHAT-ID: LTc3Nzc3Nzc3Nzc=
kubectl apply -f telegram-secrets.yaml
# Создать секрет в формате ключ-значение
# Создать секрет из содержимого файла
# Получить список всех секретов
# Получить информацию о секрете (размер в байтах)
# Получить содержимое секретов в кодировке base64
# Декодировать содержимое секрета
|
# Удалить секрет
RBAC
ServiceAccount - это УЗ, с помощью которой поды приложений могут использовать для идентификации себя от ее имени на внешних ресурсах, так и внутри самого кластера.
Пример создания ServiceAccount (который подключается в спецификации пода через spec.serviceAccountName) для доступа ко всем ресурсам кластера на чтение.
apiVersion: v1
kind: ServiceAccount
metadata:
name: event-exporter-sa
namespace: monitoring
Создаем кастомные права доступа на уровне кластера:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: event-exporter-role
rules:
# Доступ ко всем группам ресурсов (включая CRD)
- apiGroups:
# Доступ ко всем типам ресурсов (поды, ноды, сервисы и т.д.)
resources:
# Доступ только на чтение и наблюдение в реальном времени
verbs:
Привязка прав доступа ClusterRole к ServiceAccount:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: event-exporter
# Какие права
roleRef:
kind: ClusterRole
apiGroup: rbac.authorization.k8s.io
name: event-exporter-role
# Для кого
subjects:
- kind: ServiceAccount
namespace: monitoring
name: event-exporter-sa
Пример создания SA с полными правами доступа:
apiVersion: v1
kind: ServiceAccount
metadata:
name: devops-sa
namespace: monitoring
Привязываем кластерные права admin к SA:
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: admin-rb
namespace: monitoring
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: admin
subjects:
- kind: ServiceAccount
name: devops-sa
namespace: monitoring
Создаем секрет с токеном доступа, который можно использовать в kubeconfig для подключения к кластеру под SA с привязанными к нему правами через RoleBinding:
apiVersion: v1
kind: Secret
type: kubernetes.io/service-account-token
metadata:
name: devops-token
namespace: monitoring
annotations:
kubernetes.io/service-account.name: devops-sa
kubectl get secret cross-devops -n monitoring -o jsonpath='{.data.token}' | base64 --decode извлечь содержимое токена
Events
Events - это записи в API, описывающие изменения состояния объектов (нодов, подов, деплойментов, сервисов и т.д.) и ошибки, которые помогают понять, почему приложение не запускается (например, нехватка ресурсов). Все события автоматически удаляются из базы данных etcd через 1 час.
# Вывести события для всех объектов в текущем пространстве имен
# Сортировка событий по времени
# Фильтрация событий по типу события
# Фильтрация событий по типу объекта (для всех подов)
Event Exporter
События в кластере принято собирать с помощью экспортера и отправлять во внешние системы для хранения и анализа.
Пример конфигурации kubernetes-event-exporter для настройки чтения всех событий в кластере Kubernetes и вывода в stdout запущенного пода (лог контейнера) или путем отправки уведомлений в Telegram через Web Hook.
apiVersion: v1
kind: ConfigMap
metadata:
name: event-exporter-config
namespace: monitoring
data:
config.yaml: |
route:
routes:
- match:
- receiver: "dump"
- match:
- receiver: "telegram"
# Фильтрация по типу события, ресурса или namespace
type: "Warning"
# kind: "Deployment"
# namespace: "dev"
receivers:
- name: dump
# Перенаправлять в лог контейнера
stdout: {}
- name: telegram
webhook:
endpoint: "https://api.telegram.org/bot{{ env "TELEGRAM_API_KEY" }}/sendMessage"
headers:
Content-Type: "application/json"
layout:
chat_id: "{{ env "TELEGRAM_CHAT_ID" }}"
parse_mode: "HTML"
text: |
<b>⚠ [K8s Warning Event]</b>
<b>Object:</b> {{ .InvolvedObject.Kind }}/{{ .InvolvedObject.Name }}
<b>Namespace:</b> {{ .InvolvedObject.Namespace }}
<b>Reason:</b> {{ .Reason }}
<b>Message:</b> {{ .Message }}
Подключение содержимого конфигурации из ConfigMap и переменных окружения из Secret в Deployment для контейнера внутри пода:
apiVersion: apps/v1
kind: Deployment
metadata:
name: event-exporter
namespace: monitoring
spec:
replicas: 1
selector:
matchLabels:
app: event-exporter
template:
metadata:
labels:
app: event-exporter
annotations:
prometheus.io/scrape: 'true'
prometheus.io/port: '2112'
prometheus.io/path: '/metrics'
spec:
serviceAccountName: event-exporter-sa
containers:
- name: event-exporter
image: ghcr.io/resmoio/kubernetes-event-exporter:v1.7
imagePullPolicy: IfNotPresent
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 200m
memory: 256Mi
env:
- name: TELEGRAM_API_KEY
valueFrom:
secretKeyRef:
name: telegram-secrets
key: TELEGRAM-API-KEY
- name: TELEGRAM_CHAT_ID
valueFrom:
secretKeyRef:
name: telegram-secrets
key: TELEGRAM-CHAT-ID
- name: HTTPS_PROXY
values: http://192.168.3.105:20171
args:
- -conf=/data/config.yaml
volumeMounts:
- mountPath: /data
name: config
volumes:
- name: config
configMap:
name: event-exporter-config
Shell Operator
Shell Operator - это инструмент для запуска bash-скриптов, которые оперируют событиями в кластере Kubernetes.
Пример отслеживания создания новых подов:
if ; then
else
podName=
fi
Пример скрипта для отправки системных событий в Telegram или любой другой месенджер с помощью Shoutrrr:
if ; then
else
KIND=
fi
Собираем контейнер:
FROM flant/shell-operator:latest
ARG TARGETARCH
ARG SHOUTRRR_VERSION=v0.8.0
COPY event-notify-operator.sh /hooks/handler.sh
RUN apk add --no-cache curl tar && \
curl -Lf "https://github.com/containrrr/shoutrrr/releases/download/${SHOUTRRR_VERSION}/shoutrrr_linux_${TARGETARCH}.tar.gz" \
-o /tmp/shoutrrr.tar.gz && \
tar -xzf /tmp/shoutrrr.tar.gz -C /usr/local/bin shoutrrr && \
chmod +x /usr/local/bin/shoutrrr /hooks/handler.sh && \
rm /tmp/shoutrrr.tar.gz && \
apk del curl tar
Собираем образ и проверяем отправку:
Создаем секрет в формате stringData (без кодирования в Base64):
apiVersion: v1
kind: Secret
type: Opaque
metadata:
name: shoutrrr-secrets
namespace: monitoring
stringData:
SHOUTRRR-TELEGRAM: "telegram://123456789:ABC...@telegram?channels=123456789&parseMode=markdown"
Запускаем оператор в кластере для отслеживания системных событий:
apiVersion: apps/v1
kind: Deployment
metadata:
name: event-notify-operator
namespace: monitoring
spec:
replicas: 1
selector:
matchLabels:
app: event-notify-operator
template:
metadata:
labels:
app: event-notify-operator
spec:
# SA с правами выполнения kubectl get events
serviceAccountName: event-exporter-sa
# serviceAccountName: event-notify-operator-sa
containers:
- name: operator
image: lifailon/event-notify-operator:v1
imagePullPolicy: Always
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 200m
memory: 256Mi
env:
# Прокидываем переменную окружения из секрета в контейнер
- name: SHOUTRRR_TELEGRAM
valueFrom:
secretKeyRef:
name: shoutrrr-secrets
key: SHOUTRRR-TELEGRAM
# Статическая переменная для фильтрации событий
- name: WARNING_ONLY
value: "false"
# Стандартные переменные для настройки пересылки трафика через прокси сервер и исключения
- name: HTTPS_PROXY
value: http://192.168.3.105:20171
- name: NO_PROXY
value: kubernetes.default,svc.cluster.local,cluster.local,localhost,10.96.0.1,127.0.0.1,192.168.3.0/24
Persistent Volume
Persistent Volume (PV) - это объект для настройки ручного подключения хранилища в кластер.
apiVersion: v1
kind: PersistentVolume
metadata:
name: local-pv
spec:
capacity:
storage: 10Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Delete
storageClassName: local-storage
# Путь на ноде для хранения данных
local:
path: /mnt/k8s_data
# Привязываем том к ноде и под при подключение хранилища
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- hv-us-101
Настройка NFS сервера для удаленного хранения данных:
# Установка NFS сервера
&&
# Создание директории
# Шарим директорию для всех *, указанной подсети 192.168.3.0/24 или конкретного IP-адреса
|
# Применение настроек
# Установка NFS клиента на всех узлах Kubernetes
Подключаем NFS хранилище к кластеру
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-pv
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
nfs:
server: 192.168.3.101
path: /mnt/k8s_data
readOnly: false
kubectl apply -f nfs-pv.yaml
kubectl get pv
Persistent Volume Claim
PersistentVolumeClaim (PVC) - это запрос пользователя на хранилище данных (PV), который позволяет монтировать в поды и использовать ресурсы настроенного PersistentVolume. Для одного настроенного PV может быть привязан только один PVC.
Создание PersistentVolumeClaim:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nfs-pvc
spec:
# Название вручную настроенного хранилища PersistentVolume
volumeName: nfs-pv
accessModes:
- ReadWriteMany # Подключен к многим нодам в режиме чтения-записи
# - ReadWriteOnce # Подключен только к одной поде в режиме чтения-записи
# - ReadOnlyMany # Подключен к многим нодам в режиме только на чтение
resources:
requests:
storage: 5Gi
kubectl apply -f nfs-pvc.yaml
kubectl get pvc
Подключение хранилища к поду:
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgres
spec:
replicas: 1
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:15
ports:
- containerPort: 5432
env:
- name: POSTGRES_PASSWORD
value: "SecretPassword"
- name: PGDATA
value: "/var/lib/postgresql/data/pgdata"
volumeMounts:
- name: pg_data
mountPath: /var/lib/postgresql/data
volumes:
- name: pg_data
persistentVolumeClaim:
claimName: nfs-pvc
Storage Class
Storage Class (SC) - это объект для автоматического создания хранилищ PV в кластере при каждом новом запросе PVC.
NFS subdir external provisioner - это инструмент для автоматизации создания постоянных томов PV и директории под новые PV в формате ${namespace}-${pvcName}-${pvName} на уже существующем и настроенном NFS-сервере.
# --set nfs.server=192.168.3.101 \
# --set nfs.path=/mnt/k8s_data \
# --set storageClass.name=nfs-sc
Создать Storage Class:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs-sc
# Совпадает с ENV в Deployment
provisioner: k8s-sigs.io/nfs-subdir-external-provisioner
# При удаление PVC (kubectl delete pvc), объект PV и данные на NFS сервере будут сохранены
reclaimPolicy: Retain
# Создать архивную папку на сервере при удаление PVC (если reclaimPolicy: Delete)
parameters:
archiveOnDelete: "true"
kubectl apply nfs-sc.yaml
kubectl get storageclass
Выпоняем запрос постоянного тома из nfs-sc:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nfs-pvc
spec:
storageClassName: nfs-sc
accessModes:
- ReadWriteMany
resources:
requests:
storage: 5Gi
CSI Driver SMB - это драйвер, который поддерживает динамическое выделение постоянных томов (PV) с помощью запросов на постоянные тома (PVC) путем создания нового подкаталога на уже существующем и настроенном SMB-сервере.
Настраиваем подключение к SMB шаре:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: smb
provisioner: smb.csi.k8s.io
parameters:
source: //192.168.3.100/k8s-data
csi.storage.k8s.io/provisioner-secret-name: smb-secret
csi.storage.k8s.io/provisioner-secret-namespace: default
csi.storage.k8s.io/node-stage-secret-name: smb-secret
csi.storage.k8s.io/node-stage-secret-namespace: default
volumeBindingMode: Immediate
allowVolumeExpansion: true
mountOptions:
- dir_mode=0777
- file_mode=0777
- uid=1001
- gid=1001
HPA
Horizontal Pod Autoscaling (HPA) - это механизм горизонтального масштабирования, который позволяет автоматически увеличивать или уменьшать количество реплик (подов) в зависимости от текущей нагрузки по показателям метрик, получаемых из metrics-server. Если нагрузка на одну поду увеличивается, то новая реплика должна снять нагрузку с первого пода, тем самым средняя нагрузка на 1 под будет ниже.
Включить автоскейлинг по нагрузке на процессор:
kubectl autoscale deployment torapi --min=1 --max=3 --cpu-percent=80
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: torapi-hpa
namespace: rest-api
spec:
# Ссылка на масштабируемый объект (Deployment, StatefulSet или ReplicaSet)
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: torapi # Имя Deployment, который будем масштабировать
minReplicas: 1 # Минимальное количество реплик
maxReplicas: 3 # Максимальное количество реплик
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization # Значекние в процентах
averageUtilization: 50 # Когда среднее использование CPU превышает 50%, будет увеличено количество реплик
- type: Resource
resource:
name: memory
target:
type: AverageValue # Точное значение
averageValue: 500Mi # Потребление памяти свыше 500 Мбайт
# Кастомные метрики, например, количество запросов в секунду на Ingress
# - type: External
# external:
# metric:
# name: nginx_ingress_controller_requests
# target:
# type: AverageValue
# averageValue: 50
behavior:
scaleUp:
stabilizationWindowSeconds: 0 # задержка перед увеличением реплик
policies:
- type: Pods # Pod или Percent (в процентном соотношение)
value: 1 # Добавлять по 1 поду за шаг (100 для Percent удвоит значение подов)
periodSeconds: 30 # Интервал между шагов в секундах
scaleDown:
stabilizationWindowSeconds: 300 # задержка в 5 минут перед уменьшением реплик
kubectl apply -f torapi-hpa.yaml
# Отобразить статус работы всех HPA и текущие таргеты (`cpu: 1%/50%`)
# Отобразить статус работы HPA (текущее и тригерное значение для масштабирования)
# Отобразить нагрузку на подах по cpu и memory
# Получить метрики напрямую из API
|
# Будет активен 1 под из 5 подов (вместо двух, изначально определенных в Deployment)
Запустить нагрузку на CPU (удобно для проверки срабатывания HPA)
# Запустить процессы
cpuCount=2
# Получить количество запущенных процессов с нагрузкой
# Остановить нагрузку
pids=
VPA
Vertical Pod Autoscaling (VPA) - механизм вертикального автоматического масштабирование позволяет выделять больше ресурсов (памяти или процессорного времени). Для применения нового значения лимитов, требуется перезапуск текущего пода.
apiVersion: "autoscaling.k8s.io/v1"
kind: VerticalPodAutoscaler
metadata:
name: torapi-vpa
spec:
targetRef:
apiVersion: "apps/v1"
kind: Deployment
name: torapi
updatePolicy:
updateMode: "Auto" # Перезапустит под для обновления лимитов (по умолчанию)
# updateMode: "Initial" # Назначает ресурсы только при создании пода (не перезапускает рабочие)
# updateMode: "Recreate" # Удаляет и пересоздает под, если нужно изменить ресурсы
# updateMode: "Off" # Только рекомендации (ничего не меняет сам)
resourcePolicy:
containerPolicies:
# Применит ко всем контейнерам пода
- containerName: '*'
# Какие ресурсы оптимизировать
controlledResources:
# Минимальные значения, ниже которых VPA не опустит ресурсы
minAllowed:
cpu: "100m"
memory: "128Mi"
# Максимальные значения, выше которых VPA не поднимет ресурсы
maxAllowed:
cpu: "2"
memory: "4Gi"
# Какие значения VPA может менять (RequestsAndLimits или только Requests)
controlledValues: RequestsAndLimits
JSONPath
JSONPath - шаблонизатор для фильтрации вывода в командах kubectl.
kubectl get nodes -o=jsonpath='{.items[*].status.addresses[*].address}' отобразить ip-адреса всех node
kubectl config view -o jsonpath='{.users[*].name}' получить список пользователей в конфигурации
kubectl config view -o jsonpath='{.users[?(@.name == "admin")].user.password}' получить пароль для пользователя admin
| Функция | Описание | Пример | Результат |
|---|---|---|---|
text | обычный текст | kind is {.kind} | kind is List |
@ | текущий объект | {@} | то же, что и ввод |
. или [] | оператор выбора по ключу | {.kind}, {['kind']} или {['name\.type']} | List |
.. | рекурсивный спуск | {..name} | 127.0.0.1 127.0.0.2 myself e2e |
* | шаблон подстановки для получение всех объектов | {.items[*].metadata.name} | [127.0.0.1 127.0.0.2] |
[start:end:step] | оператор индексирования | {.users[0].name} | myself |
[,] | оператор объединения | {.items[*]['metadata.name', 'status.capacity']} | 127.0.0.1 127.0.0.2 map[cpu:4] map[cpu:8] |
?() | фильтрация | {.users[?(@.name=="e2e")].user.password} | secret |
range и end | перебор списка (цикл) | {range .items[*]}[{.metadata.name}, {.status.capacity}] {end} | [127.0.0.1, map[cpu:4]] [127.0.0.2, map[cpu:8]] |
'' | интерпретируемая в кавычках строка | {range .items[*]}{.metadata.name}{'\t'}{end} | 127.0.0.1 127.0.0.2 |
Вывести список подов в формате nodeName: podeName используя JSONPath и Go Template:
kubectl get pods -o jsonpath='{range .items[*]}{.spec.nodeName}{": "}{.metadata.name}{"\n"}{end}'
kubectl get pods -o go-template --template '{{range .items}}{{.spec.nodeName}}: {{.metadata.name}}{{"\n"}}{{end}}'
Пример цикла и условия:
pause
Go
Для шаблона:
Items:
- start
- sleep
- stop
CRD
Custom Resource Definition (CRD) - это расширение API Kubernetes, которое позволяет создавать собственные типы ресурсов.
Traefik
Ingress - это прокси-сервер, который управляет входящим HTTP-трафиком в кластере и направляет его к нужным внутренним сервисам через единую точку входа, поддерживая балансировку между нодами по имени и маршрутизацию запросов к разным конечным точкам в пути.
Ingress Controller
Устанавливаем Traefik в роле Ingress Controller:
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: traefik
name: traefik
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: traefik
template:
metadata:
labels:
app: traefik
spec:
# SA с правами доступа
serviceAccountName: traefik-ingress-controller
containers:
- name: traefik
image: traefik:v3.5.3
args:
- --api.dashboard=true
- --api.insecure=true
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
- --providers.kubernetescrd=true
# Включить режим работы Ingress
- --providers.kubernetesingress=true
# Название класса, которое указывается в Ingress
- --providers.kubernetesingress.ingressclass=traefik
- --metrics.prometheus=true
- --metrics.prometheus.entryPoint=traefik
- --metrics.prometheus.addEntryPointsLabels=true
- --metrics.prometheus.addServicesLabels=true
ports:
- name: web
containerPort: 80
# hostPort: 80
- name: websecure
containerPort: 443
# hostPort: 8443
- name: admin
containerPort: 8080
hostPort: 8888
livenessProbe:
httpGet:
path: /dashboard/
port: 8080
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet:
path: /dashboard/
port: 8080
initialDelaySeconds: 10
periodSeconds: 10
kubectl apply -f traefik-deployment.yaml
Установить RBAC для Traefik:
kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.6/docs/content/reference/dynamic-configuration/kubernetes-crd-rbac.yml
Сервис для доступа к Ingress за пределами кластера:
apiVersion: v1
kind: Service
metadata:
name: traefik-service
namespace: default
annotations:
metallb.universe.tf/address-pool: "default-pool"
metallb.universe.tf/loadBalancerIPs: "192.168.3.200"
spec:
type: LoadBalancer
selector:
app: traefik
ports:
- name: web
port: 80
protocol: TCP
targetPort: 80
- name: websecure
port: 443
protocol: TCP
targetPort: 443
- name: admin
port: 8888
protocol: TCP
targetPort: 8080
Создаем тестовый под и сервис go-httpbin:
kubectl apply -f traefik-service.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: httpbin
spec:
replicas: 1
selector:
matchLabels:
app: httpbin
template:
metadata:
labels:
app: httpbin
spec:
containers:
- name: httpbin
image: mccutchen/go-httpbin:2.18
ports:
- containerPort: 8080
livenessProbe:
httpGet:
path: /status/200
port: 8080
readinessProbe:
httpGet:
path: /status/200
port: 8080
kubectl apply -f httpbin-deployment.yaml
apiVersion: v1
kind: Service
metadata:
name: httpbin-service
spec:
selector:
app: httpbin
ports:
- port: 80
targetPort: 8080
protocol: TCP
kubectl apply -f httpbin-service.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: httpbin
namespace: default
spec:
# Указывает, что этот Ingress должен обрабатывать Traefik
ingressClassName: traefik
rules:
- host: httpbin.k8s.local
# Настройка маршрутизации трафика
http:
paths:
# Входящий endpoint
- path: /
# Начинается с (/.+)
pathType: Prefix
# Точное совпадение
# pathType: Exact
# На какой сервис и порт перенаправлять трафик
backend:
service:
name: httpbin-service
port:
number: 80
Ingress Route
Traefik CRD - предоставляет дополнительные пользовательские ресурсы, такие как IngressRoute (вместо стандартного kind: Ingress), Middleware (промежуточный обработчик, например, для добавления авторизации), и другие.
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: httpbin
namespace: default
spec:
entryPoints:
- web
routes:
- match: Host(`httpbin.k8s.local`)
kind: Rule
services:
- name: httpbin-service
port: 80
Middleware
Секрет c кредами для авторизации:
# htpasswd -nbB admin admin | base64
apiVersion: v1
kind: Secret
type: Opaque
metadata:
name: httpbin-secret
namespace: default
data:
users: |
YWRtaW46JDJ5JDA1JGMwcjVBNlNDS1g0UjZGanVDZ1JxcnVmYklFNXRtWHcyc0RQcTF2Wjh6TnJy
d05aSUg5amdXCgo=
Посредник для проверки авторизации:
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: traefik-auth
namespace: default
spec:
basicAuth:
secret: httpbin-secret
Настройка входящего трафика через Ingress с поддержкой авторизации:
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: httpbin
namespace: default
spec:
entryPoints:
- web
routes:
- match: Host(`httpbin.k8s.local`)
kind: Rule
services:
- name: httpbin-service
port: 80
middlewares:
- name: traefik-auth
Istio
Istio - это платформа Service Mesh, обеспечивая прозрачную маршрутизацию трафика, повышенную безопасность (mTLS) и детальную телеметрию, используя прокси-серверы Envoy, развернутые как sidecar-контейнеры.
graph TD
Internet == Input ==> Ingress[Ingress Gateway]
Ingress ==> IngressVS[Virtual Service]
IngressVS ==> Sidecar{Envoy Sidecar}
Sidecar <==> App([Pod/Container/App])
EF[[Envoy Filter]] ==> Sidecar
Sidecar == Output ==> EgressVS[Virtual Service]
EgressVS ==> SE[Service Entry]
SE ==> Egress[Egress Gateway]
Egress ==> DR[Destination Rule]
DR ==> Internet
Istioctl
istioctl - утилита командной строки для настройки Istio, предназначенная для операторов сервисов, позволяющая отлаживать и диагностировать их сеть Istio.
# Скачивание последней версии Istio
|
# Установить поды и сервисы для istio-ingressgateway и istio-egressgateway из профиля default
# https://github.com/istio/istio/blob/master/manifests/profiles/default.yaml
# Установить аддоны для Kiali, Jaeger и Prometheus
# https://github.com/istio/istio/tree/master/samples/addons
# При создании любого пода в указанном namespace будет автоматически подселяться прокси-контейнер Envoy
# Изменить глобальный режим работы c ALLOW_ANY на REGISTRY_ONLY
# Любой исходящий трафик, который не указан в ServiceEntry конфигурациях будет отбрасываться
# Проверить текущую конфигурацию кластера на наличие ошибок (например, VS ссылается на несуществующий GW)
# Вывести таблицу статуса (proxy-status) работы всех подов и синхронизации (SYNCED или STALE) с контрольной панелью
# Вывести маршруты (proxy-config), куда Envoy может слать трафик
# Вывести правила Istio (VS, DR, политики TLS) применяются к конкретному поду
# Веб-интерфейс мониторинга Envoy контейнера (port-forward для 15000 порта)
Kiali
Kiali - это графическая панель, которая на основе метрик рисует карту сети в реальном времении и отображает как сервисы связаны друг с другом (стрелочки трафика), где проходят ошибки (красные линии) и объем трафика (RPS), а также поддерживает управление ресурсами Istio.
# Пробросить панель через port-forward
# Обновить конфигурация сервиса Kiali для постоянного доступа к его Dashboard
Gateway
Gateway (GW) - определяет правила маршрутизации трафика на транспортном уровне (L4-L6), т.е. на каких портах, по каким протоколам (HTTP, HTTPS, TCP), в каком режиме (TLS или mTLS) и для каких доменов кластер готов принимать входящий трафик из Интернета.
mTLS (Mutual Transport Layer Security) - это метод взаимной аутентификации, при котором клиент и сервер проверяют сертификаты друг друга перед установкой защищенного соединения (в обычном TLS только клиент проверяет подлинность сервера). В режиме MUTUAL, шлюз отдает сертификат клиенту и требует сертификат у клиента, подписанный твоим caCertificates, или запрос будет сброшен еще на входе.
Выпустить сертификаты и создать секрет с их содержимым:
# Выпускаем корневой сертификат (CA) и ключ
# Выпускаем сертификат для домена (получаем csr - запрос на подпись и приватный ключ)
# Подписываем сертификат домена в CA и получаем подписанный сертификат
# Создаем секрет в кластере с содержимым сертификатов и ключа
Шлюз для приема трафика на внутренних сервисах через ingressgateway:
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
name: httpbin-ingress-gw
namespace: default
spec:
selector:
istio: ingressgateway
servers:
# Принимать запросы только с указанными Host-заголовками
- hosts:
- "httpbin.k8s.local"
port:
number: 80
name: http
protocol: HTTP
- hosts:
- "httpbin.k8s.local"
port:
name: https-443
number: 443
protocol: HTTPS
tls:
# Шлюз не трогает трафик, пересылая зашифрованный поток внутрь кластера.
# Используется, если приложение само хранит сертификаты и умеет в TLS
# mode: PASSTHROUGH
# Шлюз отдает клиенту свой сертификат и клиент доверяет шлюзу (односторонний TLS)
# mode: SIMPLE
# Требует, чтобы клиент тоже предъявил сертификат, подписанный нашим CA
mode: MUTUAL
credentialName: httpbin-certs
# caCertificates: /certs/ca.crt
# serverCertificate: /certs/httpbin.crt
# privateKey: /certs/httpbin.key
Шлюз для внешней интеграции через egressgateway:
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
name: httpbingo-egress-gw # Должно совпадать с именем в VirtualService
namespace: default
spec:
selector:
istio: egressgateway
servers:
- hosts:
- "httpbingo.org"
port:
number: 8080 # Порт сервиса для шлюза
name: http
protocol: HTTP
Virtual Service
VirtualService (VS) - определяет правила маршрутизации трафика на прикладном уровне (L7), такие как разделение по весу для статегии Canary, перенаправление по конечным точкам или HTTP-заголовкам.
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: httpbin-ingress-vs
namespace: default
spec:
# Список шлюзов, через которые этот сервис доступен извне
gateways:
- httpbin-ingress-gw
# Список имен, которые слушает Istio (ищет VirtualService, у которого в hosts есть указанное имя)
hosts:
- "httpbin.k8s.local"
- "httpbin" # Для запросов в том же namespace
- "httpbin.default" # Для запросов из других namespace
- "httpbin.default.svc.cluster.local" # Полное имя (FQDN)
# Правила для HTTP-трафика
http:
- name: httpbin-default-route
# Условие для приема трафика
match:
- uri:
prefix: "/api/v1/"
# method:
# exact: "GET"
# headers:
# user-agent:
# prefix: "curl"
# Подмена пути для пересылки внутрь сервиса
# httpbingo.org/api/v1/headers -> httpbin-service/headers
rewrite:
uri: "/"
# Куда отправить трафик
route:
- destination:
host: httpbin-service
port:
number: 80 # Порт на сервисе (spec.ports.port)
subset: v2 # Версия subset определяется в DestinationRule
weight: 100 # 100% трафика из этого матча идет на v2
# Если предыдущее правило не сработало, обрабатываем следующие
- name: "canary-rollout"
route:
- destination:
host: httpbin-service
subset: v1
weight: 90 # 90% трафика на старую версию
- destination:
host: httpbin-service
subset: v2
weight: 10 # 10% трафика на новую (Canary)
# Настройки для L4 трафика (TCP и TLS)
# tcp:
# - match:
# - port: 5432
# route:
# - destination:
# host: pg-svc
Внешняя интеграция:
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: httpbingo-egress-vs
namespace: default
spec:
# Правила маршрутизации для указанных шлюзов
gateways:
- mesh
- httpbingo-egress-gw
hosts:
- "httpbingo.org"
http:
# Правило для пересылки запроса от пода через Sidecar Envoy Proxy до сервиса Egress Gateway
- match:
- gateways:
- mesh
# Фильтровать правило по указанному порту (запросы на httpbingo.org:80)
# port: 80
rewrite:
authority: "httpbingo.org"
# Sidecar отправляет запрос на сервис istio-egressgateway:8080
route:
- destination:
host: istio-egressgateway.istio-system.svc.cluster.local
port:
number: 8080
# Запретить отправку подом на 443 порт
# - match:
# - gateways:
# -mesh
# port: 443
# fault:
# abort:
# httpStatus: 403
# percentage:
# value: 100
# Правило для пересылки запроса из Egress Gateway Service в Интернет
- match:
- gateways:
- httpbingo-egress-gw
port: 8080
rewrite:
authority: "httpbingo.org"
route:
- destination:
host: "httpbingo.org"
port:
number: 443
Service Entry
ServiceEntry (SE для Egress) - используется для регистрации внешних сервисов в адресной книге Istio (т.к. внешние сервисы не имею свои объекты Service), что позволяет Sidecar Envoy Proxy разрешать (требуется при использование режима работы REGISTRY_ONLY в OutboundTrafficPolicy глобальной конфигурации Mesh) и применять направление трафика из VirtualService.
apiVersion: networking.istio.io/v1beta1
kind: ServiceEntry
metadata:
name: httpbingo-egress-se
namespace: default
spec:
hosts:
- "httpbingo.org" # Должно совпадать с hosts в VirtualService
location: MESH_EXTERNAL # Указываем, что ресурс находится вне кластера
resolution: DNS # Istio сам определит IP-адреса у хоста через DNS
ports:
# Чтобы под мог достучаться до Gateway через Sidecar
- number: 80
name: http
protocol: HTTP
# Чтобы шлюз мог выпустить трафик в Интернет через Gateway
- number: 443
name: https
protocol: HTTPS
Destination Rule
DestinationRule (DR) - настраивает политики трафика, применяемые после маршрутизации (VS -> SE -> GW -> DR). Отвечает за использование протокола TLS, режим балансировки, ограничение количества соединений и разделение на версии через subsets.
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: httpbingo-egress-dr
namespace: default
spec:
host: "httpbingo.org"
trafficPolicy:
# Настройка TLS для подключений к вышестоящему сервису по указанному порту
portLevelSettings:
- port:
number: 443
# Заставляет использовать протокол TLS на шлюзе
tls:
mode: SIMPLE
# Определяет режим работы, если у внешнего сервиса несколько IP-адресов
# Которые получены с помощью resolution DNS через ServiceEntry
loadBalancer:
simple: ROUND_ROBIN
# Отправляет на адрес, у которого меньше активных TCP-соединений или HTTP-запросов
# simple: LEAST_CONN
# simple: LEAST_REQUEST
# Закрепляет клиента за определенным IP на основе IP клиента, заголовке или cookie
# consistentHash:
# useSourceIp: true
# httpHeaderName: "X-User-ID"
# httpCookie:
# name: "user-session"
# ttl: "3600s"
connectionPool:
tcp:
# Максимальное количество одновременных TCP-соединений
maxConnections: 100
# Время ожидания для установки соединения
connectTimeout: 3s
http:
# Максимальное количество активных запросов к целевому адресу
http2MaxRequests: 1000
# Максимальное количество запросов на одно соединение
maxRequestsPerConnection: 10
# Максимальное количество попыток повторного подключения
maxRetries: 3
Envoy Filter
EnvoyFilter (EF) - низкоуровневый ресурс, который позволяет внедрять кастомные настройки прямо в прокси-сервера Envoy, когда возможностей VirtualService не хватает (например, для настройки Rate Limit).
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: add-egress-header
namespace: istio-system
spec:
# Применяем действие для шлюза
workloadSelector:
labels:
istio: egressgateway
configPatches:
- applyTo: HTTP_FILTER
match:
context: GATEWAY
listener:
portNumber: 8080
filterChain:
filter:
name: "envoy.filters.network.http_connection_manager"
# Вставляем фильтр на lua-скрипте для добавления заголовка перед отправкой
patch:
operation: INSERT_BEFORE
value:
name: envoy.lua
typed_config:
"@type": type.googleapis.com/envoy.config.filter.http.lua.v2.Lua
inlineCode: |
function envoy_on_request(request)
request:headers():add("X-Egress-Validated", "true")
end
MetalLB
MetalLB - балансировщик нагрузки для локальных кластеров, эмулирующий работу облачных провайдеров. Настраивается пул адресов, и в случае падения ноды, переводит IP-адреса сервисов на другую ноду. Для сервисов LoadBalancer (включая Ingress-контроллер) выдается один внешний виртуальный IP-адрес, который прописывается на внешнем DNS-сервере.
Создать новый пул адресов:
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: default-pool
namespace: metallb-system
spec:
addresses:
# - 192.168.3.201/32
- 192.168.3.201-192.168.3.210
autoAssign: false # требует аннотации пула и указание адреса в service
kubectl apply -f ip-address-pool.yaml
Разрешить анонсирование IP-адресов из default-pool в локальной сети через протокол ARP на канальном уровне L2:
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: default-l2
namespace: metallb-system
spec:
ipAddressPools:
- default-pool
kubectl apply -f l2-advertisement.yaml
Добавляем annotations на целевом объекте Service:
apiVersion: v1
kind: Service
metadata:
name: traefik-service
namespace: default
annotations:
metallb.universe.tf/address-pool: "default-pool"
metallb.universe.tf/loadBalancerIPs: "192.168.3.200"
spec:
type: LoadBalancer
selector:
app: traefik
ports:
- name: web
port: 80
protocol: TCP
targetPort: 80
- name: websecure
port: 443
protocol: TCP
targetPort: 443
Longhorn
Longhorn - это распределенная блочная система хранения данных для Kubernetes с поддержкой управления через Web UI, которая превращает локальные диски нод в высокодоступное кластерное хранилище за счет механизма репликации. Поды обращаются к тому хранилища через движок, который в свою очередь записывает данные во все реплики синхронно, при этом чтение может происходить из любой реплики параллельно.
# Сделить за процессом установки
# Отобразить все доступные хранилища
Требуется установить зависимости на каждой ноде:
# Установка NFS-клиента для доступа в режиме ReadWriteMany
# Установка iSCSI-клента для размещения томов как таргетов на нодах
Создание VPC:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: beszel-pvc
namespace: monitoring
spec:
# storageClassName: local-path
storageClassName: longhorn
accessModes:
- ReadWriteMany
# - ReadWriteOnce
# - ReadOnlyMany
resources:
requests:
storage: 5Gi
Подключение хранилища к контейнеру в спецификации пода:
spec:
containers:
- name: beszel-server
image: henrygd/beszel:latest
volumeMounts:
- name: beszel-data
mountPath: /beszel_data
volumes:
- name: beszel-data
persistentVolumeClaim:
claimName: beszel-pvc
Доступ к данным:
# Директория хранения данных на нодах
# Проверить, что образ используется процессом longhorn
# Просмотреть содержимое образа
# Создать виртуальный диск
# Примонтировать диск
&&
# Поключиться к фс без монтирования
# Отмонтировать и отключить диск
ArgoCD
Argo CD - это декларативный инструмент непрерывного развертывания Kubernetes, использующий методологию GitOps, где Git репозиторий является единственным источником правды. Из коробки поддерживает изменение исходной ветки и параметров для Kustomize или Helm, Diff изменений в кластере по сравнениюю с репозиторием, выбор ресурсов с фильтрацией для их обновления и визуализацию всех объектов.
# Включить режим NodePort или LoadBalancer
# Получить пароль от пользователя admin
|
Режимы синхронизации:
- Force - принудительно пересоздает ресурсы, даже если Kubernetes запрещает их изменение без подтверждения (эквивалент команде
kubectl --force). - Prune - удаляет любые ресурсы в кластере, которые отсутствуют в текущем состоянии Git-репозитория (т.е. помечены желтой корзиной, эквивалент команде
kubectl apply --prune). - Dry Run - тестовый запуск (проверка синхронизации), позволяющий отобразить какие изменения будут применены к кластеру без фактического их выполнения.
- Apply Only - будет только добавлять/обновлять ресурсы, но никогда не удалит ресурсы (обратное действие Prune).
Опции синхронизации:
- Skip Schema Validation - отключает проверку на соответствие YAML-манифестов официальной схеме Kubernetes OpenAPI, например, для работы с кастомными ресурсами (CRD) или при ошибках валидации.
- Auto-Create Namespace - автоматическое создание namespace перед синхронизацией ресурсов, если пространство имен, указанное в манифесте, не существует.
- Prune Last - изменяет порядок, сначала применяет новые ресурсы, дожидается их готовности, и только потом удаляет старые/устаревшие ресурсы.
- Apply Out of Sync Only - обрабатывает только те ресурсы, которые Argo CD пометил как Out of Sync (который определяется после Refresh в процессе сравнения с Git-репозиторием), пропуская все остальные, ускоряя процесс.
- Respect Ignore Differences - игнорировать изменения в определенных полях, например, количество реплик, если используется HPA (Horizontal Pod Autoscaling).
- Server-Side Apply - использует логику объединения изменений на стороне API-сервера Kubernetes, а не на стороне клиента Argo CD, что помогает с большими ресурсами и предотвращает конфликты last-applied-configuration.
- Replace - вместо стандартного
kubectl apply, для объединения изменений, используетkubectl replace(deleteиcreateвместоpatch), где полностью заменяются существующие объекты, илиkubectl create, если объекта нет. - Retry - если синхронизация завершается с ошибкой (например, API-сервер недоступен или лимит запросов превышен), будет повторяться попытка синхронизации через заданные интервалы времени.
Аннотации для настройки:
annotations:
# Управляет очередностью развертывания ресурсов
argocd.argoproj.io/sync-wave: "0"
# Настройка хуков (например, для для Jobs)
argocd.argoproj.io/hook: PreSync # До начала синхронизации
# argocd.argoproj.io/hook: Sync # Во время синхронизации
# argocd.argoproj.io/hook: PostSync # После успешной синхронизации (Healthy)
# argocd.argoproj.io/hook: SyncFail # Если синхронизация завершилась с ошибкой
# Удаление хуков
argocd.argoproj.io/hook-delete-policy: HookSucceeded # После успеха
argocd.argoproj.io/hook-delete-policy: HookFailed # После ошибки
argocd.argoproj.io/hook-delete-policy: BeforeHookCreation # Перед новым запуском ошибки
# Опции синхронизации (можно комбинировать через запятую)
argocd.argoproj.io/sync-options: "Force=true,Prune=true,Replace=false"
# Не помечать приложение как OutOfSync, если этот ресурс есть в кластере, но нет в Git
argocd.argoproj.io/compare-options: IgnoreExtraneous
# Игнорировать ресурс при проверке здоровья всего приложения (Application Health)
argocd.argoproj.io/ignore-healthcheck: "true"
# Создать ресурс один раз и отключить его синхронизацию и проверку изменений с Git
argocd.argoproj.io/compare-options: IgnoreAll
Пример добавления приложения:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: dozzle
spec:
project: default
syncPolicy:
automated:
enabled: false
syncOptions:
- CreateNamespace=true
source:
path: Kubernetes/dozzle
repoURL: https://github.com/Lifailon/rudocs
targetRevision: main
destination:
namespace: monitoring
server: https://kubernetes.default.svc
Добавляем поддержку источника Kustomize в ArgoCD:
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: kustomizations.kustomize.config.k8s.io
annotations:
"api-approved.kubernetes.io": "https://github.com/kubernetes-sigs/kustomize/blob/master/cmd/config/docs/api-conventions/full-resource.md"
spec:
group: kustomize.config.k8s.io
names:
kind: Kustomization
listKind: KustomizationList
plural: kustomizations
singular: kustomization
scope: Namespaced
versions:
- name: v1beta1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
x-kubernetes-preserve-unknown-fields: true
kubectl apply -f argocd-crd-kustomization.yaml
Добавляем поддержку использования helmCharts с помощью Kustomize:
apiVersion: v1
kind: ConfigMap
metadata:
name: argocd-cm
namespace: argocd
labels:
app.kubernetes.io/part-of: argocd
data:
kustomize.buildOptions: "--enable-helm --load-restrictor LoadRestrictionsNone"
kubectl kustomize --enable-helm --load-restrictor LoadRestrictionsNone
MinIO
MinIO - это высокопроизводительное, совместимое с S3 решение для хранения объектов.
services:
minio1:
image: minio/minio
container_name: minio1
restart: unless-stopped
hostname: minio1
command: server http://minio1:9000/data http://minio2:9000/data --console-address ":9001"
environment:
- MINIO_ROOT_USER=admin
- MINIO_ROOT_PASSWORD=MinioAdmin
volumes:
- ./minio1_data:/data
ports:
- 9000:9000 # API
- 9001:9001 # WebUI
minio2:
image: minio/minio
container_name: minio2
restart: unless-stopped
hostname: minio2
command: server http://minio1:9000/data http://minio2:9000/data --console-address ":9001"
environment:
MINIO_ROOT_USER: admin
MINIO_ROOT_PASSWORD: MinioAdmin
volumes:
- ./minio2_data:/data
ports:
- 9002:9000
- 9003:9001
S3fs
S3fs - инструмент для монтирования S3 совместимого хранилища на базе FUSE, позволяя управлять файлами и каталогами в локальной файловой системе.
sudo apt install -y s3fs установка
sudo mkdir -p /mnt/s3 создать директорию для монтирования
echo "admin:MinioAdmin" > /tmp/s3cred && chmod 600 /tmp/s3cred сохранить авторизационные данные для подключения к s3
s3fs <BUCKET_NAME:PATH> <MOUNTPOINT_PATH> <OPTION> формат монтирования
sudo s3fs velero /mnt/s3 -o url=http://localhost:9000 -o use_path_request_style -o passwd_file=/tmp/s3_cred монтировать файловую систему
mount | grep /mnt/s3 отобразить точки монтирования
sudo umount /mnt/s3 отмонтировать
services:
s3fs:
image: efrecon/s3fs:1.95
container_name: velero_data
restart: unless-stopped
privileged: true
stdin_open: true
tty: true
devices:
- /dev/fuse
cap_add:
- SYS_ADMIN
security_opt:
- apparmor=unconfined
environment:
- AWS_S3_URL=http://minio1:9000
- AWS_S3_BUCKET=velero
- AWS_S3_ACCESS_KEY_ID=admin
- AWS_S3_SECRET_ACCESS_KEY=MinioAdmin
- S3FS_ARGS=use_path_request_style,allow_other
volumes:
- ./velero_data:/opt/s3fs/bucket:rshared
Velero
Velero (ранее Heptio Ark) - это инструменты для резервного копирования и восстановления ресурсов кластера Kubernetes и постоянных томов.
Создаем креды для подключения к s3 хранилищу minio:
Установка в кластер:
kubectl get pods -n velero проверяем, что под запущен
kubectl logs deploy/velero -n velero проверяем, что нет ошибок подключения к s3
velero backup-location get отобразить статус BSL (Backup Storage Location) (PHASE - Available)
velero backup create telegram-bot-backup --include-namespaces telegram запустить backup
velero schedule create telegram-daily --schedule "0 3 * * *" --include-namespaces telegram --ttl 168h запускать каждый день в 03:00 (ttl определяет автоматическуое удаление всех созданных velero ресурсов через 7 дней)
velero backup describe telegram-bot-backup --details отобразить статус резервного копирования (ключевое - статус, продолжительность копирования и список ресурсов)
velero backup get отобразить список всех бэкапов, их статус (Completed, Failed, InProgress) и namespace
velero restore create --from-backup telegram-bot-backup --include-namespaces telegram восстанавливает все ресурсы из указанного бэкапа
kubectl get deployments -n telegram --show-labels отобразить все доступные лейблы в deployments
kubectl get all -n telegram --show-labels отобразить все доступные ресурсы в указанном namespace
velero restore create --from-backup telegram-bot-backup --include-namespaces telegram --include-resources deployments,configmaps --selector app=your-deployment-name восстановить только конкретные ресурсы с фильтрацией по label
velero restore get отобразить статус восстановления
Velero UI
velero-ui - веб-интерфейс для управления Velero (vmware-tanzu).
services:
velero-ui:
image: otwld/velero-ui:latest
container_name: velero-ui
restart: unless-stopped
volumes:
- ~/.kube/config:/app/.kube/config:ro
# - /etc/rancher/k3s/k3s.yaml:/app/.kube/config:ro
environment:
- PORT=3504
- KUBE_CONFIG_PATH=/app/.kube/config
# network_mode: host # use for k3s cluster config on localhost
ports:
- 3504:3504 # admin:admin
Kustomize
Kustomize - это встроенный в kubectl (с версии 1.14) инструмент для управления и слияния конфигураций Kubernetes без использования шаблонизаторов (как в Helm). Он похож на Make, т.к. его действия объявлены в файле kustomization.yaml, и на sed, т.к. он выводит отредактированный текст (без создания новых и изменения исходных манифестов).
Resources
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: rest-api
commonLabels:
app: torapi
commonAnnotations:
argocd.argoproj.io/sync-wave: "0"
resources:
- deployment.yaml
- service.yaml
# - hpa.yaml
- ingress.yaml
Формат запуска: kubectl kustomize <path/url>
Объеденить все манифесты перечисленные в resources в один yaml файл в заданном порядке:
Для каждого ресурса автоматически добавляется указанный namespace, а также все labels и annotations указанные в файле.
Применить все перечисленные манифесты из файла kustomization.yaml в кластере:
Kustomize позволяет отключать объекты ресурсов и удалять их из кластера по лейблу:
Overlays Patch
Kustomize работает по принципу наследования конфигураций:
base/- это директори, которая содержит базовые манифесты (например,deployment.yamlиservice.yaml)overlays/- это директория, которая содержит изменения для разных окружений (например,overlays/devиoverlays/test), переопределяя только указанные параметры, соблюдая исходнуюYAMLструктуру.
Пример структуры каталога:
Пример дочернего файла overlays/test/kustomization.yaml:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
# Наследуем все манифесты из base
resources:
- ../../base
# Добавляем патч для изменения ресурсов контейнера
patches:
- path: patch-resources.yaml
Пример конфигурации для overlays/test/path-resources.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: torapi
namespace: rest-api
spec:
template:
spec:
containers:
- name: torapi
resources:
requests:
cpu: "500m"
memory: "1Gi"
limits:
cpu: "1000m"
memory: "2Gi"
kubectl kustomize overlays/test/ проверить конфигурацию
kubectl apply -k overlays/test применить конфигурацию
ConfigMap Generator
Генерация configMap из файлов и переменных окружения:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: telegram
resources:
# - configmap.yaml
- deployment.yaml
configMapGenerator:
- name: openrouter-bot-config
# Передать содержимое файла в configMap
files:
- .env
# Передать переменные окружения из env файла
# envs:
# - .env
# Передать переменные окружения вручную (key=value)
# literals:
# - LOG_MODE=DEBUG
Helm Charts
Используя Kustomize, возможно объединить запуск нескольких Helm-чартов, например, для группировки в один ArgoCD Application.
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: telegram
helmCharts:
- name: ssh-bot
includeCRDs: true
releaseName: ssh-bot
additionalValuesFiles:
- ./values/ssh-bot/values.yaml
- ./values/default/values.yaml
- name: openrouter-bot
includeCRDs: true
releaseName: openrouter-bot
additionalValuesFiles:
- ./values/openrouter-bot/values.yaml
- ./values/default/values.yaml
helmGlobals:
chartHome: ./charts
Структура:
Helm
Charts
Helm - это шаблонизатор для управления конфигурациями, а также менеджер пакетов для управления версионированием в Kubernetes, использующий чарты (charts, которые являются пакетами), содержащими всю информацию для установки и управления приложениями в Kubernetes.
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
Пример базовой структуры:
Chart.yaml(содержит описание пакета)
apiVersion: v2
name: torapi
description: Unofficial API for torrent trackers
version: 0.1.0
appVersion: "0.5.2"
values.yaml(содержит переменные по умолчанию)
# Deployment
replicaCount: 2
image: "lifailon/torapi:latest"
containerPort: 8443
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "200m"
memory: "256Mi"
probe:
path: "/api/provider/list"
port: 8443
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 3
# Service
service:
type: LoadBalancer
port: 8444
targetPort: 8443
Все шаблоны манифестов харнятся в директории templates.
templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name:
namespace: rest-api
spec:
replicas:
selector:
matchLabels:
app:
template:
metadata:
labels:
app:
spec:
containers:
- name:
image:
ports:
- containerPort:
resources:
requests:
cpu: "{{ .Values.resources.requests.cpu | default "100m" }}"
memory: "{{ .Values.resources.requests.memory | default "128Mi" }}"
limits:
cpu: "{{ .Values.resources.limits.cpu | default "200m" }}"
memory: "{{ .Values.resources.limits.memory | default "256Mi" }}"
livenessProbe:
httpGet:
path:
port:
initialDelaySeconds:
periodSeconds:
timeoutSeconds:
failureThreshold:
templates/service.yaml
apiVersion: v1
kind: Service
metadata:
name: -service
namespace: rest-api
spec:
selector:
app:
ports:
- protocol: TCP
targetPort:
port:
type:
helm template torapi . напечатать итоговую спецификацию (проверить подстановку переменных)
helm install torapi . -n monitoring --create-namespace установка в кластер
helm upgrade --install torapi . -n monitoring обновление релиза (при изменение значение в values.yaml)
helm uninstall torapi . удалить
Публикация и установка:
# Разместить <repo_name>-0.1.0.tgz и index.yaml в новую ветку gh-pages
Hooks
В Helm хуки позволяют запускать кастомные скрипты или задания (Jobs) на определенных этапах жизненного цикла релиза (например, до или после установки).
apiVersion: batch/v1
kind: Job
metadata:
name: db-migrate
annotations:
# Очередность выполнения хуков (чем меньше число, тем выше приоритет)
"helm.sh/hook-weight": "0"
# Политика по умолчанию, для удаления старого ресурса того же типа и имени перед тем, как запустить новый
"helm.sh/hook-delete-policy": before-hook-creation
# Политика удаления ресурса (например, Job) после успешного завершения (статус Complete)
"helm.sh/hook-delete-policy": hook-succeeded
# Политика удаления ресурса, если выполнение прошло успешно или произошла ошибка
"helm.sh/hook-delete-policy": hook-succeeded,hook-failed
# Политика не удалять ресурс (например, configMap) после установки, т.к. он нужен приложению
# "helm.sh/hook-delete-policy": before-hook-creation
# Запускать после рендеринга шаблонов, но до создания ресурсов в Kubernetes
# Если pre-install job упадет, Helm не начнет установку чарта
"helm.sh/hook": pre-install
# Запускать после того, как все ресурсы манифеста уже созданы
# "helm.sh/hook": post-install
# Запускается перед удалением любых ресурсов из Kubernetes
# "helm.sh/hook": pre-delete
# Запускается после того, как все ресурсы релиза были удалены
# "helm.sh/hook": post-delete
# Запуск перед или после обновления ресурсов
# "helm.sh/hook": pre-upgrade
# "helm.sh/hook": post-upgrade
# Запуск перед или после выполнения команды отката
# "helm.sh/hook": pre-rollback
# "helm.sh/hook": post-rollback
# Запускается при выполнении команды helm test
# "helm.sh/hook": test
Переменные
Все переменные, объявленные в файлах переменных (в т.ч. переданные через флаг --values) начинаются с {{ .Values. }}, поддерживая вложенность структуры yaml.
name:
Встроенные переменные:
Chart содержимое файла Chart.yaml, где доступны любые объявленные в нем переменные, например, {{ .Chart.Name }}-{{ .Chart.Version }}
Subcharts предоставляет родительскому элементу доступ к области видимости (.Values, .Charts, .Releases и т. д.) дочерних диаграмм
Release.Name название релиза
Release.Namespace пространство имен (если манифест не переопределяет это)
Release.IsInstall параметр устанавливается как true, если текущая операция - установка
Release.IsUpgrade параметр устанавливается как true, если текущая операция представляет собой обновление или откат
Release.Revision: номер ревизии для этого выпуска (при первой установке он равен 1 и увеличивается при каждом обновлении и откате)
Release.Service: сервис, который отображает текущий шаблон (в Helm это всегда Helm)
Capabilities предоставляет информацию о возможностях, которые поддерживает кластер Kubernetes
Capabilities.APIVersions представляет набор версий
Capabilities.APIVersions.Has $versionу показывает, доступна ли версия (например, batch/v1) или ресурс (например, apps/v1/Deployment) в кластере
Capabilities.KubeVersion или Capabilities.KubeVersion.Version версия для Kubernetes
Capabilities.KubeVersion.Major основная версия Kubernetes
Capabilities.KubeVersion.Minor минорная версия Kubernetes
Capabilities.HelmVersion содержит подробную информацию о версии Helm (как в helm version)
Capabilities.HelmVersion.Version текущая версия Helm в формате semver
Capabilities.HelmVersion.GitCommit helm git sha1
Capabilities.HelmVersion.GitTreeState состояние дерева Helm в Git
Capabilities.HelmVersion.GoVersion это версия используемого компилятора Go
Template содержит информацию о текущем выполняемом шаблоне
Template.Name путь к файлу с указанием пространства имен для текущего шаблона (например, mychart/templates/mytemplate.yaml)
Template.BasePath путь с указанием пространства имен к каталогу шаблонов текущей диаграммы (например, mychart/templates)
Файлы
Функции с файлами:
Files функция предоставляет доступ ко всем неспециальным файлам в диаграмме
Files.Get функция для получения файла по имени (.Files.Get config.ini)
Files.GetBytes функция для получения содержимого файла в виде массива байтов, а не строки (полезно, например, для изображений)
Files.Glob функция, которая возвращает список файлов, имена которых соответствуют заданному шаблону оболочки
Files.Lines функция, которая считывает файл построчно (полезно для перебора каждой строки в файле)
Files.AsSecrets функция, которая возвращает содержимое файлов в виде строк, закодированных в Base64
Files.AsConfig функция, которая возвращает тела файлов в виде YAML-карты
Структура:
Читаем содержимое файла configs/application.yml для передачи его содержимого в ConfigMap:
data:
application.yml: |-
Предварительная шаблонизация содержимого файла с помощью функции tpl:
data:
application.yml: |-
Массовая загрузка файлов по шаблону (например, все .yml файлы из директории):
data:
: |-
Автоматическое заполнение ConfigMap всеми файлами из директории:
data:
Читаем содержимое бинарного файла (картинки, сертификаты и т.п.):
data:
logo.png:
Читаем содержимое в бинарном формате из содержимого переменной в Values:
binaryData:
keystore.p12: |-
{{ .Values.app.integrations.kafka.keystore }}
truststore.p12: |-
{{ .Values.app.integrations.kafka.truststore }}
Чтение списка имен, IP-адресов или других слов из файла для цикла:
# Файл ip-addr-list.txt содержит список IP по одному на каждой строке
metadata:
annotations:
ip-allow-{{ $index }}:
Пример вывода:
metadata:
annotations:
ip-allow-0: "192.168.1.101"
ip-allow-1: "192.168.1.105"
ip-allow-2: "192.168.1.106"
Функции
Встроенные функции для обработки текста:
name: # оборачивает значение в кавычки
name: # преобразует строку в нижний регистр
name: # преобразует строку в верхний регистр
name: # удаляет пробелы по краям строки
name: # удаляет все указанные символы (/) с обоих концов строки
name: # удаляет указанный префикс (/) в начале строки
name: # удаляет указанный суффикс (/) в конце строки
name: # форматирует строку по шаблону (объединяет значения разных типов, например, string и int)
name: # устанавливает значение по умолчанию, если переменная пуста
name: # прерывает установку с ошибкой, если значение не задано
Замена текста с помощью функции replace.
Содержимое в файле Values:
dbHost: "prod-db-01.internal"
connectionString: "postgresql://user:password@{HOST}:5432/mydb"
Заменяем заглушку {HOST} на значение из dbHost в манифесте:
url: Заполнение содержимого с помощью функции toYaml:
Содержимое в файле Values:
env:
DN_NAME: test
DN_USERNAME: admin
Заполняем содержимое:
env:
Вывод:
env:
DN_NAME: test
DN_USERNAME: admin
Условия
Условия для проверки значений:
# Проверка на присутствия значений в переменных
enablePersistence: true
enableFilesystem: true
enablePersistence: false
enableFilesystem: false
# Сокращенный формат (?:) для проверки булевого значения
# Если переменная содержит false или значение отсутствует, вернет второе значение
# Проверка на равенство и неравенство
# Проверка нескольких условий (и/или)
# Отрицание
# Арфеметические условия
# Больше
# Больше или равно
# Меньше
# Меньше или равно
# Проверка строковых значения
# Присутствие значения в списке
# Этот блок выполнится, если "example" есть в массиве items
Циклы
Циклы для перебора значений и заполнения массивов:
volumes:
- volumeName:
volumes:
- volumeName:
# Используем индекс и значение
volumes:
- :
Пример заполнения набора манифестов:
kind: Service
apiVersion: v1
metadata:
name: egressgateway-svc-{{ $task.name }}
labels:
app: egress-gateway
spec:
type: ClusterIP
selector:
app: egress-gateway
ports:
- name: tcp-{{ $task.port }}
protocol: TCP
port:
targetPort:
Define
Используются как как функции, позволяя не дублировать код и поддерживают глобальную область видимости. Они объявляются в любом манифесте из директории templates или в специальном файле шаблона (например, _helpers.tpl).
Переменные в файле Values:
app:
image: app
tag: "v1.2.3"
Объявляем define:
Используем в любом манифесте:
spec:
containers:
- name: web
image:
Helper
_helper - это вспомогательный файл шаблона, который можно использовать повторно в разных манифестах одного чарта. Любой файл, имя которого начинается с нижнего подчеркивания не превращаются в Kubernetes-манифесты, из их содержимого сохраняется шаблоны define, но результат их выполнения не отправляются в кластер.
:
:
Пример использования:
resources:
# Опредиление ресурсов вручную с принудительным добавлением кавычек
requests:
cpu:
memory:
limits:
cpu:
memory:
# Подставить все ресурсы в формате YAML и добавить 12 пробелов для каждой строки
# Использовать шаблон, котоырй гарантирует пропуск пустых значений
Helmfile
Helmfile - это декларативный инструмент для одновременного управления несколькими Helm-чартами с помощью одного helmfile.yaml, котоырй позволяет параметризовать чарты для разных сред (dev, test, prod), определять порядок развертывания и автоматизировать процесс установки/обновления приложений (как docker-compose для Helm).
| &&
Пример для установки стека Prometheus:
# Helm репозитории, откуда будет скачивать чарты
# Заменяет команды: helm repo add prometheus-community https://prometheus-community.github.io/helm-charts и helm repo update
repositories:
- name: prometheus-community
url: https://prometheus-community.github.io/helm-charts
- name: headlamp
url: ttps://kubernetes-sigs.github.io/headlamp
# Стендозависимые параметры
environments:
dev:
values:
- namespace: "dev"
path: "env/dev/values.yaml"
test:
values:
- namespace: "test"
path: "env/test/values.yaml"
# Список чартов для установки
releases:
- name: prometheus-stack
# <название репозитория>/<название чарта>
chart: prometheus-community/kube-prometheus-stack
# Локальный путь к чарту
# chart: charts/prometheus-stack
namespace:
createNamespace: true
# Локальный путь к стендозависимым переменным
values:
-
# Игнорировать ошибку, если файл с переменными не найден
missingFileHandler: Warn
# Приоритет (запускать после указанных чартов)
needs:
- headlamp
- name: headlamp
chart: headlamp/headlamp
namespace: kube-system
helmfile init проверяет и устанавливает/обновляет зависимости, необходимые для работы Helmfile (helm и плагины)
helmfile deps добавление и обновление указанных репозиториев
helmfile diff отобразить разницу для всех charts c состоянием кластера используя плагин helm diff
helmfile apply применить конфигурацию (синхронизировать состояние кластера с файлом)
helmfile -e dev apply применить конфигурацию для окружения dev
helmfile -l name=kube-prometheus-stack sync --include-needs запустить только указанный чарт с учетом зависимых чартов
GitHub API
$user = "Lifailon"
$repository = "ReverseProxyNET"
Invoke-RestMethod https://api.github.com/users/$($user) получаем информацию о пользователе
Invoke-RestMethod https://api.github.com/users/$($user)/repos получаем список последних (актуальные коммиты) 30 репозиториев указанного пользователя
Invoke-RestMethod https://api.github.com/users/$($user)/repos?per_page=100 получаем список последних (актуальные коммиты) 100 репозиториев указанного пользователя
Invoke-RestMethod https://api.github.com/repos/$($user)/$($repository)/contents получаем содержимое корневой директории репозитория
Invoke-RestMethod https://api.github.com/repos/$($user)/$($repository)/contents/source/rpnet.cs получаем содержимое файла в формате Base64
$commits = Invoke-RestMethod https://api.github.com/repos/$($user)/$($repository)/commits получаем список коммитов
$commits[0].commit.message читаем комментарий последнего коммита
$commits[0].commit.committer.date получаем дату последнего коммита
Invoke-RestMethod https://api.github.com/repos/$($user)/$($repository)/commits/$($commits[0].sha) получаем подробную информацию изменений о последнем коммите в репозитории
$releases_latest = Invoke-RestMethod "https://api.github.com/repos/$($user)/$($repository)/releases/latest" получаем информацию о последнем релизе в репозитории
$releases_latest.assets.name список приложенных файлов последнего релиза
$releases_latest.assets.browser_download_url получаем список url для загрузки файлов
$($releases_latest.assets | Where-Object name -like "*win*x64*exe*").browser_download_url фильтруем по ОС и разрядности
$(Invoke-RestMethod -Uri "https://api.github.com/repos/Lifailon/epic-games-radar/commits?path=api/giveaway/index.json")[0].commit.author.date узнать дату последнего обновления файла в репозитории
$issues = Invoke-RestMethod https://api.github.com/repos/LibreHardwareMonitor/LibreHardwareMonitor/issues?per_page=500 получаем список открытых проблем в репозитории (получаем максимум 100 последних, по умолчанию забираем последние 30 issues)
$issue_number = $($issues | Where-Object title -match "PowerShell").number получаем номер issue, в заголовке которого есть слово “PowerShell”
Invoke-RestMethod https://api.github.com/repos/LibreHardwareMonitor/LibreHardwareMonitor/issues/$($issue_number)/comments отобразить список комментарием указанного issues
Invoke-RestMethod https://api.github.com/repos/LibreHardwareMonitor/LibreHardwareMonitor/languages получаем список языков программирования, используемых в репозитории
Invoke-RestMethod https://api.github.com/repos/LibreHardwareMonitor/LibreHardwareMonitor/pulls получаем список всех pull requests в репозитории
Invoke-RestMethod https://api.github.com/repos/LibreHardwareMonitor/LibreHardwareMonitor/forks получаем список форков (forks)
Invoke-RestMethod https://api.github.com/repos/LibreHardwareMonitor/LibreHardwareMonitor/stargazers?per_page=4000 получаем список пользователей, которые поставили звезды репозиторию
Invoke-RestMethod https://api.github.com/repos/LibreHardwareMonitor/LibreHardwareMonitor/subscribers получаем список подписчиков (watchers) репозитория
GitHub Actions
Docker Build and Push
Сборка Docker образа из указанного коммита и публикация указанной версии на Docker Hub.
name: Docker build and push from specified commit
on:
workflow_dispatch:
inputs:
# Принимает два строковых параметра
Commit:
description: "Commit for git checkout"
required: true
default: ""
Version:
description: "Version for docker tag"
required: true
default: ""
jobs:
build:
name: Docker build on ubuntu-latest
runs-on: ubuntu-latest
env:
REPO_NAME: 'lazyjournal'
steps:
# Клонируем репозиторий (ветку main и историю всех комиттов)
- name: Checkout repository (main branch and all commits)
uses: actions/checkout@v6
with:
fetch-depth: 0
ref: main
# Переключаем состояние истории репозитория на указанный коммит
- name: Checkout the specified commit
run: git checkout ${{ github.event.inputs.Commit }}
# Авторизуемся
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
# Предварительно создаем секреты в https://github.com/<userName>/<repoName>/settings/secrets/actions
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
# Устанавливаем плагин buildx для мультиархитектурной сборки (amd64 и arm64)
- name: Install Docker Buildx
uses: docker/setup-buildx-action@v3
with:
driver: docker-container
install: true
# Собираем и публикуем одной командой (используем тег из параметра)
- name: Build and push Docker images for Linux on amd64 and arm64
run: |
version=${{ github.event.inputs.Version }}
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t ${{ secrets.DOCKER_USERNAME }}/${{ env.REPO_NAME }}:$version \
--push .
Dockerfile Linters Check
Проверка Dockerfile на базовые линтеры и с помощью инструмента Hadolint.
name: Dockerfile linters check
on:
workflow_dispatch:
# Принимает два булевых параметра
inputs:
Lint:
description: 'Dockerfile basic linters check'
default: false
type: boolean
Hadolint:
description: 'Dockerfile hadolint check'
default: false
type: boolean
jobs:
build:
name: Docker build on ubuntu-latest
runs-on: ubuntu-latest
steps:
# Клонируем репозиторий (ветку main и последний коммит)
- name: Checkout repository (main branch and all commits)
id: dockerPull
uses: actions/checkout@v6
with:
fetch-depth: 1
ref: main
- name: Dockerfile basic linters check
# Проверка условия перед выполнения шага
if: ${{ github.event.inputs.Lint == 'true' }}
# Определяем индивидуальный идентификатор для извлечения данных из шага
id: lint
run: docker build --check --build-arg "BUILDKIT_DOCKERFILE_CHECK=experimental=all" .
- name: Dockerfile hadolint check
if: ${{ github.event.inputs.Hadolint == 'true' }}
id: hadolint
run: docker run --rm -i hadolint/hadolint:latest < Dockerfil
Telegram Notification
Отправка уведомлений в Telegram с помощью Telegram Actions.
- name: Send report to Telegram
uses: appleboy/telegram-action@master
with:
token: ${{ secrets.TELEGRAM_API_TOKEN }}
to: ${{ secrets.TELEGRAM_CHANNEL_ID }}
debug: true
format: markdown
message: |
🔔 **Action**: Docker linters check
📁 **Repository**: ${{ github.repository }}
👤 **User**: ${{ github.actor }}
${{ steps.lint.outcome == 'failure' && '❌' || '✅' }} **Dockerfile basic linters check**: ${{ steps.lint.outcome }}
${{ steps.hadolint.outcome == 'failure' && '❌' || '✅' }} **Dockerfile hadolint check**: ${{ steps.hadolint.outcome }}
Или отправка с помощью Shoutrrr
- name: Send report to Telegram using Shoutrrr
uses: containrrr/shoutrrr-action@v1
with:
# Секрет в формате: telegram://123456789:ABC...@telegram?channels=123456789
url: ${{ secrets.SHOUTRRR_URL }}
title: "🔔 Action from ${{ github.repository }}"
message: |
Commit: ${{ github.sha }}
Compare: ${{ github.event.compare }}
Author: ${{ github.actor }}
AI Issue Analysis
Анализ Issues с использованием AI Inference и автоматическим ответом в комментариях.
name: AI Issue Analysis
on:
issues:
types:
issue_comment:
types:
run-name: "Issue #${{ github.event.issue.number }}: ${{ github.event.issue.title }}"
jobs:
issue_analysis:
permissions:
issues: write
models: read
runs-on: ubuntu-latest
steps:
- name: Checkout repository (main branch and 1 last commits)
uses: actions/checkout@v6
with:
fetch-depth: 1
ref: main
# Отправляем оповещение в Telegram с темой и содержимым проблемы
# Срабатываем на открытие, закрытие и комментарии в задаче
- name: Send message to Telegram
uses: appleboy/telegram-action@master
with:
token: ${{ secrets.TELEGRAM_API_TOKEN }}
to: ${{ secrets.TELEGRAM_CHANNEL_ID }}
debug: true
format: markdown
message: |
🔔 **Action**: ${{ github.event_name }} ${{ github.event.action }} [#${{ github.event.issue.number }}](${{ github.event.comment.html_url || github.event.issue.html_url }})
📁 **Repository**: ${{ github.repository }}
👤 **From user**: ${{ github.actor }}
📌 **Title**: ${{ github.event.issue.title }}
💬 **Description**:
${{ github.event.comment.body || github.event.issue.body }}
# Генерируем отчет (только при открытие новой проблемы)
- name: Generate report using AI
if: ${{ github.event_name == 'issues' && github.event.action == 'opened' }}
id: ai_query
uses: actions/ai-inference@v2
with:
token: ${{ secrets.GITHUB_TOKEN }}
endpoint: https://models.github.ai/inference
model: gpt-4.1
max-tokens: 1024
system-prompt: |
Ты консультант разработчика.
Твоя задача анализировать проблемы (issues) на GitHub и предлагать решения.
Твои ответы должны быть краткими и на английском языке.
prompt: |
Title: ${{ github.event.issue.title }}
Description: ${{ github.event.issue.body }}
# Постим комментарий от AI в ответ на Issue
- name: Post comment from AI
if: ${{ github.event_name == 'issues' && github.event.action == 'opened' && steps.ai_query.outputs.response != '' }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ISSUE_NUMBER: ${{ github.event.issue.number }}
AI_RESPONSE: ${{ steps.ai_query.outputs.response }}
run: |
gh pr comment "$PR_NUMBER" --body "### AI Issue Analysis
$AI_RESPONSE"
AI README Translate
Перевод README файла на англйский язык с помощью AI.
name: AI Translate
on:
workflow_dispatch:
inputs:
Input:
description: Input README file
required: true
default: README_RU.md
Output:
description: Output README file
required: true
default: README.md
Language:
description: Target language for translation
required: true
default: English
jobs:
translate:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
models: read
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Read README
id: read_file
run: |
INPUT_README=$(cat ${{ github.event.inputs.Input }})
{
echo "readme<<EOF"
echo "$INPUT_README"
echo "EOF"
} >> "$GITHUB_OUTPUT"
- name: AI-powered translation
id: ai_translate
uses: actions/ai-inference@v2
with:
token: ${{ secrets.GITHUB_TOKEN }}
endpoint: https://models.github.ai/inference
model: gpt-4.1
max-tokens: 4096
system-prompt: |
You are a professional translator in the automated GitHub Actions CI system.
Translate the file submitted in the README message into ${{ github.event.inputs.Language }}.
You should not provide answers or change the original text structure.
Preserve the original Markdown formatting and links.
prompt: ${{ steps.read_file.outputs.readme }}
- name: Write README
env:
README_TRANSLATE: ${{ steps.ai_translate.outputs.response }}
run: |
cat <<'EOF' > ${{ github.event.inputs.Output }}
${{ env.README_TRANSLATE }}
EOF
- name: Create PR
uses: peter-evans/create-pull-request@v8
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: "actions: readme translation"
branch: "docs/readme"
title: "README translation on ${{ github.event.inputs.Language }}"
body: "AI-powered translation of a README file"
add-paths: |
${{ github.event.inputs.Output }}
Go Build and Testing
Процесс тестирования и сборки Go приложений с выгрузкой артифактов.
name: CI (Build and Testing)
on:
workflow_dispatch:
inputs:
# Параметр выпадающего списка для выбора
# Список доступных дистрибутивов на публичных сборщиках: https://github.com/actions/runner-images
Distro:
description: 'Select runner image'
required: true
default: 'ubuntu-24.04'
type: choice
options:
- 'ubuntu-22.04'
- 'ubuntu-24.04'
- 'macos-26'
- 'macos-15'
- 'windows-2025'
- 'windows-2022'
# Обновление зависимостей
Update:
description: 'Update dependencies'
default: false
type: boolean
# Статическая проверка кода
Linters:
description: 'Go linters check'
default: false
type: boolean
# Запуск unit тестов
Test:
description: 'Go unit testing'
default: false
type: boolean
# Сборка
Binary:
description: 'Build binary and deb packages'
default: false
type: boolean
# Определяем индивидуальное название для каждой сборки
run-name: "Build #${{ github.run_number }} on ${{ github.event.inputs.Distro }}"
jobs:
test:
name: Testing on ${{ github.event.inputs.Distro }}
runs-on: ${{ github.event.inputs.Distro }}
# Объявляем переменные окружения, которые будут использоваться в процессе сборки
env:
APP_NAME: 'app'
APP_VERSION: 'latest'
COVERAGE: 'n/a'
steps:
- name: Checkout repository (main branch and 1 last commits)
uses: actions/checkout@v6
with:
fetch-depth: 1
ref: main
# Установка Go указанной версии
- name: Install Go
uses: actions/setup-go@v5
with:
go-version: 1.25
# Форматирование, статический анализ, установка зависимостей и проверка компиляции
- name: Install dependencies
run: |
go fmt ./...
go vet ./...
go get ./...
go mod tidy
go mod verify
go build -v ./...
# Извлекаем версию приложения и сохраняем ее в переменные окружения для использования в других шагах
- name: Get app version in gh env for build
run: |
version=$(go run main.go -v)
echo "APP_VERSION=$version" >> $GITHUB_ENV
# Обновление зависимостей в go.mod
- name: Update dependencies
if: ${{ github.event.inputs.Update == 'true' }}
run: go get -u ./...
# Устанавливаем пакет golangci-lint и запуск анализа кода
- name: Golangci linters check
if: ${{ github.event.inputs.Linters == 'true' }}
# Исключаем падение всего шага при ошибке (last exit code != 0)
continue-on-error: true
run: |
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
golangci-lint run -v ./main.go
# Запуск unit тестов и записываем результат в лог файл
- name: Unit testing
if: ${{ github.event.inputs.Test == 'true' }}
# Ограничиваем время выполнения
timeout-minutes: 10
continue-on-error: true
run: sudo env "PATH=$PATH" go test -v -cover | tee test.log
# Извлекаем результат покрытия кода и публикуем на страницу сборки в формате Markdown
- name: Unit testing
if: ${{ github.event.inputs.Test == 'true' }}
continue-on-error: true
run: |
COVERAGE=$(cat test.log | tail -n 2 | head -n 1 | sed "s/coverage: //" | sed -E "s/of.+//g")
echo "## Test results" >> $GITHUB_STEP_SUMMARY
echo -e "Version: $APP_VERSION" >> $GITHUB_STEP_SUMMARY
echo -e "Coverage: $COVERAGE" >> $GITHUB_STEP_SUMMARY
# Собираем приложение для разных ОС и архитектур
- name: Build binaries
if: ${{ github.event.inputs.Binary == 'true' }}
run: |
mkdir -p bin
architectures=("amd64" "arm64")
oss=("linux" "darwin" "openbsd" "freebsd" "windows")
for arch in "${architectures[@]}"; do
for os in "${oss[@]}"; do
binName="bin/$APP_NAME-$APP_VERSION-$os-$arch"
if [[ "$os" == "windows" ]]; then
binName="$binName.exe"
fi
CGO_ENABLED=0 GOOS=$os GOARCH=$arch go build -o "$binName"
done
done
ls -lh bin
# Формируем названия архива для выгрузки артефактов
echo "ARTIFACT_NAME=$APP_NAME-$APP_VERSION" >> $GITHUB_ENV
# Выгружаем все файлы из директории bin в артефакты GitHub
- name: Upload binaries
if: ${{ github.event.inputs.Binary == 'true' }}
uses: actions/upload-artifact@v4
with:
name: ${{ env.ARTIFACT_NAME }}
path: bin/
env:
ARTIFACT_NAME: ${{ env.ARTIFACT_NAME }}
Ubuntu PPA Repository
Создание и подготовка PPA (Personal Package Archives) репозитория для публикации deb пакетов.
Генерируем ключ:
gpg --gen-key
Получить путь к pubring.kbx, а также спислк всех ключей с их отпечаток и сроком действия:
gpg --list-secret-keys --keyid-format=long
Получить приватный ключ по email или отпечатку:
gpg --armor --export-secret-keys lifailon@main.com \
Отправляем ключ на сервер ключей Ubuntu:
gpg --keyserver keyserver.ubuntu.com --send-keys <ОТПЕЧАТОК>
Поиск ключа на сервере ключей: https://keyserver.ubuntu.com
Импортируем ключ: https://launchpad.net/~lifailon/+editpgpkeys
Выбираем: Import an OpenPGP key
Вставляем отпечаток в Fingerprint и нажимаем Import Key
Дожидаемся сообщение на почту, указанную в ключе
Расшифровать содержимое pgp сообщения в формате BEGIN PGP MESSAGE:
gpg --decrypt pgp.txt
Переходим по полученной ссылке после расшифровки и подтверждаем добавление ключа.
Загружаем Ubuntu Codes of Conduct последней версии: https://launchpad.net/codeofconduct/2.0
Подписываем кодекс поведения:
gpg -u A60D863D --clearsign UbuntuCodeofConduct-2.0.txt
cat UbuntuCodeofConduct-2.0.txt.asc
Переходим по ссылке: https://launchpad.net/codeofconduct
Нажимаем Sign it! и вставляем содержимое UbuntuCodeofConduct-2.0.txt.asc
После этих действий создаем PPA репозиторий.
Идем в настройки репозитория: Использование данного подхода может сэкономить много времени на подготовке к сборке и публикации Go приложения в PPA. API запрос для получения версии и создание динамического бейджа в Shields c помощью json запрос. Пример создания своего Action для подключения в Workflow других проектах на примере объединения результатов unit-тестирования в Go приложении с использованием gocovmerge. Использование в Workflow: Создаем действие для реакции на событие: Отправляем запрос для вызова события: Вызываем событие из одного workflow для запуска другого workflow с помощью Repository Dispatch: act - пользволяет запускать действия GitHub Actions локально (используется в Gitea). Apache Groovy - скриптовый язык для Базовый синтаксис: Примеры использования метода Загрузка конфигурации из Jenkins: Добавляем логин и Сценарий проверяет доступность удаленной машины, подключается к ней по ssh, выполняет скрипт hwstat для сбора метрик и выгружает json отчет в артефакты: Передача файла через параметр и чтение его содержимого: Останавливает выполнение Любой код Groovy возможно запустить и проверить через Пример Пример HTTP запроса и чтения Пример получения списка доступных версий в выбранном репозитории и содержимого файлов для выбранного релиза, а также загрузка указанного файла: Интеграция HashiCorp Vault в Jenkins Pipeline через Команда (скрипт) для загрузки Получение секретов (на примере содержимого Для отправки на почту и настроить SMTP сервер в настройках Jenkins ( SMTP server: Настройка логирования в System Log: SimpleTemplateEngine - простой движок шаблонов, который встроен в язык Groovy. Он принимает текст, в котором заменяет значения на содержимое переданных переменных. Шаблон для подстановки значений: Запускаем рендеринг (шаблонизируем конфигурацию): Пример условий и циклов: Переменные для заполнения: YamlSlurper - преобразует Отправка данных: Конфигурация настроек Ansible в файле Настройка списка групп хостов в файле Локальное использование: Использование в Ansible для обновления файла Bolt - это инструмент оркестровки, который выполняет заданную команду или группу команд на локальной рабочей станции, а также напрямую подключается к удаленным целям с помощью SSH или WinRM, что не требует установки агентов. Docs: https://www.puppet.com/docs/bolt/latest/getting_started_with_bolt.html Sake - это командный раннер для локальных и удаленных хостов. Вы определяете серверы и задачи в файле Пример конфигурации: Создание экспортера на примере получения метрик температуры всех дисков из CrystalDiskInfo с помощью PowerShell и отправки в Prometheus через PushGateway. Формат метрик: Типы данных: Строки метрик содержат имя, лейблы (в фигурных скобках) и значение. Переменные для фильтрации запроса: Функции Установка aws cli: Настройка профиля по умолчанию: Переменные окружения для подключения к облаку или localstack: Создание s3 хранилища: Запускаем localstack вместе с fluent-bit для пересылки логов: Создаем конфигурацию Подключаем драйвер Создание группы, потока и запись логов в CloudWatch: Vercel - это бессерверная платформа для публикации веб-приложений и Развертвывание Запускаем контейнере Traefik в стеке с Jaeger для анализа трассировки трафика и DNS сервером: Конфигурация настроек в файле Настройка дополнительных конфигураций из провайдера Настройка промежуточной переадресации (между Traefik и веб-приложением) в файле Настройка маршрутизации в файле Запускаем HAProxy в контейнере Docker: Конфигурация с проверкой тела ответа: VRRP (Virtual Router Redundancy Protocol) - сетевой протокол, предназначенный для увеличения доступности маршрутизаторов, выполняющих роль шлюза GlusterFS - это распределенная и масштабируемая файловая система, которая объединяет хранилища с разных серверов в единое виртуальное сетевое хранилище, обеспечивая отказоустойчивость и высокую производительность без необходимости отдельного сервера для метаданных. Работает поверх обычных файловых систем (например,
https://launchpad.net/~
Добавляем amd64 и arm64 в Processors для мультиархитектурной сборки Ubuntu Build and Push
name: Build deb package and push to PPA
on:
workflow_dispatch:
inputs:
# Запустить только установку
Install:
description: 'Check install only'
default: false
type: boolean
# Дистрибутив на котором будет производиться сборка
# Это влияет на название версии дистрибутива Ubuntu в файле changelog по умолчанию
Runner:
description: 'Select runner image'
required: true
default: 'ubuntu-22.04'
type: choice
options:
- 'ubuntu-24.04' # noble
- 'ubuntu-22.04' # jammy
# Название дистрибутива Ubuntu для обновления в файле changelog
Distro:
description: 'Ubuntu distro name in changelog'
required: true
default: 'jammy'
type: choice
options:
- 'resolute' # 26.04
- 'questing' # 25.10
- 'noble' # 24.04
- 'jammy' # 22.04
- 'focal' # 20.04
- 'bionic' # 18.04
- 'xenial' # 16.04
- 'trusty' # 14.04
# Обновить версию в changelog (для пересборок)
Version:
description: 'Version for build'
required: false
default: '' # 0.8.4 -> 0.8.4.1 -> 0.8.4.2
# Публикация в репозитория PPA
Push:
description: 'Push to PPA'
default: false
type: boolean
jobs:
test:
name: Build deb package and push to PPA
runs-on: ${{ github.event.inputs.Runner }}
steps:
- name: Checkout repository (main branch and 1 last commits)
uses: actions/checkout@v6
with:
fetch-depth: 1
ref: main
# Шаг проверки установки пакета
- name: Check install package from PPA
if: ${{ github.event.inputs.Install == 'true' }}
run: |
sudo add-apt-repository -y ppa:lifailon/lazyjournal
sudo apt update
apt-cache policy lazyjournal
sudo apt install -y lazyjournal
lazyjournal -v
sudo apt remove -y lazyjournal
# Устанавливаем Go
- name: Install Go
if: ${{ github.event.inputs.Install != 'true' }}
run: |
sudo apt-get update
sudo apt-get install -y golang-1.23-go
/usr/lib/go-1.23/bin/go version
# Устанавливаем зависимости для сборки
- name: Install dependencies for build deb package
if: ${{ github.event.inputs.Install != 'true' }}
run: |
sudo apt-get install -y \
devscripts \
debhelper \
dh-golang \
dh-make-golang \
dput \
golang-golang-x-text-dev \
golang-gopkg-yaml.v3-dev
# Импортируем GPG ключ из секретов в систему
- name: Import GPG key
if: ${{ github.event.inputs.Install != 'true' }}
uses: crazy-max/ghaction-import-gpg@v6
with:
gpg_private_key: ${{ secrets.PPA_GPG_PRIVATE_KEY }}
- name: Build deb package and publish to Launchpad PPA
if: ${{ github.event.inputs.Install != 'true' }}
env:
DEB_FULLNAME: "lifailon"
DEB_EMAIL: ${{ secrets.PPA_EMAIL }}
DISTRO: ${{ github.event.inputs.Distro }}
APP_VERSION: ${{ github.event.inputs.Version }}
run: |
# Объявляем переменные для локальной сборки
export PATH="/usr/lib/go-1.23/bin:$PATH"
# Не загружать зависимости
export GOTOOLCHAIN=local
export DEBFULLNAME="$DEB_FULLNAME"
# Email из gpg ключа
export DEBEMAIL="$DEB_EMAIL"
# Генерируем Debia шаблоны (структуру файлов и каталогов) для Go приложения
echo -e "\n\033[33m>>> Debian template generation\033[0m\n"
dh-make-golang make github.com/Lifailon/lazyjournal
cd lazyjournal
# Загружаем все зависимости (используемые пакеты) и обновляем архив для сборки без интернета на серверах Ubuntu
echo -e "\n\033[33m>>> Download dependencies for offline build\033[0m\n"
go mod vendor
VERSION=$(ls ../*.orig.tar.gz | sed -E 's/.*_([0-9.]+)\.orig\.tar\.gz/\1/')
rm ../*.orig.tar.gz
tar --exclude-vcs -C .. -czf ../lazyjournal_$VERSION.orig.tar.gz lazyjournal
sed -i '1a export GOFLAGS=-mod=vendor' debian/rules
# Обновляем версию Go на актуальную во избежание ошибок при сборке
echo -e "\n\033[33m>>> Update go version\033[0m\n"
sed -i 's/golang-any/golang-1.23-go/' debian/control
# Экспортируем путь к Go в основной Makefile для сборки
sed -i '1a export PATH := /usr/lib/go-1.23/bin:$(PATH)' debian/rules
sed -i '1a export GOTOOLCHAIN := local' debian/rules
# Обновляем секцию TODO в файл debian control для избежания ошибок
echo -e "\n\033[33m>>> Update debian control\033[0m\n"
sed -i 's/Section: TODO/Section: utils/' debian/control
# Используем мультиархитектурную сборку
sed -i -E 's/Architecture:.+/Architecture: any/' debian/control
# Пропускаем встроенные тесты и dwz проверки
echo -e "\n\033[33m>>> Skip go test and dwz in build\033[0m\n"
# export DEB_BUILD_OPTIONS=nocheck
printf "\noverride_dh_auto_test:\n\t:\n" >> debian/rules
printf "\noverride_dh_dwz:\n\t:\n" >> debian/rules
echo -e "\n\033[33m>>> Rules\033[0m\n"
cat debian/rules
# Обновляем название дистрибутива
echo -e "\n\033[33m>>> Update distro name in changelog\033[0m\n"
sed -i "1s/)[[:space:]]\+[^;]\+;/) $DISTRO;/" debian/changelog
sed -i 's/(Closes: TODO)//' debian/changelog
# Обновляем версию (если значение определено в параметре)
if [ -n "$APP_VERSION" ]; then
echo -e "\n\033[33m>>> Update version in changelog\033[0m\n"
VERSION=$(cat debian/changelog | head -n 1 | sed -E "s/.+\(//" | sed -E "s/\).+//" | sed -E "s/-[0-9]+//")
sed -i -E "s/$VERSION-/$APP_VERSION-/" debian/changelog
mv ../lazyjournal_$VERSION.orig.tar.gz ../lazyjournal_$APP_VERSION.orig.tar.gz
echo -e "\n\033[33m>>> Changelog\033[0m\n"
cat debian/changelog
fi
# Проверяем сборку (эта команда выполняется на серверах Ubuntu перед публикацией)
echo -e "\n\033[33m>>> Check build\033[0m\n"
dpkg-buildpackage -us -uc -b
# Собираем файл changes
echo -e "\n\033[33m>>> Build changes\033[0m\n"
rm -f ../*.upload ../*.changes
debuild -S -sa -k"$DEBEMAIL"
cd ..
ls *.changes
# Публикуем пакет в репозиторий PPA
- name: Push to PPA
if: ${{ github.event.inputs.Install != 'true' && github.event.inputs.Push == 'true' }}
run: |
dput ppa:lifailon/lazyjournal *.changes
apiUrl="https://api.launchpad.net/1.0/~lifailon/+archive/ubuntu/lazyjournal?ws.op=getPublishedSources&distro_series=https://api.launchpad.net/1.0/ubuntu/jammy&status=Published"
apiQuery="entries[0].source_package_version"
|
# Кодируем URL
ppaUrl=
# Формируем динамический json запрос в Shields для получения бейджа с версией
Composite Action
name: 'Go Coverage Merge Action'
description: 'Merges multiple Go coverage profiles using gocovmerge'
author: 'Lifailon'
inputs:
go-version:
description: 'Go version'
required: false
default: 'stable'
input-pattern:
description: 'Glob pattern for coverage files'
required: false
default: '*.out'
output-coverage:
description: 'Output merged filename'
required: false
default: 'summary.out'
runs:
using: composite
steps:
- name: Install Go
uses: actions/setup-go@v6
with:
go-version: ${{ inputs.go-version }}
- name: Install gocovmerge
shell: bash
run: go install github.com/wadey/gocovmerge@latest
- name: Merge files
shell: bash
run: |
files=$(ls ${{ inputs.input-pattern }})
if [ -z "$files" ]; then
echo "No coverage files found for pattern: ${{ inputs.input-pattern }}"
exit 1
fi
gocovmerge $files > "${{ inputs.output-coverage }}"
- name: Summary
shell: bash
run: go tool cover -func "${{ inputs.output-coverage }}"
- name: Go coverage merge
uses: Lifailon/gocovmerge-action@v1
with:
pattern: 'coverage/*.out'
output: 'coverage/total.out'
Repository Dispatch
name: Webhook Payload
on:
repository_dispatch:
# Workflow запустится только если event_type совпадает с одним из списка
types:
jobs:
payload:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Check params
run: |
echo "App version: ${{ github.event.client_payload.app_version }}"
echo "Run tests: ${{ github.event.client_payload.run_tests }}"
userName=Lifailon
repoName=lazyjournal
token=ghp_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
jobs:
Dispatch:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Repository Dispatch
uses: peter-evans/repository-dispatch@v4
with:
event-type: action_run
# Other repository
# repository: username/my-repo
# token: ${{ secrets.PAT }}
Actions API
$userName = "Lifailon"
$repoName = "lazyjournal"
# Получить количество всех рабочих процессов
.total_count
# Подробная информации о запускаемых рабочих процессах
.workflows
# Получить идентификатор первого workflow
$workflowId = .workflows[0].id
# Подробная информация о последней сборке
.workflow_runs
# Получить идентификатор последнего запуска указанного workflow
$lastRunId = .workflow_runs.id[0]
# Подробная информация для всех шагов выполнения (время работы и статус выполнения из conclusion)
.jobs.steps
# Получить идентификатор последнего jobs в конкретной сборке
$lastJobsId = .jobs[-1].id
# Отобразить логи выполнения указанного задания
$url = "https://api.github.com/repos/$userName/$repoName/actions/jobs/$lastJobsId/logs"
$headers =
Invoke-RestMethod -Uri $url -Headers $headers
Actions locally
version=
act --list список доступных действий, указаных в файлах .github/workflows
act -j build запуск указанного действия по Job ID (имя файла, не путать с названием Workflow)
act -n -j build пробный запуск (–dry-run), без выполнения команд, для отображения всех выполняемых jobs и steps
act -e event.json -W .github/workflows/build.yml -P ubuntu-24.04=catthehacker/ubuntu:act-latest запустить указанный файл workflow с переданным файлом переменных (предварительно определенных параметров) и указанным сборщиком
act -e event.json -W .github/workflows/build.yml -P ubuntu-24.04=catthehacker/ubuntu:act-latest --artifact-server-path $PWD/artifacts примонтировать рабочий каталог в контейнер для сохранения артефактов
act --secret-file .secrets
act -s DOCKER_HUB_USERNAME=username -s DOCKER_HUB_PASSWORD=password передать содержимое секретов
act push симуляция push-ивента (имитация коммита и запуск workflow, который реагирует на push)
act --reuse не удалять контейнер из успешно завершенных рабочих процессов для сохранения состояния между запусками (кэширование)
act --parallel запуск всех jobs одновременно или последовательно (–no-parallel, по умолчанию) Groovy
JVM, похожий на Java по стилю и синтаксису, но не требующий компиляции (как PowerShell в экосистеме .NET). Используется для описания в DSL (Domain-Specific Language) формате, например, процесса сборки Java-приложений в Gradle и Jenkins Pipeline.// Переменные
javaString = 'java'
javaString
println javaString
javaString.class // class java.lang.String
println 100.class // class java.lang.Integer
j = '${javaString}' // не принимает переменные в одинарных кавычках
groovyString = ""
bigGroovyString = """
"""
// java
// ${javaString}
// java
// 4
a = "a" // a
a + "123" // a123
a * 5 // aaaaa
// Массивы и списки
list =
list // 1
list // [1, 2]
range = "0123456789"
range // 12345
map =
map // true
server =
server.ip = "192.168.3.1"
server.port = 22
println(server) // [ip:192.168.3.1, port:22]
// Функции
println a+b
}
// 4
// Условия
if (x < 10) else if (x == 10) else
}
// 11 > 10
// Циклы
list.each
// 123
for (i in 0..5)
// 012345
for (int i = 0; i < 10; i++)
// 0123456789
i = 0
while (i < 3)
// 0
// 1
// 2
// Классы
def str = "start"
println str
println param
}
}
def main = new Main()
def array =
for (element in array)
// 1
// 2
// 3
// Обработка ошибок
def newList =
newList = 1
newList = 2
for (index in 0..1)
// 4
// 5
// 6
println str.
// Коллекция для синхронизации сохранения данных в потоках
def sharedList = Collections.
// Анонимная функция для обработки данных в потоке
def runTask =
def threads =
// Ждем завершения всех потоков
threads <<
threads <<
threads <<
threads*.
println "Результат: $sharedList"
// Функции строк
" text ". // удаляет пробелы в начале и конце => "text"
"ping". // заменяет буквы в строке => pong
"a,b,c". // разбивает строку по разделителю => ["a", "b", "c"]
"abc". // возвращает длину строки или размер списка (кол-во элементов) => 3
"abc". // переворачивает строку => "cba"
"abc". // проверяет наличие подстроки => true
"abc". // проверяет начало строки => true
"abc". // проверяет конец строки => true
"123". // проверяет, является ли строка числом => true
"abc". // проверяет соответствие регулярному выражения => true
"hello". // преобразует строку в верхний регистр => "HELLO"
"HELLO". // преобразует строку в нижний регистр => "hello"
// Функции массивов
. // объединяет элементы в строку => "a,b,c"
. // проверяет наличие элемента => true
. // суммирует элементы => 6
. // находит максимум => 3
. // находит минимум => 1
. // вычисляет среднее => 2
. // переворачивает список => [3, 2, 1]
. // сортирует список => [1, 2, 3]
. // удаляет дубли => [1, 2, 3]
.findAll // фильтрует элементы => [2, 3]
.collect // преобразует элементы => [2, 4, 6]
.collect // строки => числа => [1, 2]
def users =
users.collect
// ["Alex", "Jack"]
// Функции карт (map)
. // получает значение по ключу => 1
. // возвращает все ключи => ["a", "b"]
. // возвращает все значения => [1, 2]
. // проверяет наличие ключа => true
.findAll // фильтрует записи => ["b": 2]
.collect // преобразует => ["a-1", "b-2"]
. // добавляет новую пару ключ-значение => ["a": 1, "b": 2]
. // объединяет мапы => ["a": 1, "b": 2]
// Директории и файлы
new File("dir"). // создает директорию => boolean
new File("dir/subdir"). // создает все недостающие директории d genb => boolean
new File("dir"). // список имен файлов => String[]
new File("dir"). // возвращает список файлов в директории => File[]
new File("dir"). // удаляет директорию (рекурсивно) => boolean
new File("dir"). // проверяет, что это директория => boolean
new File("file.txt"). // создает пустой файл => boolean
new File("file.txt"). // удаляет файл => boolean
new File("file.txt"). // проверяет существование файла => boolean
new File("file.txt"). // проверяет, что это файл => boolean
new File("file.txt"). // возвращает размер файла в байтах => long
new File("file.txt"). // возвращает время последнего изменения => long (timestamp)
new File("file.txt"). // возвращает имя файла (без пути) => String
new File("file.txt"). // возвращает относительный путь => String
new File("file.txt"). // возвращает абсолютный путь => String
new File("file.txt").text // читает содержимое файла в строку
new File("file.txt"). // указать кодировку при чтение
new File("file.txt"). // читает файл как массив байтов => byte[]
new File("file.txt"). // читает файл построчно (получаем массив из строк) => List<String>
new File("file.txt").eachLine // обработать каждую строку
new File("file.txt"). // перезаписывает файл (если существует) => void
new File("file.txt"). // аналог write() => void
new File("file.txt").bytes = // записывает массив байтов => void
new File("file.txt") << "text" // добавляет текст в конец файла => void
(versions. + 1).. // 0019 + 1 = 0020 ("19".padLeft(4, '0') -> "0019")
Gradle
// Подключаем плагины для работы с Java
plugins
// Метаданные проекта
group = 'com.example'
version = '1.0.0'
// Настраиваем репозитории, откуда Gradle будет качать библиотеки
repositories
// Описание зависимостей
dependencies
// Настройка встроенной задачи (task) для тестирования
test
Jenkins
docker run -d --name=jenkins -p 8080:8080 -p 50000:50000 --restart=unless-stopped -v jenkins_home:/var/jenkins_home jenkins/jenkins:latest
ls /var/lib/docker/volumes/jenkins_home/_data/jobs директория хранящая историю сборок в хостовой системе
docker exec -it jenkins /bin/bash подключиться к контейнеру
cat /var/jenkins_home/secrets/initialAdminPassword получить токен инициализации
# Или ссылаться на локальный контейнер сервера по имени
# --link jenkins:jenkins
# -e JENKINS_URL=http://jenkins:8080
docker exec -u root -it jenkins-remote-agent-01 /bin/bash подключиться к slave агенту под root
apt-get update && apt-get install -y iputils-ping netcat-openbsd установить ping и nc на машину сборщика (slave)jenkinsVolumePath=$(docker inspect jenkins | jq -r .[].Mounts.[].Source) получить путь к директории Jenkins в хостовой системе
sudo tar -czf $HOME/jenkins-backup.tar.gz -C $jenkinsVolumePath . резервная копия всех файлов
(crontab -l ; echo "0 23 * * * sudo tar -czf /home/lifailon/jenkins-backup.tar.gz -C /var/lib/docker/volumes/jenkins_home/_data .") | crontab -
sudo tar -xzf $HOME/jenkins-backup.tar.gz -C /var/lib/docker/volumes/jenkins_home/_data восстановлениеwget http://127.0.0.1:8080/jnlpJars/jenkins-cli.jar -P $HOME/ скачать jenkins-cli (http://127.0.0.1:8080/manage/cli)
apt install openjdk-17-jre-headless установить java runtime
java -jar jenkins-cli.jar -auth lifailon:password -s http://127.0.0.1:8080 -webSocket help получить список команд
java -jar jenkins-cli.jar -auth lifailon:password -s http://127.0.0.1:8080 groovysh запустить консоль Groovy
java -jar jenkins-cli.jar -auth lifailon:password -s http://127.0.0.1:8080 install-plugin ssh-steps -deploy устанавливаем плагин SSH Pipeline Steps API
$username = "Lifailon"
$password = "password"
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $username,$password)))
$headers =
Invoke-RestMethod "http://192.168.3.101:8080/rssAll" -Headers $headers # RSS лента всех сборок и их статус в title
Invoke-RestMethod "http://192.168.3.101:8080/rssFailed" -Headers $headers # RSS лента всех неудачных сборок
.offline # проверить статус работы slave агента
$jobs = Invoke-RestMethod "http://192.168.3.101:8080/api/json/job" -Headers $headers
$jobs.jobs.name # список всех проектов
$jobName = "Update SSH authorized_keys"
$job = Invoke-RestMethod "http://192.168.3.101:8080/job/${jobName}/api/json" -Headers $headers
$job.builds # список всех сборок
$buildNumber = $job.lastUnsuccessfulBuild.number # последняя неуспешная сборка
Invoke-RestMethod "http://192.168.3.101:8080/job/${jobName}/${buildNumber}/consoleText" -Headers $headers # вывести лог указанной сборки
$lastCompletedBuild = $job.lastCompletedBuild.number # последняя успешная сборка
$crumb = .crumb # получаем временный токен доступа (crumb)
$headers["Jenkins-Crumb"] = $crumb # добавляем crumb в заголовки
$body = # добавляем crumb в тело запроса
Invoke-RestMethod "http://192.168.3.101:8080/job/${jobName}/${lastCompletedBuild}/rebuild" -Headers $headers -Method POST -Body $body # перезапустить сборку
Plugins
Плагин Описание Pipeline Nodes and Processes Плагин, который предоставляет доступ к интерпретаторам sh, bat, powershell и pwshPipeline Utility Steps Добавляет методы readJSON, writeJSON, readYaml, writeYaml, readTOML, writeTOM, untar, unzip, и другиеHTTP Request Простой REST API Client для отправки и обработки GET и POST запросов через метод httpRequest(url: url, httpMode: "GET")Credentials Binding Добавляет метод withCredentials для доступа к секретамHashiCorp Vault Автоматизирует процесс получения содержимого значений из HashCorp Vault с помощью метода withVaultAnsible Параметраризует запуск ansible-playbook (требуется установка на агенте) через метод ansiblePlaybookSSH Pipeline Steps Плагин для подключения к удаленным машинам через протокол ssh по ключу или паролюSSH Agent Плагин для подключения к удаленным машинам с использованием ssh-agent и CredentialsWorkspace Cleanup Плагин добавляет метод cleanWs() для удаления рабочей область сборки.Pipeline Stage View Визуализация шагов ( stages) в интерфейсе проекта с временем их выполненияRebuilder Позволяет перезапускать параметризованную сборку с предустановленными параметрами в выбранной сборке Schedule Build Позволяет запланировать сборку на указанный момент времени Webhook Trigger Принимает POST запросы на конечной точке /generic-webhook-trigger/invoke для извлечения значений и запуска PipelineJob Configuration History Сохраняет копию файла сборки в формате xml (который хранится на сервере) в истории для сверкиExport Job Parameters Добавляет кнопку Export Job Parameters для конвертации все параметров в декларативный синтаксис PipelineActive Choices Parameters Активные параметры, которые позволяют динамически обновлять содержимое параметров File Parameters Добавляет параметры для загрузки файлов Separator Parameter Параметр для визуального разделения набора параметров на странице сборки задания с поддержкой HTMLCustom Tools Позволяет загружать пакеты (исполняемые файлы) из Интернета с помощью предустановленного набора команд Copy Artifact Позволяет копировать артифакты из одной сборки в другую (например, из последней успешной copyArtifacts(projectName: jobName))ANSI Color Добавляет поддержку стандартных escape-последовательностей ANSI для покраски выводаEmail Extension Отправка сообщений на почту по протоколу SMTP из PipelineConfig File Provider Хранение конфигураци (например, settings.xml для Maven) в интерфейсе Jenkins и их шаблонизация c CredentialsAllure Создает отчеты Allure для автотестов в интерфейсе Pipeline с отправкой в TestOps SonarQube Scanner Интегрирует статический анализ кода с помощью метода withSonarQubeEnv Test Results Analyzer Показывает историю результатов сборки junit тестов в табличном древовидном видеEmbeddable Build Status Предоставляет настраиваемые значки Shields, который возвращает статус сборки Prometheus Metrics Предоставляет конечную точку /prometheus с метриками, которые используются для сбора данныхWeb Monitoring Добавляет конечную точку /monitoring для отображения графиков мониторинга в веб-интерфейсеCloudBees Disk Usage Отображает использование диска всеми заданиями во вкладке Manage-> Disk usage для анализа Credentials
withCredentials для извлечения и использования секретов:
configFile
def config =
SSH Agent
sshagent (credentials: )
SSH Steps and Artifacts
Private Key для авторизации по ssh: Manage (Settings) => Credentials => Global => Add credentials => Kind: SSH Username with private key// Глобальный массив для хранения данных подключения по ssh
def remote =
pipeline
Upload File Parameter
pipeline
Input Text and File
Pipeline и заставляет пользователя передать текстовый параметр и файл:pipeline
HttpURLConnection
Script Console (http://127.0.0.1:8080/manage/script)API запроса к репозиторию PowerShell на GitHub для получения последней версии и всех доступных версий:def url = new URL("https://api.github.com/repos/PowerShell/PowerShell/tags")
def connection = url.
connection.
connection.
def responseCode = connection.
if (responseCode == 200) else
connection.
httpRequest
json файла с помощью плагинов HTTP Request и Pipeline Utility Steps:pipeline
Active Choices Parameter
pipeline
Vault
REST API для получения содержимого секретов и использовая в последующих стадиях/этапах сборки:
def url = new URL("/")
def connection = url.
connection.
connection.
connection.
def response = new groovy.json.JsonSlurper().
def user = response.data.data.user
def password = response.data.data.password
return
}
def USER_NAME
def USER_PASS
pipeline
withVault
kubectl в Custom tool:
# Домашний каталог утилиты: bin
kubeconfig) с помощью метода withVault:def log = ()
pipeline
Email Extension
System => Extended E-mail Notification)smtp.yandex.ru SMTP port: 587 Credentials: Username with password (username@yandex.ru и app-password) Use TLS Default Content Type: HTML (text/html)emailDebug + фильтр hudson.plugins.emailext и уровень ALLpipeline
Parallel
pipeline
SimpleTemplateEngine
apiVersion: v1
kind: Config
current-context: ${contextName}
preferences:
clusters:
- name: ${contextName}-ctx
cluster:
server: ${clusterUrl}
users:
- name: ${contextName}-usr
user:
token: ${token}
contexts:
- name: ${contextName}
context:
cluster: ${contextName}-ctx
namespace: ${namespace}
user: ${contextName}-usr
def templateFile =
def outputFile = fileName..
echo "\n\u001B[34mRender template: => \u001B[0m\n"
def engine = new SimpleTemplateEngine()
def result = engine...
writeFile file: outputFile, text: result
return "\u001B[34m\u001B[0m"
}
pipeline
apiVersion: v1
kind: Config
current-context: <%= clusters[0].name %>
preferences:
clusters:
<% clusters.each { c -> %>
- name: ${c.name}-cluster
cluster:
server: ${c.url}
<% } %>
users:
<% clusters.each { c -> %>
<% if (c.token) { %>
- name: ${c.name}-usr
user:
token: ${c.token}
<% } %>
<% } %>
contexts:
<% clusters.each { c -> %>
- name: ${c.name}-ctx
context:
cluster: ${c.name}-cluster
namespace: ${c.namespace ?: 'default'}
user: ${c.token ? c.name + '-usr' : ''}
<% } %>
def varMap =
YamlSlurper
YAML файлы в объекты Groovy (списки и мапы), например, для чтения конфигураций с специфичными параметрами для разных окружений и заполнения шаблонов в процессе рендеринга.// @Grab('org.apache.groovy:groovy-yaml:3.0.9')
def yamlConfig = '''
clusters:
dev:
contextName: "ctx-test"
clusterUrl: "https://api.test.local:6443"
namespace: "ns-test"
token: "TEST_KEY"
prod:
contextName: "ctx-prod"
clusterUrl: "https://api.prod.local:6443"
namespace: "ns-prod"
token: "PROD_KEY"
'''
def config = new YamlSlurper().
config.clusters.each
Generic Webhook Trigger
pipeline
jenkinsUrl=192.168.3.105:8080
curl -X POST -H "Content-Type: application/json" \
--data-binary @- \
"http://$jenkinsUrl/generic-webhook-trigger/invoke?token=test-token" <<EOF
EOF
IaC
Ansible
apt -y update && apt -y upgrade
apt -y install ansible v2.10.8
apt -y install ansible-core v2.12.0
apt -y install sshpassansible-galaxy collection install ansible.windows установить коллекцию модулей
ansible-galaxy collection install community.windows
ansible-galaxy collection list | grep windows
ansible-config dump | grep DEFAULT_MODULE_PATH путь хранения модулейapt-get -y install python-dev libkrb5-dev krb5-user пакеты для Kerberos аутентификации
apt install python3-pip
pip3 install requests-kerberos
nano /etc/krb5.conf настроить [realms] и [domain_realm]
kinit -C support4@domail.local
klistansible --version/etc/ansible/ansible.cfg
inventory = /etc/ansible/hosts
# Отключить проверку ключа ssh (для подключения используя пароль)
host_key_checking = False
/etc/ansible/hosts
pi-hole-01 ansible_host=192.168.3.101
zabbix-01 ansible_host=192.168.3.102
grafana-01 ansible_host=192.168.3.103
netbox-01 ansible_host=192.168.3.104
ansible_ssh_port=2121
ansible_user=lifailon
ansible_password=123098
path_user=/home/lifailon
ansible_python_interpreter=/usr/bin/python3
huawei-book-01 ansible_host=192.168.3.99
plex-01 ansible_host=192.168.3.100
ansible_port=5985
#ansible_port=5986
ansible_user=Lifailon
#ansible_user=support4@DOMAIN.LOCAL
ansible_password=123098
ansible_connection=winrm
ansible_winrm_scheme=http
ansible_winrm_transport=basic
#ansible_winrm_transport=kerberos
ansible_winrm_server_cert_validation=ignore
validate_certs=false
huawei-book-01 ansible_host=192.168.3.99
plex-01 ansible_host=192.168.3.100
ansible_python_interpreter=C:\Users\Lifailon\AppData\Local\Programs\Python\Python311\` добавить переменную среды интерпритатора Python в Windows
ansible_connection=ssh
#ansible_shell_type=cmd
ansible_shell_type=powershell
ansible-inventory --list проверить конфигурацию (читает в формате JSON) или YAML (-y) с просмотром все применяемых переменных Windows Modules
ansible us -m ping
ansible win_ssh -m ping
ansible us -m shell -a "uptime && df -h | grep lv"
ansible us -m setup | grep -iP "mem|proc" информация о железе
ansible us -m apt -a "name=mc" -b повысить привилегии sudo (-b)
ansible us -m service -a "name=ssh state=restarted enabled=yes" -b перезапустить службу
echo "echo test" > test.sh
ansible us -m copy -a "src=test.sh dest=/root mode=777" -b
ansible us -a "ls /root" -b
ansible us -a "cat /root/test.sh" -bansible-doc -l | grep win_ список всех модулей Windows
ansible ws -m win_ping windows модуль
ansible ws -m win_ping -u WinRM-Writer указать логин
ansible ws -m setup собрать подробную информацию о системе
ansible ws -m win_whoami информация о правах доступах, группах доступа
ansible ws -m win_shell -a '$PSVersionTable'
ansible ws -m win_shell -a 'Get-Service | where name -match "ssh|winrm"'
ansible ws -m win_service -a "name=sshd state=stopped"
ansible ws -m win_service -a "name=sshd state=started"nano /etc/ansible/PowerShell-Vars.yml- hosts: ws
` Указать коллекцию модулей
collections:
- ansible.windows
` Задать переменные
vars:
SearchName: PermitRoot
tasks:
- name: Get port ssh
win_shell: |
Get-Content "C:\Programdata\ssh\sshd_config" | Select-String "{{SearchName}}"
` Передать вывод в переменную
register: command_output
- name: Output port ssh
` Вывести переменную на экран
debug:
var: command_output.stdout_lines
ansible-playbook /etc/ansible/PowerShell-Vars.yml
ansible-playbook /etc/ansible/PowerShell-Vars.yml --extra-vars "SearchName='LogLevel|Syslog'" передать переменнуюnano /etc/ansible/powershell-param.yml- hosts: ws
tasks:
- name: Run PowerShell script with parameters
ansible.windows.win_powershell:
parameters:
Path: C:\Temp
Force: true
script: |
[CmdletBinding()]
param (
[String]$Path,
[Switch]$Force
)
New-Item -Path $Path -ItemType Directory -Force:$Force
ansible-playbook /etc/ansible/powershell-param.ymlnano /etc/ansible/setup-adobe-acrobat.yml- hosts: ws
tasks:
- name: Install Acrobat Reader
win_chocolatey:
name: adobereader
state: present
ansible-playbook /etc/ansible/setup-adobe-acrobat.ymlnano /etc/ansible/setup-openssh.yml- hosts: ws
tasks:
- name: install the Win32-OpenSSH service
win_chocolatey:
name: openssh
package_params: /SSHServerFeature
state: present
ansible-playbook /etc/ansible/setup-openssh.ymlnano /etc/ansible/win-set-shell-ssh-ps7.yml- hosts: ws
tasks:
- name: Set the default shell to PowerShell 7 for Windows OpenSSH
win_regedit:
path: HKLM:\SOFTWARE\OpenSSH
name: DefaultShell
` data: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
data: 'C:\Program Files\PowerShell\7\pwsh.exe'
type: string
state: present
ansible-playbook /etc/ansible/win-set-shell-ssh-ps7.ymlnano /etc/ansible/win-service.yml- hosts: ws
tasks:
- name: Start service
win_service:
name: sshd
state: started
# state: stopped
# state: restarted
# start_mode: auto
ansible-playbook /etc/ansible/win-service.ymlnano /etc/ansible/get-service.yml- hosts: ws
tasks:
- name: Get info for a single service
win_service_info:
name: sshd
register: service_info
- name: Print returned information
ansible.builtin.debug:
var: service_info.services
ansible-playbook /etc/ansible/get-service.ymlnano /etc/ansible/copy-from-win-to-local.yml- hosts: ws
tasks:
- name: Retrieve remote file on a Windows host
# Скопировать файл из Windows-системы
ansible.builtin.fetch:
# Прочитать файл (передать в память в формате Base64)
# ansible.builtin.slurp:
src: C:\Telegraf\telegraf.conf
dest: /root/telegraf.conf
flat: yes
register: telegraf_conf
- name: Print returned information
ansible.builtin.debug:
msg: "{{ telegraf_conf['content'] | b64decode }}"
ansible-playbook /etc/ansible/copy-from-win-to-local.ymlecho "Get-Service | where name -eq vss | Start-Service" > /home/lifailon/Start-Service-VSS.ps1
nano /etc/ansible/copy-file-to-win.yml- hosts: ws
tasks:
- name: Copy file to win hosts
win_copy:
src: /home/lifailon/Start-Service-VSS.ps1
dest: C:\Users\Lifailon\Desktop\Start-Service-VSS.ps1
ansible-playbook /etc/ansible/copy-file-to-win.ymlcurl -OL https://github.com/PowerShell/PowerShell/releases/download/v7.3.6/PowerShell-7.3.6-win-x64.msi
nano /etc/ansible/copy-file-to-win.yml- hosts: ws
tasks:
- name: Copy file to win hosts
win_copy:
src: /home/lifailon/PowerShell-7.3.6-win-x64.msi
dest: C:\Install\PowerShell-7.3.6.msi
ansible-playbook /etc/ansible/copy-file-to-win.ymlnano /etc/ansible/run-script-ps1.yml- hosts: ws
tasks:
- name: Run PowerShell Script
win_command: powershell -ExecutionPolicy ByPass -File C:\Users\Lifailon\Desktop\Start-Service-VSS.ps1
ansible-playbook /etc/ansible/run-script-ps1.ymlnano /etc/ansible/setup-msi-package.yml- hosts: ws
tasks:
- name: Install MSI Package
win_package:
# path: C:\Install\7z-23.01.msi
path: C:\Install\PowerShell-7.3.6.msi
arguments:
- /quiet
- /passive
- /norestart
ansible-playbook /etc/ansible/setup-msi-package.ymlnano /etc/ansible/win-fw-open.yml- hosts: ws
tasks:
- name: Open RDP port
win_firewall_rule:
name: Open RDP port
localport: 3389
action: allow
direction: in
protocol: tcp
state: present
enabled: yes
ansible-playbook /etc/ansible/win-fw-open.ymlnano /etc/ansible/win-creat-group.yml- hosts: ws
tasks:
- name: Create a new group
win_group:
name: deploy
description: Deploy Group
state: present
ansible-playbook /etc/ansible/win-creat-group.ymlnano /etc/ansible/add-user-to-group.yml- hosts: ws
tasks:
- name: Add a local and domain user to a local group
win_group_membership:
name: deploy
members:
- WinRM-Writer
state: present
ansible-playbook /etc/ansible/add-user-to-group.ymlnano /etc/ansible/creat-win-user.yml- hosts: ws
tasks:
- name: Creat user
win_user:
name: test
password: 123098
state: present
groups:
- deploy
ansible-playbook /etc/ansible/creat-win-user.ymlnano /etc/ansible/delete-win-user.yml- hosts: ws
tasks:
- name: Delete user
ansible.windows.win_user:
name: test
state: absent
ansible-playbook /etc/ansible/delete-win-user.ymlnano /etc/ansible/install-feature.yml- hosts: ws
tasks:
- name: Install Windows Feature
win_feature:
name: SNMP-Service
state: present
ansible-playbook /etc/ansible/install-feature.ymlnano /etc/ansible/win-reboot.yml- hosts: ws
tasks:
- name: Reboot a slow machine that might have lots of updates to apply
win_reboot:
reboot_timeout: 3600
ansible-playbook /etc/ansible/win-reboot.ymlnano /etc/ansible/win-ls.yml- hosts: ws
tasks:
- name: Find files in multiple paths
ansible.windows.win_find:
paths:
- D:\Install\OpenSource
patterns:
` Файл созданный менее 7 дней назад
age: -7d
` Размер файла больше 10MB
size: 10485760
` Рекурсивный поиск (в дочерних директориях)
recurse: true
register: command_output
- name: Output
debug:
var: command_output
ansible-playbook /etc/ansible/win-ls.ymlnano /etc/ansible/rest-get.yml- hosts: ws
tasks:
- name: REST GET request to endpoint github
ansible.windows.win_uri:
url: https://api.github.com/repos/Lifailon/pSyslog/releases/latest
register: http_output
- name: Output
debug:
var: http_output
ansible-playbook /etc/ansible/rest-get.ymlnano /etc/ansible/win-update.yml- hosts: ws
tasks:
- name: Install only particular updates based on the KB numbers
ansible.windows.win_updates:
category_names:
- SecurityUpdates
- CriticalUpdates
- UpdateRollups
- Drivers
` Фильтрация
` accept_list:
` - KB2267602
` Поиск обновлений
` state: searched
` Загрузить обновления
` state: downloaded
` Установить обновления
state: installed
log_path: C:\Ansible-Windows-Upadte-Log.txt
reboot: false
register: wu_output
- name: Output
debug:
var: wu_output
ansible-playbook /etc/ansible/win-update.yml- name: Ensure Chocolatey installed from internal repo
win_chocolatey:
name: chocolatey
state: present
# source: URL-адрес внутреннего репозитория
source: https://community.chocolatey.org/api/v2/ChocolateyInstall.ps1
Jinja
pip install jinja2 --break-system-packagesinventory.j2 шаблон для генерации[dev]
{% for host in hosts -%}
{{ host }} ansible_host={{ host }}
{% endfor %}
env.json файл с переменными
render.py скрипт для генерации файла inventory
# Загружаем переменные из JSON
=
# Настройка шаблонизатора
=
=
=
# Сохраняем результат в файл
python render.pyhosts:inventory.ini[dev]
dev1 ansible_host=192.168.3.101
dev2 ansible_host=192.168.3.102
dev3 ansible_host=192.168.3.103
templates/hosts.j2127.0.0.1 localhost
{% for host in groups['all'] -%}
{{ hostvars[host]['ansible_host'] }} {{ host }}
{% endfor %}
playbook.yml- name: Update hosts file
hosts: all
become: true
tasks:
- name: Generate hosts file
template:
src: hosts.j2
dest: /etc/hosts
owner: root
group: root
mode: '0644'
ansible-playbook -i inventory.ini playbook.yml --check --diff отобразит изменения без их реального применения
ansible-playbook -i inventory.ini playbook.yml -K позволяет передать пароль для root Puppet/Bolt
nano inventory.yamlgroups:
- name: bsd
targets:
- uri: 192.168.3.102:22
name: openbsd
- uri: 192.168.3.103:22
name: freebsd
config:
transport: ssh
ssh:
user: root
# password: root
host-key-check: false
bolt command run uptime --inventory inventory.yaml --targets bsd выполнить команду uptime на группе хостов bsd, заданной в файле inventoryecho name: $APP_NAME > bolt-project.yaml создать файл проектаmkdir plans && nano test.yaml создать директорию и файл с планом работparameters:
targets:
type: TargetSpec
steps:
- name: clone
command: rm -rf $APP_NAME && git clone https://github.com/Lifailon/$APP_NAME
targets: $targets
- name: test
command: cd $APP_NAME && go test -v -cover --run TestMainInterface
targets: $targets
- name: remove
command: rm -rf $APP_NAME
targets: $targets
bolt plan show вывести список всех плановbolt plan run $APP_NAME::test --inventory inventory.yaml --targets bsd -v запустить план Sake
sake.yaml, а затем запускаете задачи на серверах. |
servers:
localhost:
host: 0.0.0.0
local: true
obsd:
host: root@192.168.3.102:22
tags:
fbsd:
host: root@192.168.3.103:22
tags:
work_dir: /tmp
env:
DATE: $(date -u +"%Y-%m-%dT%H:%M:%S%Z")
specs:
info:
output: table
ignore_errors: true
omit_empty_rows: true
omit_empty_columns: true
any_fatal_errors: false
ignore_unreachable: true
strategy: free
tasks:
ping:
desc: Pong
spec: info
cmd: echo "pong"
uname:
name: OS
desc: Print OS
spec: info
cmd: |
os=$(uname -s)
release=$(uname -r)
echo "$os $release"
uptime:
name: Uptime
desc: Print uptime
spec: info
cmd: uptime
info:
desc: Get system overview
spec: info
tasks:
- task: ping
- name: date
cmd: echo $DATE
- name: pwd
cmd: pwd
- task: uname
- task: uptime
sake run info --tags bsd запустить набор из 5 заданий из группы info Prometheus
название_метрики{лейбл="НАЗВАНИЕ ДИСКА 1", instance="HOSTNAME"} ЗНАЧЕНИЕ
название_метрики{лейбл="НАЗВАНИЕ ДИСКА 2", instance="HOSTNAME"} ЗНАЧЕНИЕ
counter - возрастающее значение (например, количество запросов, ошибок, завершенных задач)gauge - переменное значение (может увеличиваться или уменьшаться, например, нагрузка CPU, объем свободной памяти, температура)histogram - разделенные данные на корзины (buckets, с помощью лэйбла le) и подсчет наблюдения в них (автоматически создает три метрики: summary - аналогичен гистограмме, но вычисляет квантилиpushgateway в контейнере:docker run -d --name pushgateway --restart unless-stopped -p 19091:9091 prom/pushgateway$instance = [System.Net.Dns]::GetHostName()
$pushgatewayUrl = "http://192.168.3.100:19091/metrics/job/disk_temperature"
# Изменить адрес шлюза на имя контейнера при запуске через compose
# $pushgatewayUrl = "http://pushgateway:9091/metrics/job/disk_temperature"
$path = "C:/Program Files/CrystalDiskInfo/Smart"
# Изменить путь при запуске в контейнере Docker через WSL
# $path = "/mnt/c/Program Files/CrystalDiskInfo/Smart"
# Необходимо строго использовать синтаксис PowerShell (избегая псевдонимы ls)
$diskArray = .Name
while ($true)
.Split("`n") | Select-String "disk_temperature"
prometheus.yml:scrape_configs:
- job_name: cdi-exporter
scrape_interval: 10s
scrape_timeout: 2s
metrics_path: /metrics
static_configs:
- targets:
- '192.168.3.100:19091'
docker-compose kill -s SIGHUP prometheus применяем измененияWSL с помощью dockerfile монтированием системного диска Windows:FROM mcr.microsoft.com/powershell:latest
WORKDIR /cdi-exporter
COPY cdi-exporter.ps1 ./cdi-exporter.ps1
CMD ["pwsh", "-File", "cdi-exporter.ps1"]
docker build -t cdi-exporter .
docker run -d -v /mnt/c:/mnt/c --name cdi-exporter cdi-exporterdocker-compose.yml:services:
cdi-exporter:
build:
context: .
dockerfile: dockerfile
container_name: cdi-exporter
volumes:
- /mnt/c:/mnt/c
restart: unless-stopped
pushgateway:
image: prom/pushgateway
container_name: pushgateway
ports:
- "19091:9091"
restart: unless-stopped
docker-compose up -dDashboard в Grafana:
hostName: label_values(exported_instance)
diskName: label_values(disk)
Метрика температуры: disk_temperature{exported_instance="$hostName", disk=~"$diskName"} PromQL
PromQL:Функция Тип данных Описание Пример rate()counterСредняя скорость роста метрики за интервал (increase / seconds) rate(http_requests_total[$__rate_interval])irate()counterМгновенная скорость роста (использует последние 2 точки) irate(http_requests_total[1m])increase()counterАбсолютный прирост метрики за интервал (end time - start time) increase(http_requests_total[5m])resets()counterКоличество сбросов counter-метрики за интервал. resets(process_cpu_seconds_total[1h])delta()gaugeРазница между первым и последним значением метрики за интервал delta(node_memory_free[[5m]])idelta()gaugeРазница между последними двумя точками delta(node_memory_free[1m])avg_over_time()gaugeСреднее значение за интервал avg_over_time(temperature[5m])max_over_time()gaugeМаксимальное значение за интервал max_over_time(temperature[5m])predict_linear()gaugeПредсказывает значение метрики через N секунд (для прогнозирования) predict_linear(disk_free[1h], 3600)count()counter/gaugeКоличество элементов метрики count(http_requests_total) by (status_code)sum()counter/gaugeСуммирует значения метрик по указанным labels sum(rate(cpu_usage[5m])) by (pod)avg()counter/gaugeСреднее значение метрики по указанным labels avg(node_memory_usage_bytes) by (instance)min() / max()counter/gaugeВозвращает минимальное/максимальное значение max(container_cpu_usage) by (namespace)round()counter/gaugeОкругляет значения до указанного числа дробных знаков round(container_memory_usage / 1e9, 2)floor() / ceil()counter/gaugeОкругляет вниз/вверх до целого числа floor(disk_usage_percent)absent()counter/gaugeВозвращает 1, если метрика отсутствует (для алертинга) absent(up{job="node-exporter"})clamp_min() / clamp_max()counter/gaugeОграничивает значения минимумом/максимумом (уменьшает если больше) clamp_max(disk_usage_percent, 100)label_replace()counter/gaugeИзменяет или добавляет labels в метрике label_replace(metric, "new_label", "$1", "old_label", "(.*)")sort() / sort_desc()counter/gaugeСортирует метрики по возрастанию/убыванию sort(node_filesystem_free_bytes) Cloud
AWS/LocalStack
# export AWS_ACCESS_KEY_ID="test"
# export AWS_SECRET_ACCESS_KEY="test"
# export AWS_DEFAULT_REGION="us-east-1"
# Windows
$env:AWS_ENDPOINT_URL="http://192.168.3.101:4566"
S3
Fluent Bit
services:
localstack:
image: localstack/localstack
container_name: localstack
restart: always
ports:
- 4566:4566
- 4510-4559:4510-4559
environment:
- DEBUG=1
- PERSISTENCE=1
- EXTRA_CORS_ALLOWED_ORIGINS=*
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./localstack_data:/var/lib/localstack
fluent-bit:
image: fluent/fluent-bit:latest
container_name: fluent-bit
ports:
- 24224:24224
environment:
- AWS_ENDPOINT_URL=http://localstack:4566
- AWS_ACCESS_KEY_ID=test
- AWS_SECRET_ACCESS_KEY=test
- AWS_REGION=us-east-1
volumes:
- ./fluent-bit.conf:/fluent-bit/etc/fluent-bit.conf
fluent-bit.conf для приема логов из контейнеров Docker и их переадресации в AWS CloudWatch:[SERVICE]
info
[INPUT]
forward
24224
[OUTPUT]
localstack_cloudwatch_logs
*
us-east-1
On
docker-logs
container-
localstack
4566
fluentd для отправки логов из любого контейнера Docker: logging:
driver: fluentd
options:
fluentd-address: localhost:24224
tag: zerobyte
CloudWatch
# Создание группы для хранения логов (Log Group)
# Отобразить все группы
# Создание потока для записи логов (Log Stream)
# Отобразить все потоки в группе
# Отправка лога (Put Log Events)
# Windows
)
# Чтение логов (Get Log Events)
# Фильтрация логов
# Чтение логов из всех потоков за последние сутки
Azure
Install-Module -Name Az -Scope CurrentUser -Repository PSGallery -Force установить все модули для работы с Azure
Get-Module *Az.* список всех модулейGet-Command -Module Az.Accounts отобразить список команд модуля Az.Accounts
Connect-AzAccount подключиться у учетной записи Azure
Get-AzContext получить текущий статус подключения к Azure
Get-AzSubscription получить список подписок Azure, доступных для текущего пользователя
Set-AzContext установить контекст Azure для конкретной подписки и/или учетной записи
Disconnect-AzAccount отключиться от учетной записи AzureGet-Command -Module Az.Compute
Get-AzVM получить список виртуальных машин в текущей подписке или группе ресурсов
Get-AzVMSize получить список доступных размеров виртуальных машин в определенном регионе
Get-AzVMImage получить список доступных образов виртуальных машин
New-AzVM создать новую виртуальную машину
Remove-AzVM удалить виртуальную машину
Start-AzVM запустить виртуальную машину
Stop-AzVM остановить виртуальную машину
Restart-AzVM перезагрузить виртуальную машинуGet-Command -Module Az.Network
Get-AzVirtualNetwork получить список виртуальных сетей в текущей подписке или группе ресурсов
New-AzVirtualNetwork создать новую виртуальную сеть
Remove-AzVirtualNetwork удалить виртуальную сеть
Get-AzNetworkInterface получить список сетевых интерфейсов
New-AzNetworkInterface создать новый сетевой интерфейс
Remove-AzNetworkInterface удалить сетевой интерфейсGet-Command -Module Az.Storage
Get-AzStorageAccount получить список учетных записей хранилища
New-AzStorageAccount создать новую учетную запись хранилища
Remove-AzStorageAccount удалить учетную запись хранилища
Get-AzStorageContainer список контейнеров в учетной записи хранилища
New-AzStorageContainer создать новый контейнер в учетной записи хранилища
Remove-AzStorageContainer удалить контейнерGet-Command -Module Az.ResourceManager
Get-AzResourceGroup получить список групп ресурсов в текущей подписке
New-AzResourceGroup создать новую группу ресурсов
Remove-AzResourceGroup удалить группу ресурсов
Get-AzResource получить список ресурсов
New-AzResource создать новый ресурс
Remove-AzResource удалить ресурсGet-Command -Module Az.KeyVault
Get-AzKeyVault список хранилищ ключей
New-AzKeyVault создать новое хранилище ключей в Azure
Remove-AzKeyVault удалить хранилище ключей в AzureGet-Command -Module Az.Identity
Get-AzADUser получить информацию о пользователях Azure Active Directory
New-AzADUser создать нового пользователя
Remove-AzADUser удалить пользователя
Get-AzADGroup получить информацию о группах
New-AzADGroup создать новую группу
Remove-AzADGroup удалить группуNew-AzResourceGroup -Name "Resource-Group-01" -Location "EastUS" создать группу ресурсов (логический контейнер, в котором происходит развертывание ресурсов Azure)
Get-AzVMImageOffer -Location "EastUS" -PublisherName "MicrosoftWindowsServer" список доступных образов Windows Server для установки
$cred = Get-Credential
New-AzVm -ResourceGroupName "Resource-Group-01" -Name "vm-01" -Location 'EastUS' -Image "MicrosoftWindowsServer:WindowsServer:2022-datacenter-azure-edition:latest" -Size "Standard_D2s_v3" -OpenPorts 80,3389 --Credential $cred создать виртуальную машину
Get-AzVM -ResourceGroupName "Resource-Group-01" -Name "vm-01" -Status | Select @{n="Status"; e={$_.Statuses[1].Code}} статус виртуальной машины
Start-AzVM -ResourceGroupName "Resource-Group-01" -Name "vm-01" запустить виртуальную машину
Stop-AzVM -ResourceGroupName "Resource-Group-01" -Name "vm-01" -Force остановить виртуальную машину
Invoke-AzVMRunCommand -ResourceGroupName "Resource-Group-01" -VMName "vm-01" -CommandId "RunPowerShellScript" -ScriptString "Install-WindowsFeature -Name Web-Server -IncludeManagementTools" установить роль веб-сервера IIS$diskConfig = New-AzDiskConfig -Location "EastUS" -CreateOption Empty -DiskSizeGB 512 -SkuName "Standard_LRS" создать диск на 512 Гб
$dataDisk = New-AzDisk -ResourceGroupName "Resource-Group-01" -DiskName "disk-512" -Disk $diskConfig создание объекта диска для подготовки диска данных к работе
Get-AzDisk -ResourceGroupName "Resource-Group-01" -DiskName "disk-512" список дисков
$vm = Get-AzVM -ResourceGroupName "Resource-Group-01" -Name "vm-01"
Add-AzVMDataDisk -VM $vm -Name "Resource-Group-01" -CreateOption Attach -ManagedDiskId $dataDisk.Id -Lun 1 подключить диск к виртуальной машине
Update-AzVM -ResourceGroupName "Resource-Group-01" -VM $vm обновить конфигурацию виртуальной машины
Get-Disk | Where PartitionStyle -eq 'raw' | Initialize-Disk -PartitionStyle MBR -PassThru | New-Partition -AssignDriveLetter -UseMaximumSize | Format-Volume -FileSystem NTFS -NewFileSystemLabel "disk-512" -Confirm:$false инициализировать диск в ОС (необходимо подключиться к виртуальной машине) с таблицей MBR, создать раздел и назначить все пространство и форматировать в файловую систему NTFS Vercel
REST API.npm i -g vercel установить глобально в систему Vercel CLI
vercel --version выводит текущую версию установленного Vercel CLI
vercel login выполняет вход в аккаунт Vercel (> Continue with GitHub)
vercel logout выполняет выход из аккаунта Vercel
vercel init инициализирует новый проект в текущей директории (создает файл конфигурации vercel.json и другие файлы, необходимые для проекта)
vercel dev запускает локальный сервер для проверки работоспособности (http://localhost:3000)
vercel deploy загружает проект на серверы Vercel и развертывает его
vercel link привязывает текущую директорию к существующему проекту на сервере Vercel (выбрать из списка)
vercel unlink отменяет привязку текущей директории от проекта Vercel
vercel env управляет переменными окружения для проекта
vercel env pull подтягивает переменные окружения с Vercel в локальный .env файл
vercel env ls показывает список всех переменных окружения для проекта
vercel env add <key> <environment> добавляет новую переменную окружения для указанного окружения (production, preview, development)
vercel env rm <key> <environment> удаляет переменную окружения из указанного окружения
vercel projects управляет проектами Vercel
vercel projects ls показывает список всех проектов
vercel projects add добавляет новый проект
vercel projects rm <project> удаляет указанный проект
vercel pull подтягивает последние настройки окружения с Vercel
vercel alias управляет алиасами доменов для проектов
vercel alias ls показывает список всех алиасов для текущего проекта
vercel alias set <alias> устанавливает алиас для указанного проекта
vercel alias rm <alias> удаляет указанный алиас
vercel domains управляет доменами, привязанными к проекту
vercel domains ls показывает список всех доменов
vercel domains add <domain> добавляет новый домен к проекту
vercel domains rm <domain> удаляет указанный домен
vercel teams управляет командами и членами команд на Vercel
vercel teams ls показывает список всех команд
vercel teams add <team> добавляет новую команду
vercel teams rm <team> удаляет указанную команду
vercel logs <deployment> выводит логи для указанного деплоя
vercel secrets управляет секретами, используемыми в проектах
vercel secrets add <name> <value> добавляет новый секрет
vercel secrets rm <name> удаляет указанный секрет
vercel secrets ls показывает список всех секретов
vercel switch <team> переключается между командами и аккаунтами VercelJavaScript приложения через GitHub Actions:name: CD (Deploy to Vercel)
on:
workflow_dispatch:
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm install
- name: Deploy to Vercel
uses: amondnet/vercel-action@v25
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
vercel-args: '--prod'
Proxy
Traefik
services:
traefik:
image: traefik:v3
container_name: traefik
restart: always
# Используем режим хоста для доступа к сети всех контейнеров
network_mode: host
# ports:
# - 8080:8080 # Web UI
# - 80:80 # HTTP Proxy
# - 443:443 # HTTPS Proxy
# - 4318:4318 # Prometheus Metrics
dns:
- 127.0.0.1
volumes:
- ./traefik.yml:/etc/traefik/traefik.yml
- ./rules:/rules
- /var/run/docker.sock:/var/run/docker.sock:ro
healthcheck:
test: wget -qO- http://127.0.0.1:8080/ping
start_period: 10s
interval: 30s
timeout: 5s
retries: 5
labels:
# Включаем маршрутизацию и определяем имя хоста
- traefik.enable=true
- traefik.http.routers.traefik.rule=Host(`traefik.docker.local`)
# Указываем порт назначения в контейнере (если используется несколько портов)
- traefik.http.services.traefik.loadbalancer.server.port=8080
# Создаем базовую авторизацию
- traefik.http.middlewares.basic-auth-traefik.basicauth.users=admin:$$2y$$05$$c0r5A6SCKX4R6FjuCgRqrufbIE5tmXw2sDPq1vZ8zNrrwNZIH9jgW # htpasswd -nbB admin admin
# Включаем базовую авторизацию в маршрутизацию текущего сервиса
- traefik.http.routers.traefik.middlewares=basic-auth-traefik
# Настраиваем подключение к Authentik
# - traefik.http.middlewares.authentik.forwardauth.address=http://192.168.3.101:9000/outpost.goauthentik.io/auth/traefik
# - traefik.http.middlewares.authentik.forwardauth.trustForwardHeader=true
# - traefik.http.middlewares.authentik.forwardauth.authResponseHeaders=X-authentik-username,X-authentik-groups,X-authentik-entitlements,X-authentik-email,X-authentik-name,X-authentik-uid,X-authentik-jwt,X-authentik-meta-jwks,X-authentik-meta-outpost,X-authentik-meta-provider,X-authentik-meta-app,X-authentik-meta-version
# Включаем авторизацию через Authentik из провайдера Docker
# - traefik.http.routers.traefik.middlewares=authentik@docker
# Включаем авторизацию через Authentik из провайдера file
# - traefik.http.routers.traefik.middlewares=authentik@file
jaeger:
image: jaegertracing/all-in-one:1.55
container_name: jaeger
restart: always
ports:
- 16686:16686 # Веб-интерфейс
- 4317:4317 # Сборщик трассировок
tech-dns-srv:
image: technitium/dns-server:latest
container_name: tech-dns-srv
restart: always
volumes:
- ./dns_data:/etc/dns
environment:
- DNS_SERVER_DOMAIN=dns.docker.local
- DNS_SERVER_FORWARDERS=1.1.1.1,8.8.8.8
- DNS_SERVER_BLOCK_LIST_URLS=https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts
network_mode: host
labels:
- traefik.enable=true
# Определяем FQDN
- traefik.http.routers.tech-dns-srv.rule=Host(`dns.docker.local`)
# Переадресация на порт
- traefik.http.services.tech-dns-srv.loadbalancer.server.port=5380
traefik.yml:entryPoints:
# Настраиваем переадресацию на websecure для принудительного использования HTTPS
web:
address: ":80"
http:
redirections:
entryPoint:
to: websecure
scheme: https
permanent: true
websecure:
address: ":443"
http:
tls:
transport:
keepAliveMaxRequests: 0
# Определяем время ожидания для неактивного соединения (для поддержания активной сессии)
respondingTimeouts:
idleTimeout: "30m"
# Адрес веб-интерфейса
traefik:
address: ":8080"
# Переопределяем порт для получения метрик
metrics:
address: ":4318"
# Включаем веб-интерфейс
api:
dashboard: true
insecure: true
# Конечная точка для healthcheck
ping:
terminatingStatusCode: 204
# Настройка переадресации трассировок в Jaeger
tracing:
serviceName: traefik
otlp:
grpc:
endpoint: jaeger:4317
insecure: true
# Включаем prometheus exporter
metrics:
prometheus:
entryPoint: metrics
addRoutersLabels: true
addServicesLabels: true
# Настраиваем формата логов
log:
format: "common" # common or json
level: "INFO" # DEBUG, INFO, WARN, ERROR, FATAL and PANIC
# Настройка фильтрации логов доступа
accessLog:
format: "common"
filters:
minDuration: "1ms"
statusCodes:
- "200-299"
- "300-399"
- "400-499"
- "500-599"
# Провайдеры для автоматического опредиления сервисов
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: true
# Формат по умолчанию при опредиление сервисов compose: <service_name>-<project_name>
# defaultRule: "Host(`{{ .Name }}.docker.local`)"
# Шаблон в формате (используем функцию index): index <map> <key>
defaultRule: "Host(`{{ index .Labels \"com.docker.compose.service\" | default .Name }}.docker.local`)"
file:
directory: /rules
watch: true
file в директории rules на примере интеграции с Authentik для авторизации с использование технологии SSO.authentik-middlewares.yml:http:
middlewares:
authentik:
forwardAuth:
address: http://192.168.3.101:9000/outpost.goauthentik.io/auth/traefik
trustForwardHeader: true
authResponseHeaders:
- X-authentik-username
- X-authentik-groups
- X-authentik-entitlements
- X-authentik-email
- X-authentik-name
- X-authentik-uid
- X-authentik-jwt
- X-authentik-meta-jwks
- X-authentik-meta-outpost
- X-authentik-meta-provider
- X-authentik-meta-app
- X-authentik-meta-version
authentik-routers.yml:http:
routers:
homepage-src:
rule: "Host(`home.docker.local`)"
service: homepage-dst
entryPoints:
- websecure
middlewares:
- authentik
dns-src:
rule: "Host(`dns.docker.local`)"
service: dns-dst
entryPoints:
- websecure
services:
homepage-dst:
loadBalancer:
servers:
- url: http://172.26.0.2:3000
dns-dst:
loadBalancer:
servers:
- url: http://192.168.3.101:5380
HAProxy
services:
httpbin-proxy:
image: haproxy:3.2.4-alpine
container_name: httpbin-proxy
restart: unless-stopped
ports:
- 8089:8080
- 2376:2376
volumes:
- ./haproxy.cfg:/haproxy.cfg
command:
- haproxy
- -f
- /haproxy.cfg
- -d
environment:
- STATS_USER=admin
- STATS_PASS=admin
- STATS_URI=/
- METRICS_URI=/metrics
httpbin-go:
image: ghcr.io/mccutchen/go-httpbin
container_name: httpbin-go
restart: unless-stopped
global
stdout format raw daemon info
4096
defaults
http
global
global
httplog
log-health-checks
connect 5000ms
client 50000ms
server 50000ms
metrics
*:2376
http
enable
uri "$STATS_URI"
auth "$STATS_USER":"$STATS_PASS"
refresh 5s
use-service prometheus-exporter if { path "$METRICS_URI" }
log
frontend_httpbin
*:8080
backend_httpbin
backend_httpbin
http
roundrobin
check 10s
inter 5s fall 3 rise 3
httpchk GET /get
expect status 200
expect string "origin"
internal-httpbin-go httpbin-go:8080 check weight 20
external-httpbin httpbin.org:443 ssl verify none check weight 10
maxconn максимальное количество одновременных соединений
nbproc количество процессов HAProxy
option httplog включает журналирование HTTP-трафика, полезно для отладки и мониторинга прохождения трафика через HAProxy и дает возможность просматривать HTTP-трафик в журнале, чтобы отслеживать запросы и ответы
option httpchk отправлять HTTP-запросы к серверам в бэкенде, чтобы определить, работают ли они, это позволяет выявлять неработающие сервера и перераспределять запросы на работающие
option httpchk GET / HTTP/1.1\r\nHost:\ localhost отправляет GET-запрос на корневой путь (/) используя версию протокола HTTP 1.1, Host:\ localhost - это часть заголовка Host, который также включается в HTTP-запрос и указывает на целевой хост, который проверяется
option tcp-check активирует общую функцию TCP-проверок для всего бэкэнда, без необходимости указывать порт явно
tcp-check connect port 443 HAProxy будет устанавливать соединение с серверами в бэкенде на порту 443 для проверки, что серверы доступны и способны принимать соединения на этом портуRound Robin (roundrobin) алгоритм используемый по умолчанию, отправляет запросы на сервера по очереди
static-rr похож на roundrobin, но он сохраняет порядок серверов в конфигурации
Least Connections (leastconn) выбирает сервер с наименьшим количеством активных соединений, это полезно, если у серверов разная производительность или загруженность, так как запросы будут отправляться на менее загруженные серверы
source использует IP-адрес источника (клиента) для привязки к одному и тому же серверу, это означает, что клиент всегда будет направляться к одному и тому же серверу, это полезно для сохранения состояния сеанса
uri запросы с одним и тем же URL (до знака вопроса) будут переправляться на один и тот же сервер, это полезно для балансировки запросов к разным частям приложения
rdp-cookie используется для балансировки запросов RDP (Remote Desktop Protocol), он анализирует cookie-заголовок RDP для принятия решений о направлении запросовssl использование SSL
verify none отсутствие проверки сертификата
weight распределение запросов по весу, если необходимо на определенный сервер отправлять больше запросов
inter изменяет интервал между проверками, по умолчанию две секунды
fall устанавливает допустимое количество неудачных проверок, по умолчанию три
rise задает, сколько проходных проверок должно быть, прежде чем вернуть ранее отказавший сервер в ротацию, по умолчанию два
check port 443 указать явную проверку порта для конкретного сервера
check backup параметр означает, что сервер будет использоваться только в случае, если все основные серверы становятся недоступными и не будет участвовать в балансировке, пока основные серверы функционируют Keepalive
VRRP-пакеты - это специальные сообщения, которые узлы (маршрутизаторы/сервера) в VRRP-группе рассылают для сообщения своего состояния
VIP (Virtual IP) - виртуальный IP адрес, который может автоматически переключаться между серверами в случае сбоя (frondend для haproxy/dns-rr), у кого в данный момент в сетевом интерфейсе прописан VIP, тот сервер и работает
Master - сервер, на котором в данный момент активен VIP (отправляет VRRP-пакеты на backup nodes)
Backup - сервера на которые переключится VIP, в случае сбоя мастера (следим за мастером)
VRID (virtual_router_id) - сервера, объединенные общим виртуальным IP (VIP) образуют виртуальный роутер, уникальный идентификатор которого, принимает значения от 1 до 255. Сервер может одновременно состоять в нескольких VRID, при этом для каждой VRID должны использоваться уникальные виртуальные IP адреса.
Master сервер с заданным интервалом отправляет VRRP пакеты на зарезервированный адрес multicast (многоадресной) рассылки или unicast на указанные ip-адреса, а все backup/slave сервера слушают этот адрес. Если Slave сервер не получает пакеты, он начинает процедуру выбора Master в соответствии с приоритетом, и если он переходит в состояние Master, то у него активирует VIP (поднимается виртуальный интерфейс) и отравляет gratuitous ARP.
Gratuitous ARP - это вид ARP ответа, который обновляет MAC таблицу на подключенных коммутаторах, чтобы проинформировать о смене владельца виртуального IP-адреса и MAC-адреса для перенаправления трафика. При настройке VRRP, в качестве адреса для виртуального IP не используется реальный адрес сервера, так как, в случае сбоя, его адрес переместится на соседний, и при восстановлении, он окажется изолированным от сети, и чтобы вернуть свой адрес, нужно отправить в сеть VRRP пакет, но не будет IP адреса, с которого это возможно сделать.nano /etc/keepalived/keepalived.confglobal_defs {
enable_script_security
}
nginx_check {
"/usr/bin/curl http://127.0.0.1"
5
nginx
}
web {
MASTERens33
110
2552
/etc/keepalived/notify-web.sh root
virtual_ipaddress {
.168.3.110
}
track_interface {
ens333
}
track_script {
nginx_check
}
}
state <MASTER|BACKUP> начальное состояние при запуске, в режиме nopreempt единственное допустимое значение - BACKUP
interface интерфейс, на котором будет работать VRRP и подниматься VIP
virtual_router_id <0-255> уникальный идентификатор VRRP экземпляра, должен совпадать на всех серверах одной группы
priority <0-255> задает приоритет при выборе MASTER, сервер с большим числом приоритета становится MASTER
advert_int <число секунд> определяет, с какой периодичностью мастер должен сообщать остальным о себе, и если по истечению данного периода сервера не получат от мастера широковещательный пакет, то они инициируют выборы нового мастера
nopreempt если мастер пропал из сети, и был выбран новый мастер с меньшим приоритетом, то по возвращении старшего мастера, он останется в состоянии BACKUP, пока новый мастер не отвалится
preempt_delay что бы мастером был конкретный сервер, то заменить настройку nopreempt на preempt_delay
notify скрипт, который будет выполняться при каждом изменении состояния сервера, и имя пользователя, от имени которого данный скрипт будет выполняться (логирование или отправка на почту)
virtual_ipaddress виртуальный IP-адрес (VIP), которые будет активирован на сервере в состоянии MASTER, должны совпадать на всех серверах внутри VRRP экземпляра
track_interface мониторинг состояния интерфейсов, переводит VRRP экземпляр в состояние FAULT, если один из перечисленных интерфейсов находится в состоянии DOWN
track_script мониторинг с использованием скрипта, который должен возвращать 0 если проверка завершилась успешно или 1, если проверка завершилась с ошибкой
fall <число> количество раз, которое скрипт вернул не нулевое значение, при котором перейти в состояние FAULT
rise <число> количество раз, которое скрипт вернул нулевое значение, при котором выйти из состояния FAULT
timeout <число> время ожидания, пока скрипт вернет результат, после которого вернуть ненулевое значениеjournalctl -u keepalived
cat /var/log/messages | grep -i keepalived
tail /var/run/keepalived.INSTANCE.web.state GlusterFS
ext4) с помощью технологии FUSE (в пользовательском пространстве).
Copy Code https://lifailon.github.io/search_index.en.json $MATCHES more matches # Определить имена хостов для всех нод
# Установить сервер на все ноды
# Подключить (peer detach для отключения) все узлы к пулу Trusted Server Pool (TSP)
# Создать директорию хранения на всех нодах
# Создать том в пуле и запустить его
# Монтирование с помощью клиента
# Включение модуля NFS для удаленного монтирования
# Включить CIFS/SMB протокол
