Прыдущая часть – AWS: миграция RTFM 2.7 – CloudFormation и Ansible – наcтройка NAT (там же ссылки на предыдущие посты).
В этой части продолжим настройку Bastion хоста.
Задача – добавить:
- роль logrotate: ротация логов NGINX (в дальнейшем логи будут сбрасываться через CloudWatch Logs агента)
- роль unattended-upgrades: автоапдейты системы
- роль Let’s Encrypt: получение и обновление сертификатов для сайтов
- роль NGINX Amplify: мониторинг хоста и NGINX (он же будет установлен и на Services, для мониторинга PHP-FPM)
- роль common: установка стандартных утилит –
curl
,mailx
etc
Запишу тут команды для тестов.
Ansible:
cd ~/Work/RTFM/Github/rtfm-blog-ansible-provision
ansible-playbook --syntax-check --limit=rtfm-dev rtfm-blog-ansible-provision.yml
ansible-playbook --limit=rtfm-dev --private-key=../../Bitbucket/aws-credentials/rtfm-dev.pem rtfm-blog-ansible-provision.yml
ansible -i hosts rtfm-dev --private-key=Work/RTFM/Bitbucket/aws-credentials/rtfm-dev.pem -a "COMMANDHERE"
CloudFormation:
cd ~/Work/RTFM/Github/rtfm-blog-cf-templates
aws cloudformation --region eu-west-1 validate-template --template-body file://rtfm-blog-cf-template.json
aws cloudformation --region eu-west-1 update-stack --stack-name rtfm-dev --template-body file://rtfm-blog-cf-template.json --parameters ParameterKey=HomeAllowLocation,ParameterValue=188.***.***.114/32 ParameterKey=JenkinsIP,ParameterValue=34.***.***.43/32
Ссылки на коммиты файлов, получившиеся в результате написания этого поста:
provision.groovy
– функции для Jenkins pipleinertfm-blog-ansible-provision.groovy
– Jenkins скрипт для запуска Ansible задачrtfm-blog-ansible-provision.yml
– Ansible плейбукrequirements.yml
– зависимости Ansibleroles/amplify/tasks/main.yml
– установка NGINX Amplify агентаroles/amplify/files/stub_status.conf
– для метрик NGINX Amplify агентаroles/common/tasks/main.yml
– установка прочих пакетов
Содержание
Ansible
Роль logrotate
Начнём с роли для logrotate
, используем плейбук manala.logrotate
.
Установка зависимостей – requirements.yml
Для установки плейбуков в Jenkins во время выполнения задач – создаём файл requirements.yml
, в который добавляем зависимость:
- src: manala.logrotate
Обновляем файл provision.groovy
, добавляем функцию для установки ролей:
... def ansibleRolesInstall() { docker.image('williamyeh/ansible:master-ubuntu16.04').inside('-v /var/run/docker.sock:/var/run/docker.sock') { git branch: "${ANSIBLE_GITHUB_BRANCH}", url: "${ANSIBLE_GITHUB_REPO_URL}" stage('Roles install') { sh "ansible-galaxy install --ignore-certs --roles-path roles --role-file requirements.yml" } } } ...
Добавляем её вызов в rtfm-blog-ansible-provision.groovy
:
... def provision = load 'ciscripts/provision.groovy' provision.ansibleRolesInstall() ...
Теперь добавим её выполнение.
В rtfm-blog-ansible-provision.yml
добавляем manala.logrotate
:
- hosts: all become: true roles: - nginx - nat - manala.logrotate
Проверяем локально, загружаем роль:
[simterm]
$ ansible-galaxy install --ignore-certs --role-file requirements.yml - downloading role 'logrotate', owned by manala - downloading role from https://github.com/manala/ansible-role-logrotate/archive/1.0.1.tar.gz - extracting manala.logrotate to /home/setevoy/.ansible/roles/manala.logrotate - manala.logrotate (1.0.1) was installed successfully
[/simterm]
Выполняем проверку с рабочей машины:
[simterm]
$ ansible-playbook --syntax-check --limit=rtfm-dev rtfm-blog-ansible-provision.yml playbook: rtfm-blog-ansible-provision.yml
[/simterm]
Вроде ОК – пушим, запускаем билд в Jenkins:
Проверяем на хосте:
[simterm]
root@ip-10-0-1-177:/home/admin# logrotate --version logrotate 3.11.0
[/simterm]
Добавим настройки для ротации логов – в файл rtfm-blog-ansible-provision.yml
добавляем:
- hosts: all become: true roles: - role: nginx - role: nat - role: manala.logrotate manala_logrotate_configs: - file: nginx config: - /var/log/nginx/*.log: - size: 100M - missingok - rotate: 5 - compress - delaycompress: - notifempty - create: 0640 www-data adm - sharedscripts - daily - postrotate systemctl reload nginx.service - endscript
Параметры можно посмотреть тут>>>.
Проверяем синтаксис:
[simterm]
$ ansible-playbook --syntax-check --limit=rtfm-dev rtfm-blog-ansible-provision.yml playbook: rtfm-blog-ansible-provision.yml
[/simterm]
Запускаем:
[simterm]
$ ansible-playbook --limit=rtfm-dev --private-key=../../Bitbucket/aws-credentials/rtfm-dev.pem rtfm-blog-ansible-provision.yml
[/simterm]
На Dev хосте проверяем:
[simterm]
root@ip-10-0-1-177:/home/admin# cat /etc/logrotate.d/nginx /var/log/nginx/*.log { size 100M missingok rotate 5 compress delaycompress notifempty create 0640 www-data adm sharedscripts daily postrotate systemctl reload nginx.service endscript }
[/simterm]
ОК, файл настроек есть, проверяем логи сейчас:
[simterm]
root@ip-10-0-1-177:/home/admin# ls -l /var/log/nginx/ total 4 -rw-r--r-- 1 root root 0 Jan 28 13:14 access.log -rw-r--r-- 1 root root 148 Jan 28 13:14 dev.rtfm.co.ua-access.log -rw-r--r-- 1 root root 0 Jan 28 13:14 dev.rtfm.co.ua-error.log -rw-r--r-- 1 root root 0 Jan 28 13:14 error.log
[/simterm]
Запускаем logrotate
:
[simterm]
root@ip-10-0-1-177:/home/admin# logrotate -f /etc/logrotate.conf
[/simterm]
И ещё раз логи:
[simterm]
root@ip-10-0-1-177:/home/admin# ls -l /var/log/nginx/ total 12 -rw-r--r-- 1 root root 0 Jan 28 13:14 access.log -rw-r----- 1 www-data adm 0 Jan 28 13:17 dev.rtfm.co.ua-access.log -rw-r--r-- 1 root root 148 Jan 28 13:14 dev.rtfm.co.ua-access.log.1 -rw-r--r-- 1 root root 0 Jan 28 13:14 dev.rtfm.co.ua-error.log -rw-r----- 1 www-data adm 63 Jan 28 13:17 error.log -rw-r--r-- 1 root root 63 Jan 28 13:16 error.log.1
[/simterm]
Ротация работает, можно продолжать.
Роль unattended-upgrades
Используем jnv.unattended-upgrades
.
Добавляем зависимость в requirements.yml
:
- src: manala.logrotate - src: jnv.unattended-upgrades
Добавляем вызов и настройки в rtfm-blog-ansible-provision.yml
:
... - role: jnv.unattended-upgrades unattended_mail: [email protected] unattended_automatic_reboot: true unattended_automatic_reboot_time: 05:00 unattended_clean_interval: 10
Полный список переменных для параметров – тут>>>.
Устанавливаем роль локально для проверки:
[simterm]
$ ansible-galaxy install –ignore-certs –role-file requirements.yml
[/simterm]
Проверяем:
[simterm]
$ ansible-playbook –syntax-check –limit=rtfm-dev rtfm-blog-ansible-provision.yml
[/simterm]
Запускаем:
[simterm]
$ ansible-playbook --limit=rtfm-dev --private-key=../../Bitbucket/aws-credentials/rtfm-dev.pem rtfm-blog-ansible-provision.yml
[/simterm]
Проверяем на хосте:
[simterm]
root@ip-10-0-1-177:/home/admin# cat /etc/apt/apt.conf.d/50unattended-upgrades | grep -v "//" | grep -v "^$" Unattended-Upgrade::Origins-Pattern { "origin=Debian,codename=${distro_codename},label=Debian-Security"; }; Unattended-Upgrade::Package-Blacklist { }; Unattended-Upgrade::Mail "[email protected]"; Unattended-Upgrade::Automatic-Reboot "true"; Unattended-Upgrade::Automatic-Reboot-Time "05:00";
[/simterm]
Готово.
Роль Let’s Encrypt
Используем thefinn93.letsencrypt
.
Добавляем в requirements.yml
:
... - src: thefinn93.letsencrypt
Устанавливаем локально:
[simterm]
$ ansible-galaxy install --ignore-certs --role-file requirements.yml
[/simterm]
Обновляем rtfm-blog-ansible-provision.yml
, добавляем вызов перед ролью nginx, с параметрами:
- hosts: all become: true roles: - 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: nat ...
Проверяем:
[simterm]
$ ansible-playbook --syntax-check --limit=rtfm-dev rtfm-blog-ansible-provision.yml
[/simterm]
Выполняем:
[simterm]
$ ansible-playbook --limit=rtfm-dev --private-key=../../Bitbucket/aws-credentials/rtfm-dev.pem rtfm-blog-ansible-provision.yml ... TASK [thefinn93.letsencrypt : Attempt to get the certificate using the webroot authenticator] **** ok: [dev.rtfm.co.ua] TASK [thefinn93.letsencrypt : Attempt to get the certificate using the standalone authenticator (in case eg the webserver isn't running yet)] **** ok: [dev.rtfm.co.ua] TASK [thefinn93.letsencrypt : Fix the renewal file] **** changed: [dev.rtfm.co.ua] => (item={'value': False, 'key': u'hsts'}) ok: [dev.rtfm.co.ua] => (item={'value': u'webroot', 'key': u'authenticator'}) changed: [dev.rtfm.co.ua] => (item={'value': u'certonly', 'key': u'verb'}) changed: [dev.rtfm.co.ua] => (item={'value': False, 'key': u'noninteractive_mode'}) changed: [dev.rtfm.co.ua] => (item={'value': False, 'key': u'os_packages_only'}) changed: [dev.rtfm.co.ua] => (item={'value': False, 'key': u'uir'}) TASK [thefinn93.letsencrypt : Fix the webroot map in the renewal file] **** changed: [dev.rtfm.co.ua] => (item=dev.rtfm.co.ua) TASK [thefinn93.letsencrypt : Install renewal cron] **** changed: [dev.rtfm.co.ua] TASK [thefinn93.letsencrypt : Create directory for `ssl_certificate` and `ssl_certificate_key`] **** skipping: [dev.rtfm.co.ua] TASK [thefinn93.letsencrypt : Symlink certificates to `ssl_certificate` and `ssl_certificate_key`] **** skipping: [dev.rtfm.co.ua] TASK [nginx : Install Nginx] **** ok: [dev.rtfm.co.ua] ...
[/simterm]
Проверяем:
[simterm]
root@ip-10-0-1-177:/home/admin# letsencrypt certificates Saving debug log to /var/log/letsencrypt/letsencrypt.log ------------------------------------------------------------------------------- Found the following certs: Certificate Name: dev.rtfm.co.ua Domains: dev.rtfm.co.ua Expiry Date: 2018-04-28 13:32:36+00:00 (VALID: 89 days) Certificate Path: /etc/letsencrypt/live/dev.rtfm.co.ua/fullchain.pem Private Key Path: /etc/letsencrypt/live/dev.rtfm.co.ua/privkey.pem -------------------------------------------------------------------------------
[/simterm]
На самом деле думаю, что Ansible будет выполнять только установку Let’s Encrypt клиента, а управлять сертификатами я буду уже вручную, потому что при создании стека для rtfm-production – домен будет направлен на старый EC2, нынешний, и авторизация не пройдёт (хотя можно выполнить DNS-верификацию). Но тогда не запустится NGINX, в конфигах которого будут указаны пути к SSL сертификатам…
Посмотрим, пока всё в процессе настройки и планирования – можно оставить так.
Роль NGINX Amplify
Для установки мониторинга – создадим новую роль.
Добавляем каталоги:
[simterm]
$ mkdir -p roles/amplify/{tasks,files}
[/simterm]
В посте NGINX: Amplify – SaaS мониторинг от NGINX установка выполнялась из скрипта, тут используем репозиторий NGINX.
На Dev хосте проверяем ОС:
[simterm]
admin@ip-10-0-1-177:~$ lsb_release -a No LSB modules are available. Distributor ID: Debian Description: Debian GNU/Linux 9.1 (stretch) Release: 9.1 Codename: stretch
[/simterm]
Создаём в roles/amplify/tasks/
файл main.yml
, выполняем импорт ключа, добавление репозитория и установку агента:
- name: Add the NGINX Apt signing key apt_key: url: http://nginx.org/keys/nginx_signing.key state: present - name: Add NGINX Amplify repository apt_repository: repo: deb http://packages.amplify.nginx.com/debian/ stretch amplify-agent state: present - name: Install Amplify agent apt: name: nginx-amplify-agent update_cache: yes
В файле rtfm-blog-ansible-provision.yml
добавляем выполнение роли после роли nginx:
... - role: nginx - role: amplify - role: nat - role: manala.logrotate manala_logrotate_configs: ...
Проверяем, запускаем:
[simterm]
$ ansible-playbook --syntax-check --limit=rtfm-dev rtfm-blog-ansible-provision.yml $ ansible-playbook --limit=rtfm-dev --private-key=../../Bitbucket/aws-credentials/rtfm-dev.pem rtfm-blog-ansible-provision.yml ... TASK [amplify : Add the NGINX Apt signing key] **** changed: [dev.rtfm.co.ua] TASK [amplify : Add NGINX AMplify repository] **** changed: [dev.rtfm.co.ua] TASK [amplify : Install Amplify agent] **** changed: [dev.rtfm.co.ua]
[/simterm]
На хосте проверяем файл:
[simterm]
admin@ip-10-0-1-177:~$ file /etc/amplify-agent/agent.conf.default /etc/amplify-agent/agent.conf.default: C++ source, ASCII text
[/simterm]
ОК, теперь надо добавить API ключ.
В roles/amplify/tasks/main.yml
добавляем копирование файла настроек и с помощью lineinfile
обновление в нём ключа:
... - name: Copy default Amplify config command: cp /etc/amplify-agent/agent.conf.default /etc/amplify-agent/agent.conf - name: Add Amplify API key lineinfile: path: /etc/amplify-agent/agent.conf regexp: '^api_key =' line: 'api_key = {{ api_key }}'
В Jenkins он будет передаваться через переменные в билде, пока передаём его через --extra-vars
, выполняем:
[simterm]
$ ansible-playbook --limit=rtfm-dev --extra-vars '{"api_key":"111222333"}' --private-key=../../Bitbucket/aws-credentials/rtfm-dev.pem rtfm-blog-ansible-provision.yml
[/simterm]
Проверяем:
[simterm]
root@ip-10-0-1-177:/home/admin# cat /etc/amplify-agent/agent.conf | grep api_key api_key = 111222333
[/simterm]
Аналогично можно сразу добавить имя хоста, которое будет отображаться в Amplify dashboard:
... - name: Add hostname for Amplify dashboard lineinfile: path: /etc/amplify-agent/agent.conf regexp: '^hostname =' line: 'hostname = {{ inventory_hostname }}'
Документация по установке Amplify агента – тут>>>.
Сюда же добавляем запуск агента и его добавление в автозапуск:
... - name: Start Amplify service systemd: name: amplify-agent state: started - name: Enable Amplify service systemd: name: amplify-agent enabled: yes
Запускаем выполнение, проверяем Amplify:
Хост dev.rtfm.co.ua появился, всё гуд.
Далее в roles/amplify/files
добавляем файл stub_status.conf
:
server { listen 127.0.0.1:80; server_name 127.0.0.1; location /nginx_status { stub_status; allow 127.0.0.1; deny all; } }
В файл roles/amplify/tasks/main.yml
– его копирование и перезагрузку конфигов NGINX:
... - name: Copy stub_status.conf copy: src: files/stub_status.conf dest: /etc/nginx/conf.d/stub_status.conf - name: Reload NGINX service systemd: name: nginx state: reloaded
Ещё раз запускаем, проверяем:
Данные пошли.
Осталось обновить скрипт для Jenkins – добавить передачу API_KEY
.
Добавляем параметр:
Обновляем provision.groovy
, функция ansiblePlaybookApply()
, добавляем --extra-vars api_key=${AMPLIFY_API_KEY}
:
... def ansiblePlaybookApply(ansibleHostLimit='1', ansiblePlaybookFile='2', ansiblePemFile='3') { ... stage('Ansible playbook apply') { sh "chmod 400 credentials/${ANSIBLE_EC2_PEM_FILE}" sh "set +x && ansible-playbook --limit=${ansibleHostLimit} --extra-vars api_key=${AMPLIFY_API_KEY} --private-key=credentials/${ansiblePemFile} ${ansiblePlaybookFile}" } } } ...
set +x
– что бы Jenkins не выводил ключ в логе билда.
Пушим, запускаем, проверяем:
Amplify:
С этим вроде всё.
Роль common
Для установки всего прочего – добавляем роль common:
[simterm]
$ mkdir -p roles/common/{tasks,templates}
[/simterm]
Добавляем roles/common/tasks/main.yml
, в цикле описываем установку необходимых пакетов:
- name: Install common packages apt: name={{item}} state=present with_items: - mailutils - curl
Добавляем вызов роли в rtfm-blog-ansible-provision.yml
:
... - role: jnv.unattended-upgrades unattended_mail: [email protected] unattended_automatic_reboot: true unattended_automatic_reboot_time: 05:00 unattended_clean_interval: 10 - role: common
Собственно – на этом пока и всё.
Сохраняем, пушим.
Теперь, для проверки, можно удалить весь стек, и пересоздать его заново:
[simterm]
$ aws cloudformation delete-stack --stack-name rtfm-dev
[/simterm]
Запускаем создание стека в Jenkins.
Запускаем Ansible в Jenkins.
Готово:
Можно переходить к ролям для DB и Services.