Представим, что у вас есть 10 серверов под разные или похожие задачи. Каждую машину нужно периодически обновлять, изменять ее конфигурацию. Часто необходимо поднять и настроить сразу несколько серверов в короткие сроки. Конечно, можно подключаться к каждому по SSH и вручную вносить необходимые изменения, но это очень долго и увеличивает возможность совершения ошибок из-за человеческого фактора.
Для эффективного решения подобных задач DevOps-специалисты используют систему удаленного управления конфигурацией - Ansible. Ansible автоматизирует поставку ПО, управление конфигурацией и развёртывание приложений. Присоединяйтесь в нашему Телеграм-каналу: пишем про облако, мануалы и решения задач.
Главные преимущества Ansible:
1) Работа по SSH. На управляемые серверы не нужно устанавливать дополнительный софт, Ansible должен быть установлен лишь на управляющем сервере.
2) Простота. Код написан на Python, поэтому написание дополнительных модулей под ваши задачи не составит труда. Конфигурационные файлы Ansible создаются в формате YAML - формат сериализованных данных, намного проще и читабельнее, чем JSON и XML.
3) Декларативность. Разработчику нужно писать не действия программы, а результат, которого она должна достичь. Ansible сам спланирует как именно он будет достигнут. Это значительно сокращает время настройки.
4) Push и Pull. Ansible может работать в обоих режимах: главный сервер может "вытягивать" информацию из управляемых, или наоборот, "проталкивать" ее в зависимые сервера.
И недостатки:
1) Проблемы с Windows. С версии 1.7 Ansible поддерживает Windows, но работать приходится с оболочкой PowerShell, необходимо установить Linux-модуль.
2) Контроль состояния. В Ansible нет постоянного мониторинга зависимых серверов, он лишь выполняет задачи и наблюдает за состоянием хоста. В других системах управления есть более широкие возможности для контроля состояния серверов.
Структура Ansible
Модули
Это программы, выполняющие работу на сервере. Например, вместо запуска команды на машине:
$ apt install nginx
Мы можем использовать модуль apt и установить nginx :
- name: Install htop
apt: name=htop
Хосты
Чтобы предоставить перечень хостов, нам нужно обозначить список, находящийся в файле инвентаризации.
Playbooks
Playbooks — основа работы Ansible. Это способ отправки команд на удалённые компьютеры с помощью скриптов в формате YAML. Вместо того, чтобы индивидуально использовать команды для удалённой настройки компьютеров из командной строки, вы можете настраивать целые сложные среды, передавая скрипт одной или нескольким серверам.
Vars
Файл содержит набор переменных, например имя пользователя и пароль базы данных.
Роли
Это способ сгруппировать несколько задач в один контейнер, чтобы эффективно автоматизировать работу с помощью понятной структуры каталогов.
Файлы конфигураций Ansible делятся на блоки, начинающиеся с “- name”. Сначала указывается название операции, а потом модуль, который нужен для ее выполнения. После этого нужно указать переменные модуля.
На первый взгляд это может показаться сложным, но структура Ansible очень проста, а модули быстро запоминаются или находятся в интернете. Существует полная документация, помимо этого, вы можете найти огромное количество самописные модулей под самые разные задачи.
Рассмотрим удобство Ansible на конкретном примере - с помощью Ansible мы установим сервисы на машины и отредактируем их конфигурации:
1) Ubuntu22, 192.168.1.2 – MariaDB
2) Ubuntu23, 192.168.1.3 – MySQL
3) Debian12, 172.16.1.4 – стек LAMP + WordPress
С помощью Ansible мы установим сервисы на машины и отредактируем их конфигурации.
ВНИМАНИЕ! В конфигурациях мы используем шаблонные пароли для наглядности, обязательно измените их.
Установка
На управляемых узлах должен быть установлен Python версии 2.4 и выше, по умолчанию он вшит в большинство дистрибутивов Linux-систем.
- На сервер ansible-controller необходимо установить Ansible, рассмотрим пример для Ubuntu:
$ sudo apt update
$ sudo apt install ansible
При установке будет создан каталог /etc/ansible с конфигурационными файлами: hosts и ansible.cfg - файл с настройками Ansible.
В простейшем виде файл hosts может содержать одну строку (адрес управляемого сервера): 192.168.1.2
- Вы можете указать только ip-адреса серверов, но будет удобнее сразу записать номера портов ssh, пользователей, пароли и т.д. Укажем информацию о хостах:
$ nano hosts
all:
hosts:
server-1:
ansible_host: 192.168.1.2
ansible_connection: ssh
ansible_user: root
ansible_password: b1Ge62qUzIZhmtrH
server-2:
ansible_host: 192.168.1.3
ansible_connection: ssh
ansible_user: root
ansible_password: h1FF8LQUmapS5uj0
server-3:
ansible_host: 172.16.1.4
ansible_connection: ssh
ansible_user: root
ansible_password: Byu0M1E7vJyxQ2m6
- Теперь мы можем проверить доступность серверов:
root@ubuntu:/etc/ansible# ansible all -i hosts -m ping
server-1 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}
server-3 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}
server-2 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}
Отлично, Ansible-controller имеет доступ ко всем необходимым серверам.
- Начнем с MariaDB, для этого создадим новую конфигурацию Ansible:
$ nano mariadb-management.yml
- Разберем из чего состоит этот файл конфигурации:
Сначала мы указываем информацию об управляемых серверах и переменные.
---
- name: MariaDB Management using Ansible
hosts: server-1
gather_facts: true
become: true
vars:
mysql_root_password: "StrongRootPassword!"
python_mysql: "{{ 'python3-mysqldb' if ansible_distribution|lower in ['debian', 'ubuntu'] else 'python3-PyMySQL' }}"
Устанавливаем и включаем сервисы:
tasks:
- name: Installing MariaDB Server
package:
name: mariadb-server
state: latest
update_cache: yes
- name: Installing python_mysql
package:
name: "{{ python_mysql }}"
state: present
update_cache: yes
- name: start and enable mariadb server
service:
name: mariadb
enabled: true
state: started
Обновляем пароль СУБД:
- name: update mysql root password for all root accounts
mysql_user:
name: root
host: "{{ item }}"
password: "{{ mysql_root_password }}"
login_user: root
login_password: "{{ mysql_root_password }}"
check_implicit_admin: yes
priv: "*.*:ALL,GRANT"
with_items:
- "{{ ansible_hostname}}"
- 127.0.0.1
- ::1
- localhost
- Создаем новую БД и пользователя с полными привилегиями:
- name: Create a new database with name 'testdb'
mysql_db:
name: testdb
state: present
login_user: root
login_password: "{{ mysql_root_password }}"
- name: Create database user & grant all privileges on the testdb
mysql_user:
name: testuser1
password: Passw0rd!
priv: 'testdb.*:ALL'
host: '%'
state: present
login_user: root
login_password: "{{ mysql_root_password }}"
Это все, что нужно для установки и настройки MariaDB на нашем сервере, пора запустить Ansible:
По выводу программы понятно, что все необходимые сервисы установлены и настроены.
Теперь поднимем MySQL-сервер, добавим необходимую базу данных и пользователя с привилегиями:
- Создадим конфигурацию Ansible для MySQL:
$ nano mysql-management.yml
---
- name: MySQL Server Management Using Ansible
become: yes
hosts: server-2
vars:
mysql_root_password: "StrongRootPassword!"
service_name: "{{ 'mysql' if ansible_distribution|lower in ['debian', 'ubuntu'] else 'mysqld' }}"
python_mysql: "{{ 'python3-mysqldb' if ansible_distribution|lower in ['debian', 'ubuntu'] else 'python3-PyMySQL' }}"
tasks:
- name: Determine package manager
ansible.builtin.set_fact:
package_manager: "{{ 'apt' if ansible_distribution|lower in ['debian', 'ubuntu'] else 'yum' }}"
- name: Installing MySQL and Required dependencies
package:
name: "{{ item }}"
state: present
update_cache: yes
loop:
- mysql-server
- name: Installing python_mysql
package:
name: "{{ python_mysql }}"
state: present
update_cache: yes
- name: Start and enable mysql service
service:
name: "{{ service_name }}"
state: started
enabled: yes
- name: update mysql root password for all root accounts
mysql_user:
name: root
host: "{{ item }}"
password: "{{ mysql_root_password }}"
login_user: root
login_password: "{{ mysql_root_password }}"
check_implicit_admin: yes
priv: "*.*:ALL,GRANT"
with_items:
- "{{ ansible_hostname }}"
- 127.0.0.1
- ::1
- localhost
- name: Create a new database with name 'testdb'
mysql_db:
name: testdb
state: present
login_user: root
login_password: "{{ mysql_root_password }}"
- name: Create database user & grant all privileges on the testdb
mysql_user:
name: testuser1
password: Passw0rd!
priv: 'testdb.*:ALL'
host: '%'
state: present
login_user: root
login_password: "{{ mysql_root_password }}"
Запускаем!
И опять можно понять, что все сработало. Теперь у нас есть конфигурации, которые позволяют создавать СУБД с необходимыми параметрами за 5 минут сразу на нескольких машинах.
Наконец, попробуем что-то более серьезное: стек LAMP и WordPress с готовыми конфигурациями сервисов.
Так будет выглядеть структура нашего проекта:
1) playbook.yml – конфигурация Ansible, в которой будет описано, какой результат нам необходим;
2) hosts – список хостов;
3) apache.conf.j2 и wp-config.php.j2 – конфигурации сервисов LAMP+WP;
4) default.yml – здесь буду указаны пароли и пользователи СУБД, хост и порт веб-сервера.
$ nano playbook.yml
---
- hosts: server-3
become: true
vars_files:
- vars/default.yml
tasks:
- name: Install prerequisites
apt: name=aptitude update_cache=yes state=latest force_apt_get=yes
tags: [ system ]
- name: Install LAMP Packages
apt: name={{ item }} update_cache=yes state=latest
loop: [ 'apache2', 'mariadb-server', 'python3-pymysql', 'php', 'php-mysql', 'libapache2-mod-php' ]
tags: [ system ]
- name: Install PHP Extensions
apt: name={{ item }} update_cache=yes state=latest
loop: "{{ php_modules }}"
tags: [ system ]
# Apache Configuration
- name: Create document root
file:
path: "/var/www/{{ http_host }}"
state: directory
owner: "www-data"
group: "www-data"
mode: '0755'
tags: [ apache ]
- name: Set up Apache VirtualHost
template:
src: "files/apache.conf.j2"
dest: "/etc/apache2/sites-available/{{ http_conf }}"
notify: Reload Apache
tags: [ apache ]
- name: Enable rewrite module
shell: /usr/sbin/a2enmod rewrite
notify: Reload Apache
tags: [ apache ]
- name: Enable new site
shell: /usr/sbin/a2ensite {{ http_conf }}
notify: Reload Apache
tags: [ apache ]
- name: Disable default Apache site
shell: /usr/sbin/a2dissite 000-default.conf
notify: Restart Apache
tags: [ apache ]
# MySQL Configuration
- name: Set the root password
mysql_user:
name: root
password: "{{ mysql_root_password }}"
login_unix_socket: /var/run/mysqld/mysqld.sock
tags: [ mysql, mysql-root ]
- name: Remove all anonymous user accounts
mysql_user:
name: ''
host_all: yes
state: absent
login_user: root
login_password: "{{ mysql_root_password }}"
tags: [ mysql ]
- name: Remove the MySQL test database
mysql_db:
name: test
state: absent
login_user: root
login_password: "{{ mysql_root_password }}"
tags: [ mysql ]
- name: Creates database for WordPress
mysql_db:
name: "{{ mysql_db }}"
state: present
login_user: root
login_password: "{{ mysql_root_password }}"
tags: [ mysql ]
- name: Create MySQL user for WordPress
mysql_user:
name: "{{ mysql_user }}"
password: "{{ mysql_password }}"
priv: "{{ mysql_db }}.*:ALL"
state: present
login_user: root
login_password: "{{ mysql_root_password }}"
tags: [ mysql ]
# UFW Configuration
- name: "UFW - Allow HTTP on port {{ http_port }}"
ufw:
rule: allow
port: "{{ http_port }}"
proto: tcp
tags: [ system ]
# WordPress Configuration
- name: Download and unpack latest WordPress
unarchive:
src: https://wordpress.org/latest.tar.gz
dest: "/var/www/{{ http_host }}"
remote_src: yes
creates: "/var/www/{{ http_host }}/wordpress"
tags: [ wordpress ]
- name: Set ownership
file:
path: "/var/www/{{ http_host }}"
state: directory
recurse: yes
owner: www-data
group: www-data
tags: [ wordpress ]
- name: Set permissions for directories
shell: "/usr/bin/find /var/www/{{ http_host }}/wordpress/ -type d -exec chmod 750 {} \\;"
tags: [ wordpress ]
- name: Set permissions for files
shell: "/usr/bin/find /var/www/{{ http_host }}/wordpress/ -type f -exec chmod 640 {} \\;"
tags: [ wordpress ]
- name: Set up wp-config
template:
src: "files/wp-config.php.j2"
dest: "/var/www/{{ http_host }}/wordpress/wp-config.php"
tags: [ wordpress ]
handlers:
- name: Reload Apache
service:
name: apache2
state: reloaded
- name: Restart Apache
service:
name: apache2
state: restarted
$ mkdir vars
$ nano vars/default.yml
---
#System Settings
php_modules: [ 'php-curl', 'php-gd', 'php-mbstring', 'php-xml', 'php-xmlrpc', 'php-soap', 'php-intl', 'php-zip' ]
#MySQL Settings
mysql_root_password: "mysql_root_password"
mysql_db: "wordpress"
mysql_user: "sammy"
mysql_password: "mysql_password"
#HTTP Settings
http_host: "my_site.com"
http_conf: "my_site.conf"
http_port: "80"
$ mkdir files
$ nano files/apache.conf.j2
<VirtualHost *:{{ http_port }}>
ServerAdmin webmaster@localhost
ServerName {{ http_host }}
ServerAlias www.{{ http_host }}
DocumentRoot /var/www/{{ http_host }}/wordpress
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
<Directory /var/www/{{ http_host }}>
Options -Indexes
AllowOverride All
</Directory>
<IfModule mod_dir.c>
DirectoryIndex index.php index.html index.cgi index.pl index.xhtml index.htm
</IfModule>
</VirtualHost>
$ nano files/wp-config.php.j2
<?php
define( 'DB_NAME', '{{ mysql_db }}' );
define( 'DB_USER', '{{ mysql_user }}' );
define( 'DB_PASSWORD', '{{ mysql_password }}' );
define( 'DB_HOST', 'localhost' );
define( 'DB_CHARSET', 'utf8' );
define( 'DB_COLLATE', '' );
define('FS_METHOD', 'direct');
define( 'AUTH_KEY', '{{ lookup('password', '/dev/null chars=ascii_letters length=64') }}' );
define( 'SECURE_AUTH_KEY', '{{ lookup('password', '/dev/null chars=ascii_letters length=64') }}' );
define( 'LOGGED_IN_KEY', '{{ lookup('password', '/dev/null chars=ascii_letters length=64') }}' );
define( 'NONCE_KEY', '{{ lookup('password', '/dev/null chars=ascii_letters length=64') }}' );
define( 'AUTH_SALT', '{{ lookup('password', '/dev/null chars=ascii_letters length=64') }}' );
define( 'SECURE_AUTH_SALT', '{{ lookup('password', '/dev/null chars=ascii_letters length=64') }}' );
define( 'LOGGED_IN_SALT', '{{ lookup('password', '/dev/null chars=ascii_letters length=64') }}' );
define( 'NONCE_SALT', '{{ lookup('password', '/dev/null chars=ascii_letters length=64') }}' );
$table_prefix = 'wp_';
define( 'WP_DEBUG', false );
if ( ! defined( 'ABSPATH' ) ) {
define( 'ABSPATH', dirname( __FILE__ ) . '/' );
}
require_once( ABSPATH . 'wp-settings.php' );
Все файлы созданы, можно запускать ansible-playbook:
Все установлено, проверяем:
Отлично, наш сервер работает. Теперь вы можете использовать эту конфигурацию Ansible, чтобы быстро устанавливать и настраивать свои веб-серверы.
Выводы
Для работы с Ansible не нужны агенты на управляемых серверах. Он использует Python и Yaml, простые и удобные языки для новичка. Так же, программа предоставляет набор надежных функций и встроенных модулей, которые облегчают написание сценариев автоматизации.
Это эффективный и расширяемый Open Source инструмент, который позволяет управлять конфигурациями удаленных серверов и сохранять их состояние. Мы лишь поверхностно прошлись по его возможностям, показали как пишутся сценарии для решения различных задач. Все варианты использования Ansible в рамках одной статьи охватить невозможно и не нужно, а для желающих узнать больше мы подготовили несколько ссылок:
1) https://github.com/ansible/ — официальный аккаунт на github c исходным кодом проекта и хорошим набором примеров проектов
2) https://docs.ansible.com/ - документация Ansible
3) https://github.com/do-community/ansible-playbooks - готовые конфигурации Ansible под разные задачи
4) https://medium.com/the-sysadmin/managing-windows-machines-with-ansible-60395445069f - управление Windows-сервером
Ставьте лайк, если было полезно! Присоединяйтесь в нашему Телеграм-каналу, пишем про облако, мануалы и кейсы работы.