Почему стоит использовать Ansible
У этого ПО множество достоинств:
- Простота использования. Достаточно одной master-ноды, с которой будет запускаться конфигурация на языке разметки YAML. Устанавливать агенты и стороннее ПО не нужно — для удаленного подключения хостов используется протокол SSH.
- Большое количество поддерживаемых модулей. Ansible поставляется с различными модулями, которые позволяют выполнять определенные действия: взаимодействовать с операционными системами, производить настройку сети, работать с файлами, пользователями и правами доступа.
- Безопасность. Ее обеспечивает протокол SSH, поэтому предпринимать дополнительные шаги нет необходимости.
Установка Ansible
Ansible — это кросс-платформенное ПО, поэтому его можно установить на ОС Windows, Linux и macOS. В этой статье мы будем использовать ОС Ubuntu 22.04.2 LTS. На официальном сайте Ansible приведены подробные инструкции для каждой ОС. Установить его можно несколькими способами, мы воспользуемся официальным репозиторием Ansible.
- Обновляем списки пакетов и устанавливаем пакет software-properties-common, необходимый для работы с репозиториями apt:
sudo apt update && sudo apt -y install software-properties-common
- Добавляем официальный репозиторий от Ansible:
sudo add-apt-repository --yes --update ppa:ansible/ansible
- Устанавливаем пакет ansible:
sudo apt -y install ansible
Когда установка завершится, необходимо убедиться, что она прошла корректно. Для этого выведем версию Ansible с помощью команды:
ansible --version
В первой строке вывода указана установленная версия — 2.15.2. Значит, установка прошла успешно.
Предварительная настройка Ansible
Прежде чем мы перейдем к использованию Ansible, необходимо настроить всего лишь один параметр, а именно выключить проверку ключей (host key verification) при подключении по протоколу SSH. Когда она включена, удаленный сервер просит подтвердить, что хост верный. При этом приходится вводить yes при подключении к каждому серверу. Для удобства выключим эту опцию.
В Ansible присутствует основной конфигурационный файл с именем ansible.cfg, который по умолчанию располагается в /etc/ansible.
- Открываем этот файл на редактирование:
sudo nano /etc/ansible/ansible.cfg
- В раздел defaults прописываем следующий параметр:
host_key_checking = False
По умолчанию файл ansible.cfg пустой, если не считать несколько строк комментариев. В этом случае необходимо вписать в него следующие строки:
[defaults]
host_key_checking = False
После этого сохраняем изменения и выходим из файла.
Подготовка файла hosts
Для того чтобы Ansible знал, на каких хостах необходимо выполнять определенные действия, используется файл с именем hosts. В нем указываются IP-адреса или доменные имена необходимых серверов. В нашем примере будут задействованы два сервера с IP-адресами 10.2.47.83 и 10.2.47.84. Подготовим файл hosts со следующим содержанием:
[all]
10.2.47.83 ansible_ssh_private_key_file=/home/alex/.ssh/id_rsa ansible_user=alex
10.2.47.84 ansible_ssh_private_key_file=/home/alex/.ssh/id_rsa ansible_user=alex
- Параметр ansible_ssh_private_key_file задает полный путь до файла с закрытым ключом.
- Параметр ansible_user задает имя уже существующего на удаленном сервере пользователя, которое будет использоваться при подключении по SSH.
Проверяем, что связь с удаленными хостами успешно установлена, выполнив команду:
ansible all -m ping -i hosts
Если в выводе отобразилось SUCCESS, то подключение по SSH успешно установлено. Если при подключении к хостам будут ошибки, необходимо отправить открытый ключ на требуемые серверы (команда ssh-copy-id).
Ansible Playbook для простых и повседневных задач
Файлы с описанием автоматических действий в Ansible называются playbook. Я приведу несколько примеров таких файлов для повседневных задач, все они запускаются командой:
ansible-playbook my_first_playbook.yaml -i hosts
my_first_playbook.yaml здесь — это имя playbook.
В каждом разделе я приведу полный текст YAML-файла для конкретной задачи.
Проверка свободного места на диске
check_disk_usage.yaml:
- hosts: all
tasks:
- name:
command: df -h
register: storage
- debug: var=storage.stdout_lines
Проверка запуска службы
Для примера я использую команду unit systemd-resolved.service, которая выполняет разрешение сетевых имен для локальных приложений. Таким же образом можно проверить статус любой другой службы.
check_resolver_status.yaml:
- hosts: all
gather_facts: yes
become: false
tasks:
- name: Make sure systemd-resolved.service unit is running
ansible.builtin.systemd:
state: started
name: systemd-resolved.service
Проверка общего количества и свободной оперативной памяти на сервере
memory_check.yaml:
- hosts: all
tasks:
- debug: var=ansible_memory_mb
- debug: msg="total RAM is {{ ansible_memory_mb.real.total }}"
Получение информации об используемом дистрибутиве и его версии
os_version.yaml:
- hosts: all
gather_facts: yes
become: false
tasks:
- name: Distribution
debug: msg="{{ ansible_distribution }}"
- name: Distribution version
debug: msg="{{ ansible_distribution_version}}"
- name: Distribution major version
debug: msg="{{ ansible_distribution_major_version }}"
Установка часового пояса
В этом примере полезно дополнить команду запуска: ansible-playbook set_timezone.yaml -i hosts -K
Опция -K запрашивает пароль перед запуском playbook от пользователя, у которого есть sudo-доступ. В этой задаче используется смена часового пояса от имени пользователя root или пользователя, у которого есть sudo-доступ.
Для примера я взял часовой пояс Лондона:
set_timezone.yaml:
- hosts: all
become: yes
tasks:
- name: set timezone to Europe/London
timezone:
name: Europe/London
Playbook для развертывания инфраструктурных компонентов
Далее рассмотрим примеры для развертывания таких компонентов, как LEMP-стек и Docker.
Установка LEMP-стека
LEMP — это набор программного обеспечения для работы с веб-сайтами. Содержит такие приложения, как Linux, Nginx, MySQL и PHP.
Структура файлов:
files/nginx.conf.j2
Конфигурационный файл Nginx в формате j2 (Jinja):
server {
listen {{ http_port }};
root /var/www/html;
index index.php index.html index.htm index.nginx-debian.html;
server_name {{ http_host }};
location / {
try_files $uri $uri/ =404;
}
location ~ .php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
}
location ~ /.ht {
deny all;
}
}
hosts
Файл инвентаризации. В нем прописан хост, на который будут установлены пакеты LEPM-стека:
10.2.47.84 ansible_ssh_private_key_file=/home/alex/.ssh/id_rsa ansible_user=alex
lemp_install.yaml
Playbook, в котором описаны действия для установки и настройки LEMP-стека.
vars/default.yml
Содержит переменные:
#пароль для пользователя root в MySQL
mysql_root_password: "mysql_root_password"
#адрес сервера на котором будет работать Nginx
http_host: "10.2.47.83"
#наименование файла сайта Nginx
http_conf: "10.2.47.83.conf"
#Порт Nginx
http_port: "80"
playbook.yml
Шаги, которые выполняются в этом playbook:
- Обновление списка пакетов.
- Установка пакетов nginx, mysql-server, python3-pymysql, php-fpm, php-mysql.
- Замена дефолтного конфигурационного файла на nginx.conf.j2.
- Установка пароля для пользователя root в MySQL.
- Удаление анонимных пользователей в MySQL.
- Открытие 80-го порта в UFW;
- Перезагрузка Nginx.
- hosts: all
become: true
vars_files:
- vars/default.yml
tasks:
- name: Install Prerequisites
apt: name={{ item }} update_cache=yes state=latest force_apt_get=yes
loop: [ 'aptitude' ]
- name: Install LEMP Packages
apt: name={{ item }} update_cache=yes state=latest
loop: [ 'nginx', 'mysql-server', 'python3-pymysql', 'php-fpm', 'php-mysql' ]
# Nginx Configuration
- name: Sets Nginx conf file
template:
src: "files/nginx.conf.j2"
dest: "/etc/nginx/sites-available/{{ http_conf }}"
- name: Enables new site
file:
src: "/etc/nginx/sites-available/{{ http_conf }}"
dest: "/etc/nginx/sites-enabled/{{ http_conf }}"
state: link
notify: Reload Nginx
- name: Removes "default" site
file:
path: "/etc/nginx/sites-enabled/default"
state: absent
notify: Reload Nginx
# MySQL Configuration
- name: Sets the root password
mysql_user:
name: root
password: "{{ mysql_root_password }}"
login_unix_socket: /var/run/mysqld/mysqld.sock
- name: Removes all anonymous user accounts
mysql_user:
name: ''
host_all: yes
state: absent
login_user: root
login_password: "{{ mysql_root_password }}"
- name: Removes the MySQL test database
mysql_db:
name: test
state: absent
login_user: root
login_password: "{{ mysql_root_password }}"
# UFW Configuration
- name: "UFW - Allow HTTP on port {{ http_port }}"
ufw:
rule: allow
port: "{{ http_port }}"
proto: tcp
handlers:
- name: Reload Nginx
service:
name: nginx
state: reloaded
- name: Restart Nginx
service:
name: nginx
state: restarted
Переходим по IP-адресу или доменному имени, которые указаны в директории vars файл default.yml. Такое сообщение показывает, что Nginx успешно установлен:
Установка Docker с помощью Ansible
Создадим еще один playbook, который установит Docker на сервер, а также запустит один контейнер с образом Ubuntu.
Файл hosts возьмем из предыдущего примера.
Порядок действий в playbook по установке Docker:
- Установка пакетов для работы с HTTPS-репозиториями.
- Добавление официального репозитория Docker.
- Установка Docker.
- Создание контейнера с Ubuntu.
Содержимое файла docker_install.yaml:
- hosts: all
become: true
vars:
container_count: 1
default_container_name: docker
default_container_image: ubuntu
default_container_command: sleep 1d
tasks:
- name: Install required system packages
apt:
pkg:
- apt-transport-https
- ca-certificates
- curl
- gnupg
- software-properties-common
- python3-pip
- virtualenv
- python3-setuptools
state: latest
update_cache: true
- name: Add signing key
ansible.builtin.apt_key:
url: "https://download.docker.com/linux/{{ ansible_distribution | lower }}/gpg"
state: present
- name: Set the stable docker repository
apt_repository:
repo: "deb [arch=amd64] https://download.docker.com/linux/ubuntu {{ ansible_lsb.codename }} edge"
state: present
update_cache: yes
- name: Install Docker
ansible.builtin.apt:
name:
- docker-ce
state: latest
update_cache: true
- name: Install Docker Module for Python
pip:
name: docker
- name: Pull default Docker image
community.docker.docker_image:
name: "{{ default_container_image }}"
source: pull
- name: Create default containers
community.docker.docker_container:
name: "{{ default_container_name }}{{ item }}"
image: "{{ default_container_image }}"
command: "{{ default_container_command }}"
state: present
with_sequence: count={{ container_count }}
Запускаем playbook командой:
ansible-playbook docker_install.yaml -i hosts -K
Проверяем, что на сервере с Docker появился контейнер с образом Ubuntu:
sudo docker ps -a
Контейнер успешно создан:
Несмотря на свою простоту, Ansible можно использовать как для простых задач вроде настройки часового пояса, так и для более масштабных, таких как установка LEMP-стека.