Автоматизація заміни дисків за допомогою Ansible

Автоматизація заміни дисків за допомогою Ansible

Всім привіт. Я працюю провідним системним адміністратором у ОК та відповідаю за стабільну роботу порталу. Хочу розповісти про те, як ми побудували процес автоматичної заміни дисків, а потім, як виключили з цього процесу адміністратора і замінили його ботом.

Ця стаття є своєрідною транслітерацією виступи на HighLoad+ 2018

Побудова процесу заміни дисків

Спочатку трохи цифр

ОК це гігантський сервіс, яким користуються мільйони людей. Його обслуговують близько 7 тис. серверів, які розташовані у 4 різних дата-центрах. На серверах коштує понад 70 тис. дисків. Якщо скласти їх один на одного, то вийде вежа заввишки понад 1 км.

Жорсткі диски - це компонент сервера, який виходить з ладу найчастіше. За таких обсягів нам доводиться міняти близько 30 дисків на тиждень, і ця процедура стала не дуже приємною рутиною.

Автоматизація заміни дисків за допомогою Ansible

Інциденти

У нас у компанії запроваджено повноцінний інцидент-менеджмент. Кожен інцидент ми фіксуємо у Jira, та тоді вирішуємо та розбираємо. Якщо інцидент був з ефектом для користувачів, ми обов'язково збираємося і думаємо, як швидше реагувати в таких випадках, як знизити ефект і звичайно ж як запобігти повторенню.

Накопичувачі не виняток. За їхнім станом слідкує Zabbix. Ми моніторимо повідомлення в Syslog щодо помилок запису/читання, аналізуємо стан HW/SW-рейдів, слідкуємо за SMART, для SSD обчислюємо знос.

Як змінювалися диски раніше

Коли в Zabbix спалахує якийсь тригер, у Jira створюється інцидент і автоматично ставиться на відповідних інженерів у дата-центрах. Ми так робимо з усіма HW-інцидентами, тобто такими, що потребують будь-якої фізичної роботи з обладнанням у дата-центрі.
Інженер дата-центру – це людина, яка вирішує питання, пов'язані із залізом, відповідає за встановлення, обслуговування, демонтаж серверів. Отримавши тикет, інженер розпочинає роботу. У дискових полицях він змінює диски самостійно. Але якщо він не має доступу до потрібного пристрою, інженер звертається до чергових системних адміністраторів за допомогою. Насамперед потрібно вивести диск із ротації. Для цього потрібно зробити необхідні зміни на сервері, зупинити програми, відмонтувати диск.

Черговий системний адміністратор протягом робочої зміни відповідає за роботу всього порталу. Він розслідує інциденти, займається ремонтом, допомагає розробникам виконувати невеликі завдання. Не займається він лише жорсткими дисками.

Раніше інженери дата-центрів спілкувалися із системним адміністратором у чаті. Інженери слали посилання на Jira-тікети, адміністратор проходив по них, вів лог робіт у якомусь блокноті. Але для таких завдань чати незручні: інформація там не структурована і швидко губиться. Та й адміністратор міг просто відійти від комп'ютера і якийсь час не відповідати на запити, а інженер стояв на сервері з пачкою дисків і чекав.

Але найгірше було в тому, що адміністратори не бачили картини цілком: які існують дискові інциденти, де потенційно може виникнути проблема. Це пов'язано з тим, що всі HW-інциденти віддаємо інженерам. Так, можна було відобразити всі інциденти на дешборді адміністратора. Але їх дуже багато, і адміністратор залучався лише за деякими з них.

Крім того, інженер не міг коректно розставити пріоритети, тому що він нічого не знає про призначення конкретних серверів, розподіл інформації по накопичувачах.

Нова процедура заміни

Перше, що ми зробили, це винесли всі дискові інциденти в окремий тип "HW-диск" і додали до нього поля "ім'я блокового пристрою", "розмір" і "тип диска", щоб ця інформація зберігалася в тикеті, і не доводилося їй постійно обмінюватись у чаті.

Автоматизація заміни дисків за допомогою Ansible
Також ми домовилися, що в рамках одного інциденту змінюватимемо лише один диск. Це суттєво спростило надалі процес автоматизації, збирання статистики та роботу.

Крім цього, додали поле «відповідальний адміністратор». Туди автоматично підставляється черговий сисадмін. Це дуже зручно, бо тепер інженер завжди бачить, хто є відповідальним. Не потрібно йти у календар та шукати. Саме це поле дозволило винести на дешборд адміністратора тікети, в яких, можливо, знадобиться його допомога.

Автоматизація заміни дисків за допомогою Ansible
Щоб усі учасники отримували максимум вигоди від нововведень, ми створили фільтри та дешборди, розповіли про них хлопцям. Коли люди розуміють зміни, вони не дистанціюються від них як від чогось непотрібного. Інженеру важливо знати номер стійки, де знаходиться сервер, розмір і тип диска. Адміністратору потрібно насамперед розуміти, що це за група серверів, який може бути ефект при заміні диска.

Наявність полів та їх відображення – це зручно, але від необхідності використовувати чати нас це не позбавило. Для цього довелося змінювати робочий процес.

Раніше він був таким:

Автоматизація заміни дисків за допомогою Ansible
Сьогодні так продовжують працювати інженери, коли їм не потрібна допомога адміністратора.

Перше, що ми зробили, — запровадили новий статус Розслідувати. У цьому статусі тикет знаходиться, коли інженер ще не вирішив, чи потрібен йому буде адміністратор чи ні. Через цей статус інженер може передати тикет адміністратору. Крім того, цим статусом ми помічаємо тикети, коли потрібно замінити диск, але самого диска на майданчику немає. Таке буває у разі CDN та віддалених майданчиків.

Також ми додали статусу Готовий. У нього тикет перекладається після заміни диска. Тобто, все вже зроблено, але на сервері синхронізується HW/SW RAID. Це може тривати досить багато часу.

Якщо до роботи залучається адміністратор, схема дещо ускладнюється.

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

Потім тикет перекладається Готовий до змін: це сигнал інженеру, що диск можна витягувати Всі поля Jira вже заповнені, інженер знає, який тип і розмір диска. Ці дані проставляються або на попередньому статусі автоматично чи адміністратором.

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

Автоматизація заміни дисків за допомогою Ansible
Додавання нових полів суттєво полегшило нам життя. Хлопці почали працювати зі структурованою інформацією, зрозуміли, що і на якому етапі потрібно робити. Пріоритети стали набагато релевантнішими, оскільки тепер їх виставляє адміністратор.

Відпала потреба у чатах. Звичайно, адміністратор може написати інженеру «тут треба замінити швидше», або «вже вечір, чи встигнеш замінити?». Але ми вже не спілкуємося щодня у чатах із цих питань.

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

Винесений досвід при побудові Workflow

  • При побудові процедури необхідно збирати інформацію із різних джерел.
    Деякі наші адміністратори не знали, що інженер змінює диски самостійно. Деякі вважали, що за синхронізацією MD RAID стежать інженери, хоча хтось із них навіть не мав доступу для цього. Деякі провідні інженери це робили, але не завжди тому, що процес ніде не був описаний.
  • Процедура має бути простою та зрозумілою.
    Людині важко пам'ятати безліч кроків. Найголовніші сусідні статуси у Jira потрібно виносити на головний екран. Можна їх перейменувати, наприклад In progress ми називаємо Ready to change. А інші статуси можна ховати у меню, щоб вони не мозолили очі. Але краще не обмежувати людей, дати можливість зробити перехід.
    Поясніть цінність нововведень. Коли люди розуміють, вони найкраще приймають нову процедуру. Для нас було дуже важливо, щоб люди не кликали весь процес, а йшли ним. Потім ми будували на цьому автоматизацію.
  • Чекати, аналізувати, розумітися.
    У нас пішло близько місяця на побудову процедури, технічну реалізацію, зустрічі та обговорення. А на впровадження — понад три місяці. Я бачив, як люди потихеньку починають користуватися нововведенням. На перших етапах було багато негативу. Але він не залежав від самої процедури, її технічної реалізації. Наприклад, один адміністратор користувався не Jira, а Jira-плагіном у Confluence, і деякі речі були йому недоступні. Показали йому Jira, адмін збільшив продуктивність і за загальним завданням, і по замінах дисків.

Автоматизація заміни дисків

До автоматизації заміни дисків ми підходили кілька разів. Ми вже мали напрацювання, скрипти, але всі вони працювали або в інтерактивному, або в ручному режимі, вимагали запуску. І тільки після впровадження нової процедури ми зрозуміли, що її нам не вистачало.

Оскільки процес заміни у нас розбитий на етапи, за кожним з яких визначено виконавця та список дій, ми можемо включати автоматизацію поетапно, а не відразу повністю. Наприклад, найпростіший етап – Ready (перевірка синхронізації RAID/даних) можна легко делегувати боту. Коли робот трохи навчиться, можна йому дати найбільш відповідальне завдання - введення диска в ротацію тощо.

Зоопарк сетапів

Перш ніж розповідати про роботу, зробимо невеликий екскурс в наш зоопарк інсталяцій. Насамперед він обумовлений гігантським розміром нашої інфраструктури. По-друге, під кожен сервіс ми намагаємось підібрати оптимальну конфігурацію заліза. У нас близько 20 моделей апаратних RAID, в основному LSI та Adaptec, але зустрічаються і HP, і DELL різних версій. Кожен RAID-контролер має власну утиліту управління. Набір команда та видача за ними може відрізнятись від версії до версії кожного RAID-контролера. Там, де не використовуються HW-RAID, може бути mdraid.

Майже всі нові установки ми робимо без дискового резервування. Ми намагаємося більше не використовувати апаратні та софтові RAID, тому що резервуємо наші системи на рівні дата-центрів, а не серверів. Але, звичайно, є багато legacy-серверів, які потрібно підтримувати.

Десь диски в RAID-контролерах прокидаються raw пристрої, десь використовуються JBOD. Існують конфігурації з одним системним диском у сервері, і якщо його потрібно замінити, то доводиться перерозкачувати сервер з установкою ОС і додатків, причому тих же версій, потім додавати конфігураційні файли, запускати програми. Також дуже багато груп серверів, де резервування здійснюється не лише на рівні дискової підсистеми, а у самих додатках.

Загалом у нас понад 400 унікальних груп серверів, на яких працює близько 100 різних програм. Щоб покрити таку величезну кількість варіантів, нам був потрібен багатофункціональний інструмент автоматизації. Бажано з простим DSL, щоб підтримувати, міг не тільки той, хто це написав.

Ми вибрали Ansible, тому що він agentless: не потрібно було готувати інфраструктуру, швидкий старт. До того ж він написаний на Python, який прийнятий як стандарт у команді.

Загальна схема

Давайте розглянемо загальну схему автоматизації з прикладу одного інциденту. Zabbix детектує, що диск sdb вийшов з ладу, спалахує тригер, створюється тикет у Jira. Адміністратор подивився його, зрозумів, що це не дублікат і не false positive, тобто потрібно міняти диск і переводить тикет в In progress.

Автоматизація заміни дисків за допомогою Ansible
Програма DiskoBot, написана на Python, періодично опитує Jira на предмет нових тикетів. Воно помічає, що з'явився новий тикет In progress, спрацьовує відповідний thread, який запускає playbook в Ansible (це робиться для кожного статусу Jira). У цьому випадку запускається Prepare2change.

Ansible відправляється на хост, виводить диск із ротації та репортує статус додатку через Callbacks.

Автоматизація заміни дисків за допомогою Ansible
За результатами бот автоматично перекладає тикет у Ready to change. Інженер отримує повідомлення та відправляється міняти диск, після чого перекладає тикет у Changed.

Автоматизація заміни дисків за допомогою Ansible
За вищеописаною схемою тикет потрапляє назад до робота, той запускає інший playbook, йде на хост і вводить диск у ротацію. Бот закриває тикет. Ура!

Автоматизація заміни дисків за допомогою Ansible
Тепер поговоримо про деякі компоненти системи.

Diskobot

Ця програма написана на Python. Воно вибирає тикети з Jira відповідно до JQL. Залежно від статусу тикета, останній потрапляє до відповідного оброблювача, який у свою чергу запускає відповідний статус Ansible playbook.

JQL та інтервали опитування визначені у файлі конфігурації програми.

jira_states:
  investigate:
    jql: '… status = Open and "Disk Size" is EMPTY'
    interval: 180

  inprogress:
    jql: '…  and "Disk Size" is not EMPTY and "Device Name" is not EMPTY'
 
  ready:
    jql: '… and (labels not in ("dbot_ignore") or labels is EMPTY)'
    interval: 7200

Наприклад, серед тикетів у статусі In progress, вибираються лише ті, у яких заповнені поля Disk size і Device name. Device name - це ім'я блокового пристрою, необхідного для виконання playbook'а. Disk size потрібний для того, щоб інженер знав якого розміру диск необхідний.

Серед тикетів зі статусом Ready відфільтровуються тикети з лейблом dbot_ignore. До речі, Jira лейбли ми використовуємо як для подібної фільтрації, так і для маркування дублікатів тикетів та збору статистики.

У разі збою playbook'а Jira надає лейбл dbot_failed, щоб згодом можна було розібратися.

Взаємодія з Ansible

Додаток взаємодіє з Ansible через Ansible Python API. У playbook_executor ми передаємо ім'я файлу та набір змінних. Це дозволяє тримати Ansible-проект у вигляді звичайних yml-файлів, а не описувати його в Python-коді.

Також в Ansible через *extra_vars* передаються ім'я блокового пристрою, статус тикета, а також callback_url, в якому зашитий issue key - він використовується для callback в HTTP.

Для кожного запуску генерується тимчасовий inventory, що складається з одного хоста та групи, до якої входить цей хост, щоб застосували group_vars.

Ось приклад тяга, в якому реалізований HTTP callback.

Результат виконання playbook'ів ми отримуємо за допомогою callaback(-ів). Вони двох типів:

  • Ansible callback plugin, він надає дані за результатами виконання playbook'а. Там описані завдання, які були запущені, виконані вдало чи невдало. Цей callback викликається після закінчення програвання playbook'а.
  • HTTP callback для отримання інформації під час відтворення playbook'а. В Ansible таску виконуємо POST/GET запроc у строну нашої програми.

Через HTTP callback(-и) передаються змінні, які були визначені при виконанні playbook'а та які ми хочемо зберегти та використовувати у наступних запусках. Ці дані ми пишемо в SQLite.

Також через HTTP callback ми залишаємо коментарі та змінюємо статус тикету.

HTTP callback

# Make callback to Diskobot App
# Variables:
#    callback_post_body: # A dict with follow keys. All keys are optional
#       msg: If exist it would be posted to Jira as comment
#       data: If exist it would be saved in Incident.variables
#       desire_state: Set desire_state for incident
#       status: If exist Proceed issue to that status

  - name: Callback to Diskobot app (jira comment/status)
    uri:
      url: "{{ callback_url }}/{{ devname }}"
      user: "{{ diskobot_user }}"
      password: "{{ diskobot_pass }}"
      force_basic_auth: True
      method: POST
      body: "{{ callback_post_body | to_json }}"
      body_format: json
    delegate_to: 127.0.0.1

Як і багато однотипних таски, ми винесли його в окремий common file і включаємо при необхідності, щоб не повторювати постійно в playbook'ах. Тут фігурує callback_ url, в якому зашиті issue key та host name. Коли Ansible виконує цей POST-запит, бот розуміє, що він прийшов у рамках такого інциденту.

А ось приклад із playbook'а, в якому ми виводили диск із MD-пристрою:

  # Save mdadm configuration
  - include: common/callback.yml
    vars:
      callback_post_body:
        status: 'Ready to change'
        msg: "Removed disk from mdraid {{ mdadm_remove_disk.msg | comment_jira }}"
        data:
          mdadm_data: "{{ mdadm_remove_disk.removed }}"
          parted_info: "{{ parted_info | default() }}"
    when:
      - mdadm_remove_disk | changed
      - mdadm_remove_disk.removed

Цей таск переводить Jira тикет у статус «Ready to change» та додає коментар. Також у змінній mdam_data зберігається список md-пристроїв, з яких був видалений диск, а parted_info — дамп партії від parted.

Коли інженер вставить новий диск, ми зможемо використовувати ці змінні, щоб відновити дамп партій, а також завести диск у ті md-пристрої, з яких він був вилучений.

Ansible check mode

Вмикати автоматику було страшно. Тому ми вирішили запускати всі playbook'и в режимі
сухий біг, в якому Ansible не виконує на серверах жодних дій, а лише емулює їх.

Такий запуск проганяється через окремий callback-модуль, а результат виконання playbook'а зберігаємо у Jira у вигляді коментаря.

Автоматизація заміни дисків за допомогою Ansible

По-перше, це дозволило валідувати роботу бота та playbook'ів. По-друге, підвищило довіру адміністраторів до роботи.

Коли ми пройшли валідацію і зрозуміли, що можна запускати Ansible не тільки в режимі dry run, то зробили в Jira кнопку Run Diskobot для запуску того ж playbook'а з тими самими змінними на тому самому хості, але в звичайному режимі.

Крім того, кнопка використовується для повторного запуску playbook'а у разі його збою.

Структура Playbooks

Я вже згадував, що залежно від статусу Jira-тікету, бот запускає різні playbook'и.

По-перше, так набагато простіше організувати вхід.
По-друге, у деяких випадках це просто необхідно.

Наприклад, при заміні системного диска необхідно в першу чергу піти в систему розгортання, розробити завдання, і після коректного розгортання сервер стане доступний по ssh, і можна на нього накотити програму. Якби ми робили все це в одному плейбуку, то Ansible не зміг би його виконати через відсутність хоста.

Ми використовуємо Ansible-ролі для кожної групи серверів. Тут видно, як організовані playbook(-и) в одній із них.

Автоматизація заміни дисків за допомогою Ansible

Це зручно, тому що відразу зрозуміло, де якісь таски розташовані. У main.yml, який є входом для Ansible-ролі, у нас може бути просто include за статусом тикета або загальні таски, необхідні для всіх, наприклад, проходження ідентифікації або отримання токена.

Investigation.yml

Запускається для тикетів у статусі Investigation та Open. Найважливіше для цього playbook'а – ім'я блокового пристрою. Ця інформація не завжди доступна.

Для її отримання ми аналізуємо Jira summary, останнє значення Zabbix-тригера. Там може бути ім'я блокового пристрою — пощастило. А може утримуватися mount point — тоді потрібно піти на сервер, пропарсувати і обчислити потрібний диск. Також тригер може передати scsi-адресу або якусь іншу інформацію. Але буває й так, що жодних зачіпок немає, і доводиться аналізувати.

З'ясувавши ім'я блокового пристрою, ми збираємо по ньому інформацію про тип і розмір диска для заповнення полів Jira. Також знімаємо інформацію про вендор, модель, прошивку, ID, SMART, і все це вставляємо в коментар в Jira-тікеті. Адміністратору та інженеру тепер не потрібно шукати ці дані. 🙂

Автоматизація заміни дисків за допомогою Ansible

prepare2change.yml

Виведення диска із ротації, підготовка до заміни. Найскладніший, відповідальний етап. Саме тут можна зупинити програму, коли її не можна зупиняти. Або витягнути диск, у якого не вистачало реплік, і тим самим вплинути на користувачів, втратити якісь дані. Тут у нас найбільше перевірок та нотифікацій у чаті.

У найпростішому випадку йдеться про видалення диска з HW/MD RAID.

У більш складних ситуаціях (у наших системах зберігання), коли резервування виконується на рівні програми, необхідно піти до програми API, повідомити про виведення диска, деактивувати його і запустити відновлення.

Ми зараз масово мігруємо у хмара, і якщо сервер хмарний, то Diskobot звертається до API хмари, каже, що він збирається працювати з цим міньйоном - сервером, на якому запущені контейнери, - і просить "змігрувати всі контейнери з цього міньйону". І заразом включає підсвічування диска, щоб інженер відразу побачив, який потрібно витягувати.

changed.yml

Після заміни диска ми насамперед перевіряємо його доступність.

Інженери не завжди ставлять нові диски, тому ми додали перевірку значень SMART, що задовольняють нас.

Які атрибути ми дивимосяReallocated Sectors Count (5) < 100
Current Pending Sector Count (107) == 0

Якщо диск не проходить перевірку, інженер повідомляє про повторну заміну. Якщо все гаразд, підсвічування вимикається, наноситься розмітка та диск вводиться в ротацію.

ready.yml

Найпростіший випадок: перевірка синхронізації HW/SW raid або закінчення синхронізації даних у додатку.

API додатків

Я кілька разів згадував про те, що бот часто звертається до API додатків. Звичайно, не всі додатки мали необхідні методи, тому довелося їх доопрацювати. Ось найважливіші методи, які ми використовуємо:

  • Status. Статус кластера або диска, щоб зрозуміти, чи можна з ним працювати;
  • Start/stop. Активація-деактивація диска;
  • Migrate/restore. Міграція та відновлення даних під час та після заміни.

Винесений досвід з Ansible

Я дуже люблю Ansible. Але часто, коли дивлюся на різні opensource-проекти і бачу, як люди пишуть playbook'і, мені стає трохи страшно. Складні логічні переплетення з when/loop, відсутність гнучкості та ідемпотентності внаслідок частого використання shell/command.

Ми вирішили максимально спростити, скориставшись перевагою Ansible - модульністю. На самому верхньому рівні знаходяться playbook'і, їх може писати будь-який адміністратор, сторонній розробник, який трохи знає Ansible.

- name: Blink disk
  become: True
  register: locate_action
  disk_locate:
      locate: '{{ locate }}'
      devname: '{{ devname }}'
      ids: '{{ locate_ids | default(pd_id) | default(omit) }}'

Якщо якусь логіку складно реалізувати в playbook'ах, ми виносимо її в Ansible-модуль чи фільтр. Скрипти можуть бути написані як на Python, так і будь-якою іншою мовою.

Їх легко та швидко писати. Наприклад, модуль підсвічування диска, приклад використання якого наведено вище, складається з 265 рядків.

Автоматизація заміни дисків за допомогою Ansible

На нижньому рівні знаходиться бібліотека. Для цього проекту ми написали окрему програму, свого роду абстракцію над апаратними та софтовими RAID, які виконують відповідні запити.

Автоматизація заміни дисків за допомогою Ansible

Найсильніші сторони Ansible – це простота та зрозумілі playbook'и. Я вважаю, що потрібно цим користуватися і не генерувати страшні yaml-файли та величезну кількість умов, shell-коду та лупів.

Якщо ви захочете повторити наш досвід з Ansible API, майте на увазі дві речі:

  • Playbook_executor і взагалі playbook'у не можна передати таймаут. Є тайм-аут на ssh-сесії, але немає тайм-аут на playbook. Якщо ми намагаємося відмонтувати диск, який у системі вже не існує, playbook буде виконуватися нескінченно, тому довелося обернути його запуск в окремий wrapper та вбивати по таймууту.
  • Ansible працює на базі fork-процесів, тому його API не є безпечним. Ми запускаємо всі наші playbook'і однопоточно.

У результаті нам вдалося автоматизувати заміну близько 80% дисків. Загалом швидкість заміни зросла вдвічі. Сьогодні адміністратор лише дивиться на інцидент і ухвалює рішення, чи потрібно міняти диск чи ні, а потім робить один клік.

Але тепер ми починаємо стикатися з іншою проблемою: деякі нові адміністратори не знають, як міняти диски. 🙂

Джерело: habr.com

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