Продолжение постов Ansible: пример установки NGINX и Azure: provisioning с Resource Manager, Jenkins и Groovy.
Задача – запускать провижен VM из Jenkins. Пока выполняется только установка NGINX, позже будет добавлен Prometheus.
Используем Jenkins Pipeline и groovy-скрипты.
Содержание
Описание
Используется два репозитория: один для скриптов Jenkins (переменная $BUILD_REPO_URL
в скриптах ниже), второй для файлов Ansbile ($INFRA_URL
), в которых описаны хосты и роли.
Первое, с чем пришлось столкнуться – это Docker образы Ansbile. Ну, то, что нет тега latest
и вместо этого под каждую ОС отдельный тег – хорошо, понятно.
Далее – официальный образ Ansbile в DockerHub не имеет исполняемых файлов. Т.е. – где-то они, видимо есть – но образ ansible/ansible:ubuntu1604
сообщал что “exec: \”ansbile\”: executable file not found in $PATH”.“.
Ещё один образ – geerlingguy/docker-ubuntu1604-ansible
“заработал” – пока не дошло дело до выполнения самих плейбуков, где он начал падать с собщением типа:
…
File “/usr/lib/python2.7/dist-packages/ansible/plugins/connection/ssh.py”, line 452, in _run
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
File “/usr/lib/python2.7/subprocess.py”, line 711, in __init__
errread, errwrite)
File “/usr/lib/python2.7/subprocess.py”, line 1343, in _execute_child
raise child_exception
OSError: [Errno 2] No such file or directory
fatal: [dev.monitor.domain.ms]: FAILED! => {
“failed”: true,
“msg”: “Unexpected failure during module execution.”,
“stdout”: “”
}
Сообщение “OSError: [Errno 2] No such file or directory” заставило меня долго перебирать параметры и файлы, передаваемые Ansbile из скриптов и Jenkins-а, пока я не догадался проверить ssh
:
[simterm]
$ docker run -ti geerlingguy/docker-ubuntu1604-ansible ssh docker: Error response from daemon: oci runtime error: container_linux.go:247: starting container process caused "exec: \"ssh\": executable file not found in $PATH".
[/simterm]
Не добавить ssh
в образ системы, которая работает использует SSH? Ну…
В конце-концов – нашёлся williamyeh/ansible:master-ubuntu16.04
– с ним всё заработало.
Jenkins
Jenkins бегает на:
[simterm]
$ lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 16.04.1 LTS
[/simterm]
Jenkinsfiles
Jenkinsfiles – groovy-скрипты, которые описывают используемые Docker-образы и действия в них.
Дальше используется два таких скрипта – один с описанием функций, второй – с их вызовом и передачей переменных и параметров.
Первый скрипт – monitoring-ansible.groovy
, с функциями, содержит две функции – ansibleVmProvisionValidate()
, которая выполняет ansible-playbook --check
:
... def ansibleVmProvisionValidate(env='1') { docker.image('williamyeh/ansible:master-ubuntu16.04').inside('-v /var/run/docker.sock:/var/run/docker.sock') { git branch: "${BRANCH}", credentialsId: 'github', url: "${INFRA_URL}" stage('Playbook validate') { sh 'chmod 400 monitoring/.ssh/monitoring' sh "ansible-playbook --limit=${env} --check ${WORKDIR}/provision.yml" } } } ...
Вторая функция – ansibleVmProvisionApply()
– аналогична, но без --check
:
... def ansibleVmProvisionApply(env='1') { docker.image('williamyeh/ansible:master-ubuntu16.04').inside('-v /var/run/docker.sock:/var/run/docker.sock') { git branch: "${BRANCH}", credentialsId: 'github', url: "${INFRA_URL}" stage('Playbook apply') { sh 'chmod 400 monitoring/.ssh/monitoring' sh "ansible-playbook --limit=${env} ${WORKDIR}/provision.yml" } } } ...
Полностью скрипт выглядит так:
#!/usr/bin/env groovy def ansibleVmProvisionValidate(env='1') { docker.image('williamyeh/ansible:master-ubuntu16.04').inside('-v /var/run/docker.sock:/var/run/docker.sock') { git branch: "${BRANCH}", credentialsId: 'github', url: "${INFRA_URL}" stage('Playbook validate') { sh 'chmod 400 monitoring/.ssh/monitoring' sh "ansible-playbook --limit=${env} --check ${WORKDIR}/provision.yml" } } } def ansibleVmProvisionApply(env='1') { docker.image('williamyeh/ansible:master-ubuntu16.04').inside('-v /var/run/docker.sock:/var/run/docker.sock') { git branch: "${BRANCH}", credentialsId: 'github', url: "${INFRA_URL}" stage('Playbook apply') { sh 'chmod 400 monitoring/.ssh/monitoring' sh "ansible-playbook --limit=${env} ${WORKDIR}/provision.yml" } } } return this
Второй скрипт – monitoring-ansible-provision.groovy
– будет вызываться непоследственно из Jenkins и содержит несколько переменных и вызывает функции из monitoring-ansible.groovy
.
Его содержимое:
#!/usr/bin/env groovy node { // 'dev' or 'production' ENV="${ENV}" TAG = "${env.BUILD_TAG}" // Ansible playbooks repo URL INFRA_URL = "${INFRA_URL}" // Jenkins build script repo URL BUILD_REPO_URL = "${BUILD_REPO_URL}" // expor variable to path to Ansible dir in Infra repo WORKDIR='monitoring/ansible/monitoring' // clone $BUILD_REPO_URL to dedicated directory ./buildscripts/ dir('buildscripts') { git branch: 'master', credentialsId: 'github', url: "${BUILD_REPO_URL}" } def provision = load 'buildscripts/monitoring/monitoring-ansible.groovy' provision.ansibleVmProvisionValidate("${ENV}") provision.ansibleVmProvisionApply("${ENV}") }
Настройка Jenkins Pipeline
В параметрах задачи Jenkins задаются переменные, которые потом используются скриптом monitoring-ansible-provision.groovy
– ENV, BRANCH, INFRA_URL, BUILD_REPO_URL:
И настройки Pipeline, где описывается вызов самого скрипта:
Ansbile
Сейчас Ansible имеет только одну роль – nginx
, которая выполняет установку NGINX.
Структура файлов в репозитории выглядит так:
$ tree monitoring/ monitoring/ ├── ansible │ └── monitoring │ ├── ansible.cfg │ ├── hosts │ ├── provision.retry │ ├── provision.yml │ └── roles │ └── nginx │ ├── handlers │ │ └── main.yml │ ├── tasks │ │ └── main.yml │ ├── templates │ │ ├── nginx.conf │ │ └── nginx_vhost.conf │ └── vars │ └── main.yml ├── monitoring.json ├── monitoring.parameters.json ├── metadata.json └── README.md
Файлы monitoring.json
и monitoring.parameters.json
– используются Azure Resource Manager для развёртывания группы ресурсов, которая включает в себя виртуальную машину, см. пост тут>>>.
Настройка самого сервера выполянется уже Ansbile.
Файлы тут:
ansible.cfg
: параметры и переменные, путь к нему передаётся с помощью переменной окружения$ANSIBLE_CONFIG
из Jenkinshosts
: содержит описание хостов и некоторых специфичных для хостов и окружений переменныхprovision.yml
: роли, которые применяются при сетапе машины
Содержимое ansible.cfg
:
[defaults] ansible_connection=ssh remote_user=admin host_key_checking=False inventory = monitoring/ansible/monitoring/hosts private_key_file = monitoring/.ssh/monitoring
hosts:
[dev] dev.monitor.domain.ms hostname=dev.monitor.domain.ms [dev:vars] upstream_name=google upstream_url=google.com [production] monitor.domain.ms
Останавливаться на них подробно не буду, описаны в предыдущем посте, с небольшими отличиями (тут часть перменных уже вынесена в глобальные параметры файла ansbile.cfg
).
И файл provision.yml
– содержит вызов роли nginx
:
- hosts: all become: true roles: - nginx
На каком хосте будет выполняться и применяться роль – определяется при вызове ansible-playbook
из groovy-скрипта с помощью --limit
:
... sh "ansible-playbook --limit=${env} --check ${WORKDIR}/provision.yml" ...
Собственно, но этом – всё.
Пример выполнения:
Лог выполнения:
[simterm]
$ curl -sL dev.monitor.domain.ms <!DOCTYPE html> <html lang=en> <meta charset=utf-8> <meta name=viewport content="initial-scale=1, minimum-scale=1, width=device-width"> <title>Error 404 (Not Found)!!1</title> <style> *{margin:0;padding:0}html,code{font:15px/22px arial,sans-serif}html{background:#fff;color:#222;padding:15px}body{margin:7% auto 0;max-width:390px;min-height:180px;padding:30px 0 15px}* > body{background:url(//www.google.com/images/errors/robot.png) 100% 5px no-repeat;padding-right:205px}p{margin:11px 0 22px;overflow:hidden}ins{color:#777;text-decoration:none}a img{border:0}@media screen and (max-width:772px){body{background:none;margin-top:0;max-width:none;padding-right:0}}#logo{background:url(//www.google.com/images/branding/googlelogo/1x/googlelogo_color_150x54dp.png) no-repeat;margin-left:-5px}@media only screen and (min-resolution:192dpi){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat 0% 0%/100% 100%;-moz-border-image:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) 0}}@media only screen and (-webkit-min-device-pixel-ratio:2){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat;-webkit-background-size:100% 100%}}#logo{display:inline-block;height:54px;width:150px}
[/simterm]
Hello, Google!
Готово.