Прыдущая часть — 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,mailxetc
Запишу тут команды для тестов.
Ansible:
cd ~/Work/RTFM/Github/rtfm-blog-ansible-provisionansible-playbook --syntax-check --limit=rtfm-dev rtfm-blog-ansible-provision.ymlansible-playbook --limit=rtfm-dev --private-key=../../Bitbucket/aws-credentials/rtfm-dev.pem rtfm-blog-ansible-provision.ymlansible -i hosts rtfm-dev --private-key=Work/RTFM/Bitbucket/aws-credentials/rtfm-dev.pem -a "COMMANDHERE"
CloudFormation:
cd ~/Work/RTFM/Github/rtfm-blog-cf-templatesaws cloudformation --region eu-west-1 validate-template --template-body file://rtfm-blog-cf-template.jsonaws 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.







