В теме описано использование vagrant с провайдером VirtualBox.
Подробное содержание доступно справа.

Установка vagrant + VirtualBox

Устанавливать будем с официального сайта, скачав подходящие deb-пакеты.

ссылка на х64 версию VirtualBox для 14.04

Ссылка на х64 версию vagrant

Скачиваем и устанавливаем vagrant:

mkdir ~/vagrant
cd ~/vagrant
wget https://releases.hashicorp.com/vagrant/1.8.4/vagrant_1.8.4_x86_64.deb
sudo dpkg -i vagrant_1.8.4_x86_64.deb
rm -f vagrant_1.8.4_x86_64.deb

Скачиваем и устанавливаем VirtualBox:

cd ~/vagrant
wget http://download.virtualbox.org/virtualbox/5.0.24/virtualbox-5.0_5.0.24-108355~Ubuntu~trusty_amd64.deb
sudo dpkg -i virtualbox-5.0_5.0.24-108355~Ubuntu~trusty_amd64.deb
rm -f virtualbox-5.0_5.0.24-108355~Ubuntu~trusty_amd64.deb

Может понадобиться пакет linux-headers:

sudo apt-get install linux-headers-$(uname -r)

После всех манипуляций выше должны в системе присутствовать:

  • vagrant 1.8.4
  • VirtualBox 5.0
  • linux-headers

Всё готово для использования.

Создаём машину по-умолчанию

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

vagrant init

эта команда в текущей директории создат Vagrantfile (можете посмотреть его в vim любимом текстовом редакторе), который будет считывать следующей командой:

vagrant up

Эта команда скачает базовый образ из репозитория vagrant и на его основе создаст виртуальную машину для VirtualBox.

Маленькие прелести vagrant:
  • Автоматическое подмонтирование корневой директории (с Vagrantfile`ом) в гостя;
  • Автоматический проброс порта для ssh (хост - 2222, гость - 22);
  • Создание ssh-ключей;
  • чтоб подключиться к машине, достаточно выполнить
vagrant ssh

И всё, мы внутри гостя.

Создаём собственный Vagrantfile

Создание собственного файла позволяет очень гибко настроить саму машину и систему. То есть, указать количество памяти, процессор, другие опции провайдера (в данном случае VirtualBox) и можно указать, что делать в самой системе - установка приложений, конфигурация, копирование файлов.

Самый быстрый способ создать уникальный Vagrantfile - создать пример(темплейт) командой

vagrant init

и далее редактировать сгенерированный файл.

Вот небольшой пример Vagrantfile с одной машиной, которая содержит образ Ubuntu 14.04.

# -*- mode: ruby -*-
# vi: set ft=ruby :
Vagrant.configure(2) do |config|
  config.vm.box = "ubuntu/trusty64"
  config.vm.box_check_update = false
  config.vm.hostname = "testing"  
  config.vm.network "public_network", ip: "192.168.1.100"
  config.vm.define "testing"
  config.vm.provider "virtualbox" do |vb|
     vb.gui = false
     vb.memory = "1024"
  end
end

Он написан на языке Ruby. Знать язык не обязательно для работы, всё просто.

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

Далее, строка

Vagrant.configure(2) do |config|

начинает цикл, подставляя вместо «Vagrant.configure» ⇒ «config»

следующая:

config.vm.box = "ubuntu/trusty64"

указываем название образа. Название образа можно выбрать отсюда (официальный репозиторий) или создать самому (немного сложнее).

Едем дальше:

config.vm.box_check_update = false

По-умолчания, vagrant перед каждым запуском образа проверяет репозиторий на наличие обновлений. Это занимает какое-то время и не особо нам нужно, так как используем LTS версию.

Тут всё понятно - задаём hostname:

config.vm.hostname = "testing"  

Следующими строками мы задаем публичный (то есть, наша виртуалка доступна по этому адресу внутри сети) адрес. Если не указать его явно, то нам его подарит DHCP.

Строка:

 config.vm.define "testing"

задаёт имя машины для vagrant и VBox. Это не обязательно, но лучше это сделать, потому что удобнее обращаться к машинам по именам (см. п. 6)

И, наконец, мы говорим нашему провайдеру, что нам не нужен графический интерфейс (для VirtualBox по-умолачнию идёт значение False) и что памяти мы готовы выделить не больше 1 Гб:

  config.vm.provider "virtualbox" do |vb|
     vb.gui = false
     vb.memory = "1024"
  end

Более развёрнутая информация о Vagrantfile тут

vagrant, VBox и сеть

Private network

С частной сетью понятно - мы делаем собственную сеть LAN, которая будет состоять из виртуальных машин. Для доступа к такой сети из хоста нужна пробрасывать порт через Vagrantfile (или через Vbox, но через vagrant удобнее). А для доступа из реальной сети, то есть, например из другой физической машины, мы должны будем стучаться на IP хоста.

Это удобно, если создавать виртуалку для «поиграться» или если планируется использовать виртуалку внутри сети и за NAT (например, она получит адрес от DHCP другой виртуалки, которая будет выполнять роль шлюза).

сконфигурировать можно в Vagrantfile следующим способом:

config.vm.network "private_network", ip xxx.xxx.xxx.xxx

IP можно не указывать, можно сделать так:

config.vm.network "private_network", type: "dhcp"

и адрес назначится автоматически.

больше информации о приватных сетях тут

Public network

Публичная сеть позволяет добавить машину в вашу подсеть так, как будто появилась дополнительная железная машина. С публичной сетью нет необходимости пробрасывать порты - всё доступно по адресу виртуалки. Для всех машин в этой же подсети.

Тут надо быть осторожным, если вы не администратор сети. Так как в подсеть включится новая машина со своим адресом и именем, это может создать некоторые проблемы с DNS и\или DHCP на основном шлюзе.

Создать публичную сеть можно так:

config.vm.network "public_network", ip: "192.168.1.100"

Если не задать адрес, то он будет задан dhcp-сервером в реальной подсети. По факту, публичная сеть использует bridge-соединение с WAN-адаптером хоста. Если у вас два физических адаптера (две сетевых карты, например проводная и беспроводная), то vagrant спросит, какую из них использовать. В случае, если у вас два адаптера, то вы можете указать его:

config.vm.network "public_network", bridge: "eth1" 

больше информации о публичных сетях тут

Port Forwarding

Как уже упоминалось выше, vagrant сам пробрасывает 22 порт гостя на 2222 порт хоста для первой машины. Для следующих машин это будет 2200, 2300 - называется эта штука auto_correct Простыми словами - если у вас где-то есть конфликт портов(пробросили на уже занятый порт), то vagrant это дело увидит и сам исправит, радостно сообщив о сделанном в консоли.

Автоматически эта опция включена только для 22 порта, для портов, которые вы задаёте вручну, нужно указать эту опцию как

auto_correct: true

Где? возле каждого порта, который вы указали (пример ниже)

Для того, чтоб принудительно открыть порты, используется следующий синтаксис:

config.vm.network  "forwarded_port", guest: 80, host: 9999, auto_correct: true

в этом примере мы перенаправили стандартный 80 порт для веб-серверов (http) с гостя на 9999 порт хоста. Теперь, если у нас в виртуальной машине есть какой-либо веб-сервер (nginx, apache), то мы сможем попасть на него с хоста двумя способами:

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

config.vm.network  "forwarded_port", guest: 8080, host: 9898, host_ip: 127.0.0.1 

Если нам требуется пробросить несколько портов, то просто задаём две строки в Vagrantfile:

config.vm.network  "forwarded_port", guest: 80, host: 9999
config.vm.network  "forwarded_port", guest: 8080, host: 9898

И второй порт тоже будет доступен на хосте.

Никогда не назначайте проброс портов на стандартные! (например, 22 на 22) Иначе можно огрести неплохих проблем на хостовой машине!

По-умолчанию проброс идёт ТСР протокола, для того, чтоб проборосить UDP порт, это нужно явно указать:

config.vm.network "forwarded_port", guest: 35555, host: 12003, protocol: "udp" 

Синхронизация каталогов

«Из коробки» vagrant синхронизирует каталог хоста с Vagrantfile в директорию /vagrant виртуальной машины.

Для того, чтоб указать дополнительный каталоги для синхронизации, нужно добавить следующую строку в Vagrantfile:

config.vm.synced_folder "src/", "/var/www/html" 

Первым указывается путь на хосте, вторым - гостевой путь. Если на хостовой машине указывать относительный путь, то корневым будет каталог с Vagrantfile.

Если абсолютный путь, то он абсолютный :)

Путь на гостевой машине должен быть только абсолютный. Если директорий не существует, они будут созданы рекурсивно.

Дополнительные опции:

  • disabled - если указать True, то синхронизация будет отключена. Удобно, если нам не нужна «изкоробочная» синхронизация.
  • mount_options - дополнительные параметры, которые будут переданы команде mount при монтировании
  • type - полезная опция, которая позволяет выбрать тип синхронизации. Доступны следующие варианты:
    • NFS
    • rsync
    • SMB
    • VirtualBox
тип SMB доступен только для Windows-host!
тип NFS доступен только для Linux-host!

Если эта поция не указана, то vagrant выберет сам подходящую.

Я рекомендую для Linux-гостей использовать rsync - этот тип не требует дополнений гостевых систем, автоматически установить rsync на всех гостей. Также, доступны дополнительные плюшки, такие как vagrant rsync и vagrant rsync-auto ( о них ниже)

  • id - имя, которое будет показываться при команде mount в гостевой

машине. Имеет смысл использовать, если у вас несколько расшареных каталогов

vagrant не поддерживает синхронизацию симлинков!
Рассмотрим подробнее вариант rsync

Первое - этот тип работает только в одну сторону Каталоги, которые синхронизированы через rsync синхронизируются автоматически только один раз - при инициализации машины (vagrant up\vagrant reload). Принудительно синхронизировать можно двумя путями:

  1. vagrant rsync
  2. vagrant rsync-auto

Отличия можно понять по названиям:
первый вариант запускается один раз (синхронизировал и всё).

Имейте ввиду, что если вы сделали изменения в Vagrantfile в области синхронизации, то вам необходимо перед vagrant rsync сделать vagrant reload.

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

Если вы хотите сделать изменения в Vagrantfile, для начали остановите vagrant rsync-auto, внесите изменения, и потом перезапустите.

Для rsync есть куча опций, которые обычно не нужны. На мой взгляд, одна из самых полезных - rsync_exclude. (аналог gitignore) Опция позволяет исключить из списка синхронизации, которые не нужны. Это нужно, когда у вас есть директория, в которой содержится 1005 файлов. И вам нужно синхронизировать 1000, а 5 - не нужно. Гораздо проще исключить 5 файлов, чем добавлять 1000.

Использование команд vagrant

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

Пожалуй, самые необходимые команды это vagrant up, vagrant destroy, vagrant suspend, vagrant resume, vagrant ssh.

Теперь по порядку. vagrant up Эта команда смотрит в Vagrantfile и создаёт виртуальную машину согласно описанию. Если в Vagrantfile описано несколько мащин, то вы должны явно указать её имя. В противном случае будут подняты все машины. О мультимашинном Vagrantfile мы поговорим позже.

vagrant destroy Эта команда уничтожает машину полностью, найдя её описание в Vagrantfile. Точно также, как и при создании машины, вы должны указать имя машины, если их несколько.

Для того, чтоб удалить машину без подтверждения, используйте ключ -f

vagrant destroy -f 
Всегда сначала уничтожайте машину, а только потом удаляйте её из Vagrantfile! Иначе вы получите ошибку, что конфигурации такой машины не найдено.

vagrant suspend Эта команда отправляет машину в сон (аналог VirtualBox - сохранить состояние)

Требует наличие дополнительного свободного места, так как сохраняет ОЗУ вируталки.

После этого, машину можно «воскресить» командой:

vagrant resume Как уже сказано выше, эта команда пробуждает машину из сна.

vagrant ssh

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

Теперь еще парочку дополнительных команд, которые могут оказаться полезными.

vagrant global-status

Команда-исключение. Её можно выполнять в любой директории и она вернёт список машин, вместе с состоянием и с дирекорией, в которой размещён Vagrantfile. Пример использования:

ivan@Punka:~$ vagrant global-status
id       name   provider   state   directory                                   
-------------------------------------------------------------------------------
06bcbfa  dev    virtualbox running /home/ivan/infrastr 
a1a8f2b  ci     virtualbox running /home/ivan/infrastr 
 

Это удобно, если вы забыли(ну, разное бывает), в какой директории запустили vagrant. Эта команда всё покажет и рааскажет.

vagrant halt

shutdown обычный.

vagrant port

показывает список проброшенных портов. Пример:

vagrant port
 22 (guest) => 2222 (host)
 80 (guest) => 9999 (host)

vagrant reload Эта команда перезагрузки - сначала выключает машину, потом поднимает. Удобно, если надо применить изменения в конфигурации

Более подробно о других команда можно узнать на официальном сайте

Конфигурирование нескольких машин

В одном Vagrantfile может быть столько машин, сколько нам нужно. Задать их можно двумя способами:

  • Используя цикл
  • Отдельно задавая каждую машину

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

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

Использование цикла

$mach_quant = 3

Vagrant.configure("2") do |config|
 
  config.vm.provider "virtualbox" do |vb|
      vb.gui = false
      vb.memory=256
      vb.cpus=1
      vb.check_guest_additions=false
  config.vm.box_check_update=false
  config.vm.box="ubuntu/trusty64"
 end

(1..$mach_quant).each do |i|
    config.vm.define "node#{i}" do |node|
        node.vm.network "public_network", ip: "192.168.1.#{24+i}"
        node.vm.hostname = "node#{i}"
    end
end
  
end 

Первая строчки - мы создали переменную mach_quant и присвоили ей значение 3. Эта переменная и будет указывать на количество машин, которые vagrant будет поднимать.

Первый цикл конфигурирует провайдер VirtualBox, а именно задаёт:

  1. консольный режим (без интерфейса)
  2. 256 Мб памяти
  3. 1 виртуальный процессор
  4. отменяем проверку на гостевые дополнения. Так как мы используем серверную

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

Дальше уже знакомая строчка - не проверять обновления образа.

Теперь мы создаём цикл, который будет создавать машины, меняя название машины, хостнейм и ip адрес.

Идёт итерация от 1 до mach_quant (в данном случае три), номер машины подставляется в переменную і.

Значение i Имя машины ip адрес Хостнейм
1 node1 192.168.1.25 node1
2 node2 192.168.1.26 node2
3 node3 192.168.1.27 node3

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

vagrant up

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

vagrant up node2

Можно еще и так - задать сразу несколько машин.

vagrant up node2 node3

Описываем каждую машину отдельно

Вот пример Vagrantfile, в котором создаются 4 разных машины:

# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure(2) do |config|
  config.vm.box = "ubuntu/trusty64"
  config.vm.box_check_update = false


  config.vm.define "dev" do |dev|
      dev.vm.network  "public_network", ip: "192.168.1.160"
      dev.vm.hostname = "dev"  
      dev.vm.provider "virtualbox" do |vb|
         vb.memory = "4096"
      end
  end

  
 config.vm.define "db" do |db|
     db.vm.network "public_network", ip: "192.168.1.161"
     db.vm.hostname = "db"  
     db.vm.provider "virtualbox" do |vb|
         vb.memory = "1024"
     end
 end


 config.vm.define "ci" do |ci|
     ci.vm.network "public_network", ip: "192.168.1.162"
     ci.vm.hostname = "ci"  
     ci.vm.provider "virtualbox" do |vb|
         vb.memory = "2048"
     end
 end

 config.vm.define "lamp_node" do |lamp|
     lamp.vm.network "public_network", ip: "192.168.1.163"
     lamp.vm.hostname = "lamp_node"  
     lamp.vm.provider "virtualbox" do |vb|
         vb.memory = "512"
     end
 end

end

Теперь разберём:

прописываем глобальный конфиг для всех машин:

config.vm.box = "ubuntu/trusty64"
config.vm.box_check_update = false

Далее создаём машины:

  config.vm.define "dev" do |dev|
      dev.vm.network  "public_network", ip: "192.168.1.160"
      dev.vm.hostname = "dev"  
      dev.vm.provider "virtualbox" do |vb|
         vb.memory = "4096"
      end
  end

Всё почти так же, как при описании одной машины.

В результате команды

vagrant up 

у нас получится 4 машины:

Имя машины ip адрес Хостнейм Память
dev 192.168.1.160 dev 4096
db 192.168.1.161 db 1024
ci 192.168.1.162 ci 2048
lamp_node 192.168.1.163 lamp_node 512

Все машины с Ubuntu внутри, без графического интерфейса, обновления отключены.

Как можно понять по названиям, машины имеют разные назначения: dev - машина для разработчиков ci - машина для Сontinuous Integration server (Jenkins, TeamCity, etc) db - база данных lamp_node - машина для разворачивания LAMP-приложения.

Для каждой отдельной машины можно переопределить глобальные конфигурации, например, переопределим образ в одной из машин с Ubuntu на CentOS:

  config.vm.define "dev" do |dev|
      dev.vm.box = "centos/centos7"
      dev.vm.network  "public_network", ip: "192.168.1.160"
      dev.vm.hostname = "dev"  
      dev.vm.provider "virtualbox" do |vb|
         vb.memory = "4096"
      end
  end

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

Используем цикл и отдельное описание машин

Предположим, что у нас есть небольшой проект, в котором 5 разработчиков, один сервер непрерывной интеграции, две машины с базами данных(репликация и\или кластер). Добавим две ноды, на которые разворачивается приложение. И добавим машину, на которой будет крутиться балансировщик нагрузки (HAProxy, Nginx, httpd), он будет балансировать между нодами с приложениями. Итого 11 машин. Vagrantfile для всей инфраструктуры выглядит следующим образом:

# -*- mode: ruby -*-
# vi: set ft=ruby :

#задаём переменные с количеством машин

$dev_mach = 5
$db_mach = 2
$lamp_mach = 2

Vagrant.configure(2) do |config|
  config.vm.box = "ubuntu/trusty64"
  config.vm.box_check_update = false

#Cоздаём машины для разработчиков, количество задали в переменных.
#Пул ip адресов перенесли, чтоб не пересекались с другими машинами.

   (1..$dev_mach).each do |i|
        config.vm.define "dev#i" do |dev|
         dev.vm.network  "public_network", ip: "192.168.1.#{10+i}"
         dev.vm.hostname = "dev#i"  
         dev.vm.provider "virtualbox" do |vb|
                vb.memory = "4096"
         end
        end
   end


#Создаём машины для баз данных.

   (1..$db_mach).each do |i|
        config.vm.define "db#i" do |db|
         db.vm.network "public_network", ip: "192.168.1.#{50+i}"
         db.vm.hostname = "db#i"  
         db.vm.provider "virtualbox" do |vb|
                vb.memory = "1024"
         end
        end
   end
#Создамём машину для сервера непрерывной интеграции.

 config.vm.define "ci" do |ci|
     ci.vm.network "public_network", ip: "192.168.1.60"
     ci.vm.hostname = "ci"  
     ci.vm.provider "virtualbox" do |vb|
         vb.memory = "2048"
     end
 end
#Создаём машины для разворачивания приложения

    (1..lamp_mach).each do |i|
         config.vm.define "lamp#i" do |lamp|
          lamp.vm.network "public_network", ip: "192.168.1.#{150+i}"
          lamp.vm.hostname = "lamp_node#i"  
          lamp.vm.provider "virtualbox" do |vb|
           vb.memory = "512"
          end
         end
     end
#Создаём машину для шлюза.

 config.vm.define "gate" do |gate|
     gate.vm.network "public_network", ip: "192.168.1.2"
     gate.vm.hostname = "gate"  
     gate.vm.provider "virtualbox" do |vb|
         vb.memory = "2048"
     end
 end


end

По традиции, сводная таблица поднятых машин после выполнения

vagrant up
Имя машины ip адрес Хостнейм Память
dev1 192.168.1.11 dev1 4096
dev2 192.168.1.12 dev2
dev3 192.168.1.13 dev3
dev4 192.168.1.14 dev4
dev5 192.168.1.15 dev5
db1 192.168.1.51 db1 1024
db2 192.168.1.52 db2
ci 192.168.1.60 ci 2048
lamp_node1 192.168.1.151 lamp_node1 512
lamp_node2 192.168.1.152 lamp_node2
gate 192.168.1.2 gate 2048

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

Vagrant provision

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

  • Puppet
  • Ansible
  • Salt
  • Shell
  • Chef
  • Docker

При чём некоторые из них, например, Chef, Puppet, Salt имеют несколько вариантов использования (мастер-агент, без мастера, запуск локально).

Автоматически provision запускается только в двух случаях:

vagrant up и vagrant reload. Если вы хотите запустить подготовку машин принудительно, то необхожимо это явно указать. Например:

vagrant resume --provision %machine_name
vagrant provision %machine_name

Рассмотрим самый простой вариант - shell.

Provisioning with shell

В этом подразделе рассмотрим пост-конфигурацию машин с помощью shell. Парочка примеров, естественно будет под Linux, хотя и PowerShell тоже годится.

Чтоб vagrant исполнил код после загрузки необходимо добавить пару строк в Vagrantfile.

Выполнение одной команды

Если необходимо выполнить одну команду (например, удалить какой-то стандартный файл), то удобнее всего использовать следующую форму записи:

Vagrant.configure("2") do |config|
  config.vm.provision "shell",
    inline: "echo Hello, World"
end

Эта строка задаёт тип провизора (подготовщика). Мы указали shell.

config.vm.provision "shell",

строкой

inline: "echo Hello, World"

Мы задаём непосредственно команду. Всё, что идёт после двоеточия, будет выполнено в интерпретаторе по-умолчанию.

Есть несколько полезных команд, которые могут использоваться вместе с shell.

args - аргументы, которые будут переданы при выполнении команды или скрипта. Могут быть заданы как обычной строкой, так и массивом.

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

Vagrant.configure("2") do |config|
  config.vm.provision "shell" do |s|
    s.inline = "echo $1"
    s.args   = "'hello, world!'"
  end
end

Обратите внимание на одинарные кавычки вокруг hello, world!

они обязательны.

Результатом будет вывод: hello, world!

Можно задать список аргументов массивом - это удобно тем, что можно не заморачиваться с кавычками и задать несколько аргументов:

Vagrant.configure("2") do |config|
  config.vm.provision "shell" do |s|
    s.inline = "echo $1; touch $2"
    s.args   = ["hello, world!", "new_file.txt"]
  end
end

privileged - определяет, от какого юзера запускать команду. По-умолчанию установленно True, что запустит скритп от root. Если команда должна запускаться от стандартного юзера (vagrant), то установите значения False.

Выполнение скрипта. Пишем скрипт на shell в Vagrantfile

Если вам нужно выполнить не одну строку, а целый скрипт, то удобнее это сделать немного по-другому - как сказано в официальной документации - «добавим совсем немного Ruby и получим отличный способ объеденить bash и Vagrantfile.»

$script = <<END
echo "I am provisioning..."
touch 1.txt
echo "I'm a text from Vagrantfile!" > 1.txt
echo "File was created, you can test it now!"
END

Vagrant.configure("2") do |config|
  config.vm.provision "shell", inline: $script
end

Тут мы создаём переменную script, в переменную пихаем нужные нам команды. Далее вызываем наш текст командой:

  config.vm.provision "shell", inline: $script

Всё предельно просто.

Выполнение скрипта. Используем внешний файл

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

В данном случае очень подойдёт умение vagrant выполнять внещние скрипты. Делается это следующим образом:

Vagrant.configure("2") do |config|
  config.vm.provision "shell", path: "script.sh"
end

При этом, корень относится в директории с Vagrantfile, если он путь относительно.

Без проблем можно использовать и абсолютные пути (нормально работают и вещи вроде ~)

Есть очень полезная возможность - можно в path указать даже URL, по которому доступен скрипт.

Если же у вас скрипт необходимо запустить из определённой директории внутри гостевой машины, vagrant позволяет делать и это. Необходимо лищь указать необходимую директорию командой upload_path.

Provisioning with Ansible

Тут не будет рассматриваться работа непосредстваенно Ansible, рассматривается взаимодействие vagrant и Ansible!

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

При необходимости создать полноценное окружение разработчика, или если нужно развернуть СУБД и БД из дампа, то более целесообразно использовать более мощные инструменты. Одним из таких инструментов является Ansible.

Чтобы применить Ansible через vagrant необходимо сделать следующее:

  • на хостовой машине должен быть установлен Ansible
  • прописать в Vagrantfile 1) примерно следующее:
Vagrant.configure("2") do |config|

  #
  # Run Ansible from the Vagrant Host
  #
  config.vm.provision "ansible" do |ansible|
    ansible.playbook = "playbook.yml"
  end

end

При этом, Playbook.yml должен находиться в одном каталоге с Vagrantfile. После того, как машина будет поднята, vagrant запустит Ansible.

Так как сам по себе Ansible не требует никакого агента на целевой машине (ноде), то всё, что нужно сделать - указать верные данные в инвентори-файле. Если используется публичная сеть, то указываем ip ноды, если используется приватная сеть, то необходимо указать ip локалхоста (обычно 127.0.0.1) и порт.

Для Ansible имеется достаточно много опций, узнать их можно на странице официальной документации vagrant

Помимо того, что можно запускать Ansible с хоста, его можно также запускать и в самой гостевой мащине. Называется это Ansible local.

Преимущество в том, что нет необходимости устанавливать Ansible на хост. Вы можете просто загрузить роль с вашего репозитория или с Ansible galaxy и она выполнится.

Недостатки - на каждую машину необходимо устанавливать Ansible (есть хук, о нём ниже), отсутствие централизованного управления. То есть, если в дальнейшем будет необходимость управлять машинами посредством Ansible, то этот метод не для вас.

По-умолчанию, vagrant попробует сам установить Ansible на гостувую машину, для этот предусмтрено несколько опций:

install_mode - по-факту, это выбор репозитория, откуда будет устанавливаться Ansible. Если оставить значение default, то будет выбран:

  1. ppa:ansible/ansible - для гостя Ubuntu + family
  2. EPEL - RedHat-family
  3. main repo - Debian, OpenSuse, FreeBSD, Arch, etc.

Есть другое значение - pip. Тогда установка будет производиться посредтсвом pip. vagrant сначала установит pip на гостя, а затем установит Ansible.

Этот вариант предпочтительней, так как это гарантирует, что версия будет свежая и одинаковая для всех нод.

Часть Vagrantfile, которая отвечает за Ansible local практически ничем не отличается от обычного Ansible:

Vagrant.configure("2") do |config|
  config.vm.provision "ansible_local" do |ansible|
    ansible.playbook = "playbook.yml"
  end
end

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

Всё, что нужно сделать вам - написать Vagrantfile, роли для Ansible с inventory и немного подправить конфигурацию Ansible.

Разберём всё подробнее.

Vagrantfile:

Vagrant.configure("2") do |config|

  config.vm.box = "ubuntu/trusty64"

  config.vm.define "node1" do |machine|
    machine.vm.network "private_network", ip: "172.17.177.21"
  end

  config.vm.define "node2" do |machine|
    machine.vm.network "private_network", ip: "172.17.177.22"
  end

  config.vm.define 'controller' do |machine|
    machine.vm.network "private_network", ip: "172.17.177.11"

    machine.vm.provision :ansible_local do |ansible|
      ansible.playbook       = "example.yml"
      ansible.verbose        = true
      ansible.install        = true
      ansible.limit          = "all"
      ansible.inventory_path = "inventory"
    end
  end

end

Создаём две машины - node1 и node2. Это будут машины, которые мы будем конфигурировать с помощью Ansible, который установим на третью машину - controller.

vagrant сам установит на нужную машину Ansible. Что нам нужно - так это сделать нормальный инвентори файл, в котором будут указаны ip адреса наших нод.

Вот пример для этого файла:

controller ansible_connection=local
node1      ansible_ssh_host=172.17.177.21 ansible_ssh_private_key_file=/vagrant/.vagrant/machines/node1/virtualbox/private_key
node2      ansible_ssh_host=172.17.177.22 ansible_ssh_private_key_file=/vagrant/.vagrant/machines/node2/virtualbox/private_key

И этот файлик положим в директорию с Vagrantfile, в корень.

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

[defaults]
host_key_checking = no

[ssh_connection]
ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o UserKnownHostsFile=/dev/null -o IdentitiesOnly=yes

Всё, можно делать vagrant up и смотреть, как создаётся окружение.

Provisioning with Puppet

Тут не будет рассматриваться работа непосредстваенно Puppet, рассматривается взаимодействие vagrant и Puppet!

Для тех, кто привык использовать Puppet, есть возможность использовать этот инструмент с vagrant.

Для этого нам нужна следующая структура Vagrantfile:

 config.vm.provider "virtualbox" do |vb|
     vb.gui = false
     vb.memory=256
     vb.cpus=1
     vb.check_guest_additions=false
 config.vm.box="puppetlabs/centos-7.2-64-puppet"
end
 config.vm.define "node1" do |n1|
   n1.vm.network "private_network", ip: "192.168.0.101"
   n1.vm.network "forwarded_port", guest: 80, host: 8081
   n1.vm.hostname ="node1"
 end

Основное изменение, которое отличает vagrant и Puppet, от vagrant и Ansible - vagrant не может сам установить Puppet в гостя, поэтому строкой

config.vm.box="puppetlabs/centos-7.2-64-puppet"

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

докер, паппет.

* FIXME

1)
В этом разделе листинги взяты с официального сайта vagrant