Ansible — автоматизація інфраструктури¶
Ansible — інструмент для автоматизації конфігурації серверів, деплою застосунків та оркестрації. Головна перевага — agentless архітектура: не потрібно нічого встановлювати на цільових серверах.
Чому Ansible?¶
┌─────────────────────────────────────────────────────────────────┐
│ ЧОМУ ANSIBLE? │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Без Ansible: │
│ ├── ssh server1 "apt update && apt install nginx" │
│ ├── ssh server2 "apt update && apt install nginx" │
│ ├── ssh server3 "apt update && apt install nginx" │
│ └── ... повторити для 50 серверів 😫 │
│ │
│ З Ansible: │
│ └── ansible webservers -m apt -a "name=nginx state=present" │
│ → Виконується паралельно на всіх серверах │
│ │
│ ─────────────────────────────────────────────────────────── │
│ │
│ Переваги Ansible: │
│ ✓ Agentless — тільки SSH потрібен │
│ ✓ YAML — людиночитабельний формат │
│ ✓ Ідемпотентність — можна запускати багато разів │
│ ✓ Push модель — контроль з однієї точки │
│ ✓ Величезна бібліотека модулів │
│ │
│ Порівняння: │
│ │ Ansible │ Agentless │ Push │ YAML │ Простий │
│ │ Chef │ Agent │ Pull │ Ruby DSL │ Складний │
│ │ Puppet │ Agent │ Pull │ Puppet DSL│ Складний │
│ │ Salt │ Optional │ Both │ YAML │ Середній │
│ │
└─────────────────────────────────────────────────────────────────┘
Встановлення¶
Ubuntu/Debian¶
# Через apt
sudo apt update
sudo apt install ansible
# Або через pip (новіша версія)
pip install ansible
macOS¶
brew install ansible
Перевірка¶
ansible --version
# ansible [core 2.15.0]
Архітектура¶
┌─────────────────────────────────────────────────────────────────┐
│ ANSIBLE АРХІТЕКТУРА │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Control Node Managed Nodes │
│ (ваш ноутбук) (сервери) │
│ │
│ ┌─────────────────┐ ┌───────────────┐ │
│ │ Ansible │ SSH │ Server 1 │ │
│ │ ┌───────────┐ │ ─────────> │ (Ubuntu) │ │
│ │ │ Inventory │ │ └───────────────┘ │
│ │ ├───────────┤ │ ┌───────────────┐ │
│ │ │ Playbooks │ │ ─────────> │ Server 2 │ │
│ │ ├───────────┤ │ │ (Debian) │ │
│ │ │ Modules │ │ └───────────────┘ │
│ │ └───────────┘ │ ┌───────────────┐ │
│ └─────────────────┘ ─────────> │ Server 3 │ │
│ │ (CentOS) │ │
│ └───────────────┘ │
│ │
│ Вимоги до managed nodes: │
│ • SSH доступ │
│ • Python (для більшості модулів) │
│ • Нічого більше не треба! │
│ │
└─────────────────────────────────────────────────────────────────┘
Inventory¶
Inventory — список серверів, якими керує Ansible.
INI формат¶
# inventory.ini
# Окремі хости
server1.example.com
server2.example.com ansible_host=192.168.1.10
# Групи
[webservers]
web1.example.com
web2.example.com
[databases]
db1.example.com
db2.example.com
# Група груп
[production:children]
webservers
databases
# Змінні для групи
[webservers:vars]
ansible_user=deploy
http_port=80
YAML формат¶
# inventory.yml
all:
children:
webservers:
hosts:
web1.example.com:
web2.example.com:
ansible_host: 192.168.1.11
vars:
ansible_user: deploy
http_port: 80
databases:
hosts:
db1.example.com:
db2.example.com:
Динамічний inventory¶
# Для AWS, GCP, тощо — плагіни
# ansible-inventory -i aws_ec2.yml --list
# Або скрипт що повертає JSON
./inventory.py
Ad-hoc команди¶
Одноразові команди без playbook.
# Перевірка зв'язку
ansible all -i inventory.ini -m ping
# Виконати shell команду
ansible webservers -m shell -a "uptime"
# Встановити пакет
ansible webservers -m apt -a "name=nginx state=present" --become
# Скопіювати файл
ansible webservers -m copy -a "src=./config.conf dest=/etc/app/config.conf"
# Перезапустити сервіс
ansible webservers -m service -a "name=nginx state=restarted" --become
# Зібрати факти про сервери
ansible all -m setup
Параметри¶
| Параметр | Опис |
|---|---|
-i |
Inventory файл |
-m |
Модуль |
-a |
Аргументи модуля |
--become |
Виконати з sudo |
-u |
Користувач SSH |
-k |
Запитати пароль SSH |
-K |
Запитати sudo пароль |
-v/-vv/-vvv |
Verbose output |
Playbooks¶
Playbook — файл з описом бажаного стану серверів.
Структура¶
# playbook.yml
---
- name: Configure web servers
hosts: webservers
become: yes # sudo
vars:
http_port: 80
app_name: myapp
tasks:
- name: Install nginx
apt:
name: nginx
state: present
update_cache: yes
- name: Copy configuration
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: Restart nginx
- name: Ensure nginx is running
service:
name: nginx
state: started
enabled: yes
handlers:
- name: Restart nginx
service:
name: nginx
state: restarted
Запуск¶
# Запустити playbook
ansible-playbook -i inventory.ini playbook.yml
# Dry run (перевірка без змін)
ansible-playbook playbook.yml --check
# Тільки певні теги
ansible-playbook playbook.yml --tags "nginx"
# Обмежити хости
ansible-playbook playbook.yml --limit "web1.example.com"
# Verbose
ansible-playbook playbook.yml -v
Основні модулі¶
Пакети¶
# apt (Debian/Ubuntu)
- name: Install packages
apt:
name:
- nginx
- python3
- git
state: present
update_cache: yes
# yum/dnf (RHEL/CentOS)
- name: Install packages
yum:
name: nginx
state: present
# pip
- name: Install Python packages
pip:
name: flask
virtualenv: /opt/myapp/venv
Файли¶
# Копіювання
- name: Copy file
copy:
src: files/config.conf
dest: /etc/app/config.conf
owner: root
group: root
mode: '0644'
# Шаблон (Jinja2)
- name: Deploy from template
template:
src: templates/nginx.conf.j2
dest: /etc/nginx/nginx.conf
# Створити директорію
- name: Create directory
file:
path: /opt/myapp
state: directory
owner: deploy
mode: '0755'
# Лінк
- name: Create symlink
file:
src: /opt/myapp/current
dest: /var/www/myapp
state: link
Сервіси¶
# systemd сервіс
- name: Start and enable service
service:
name: nginx
state: started
enabled: yes
# Перезапустити
- name: Restart service
service:
name: nginx
state: restarted
Команди¶
# Shell команда
- name: Run command
shell: |
cd /opt/myapp
./deploy.sh
# Command (безпечніше, без shell)
- name: Run command
command: /opt/myapp/deploy.sh
args:
chdir: /opt/myapp
creates: /opt/myapp/.deployed # Не виконувати якщо існує
Користувачі¶
- name: Create user
user:
name: deploy
groups: sudo,docker
shell: /bin/bash
create_home: yes
- name: Add SSH key
authorized_key:
user: deploy
key: "{{ lookup('file', '~/.ssh/id_ed25519.pub') }}"
Variables¶
Де визначати¶
# 1. В playbook
vars:
app_port: 8080
# 2. В окремому файлі
vars_files:
- vars/production.yml
# 3. В inventory
[webservers:vars]
app_port=8080
# 4. В group_vars/
# group_vars/webservers.yml
app_port: 8080
# 5. В host_vars/
# host_vars/web1.example.com.yml
app_port: 8081
# 6. З командного рядка
ansible-playbook playbook.yml -e "app_port=8080"
Пріоритет (від низького до високого)¶
1. Defaults в role
2. Inventory group_vars
3. Inventory host_vars
4. Playbook group_vars
5. Playbook host_vars
6. Playbook vars
7. Playbook vars_files
8. Task vars
9. Extra vars (-e)
Facts (автоматичні змінні)¶
# Ansible збирає інформацію про хости
- name: Show OS
debug:
msg: "OS: {{ ansible_distribution }} {{ ansible_distribution_version }}"
# ansible_hostname — ім'я хоста
# ansible_default_ipv4.address — IP адреса
# ansible_memtotal_mb — RAM
# ansible_processor_cores — CPU cores
Templates (Jinja2)¶
{# templates/nginx.conf.j2 #}
server {
listen {{ http_port }};
server_name {{ ansible_hostname }};
root /var/www/{{ app_name }};
{% if ssl_enabled %}
listen 443 ssl;
ssl_certificate /etc/ssl/{{ domain }}.crt;
ssl_certificate_key /etc/ssl/{{ domain }}.key;
{% endif %}
{% for backend in backends %}
upstream backend_{{ loop.index }} {
server {{ backend.host }}:{{ backend.port }};
}
{% endfor %}
}
Handlers¶
Handlers виконуються тільки якщо task повідомив про зміну.
tasks:
- name: Copy nginx config
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: Restart nginx # Викличе handler якщо файл змінився
handlers:
- name: Restart nginx
service:
name: nginx
state: restarted
Умови та цикли¶
When (умови)¶
- name: Install on Debian
apt:
name: nginx
when: ansible_os_family == "Debian"
- name: Install on RedHat
yum:
name: nginx
when: ansible_os_family == "RedHat"
# Складні умови
- name: Task
command: /bin/true
when:
- ansible_distribution == "Ubuntu"
- ansible_distribution_version is version('20.04', '>=')
Loop (цикли)¶
- name: Create users
user:
name: "{{ item.name }}"
groups: "{{ item.groups }}"
loop:
- { name: alice, groups: sudo }
- { name: bob, groups: docker }
# Або з with_items (старий синтаксис)
- name: Install packages
apt:
name: "{{ item }}"
with_items:
- nginx
- git
- curl
Roles¶
Roles — спосіб організації коду для повторного використання.
Структура¶
roles/
└── nginx/
├── tasks/
│ └── main.yml # Основні tasks
├── handlers/
│ └── main.yml # Handlers
├── templates/
│ └── nginx.conf.j2 # Шаблони
├── files/
│ └── index.html # Статичні файли
├── vars/
│ └── main.yml # Змінні
├── defaults/
│ └── main.yml # Дефолтні змінні
└── meta/
└── main.yml # Залежності
Використання¶
# playbook.yml
- hosts: webservers
roles:
- nginx
- { role: app, app_port: 8080 }
- role: database
vars:
db_name: production
Ansible Galaxy¶
# Встановити role з Galaxy
ansible-galaxy install geerlingguy.docker
# Використати
roles:
- geerlingguy.docker
Ansible Vault¶
Зберігання секретів у зашифрованому вигляді.
# Створити зашифрований файл
ansible-vault create secrets.yml
# Редагувати
ansible-vault edit secrets.yml
# Переглянути
ansible-vault view secrets.yml
# Зашифрувати існуючий файл
ansible-vault encrypt vars.yml
# Розшифрувати
ansible-vault decrypt vars.yml
# Запустити playbook з vault
ansible-playbook playbook.yml --ask-vault-pass
# Або з файлом паролю
ansible-playbook playbook.yml --vault-password-file ~/.vault_pass
Використання¶
# secrets.yml (зашифрований)
db_password: supersecret
api_key: abc123
# playbook.yml
vars_files:
- secrets.yml
tasks:
- name: Configure app
template:
src: config.j2
dest: /etc/app/config
# {{ db_password }} буде підставлено
Практичний приклад: налаштування VPS¶
# setup-vps.yml
---
- name: Initial VPS setup
hosts: newservers
become: yes
vars:
deploy_user: deploy
ssh_port: 22
packages:
- ufw
- fail2ban
- htop
- curl
- git
tasks:
- name: Update apt cache
apt:
update_cache: yes
cache_valid_time: 3600
- name: Install essential packages
apt:
name: "{{ packages }}"
state: present
- name: Create deploy user
user:
name: "{{ deploy_user }}"
groups: sudo
shell: /bin/bash
create_home: yes
- name: Add SSH key for deploy user
authorized_key:
user: "{{ deploy_user }}"
key: "{{ lookup('file', '~/.ssh/id_ed25519.pub') }}"
- name: Configure SSH
lineinfile:
path: /etc/ssh/sshd_config
regexp: "{{ item.regexp }}"
line: "{{ item.line }}"
loop:
- { regexp: '^#?PermitRootLogin', line: 'PermitRootLogin no' }
- { regexp: '^#?PasswordAuthentication', line: 'PasswordAuthentication no' }
notify: Restart SSH
- name: Configure UFW defaults
ufw:
direction: "{{ item.direction }}"
policy: "{{ item.policy }}"
loop:
- { direction: incoming, policy: deny }
- { direction: outgoing, policy: allow }
- name: Allow SSH through UFW
ufw:
rule: allow
port: "{{ ssh_port }}"
proto: tcp
- name: Enable UFW
ufw:
state: enabled
- name: Enable fail2ban
service:
name: fail2ban
state: started
enabled: yes
handlers:
- name: Restart SSH
service:
name: sshd
state: restarted
Приклад: Deploy Docker Compose¶
# deploy-app.yml
---
- name: Deploy application
hosts: appservers
become: yes
vars:
app_dir: /opt/myapp
compose_file: docker-compose.yml
tasks:
- name: Install Docker
include_role:
name: geerlingguy.docker
- name: Create app directory
file:
path: "{{ app_dir }}"
state: directory
owner: "{{ ansible_user }}"
- name: Copy docker-compose.yml
template:
src: "{{ compose_file }}.j2"
dest: "{{ app_dir }}/{{ compose_file }}"
- name: Copy .env file
template:
src: env.j2
dest: "{{ app_dir }}/.env"
mode: '0600'
- name: Pull images
community.docker.docker_compose:
project_src: "{{ app_dir }}"
pull: yes
state: present
- name: Start application
community.docker.docker_compose:
project_src: "{{ app_dir }}"
state: present
restarted: yes
MikroTik з Ansible¶
# mikrotik.yml
---
- name: Configure MikroTik
hosts: mikrotik_routers
gather_facts: no
connection: ansible.netcommon.network_cli
vars:
ansible_network_os: community.routeros.routeros
tasks:
- name: Set system identity
community.routeros.command:
commands:
- /system identity set name=Router-{{ inventory_hostname }}
- name: Configure NTP
community.routeros.command:
commands:
- /system ntp client set enabled=yes servers=pool.ntp.org
- name: Backup configuration
community.routeros.command:
commands:
- /export file=backup-{{ ansible_date_time.date }}
Типові помилки¶
Permission denied¶
# Забули --become
ansible-playbook playbook.yml --become
# Або в playbook:
become: yes
Host unreachable¶
# Перевірити SSH
ssh user@server
# Перевірити inventory
ansible-inventory -i inventory.ini --list
# Debug
ansible all -m ping -vvv
Module not found¶
# Встановити collection
ansible-galaxy collection install community.docker
ansible-galaxy collection install community.routeros
Див. також¶
- SSH ключі — налаштування SSH доступу
- Linux основи — базові команди
- Docker — контейнеризація
- MikroTik — мережеве обладнання
Шлях: infrastructure/ansible-basics.md