Это старая версия документа.
Содержание
В теме описано использование vagrant с провайдером VirtualBox.
Подробное содержание доступно справа.
Установка vagrant + VirtualBox
Устанавливать будем с официального сайта, скачав подходящие deb-пакеты. При этом нужно учесть, что последняя версия vagrant(1.8.4) НЕ поддерживает последнюю версию VirtualBox(5.1.0).
ссылка на х64 версию VirtualBox для 14.04
Скачиваем и устанавливаем 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
Публичная сеть позволяет добавить машину в вашу подсеть так, как будто появилась дополнительная железная машина. С публичной сетью нет необходимости пробрасывать порты - всё доступно по адресу виртуалки. Для всех машин в этой же подсети.
Создать публичную сеть можно так:
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
И второй порт тоже будет доступен на хосте.
По-умолчанию проброс идёт ТСР протокола, для того, чтоб проборосить 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
Если эта поция не указана, то vagrant выберет сам самую подходящую.
Я рекомендую для Linux-гостей использовать rsync - этот тип не требует дополнений гостевых систем, автоматически установить rsync на всех гостей. Также, доступны дополнительные плюшки, такие как vagrant rsync и vagrant rsync-auto ( о них ниже)
- id - имя, которое будет показываться при команде mount в гостевой
машине. Имеет смысл использовать, если у вас несколько расшареных каталогов
Рассмотрим подробнее вариант rsync
Первое - этот тип работает только в одну сторону Каталоги, которые синхронизированы через rsync синхронизируются автоматически только один раз - при инициализации машины (vagrant up\vagrant reload). Принудительно синхронизировать можно двумя путями:
- vagrant rsync
- vagrant rsync-auto
Отличия можно понять по названиям:
первый вариант запускается один раз (синхронизировал и всё).
Второй же работает в режиме демона и отслеживает изменения на хосте. Это удобно, так как один каталог можно шарить на несколько машин сразу, передавая изменения на всех гостей.
Для rsync есть куча опций, которые обычно не нужны. На мой взгляд, одна из самых полезных - rsync_exclude. (аналог gitignore) Опция позволяет исключить из списка синхронизации, которые не нужны. Это нужно, когда у вас есть директория, в которой содержится 1005 файлов. И вам нужно синхронизировать 1000, а 5 - не нужно. Гораздо проще исключить 5 файлов, чем добавлять 1000.
Использование команд vagrant
Пожалуй, самые необходимые команды это vagrant up, vagrant destroy, vagrant suspend, vagrant resume, vagrant ssh.
Теперь по порядку. vagrant up Эта команда смотрит в Vagrantfile и создаёт виртуальную машину согласно описанию. Если в Vagrantfile описано несколько мащин, то вы должны явно указать её имя. В противном случае будут подняты все машины. О мультимашинном Vagrantfile мы поговорим позже.
vagrant destroy Эта команда уничтожает машину полностью, найдя её описание в Vagrantfile. Точно также, как и при создании машины, вы должны указать имя машины, если их несколько.
Для того, чтоб удалить машину без подтверждения, используйте ключ -f
vagrant destroy -f
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, а именно задаёт:
- консольный режим (без интерфейса)
- 256 Мб памяти
- 1 виртуальный процессор
- отменяем проверку на гостевые дополнения. Так как мы используем серверную
версию дистрибутива, а проверка замедляет запуск виртуальной мащины, то это вполне логично.
Дальше уже знакомая строчка - не проверять обновления образа.
Теперь мы создаём цикл, который будет создавать машины, меняя название машины, хостнейм и 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 имеют несколько вариантов использования (мастер-агент, без мастера, запуск локально).
Рассмотрим самый простой вариант - 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.
* FIXME