Задача — установить NGINX на удалённый хост с помощью Ansbile.
Содержание
Подготовка
На рабочей машине — устанавливаем Ansible:
В Jenkins — будем использовать
Проверяем:
Создание Inventory
Пока есть только один Dev сервер, позже добавим Production.
Документация по Ansbile Inventory —
В корне репозитория создаём каталоги:
Переходим в него, и создаём файл hosts
в который добавляем URL-ы серверов:
[dev] dev.monitor.domain.ms [production] monitor.domain.ms
hosts
у меня в INI, хотя остальные конфиги в YAML — Ansible позволяет использовать оба формата одновременно.
Тут же добавляем переменную user
, что бы не указывать его каждый раз (по умолчанию — Ansbile использует имя пользователя, от которого запускается):
[dev] dev.monitor.domain.ms ansible_connection=ssh ansible_user=jmadmin [production] monitor.domain.ms
Далее — указываем Ansible где искать файл hosts
. Создаём файл ansible.cfg
с блоком [defaults]
:
[defaults] inventory = hosts
Выполнение команд
Теперь, когда inventory создан — можно начать выполнение команд на хостах.
Попробуем пропинговать Dev — используем модуль ping
--private-key
— путь к RSA ключу сервера:
Что бы выполнить произвольную команду на хосте, без использования модуля — используем -a
:
Playbook
Документация по плейбукам —
Создаём файл плейбука с именем provision.yml
:
- hosts: all become: true become_method: sudo roles: - nginx
Тут:
... become: true ...
Выполнять от root
.
... become_method: sudo ...
Использовать sudo
(хотя sudo
используется по умолчанию).
Roles
В provision.yml
мы добавили роль nginx
, которая будет влючать в себя установку NGINX.
Позже — добавим роли для установки Prometheus, Docker, настройки окружения и т.д.
Для каждой роли — Ansible будет искать стандартный набор каталогов — files
, tasks
, handlers
и т.д.
Для nginx
роли создаём каталог tasks
— пока этого хватит:
Структура сейчас выглядит так:
В каждом из каталогов Ansible будет искать файл main.yml
— создаём файл roles/nginx/tasks/main.yml
и описываем в нём задачу:
- name: Install Nginx package: name: nginx state: latest
Тут мы используем модуль package
Запускаем плейбук на выполнение.
Используем --limit
, что бы указать хосты (тут — dev, один сервер из hosts
файла созданного ранее), и --private-key
, как ранее с absible
:
Готово.
Проверяем состояние сервиса:
Проверяем состояние NGINX:
Переменные
Следующим шагом — требуется добавить файл настроек виртуалхоста. Реализовываться это будет через
tasks
. Важную роль играет приоритет и порядок задания переменных. Подробнее —
Ещё один момент — это область действия переменных:
- Global: глобальные переменные, заданные через
ansible.cfg
, переменные окружения и командную строку - Play: переменные, определённые для плейбуков и ролей
- Host: и переменные для хостов, заданные через inventory или
tasks
Создаём каталог vars
:
Для файла nginx.conf
роли nginx
пока зададим такие переменные в файле main.yml
:
listen_http: 80 listen_https: 443 nginx_user: www-data worker_processes: auto worker_connections: 512
Это — глобальные переменные роли nginx
, значения которых будут одинаковы на всех хостах с NGINX.
Пока с переменными всё — переходим к шаблону.
Templates
Шаблонизатор Ansible —
Нам потребуется два шаблона — nginx.conf
, что бы переопределить логгирование (добавим log proxy
), и шаблон для виртуалхоста (хотя пока бекендов нет — будем редиректить на Google).
Создаём каталог templates
:
В нём создаём файл nginx.conf
:
user {{ nginx_user: }}; worker_processes {{ worker_processes }}; pid /run/nginx.pid; events { worker_connections {{ worker_connections }} ; } http { sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; types_hash_max_size 2048; include /etc/nginx/mime.types; default_type application/octet-stream; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_prefer_server_ciphers on; access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log; log_format proxy '[$time_local] $remote_addr - $server_name to: ' '$upstream_addr: $request upstream_response_time ' '$upstream_response_time msec $msec request_time $request_time'; gzip on; gzip_disable "msie6"; include /etc/nginx/conf.d/*.conf; }
Собственно — вот наши переменные:
user {{ nginx_user: }};
и т.д.
Далее — настройки виртуалхоста. Т.к. параметры в нём будут зависеть от хоста — добавим переменные в inventory файл, например — адрес upstream
.
В hosts
переменные могут быть добавлены для каждого хоста —
[dev] dev.monitor.domain.ms ansible_connection=ssh ansible_user=jmadmin hostname=dev.monitor.domain.ms
Тут мы добавили переменную hostname=dev.monitor.domain.ms
, которую потом используем для конфига виртуалхоста Dev хоста.
Либо для всех хостов в группе dev,
[dev] dev.monitor.domain.ms ansible_connection=ssh ansible_user=jmadmin hostname=dev.monitor.domain.ms [dev:vars] upstream_name-google upstream_url=google.com ...
Добавляем шаблон конфига виртуалхоста:
upstream {{ upstream_name }} { server {{ upstream_url }}; } server { listen {{ listen_http }} ; server_name www.{{ hostname }}; return 301 $scheme://{{ hostname }}$request_uri; } server { server_name {{ hostname }}; listen {{ listen_http }} ; # listen {{ listen_https }} ssl; access_log /var/log/nginx/{{ hostname }}-access.log proxy; error_log /var/log/nginx/{{ hostname }}-error.log notice; # ssl on; # ssl_certificate /etc/letsencrypt/live/{{ hostname }}/fullchain.pem; # ssl_certificate_key /etc/letsencrypt/live/{{ hostname }}/privkey.pem; root /var/www/{{ hostname }}; location ~ /.well-known { allow all; } location / { proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_pass http://{{ upstream_name }}$request_uri; } }
Настройки SSL пока закомментированы — добавим их чуть позже.
Возвращаемся к tasks
, и добавляем копирование шаблонов:
- name: Install Nginx package: name: nginx state: latest - name: Replace NGINX config template: src=templates/nginx.conf dest=/etc/nginx/nginx.conf - name: Add NGINX virtualhost config template: src=templates/nginx_vhost.conf dest=/etc/nginx/conf.d/{{ hostname }}.conf
На этом этапе уже можно проверить — всё ли будет работать. Для этого у ansible-playbook
есть опция --check
(dry-run), выполняем:
Эм…
А!
... [dev:vars] upstream_name-google ...
Тире вместо =.
Ещё раз:
Теперь ошибка в шаблоне nginx.conf
:
... String: user {{ nginx_user: }}; ...
Двоеточие.
И ещё раз:
Теперь всё нормально.
Handlers
После того, как конфиги NGINX будут скопированы на хост — надо указать NGINX на необходимость перечитать файлы настроек — выполнить nginx reload
.
Тут используем
Создаём каталог handlers
:
И файл main.yml
, в котором используем модуль service
- name: NGINX reload service: name=nginx state=reloaded
Далее в tasks
требуется указать — когда выполнять reload
, используем notify
.
Возвращаемся к tasks
, добавляем notify
и имя обработчика:
- name: Install Nginx package: name: nginx state: latest - name: Replace NGINX config template: src=templates/nginx.conf dest=/etc/nginx/nginx.conf notify: - NGINX reload - name: Add NGINX virtualhost config template: src=templates/nginx_vhost.conf dest=/etc/nginx/conf.d/{{ hostname }}.conf notify: - NGINX reload
Вроде ОК — выполняем проверку ещё раз:
Хорошо — запускаем на выполнение:
Проверяем файл виртуахоста на сервере:
Проверяем как работает редирект NGINX:
Всё отлично.
В принципе — на этом всё.
SSL и files настроим уже в следующий раз.
Структура файлов и каталогов сейчас получается такой:
$ tree . ├── ansible.cfg ├── hosts ├── provision.retry ├── provision.yml └── roles └── nginx ├── handlers │ └── main.yml ├── tasks │ └── main.yml ├── templates │ ├── nginx.conf │ └── nginx_vhost.conf └── vars └── main.yml
Ссылки по теме
Provisioning remote machines using Ansible
Ansible: роли (roles) – пример