Предыдущий пост серии — Ansible: миграция RTFM 2.8 – logrotate, unattended-upgrades и Let’s Encrypt для Bastion хоста.
Сейчас Bastion запущен и настроен, сегодня надо выполнить настройку NGINX.
Как и прежние посты этой серии — это скорее заметки для себя по выполненным обновлениям плюс примеры настроек и действий.
План таков:
- обновить роль
commonи добавить монтирование диска для Bastion с данными для NGINX - обновить
nginxроль для копирования и использования конфигов из этого каталога
Ссылки на коммиты файлов, которые получились в результате написания этого поста:
hostsrtfm-blog-ansible-bastion-provision.ymlroles/nginx/templates/nginx.confroles/common/tasks/main.yml
По первому пункту: к Bastion во время создания окружения подключается EBS раздел, который будет содержать конфиги виртуалхостов.
Сейчас этот раздел пустой — ещё ничего не делалось:
[simterm]
root@dev:/home/admin# lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT xvda 202:0 0 8G 0 disk └─xvda1 202:1 0 8G 0 part / xvdb 202:16 0 8G 0 disk
[/simterm]
/dev/xvdb — вот он.
Вообще хочется на Bastion использовать OpenBSD — люблю я *BSD ещё со времён FreeBSD 6.1, но пока закончить бы с настройкой вообще, а потом уже просто (угу…) обновить роли. Пока на всех хостах будет Debian.
Содержание
Создание раздела на /dev/xvdb
Начнём с диска.
Создаём раздел:
[simterm]
root@dev:/home/admin# echo ';' | sfdisk /dev/xvdb Checking that no-one is using this disk right now ... OK Disk /dev/xvdb: 8 GiB, 8589934592 bytes, 16777216 sectors Units: sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes >>> Created a new DOS disklabel with disk identifier 0x54a32780. /dev/xvdb1: Created a new partition 1 of type 'Linux' and of size 8 GiB. /dev/xvdb2: Done. New situation: Device Boot Start End Sectors Size Id Type /dev/xvdb1 2048 16777215 16775168 8G 83 Linux The partition table has been altered. Calling ioctl() to re-read partition table. Syncing disks.
[/simterm]
Проверям:
[simterm]
root@dev:/home/admin# lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT xvda 202:0 0 8G 0 disk └─xvda1 202:1 0 8G 0 part / xvdb 202:16 0 8G 0 disk └─xvdb1 202:17 0 8G 0 part
[/simterm]
Создаём файловую систему:
[simterm]
root@dev:/home/admin# mkfs.ext4 /dev/xvdb1
mke2fs 1.43.4 (31-Jan-2017)
Creating filesystem with 2096896 4k blocks and 524288 inodes
Filesystem UUID: 1a063fba-dc1c-4483-b36a-d16e4de00eac
Superblock backups stored on blocks:
32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632
Allocating group tables: done
Writing inode tables: done
Creating journal (16384 blocks): done
Writing superblocks and filesystem accounting information: done
[/simterm]
Добавляем каталог /data (сейчас вручную, потом это будет выполняться Ansible):
[simterm]
root@dev:/home/admin# mkdir /data
[/simterm]
Монтируем раздел:
[simterm]
root@dev:/home/admin# mount /dev/xvdb1 /data/
[/simterm]
Проверяем, пока тут ничего:
[simterm]
root@dev:/home/admin# ls -l /data/ total 16 drwx------ 2 root root 16384 Feb 3 13:44 lost+found
[/simterm]
Ручная настройка NGINX
Создаём каталог для конфигов NGINX:
[simterm]
root@dev:/home/admin# mkdir /data/data-nginx.d
[/simterm]
Перемещаем файл настроек виртуалхоста
[simterm]
root@dev:/home/admin# mv /etc/nginx/conf.d/dev.rtfm.co.ua.conf /data/data-nginx.d/
[/simterm]
Обновляем /etc/nginx/nginx.conf, добавляем этот каталог:
...
gzip on;
gzip_disable "msie6";
include /etc/nginx/conf.d/*.conf;
include /data/data-nginx.d/*.conf;
}
Проверяем, перечитываем конфиги:
[simterm]
root@dev:/home/admin# nginx -t && service nginx reload nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful
[/simterm]
Проверяем доступность хоста:
[simterm]
$ curl -I dev.rtfm.co.ua HTTP/1.1 200 OK Server: nginx/1.10.3 Date: Sat, 03 Feb 2018 13:52:09 GMT Content-Type: text/html Content-Length: 612 Last-Modified: Mon, 29 Jan 2018 16:40:45 GMT Connection: keep-alive ETag: "5a6f4e8d-264" Accept-Ranges: bytes
[/simterm]
Ansible — разделение хостов
Теперь надо всё это вынести в роли Ansible.
Со времени последнего поста файл hosts немного изменился — добавился DB хост и команда для SSH для доступа к нему через Bastion хост (см. пост Ansible: подключение в приватную сеть через Bastion хост):
[rtfm-bastion-dev] dev.rtfm.co.ua [rtfm-db-dev] db.dev.rtfm.co.ua [rtfm-db-dev:vars] ansible_ssh_common_args='-o ProxyCommand="ssh -W %h:%p -q [email protected] -i {{ host_key }}"'
Теперь пора разделить задачи — часть будет выполняться на всех (logrotate, junattended-upgrades, common), часть — только на Bastion, DB или Services.
Сейчас в Jenkins это выполняется через --limit — --limit=${ansibleHostLimit} (скрипт полностью тут>>>):
...
sh "set +x && ansible-playbook --limit=${ansibleHostLimit} --extra-vars api_key=${AMPLIFY_API_KEY} --private-key=credentials/${ansiblePemFile} ${ansiblePlaybookFile}"
...
Наверно лучше будет сделать отдельные плейбуки для каждого из трёх хостов: в --limit будет передаваться Dev или Production, а в переменной ${ansiblePlaybookFile} — плейбук для Bastion, DB или Services.
Т.е., для выполнения на Dev Bastion — команда будет выглядеть так:
[simterm]
$ ansible-playbook --limit=rtfm-bastion-dev --private-key=../../Bitbucket/aws-credentials/rtfm-dev.pem rtfm-blog-ansible-bastion-provision.yml
[/simterm]
А для Production Bastion — так:
[simterm]
$ ansible-playbook --limit=rtfm-bastion-production --private-key=../../Bitbucket/aws-credentials/rtfm-production.pem rtfm-blog-ansible-bastion-provision.yml
[/simterm]
Dev и Prod будут иметь в hosts свои переменные.
И заодно переименуем файл плейбука:
[simterm]
$ git mv rtfm-blog-ansible-provision.yml rtfm-blog-ansible-bastion-provision.yml
[/simterm]
Переменные хостов и монтирование диска
Что бы Ansbile монтировал определённый раздел — в hosts добавим переменную data_volume_id для хоста rtfm-bastion-dev, в которой укажем раздел:
[rtfm-bastion-dev] dev.rtfm.co.ua [rtfm-bastion-dev:vars] data_volume_id="/dev/xvdb1" ...
Каталог для монтирования у всех хостов будет один — /data, поэтому добавим блок [all:vars] в котором определим путь:
[all:vars] data_volume_mount_path="/data" ...
Монтирование раздела можно добавить в роль common, выносим её в начало списка плейбука rtfm-blog-ansible-bastion-provision.yml:
- hosts: all
become:
true
roles:
- role: common
# - role: thefinn93.letsencrypt
# letsencrypt_email: [email protected]
# letsencrypt_cert_domains:
# - "{{ inventory_hostname }}"
# letsencrypt_webroot_path: /var/www/html/
# letsencrypt_renewal_command_args: '--renew-hook "systemctl restart nginx"'
- role: nginx
- role: amplify
...
(с Let’s Encrypt пока не придумал, что делать, т.к. домен будет не один и светить их все в репозитории не хочется — пусть будет закомментирован)
Обновляем роль common, в файл tasks/main.yml добавляем монтирование диска:
- name: Mount data-volume "{{ data_volume_id }}" to "{{ data_volume_mount_path }}"
mount:
path: "{{ data_volume_mount_path }}"
src: "{{ data_volume_id }}"
state: mounted
fstype: ext4
...
Проверяем синтаксис:
[simterm]
$ ansible-playbook --syntax-check --limit=rtfm-bastion-dev rtfm-blog-ansible-bastion-provision.yml playbook: rtfm-blog-ansible-bastion-provision.yml
[/simterm]
На Dev хосте создадим тестовый файлик:
[simterm]
root@dev:/home/admin# touch /data/data-nginx.d/test.file
[/simterm]
Отмонтируем раздел:
[simterm]
root@dev:/home/admin# umount /data/ root@dev:/home/admin# ls -l /data/ total 0
[/simterm]
Запускаем выполнение:
[simterm]
$ ansible-playbook --limit=rtfm-bastion-dev --private-key=../../Bitbucket/aws-credentials/rtfm-dev.pem rtfm-blog-ansible-bastion-provision.yml PLAY [all] **** TASK [Gathering Facts] **** ok: [dev.rtfm.co.ua] TASK [common : Mount data-volume "/dev/xvdb1" to "/data"] **** changed: [dev.rtfm.co.ua] ...
[/simterm]
Проверяем на хосте:
[simterm]
root@dev:/home/admin# ls -l /data/ total 20 drwxr-xr-x 2 root root 4096 Feb 3 14:38 data-nginx.d drwx------ 2 root root 16384 Feb 3 13:44 lost+found root@dev:/home/admin# ls -l /data/data-nginx.d/ total 4 -rw-r--r-- 1 root root 324 Feb 3 13:49 dev.rtfm.co.ua.conf -rw-r--r-- 1 root root 0 Feb 3 14:38 test.file
[/simterm]
ОК, всё работает.
Обновление Jenkins
Что бы не забыть — сразу обновим Jenkins.
Сейчас задача одна, называется rtfm-blog-dev-ansible-provision — переименуем в rtfm-blog-bastion-dev-ansible-provision.
Затем обновим ANSIBLE_HOST_LIMIT со старого значения rtfm-dev на rtfm-bastion-dev (как в обновлённом hosts), и обновим имя файла плейбука с rtfm-blog-ansible-provision.yml на rtfm-blog-ansible-bastion-provision.yml:
Наверно — можно сейчас запушить все изменения.
Просмотреть лог красиво можно так:
[simterm]
$ git log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit
[/simterm]
(нагуглено тут>>>)
Пушим:
[simterm]
$ git push
[/simterm]
И проверяем билд:
Создание каталога /data и обновление роли nginx
Сейчас всё работает, т.к. сервер и каталог уже есть.
Если удалить стек и запустить создание всего с нуля — билд упадёт, т.к. каталога /data на сервере не будет.
На Dev хосте проверяем права на каталог:
[simterm]
root@dev:/home/admin# stat -c "%a %n" /data/ 755 /data/
[/simterm]
Добавляем его создание в ту же роль common, перед монтированием диска:
- name: Create "{{ data_volume_mount_path }}" directory
file:
path: "{{ data_volume_mount_path }}"
state: directory
mode: 0755
recurse: yes
- name: Mount data-volume "{{ data_volume_id }}" to "{{ data_volume_mount_path }}"
mount:
...
В роли nginx меняем путь копирования файлов.
Сейчас задача выглядит так:
...
- name: Add NGINX {{ inventory_hostname }} virtualhost config
template:
src=templates/virtualhosts/{{ inventory_hostname }}.conf
dest=/etc/nginx/conf.d/{{ inventory_hostname }}.conf
...
Меняем dest с /etc/nginx/conf.d/ на {{ data_volume_mount_path }}/data-nginx.d/:
...
- name: Add NGINX {{ inventory_hostname }} virtualhost config
template:
src=templates/virtualhosts/{{ inventory_hostname }}.conf
dest={{ data_volume_mount_path }}/data-nginx.d/{{ inventory_hostname }}.conf
...
Проверяем:
[simterm]
$ !810 ansible-playbook --syntax-check --limit=rtfm-bastion-dev rtfm-blog-ansible-bastion-provision.yml playbook: rtfm-blog-ansible-bastion-provision.yml
[/simterm]
(!810 — повторить из history команд)
Обновляем шаблон roles/nginx/templates/nginx.conf:
...
include /etc/nginx/conf.d/*.conf;
include {{ data_volume_mount_path }}/data-nginx.d/*.conf;
}
Запускаем:
[simterm]
$ !813 ansible-playbook --limit=rtfm-bastion-dev --private-key=../../Bitbucket/aws-credentials/rtfm-dev.pem rtfm-blog-ansible-bastion-provision.yml ...
[/simterm]
Проверяем:
[simterm]
root@dev:/home/admin# cat /etc/nginx/nginx.conf | grep include
include /etc/nginx/mime.types;
include /etc/nginx/conf.d/*.conf;
include /data/data-nginx.d/*.conf;
[/simterm]
Ну — вроде всё…
Для полной проверки — удаляем весь CloudFormation стек:
[simterm]
$ aws cloudformation delete-stack --stack-name rtfm-dev
[/simterm]
Создаём его заново из Jenkins задачи:
И повторяем Ansible provision:
Проверяем:
[simterm]
$ curl -I dev.rtfm.co.ua HTTP/1.1 200 OK Server: nginx/1.10.3 Date: Sat, 03 Feb 2018 15:40:28 GMT Content-Type: text/html Content-Length: 612 Last-Modified: Sat, 03 Feb 2018 15:38:36 GMT Connection: keep-alive ETag: "5a75d77c-264" Accept-Ranges: bytes
[/simterm]
Готово — на сегодня хватит 🙂
Что ещё надо сделать на Bastion:
- Let’s Encrypt
- exim config
- hostname config
- before-after reboot notify
- .bashrc, .vimrc
- dnsutils
- simple-backup





