Продолжение сетапа Jenkins для RTFM.
Начало – AWS: CloudFormation для EC2 c Jenkins.
Для того, что бы развернуть Jenkins на EC2 в созданном стеке – потребуются:
- Docker для запуска самого Jenkins
- docker-compose файл для запуска Jenkins и подключения разделов
Т.к. CI для развёртывания CI не будет 🙂 – то добавлю скрипт, который будет запускать создание стека а потом выполнять установку и настройку сервисов.
Т.е, идея такова:
- наверно – стоит выполнить снапшот EBS перед провиженом SloudFormation стека с EC2 – just in case
- скрипт запускает Ansible и выполняет провижен инстанса
- устанавливает Docker
- копирует на CI хост Docker Compose файл, и запускает Jenkins с монтированием раздела с данными Jenkins
Ограничение доступа к CI хосту – на уровне AWS Security Group + авторизация в самом Jenkins.
Получившийся проект – в Github.
Поехали.
Содержание
Создание стека Jenkins
Создание шаблона описано в посте AWS: CloudFormation для EC2 c Jenkins (только шаблон в другом месте и немного изменён, см тут>>>).
Перед написанием ролей, что бы было где тестить роли – создаём стек:
Стек готов:
Проверяем доступ:
Диск с данными Jenkins – /dev/xvdb
:
Добавляем субдомен ci.rtfm.co.ua (пока – в панели регистратора):
Ansible
ansible.cfg
Начнём с создания локального файла настроек.
Переменные в него будут передаваться из скрипта:
[defaults] ansible_connection=ssh remote_user=admin host_key_checking=False inventory = hosts private_key_file = $JENKINS_RSA_KEY
inventory
Тут же создаём файл hosts
, в который вносим FQDN сервера с Jenkins:
[jenkins] ci.rtfm.co.ua
playbook
И плейбук – файл jenkins-provision.yml
, в roles пока ничего::
- hosts: jenkins become: true roles:
Содержимое репозитория сейчас:
Роли
Docker и Docker Compose
На сервере надо будет установить Docker и Docker Compose для запуска Jenkins.
В jenkins-provision.yml
добавляем:
... roles: - angstwad.docker_ubuntu - franklinkim.docker-compose
Создаём requirements.yml
, добавляем в него зависимости:
- src: mongrelion.docker - src: franklinkim.docker-compose
Проверяем.
Устанавливаем зависимости локально:
Проверяем синтаксис плейбука:
Запускаем установку:
Проверяем Docker на хосте:
Compose:
Jenkins
Теперь – создаём роль Jenkins.
Тут надо будет:
- создать каталог
/jenkins
- смонтировать
/dev/xvdb1
в/jenkins
- скопировать файл
docker-compose.yml
на хост - запустить
docker-compose
Создаём каталоги:
В vars/main.yml
добавляем переменные с именем раздела и каталогом монтирования:
ebs_volume: "/dev/xvdb1" jenkins_mount_path: "/jenkins/"
В templates
– создаём docker-compose.yml.j2
:
version: '2' services: jenkins: user: root image: jenkins ports: - '80:8080' volumes: - {{ jenkins_mount_path }}:/var/jenkins_home - /var/run/docker.sock:/var/run/docker.sock environment: - JENKINS_HOME=/var/jenkins_home
В tasks/main.yml
– создаём задачи – создать каталог, смонтировать раздел, скопировать шаблон и запустить docker-compose
:
- name: Create "{{ jenkins_mount_path }}" directory file: path: /jenkins owner: root group: root mode: 0755 state: directory - name: Mount EBS "{{ ebs_volume }}" mount: path: "{{ jenkins_mount_path }}" src: "{{ ebs_volume }}" state: mounted fstype: ext4 - template: src: templates/docker-compose.yml.j2 dest: /home/admin/docker-compose.yml owner: root group: root mode: 0644 - name: Start Jenkins service docker_service: project_src: /home/admin
Проверяем синтаксис:
И запускаем:
Проверяем:
ОК – вроде всё работает.
Скрипт запуска
Удаляем стек:
Создаём скрипт:
#!/usr/bin/env bash RSA_LOCAL_PATH="/home/setevoy/Work/RTFM/Bitbucket/aws-credentials/rtfm-jenkins.pem" JENKINS_STACK_WORKDIR="/home/setevoy/Work/RTFM/Github/rtfm-blog-cf-templates" JENKINS_STACK_TEMPLATE="rtfm_jenkins_stack.json" JENKINS_ANSIBLE_WORKDIR="/home/setevoy/Work/RTFM/Github/rtfm-jenkins-ansible-provision/" JENKINS_ANSIBLE_PLAYBOOK="jenkins-provision.yml" JENKINS_EBS_ID="vol-0085149b3a0a45d0c" HELP="\n\tCreate and provision Jenkins stack script.\n\n\t-b: backup Jenkins EBS to S3\n\t-c: run Stack create (use -s for Stack name!)\n\t-u: run Stack update (use -s for Stack name!)\n\t-a: run Ansible playbook\n\t-i: Allowed IP\n\t-s: Stack name\n\t-h: print this Help\n" backup_ebs= create_stack= update_stack= run_ansible= allow_ip= stack_name= while getopts "bcuai:s:h" opt; do case $opt in b) backup_ebs=1 echo "Creating stack" ;; c) create_stack=1 echo "Creating stack" ;; u) update_stack=1 echo "Update stack" ;; a) run_ansible=1 echo "Run Ansible" ;; i) allow_ip=$OPTARG echo "Allowed IP $allow_ip" ;; s) stack_name=$OPTARG echo "Stack name $stack_name" ;; h) echo -e "$HELP" ;; esac done create_ebs_backup () { now=$(date +"%y-%m-%d-%H-%M") aws ec2 create-snapshot --volume-id $JENKINS_EBS_ID --description "$now Jenkins EBS backup" } create_aws_stack () { aws cloudformation create-stack --stack-name $stack_name --template-body file://$JENKINS_STACK_WORKDIR/$JENKINS_STACK_TEMPLATE --parameters ParameterKey=HomeAllowLocation,ParameterValue=$allow_ip/32 } update_aws_stack () { aws cloudformation update-stack --stack-name $stack_name --template-body file://$JENKINS_STACK_WORKDIR/$JENKINS_STACK_TEMPLATE --parameters ParameterKey=HomeAllowLocation,ParameterValue=$allow_ip/32 } ansible_run_playbook () { cd $JENKINS_ANSIBLE_WORKDIR || exit 1 ansible-galaxy install --role-file requirements.yml || exit 1 ansible-playbook --syntax-check $JENKINS_ANSIBLE_PLAYBOOK || exit 1 ansible-playbook $JENKINS_ANSIBLE_PLAYBOOK --private-key=$RSA_LOCAL_PATH } if [[ $backup_ebs == 1 ]]; then echo -e "\nRunning Jenkins EBS backup...\n" create_ebs_backup && echo -e "\nDone.\n" || { echo -e "ERROR: can't finish backup. Exit.\n"; exit 1; } fi if [[ $create_stack == 1 ]] && [[ $stack_name ]] && [[ $allow_ip ]]; then echo -e "\nCreating Jenkins CloudFormation stack $stack_name with AllowedIP $allow_ip...\n" create_aws_stack && echo -e "\nDone.\n" || { echo -e "ERROR: can't execute create-stack. Exit.\n"; exit 1; } fi if [[ $update_stack == 1 ]] && [[ $stack_name ]] && [[ $allow_ip ]]; then echo -e "\nUpdating Jenkins CloudFormation stack $stack_name with AllowedIP $allow_ip...\n" update_aws_stack && echo -e "\nDone.\n" || { echo -e "ERROR: can't execute create-stack. Exit.\n"; exit 1; } fi if [[ $run_ansible == 1 ]]; then echo -e "\nRunning Jenkins Ansible playbook...\n" ansible_run_playbook && echo -e "\nDone.\n" || { echo -e "ERROR: can't finish backup. Exit.\n"; exit 1; } fi
Помощь:
Теперь – создаём стек:
Ждём завершения, обновляем DNS на новый IP, пока ждём несколько минут обновления DNS – можно сделать бекап раздела:
Запускаем Ansible:

Проверяем:

Ссылки по теме
Файлы, шаблоны, скрипт – тут>>>.
BASH: функция getopts – используем опции в скриптах
AWS: CloudFormation для EC2 c Jenkins
Ansible: ansible-galaxy – репозиторий ролей и Jenkins VM provision
Ansible: роли для Docker Compose, Prometheus и node_exporter
Jenkins: Pipeline, Groovy, Ansible и VM provisioning