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

Див. також

Шлях: infrastructure/ansible-basics.md

UMTC Wiki © 2026 | Ukrainian Military Tactical Communications