В прошлой части мы разработали механизм сборки образов виртуальных машин с необходимым программным обеспечением для тестирования клиент-серверных баз 1С. В этот раз мы разберёмся, как из этих образов развернуть необходимое нам количество машин для их работы в качестве узлов CI-контура и обеспечить их уникальную идентификацию в сети. Иными словами, построим сеть из виртуальных машин, развёрнутых из одного или нескольких разных образов Vagrant.
Если Вы предпочли не собирать собственный образ машины через Packer, а решили загрузить уже готовый из облака HashiCorp, то всё написанное далее также будет справедливо, поскольку относится к возможностям именно Vagrant и не зависит от того как именно собран образ. Разумеется, если образ не собирался самостоятельно, то в нём не будет платформы 1С и специальной версии PostgreSQL для 1С - и их нужно будет установить уже после развёртывания.
Схема конфигурации узлов
Мы уже использовали команду "vagrant up" для запуска виртуальной машины с настройками, заданными в файле Vagrantfile. И в этот раз предстоит сделать то же самое. Но в этот раз управляющий запуском Vagrantfile должен обеспечить настройку виртуальной машины таким образом, чтобы она могла:
- Подключаться к главному узлу CI-сервера. В нашем случае это Jenkins на хостовой машине.
- Получать от него команды и выполнять как задачи по эмуляции интерактивных действий пользователя, так и служебные задачи развертывания баз, выгрузки данных из баз 1С и хранилища конфигураций.
- Загружать необходимые для этого файлы с хостовой машины. И наоборот, размещать результаты своей работы на хостовой машине.
Для этого нам предстоит:
- Выделить ресурсы виртуальной машине в соответствии с возможностями хостовой машины.
- Подключить общий для всех виртуальных машин каталог, примонтировав его с хостовой машины.
- Установить разрешение экрана, пригодное для запуска сценарных тестов на 1С.
- Загрузить недостающие конфигурационные файлы с хостовой машины.
- Настроить автозапуск агента CI-сервера, его подключение к мастер-узлу, находящемуся на хостовой машине, и обновление агента при обновлении самого сервера.
Также мы обеспечим связь виртуальных машин друг с другом и с внешним окружением через bridge-подключение к физической сетевой карте. При этом необходимо избежать конфликта IP-адресов и присвоить машинам уникальные имена (имена хостов). Такая настройка позволит добиться нескольких целей:
- Можно будет подключиться с любой машины в подсети по ssh и выполнить мониторинг системы, при этом не придется запускать никаких окон в пользовательском сеансе, выполняющем тестирование. Это позволяет работать с со сборочным узлом и не мешать при этом процессам сценарного тестирования 1С, которые на нём выполняются.
- Появится возможность скопировать файлы через scp. Даже если графический интерфейс "завис", то такое подключение часто всё ещё можно установить и забрать с узла необходимые логи или артефакты сборки.
- Также будет возможен поиск клиентских лицензий 1С в локальной сети, в том числе на машинах отличных от хостовой. Для этого достаточно будет копировать преднастроенный файл nethasp.ini с указанием нужного IP-адреса каталог /opt/1C/v8.3/x86_64/conf/ виртуальной машины, аналогично тому, как мы будем делать это с другими конфигурационными файлами.
В схеме, описанной в первой части этого руководства, все узлы CI-контура полностью изолированы друг от друга. Но обеспечивая связь между ними по сети мы также получаем возможность создать и другую конфигурацию CI-контура.
Например разместить сервер СУБД и сервер 1С на одной из виртуальных машин или даже на хостовой машине. Остальные машины при этом будут осуществлять подключение к этому серверу. Такая схема менее удобна с точки зрения управления базами при многопоточном CI-процессе. Удаление узла CI ещё не будет означать удаления созданной через него базы. За базами придётся следить, обеспечивать им уникальные имена в алгоритмах сборок и не забывать удалять их вместе с виртуальными машинами. В то же время будет получен значительный выигрыш по скорости развертывания тестовых базы, особенно если в качестве основы для них используются копии больших рабочих баз. Ведь в этом случае процесс развертывания можно настроить и оптимизировать только на одной машине и выделить на ней больше ресурсов под СУБД.
Jenkins - на потом
Один названных выше пунктов - настройку автозапуска и обновления агента Jenkins - в этот раз выполнять не будем. Дело в том, что перед этим необходимо рассмотреть процесс конфигурирования самого CI-сервера Jenkins на хостовой машине, чтобы понимать причины и следствия всех выполняемых действий. Этому будет целиком посвящена следующая публикация. Но все остальные действия можно выполнить уже сейчас.
Расположение Vagrantfile
Вагрантфайл пишется на языке Ruby. Изучение этого языка не является необходимым, и работа с этим файлом возможна также, как и с другими конфигурационными файлами - путем комбинации различных примеров из документации и открытых репозиториев. И судя по документации даже авторы утилиты vagrant предпочитают, чтобы пользователи воспринимали этот файл именно как конструктор, который формируется на основе сниппетов - блоков готового кода, где вы заменяете одни параметры на другие.
Шаблон Вагратфайла создается в текущей директории командой vagrant init. Файл создаётся фактически пустым, затем его нужно открыть на редактирование и заполнить таким содержимым, которое "объясняет" утилите vagrant из какого образа развернуть виртуальную машину и с какими настройками это сделать:
Затем на основе этого файла командой vagrant up разворачивается и загружается виртуальная машина. Напоминаю, что простейший пример, иллюстрирующий эти действия был рассмотрен в конце прошлой публикации.
Файлы самой виртуальной машины хранятся при этом не в этом каталоге, а в том, который задан в настройках гипервизора:
В каталоге, где находится Vagrantfile, при запуске машины сохраняются только основные данные о ней. При выполнении команды vagrant up создается подкаталог .vagrant, хранящий:
- идентификатор виртуальной машины
- ключ для установки ssh-подключения без пароля
- и некоторые её метаданные.
То есть Vagrantfile и его каталог предназначены только для управления виртуальной машиной - ее запуска, конфигурирования и остановки. А с точки зрения гипервизора созданная виртуальная машина ничем не отличается от созданной вручную, располагается там же и может точно также управляться через стандартный интерфейс гипервизора.
Каталог .vagrant, создаваемый при выполнении команды vagrant up может хранить информацию только об одной машине.
Для хранения метаданных каждой виртуальной машины необходимо создавать отдельный подкаталог. В нем также удобно размещать и прочие конфигурационные файлы, специфичные для конкретной машины.
В документации (https://www.vagrantup.com/docs/vagrantfile) говорится, что Vagrantfile можно расположить в любой директории верхнего уровня относительно той, где выполняется команда vagrant up. При этом утилита vagrant сама рекурсивно поднимется выше по дереву каталогов, обнаружит где находится этот файл и запустит виртуальную машину на его основе. Но на практике в этом нет большого смысла. Потому что каталог .vagrant всё равно будет создан ровно в том же каталоге, где будет найден Vagrantfile. То есть нельзя просто создать разные подкаталоги для каждой виртуальной машины и создать один общий файл Vagrantfile для них, разместив его на один каталог выше. Этот файл нужно разместить в каждом каталоге для каждой виртуальной машины.
Но наш Vagrantfile по аналогии с JSON-файлом для Packer будет универсальным. Он будет содержать общий код для запуска любой из виртуальных машин, которые предполагается использовать как узлы CI-сервера Jenkins. За параметры запуска при этом будут отвечать небольшие командные файлы специфичные для каждой машины.
Чтобы избежать многократного дублирования кода разместим наш универсальный Vagrantfile в родительском каталоге относительно каталогов, предназначенных для хранения данных отдельных машин. Запуск виртуальных машин организуем через командные файлы (bat-файлы), и в этих файлах перед выполнением команды vagrant up, запускающей машину, будем просто копировать файл из родительского каталога в каталог отдельной виртуальной машины, выполняя команду cp ..\Vagrantfile Vagrantfile.
Можно также "обмануть" утилиту vagrant и вместо копирования файла создавать одноименную символическую ссылку на этот общий Vagrantfile командой mklink Vagrantfile "..\Vagrantfile", однако эту команду обязательно требуется запускать с правами администратора, что может быть неудобно.
Взаимное расположение файлов будет следующим:
Структура Vagrantfile
Весь код этого файла обернут в конструкцию следующего вида
Vagrant.configure("2") do |config|
#...................
end
Эта конструкция говорит, что внутри нее мы работаем с объектом config, предоставляющим доступ к конфигурации виртуальной машины, и структура этого объекта соответствует второй версии конфигурационного объекта. Более подробно это описано в документации https://www.vagrantup.com/docs/vagrantfile/version.html.
Обращаясь через точку к полям этого объекта мы задаем все необходимые параметры для развертывания и запуска виртуальной машины. Например, чтобы сказать, что
- мы хотим развернуть виртуальную машину из образа, имя которого задается переменной окружения 'box_name',
- при этом следует проигнорировать Vagrantfile, включенный в состав этого образа как файл по умолчанию,
- машина в этом образе относится к классу Linux,
- по завершении запуска машины нужно вывести в консоль сообщение "Jenkins node started"
нам достаточно задать следующий код:
Vagrant.configure("2") do |config|
config.vm.box = ENV['box_name']
config.vm.ignore_box_vagrantfile = true
config.vm.guest = :linux
config.vm.post_up_message = "Machine started"
end
В целом назначение и отдельные блоки этого файла напоминают конфигурационный файл Packer. Можно создать отдельный раздел, определяющий какие ресурсы хостовой машины мы выделяем для гостевой машины. Как и в случае Packer делается это через доступ к свойствам отдельных провайдеров (гипервизоров). В нашем случае нужно обратиться к свойствам VirtualBox:
config.vm.provider :virtualbox do |v, override|
v.gui = true # Display the VirtualBox GUI when booting the machine
v.customize ["modifyvm", :id, "--memory", ENV['ram_memory_size_mb']]
v.customize ["modifyvm", :id, "--cpus", ENV['cpu_count']]
v.customize ["modifyvm", :id, "--vram", 64] # Video memory 64 MB
end
Как и в случае с Packer есть "провизионеры" - поставщики. Поставщики добавляются вызовом метода config.vm.provision. В них мы определяем какие-то действия с уже запущенной виртуальной машиной и то, какой механизм и при каких условиях эти действия будет выполнять.
Сейчас нам необходимы только два типа поставщиков, определяемых параметром type
- "file" - копирование файла с хостовой машины в виртуальную
- "shell" - исполнение команды оболочки sh/bash.
Но в отличие от JSON-файла для Packer, используемого один раз при сборке образа виртуальной машины, Vagrantfile используется при каждом ее запуске. Очевидно, что далеко не все команды, заданные в этом файле, имеет смысл выполнять при каждом запуске. Часть команд будет выполнять первоначальное конфигурирование при развертывании машины. Другая часть должна выполняться при каждом запуске. И управлять этим поведением позволяет параметр run.
Можно задать его равным "once" и команды будут выполнены только при первом развертывании машины, а при последующих запусках будут проигнорированы. Например копировать файл настройки сети из хостовой машины в виртуальную достаточно только один раз в процессе ее развертывания:
config.vm.provision "Copying netplan configuration file to VM", type: "file", run: "once" do |f|
f.source = Dir.pwd + "/netplan_config.yaml"
f.destination = "~/netplan_config.yaml"
end
Можно задать параметр run равным "always" и тогда команда будет выполняться при каждом запуске виртуальной машины через команду vagrant up. Например при каждом запуске можно устанавливать одинаковое разрешение экрана:
config.vm.provision "Setting screen resilution via xrandr", type: "shell", run: "always" do |s|
s.inline = "xrandr -display :0.0 -s 1600x1200" # sleep 10 - waiting boot process to end and GUI to appear
s.privileged = false
end
Можно указать run "never" и тогда команды никогда не будут выполняться при запуске виртуальной машины через команду vagrant up.
Тем не менее их можно будет явно вызвать командой vagrant provision --provision-with имя_блока_provision. Это очень удобная возможность для вывода диагностических сообщений. Например чтобы выполнить команду ps aux | grep "agent.jar" внутри виртуальной машины, но отобразить её вывод в консоли хостовой машины, из консоли хостовой машины достаточно будет выполнить команду vagrant provision --provision-with CheckJenkinsNode.
config.vm.provision "CheckJenkinsNode", type: "shell", run: "never" do |s|
$checkJenkinsAgentScript =<<-SCRIPT
sleep 30
echo "Output of ps aux | grep agent.jar must contain information about running jenkins agent."
echo "If there is no such information maybe Jenkins server or Jenkins agent is stopped. The output is below:"
ps aux | grep agent.jar
SCRIPT
s.inline = $checkJenkinsAgentScript
end
В Vagrantfile можно объявлять переменные. Например в целях отладки тип запуска большинства провизионеров можно задать в переменной $initRunType, а затем вместо констант "once", "never" или "always" использовать эту переменную. Таким образом можно быстро переключаться между выполнением команд только при первом запуске и выполнением команд при каждом запуске машины. Такой подход позволит упростить отладку Vagrantfile и выполняемых с помощью него действий:
$initRunType = "once"
#..............
config.vm.provision "Copying netplan configuration file to VM", type: "file", run: $initRunType do |f|
f.source = Dir.pwd + "/netplan_config.yaml"
f.destination = "~/netplan_config.yaml"
end
Параметры взаимодействия с виртуальной машиной
Перейдем к детальному рассмотрению содержимого нашего Vagrantfile, ссылки на файл:
- на Гитлаб: https://gitlab.com/vladimirlitvinenko84/ci-infrastructure-for-1c/-/blob/master/jenkins-nodes/Vagrantfile
- на Гитхаб: https://github.com/VladimirLitvinenko84/ci-infrastructure-for-1c/blob/master/jenkins-nodes/Vagrantfile
Также как и в случае с Packer, взаимодействие с гостевой машиной будем выполнять через ssh-подключение. При запуске машины утилита vagrant будет периодически проверять её доступность и, как только появится возможность, установит с ней связь по ssh.
При этом в отличие от сборки образа с Packer здесь имя пользователя и пароль мы зададим непосредственно в файле в переменных $ssh_username и $ssh_password. Это единственное место, где они будут задаваться и они одинаковы во всех используемых образах, поэтому нет необходимости выносить эти параметры куда-либо ещё (хотя Вы, конечно, можете сделать более универсальное решение).
Имя образа, из которого происходит создание виртуальной машины, будем передавать через переменную окружения 'box_name' из управляющих командных файлов. Для того чтобы обратиться к переменной окружения следует использовать конструкцию ENV['имя_переменной']:
$ssh_username = "vagrant"
$ssh_password = "vagrant"
$initRunType = "once"
config.vm.box = ENV['box_name']
config.vm.communicator = "ssh"
config.ssh.username = $ssh_username
config.ssh.password = $ssh_password
config.vm.network :forwarded_port, guest: 22, host: 2222, id: "ssh", auto_correct: true
Далее мы указываем общий каталог, который будет монтироваться в виртуальную машину при каждом выполнении команды vagrant up. Исходный каталог хостовой машины и каталог, в который будет производиться монтирование внутри гостевой машины также будем передавать через переменные окружения. Это позволит при необходимости управлять этой настройкой на уровне отдельных машин:
config.vm.synced_folder ENV['shared_ci_host_directory'],
ENV['shared_ci_guest_directory'],
automount: true,
create: true,
group: "vagrant",
owner: "vagrant"
Из-за особенностей реализации механизма монтирования в Vagrant, каталог будет подключен таким образом, что не будет монтироваться при последующем ручном запуске виртуальной машины из графического интерфейса VirtualBox. Vagrant оставляет пустой точку монтирования в настройках и выполняет монтирование в указанный каталог самостоятельно:
В то же время вносить изменения в эти настройки не следует. Если указать точку монтирования вручную, то возникнут проблемы как с правами доступа к этому каталогу из виртуальной машины, так и с последующим его монтированием при запуске через Vagrant. В общем если машина создана с помощью Vagrant, то для ее запуска всегда стоит использовать команду vagrant up, а не GUI-инструменты гипервизора (это действительно важно для корректной работы).
Через этот каталог будет осуществляться доступ к общим для всех машин ресурсам и обеспечиваться публикация результатов сборок на хостовой машине. Например можно выводить сюда отчетность Allure о результатах тестирования и настроить веб-сервер на хостовой машине, чтобы он показывал отчетность из этой папки. Сюда будут выгружаться версии хранилища конфигурации и подготовленные образы тестовых баз.
Здесь же можно разместить фреймворк для сценарного тестирования. Загружать его в каждую виртуальную машину или автоматически обновлять c github-а может быть не лучшим решением. Разработку сценарных тестов и их выполнение лучше вести на одной и той же версии фреймворка, поэтому вариант размещения фреймворка в одном общем каталоге может быть удобен. В этом случае все узлы CI могут обращаться к нему по фиксированному пути:
Настройка ресурсов, выделяемых машине
Настройка системных ("железных") ресурсов выполняется через поле config.vm.provider.
Также здесь можно определить как машина будет вести себя визуально:
- Запросить от VirtualBox отображать ее в графическом интерфейсе (показывать окно сразу при запуске), задав параметр gui = true
- Явно указать имя виртуальной машины , чтобы утилита vagrant не добавляла к нему отметку времени. Удобно если это имя будет совпадать с именем узла CI, которое определяется у нас через переменную окружения: name = ENV['node_name']
По практике почти все настройки в этом блоке можно задавать константами. Всегда удобна возможность использования буфера обмена и механизма перетаскивания - для них стоит включить двунаправленный обмен (bidirectional). Также всегда хватает видеопамяти 64МБ. Такие настройки нет смысла определять в отдельных конфигурационных файлах. А вот количество CPU и объем оперативной памяти может быть удобно задавать разным для разных машин, входящих в один CI-контур. Память и ресурсы процессора имеет смысл менять и в зависимости от того на какой хостовой машине разворачивается CI. Поэтому их будем передавать извне через переменные окружения 'ram_memory_size_mb' и 'cpu_count':
config.vm.provider :virtualbox do |v, override|
v.name = ENV['node_name']
v.gui = true # Display GUI when booting the machine
v.customize ["modifyvm", :id, "--memory", ENV['ram_memory_size_mb']]
v.customize ["modifyvm", :id, "--cpus", ENV['cpu_count']]
v.customize ["modifyvm", :id, "--vram", 64] # Video memory 64 MB
v.customize ["modifyvm", :id, "--clipboard", "bidirectional"]
v.customize ["modifyvm", :id, "--draganddrop", "bidirectional"]
v.customize ["modifyvm", :id, "--accelerate3d", "on"]
v.customize ["modifyvm", :id, "--nic1", "nat"] # epn0s3
v.customize ["modifyvm", :id, "--nic2", "bridged"] # Bridged adapter, epn0s8
v.customize ["modifyvm", :id, "--bridgeadapter2", ENV['host_phisical_adapter_name_for_bridge_connection']] #
end
Настройка сети
Согласно документации по Packer и Vagrant для корректного взаимодействия с гостевой машиной по ssh
- необходимо создать для нее хотя бы один виртуальный сетевой адаптер
- и первым по порядку всегда должен быть адаптер с типом NAT.
В Vagrantfile это делается следующим образом:
v.customize ["modifyvm", :id, "--nic1", "nat"]
Но только сети NAT нам будет недостаточно для возможности взаимодействия машин друг с другом и с компьютерами в локальной сети. Необходимо создать еще один аптер, указав для него тип подключения "bridged".
v.customize ["modifyvm", :id, "--nic2", "bridged"]
При выборе такого типа подключения нужно также указать имя устройства - физического адаптера на хостовой машине, через который будет осуществляться соединение:
Разумеется это имя будет разным на разных хостовых машинах и его указание непосредственно в Vagrantfile усложнило бы переносимость наших механизмов между хвостовыми машинами. Поэтому передавать имя устройства в Vagrantfile будем через переменную окружения:
v.customize ["modifyvm", :id, "--bridgeadapter2", ENV['host_phisical_adapter_name_for_bridge_connection']]
Для корректного взаимодействия машин в этой сети им необходимо также назначить разные IP-адреса и имена хостов. Начнем с IP-адресов.
В последних версиях Ubuntu используется утилита для настройки сети Netplan. Ее работа основывается на конфигурационных YML-файлах. Утилита объединяет содержимое всех YML-файлов из каталога /etc/netplan и объединенный текст воспринимается как итоговая конфигурация, которую необходимо применить к системе. Имена файлов при этом не играют роли. В большинстве примеров в сети описывается как работать только одним конфигурационным файлом. И в нашем простейшем случае также будет достаточно одного файла.
Генерировать этот файл программно было бы довольно сложно, поэтому просто создадим шаблон этого файла и будем копировать его в каталог каждой виртуальной машины, заменяя в нем только IP-адрес:
Перед копированием файла в виртуальную машину удалим все существующие файлы, воспользовавшись командой
find /etc/netplan -maxdepth 1 -type f -exec rm {} \;
При записи этой команды в Vagrantfile обратный слеш придется экранировать согласно синтаксису Ruby.
Затем используем provisioner с типом file для передачи файла с хоста в гостевую машину. Скопируем файл сначала в домашний каталог текущего пользователя, что не потребует полных прав. А затем указав необходимость привилегий суперпользователя (s.privileged = true) скопируем его в каталог /etc/netplan.
Всё, что остается сделать после этого для применения изменений и назначения IP адреса - это вызвать команду netplan apply, опять же с правами суперпользователя:
config.vm.provision "Removing current netplan configuration", type: "shell", run: $initRunType do |s|
s.inline = "find /etc/netplan -maxdepth 1 -type f -exec rm {} \\;"
s.privileged = true
end
config.vm.provision "Copying netplan configuration file to VM", type: "file", run: $initRunType do |f|
f.source = Dir.pwd + "/netplan_config.yaml"
f.destination = "~/netplan_config.yaml"
end
config.vm.provision "Copying netplan configuration file to netplan configuration directory", type: "shell", run: $initRunType do |s|
s.inline = "cp /home/vagrant/netplan_config.yaml /etc/netplan/netplan_config.yaml"
s.privileged = true
end
config.vm.provision "Applying netplan configuration from file", type: "shell", run: $initRunType do |s|
s.inline = "netplan apply"
s.privileged = true
end
Изменение имени компьютера, обработка файлов сервера 1С
При сборке образа виртуальной машины мы определяли имя хоста для нее как "vagrant-ci" (это имя задавалось в параметре boot_command конфигурационного файла Packer). И сейчас при развёртывании машины оно будет именно таким. Но было бы ошибкой оставлять одинаковое имя у каждой машины, входящей в нашу локальную сеть. Имя хоста следует изменить как на постоянной основе, так и для текущего сеанса пользователя. Для этого надо выполнить команды:
hostname новое-имя-хоста
hostnamectl set-hostname новое-имя-хоста
Однако тут мы столкнемся с сервером платформы 1С. Его мы устанавливали при сборке образа виртуальной машины. Сервер 1С привязывается к имени хоста той машины, на которой он выполняется, и если имя хоста начнет отличаться от заданного в его конфигурационных файлах, то сервер перестанет запускаться. Проблема достаточно известная, как известно и её решение. Нам придется выполнить замену имени хоста во всех конфигурационных файлах сервера 1С.
Сделать это можно как и ранее утилитой sed выполнив команду:
find /home/usr1cv8/.1cv8/1C/1cv8 -type f -not -name '*.lgp' -exec sed -i 's/ текущее-имя-хоста / новое-имя-хоста /g' {} \;
Также потребуется перезапуск служб Avahi и 1С.
Чтобы не выполнять каждую команду в отдельности можно воспользоваться возможностью Vagrantfile по выполнению скриптов. Текст скрипта при этом можно задавать в самом файле:
$changeHostNameScript =<<-SCRIPT
systemctl stop srv1cv83.service
systemctl stop avahi-daemon.service
CURRENT_HOSTNAME=`hostname`
NEW_HOSTNAME=#{ENV['node_name']}
echo "Setting hostname to : " $NEW_HOSTNAME
hostnamectl set-hostname $NEW_HOSTNAME
hostname $NEW_HOSTNAME
CHANGE_1C_HOSTNAME_COMMAND="find /home/usr1cv8/.1cv8/1C/1cv8 -type f -not -name '*.lgp' -exec sed -i 's/$CURRENT_HOSTNAME/$NEW_HOSTNAME/g' {} \\;"
echo $CHANGE_1C_HOSTNAME_COMMAND
eval "$CHANGE_1C_HOSTNAME_COMMAND"
systemctl start avahi-daemon.service
systemctl start srv1cv83.service
SCRIPT
config.vm.provision "Changing hostname for OS and 1C server", type: "shell", run: $initRunType do |s|
s.inline = $changeHostNameScript
s.privileged = true
end
Прочие действия по конфигурированию в Vagrantfile
Как и в случае с Packer после старта графической оболочки имеет смысл настроить разрешение экрана:
config.vm.provision "Setting screen resilution via xrandr", type: "shell", run: $initRunType do |s|
s.inline = "xrandr -display :0.0 -s 1600x1200"
s.privileged = false
end
Также при необходимости можно передать с хостовой машины в гостевую файл nethasp.ini. Это имеет смысл если ключи находятся не на хостовой машине, а на другом компьютере в локальной сети, и скорость их поиска через бродкаст Вас не устраивает.
Передать этот файл можно тем же методом, который мы применяли для передачи файла конфигурации Netplan. За образец также можно взять пример копирования файла настроек для эмулятора терминала terminator (его можно увидеть в полном тексте файла в репозитории на github или gitlab).
Единственное отличие будет в том, что после его размещения в каталоге /opt/1C/v8.3/x86_64/conf/ гостевой машины для этого файла необходимо назначить права:
sudo chown usr1cv8:grp1cv8 /opt/1C/v8.3/x86_64/conf/nethasp.ini
Содержимое файла может быть например таким:
[NH_COMMON]
NH_TCPIP=Enabled
[NH_TCPIP]
NH_SERVER_ADDR=192.168.1.40
NH_PORT_NUMBER=475
NH_TCPIP_METHOD=UDP
NH_USE_BROADCAST=Disabled
Конфигурационные файлы, определяющие специфичные для каждой машины параметры
Многие параметры машины в Vagrantfile мы задавали не константами, а указывали необходимость их установки из переменных окружения. Это позволяет нам внутри каталога каждой виртуальной машины создать небольшой файл, где будут задаваться эти параметры, и при необходимости менять только этот файл.
Таким файлом у нас будет node-settings.bat
Его содержимое может быть следующим:
SET node_name=ubuntu-interactive-1
SET version_of_1c_platform_with_underscores=8_3_15_1656
SET version_of_vm_os_with_underscore=19_04
SET version_of_postgresql_with_underscores=10_10_4
SET host_phisical_adapter_name_for_bridge_connection=NVIDIA nForce (Networking Controller)
SET ram_memory_size_mb=6144
SET cpu_count=3
Переменные окружения из такого файла можно считывать в любом другом командном файле выполнив короткую команду
CALL node-settings.bat
В этом файле мы определяем версии нужных нам компонент системы. Но не для этого, чтобы их установить при запуске, а для того, чтобы затем в общем командном файле составить из них "стандартизированное" в рамках нашего репозитория имя образа, из которого будет разворачиваться машина:
SET box_name=ci_node_ubuntu_%version_of_vm_os_with_underscore%_1c_%version_of_1c_platform_with_underscores%_pg_%version_of_postgresql_with_underscores%
Далее определяем количество виртуальных процессоров, объем ОЗУ и имя устройства - сетевого адаптера для создания bridge-подключения. Также задаем имя узла, которое сейчас будет использоваться для установки имени хоста в виртуальной машине, а затем ещё и для подключения машины в качестве узла Jenkins.
Ранее уже упоминалось про переносимость кода между различными хостовыми машинами. Теперь же мы создаем файл, текст которого по идее должен отличаться от узла к узлу, и тем более от сервера к серверу. Если работаем на боевом CI - то его содержимое будет одно, на компьютере разработчика - другое, а если захочется поэкспериментировать с механизмами CI на домашнем компьютере - то третье.
При таком подходе данный файл неправильно помещать в общий репозиторий и его следовало бы добавить его в .gitignore.
Но можно поступить и иначе. Для каждого хостового компьютера, на котором планируется выполнять запуск определить в файле свои параметры, после чего сохранить файл в git-репозитории. В этом случае файл может выглядеть следующим образом:
SET node_name=ubuntu-interactive-3
SET version_of_1c_platform_with_underscores=8_3_16_1063
SET version_of_vm_os_with_underscore=19_10
SET version_of_postgresql_with_underscores=11_5_7
if "%computername%"=="SERVER-HOSTNAME" (
goto :working_server_config
) else if "%computername%"=="HOME-HOSTNAME" (
goto :development_config
) else (
goto :end
)
:working_server_config
REM Заменить имя вашего контроллера
SET host_phisical_adapter_name_for_bridge_connection=NVIDIA nForce (Networking Controller)
SET ram_memory_size_mb=6144
SET cpu_count=3
goto :end
:development_config
REM Заменить на Ваш IP-адрес, если Linux-машина не видит хостовую по имени компьютера
SET host_machine_ip_or_hostname=192.168.1.64
REM Заменить имя вашего контроллера
SET host_phisical_adapter_name_for_bridge_connection=Realtek PCIe GBE Family Controller
SET ram_memory_size_mb=2048
SET cpu_count=2
goto :end
:end
Конечно, это не отменяет того, что файл будет меняться чаще других. Но таким образом можно добиться значительно более постоянного содержимого этого файла.
Здесь мы не помещаем определение параметров в условный оператор if-else, так как в имени сетевого контроллера могут присутствовать скобки. В этом случае задание таких значений внутри условного оператора в bat-файле выглядело бы очень некрасиво: https://stackoverflow.com/questions/12976351/escaping-parentheses-within-parentheses-for-batch-file
Командные файлы запуска
Команда vagrant up должна выполняться из каталога каждой виртуальной машины в отдельности. Но как и всегда, в сценарии запуска у нас почти весь код будет общим. Поэтому внутри каталога отдельной машины будет находится небольшой файл vagrant-up.bat, содержащий всего две строки :
SET vagrant_action=up
cmd /C "..\vagrant-up-destroy-common.bat
Весь исполняемый код вынесем в общий файл vagrant-up-destroy-common.bat, расположенный на один каталог выше.
При этом текущий каталог меняться не будет и все действия фактически будут выполняться внутри каталога отдельной машины:
В общем файле сначала читаются переменные окружения, определяющие настройки отдельной машины (файл node-settings.bat из текущего каталога), затем устанавливается каталог хранения образов виртуальных машин - переменная окружения VAGRANT_HOME. Значение этой переменной окружения устанавливается то же самое, которое мы устанавливали при сборке образа через Packer.
Далее формируется имя образа и задаются прочие параметры, которые являются общими для всех машин: имя хоста (имя хостовой машины), путь к общему каталогу как со стороны хоста, так и со стороны гостевой машины:
CALL node-settings.bat
cp ..\Vagrantfile Vagrantfile
SET VAGRANT_HOME=C:/HashiCorp/vagrant_home
SET box_name=ci_node_ubuntu_%version_of_vm_os_with_underscore%_1c_%version_of_1c_platform_with_underscores%_pg_%version_of_postgresql_with_underscores%
if NOT DEFINED host_machine_ip_or_hostname (
SET host_machine_ip_or_hostname=%computername%
)
if NOT DEFINED shared_ci_guest_directory (
SET shared_ci_guest_directory=/home/vagrant/shared_ci
)
if NOT DEFINED shared_ci_host_directory (
SET shared_ci_host_directory=%~dp0/shared
)
time /T
if %vagrant_action%==up (
vagrant up
) else if %vagrant_action%==destroy (
vagrant destroy -f && rmdir /s /q .vagrant
)
time /T
Последней командой выполняется либо vagrant up для запуска машины, либо vagrant destroy для ее удаления (механизм удаления рассмотрим ниже).
В файле vagrant-up.bat значение переменной vagrant_action задается равным "up", и поэтому запуске данного файла произойдет развертывание машины из образа с именем %box_name% и ее последующий запуск с выполнением команд из Vagrantfile.
Удаление созданной виртуальной машины
Удаление виртуальной машины, развернутой через Vagrant выполняется командой vagrant destroy.
При выполнении этой команды также читается Vagrantfile и происходит инициализация части параметров, заданных в этом файле.
В результате инициализации параметров утилите vagrant обязательно должно стать известно имя образа и общие каталоги, а у нас они определяются через переменные окружения. Зачем так сделано - не ясно. Ведь уже есть каталог .vagrant , содержащий все необходимые для удаления данные. Но тем не менее с таким поведением команды vagrant destroy приходится мириться.
Поэтому и перед запуском и перед удалением виртуальной машины будем устанавливать одни и те же переменные окружения. Проще всего сделать это, если команды запуска и удаления перенести в общий командный файл, а извне передавать только режим, определяющий действие: "up" - для запуска, "destroy" - для удаления.
Выше уже приводились соответствующие команды из общего файла:
if %vagrant_action%==up (
vagrant up
) else if %vagrant_action%==destroy (
vagrant destroy -f && rmdir /s /q .vagrant
)
Для удаления создадим в каталогах, специфичных для отдельных машин, файл vagrant-destroy.bat, который будет отличаться от vagrant-up.bat только одной строкой:
SET vagrant_action=destroy
cmd /C "..\vagrant-up-destroy-common.bat
Конечно всегда может произойти какая-то ошибка и виртуальная машина не будет удалена. В этом случае для освобождения ресурсов ее необходимо будет удалить вручную, выбрав удаление всех ее файлов, и не забыть удалить соответствующий каталог .vagrant:
Итоги и проверка результата
Итак, мы получили возможность
- Задавая параметры всего в двух конфигурационных файлах (node-settings.bat и netplan_config.yaml) разворачивать из подготовленных с помощью Packer образов новые виртуальные машины.
- Вести простейший реестр хостовых машин прямо в командных файлах, задавая для них разные параметры запуска. Тем самым обеспечивая возможность выделения разных ресурсов виртуальным машинам и их запуска на разных серверах, и даже рабочем или домашнем компьютере.
Код во всех остальных файлах является общим для всех машин.
В файлах Вы обязательно должны задать свои значения:
- IP-адреса и имени хоста для каждой машины
- Версии серверного программного обеспечения, из которых механизм развертывания составит имя бокса с образом машины.
- Имя физического сетевого адаптера, на основе которого будет создаваться bridge-подключение.
В этом случае, каждая из запущенных машин будет включена в общую локальную сеть, иметь уникальный IP адрес и имя хоста. Этого достаточно, чтобы следующим шагом запустить на ней агента CI-сервера Jenkins и подключить машину в качестве сборочного узла. При этом полное пересоздание каждого узла будет занимать менее 10 минут и всего четыре клика мыши.
Можно создать отдельный командный файл для запуска и остановки всех машин разом, но мне кажется что поочередный запуск также достаточно удобен:
Количество узлов, которые можно запускать в процессе разработки и отладки механизмов, будет ограничиваться ресурсами хостовой машины. Если вести разработку на среднем "бытовом" компьютере, то в большинстве случаев получится запустить только два или три узла, выполняющих задачи сборки и тестирования 1С. Для запуска большего их количества потребуется сервер помощнее, иначе на хостовой машине больше ничего не удастся делать :
Два узла, исполняющие задачи сборки: |
Три узла, исполняющие задачи сборки: |
Традиционно, ссылки на очередные коммиты в репозиторий на
- Гитлаб: https://gitlab.com/vladimirlitvinenko84/ci-infrastructure-for-1c/-/tree/master/jenkins-nodes
- Гитхаб: https://github.com/VladimirLitvinenko84/ci-infrastructure-for-1c/tree/master/jenkins-nodes
В следующей части мы наконец завершим разработку "инфраструктуры" для CI, перейдем к конфигурированию Jenkins и подключению к нему развернутых машин в качестве сборочных узлов.