Здравствуйте. Меня зовут Николай, и сегодня я расскажу, как мне пригодились Ansible и Python для работы с Yandex Cloud, и совсем не пригодился Terraform. 

Автоматизация по-взрослому

Команда DevOps-инженеров в компании Nobilis.Team участвует во многих проектах по интеграции CRM (BPMSoft, ELMA365, GreenData). Используем Terraform, Ansible, Bash, Python для автоматизации процессов, код доставляем через GitlabCi, а рабочая среда для всех приложений — Docker и Kubernetes. Возможны варианты, но в целом основа остаётся постоянной. 

В нашем портфолио есть один интересный и сложный клиент, у которого множество сервисов, несколько независимых команд разработки и сотни технологических процессов. И всё это плотно завязано на использование сервисов в Yandex Cloud.   

Рутинные операции наша команда автоматизировала через Ansible. Где-то использовали Bash, но дозировано и строго для разовых операций. Кода для Terraform’а почти нет, так как у клиента собственная команда для работы с облачной инфраструктурой, а для тех действий, которые требуются от DevOps-инженеров на проекте, Terraform не предлагает нужной функциональности. 

Регулярные операции по обслуживанию, которые мы обернули в плейбуки Ansible, часто используют чувствительную информацию. Но в силу специфики проекта мы не могли использовать Vault или аналогичное хранилище секретов и до недавнего времени передавали секреты, используя комбинацию из утилит SOPS и Age. Не очень удобно и потенциально небезопасно. С этим надо было что-то делать. 

Ansible и Lockbox 

Единственное хранилище секретов, которое можно было использовать — это облачный Yandex Lockbox. Необходимо было придумать, как работать с этим хранилищем в рамках текущего стека на проекте. Требовалось создавать секреты с большим количеством ключей внутри, обновлять их и, самое главное, быстро и безопасно получать к ним доступ при выполнении Ansible-плейбуков. Задача была сложная и многоуровневая, но в конце концов я её решил. Сейчас расскажу, как это было и что получилось в итоге. 

Первый подход с использованием «секретницы» через Terraform получился не очень удачным и удобным. Причина была в выборе инстремента. Дело в том, что продукт Hashicorp декларативен. Это, безусловно, плюс для работы с облачной инфраструктурой в целом, но для тонкой настройки определённых сервисов этого недостаточно. Кроме этого, есть ещё минусы: необходимо держать отдельный репозиторий с кодом для Terraform, хранить и передавать его state, каждый раз использовать модули shell, terraform, потом обрабатывать их вывод… Лучше уж использовать Bash. 

Второй подход я совершил с использованием нативного облачного инструмента - консольной утилитой Yandex CLI, которая позволяет управлять облачными сервисами. Но результат, который появился в итоговом плейбуке меня не устроил: опять были вызовы shell,снова обработки вывода, регистры. Получались объёмные сценарии, которые было сложно читать и ещё сложнее встраивать в текущую систему автоматизации процессов. 

Однако, обе попытки навели меня на мысль. Если Terraform использует API облака и CLI использует API облака,  почему бы мне не сделать то же самое в Ansible? Это же просто: написать плейбук, который напрямую использует API, благо в community.general есть модуль rest. 

Такой подход работал, но в нём тоже были недостатки. Во-первых, каждый раз приходилось вручную запрашивать IAM-токен через CLI, а это не очень удобно. А во-вторых, это, фактически, была замена "шила" на "мыло", т.к. вместо консольной утилиты использовался модуль ansible, который давал в итоге такой же объём ифраструктурного кода. Решение первой проблемы напрашивалось само собой: написать собственный Ansible-модуль для получения токенов. Ну, а со второй можно было жить. 

Как работать с API правильно 

В документации Yandex Cloud описано, как обменивать ключ сервисного аккаунта на временный токен через  JSON Web Token. Отлично! Вооружившись инструкциями и примерами от Ansible, я написал модуль, который получает на вход путь до файла с ключами сервисного аккаунта и обменивает их на IAM-токен. Тесты прошли успешно. Но встал вопрос: а что делать, если нужно использовать не сервисный аккаунт? Добавил поддержку обмена OAuth-токена на IAM. Это тоже заработало, и количество строк в плейбуке для работы с секретами значительно сократилось. 

Однако, каждый раз писать запросы в API всё ещё было неудобно. Один модуль я уже сделал — почему бы не пойти дальше? Пишем ещё немного кода на Python для работы с секретами. Первым делом автоматизируем создание секретов. Task для плейбука стал намного удобнее. Теперь он выглядел так:

- name: Create secret

  yc_lockbox:

    iam_token: your_iam_token

    folder_id: your_folder_id

    secret_name: my_secret

    text_payload_entries:

        key1: "value1"

        key2: "value2"

    state: present

Стало действительно удобнее, но в нашем проекте чувствительной информации очень много. Кроме того, сохранять в плейбук пароли и другую секретную информацию небезопасно (не делайте так!). Поэтому я добавил функционал по загрузке секретных ключей из файла. 

И вот тут у меня что-то пошло не так. Код правильный, файл читается, функциональные тесты тоже завершаются успешно, однако Lockbox упорно отвечает ошибкой. Где не так, и что не так, стало ясно позже, когда было испробовано множество вариантов: оказывается, количество ключей в одном секрете ограничено, больше 32 нельзя. ОК, сокращаем файл, добавляем проверку перед загрузкой, и всё работает. Дальше было проще: шаг за шагом были написаны методы удаления, обновления, деактивации и активации секретов. 

Следующей задачей стало использование Lockbox на лету, чтобы можно было легко и безопасно получить нужный секрет. Решение я подсмотрел в ansible-коллекции Hashicorp: пишем lookup-модуль, который получает IAM-токен, а после идёт в API Lockbox и извлекает нужный ключ из секрета. Теперь получение секретной информации в плейбуке состоит из одной строки 

- name: Fetch secret from Yandex Cloud Lockbox using service account key

  debug:

    msg: "{{ lookup('yc_lockbox', 'secret_id', 'key') }}"

Итого: Ansible может читать и редактировать секреты Lockbox, используя JSON-ключи сервисных аккаунтов или OAuth-токены. Набор модулей YCM (Yandex Cloud Modules) позволяет безопасно авторизоваться в API облака и даёт удобные иструменты для работы с сервисами Lockbox и IAM. 

gRPC вместо Restful API 

Но можно ли улучшить процесс? Да. Вместо REST API я решил попробовать gRPC, использовав уже готовый SDK от Яндекса, который доступен через pip. И результат меня удовлетворил. Код модулей стал компактнее, за счёт использования SDK, а выполнение операций над секретами стало быстрее в 2-3 раза, с поправкой на качество интернет-соединения.  Особенно прирост в скорости был заметен при использовании lookup-плагинов.

На достигнутом я не планирую останавливаться. На момент написания статьи в модуль get_yc_iam и в lookup-плагин yc_iam_token добавлена возможность получать iam-token из метаданных виртуальной машины в облаке Yandex Cloud. В дальнейших планах — собственная коллекция Ansible, в которой хочется охватить те аспекты работы с облачными сервисами, где terraform’у не хватает гибкости. 

P. S. Модули доступны для использования из репозитория https://github.com/krang404/ycm

Комментарии (0)