GitOps: ArgoCD, Helm, Kustomize
Поднял сервис по описанию из файла: лимит памяти 256 мегабайт. Запустил ту же команду ещё раз - 208 миллисекунд и контейнер даже не пересоздан, потому что менять нечего. Потом «кто-то» поправил лимит руками, до 64. Запустил применение описания заново - и лимит остался 64. Описание говорит одно, реальность другое, и само оно не сходится.
Стержень: описание в репозитории становится источником правды только тогда, когда кто-то постоянно сверяет его с реальностью и приводит её в соответствие.
// Формулировки: «что такое GitOps?», «чем отличается pull-модель от push?», «как хранить секреты в репозитории?»
Описание против команд
Императивный подход - это набор команд: сделай то, потом это. Декларативный - описание нужного состояния: должно быть три экземпляра такой-то версии с такими лимитами. Разница видна в повторном запуске: команда «создай» второй раз ломается или плодит лишнее, а применение описания - это просто сверка. У меня повторный запуск занял 208 миллисекунд и ничего не тронул.
GitOps добавляет к этому одно правило: единственный источник правды - репозиторий. Правки в прод попадают только через изменение файлов и их вливание в основную ветку, а не через команды с чьего-то ноутбука. Побочные выгоды приходят сами: история изменений инфраструктуры - это история коммитов (зафиксированных изменений с автором, датой и описанием), откат - это возврат коммита, а обязательный просмотр изменений коллегой уже встроен в процесс.
// Различают две схемы доставки. При push-модели пайплайн после сборки сам идёт в кластер (группу машин, где крутятся сервисы) и применяет изменения - значит, у пайплайна должны быть ключи от прода. При pull-модели внутри кластера живёт агент, который сам ходит в репозиторий, сравнивает описанное с фактическим и приводит в соответствие. Ключи от прода наружу при этом не выдаются, и это главный аргумент за pull.
- декларативное описание
- запись нужного состояния, а не шагов до него
- push-модель
- пайплайн сам заходит в кластер; ему нужны ключи от прода
- pull-модель
- агент внутри кластера сам тянет описание из репозитория
Дрейф: почему одного применения мало
Дрейф - это расхождение между описанным и фактическим. Он появляется от любой правки руками: кто-то в аварию поднял лимит, кто-то поменял переменную, кто-то удалил лишний экземпляр. Я это воспроизвёл: поменял лимит памяти у живого контейнера с 256 мегабайт на 64, а потом заново применил описание, где написано 256. Лимит остался 64 - инструмент увидел, что описание не менялось, и ничего делать не стал.
И только принудительное пересоздание вернуло 256. Вот тут и проходит граница между «мы храним конфигурацию в репозитории» и настоящим GitOps: во втором случае агент сверяет состояние постоянно, а не в момент выкатки, и либо чинит расхождение сам, либо громко о нём сообщает.
// Отсюда практика: правки руками на проде считаются аварийными и обязательно возвращаются в репозиторий тем же днём, иначе следующий выкат их молча снесёт - или, как в моём опыте, наоборот, не снесёт, и расхождение поедет дальше. Второй вариант хуже: про него никто не знает.
- дрейф
- расхождение между описанным состоянием и фактическим
- сверка состояния
- постоянная проверка «что описано» против «что есть»
Шаблоны и секреты
Одно и то же приложение едет в несколько сред - отдельных контуров вроде тестового, предпродакшна и боевого, - и описания различаются в мелочах: адреса, лимиты, число экземпляров. Отсюда два подхода. Шаблонизация: один комплект файлов с подстановками и отдельный файл значений на каждую среду. Наложение: базовый комплект плюс небольшие заплатки поверх для каждой среды. Первый гибче и легко превращается в нечитаемую кашу из условий, второй строже и требует, чтобы базовый комплект был аккуратным.
Отдельная и самая болезненная тема - секреты. В репозитории им не место в открытом виде, и это не паранойя: я проверил, что значение переменной из файла описания видно любому, кто может смотреть контейнеры на машине. То есть даже попав в прод, оно остаётся читаемым.
// Рабочих вариантов три. Шифровать значения прямо в репозитории, чтобы в коммите лежал зашифрованный текст, который без ключа не прочитать, а расшифровка происходила уже на месте. Хранить только ссылку на секрет, а сам секрет держать в отдельном хранилище с доступом по ролям. Или подкладывать секреты снаружи, из настроек среды выполнения. Общее у всех трёх одно: в коммит попадает что угодно, кроме самого значения.
- шаблонизация
- один комплект файлов с подстановкой значений для каждой среды
- наложение
- базовый комплект плюс небольшие заплатки под конкретную среду
- хранилище секретов
- отдельная система, откуда значения выдаются по правам
Как отвечать: «Что такое GitOps и чем pull-модель лучше push?»
GitOps это когда единственный источник правды о состоянии инфраструктуры - репозиторий, а изменения в прод попадают только через коммит и слияние. Отсюда бесплатно получаются история, откат возвратом коммита и обязательный просмотр изменений коллегой. Про модели: при push-модели пайплайн после сборки сам заходит в кластер, и значит, наружу выданы ключи от прода. При pull-модели внутри кластера живёт агент, который сам тянет описание, сверяет с фактическим состоянием и приводит в соответствие - ключи наружу не уходят, и это главный аргумент. Второй аргумент про дрейф: агент сверяет постоянно, а не в момент выкатки. Я специально проверял этот эффект: поменял настройку живого контейнера руками, применил описание заново - и расхождение осталось, потому что само описание не менялось. Без постоянной сверки репозиторий - просто место, где лежат файлы.
Ответ называет суть, оба аргумента за pull и подкрепляет их воспроизведённым дрейфом. Разговор про дрейф сразу показывает, что человек это эксплуатировал.
На чём валятся
- −Называют GitOps любое хранение конфигурации в репозитории, без постоянной сверки состояния.
- −Считают, что повторное применение описания само исправит правку, сделанную руками.
- −Правят прод руками в аварию и не возвращают изменение в репозиторий.
- −Кладут секреты в репозиторий открытым текстом, полагая, что приватного репозитория достаточно.
- −Выдают пайплайну ключи от прода там, где хватило бы агента внутри кластера.
Проверьте себя
Пять вопросов из банка по этой подтеме. Всего их 7, остальные разбираются в тренажёре.
- Все манифесты в git, включая Secret с паролем базы. Репозиторий приватный. Что с этим не так?A)Секрет в манифесте не переживёт применение агентомB)Kubernetes откажется принимать секрет из внешнего источникаC)Пароль открыт всем с доступом и живёт в историиD)Приватный репозиторий подходит, если включить подпись коммитов
показать ответ и разбор
+C)Пароль открыт всем с доступом и живёт в истории// разбор: Приватность репозитория — не защита: доступ есть у всей команды и у CI, значение видно в истории и в форках, а ротация означает новый коммит с новым паролем. Рабочие схемы: шифрование значений в репозитории (SOPS с ключом в KMS), sealed-secrets с расшифровкой только внутри кластера или внешний оператор, который подставляет секреты из Vault по ссылке.
- Инженер вручную поправил живой Deployment через kubectl edit. Через минуту GitOps-агент вернул всё как было. Почему?A)Агент сводит кластер к состоянию из gitB)kubectl edit вообще не применяется в GitOps-кластерахC)Агент увидел ошибку в правке и откатил её как невалиднуюD)Изменение потерялось из-за перезапуска пода агента
показать ответ и разбор
+A)Агент сводит кластер к состоянию из git// разбор: В GitOps желаемое состояние живёт в git, а агент (Argo CD, Flux) постоянно сравнивает его с кластером и сводит расхождение (drift) обратно к git. Ручная правка kubectl edit — это дрейф, который агент откатит при следующей синхронизации. Чтобы изменение осталось, его коммитят в git. Плюс такого подхода: конфиг-дрейф самозалечивается, а история изменений — в git.
- Все манифесты в git, и туда же хотят класть Secret с паролем базы. Репозиторий приватный. Как правильно?A)Приватного репозитория достаточно, класть как естьB)Закодировать пароль в base64 перед коммитом в gitC)Держать пароль в имени файла, а не в его содержимомD)Шифровать (SOPS/sealed-secrets) или внешний стор
показать ответ и разбор
+D)Шифровать (SOPS/sealed-secrets) или внешний стор// разбор: GitOps любит «всё в git», но открытый секрет в репозитории — это утечка: доступ есть у всех с правами на репо, и он остаётся в истории навсегда. Решения: шифровать секреты в git (SOPS с KMS, Sealed Secrets — в git лежит зашифрованное, расшифровывает только контроллер в кластере) или хранить снаружи (Vault, External Secrets, облачный секрет-менеджер) и подтягивать в рантайме. Приватность репо и base64 защитой не являются.
- В GitOps выкатили релиз, он оказался битым. Как правильно откатиться?A)kubectl rollout undo прямо в кластере, минуя gitB)git revert релиза — агент сведёт кластер к прошломуC)Вручную поправить образ на живом Deployment через kubectlD)Удалить приложение из агента и создать заново из git
показать ответ и разбор
+B)git revert релиза — агент сведёт кластер к прошлому// разбор: В GitOps источник истины — git, поэтому и откат делают через git: git revert коммита релиза (или переключение на прошлый тег), а агент синхронизацией приводит кластер к откаченному состоянию. Правки прямо в кластере (rollout undo, kubectl edit) — это дрейф, который агент перезатрёт версией из git. Плюс: откат проходит через ту же историю и ревью, что и накат.
- Что такое манифест в Kubernetes?A)Отчёт о текущей загрузке кластераB)Журнал выполненных в кластере командC)Список установленных на ноду пакетовD)YAML-описание желаемого состояния объекта
показать ответ и разбор
+D)YAML-описание желаемого состояния объекта// разбор: В манифесте описывают, что должно быть: тип объекта, имя, число реплик, образ. Кластер работает декларативно — вы отдаёте желаемое состояние, а контроллеры сами приводят к нему фактическое.
дальше
Теорию прочитали. Навык ставится повторением
В Сеньорчике эта подтема идёт в ежедневных сессиях: движок возвращает её, пока ответы не станут уверенными, и ведёт прогресс отдельно по каждой подтеме. Теория внутри тоже бесплатна, лимит только на количество вопросов в день.