Есть веские причины для применения неизменных методов инфраструктуры; Одним из них является то, что вам не нужно беспокоиться о своих серверах, содержащих остатки от предыдущих развертываний. Кроме того, ваши процессы развертывания могут быть чрезвычайно чистыми, поскольку они не связаны с тонкостями живого обновления промежуточного программного обеспечения и кода, а скорее с созданием образов / контейнеров и инфраструктуры, содержащей их.
В Cloudify у нас есть много артефактов для создания. От Docker и образов машин до потенциально перемещаемых Python virtualenvs, tar-файлов node.js и двоичных файлов Windows.
Мы хотим, чтобы наш процесс сборки был неизменным.
бродяга
Поскольку мы не SaaS-компания, а скорее поставщик программного обеспечения с открытым исходным кодом, мы подумали: почему бы не использовать ту же концепцию неизменной инфраструктуры для создания наших артефактов? Это означает, что каждый раз, когда мы хотим создать двоичный файл, создается новая среда. И какой самый простой способ раскрутить временную среду? Бродяга .
Допустим, мы хотим создать образ Docker. Мы создаем Vagrantfile, содержащий 2 виртуальные машины:
# -*- mode: ruby -*-
# vi: set ft=ruby :
AWS_ACCESS_KEY_ID = ENV['AWS_ACCESS_KEY_ID']
AWS_ACCESS_KEY = ENV['AWS_ACCESS_KEY']
BASE_BOX_NAME = 'ubuntu/trusty64'
Vagrant.configure('2') do |config|
config.vm.define "ubuntu" do |ubuntu|
#dummy box, will be overridden
ubuntu.vm.box = "dummy"
ubuntu.vm.box_url = "https://github.com/mitchellh/vagrant-aws/raw/master/dummy.box"
ubuntu.vm.provider :aws do |aws, override|
aws.access_key_id = AWS_ACCESS_KEY_ID
aws.secret_access_key = AWS_ACCESS_KEY
# official ubuntu 14.04 64bit box
aws.ami = "ami-036eaa74"
aws.region = "eu-west-1"
aws.instance_type = "m3.medium"
aws.keypair_name = "vagrant_build"
override.ssh.username = "ubuntu"
override.ssh.private_key_path = "/home/.ssh/aws/vagrant_build.pem"
aws.tags = {
"Name" => "vagrant docker images build",
}
aws.security_groups = "vagrant_cfy_build"
end
# need to sync folders
ubuntu.vm.synced_folder "../../", "/cloudify-packager", create: true
ubuntu.vm.provision "shell", path: "provision.sh", privileged: false
end
config.vm.define :local do |local|
local.vm.provider :virtualbox do |vb|
vb.customize ['modifyvm', :id, '--memory', '1024']
end
local.vm.box = BASE_BOX_NAME
local.vm.hostname = 'local'
local.vm.synced_folder "../../", "/cloudify-packager", create: true
local.vm.provision "shell", path: "provision.sh", privileged: false
end
end
Первая виртуальная машина AWS используется в нашем официальном процессе сборки.
Благодаря абстракции Vagrant над IaaS, мы можем выбрать свойства машины, подходящей для конкретной сборки, что упрощает оптимизацию процесса сборки. Например, если мы знаем, что у нас есть длинный и сложный процесс сборки для конкретного артефакта, мы можем захотеть предоставить много ресурсов для машины сборки (и наоборот).
Используя Vagrant, мы также можем предоставлять локально исполняемые системы сборки. Вторая виртуальная машина позволяет людям создавать любые артефакты, в которых они нуждаются, локально, а не привязываться к конкретной системе сборки.
Сценарий `provision.sh` — это просто скрипт, необходимый для генерации артефакта. В этом конкретном случае мы устанавливаем и настраиваем Docker и docker-compose ; запустите `docker-compose build …` в Dockerfile, находящемся в синхронизированном каталоге, и сохраните изображение в tar. Затем мы автоматически добавляем tar в S3. В приведенном выше примере cloudify-packager содержит файлы, необходимые для процесса сборки, поэтому они автоматически синхронизируются и используются из виртуальной машины.
Другим примером может быть создание агентов Cloudify для разных дистрибутивов. Мы требуем компиляции наших агентов на Ubuntu Trusty and Precise, CentOS 6.4, Debian Jessie и Windows Server 2013.
У нас есть Vagrantfile, содержащий виртуальные машины на основе AWS и Virtualbox с образами, соответствующими ОС и дистрибутивам, которые нам нужны. Мы поставляем мульти-дистрибутив, поддерживающий файл ‘обеспечить.
Осталось только запустить `Vagrant up debian_jessie_aws` и poof !, сгенерирован агент Джесси Debian.
Отделение Небес
Этот процесс полностью отделяет механизм генерации каждого артефакта от конкретной системы сборки, управляющей им. Это также позволяет нам распараллелить процесс сборки, поскольку мы можем раскрутить бесконечное количество машин Vagrant в любом выбранном нами провайдере IaaS (при условии, что для этого провайдера есть плагин Vagrant) и не должны полагаться на емкость нашего кластера Jenkins или на нас пишут логику для генерации машин для процесса сборки.
Кроме того, не имеет значения, какую систему сборки мы используем для создания артефактов. Это все о выполнении одной и той же команды в разных средах.
Этот механизм развязки также позволяет нашим клиентам самостоятельно создавать артефакты. Будучи открытым исходным кодом, «git clone» и «Vagrant up» — все, что нужно заказчику для создания своего собственного артефакта.
Читаемые журналы
Vagrant предоставляет прилично читаемые журналы во время процесса подготовки. Это позволяет нам относительно легко отлаживать проблемы, связанные со сборкой. В будущем мы намереваемся отправить эти журналы во внешний стек ELK, в Logentries или в Loggly, чтобы иметь возможность анализировать наш процесс сборки с большей степенью детализации.
Где Packer входит, чтобы играть
Очевидно, что мы не заинтересованы в установке всех требований к сборке каждый раз, когда виртуальная машина запускается.
Мы собираемся использовать Packer для создания образов, содержащих основные требования для нашего процесса сборки.
Мы будем использовать файл packerfile.json, который содержит конфигурацию для создания образа, содержащего Docker, который будет использоваться нашим Vagrantfile позже. (Да. Мы могли бы использовать Docker Builder, но, поскольку он работает на локальной машине, и мы бы предпочли запустить новую чистую ВМ, мы делаем это так.)
{
"variables": {
"aws_access_key": "{{env `AWS_ACCESS_KEY_ID`}}",
"aws_secret_key": "{{env `AWS_ACCESS_KEY`}}",
"aws_source_ami": "",
"instance_type": "m3.large",
"virtualbox_source_image": "",
"insecure_private_key": "./keys/insecure_private_key"
},
"builders": [
{
"name": "cloudify_docker_image_generator_image",
"type": "amazon-ebs",
"access_key": "{{user `aws_access_key`}}",
"secret_key": "{{user `aws_secret_key`}}",
"ssh_private_key_file": "{{user `insecure_private_key`}}",
"region": "eu-west-1",
"source_ami": "{{user `aws_source_ami`}}",
"instance_type": "{{user `instance_type`}}",
"ssh_username": "vagrant",
"user_data_file": "userdata/add_vagrant_user.sh",
"ami_name": "cloudify docker image builder {{timestamp}}"
},
... more builders with different images according to the relevant distro...
],
"provisioners": [
{
"type": "shell",
"script": "provision/provision.sh"
}
]
}
В этом случае файл provision.sh устанавливает Docker и `docker-compose` и предоставляет Docker API (для docker-compose). Сгенерированный AMI затем используется в Vagrantfile. Другим примером будет генерация пакетов наших агентов, для которых требуются ruby, fpm и packman . Мы можем использовать Packer для создания образа, содержащего их для каждого дистрибутива и… веселья!
Есть минусы, но даже так …
Это решение не идеально. Вращение виртуальных машин занимает много времени и является процессом, склонным к сбоям в среде. Кроме того, это стоит больше денег и выявляет некоторые проблемы безопасности (очевидно, с точки зрения безопасности, отсутствие виртуальных машин лучше, чем виртуальных машин).
Выиграть!
Мы пришли к выводу, что оно того стоит. До того, как сделать это, нечистые машины генерировали наши артефакты, и мы постоянно сталкивались с неудачными сборками. Добавление нескольких минут к параллельному выполнению нескольких потоков сборки артефактов не является преградой. Машины не работают достаточно долго, чтобы их можно было взломать (и группы безопасности, конечно, настроены правильно); и несколько сотен долларов в месяц не являются проблемой, когда это решение дает нам возможность каждый раз получать чистую окружающую среду.
Кстати, время, добавляемое для раскручивания виртуальных машин, также может быть сокращено за счет создания облаков, обеспечивающих более быстрое выделение виртуальных машин, таких как локальные установки OpenStack, Exoscale (хостинг Cloudstack) или Digital-Ocean. Мы выбрали AWS благодаря его стабильности и доступности. Мы можем (и, вероятно, будем) переключиться на другого поставщика в ближайшем будущем. Кто знает … может быть, мы начнем использовать Docker для этого …
Так?
С тех пор, как мы начали создавать наши артефакты, как описано выше, мы в основном сталкивались только с проблемами с логикой процесса сборки и (редко) отсутствующими предварительными требованиями. Их можно существенно уменьшить, используя изображения, которые содержат столько предварительных требований, сколько возможно.