# Vagrant Multi-Node В данной практике рассмотрим такие функции [vagrant][] как создание собственного [box][] из развернутой машины, а также создание нескольких машин в одном [Vagrantfile][]. В качестве лабораторного стенда попробуем реализовать простую схему развертывания в виде трех виртуальных машин: front - машина отдающая статическую html страницу, back - машина с приложением реализующим бизнес логику, db - машина с базой данных. ## Package Для удобства развертывания можно на основе базового бокса создать боксы с необходимой конфигурацией, чтобы не производить подготовку при каждом запуске. Для данной операции есть команда `vagrant package`, которая создаст и сохранит на файловой системе бокс из текущей запущенной виртуальной машины. ### Front Создадим подготовленный бокс для виртуальной машины front, за базовый бокс возьмем `ubuntu/lunar64` и напишем `Vagrantfile` для установки `nginx`, который будет отдавать статическую страницу: ```ruby Vagrant.configure("2") do |config| config.vm.box = "ubuntu/lunar64" config.vm.provision "shell", inline: <<-SHELL apt-get update apt-get install -y nginx SHELL end ``` Запустим вм: ```console $ vagrant up Bringing machine 'default' up with 'virtualbox' provider... ==> default: Importing base box 'ubuntu/lunar64'... ==> default: Matching MAC address for NAT networking... ==> default: Setting the name of the VM: vagrant2_default_1694453192956_9259 ==> default: Clearing any previously set network interfaces... ==> default: Preparing network interfaces based on configuration... default: Adapter 1: nat ==> default: Forwarding ports... default: 22 (guest) => 2222 (host) (adapter 1) ==> default: Running 'pre-boot' VM customizations... ==> default: Booting VM... ==> default: Waiting for machine to boot. This may take a few minutes... default: SSH address: 127.0.0.1:2222 default: SSH username: vagrant default: SSH auth method: private key ==> default: Machine booted and ready! ``` Дополнительно сохраним себе приватный ключ для доступа по ssh к виртуальной машине - он нам понадобится в дальнейшем, путь к нему указан в параметре `IdentityFile` в выводе команды `vagrant ssh-config`: ```console $ vagrant ssh-config | grep IdentityFile IdentityFile /home/alex/infra-course/example/vagrant2/vagrant_insecure_key # сохранить в текущую директорию по именем "key" можно следующей командой $ vagrant ssh-config | awk '/IdentityFile/{print $2}' | xargs -i cp {} ./key ``` Сохраним бокс на файловую систему и добавим в локальное хранилище: ```console $ vagrant package --output front.box ==> default: Attempting graceful shutdown of VM... ==> default: Clearing any previously set forwarded ports... ==> default: Exporting VM... ==> default: Compressing package to: /home/alex/infra-course/example/vagrant2/front.box $ vagrant box add --name front front.box ==> box: Box file was not detected as metadata. Adding it directly... ==> box: Adding box 'front' (v0) for provider: box: Unpacking necessary files from: file:///home/alex/infra-course/example/vagrant2/front.box ==> box: Successfully added box 'front' (v0) for 'virtualbox'! ``` После чего уничтожим вм и удалим бокс из файловой системы: ```console $ vagrant destroy -f ==> default: Forcing shutdown of VM... ==> default: Destroying VM and associated drives... $ rm front.box ``` ### Back Для виртуальной машины back подготовим среду для работы приложения, для бэкенда будем использовать язык golang, так что нам понадобится установить пакеты для сборки. В качестве базового образа также возьмем `ubuntu/lunar64`, напишем `Vagrantfile`: ```ruby Vagrant.configure("2") do |config| config.vm.box = "ubuntu/lunar64" config.vm.provision "shell", inline: <<-SHELL apt-get update apt-get install -y golang SHELL end ``` Проделаем те же действия, что и в предыдущем пункте: ```console $ vagrant up Bringing machine 'default' up with 'virtualbox' provider... ==> default: Importing base box 'ubuntu/lunar64'... ==> default: Matching MAC address for NAT networking... ==> default: Setting the name of the VM: vagrant2_default_1694457463073_44923 ==> default: Clearing any previously set network interfaces... ==> default: Preparing network interfaces based on configuration... default: Adapter 1: nat ==> default: Forwarding ports... default: 22 (guest) => 2222 (host) (adapter 1) ==> default: Running 'pre-boot' VM customizations... ==> default: Booting VM... ==> default: Waiting for machine to boot. This may take a few minutes... default: SSH address: 127.0.0.1:2222 default: SSH username: vagrant default: SSH auth method: private key ==> default: Machine booted and ready! $ vagrant package --output back.box ==> default: Attempting graceful shutdown of VM... ==> default: Clearing any previously set forwarded ports... ==> default: Exporting VM... ==> default: Compressing package to: /home/alex/infra-course/example/vagrant2/back.box $ vagrant box add --name back back.box ==> box: Box file was not detected as metadata. Adding it directly... ==> box: Adding box 'back' (v0) for provider: box: Unpacking necessary files from: file:///home/alex/infra-course/example/vagrant2/back.box ==> box: Successfully added box 'back' (v0) for 'virtualbox'! $ vagrant destroy -f ==> default: Destroying VM and associated drives... $ rm back.box ``` ### DB Для виртуальной машины с базой данных также используем в качестве базового образа `ubuntu/lunar64`, в качестве субд возьмем postgresql. Также при подготовке добавим конфигурацию, которая даст возможность удаленного подключения из любой доступной сети. Таким образом получим следующее содержание для `Vagrantfile`: ```ruby Vagrant.configure("2") do |config| config.vm.box = "ubuntu/lunar64" config.vm.provision "shell", inline: <<-SHELL apt-get update apt-get install -y postgresql echo "listen_addresses = '*'" >> /etc/postgresql/15/main/conf.d/listen.conf echo "host all all 0.0.0.0/0 trust" >> /etc/postgresql/15/main/pg_hba.conf SHELL end ``` Проделаем те же действия, что и в предыдущем пункте: ```console $ vagrant up Bringing machine 'default' up with 'virtualbox' provider... ==> default: Importing base box 'ubuntu/lunar64'... ==> default: Matching MAC address for NAT networking... ==> default: Setting the name of the VM: vagrant2_default_1694458609907_72643 ==> default: Clearing any previously set network interfaces... ==> default: Preparing network interfaces based on configuration... default: Adapter 1: nat ==> default: Forwarding ports... default: 22 (guest) => 2222 (host) (adapter 1) ==> default: Running 'pre-boot' VM customizations... ==> default: Booting VM... ==> default: Waiting for machine to boot. This may take a few minutes... default: SSH address: 127.0.0.1:2222 default: SSH username: vagrant default: SSH auth method: private key ==> default: Machine booted and ready! $ vagrant package --output db.box ==> default: Attempting graceful shutdown of VM... ==> default: Clearing any previously set forwarded ports... ==> default: Exporting VM... ==> default: Compressing package to: /home/alex/infra-course/example/vagrant2/db.box $ vagrant box add --name db db.box ==> box: Box file was not detected as metadata. Adding it directly... ==> box: Adding box 'db' (v0) for provider: box: Unpacking necessary files from: file:///home/alex/infra-course/example/vagrant2/db.box ==> box: Successfully added box 'db' (v0) for 'virtualbox'! $ vagrant destroy -f ==> default: Destroying VM and associated drives... $ rm db.box ``` ## Multi-Node После проделанных действий у нас появилось еще 3 бокса: ```console $ vagrant box list back (virtualbox, 0) db (virtualbox, 0) front (virtualbox, 0) ubuntu/lunar64 (virtualbox, 0) ``` Теперь имея боксы для разных виртуальных машин можно позаботиться их наполнением. ### Code Опишем программный код, который будет деплоиться на каждую машину. #### Front На виртуальной машине front мы будем отдавать статичную `html` страницу с простым `js` скриптом, сохраним его в файл `index.html`: ```html