Info
Content

Packer

https://www.packer.io/docs
https://rtfm.co.ua/packer-vvedenie-primery/

Терминология

  • Артефакт - результат одиночной сборки. Обычно это id'шники или файлы для воссоздания ВМ
  • Сборка - одиночная задача, которая произведет образ для одной из платформ
  • Сборщик - компонент packer'а, который позволяет создать образ
  • Post-processor - компонент packer'а, который берет результат работы сборщика или другого пост-обработчика и обрабатывает его для получения новых артефактов
  • Provisioner - устанавливает и конфигурирует ПО на запущенной машине
  • Шаблоны - json-файлы в которых определяется сборка образа и конфигурирование компонентов packer'а. Packer позволяет читать шаблон и создавать множество образов машин параллельно

CLI

Packer имеет очень человеко-читаемый вывод. Но он также имеет и машино-читаемый режим.
Его можно включить флагом -machine-readable для любой команды и не важно в каком порядке, команда флаг или флаг команда, главное чтобы он присутствовал.


  • packer build - на основе шаблона генерирует набор артефактов. Разные сборки, опреленные в шаблоне, запускаются параллельно

  • packer console - позволяет проверить как накладываются передаваемые переменные на шаблон
    Переменные (уже определенные или пустые) можно вызывать так:

    {{user `aws_access_key`}} # Этот шаблон заменится на значение переменной определенное пользователем в секции __variables__ в темплейте  
    

    Также в console можно загонять разный текст через пайплайн и получать на выходе то как он выглядел бы при обработке

  • packer fix - позволяет исправить шаблон в соответствии с новой версией packer'а (если после обновления packer'а шаблон перестал работать)

  • packer inspect - позволяет получить описание содержимого темплейта без погружения в json. Пример:

    $ packer inspect main.json 
    Optional variables and their defaults:
      aws_access_key = 
      aws_secret_key = 
    Builders:
      amazon-ebs
    Provisioners:
      <No provisioners>
    
  • packer validate - валидирует шаблон и выводит допущенные в шаблоне ошибки

Шаблоны

Шаблоны это json-файлы которые конфигурируют разные компоненты packer'а и описывают создание одного или более образов ВМ
Компоненты шаблона:

  • builders - единственный обязательный элемент шаблона. Список из одного или более объектов, которые описывают сборщиков
  • description - строка с описанием того что делает шаблон
  • min_packer_version - минимальная версия packer'а для этого шаблона
  • post-processors - список объектов, которые описывают разные шаги постобработки при сборке образа
  • provisioners - описывает какие средства провижининга будут задействованы для установки и конфигурации ПО при сборке образа
  • variables - набор key:value строк, которые описывают переменные
  • sensitive-variables - то же что и variables, но эти переменные не пишутся в лог и stdout

Так как JSON не предоставляет комментарии, то чтобы оставить комментарий в шаблоне packer нужно добавить корневой элемент, имя которого начинается с подчеркивания. Пример:

{
 "_super-comment": "My cool comment", 
 "variables": {
   "aws_access_key": "",
   "aws_secret_key": ""
 ...

Только такие элементы могут быть комментариями. Другие будут вызывать ошибки при валидации

builders

Билдер (сборщик) - это объект, который определяет параметры сборки
Выглядит так:

{
  "builders": [
    {
      "type": "amazon-ebs",
      "access_key": "...",
      "secret_key": "...",
      // еще куча разных аттрибутов
    }
  ]
}

Поле type является обязательным и по умолчанию является именем по которому можно обратиться к билдеру. Имя используется в выводе при сборке, чтобы было видно что сейчас происходит. Если просто типа билдера не достаточно, то можно и явно указать имя билдера через аттрибут name

communicators

Коммуникаторы это механизм используемый packer'ом для загрузки файлов, запуска скриптов и прочего провижининга запущенной машины. Каждый билд ассоциируется с каким-то коммуникатором для связи, обычно это SSH. Коммуникатор конфигурируется в секции builders и может быть трех видов (это значения ключа communicator):

  • none - коммуникатор отключен. В таком случае провижининг не возможен
  • ssh - будет установлено ssh соединение с машиной (по умолчанию)
  • winrm - WinRM

Аттрибут pause_before_connecting позволяет указать паузу перед подключением к собранной машине. На самом деле это время ожидания перед повторным подключением после первого (тестового) успешного подключения.

template engine

В шаблонах используются следующие постановления:

  • Все связанное с шаблонизацией происходит в двойных фигурных скобках {{ }} (без пробелов между скобкой и словом как в jinja)
  • Функции вызываются в двойных фигурных скобках
  • Переменные префиксируются точкой и начинаются с большой буквы
Функции

Имеется множество разных функций, которые могут быть использованы в шаблоне

post-processors

Секция пост-процессинга в шаблоне определяет какие дополнительные действия должны сделаны билдерами при сборке образа. Например сжатие образа или загрузка его куда-либо

Каждый описанный пост-процессор будет выполнен для каждого описанного билдера. То есть при двух билдерах и одном пост-процессоре, пост-процессор будет выполнен дважды (по одному разу для каждого билдера)

Есть три варианта описания пост-процессора: простой, детализированный и последовательный. Можно представлять, что простой и детализированный варианты это ссылки на последовательный вариант

  • Просто это просто строка с именем пост-процессора
    {
      "post-processors": ["compress"]
    }
    
  • Детализированный похож на определение билдера или провижинера. Это объект с разными аттрибутами
    {
      "post-processors": [
        {
          "type": "compress",
          "format": "tar.gz"
        }
      ]
    }
    
  • Последовательный еще более подробный

Чтобы сохранять промежуточные артефакты во время пост-процессинга нужно указать true в аттрибуте keep_input_artifact для нужного пост-проссинга

Провижининг

{
  "provisioners": [
    // ... one or more provisioner definitions here
  ]
}

Провизоры будут запускаться для каждой сборки в том порядке в котором они определены в шаблоне

В определение провизора должен содержаться аттрибут type, оно определяет имя используемого провизора. У разных провизоров разные обязательные поля, например провизор shell требует аттрибута shell

Можно использовать аттрибуты only или except в объекте провизора, чтобы избрать нужные билды. Они принимают в себя список имен билдов (не типов билдеров, нужно указать билдам имена и использовать их тут)

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

Можно делать переопределения в провизорах. Это может быть нужно когда мы хотим чтобы создаваемые машины отличались

{
  "type": "shell",
  "script": "script.sh",
  "override": {
    "vmware-iso": {
      "execute_command": "echo 'password' | sudo -S bash {{.Path}}"
    }
  }
}

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

max_retries - определяет количество повторных попыток после первой неудачной

timeout - это таймаут на выполнение провизора прежде чем он будет считаться зафейленым

Пользовательские переменные

Переменные позволяют шаблонам быть параметризированными и не хранить в шаблонах секретные данные (например токены)

Переменные объявляются в секции variables, а также могут быть переданны через файл или флаг -var

Если переменная будет передваться как аргумент, то лучше ее объявить пустой в шаблоне. Тогда будет понятно, что эта переменная будет заменена

Например тут мы сразу видим какие переменные нам нужно передать аргументами

{
  "variables": {
    "aws_access_key": "",
    "aws_secret_key": ""
  },

  "builders": [
    {
      "type": "amazon-ebs",
      "access_key": "{{user `aws_access_key`}}",
      "secret_key": "{{user `aws_secret_key`}}"
      // ...
    }
  ]
}

Устанавливать переменные можно из файла или через командную строку

Через командную строку это делается через ключ -var которому передается строка в формате "var_name=var_value". Ключи -var может быть передан сколько угодна раз (в зависимости от того как много у вас переменных). Переменные из командной строки переопределяют любые ранее заданные переменные

Чтобы определить переменные в отдельном файле, нужно в нем описать объект variables из шаблона:

{
  "aws_access_key": "foo",
  "aws_secret_key": "bar"
}

Переменные из файла с переменными указанного в конце переопределят предыдущие
То же и с комбинированием -var-file и -var, то что указано позже - важнее. Не важно файл это или параметр команды

Существует секция sensitive-variables, в ней можно указывать чувствительные переменные, которые не должны быть видно в журналах и прочих местах (например это токены)

{
  "variables": {
    "my_secret": "{{env `MY_SECRET`}}",
    "not_a_secret": "plaintext",
    "foo": "bar"
  },

  "sensitive-variables": ["my_secret", "foo"],
}

На работу приложения это никак не влияет, влияет только на вывод и журналы

Коммуникаторы

Коммуникаторы это механизм для загрузки файлов, выполнения скриптов и прочих действий в созданной машине. Коммуникаторы конфигурируются в секции builders

Поддерживаются три вида коммуникаторов:

  • none - никакой
  • ssh - ssh (по умолчанию)
  • winrm - winrm

Иногда ssh может быть не сконфигурирован в дефолтном образе, тогда нужно использовать файл preseed или kickstart для предконфигурирования

SSH коммуникотор имеет огромное количество опций, которые можно посмотреть здесь https://www.packer.io/docs/communicators/ssh

Builders

Билдеры отвечают за создание машины и генерацию образа из нее для различных платформ. Packer может работать с различными билдерами по умолчанию и может быть расширен для добавления новых билдеров

По умолчанию в packer доступно огромное количество билдеров для огромного количества платформ. Опишу здесь лишь то что интересно мне:

docker

Docker билдер собирает образы для докера. Он стартует докер контейнер, запускает провижининг внутри этого контейнера и экспортирует контейнер в образ

Провижининг в этом случае происходит без использования Dockerfile, это происходит так же как если бы вы настраивали железный или виртуальный сервер

Для работы docker-builder'а требуется docker engine

Обязательные опции:

  • commit/discard/export_path - закоммитить контейнер/отменить/экспортировать в указанный tar файл (одновременно может быть указано только одно из трех)
  • image - базовый образ для контенера (из которого он будет запущен)

Также есть еще миллион опциональных параметров
https://www.packer.io/docs/builders/docker

Packer умеет добавлять к образу различную информацию (тэги, и прочее) и импортировать его сразу в репозиторий образов

file

file-builder - то не настоящий билдер, он просто создает артефакты из файла и может быть использован для отладки пост-процессоров без длительного ожидания

null

Это тоже не настоящий билдер. Он просто настраивает ssh-подключение и запускает провижининг. Полезно для дебагинга провизоров так как занимает мало времени

Vagrant

Обязательные:

  • source_path - URL или имя используемого vagrant-бокса (имя на vagrant-cloud, имя на другом хранилище, имя локального файла). Если используется .box имя (url или локальный файл), то нужно также указать box_name опцию
  • global_id - айдишник уже существующего в системе бокса (посмотреть айдишники можно командой vagrant global-status)

Две эти ^ опции - взаимоисключающие, возможна только одна из них

Остальные опции:

  • output_dir - папка в которую будет помещен бокс и прочее. Это нужно для избежания коллизий (чтобы файлы не смешивались). Если не указывать, то папка будет создана все-равно output-BUILDERNAME
  • box_name - нужно при использовании source_path
  • provider - нужно при использовании source_path, указывает на используемый провайдер (virtualbox, docker, etc)
  • checksum - чексумма .box файла
  • checksum_type - тип указываемой выше чексуммы. Без этого поля чексумма не может быть проверена. Поддерживаемые значения:
    • none
    • md5
    • sha1
    • sha256
    • sha512

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

  • template - путь до темплейта с vagrantfile'ом. Используется шаблонизация на языке Go
  • skip_add - не вызывать vagrant add для добавления бокса в локальное окружение
  • teardown_method - что сделать с боксом после сборки, возможные значения:
    • halt <- default
    • suspend
    • destroy
  • box_version - версия
  • add_cacert - эквивалент vagrant box add --cacert
  • add_capath - эквивалент vagrant box add --capath
  • add_cert - эквивалент vagrant box add --cert
  • add_clean - эквивалент vagrant box add --clean
  • add_force - эквивалент vagrant box add --force
  • add_insecure - эквивалент vagrant box add --insecure
  • output_vagrantfile - эквивалент vagrant package --vagrantfile
  • package_include - эквивалент vagrant package --include

Эквиваленты можно посмотреть здесь https://www.vagrantup.com/docs/cli/box.html#box-add

  • skip_package - пакер не будет упаковывать бокс в .box-файл

virtualbox

VirtualBox builder может создавать ВМ и экспортировать их в OVA/OVF формат

  • virtualbox-iso - начинает с iso, создает новую ВМ, устанавливает и провижинит ПО и экспортирует машину в образ
  • virtualbox-ovf - импортирует существующий OVF/OVA файл, запускает провизоры поверх этой ВМ и экспортирует машину для создания образа
  • virtualbox-vm - поверх существующей ВМ запускает провижининг и может сделать снапшот или экспортировать машину

provisioners

Провизоры это встроенное и стороннее ПО для конфигурирования и установки машины после загрузки
Обычно они устанавливают пакеты, патчат ядро, создают юзеров, загружают код приложений

ansible-local

В машину будет закинут плейбук и запущен ансибл в локальном режиме (то есть виртуалка сама себя сконфигурирует [без ssh])

{
  "type": "ansible-local",
  "playbook_file": "local.yml"
}

ansible-remote

{
  "type": "ansible",
  "playbook_file": "./playbook.yml"
}

file

В машину будет загружен файл

Для загрузки файла будет использовать пользователь для провижининга (обычно это не рут), поэтому рекомендуется загрузить таким образом файл куда-нибудь, а потом уже с помощью модуля shell делать с ним что надо (установить права, положить в нужно место и тд)

{
  "type": "file",
  "source": "app.tar.gz",
  "destination": "/tmp/app.tar.gz"
}

shell

{
  "type": "shell",
  "inline": ["echo foo"]
}

Вместо инлайн можно указывать скрипты (которые там уже лежат)

post-proccessors

Пост-процессоры запускаются после сборки и провижининга
Могут загружать собранный образ куда-то (в облако), сжимать, или что угодно еще

No Comments
Back to top