Предыдущий пост серии – Ansible: миграция RTFM 2.8 – logrotate, unattended-upgrades и Let’s Encrypt для Bastion хоста.
Сейчас Bastion запущен и настроен, сегодня надо выполнить настройку NGINX.
Как и прежние посты этой серии – это скорее заметки для себя по выполненным обновлениям плюс примеры настроек и действий.
План таков:
- обновить роль
common
и добавить монтирование диска для Bastion с данными для NGINX - обновить
nginx
роль для копирования и использования конфигов из этого каталога
Ссылки на коммиты файлов, которые получились в результате написания этого поста:
hosts
rtfm-blog-ansible-bastion-provision.yml
roles/nginx/templates/nginx.conf
roles/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.ym
l:
Наверно – можно сейчас запушить все изменения.
Просмотреть лог красиво можно так:
[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