company_banner

Используем Ansible вместе с Terraform

Автор оригинала: Alex Dzyoba
  • Перевод


Недавно я начал применять Terraform для создания облачной лабы для тестов, и это довольно круто. Буквально за несколько дней я поднялся с «никогда не использовал AWS» до «я умею декларативно создавать изолированную инфраструктуру в облаке». Я поставил парочку серверов в выделенной сети в VPC с security group и отдельными ключами SSH, все это заняло у меня несколько сотен строк кода.


Все приятно и прельстиво, но после создания сервера из некоторого базового AMI мне надо его развернуть. Мой типовой инструмент для этого — Ansible, но, к сожалению, у Terraform нет встроенного модуля для Ansible (есть обратный, начиная с Ansible 2.5, прим. переводчика), в отличие от Chef и Salt. Это не похоже на Packer, имеющий ansible (удаленный) и ansible-local, который я использовал для сборки образов Docker.


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


Нужно ли развертывание в облаке?


Вы, конечно же, можете сделать свой собственный AMI вместо использования типовых пустых, и, следовательно, пропустить полностью развертывание, но у этого способа есть фатальный недостаток. Каждое минимальнейшее изменение потребует пересоздания всего сервера. Если надо что-то поправить на базовом уровне, придется перезаливать все сервера. Это быстро надоедает в случае развертывания, установки обновлений безопасности, добавления или удаления пользователей, изменения настроек и прочих простых вещей.


Далее — если делаете свои AMI, вам все равно нужно как-то запускать развертывание, так что опять появляются такие вещи, как Ansible. Ну и опять же, я рекомендую использовать Packer вместе с Ansible.


Получается, что в большинстве случаев развертывание нужно, потому что оно — неизбежно.


Как использовать Ansible вместе с Terraform


Возвращаемся к задаче развертывания: я нашел три способа использовать Ansible совместно с Terraform после прочтения этого обсуждения. Читайте дальше, чтобы выбрать подходящий вам.


Встроенный inventory с IP сервера


Наиболее очевидное и хакерское решение — запускать Ansible с помощью local-exec, например так:


provisioner "local-exec" {
    command = "ansible-playbook -i '${self.public_ip},' --private-key ${var.ssh_key_private} provision.yml"
}

Просто и приятно, но тут сразу же есть нюанс. local-exec запускается без ожидания запуска сервера, так что в большинстве случаев это не сработает, потому что на момент подключения еще некуда подключаться.


В качестве изящного обходного способа вы можете предварительно использовать remote-exec, который будет ожидать соединения с сервером, а затем запускать local-exec.


В результате у меня получилась следующая вещь, запускающая роль «Ansible provisioner»:


provisioner "remote-exec" {
    inline = ["sudo dnf -y install python"]

    connection {
      type        = "ssh"
      user        = "fedora"
      private_key = "${file(var.ssh_key_private)}"
    }
  }

  provisioner "local-exec" {
    command = "ansible-playbook -u fedora -i '${self.public_ip},' --private-key ${var.ssh_key_private} provision.yml"
  }

Чтобы ansible-playbook работал, вам надо иметь код для Ansible рядом с кодом для Terraform:


$ ll infra
drwxrwxr-x. 3 avd avd 4.0K Mar  5 15:54 roles/
-rw-rw-r--. 1 avd avd  367 Mar  5 15:19 ansible.cfg
-rw-rw-r--. 1 avd avd 2.5K Mar  7 18:54 main.tf
-rw-rw-r--. 1 avd avd  454 Mar  5 15:27 variables.tf
-rw-rw-r--. 1 avd avd   38 Mar  5 15:54 provision.yml

Встроенный inventory будет работать в большинстве случаев, кроме тех, когда необходимо иметь несколько серверов в inventory. Например, если вы устанавливаете агент Consul, вам нужен список серверов для создания файла настроек, это как правило можно найти в обычном репозитории. Но в приведенном выше способе это не сработает, поскольку у нас всего один сервер в inventory.


В любом случае, я использую такую возможность для базовых вещей, например, добавления пользователей или установки некоторых базовых пакетов.


Динамический inventory после работы Terraform


Еще одно простое решение для раскатки инфраструктуры, созданной Terraform — не связывать вместе Terraform и Ansible. Создаем инфраструктуру с помощью Terraform, а затем используем Ansible с динамическим inventory без привязки к тому, как сервера были созданы.


Так что вы сначала создаете инфраструктуру с помощью terraform apply, затем запускаете ansible-playbook -i inventory site.yml, где inventory — каталог, содержащий скрипты динамического inventory.


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


А это то, что я использую в дополнение к предыдущему способу.


Inventory, создаваемая из состояния Terraform


Есть еще одна интересная штука, которая, возможно, будет у вас работать — создание статического inventory из состояния Terraform.


Terraform при работе поддерживает состояние инфраструктуры, содержащее все, включая ваши сервера. При использовании local backend это состояние сохраняется в файле JSON, который можно потом легко разобрать и сконвертировать в inventory для Ansible.


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


Первый


[all]
52.51.215.84

[all:vars]

[server]
52.51.215.84

[server.0]
52.51.215.84

[type_aws_instance]
52.51.215.84

[name_c10k server]
52.51.215.84

[%_1]
52.51.215.84

Второй


$ ~/soft/terraform.py --root . --hostfile
## begin hosts generated by terraform.py ##
52.51.215.84        C10K Server
## end hosts generated by terraform.py ##

Дополнение Ansible для Terraform, которое у меня не заработало


Наконец, есть несколько проектов, которые пытаются внедрить поддержку Ansible в Terraform, как это уже, например, сделано для Chef.


Первая попытка сделать дополнение, но, к сожалению, он уже не поддерживается автором и даже больше — не поддерживается актуальной системой дополнений Terraform.


Вторая, более свежая и поддерживаемая, позволяет такой вариант развертывания:


...
provisioner "ansible" {
    plays {
        playbook = "./provision.yml"
        hosts = ["${self.public_ip}"]
    }
    become = "yes"
    local = "yes"
}
...

К сожалению, у меня не получилось заставить это работать, так что я тупо забил, поскольку первые два способа работают во всех моих случаях.


Заключение


Terraform и Ansible — мощная связка, которую я использую для развертывания облачной инфраструктуры. Для типовых облачных серверов я запускаю Ansible через local-exec, позднее я запускаю Ansible отдельно с динамическим inventory.


Примеры можно найти здесь.


Благодарю за внимание и до новых встреч!

Southbridge
Обеспечиваем стабильную работу highload-проектов

Комментарии 38

    +4
    У меня такое чувство что Hashicorp всеми силами стараются избежать хоть какой-то интеграции с Ansible. Иначе не совсем понятно почему есть провижинеры Chef, Puppet, Salt, но нет Ansible. При всем уважении к этим трем, по популярности они не сравнятся с Ansible.
    Поэтому и куча статей, проектов и прочих велосипедов где каждый упражняется в попытках прикрутить Ansible к терраформу.
      0

      Потому что ансибл не нужен. Я бы сказал, что сейчас за ансиблом стоит редхат со всеми вытекающими, но и шеф, и солт, и паппет тоже коммерческие продукты, если подумать.

        +1

        Это терраформ не нужен, ансибл вполне его заменяет если добавить напильник.

          +1
          как ансибл хранит состояние о развернутых ресурсах? как разрешаются зависимости?
            0
            Вы явно спрашиваете из парадигмы терраформа, у ансибла немного другой подход.
            как ансибл хранит состояние о развернутых ресурсах?
            inventory plugins хранят где вам угодно
            как разрешаются зависимости?
            Типа depends_on в терраформе? В ансибле это не нужно. Он не только декларативный как терраформ.
              0
              как ансибл хранит состояние о развернутых ресурсах?

              хранение состояния у терраформа — это его же Ахиллесова пята. Никогда не приходилось чинить tfstate? А строить зависимости ресурсов через null_resource (фу-фу-фу)? А конкурентные запуски терраформа как блокируете? Вот оно и есть.
              Я не говорю, что у ансибла нет проблем. Но они другие в массе своей.

            0

            Если ансибл не нужен, то что использовать?

            +1
            Иначе не совсем понятно почему есть провижинеры Chef, Puppet, Salt, но нет Ansible

            Ну вообще то уже отказываются


            The built-in vendor (third-party) provisioners, which include habitat, puppet, chef, and salt-masterless are now deprecated and will be removed in a future version of Terraform

              0

              А что взамен? Т.е. хочется понимать логику такого шага и последствия

              +1
              О, «справедливость восторжествовала». Действительно интересно, что дальше. Может они просто свой провиженер готовят? Походу избавляясь от всяких 3party.
            +1
            В чем проблема развернуть пакером, создать AMI и уже ее использовать в терраформ? Или опять решалась проблема из ничего?
              0
              Не всегда такой вариант подходит. Например, мне недавно нужно было сделать NATS Streaming кластер, прямо в строке запуска nats-нод там статически прописываются routes до других нод. В моем случае реально было написать скрипты, чтобы через Terraform поднять надо и сгенерировать output как inventory выше. А дальше на основе этого inventory сгенерировать под каждую ноду в кластере собственный unit-файл systemd и задеплоить через ansible.
                +1
                Возможно, но здесь-то я такой проблемы не вижу
                  0

                  Консул (как источник конфигурации) и консул темплейт (как способ доставки)? И все в пределах стека хашикорпа. Они очень грамотные ребята и реально предлагают… хорошие продукты. Хотя насчёт терраформ очень большие сомнения. Для Амазона тот же CF будет как минимум не хуже

                    0
                    Ну скажем синтаксис CF далек от идеала, да и опять же преимущества terraform как-раз в его универсальности, возможности интеграции с другими провайдерами и кастомная логика. Плюс контроль стейтов — терраформ дает больше возможности в плане ручного контроля. Ну и да — огромное количество готовых модулей, много возможностей в плане разделения окружений и workspaces.
                      0
                      да и опять же преимущества terraform как-раз в его универсальности

                      нет ) В смысле — конечно, терраформ более универсальное решение, но все эти рекламные истории про мультиклауд — это ложь. Потому что инкапсулировать специфику каждого конкретного провайдера и предоставлять пользователю более абстрактные объекты (скажем не просто "виртуальная машина", а "сервер БД PSQL" или вообще "инстанс бизнес-приложения) — это там еще писать и писать обертку. Но, да, это, конечно, возможно )


                      возможности интеграции с другими провайдерами

                      Звездочка — при наличии этих самых провайдеров. Либо их можно написать самим — там достаточно простой интерфейс. Как пример боли — провайдер для k8s. Лучше б его в ТФ вообще не было :-)


                      В общем — инструмент неплохой, но вокруг него слишком много хайпа.

                        0
                        вы не совсем правы. Провайдер k8s, например, используется в модуле создания кластера для AWS EKS — там ему как-раз таки находится отличное место. В здравом уме никто не будет использовать его для описания собственной инфраструктуры. Далее, мультиклауд — это тоже не для конечной инфраструктуры, это для продукта, который изначально разрабатывается как мультиклаудное решение (как пример могу привести tectonic).
                          0

                          tectonic? Это, который coreos? Так нет его )


                          Далее, мультиклауд — это не тоже не для конечной инфраструктуры, это для продукта, который изначально разрабатывается как мультиклаудное решение

                          • Но многие сейчас очень гипнотизированы "мультиклаудом", который им не нужен на самом деле
                            0
                            это только пример. Многие компании сейчас предоставляют услуги развертывания технологий поверх клауда на выбор (Confluent например), компенсируя отсутствие либо недоработанность технологии в клауде, при этом предоставляя фактически SaaS поверх IaaS клауда. И это часть их бизнес-модели. В таком случае использовать нативные подходы описания инфраструктуры для каждого клауда становится просто невыгодно.Опять же — банальная экономия ресурсов разработчика, ведь написать решение под другой клауд, если уже знаешь терраформ, проще, чем что-то еще осваивать.
                              0
                              Опять же — банальная экономия ресурсов разработчика, ведь написать решение под другой клауд, если уже знаешь терраформ, проще, чем что-то еще осваивать.

                              спорно — потому что проще сказать "дайте кубернетес" и разворачивать в нем — он уже практически стал таким "универсальным" платформенным решением. И не нужно пытаться корчить из себя мультиоблако с непонятными расходами на поддержку

                                0
                                Сами себе противоречите — то сначала упоминаете про managed сервисы, то теперь — дайте k8s. Да и сам k8s надо где-то сконфигурировать сначала.
                                  0

                                  Вы путаете ) в моей модели все правильно.
                                  Если поставщик предполагает менеджед сервисы — вам как потребителю все равно как они реализованы. Берёте и используете.
                                  Для коробочных продуктов от 3rd party — кубернетес уже является одним из вариантов целевых платформ. Например, так устанавливаются GitLab, Artifactory и прочие. Потому что вендору нет никакого резона пытаться учесть специфику любого облака.
                                  Для собственной же разработки — тоже зачастую проще отбазироваться от кубернетеса. Не берём сейчас историю «как его развернуть» — с ней действительно ТФ может помочь. И здесь я ни в коем разе не спорю )

                                    0

                                    Ну вот решили мы предоставлять managed РСУБД. Что-то мне подсказывает, что если развернём кластер в ДЦ AWS во Франкфурте, то если закажет его кто, то те, у кого EC2 в том же ДЦ.

                  0
                  Ну не все упирается в image. Например задача по управлению конфгурацию EMR не описанной classification
                    0

                    Если у вас, например, есть MongoDB, который запускается на EC2 и вам нужно поставить новый mongodb exporter для Prometheus — что будете делать? И все без даунтайма.

                      0

                      Без даунтайма — решается кластерными базами. То же обновление MongoDB в случае единственного инстанса (скажем, в монге CVЕ нашли и НУЖНО обновляться) тоже нельзя без даунтайма сделать. Это только лишь говорит нам, что нужно переходить от парадигмы pets к парадигме cattle.
                      А еще лучше — использовать managed services от самого амазона....

                        0
                        у AWS нет MongoDB, есть совместимая с какой-то версией по API Document DB — но опять же, без map-reduce например.
                    0

                    У меня терраформ генерит несколько файлов переменных для ансибль и собственно. hosts.ini.
                    Используя стандартные шаблоны, никакой магии.
                    Причина — нужно разворачивать множество очень похожих но не одинаковых кластеров.
                    Так что все раскидано по workspace-ам в терраформе и по директориям как завещано в. best practices в ансибле.
                    И что-бы не мучаться с гигантскими строками командной строки все это завернуто в Makefile,
                    Полгода, пока полет нормальный.

                      +1

                      / в процессе прочтения не покидало чувство дежавю, что я это уже читал… и точно! тьфу! только время потратил/
                      А точно надо переводить статью которой больше двух лет и которую все кто хотел уже прочитали давно в оригинале?


                      Дополнение Ansible для Terraform, которое у меня не заработало

                      Ну спустя то два года, даже переводчик уже мог бы добиться, чтобы заработало. Но нет(

                        0

                        Спасибо за отзыв! У меня в связи с ним возникает философский вопрос — а зачем что-то делать, если можно не делать )


                        P.s. У меня было строго такое же ощущение, когда в меня тыкали известной статьей про то, что «докер в докере плохо», хотя все поменялось чуть ли не на 180 градусов

                        0

                        Вообще есть еще 3ий вариант интеграции через встроенный host_list inventory plugin https://docs.ansible.com/ansible/latest/collections/ansible/builtin/host_list_inventory.html


                        1. Terraform экспортит список хостов
                        2. Ansible принимает их через -i списком
                          0

                          Terraform экспортит список всех хостов и Ansible принимает все хосты через -i списком, а хотелось бы работать только с группой хостов.

                            +1

                            Терраформ экспортит ровно то, что вы указали. Укажите все хосты — будут все.


                            ansible-playbook -i $(terraform output hosts_list) play_me.yml


                            Но есть особенность — вы когда используется hosts_list не прлучится забрать host/group inventory vars с диска

                          +1

                          Проблемы несвоевременных коннектов в ансибле тривиально решаются вот так:


                          - hosts: target_group
                            gather_facts: false
                            tasks:
                              - wait_for_connection:
                            +2
                            Понимаю автора. Но многие terraform эксперты рекомендуют избегать как remote-exec так и local-exec. По причине того что то, что делает local/remote-exec уже вне контроля terraform. Terraform plan уже не покажет что именно там сделает с инстансом exec. Что может однажды выстрелить в ногу. И дело тут не в Ansible.
                            А почему просто не создавать инстансы на базовом образе, а provision делать в userdata?
                            Userdata хорошо управляется с помощью terraform, а там внутри используйте хоть bash хоть ansible.
                              0
                              Неудобно если нужны всякие пароли, их в юзердате не спрячешь.
                                +2
                                пароли можно и не заполнять plain text в userdata, а например вытягивать из SSM во время provision

                            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                            Самое читаемое