Как я рефакторила Ansible-плейбуки с помощью нейросети. Плюсы и минусы ChatGPT

vimlkqq2o8gkxdzqqsmpct3-rxg.png


Работать с большими Ansible-плейбуком не всегда удобно — в нем трудно читать и переписывать код. В этой статье я поделюсь опытом — расскажу, как «распилила» плейбук на 1000 строк с помощью ChatGPT и как нейросеть справилась с этой задачей. Подробности — под катом.

Привет, я Маша, младший системный администратор облачной инфраструктуры Selectel. Наша команда обслуживает «железную» инфраструктуру облака, мы деплоим серверы для смежных команд и тестируем новое оборудование.

Мы используем в работе разные инструменты, в том числе Ansible, он подходит для проверок серверов на этапе тестирования. Для этих целей у нас есть несколько плейбуков. Один проверяет правильность подключения портов сервера. Другой сверяет информацию с сервера — правильно ли она занесена в систему документирования оборудования Netbox, и исправляет ошибки.

Один из наших главных плейбуков — основные проверки железа и ПО сервера, мы называем его «чек-листом». Он проверяет прошивки дисков и сетевых карт, версии ОС и количество алертов на хосте, но на самом деле проверок гораздо больше. Чек-лист используем не только мы, но и дежурные из Cloud Duty при обслуживании аварийных серверов — эта команда занимается авариями и инцидентами. Он применяется буквально во всем продакшене, особенно часто при вводе новых хостов.

Изначально этот плейбук был структурирован, но постепенно в него добавляли новые и новые проверки, в итоге он начал занимать тысячи (!) строк кода. Его стало тяжело обслуживать и дебажить, было сложно отследить зависимости. Например, переменная могла объявляться в одном месте, а использоваться в другом.

7z5ujephkhpn3i1qzfzodrff2vw.png


Есть негласное правило: не писать плейбуки на такое количество задач и строк, поэтому один из наших ведущих системных администраторов решил «распилить» чек-лист.

Что предстояло сделать:

  • создать структуру;
  • разделить проверки по иерархии в зависимости от того, что они проверяют;
  • убрать легаси в коде (например, связки Bash + Ansible);
  • убрать все сообщения об ошибках в конец плейбука и перенести его в роль.


Это должно было «освежить» плейбук: упростить дебаг и обслуживание, улучшить структуру. А еще такой «распил» генерирует большое количество разных задач на Ansible для джунов — таких сотрудников было много, и не все их них сталкивались с Ансиблом.

Проверки я писала сама. Пробовала использовать ChatGPT, но отказалась от этой идеи. Нейросеть не всегда может понять, что конкретно тебе нужно и что у тебя есть из наработок, поэтому мне было легче написать задачу самой. Но зато я использовала ChatGPT для дебага — об этом дальше.

Важный момент в работе с ChatGPT — безопасность. Нужно помнить, что все, что ты скармливаешь нейросети, идет на серверы Microsoft и потенциально может быть слито и использовано против вас. Чувствительными данными разбрасываться точно не стоит. Я это в голове держу постоянно.


Используйте навигацию, если не хотите читать текст полностью:

→ Проверка дисков на хосте
→ Проверка процессоров
→ Проверка на сетевые карты
→ Проверка на SWAP-раздел
→ Плюсы и минусы работы с СhatGPT
→ Заключение

Проверка дисков на хосте


Одной из задач была проверка количества дисков на хосте. Она достаточно проста. Сначала отдельной переменной задаем допустимые значения количества дисков для конкретного типа хостов, например ss. Потом смотрим, соответствует ли количество дисков допустимому значению Если нет, в массив сообщений об ошибках добавляем описание проблемы.

nwlieefpl0cxa2zmeckfqq9vlsm.png
- name: Set disks for ss
  become: true
  ansible.builtin.set_fact:
    allowed_disk_amount: [26, 27, 28, 38, 40]
  when:
    - "'device_roles_ss' in group_names"

- name: Check disks amount
  vars:
    disks_amount: "{{ facter_disks|length }}"
  ansible.builtin.set_fact:
    failed_messages: "{{ failed_messages + ['Check amount of disks in host, got {{ disks_amount|string }} , but required for this type of host: {{ allowed_disk_amount|string }}' ] }}"
  when: disks_amount not in allowed_disk_amount

У нас Ansible обернут в Линтер. Когда я загрузила код в GitLab, Линтер выдал мне стену сообщений. Как обычный человек, я прочитала то, что было в самом начале, и только после этого обратила внимание на саму суть.

WARNING  Listing 8 violation(s) that are fatal

roles/cops/checklist/tasks/hardware/disks.yaml:166 Task/Handler: Check disks amount
package-latest: Package installs should not use latest.
roles/cops/netbox_sd/tasks/install.yml:16 Task/Handler: install netbox_sd
package-latest: Package installs should not use latest.
roles/legacy/libvirt-exporter/tasks/install.yml:16 Task/Handler: install libvirt-exporter
package-latest: Package installs should not use latest.
roles/legacy/udping_agent/tasks/install.yml:16 Task/Handler: install udping-agent
package-latest: Package installs should not use latest.
roles/legacy/udping_collector/tasks/install.yml:16 Task/Handler: install udping-collector
You can skip specific rules or tags by adding them to your configuration file:
# .config/ansible-lint.yml
warn_list:  # or 'skip_list' to silence them completely
  - experimental  # all rules tagged as experimental
  - no-jinja-nesting  # Nested jinja pattern.
Finished with 1 failure(s), 7 warning(s) on 684 files.
Cleaning up project directory and file based variables
00:00
ERROR: Job failed: command terminated with exit code 1


2dwxywwpkd-g0fm8fxhourfomfw.png


Я закинула кусок кода, на который жаловался Линтер, и само сообщение об ошибке. Вот каким вышел мой диалог с нейросетью:

Masha:

Помоги, пожалуйста, с Ансиблом, Линтер пишет:

roles/cops/checklist/tasks/hardware/disks.yaml:166
Task/Handler: Check disks amount
package-latest: Package installs should not use latest.

кусок кода:

- name: Check amount
ansible.builtin.set_fact:
failed_messages: "{{ failed_messages + ['Check amount, got {{ facter_disks|length|string }}, but required: {{ allowed_disk_amount|string }}' ] }}"
when: facter_disks|length not in allowed_disk_amount

ChatGPT:

Ошибка, указанная Линтером, связана с использованием ключевого слова «latest» при установке пакетов. Вместо этого рекомендуется указывать конкретную версию пакета. Чтобы исправить ошибку, вам необходимо изменить код …

- name: Install package
ansible.builtin.package:
name: package-name
state: present
version: specific-version


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

Masha:

Но тут же нет установки пакетов?

ChatGPT:

Извините за недоразумение. Похоже, я неправильно понял контекст вашего вопроса. В данном случае ошибка Линтера связана с установкой пакета `package-latest`

** (пишет то же самое и предлагает тот же код)
**здесь я заметила, что скинула не ту ошибку, и отправила нужную**

Masha:

Неправильно увидела, он жалуется на no-jinja-nesting: Nested jinja pattern.

** ещё несколько попыток ChatGPT, но по итогу он обнаружил и решил проблему **

ChatGPT:

К сожалению, ошибка «no-jinja-nesting: Nested jinja pattern» остается в коде, потому что в данном случае используется вложенный шаблон Jinja внутри ключа `failed_messages`. Линтер ansible-lint ругается на такую структуру, поскольку вложенные шаблоны Jinja не поддерживаются.

Один из способов исправить это — использовать фильтр `format` для форматирования строки. Вот обновленная версия кода без вложенных шаблонов Jinja.

- name: Check amount
vars:
disks_amount: "{{ facter_disks|length }}"
failure_message: "Check amount {{ disks_amount|string }}, but required: {{ allowed_disk_amount|string }}"
ansible.builtin.set_fact:
failed_messages: "{{ failed_messages + [failure_message] }}"
when: disks_amount not in allowed_disk_amount

ChatGPT:

В этом коде мы создаем переменную `failure_message`, которая содержит отформатированное сообщение об ошибке. Затем мы добавляем это сообщение в список `failed_messages` без использования вложенных шаблонов Jinja.


В итоге ChatGPT не сразу, но решил проблему, вывел вложенный шаблон в отдельную переменную и исправил проверку. Как стала выглядеть проверка:

- name: Check disks amount
  vars:
    disks_amount: "{{ facter_disks|length }}"
    failed_disks_message: "Check amount of disks in host, got {{ disks_amount|string }} , but required for this type of host: {{ allowed_disk_amount|string }}"
  ansible.builtin.set_fact:
    failed_messages: "{{ failed_messages + [ failed_disks_message ] }}"
  when: disks_amount not in allowed_disk_amount

Ошибка простая и решение тоже, но даже тут я сэкономила время на дебаге. Стоит отметить, что проблема в большей степени завязана именно на Python, с которым я до этого практически не работала. Неизвестно, как долго я бы решала проблему сама. Можно было, например, почитать документацию по Jinja, которая занимает не одну страницу текста.

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

- name: Check disks amount
  vars:
    disks_amount: "{{ facter_disks|length }}"
  ansible.builtin.set_fact:
    failed_messages: "{{ failed_messages + ['Check amount of disks in host, got’ + {{ disks_amount|string }} + ’, but required for this type of host: ‘ + {{ allowed_disk_amount|string }}' ] }}"
  when: disks_amount not in allowed_disk_amount

Такой способ тоже сработал бы, но первый вариант удобнее читать.

Итог: ChatGPT сэкономил мне время на решение проблем с Python и помог разобраться с шаблонами Jinja, с которыми я раньше не сталкивалась.

1hdqmj1bvguax5hnugdz0ci_jbw.jpeg

Проверка процессоров


ChatGPT не всегда решает проблему замыленных глаз. Я писала проверку на разные процессоры и память — там было пять разных условий, но при этом ошибки ссылались на конкретные строки кода.

- name: Check for 2 processors
  become: True
  ansible.builtin.set_fact:
    failed_messages: "{{ failed_messages + ['Decrease memory, there are more than 256Gb on host with 2 processors, don't forget SILENCE for 2 days!'] }}"
  when:
    - ansible_processor_count == 2
    - ansible_memtotal_mb > 258000
    - ansible_processor is not search("AMD")
    - ansible_fqdn is search('zone-{1,2,3,7,9}')
    - "'device_roles_cmp' in group_names"
- name: Check for 4 processors
  become: True
    ***
##Ошибки:
yaml: truthy value should be one of [false, true] (truthy)
roles/cops/checklist/tasks/hardware/memory.yaml:2
yaml: truthy value should be one of [false, true] (truthy)
roles/cops/checklist/tasks/hardware/memory.yaml:13 

Код работал, но Линтер снова вывел ошибку, с которой я и пошла к ChatGPT:

Masha:

Мой кусок кода выглядит так:

** отправляю код **

Линтер фейлится:

yaml: truthy value should be one of [false, true] (truthy)
roles/cops/checklist/tasks/hardware/memory.yaml:2
yaml: truthy value should be one of [false, true] (truthy)
roles/cops/checklist/tasks/hardware/memory.yaml:13

как это можно исправить?

**в этот раз у него не особо получилось что-то исправить**

ChatGPT:

Ошибки указывают на то, что ваши условия в блоках `when` должны возвращать либо `true`, либо `false`. Пожалуйста, убедитесь, что ваши условия возвращают одно из этих значений…

** говорю, что не в ту сторону пошёл **

ChatGPT:

Извините за путаницу. Ошибка может быть связана с тем, что условие `ansible_fqdn is search ('zone-{1,2,3,7,9}')` не является булевым выражением, и Линтер считает это неверным. Давайте попробуем …

**также предлагает исправить условия **


В итоге я нашла ошибку сама раньше ChatGPT.

Masha:

Была проблема с тем, что я записала true с большой буквы.

ChatGPT:

Понял, рад, что проблема была решена! Действительно, в YAML true и false должны быть в нижнем регистре.


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

Итог: хотя ChatGPT не помог мне решить проблему, я посмотрела на свои условия с другой стороны. Иногда при дебаге такой уход в сторону полезен для решения проблемы. Ну и лишний раз напоминание: даже ИИ может не спасти вас от невнимательности.

Проверка на сетевые карты


Приведу кейс моего коллеги: нужно было достать из большого JSON-файла строку с версией прошивки сетевой карты. Коллега отправил запрос:

Colleague:

write an ansible loop task to iterate over this dict to show only version line ***json example***

ChatGPT:

You can use the `with_items` loop in Ansible to iterate over the dictionary and display only the «version» lines. Here’s an example playbook to achieve this:

** представляет не очень рабочий плейбук **

Colleague:

write an ansible task to iterate over this dict to show only version line using json_query

ChatGPT:

You can use the `json_query` filter in Ansible to extract the «version» lines from the provided dictionary. Here’s an example task to achieve this:

** представляет новый плейбук с использованием json_query, но тоже нерабочий**


Ни один из вариантов не сработал как нужно, поэтому коллега написал:

nu0q_4itbym59cz2zf9oqmczndw.png

Итог: задачу помог решить наш товарищ по команде. Он написал факт для Ansible, собирающий всю информацию о сетевой карте, в том числе и её прошивку. Более опытный коллега оказался сильнее нейросети.

Можете заметить, в последнем случае запросы были на английском. Но я не заметила разницы в ответах, хотя пробовала спрашивать и на русском, и на английском. Ради интереса попробовала отправить один и тот же запрос на двух языках, чтобы сравнить время ответа. Действительно, на английском ответ приходит быстрее: пять секунд вместо десяти. Но мне кажется, нужно выбирать тот язык, на котором вам удобнее писать запрос и получать ответ.

Проверка на SWAP-раздел


Попросила ChatGPT написать проверку на наличие SWAP-раздела с нуля, без моих наработок. Выбрала самую простую проверку, чтобы точно что-то получилось. Вот как выглядел мой запрос:

Masha:

Привет, я хочу написать проверку в ансибл плейбук. Мне нужно проверить, есть ли на хосте swap раздел, и если есть, то вывести сообщение об этом. Также нужно проверить, есть ли на хосте файл /swap.img, и если есть, то вывести сообщение о его существовании, и что его необходимо освободить (сделать swapoff) и удалить

ChatGPT:

Привет! Для проверки наличия swap раздела и файла /swap.img на хосте с использованием Ansible, вы можете воспользоваться модулем `command` или `shell`.


Вот пример Ansible плейбука для выполнения этих проверок:

---
- name: Проверка swap раздела и файла /swap.img
  hosts: your_target_hosts
  become: true  # Выполнение команд с правами суперпользователя
  tasks:
    - name: Проверить наличие swap раздела
      command: "swapon --show"
      register: swap_result
      ignore_errors: true  # Игнорировать ошибку, если swap раздел не найден
    - name: Вывести сообщение, если swap раздел существует
      debug:
        msg: "Swap раздел существует на хосте."
      when: swap_result.rc == 0  # Проверить код возврата
    - name: Проверить наличие файла /swap.img
      stat:
        path: /swap.img
      register: swap_file_result
    - name: Вывести сообщение, если файл /swap.img существует
      debug:
        msg: "Файл /swap.img существует на хосте. Освободите его и удалите."
      when: swap_file_result.stat.exists  # Проверить существование файла

Также СhatGPT подробно объяснил каждое действие, которое происходит в плейбуке:

ChatGPT:

Этот плейбук выполняет следующие действия:

1. Проверяет наличие swap раздела с использованием команды `swapon --show`. Если swap раздел существует, регистрирует результат в переменной `swap_result`.

2. Выводит сообщение, если swap раздел найден (если код возврата равен 0).

3. Проверяет наличие файла /swap.img с использованием модуля `stat`. Регистрирует результат в переменной `swap_file_result`.

4. Выводит сообщение, если файл /swap.img существует.

Вы можете запустить этот плейбук, указав свои целевые хосты вместо `your_target_hosts`. Не забудьте также настроить соединение с хостами (например, использовать модуль `ansible_ssh_user` и другие соответствующие параметры).

Надеюсь, это поможет! Если у вас есть дополнительные вопросы или нужна дополнительная помощь, дайте знать.


После этого я попросила перевести комментарии и сообщения на английский — мы не используем русский язык в коде. После небольших правок код даже работал. Я разделила большой блок с задачами на отдельные, и всё взлетело, правда с некоторыми оговорками:

TASK [cops/checklist : Check for the existence of a swap partition] *********************************
ok: [cmp15]
ok: [cmp18]
TASK [cops/checklist : Display a message if a swap partition exists] ********************************
ok: [cmp15] => {
    "msg": "Swap partition exists on the host."
}
ok: [cmp18] => {
    "msg": "Swap partition exists on the host."
}
TASK [cops/checklist : Check for the existence of the swap.img file] ********************************
ok: [cmp15]
ok: [cmp18]
TASK [cops/checklist : Display a message if the swap.img file exists] *******************************
ok: [cmp15] => {
    "msg": "The /swap.img file exists on the host. Free it up and delete it."
}
ok: [cmp18] => {
    "msg": "The /swap.img file exists on the host. Free it up and delete it."
}

Из слабых мест был только Линтер, который снова ругался на конкретную ошибку:

WARNING  Listing 1 violation(s) that are fatal
no-changed-when: Commands should not change things if nothing needs doing.
roles/cops/checklist/tasks/hardware/memory.yaml:1 Task/Handler: Check for the existence of a swap partition


Отправила ошибку СhatGPT, и он ее исправил, просто добавив в проверки changed_when: false. Но куда именно их надо было добавлять, все равно пришлось объяснять:

ChatGPT:

Извините за путаницу. Ваш Линтер предупреждает о том, что задача выполняет команду, но не предпринимает никаких изменений, если условие (`when`) не выполняется. Это может быть проблемой в некоторых случаях, когда Ansible рекомендует избегать выполнения лишних команд.

Для устранения предупреждения Линтера можно попробовать изменить ваш плейбук следующим образом:

- name: Check for the existence of a swap partition
command: "swapon --show"
register: swap_result
ignore_errors: true  # Ignore errors if the swap partition is not found
- name: Display a message if a swap partition exists
debug:
msg: "Swap partition exists on the host."
when: swap_result.rc == 0  # Check the return code
- name: Check for the existence of the /swap.img file
stat:
path: /swap.img
register: swap_file_result
changed_when: false  # Set changed_when to false to avoid "changed" status
- name: Display a message if the /swap.img file exists
debug:
msg: "The /swap.img file exists on the host. Free it up and delete it."
when: swap_file_result.stat is defined and swap_file_result.stat.exists  # Check file existence

Добавлен параметр `changed_when: false` в задаче `stat`, который указывает Ansible не помечать задачу как измененную, даже если она выполнена. Это может решить предупреждение Линтера.

Попробуйте внести эти изменения и проверьте, решает ли это вашу проблему. Если у вас по-прежнему есть вопросы или проблемы, дайте мне знать!

Masha:

там же ругань именно на 1 проверку.

ChatGPT

Понял, извините за путаницу. Попробуйте изменить вашу задачу проверки swap раздела следующим образом:

** тут добавляет в первую проверку аналогичный changed_when: false**


Решила сравнить этот вариант со своей проверкой — я сделала ее немного иначе:

- name: Check SWAP on ceph
  become: true
  ansible.builtin.set_fact:
    failed_messages: "{{ failed_messages + ['SWAP on ceph hosts should be deleted'] }}"
  when:
    - ansible_swaptotal_mb > 0
    - ('device_roles_fs' in group_names) or ('device_roles_ms' in group_names) or ('device_roles_cs' in group_names) or ('device_roles_meta' in group_names) or ('device_roles_ss' in group_names)
- name: Register swap.img file on ceph
  stat:
    path: "/swap.img"
  register: swapfile
- name: Generate message if exists
  ansible.builtin.set_fact:
    failed_messages: "{{ failed_messages + ['Swapoff and delete swap.img, it exists'] }}"
  when:
    - swapfile.stat.exists

Задача состоит из двух частей: первая проверяет swap раздел на диске, вторая — файл swap.img. И если первая у нас с ИИ реализована по-разному, я иначе смотрю swap раздел и добавила больше условий, то вторая проверка практически идентична моей:

oq5tqb2aarlzrpwp0v8mfy5b5ty.png


Итог: ChatGPT, хотя и не без изъянов, написал мне заготовку для рабочего плейбука, который я затем доработала. Результат вполне подошел бы в качестве решения. Похоже, нужно уделять запросам особое внимание: чем больше вводных дать, тем качественнее результат.

Плюсы и минусы работы с СhatGPT


Нейросеть справляется с задачами, но не всегда. Какие плюсы и минусы я заметила во время рефакторинга.

Минусы


При использовании СhatGPT всегда нужно помнить о рисках, связанных с конфиденциальностью данных, передачей информации стороннему сервису и возможной устаревшей или неточной информацией.

Риск утечки данных. При работе с моделью можно случайно раскрыть конфиденциальную информацию. Все введенные данные передаются и обрабатываются серверами Microsoft, то есть вы передаете информацию стороннему сервису. Это рискованно, если вы имеете дело с чувствительной информацией, например, с какими-то закрытыми данными, связанными с инфраструктурой дата-центров.

Актуальность информации. Модель обучена на данных, доступных на момент завершения обучения (примерно конец 2021 года). Она не всегда учитывает последние изменения или события. Это может привести к тому, что полученные варианты решения проблем могут быть неактуальными или не учитывать последних разработок в той или иной области.

Плюсы


Решение проблем. ChatGPT действительно помогает решать некоторые задачи, а также взглянуть на проблему с другой стороны. Даже в случае, когда он пошел совсем не туда, он подкинул мне пару идей, как переписать условия, чтобы они стали более легкими для чтения. Когда ты дебажишь, полезно иметь «взгляд со стороны», и нейросеть в этом плане способна помочь.

Экономия времени. Я не знаю, сколько бы времени решала проблемы с Jinja, так как они касаются больше Python, чем Ansible. ChatGPT кажется особенно полезным, если ты не очень хорошо разбираешься в конкретной области.

Это был один из первых моментов, когда я переписывала плейбуки Я часто спотыкалась об ошибки синтаксиса, неправильный парсинг JSON-файлов и т.д. А когда ты делаешь первые шаги, важно и полезно иметь инструмент, который поможет исправить глупости не за три часа, а за пять минут.

Самообучение. ChatGPT помогает построчно объяснить код. Так, можно разобраться в вещах, которые тебе непонятны, а загуглить их невозможно. Например, без привязки к Ansible, я пробовала разобраться и потренироваться в некоторых запросах в Grafana, и нейросеть подробно пояснила непонятные вещи. Не думаю, что ChatGPT и Google способны полностью заменить ментора, но они точно помогут при его отсутствии.

asuaxvva7bvitobavnrxt0vdo9o.gif


Возможно, эти тексты тоже вас заинтересуют:

→ Гиперскейлинг в 2024: самые важные продуктовые обновления за прошлый год
→ Одноплатники начала 2024 года: 5 моделей для самых разных целей
→ Ловкость рук, четкость алгоритма и никакого мошенничества: чек-лист для дизайнеров интерфейсов и фронтенд-разработчиков


Заключение


Мы продолжаем переделывать плейбук, но многие задачи уже сделали. Чек-лист приобрел четкую структуру, его стало легче читать. Мы, джуны из отдела облачной инфраструктуры, поработали с Ansible и набрались опыта. Мне понравилось использовать ChatGPT для дебага и стало интересно, как он справится с другими задачами. Надеюсь, что хорошо — узнаю со временем.

Я использовала именно ChatGPT потому что он был самым популярным, а у меня был временный прямой доступ к нему без VPN. Однако есть и другие нейросети, которые теоретически могут помочь с подобными проблемами:

  • Google Bard,
  • Github Copilot,
  • Codeium,
  • YandexGPT,
  • и другие.


Многие из них можно развернуть локально, например LocalAI. Вариантов достаточно, главное — начать погружаться в тему.

Мне кажется интересным использование СhatGPT для объяснения непонятных вещей, дебага и самообразования. Хотелось бы узнать ваше мнение в комментариях.

© Habrahabr.ru