Продолжение постов 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!
Готово.








