Пристрій Helm та його підводні камені

Пристрій Helm та його підводні камені
Typhon freight hauler concept, Anton Swanepoel

Мене звуть Дмитро Сугробов, я розробник у «Леруа Мерлен». У статті розповім, навіщо потрібен Helm, як він спрощує роботу з Kubernetes, що змінилося у третій версії і як за його допомогою оновлювати програми у продакшені без простою.

Це конспект із мотивів виступу на конференції @Kubernetes Conference by Mail.ru Cloud Solutions - Якщо не хочете читати, дивіться відео.

Чому ми використовуємо Kubernetes у продакшені

"Леруа Мерлен" - лідер на ринку DIY-рітейлу в Росії та Європі. У нашій компанії більше ста розробників, 33 000 внутрішніх співробітників та величезна кількість людей, які відвідують гіпермаркети та сайт. Для того щоб зробити їх щасливими, ми вирішили дотримуватися стандартних підходів в індустрії. Розробляти нові програми, використовуючи мікросервісну архітектуру; для ізоляції оточень та правильної доставки використовувати контейнери; а для оркестрації використати Kubernetes. Ціна використання оркестраторів стрімко дешевшає: на ринку зростає кількість інженерів, які володіють технологією, з'являються провайдери, які пропонують Kubernetes як сервіс.

Все, що робить Kubernetes, звичайно, можна зробити іншими способами, наприклад, обмазавши скриптами якийсь Дженкінс і docker-compose, але навіщо ускладнювати життя, якщо є готове та надійне рішення? Тому ми прийшли до Kubernetes і вже рік його використовуємо у продакшені. Зараз у нас двадцять чотири кластери Kubernetes, найстарішому з них більше року, у ньому близько двохсот подів.

Прокляття великої кількості YAML-файлів у Kubernetes

Для запуску мікросервісу в Kubernetes створимо щонайменше п'ять YAML-файлів: для Deployment, Service, Ingress, ConfigMap, Secrets і відправимо в кластер. Для наступного додатка напишемо той самий пакет ямликов, з третім ще один і так далі. Помножимо кількість документів на кількість оточень, вже отримаємо сотні файлів, і це ще не зважаючи на динамічні оточення.

Пристрій Helm та його підводні камені
Adam Reese, core maintainer Helm, ввів поняття «Цикл розробки в Kubernetes», яке виглядає так:

  1. Copy YAML — копіювати файл YAML.
  2. Paste YAML – вставити його.
  3. Fix Indents - полагодити відступи.
  4. Repeat – повторити заново.

Варіант робочий, але багато разів копіювати YAML-файли. Щоб цей цикл змінити, і вигадали Helm.

Що таке Helm

По-перше, Helm - пакетний менеджер, що допомагає знаходити та встановлювати потрібні програми. Для встановлення, наприклад, MongoDB не потрібно заходити на офіційний сайт і завантажувати бінарники, достатньо виконати команду helm install stable/mongodb.

По-друге, Helm - шаблонізатор, допомагає налаштувати файли. Повернемося до ситуації з YAML-файлами Kubernetes. Простіше написати той же файл YAML, додати в нього деякі placeholder-и, в які Helm підставить значення. Тобто замість великого набору ямликов буде набір темплейтів (шаблонів), які в потрібний момент підставляться потрібні значення.

По-третє, Helm - майстер з розгортання. З його допомогою можна встановлювати, відкочувати та оновлювати програми. Давайте розберемося, як це робити.

Пристрій Helm та його підводні камені

Як користуватися Helm для деплою власних програм

Встановимо Helm клієнт на комп'ютер, слідуючи офіційній інструкції. Потім створимо набір YAML-файлів. Замість вказівки конкретних значень залишимо плейсхолдери, які у майбутньому Helm заповнить інформацією. Набір таких файлів називається Helm чарт. До консольного клієнта Helm його можна відправити трьома способами:

  • вказати татку з шаблонами;
  • запакувати в .tar архів та вказати на нього;
  • покласти шаблон у віддалений репозиторій та додати посилання на репозиторій у Helm клієнт.

Ще потрібен файл зі значеннями – values.yaml. Дані будуть підставлятися в шаблон. Створимо та його.

Пристрій Helm та його підводні камені
У другій версії Helm є додатковий серверний додаток - Tiller. Воно висить зовні Kubernetes і чекає запити від Helm-клієнта, а під час виклику підставляє потрібні значення шаблон і відправляє в Kubernetes.

Пристрій Helm та його підводні камені
Helm 3 влаштований простіше: замість обробки шаблонів на сервері інформація тепер обробляється повністю на стороні Helm-клієнта і відправляється безпосередньо в Kubernetes API. Це спрощення підвищує безпеку кластера та полегшує схему викочування.

Як все це працює

Запускаємо команду helm install. Вкажемо назву релізу програми, дамо шлях до values.yaml. Наприкінці вкажемо репозиторій, де лежить чарт і назва чарту. У прикладі це «lmru» та «bestchart» відповідно.

helm install --name bestapp --values values.yaml lmru/bestchart

Виконання команди можливе лише один раз, при повторному виконанні замість install потрібно використовувати upgrade. Для простоти замість двох команд можна виконувати команду upgrade з додатковим ключем --install. При першому виконанні Helm відправить команду на встановлення релізу, а надалі його оновлюватиме.

helm upgrade --install bestapp --values values.yaml lmru/bestchart

Підводні камені деплою нових версій програми з Helm

У цьому місці оповідання я граю із залом у «Хто хоче стати мільйонером», і ми з'ясовуємо, як змусити Helm оновити версію програми. Дивитися відео.

Коли я вивчав роботу Helm, мене здивувала дивна поведінка під час спроби оновлення версій запущених програм. Код програми оновив, докер-реджистри завантажив новий образ, відправив команду на деплой - і нічого не сталося. Нижче кілька не зовсім вдалих способів оновлення програм. Вивчаючи кожен із них докладніше, починаєш розуміти внутрішній пристрій інструменту та причини такої не очевидної поведінки.

Спосіб 1. Не міняти інформацію з моменту останнього запуску

Як говорить офіційний сайт Helm, "Kubernetes чарти бувають великими і складними, тому Helm намагається зайвий раз нічого не чіпати". Тому, якщо оновити latest-версію образу програми в docker registry та виконати команду helm upgrade, то нічого не станеться. Helm буде думати, що нічого не змінилося і посилати в Kubernetes команду на оновлення програми не потрібно.

Тут і далі тег latest показаний виключно як приклад. При вказівці цього тегу Kubernetes щоразу завантажуватиме образ із docker registry, незалежно від параметра imagePullPolicy. Використання latest у продакшені небажане та викликає побічні ефекти.

Спосіб 2. Оновлювати LABEL в image

Як написано в тій самій документації, «Helm буде оновлювати програму, тільки якщо вона змінилася з моменту останнього релізу». Логічним варіантом для цього буде оновлення мітки LABEL у самому докер-образі. Однак Helm не заглядає в образи додатків і не має уявлення про зміни в них. Відповідно, при оновленні міток в образі Helm про них не дізнається, і команда оновлення програми Kubernetes не надійде.

Спосіб 3. Використовувати ключ --force

Пристрій Helm та його підводні камені
Звернемося до мануалів і шукаємо потрібний ключик. Найбільше за змістом підходить ключ --force. Незважаючи на назву, що говорить, поведінка відрізняється від очікуваного. Замість форсованого оновлення програми, його реальне призначення - відновлення релізу, що знаходиться в статусі FAILED. Якщо не використовувати цей ключ, потрібно послідовно виконувати команди helm delete && helm install --replace. Натомість пропонується використовувати ключ --forceщо автоматизує послідовне виконання цих команд. Більше інформації у цьому пулл-реквесті. Для того, щоб сказати Helm все-таки оновити версію програми, на жаль, цей ключ не підійде.

Спосіб 4. Змінювати labels безпосередньо в Kubernetes

Пристрій Helm та його підводні камені
Оновлення label безпосередньо в кластері за допомогою команди kubectl edit - погана ідея. Ця дія призведе до неконсистентності інформації між працюючим додатком і тим, що відправилося на деплой. Поведінка Helm при депло в цьому випадку відрізняється від його версії: Helm 2 нічого робити не буде, а Helm 3 задепло нову версію програми. Для розуміння причини потрібно зрозуміти, як працює Helm.

Як влаштований Helm

Для визначення, чи змінилася програма з моменту останнього релізу, Helm може скористатися:

  • запущеним додатком у Kubernetes;
  • новим values.yaml та актуальним чартом;
  • внутрішньою інформацією Helm про релізи.

Для найцікавіших: де Helm зберігає внутрішню інформацію про релізи?Виконавши команду helm history, ми отримаємо всю інформацію про версії, встановлені за допомогою Helm.

Пристрій Helm та його підводні камені
Ще є докладна інформація про відправлені шаблони та значення. Ми можемо її запитати:

Пристрій Helm та його підводні камені
У другій версії Helm ця інформація лежить у тому ж неймспейсі, де запущено Tiller (за замовчуванням — kube-system), у ConfigMap, позначеному міткою «OWNER=TILLER»:

Пристрій Helm та його підводні камені
У момент появи третьої версії Helm інформація переїхала в секрети, причому в той же неймспейс, де запущено програму. Завдяки цьому можна запускати одночасно кілька додатків у різних неймспейсах з однаковою назвою релізу. У другій версії це був сильний біль голови, коли неймспейси ізольовані, але можуть один на одного впливати.

Пристрій Helm та його підводні камені

Другий Helm, коли намагається зрозуміти, чи потрібне оновлення, використовує лише два джерела інформації: те, що йому надали зараз, та внутрішню інформацію про релізи, що лежить у ConfigMap.

Пристрій Helm та його підводні камені
Третій Helm використовує стратегію three-way merge: на додаток до тієї інформації враховує ще й додаток, який працює зараз у Kubernetes.

Пристрій Helm та його підводні камені
З цієї причини стара версія Helm не нічого не робитиме, оскільки не враховує інформацію програми в кластері, а ось Helm 3 отримає зміни і відправить новий додаток на деплой.

Спосіб 5. Використовувати ключ-recreate-pods

За допомогою ключа --recreate-pods можна досягти того, що спочатку планувалося отримати за допомогою ключа --force. Контейнери перезапустяться і, згідно з політикою imagePullPolicy: Always для тега latest (про це у виносці вище), Kubernetes скачає і запустить нову версію образу. Робити це буде не найкращим чином: не враховуючи StrategyType деплойменту, різко вимкне всі старі інстанси програми і піде запускати нові. Під час перезапуску система не працюватиме, користувачі страждатимуть.

У самому Kubernetes схожа проблема також існувала тривалий час. І ось, через 4 роки після відкриття Питання, проблему виправили, і з 1.15 версії Kubernetes з'являється можливість rolling-restart подов.

Helm просто вимикає всі програми і запускає поруч нові контейнери. У продакшені так робити не можна, щоб не викликати простий додаток. Таке потрібне лише для потреб розробки, можна виконувати лише в stage-оточеннях.

Як оновити версію програми за допомогою Helm?

Змінюватимемо значення, що відправляються в Helm. Як правило, це значення, що підставляються місце тега образу. У випадку latest, що часто використовується для непродуктивних оточень, в ролі змінної інформації виступає анотація, яка для самого Kubernetes марна, а для Helm виступатиме сигналом до необхідності оновлення програми. Варіанти заповнення значення інструкції:

  1. Рандомне значення за допомогою стандартної функції - {{ randAlphaNum 6 }}.
    Є нюанс: після кожного деплою з використанням чарту з такою змінною значення анотації буде унікальним, і Helm думатиме, що є зміни. Виходить, завжди будемо перезапускати програму, навіть якщо не змінили її версію. Це не критично, тому що простою не буде, але все ж таки неприємно.
  2. Вставляти поточну дату та час - {{ .Release.Date }}.
    Варіант схожий на рандомне значення із постійно унікальною змінною.
  3. Більш правильний спосіб використовувати контрольні суми. Це SHA образу або SHA останнього комміта в гіті {{ .Values.sha }}.
    Їх треба буде підраховувати і відправляти в Helm клієнт на зухвалій стороні, наприклад, у Дженкінсі. Якщо програма змінилася, те й контрольна сума зміниться. Отже, Helm буде оновлювати програму лише тоді, коли потрібно.

Підсумуємо наші спроби

  • Helm робить зміни менш інвазивним способом, тому будь-яка зміна на рівні образу програми в Docker Registry не призведе до оновлення: після виконання команди нічого не відбудеться.
  • Ключ --force використовується для відновлення проблемних релізів та не пов'язаний із примусовим оновленням.
  • Ключ --recreate-pods примусово оновить програми, але зробить це вандальним способом: різко вимкне всі контейнери. Від цього постраждають користувачі, на продажі так робити не варто.
  • Безпосередньо вносити зміни до кластера Kubernetes за допомогою команди kubectl edit не треба: порушимо консистентність, а поведінка відрізнятиметься залежно від версії Helm.
  • З виходом нової версії Helm з'явилося багато нюансів. Ісусі в репозиторії Helm описані зрозумілою мовою, вони допоможуть розібратися в деталях.
  • Додавання змінної інструкції в чарт зробить його гнучкішим. Це дозволить викочувати програму правильно, без простою.

Думка з розряду «світ у всьому світі», що працює у всіх сферах життя: читайте інструкцію перед застосуванням, а чи не після. Тільки володіючи повною інформацією, вдасться будувати надійні системи та робити користувачів щасливими.

Інші посилання на тему:

  1. Знайомство з Кермо 3
  2. Офіційний сайт Helm
  3. Репозиторій Helm на GitHub
  4. 25 корисних інструментів Kubernetes: розгортання та керування

Ця доповідь вперше прозвучала на @Kubernetes Conference by Mail.ru Cloud Solutions. Дивіться відео інших виступів та підписуйтесь на анонси заходів у Telegram Навколо Kubernetes у Mail.ru Group.

Джерело: habr.com

Додати коментар або відгук