> Привет
> Я — студент, изучаю Ansible на реальных одноплатниках через Tailscale. Делюсь полным путем от первой ошибки до работающих веб-серверов. Код + выводы + уроки. Репозиторий на GitHub.


Оглавление

  1. День 1: Подготовка лаборатории

  2. День 2: Первый Nginx (простой плейбук)

  3. День 3: Nginx с Jinja2

  4. День 4: Ansible.cfg


День 1: Подготовка лаборатории

Инфраструктура

Я решил работать с реальным оборудованием:

  • Основная машина мой ноут(Ubuntu с Ansible)

  • pi1: Raspberry Pi

  • pi2: Orange Pi Lite

На каждое устройство установлен Tailscale — это VPN, который создает приватную сеть со статическими IP для всех хостов.

inventory.ini — сердце Ansible

Файл inventory.ini определяет, с какими хостами работает Ansible. Вот мой:

[mal]
malina ansible_host=100.64.0.XXX ansible_user=root ansible_python_interpreter=/usr/bin/python3
[ap]
orange ansible_host=100.64.0.XXX ansible_user=root ansible_python_interpreter=/usr/bin/python3
[servers:children]
mal
ap

Пояснение строк:

  • [mal] — группа для Raspberry Pi

  • malina — имя хоста в Ansible (произвольное, удобное для работы)

  • ansible_host=100.94.xx.xx — реальный IP в Tailscale-сети

  • ansible_user=root — подключаться под пользователем root

  • ansible_python_interpreter=/usr/bin/python3 — путь к Python на целевом хосте

  • [servers:children] — мета-группа, объединяющая mal и ap

Проверка:

ansible all -i inventory.ini --list-hosts

Вывод:

hosts (2):  100.94.xx 100.74.xx

Отлично! Ansible видит оба хоста.

Первая проблема: SSH-доступ

Попытался пингануть хосты:

ansible servers -i inventory.ini -m ping

Ошибка:

"msg": "Failed to connect to the host via ssh"

Решение: Настройка SSH на Pi

На каждой из Pi (через dwservice терминал) отредактировал SSH-конфигурацию:

sudo nano /etc/ssh/sshd_config

Расскоментировал три строки:

PasswordAuthentication yes      # Разрешить пароли
PubkeyAuthentication yes        # Разрешить ключи
PermitRootLogin yes             # Разрешить root-доступ

Перезапустил SSH:

sudo systemctl restart ssh

На основной машине добавил SSH-ключи:

ssh-copy-id root@100.94.xx
ssh-copy-id root@100.74.xx

Финальный тест:

ansible servers -i inventory.ini -m ping

Вывод:

pi1 | SUCCESS => {    "changed": false,    "ping": "pong"
}
pi2 | SUCCESS => {    "changed": false,    "ping": "pong"
}

SSH работает!


День 2: Первый Nginx (простой плейбук)

Решил установить Nginx на orange (Orange Pi Lite) с помощью простого плейбука.

Файл nginx-server-pi2.yml

---
- name: Install Nginx on pi2  
hosts: ap  
become: yes  
tasks:    
- name: Update apt package cache      
apt:        
update_cache: yes    
- name: Install Nginx package
apt:        
name: nginx        
state: latest    
- name: Start and enable Nginx service      
systemd:        
name: nginx        
state: started        
enabled: yes    
- name: Copy custom index.html      
copy:        
content: "<h1>Hello from Ansible! Host: {{ inventory_hostname }}</h1>"        
dest: /var/www/html/index.html        
mode: '0644'      
notify: Restart Nginx  
handlers:    
- name: Restart Nginx      
systemd:        
name: nginx        
state: restarted

Разберемся с tasks

1. Update apt package cache

- name: Update apt package cache  apt:    update_cache: yes

Обновляет кэш пакетов. Эквивалент apt update на машине.

2. Install Nginx package

- name: Install Nginx package  
apt:    
name: nginx    
state: latest

Устанавливает Nginx в последней версии. Если уже установлен — обновляет.

3. Start and enable Nginx service

- name: Start and enable Nginx service  
systemd:    
name: nginx    
state: started    
enabled: yes
  • state: started — запускает Nginx сейчас

  • enabled: yes — добавляет в автозапуск (systemd enable)

4. Copy custom index.html

- name: Copy custom index.html  
copy:    
content: "<h1>Hello from Ansible! Host: {{ inventory_hostname }}</h1>"    
dest: /var/www/html/index.html    
mode: '0644'  
notify: Restart Nginx
  • Создает файл /var/www/html/index.html с HTML

  • {{ inventory_hostname }} — переменная Ansible (подставит orange)

  • notify: Restart Nginx — вызовет handler только если файл изменился

Handler: Restart Nginx

handlers:  
- name: Restart Nginx    
systemd:      
name: nginx      
state: restarted

Перезапускает Nginx только после изменений (не каждый раз).

Запуск плейбука

ansible-playbook -i inventory.ini nginx-server-pi2.yml

Вывод:

PLAY [Install Nginx on pi2] ****
TASK [Gathering Facts] ****
pi2 | SUCCESS
TASK [Update apt package cache] ****
pi2 | CHANGED
TASK [Install Nginx package] ****
pi2 | CHANGED
TASK [Start and enable Nginx service] ****
pi2 | CHANGED
TASK [Copy custom index.html] ****
pi2 | CHANGED
RUNNING HANDLER [Restart Nginx] ****
pi2 | CHANGED
PLAY RECAP ****
pi2 : ok=6 changed=5 unreachable=0 failed=0

Результат

Открыл браузер и вбил адрес: http://100.74.xx.xx

На экране:

Hello from Ansible! Host: orange

День 3: Nginx с Jinja2

Понял, что нужно попробовать разные конфигурации для каждой Pi, переменные, шаблоны. Решил использовать Jinja2 — систему шаблонов Ansible.

Структура проекта

mkdir -p ~/malina_ansible/{templates,group_vars,host_vars}
cd ~/malina_ansible

Итоговая стру��тура:

~/malina_ansible/
├── ansible.cfg
├── inventory.ini
├── group_vars/
│   └── servers.yml
├── host_vars/
│   ├── pi1.yml
│   └── pi2.yml
├── templates/
│   ├── nginx-site.j2
│   └── index.html.j2
└── nginx-advanced.yml

Переменные конфигурации

group_vars/servers.yml (общие для всех)

nano group_vars/servers.yml
---
# Общие настройки для всех серверов
nginx_user: www-data
nginx_worker_processes: auto
nginx_worker_connections: 1024

Описание:

  • nginx_user — пользователь, под которым работает Nginx

  • nginx_worker_processes — кол-во worker-процессов (auto = по количеству CPU)

  • nginx_worker_connections — макс. соединений на worker

host_vars/pi1.yml (только для Raspberry Pi)

nano host_vars/pi1.yml
---
# Настройки только для malina
nginx_port: 80
nginx_root: /var/www/pi1
nginx_server_name: pi1.local
nginx_index: index.html index.htm

host_vars/pi2.yml (только для Orange Pi Lite)

nano host_vars/pi2.yml
---
# Настройки только для orange
nginx_port: 8080
nginx_root: /var/www/pi2
nginx_server_name: pi2.local
nginx_index: index.html index.htm

Разница: malina на 80-м порту, orange на 8080-м. Каждая система имеет свою корневую папку.

Jinja2 шаблоны

templates/nginx-site.j2

nano templates/nginx-site.j2
server {    listen {{ nginx_port }};    
    server_name {{ nginx_server_name }};    
    root {{ nginx_root }};    
    index {{ nginx_index }};    
    location / 
    {        
    try_files $uri $uri/ =404;    }    
  location ~ \.php$ {        
    return 404;    }    
  access_log /var/log/nginx/{{ nginx_server_name }}-access.log;    
  error_log /var/log/nginx/{{ nginx_server_name }}-error.log;
}

Что происходит:

  • {{ nginx_port }} — подставляется значение из host_vars (80 или 8080)

  • {{ nginx_root }} — подставляется своя папка для каждой Pi

  • {{ nginx_server_name }} — уникальное имя для каждого хоста

  • Для pi1: получится конфиг на порту 80 с /var/www/pi1

  • Для pi2: получится конфиг на порту 8080 с /var/www/pi2

templates/index.html.j2

nano templates/index.html.j2
    <title>{{ inventory_hostname }} - Ansible Nginx</title>    
<style>        
body {             
font-family: Arial;             
text-align: center;             
margin-top: 100px;             
background: #f0f8ff;         
}        
h1 
{ color: #2c3e50;
}        
.info 
{             
margin: 20px;             
padding: 20px;             
background: white;             
border-radius: 10px;             
display: inline-block;         
}    
</style>    
<div class="info">       
<h1>? Hello from {{ inventory_hostname }}!</h1>        
<p><strong>Port:</strong> {{ nginx_port }}</p>        
<p><strong>Root:</strong> {{ nginx_root }}</p>        
<p><strong>OS:</strong> {{ ansible_distribution }} 
{{ ansible_distribution_version }}</p>        
<p><strong>Uptime:</strong> {{ ansible_uptime_seconds }} seconds</p>    
</div>

Динам��ческие переменные:

  • {{ inventory_hostname }} — имя хоста из inventory (pi1, pi2)

  • {{ ansible_distribution }} — ОС (Ubuntu, Debian и т.д.)

  • {{ ansible_uptime_seconds }} — время работы системы в секундах

? Главный плейбук nginx-advanced.yml

nano nginx-with-jinja.yml
---
- name: Deploy Advanced Nginx with Jinja2
  hosts: ap
  become: yes
  vars:
    nginx_config_dir: /etc/nginx/sites-available

  tasks:

    - name: Update apt cache
      apt:
        update_cache: yes

    - name: Install nginx
      apt:
        name: nginx
        state: latest

    - name: Create web roots
      file:
        path: "{{ nginx_root }}"
        state: directory
        mode: '0755'
        owner: "{{ nginx_user }}"
        group: "{{ nginx_user }}"

    - name: Deploy custom index.html
      template:
        src: index.html.j2
        dest: "{{ nginx_root }}/index.html"
        mode: '0644'
        owner: "{{ nginx_user }}"
        group: "{{ nginx_user }}"
      notify:
        - Test nginx config
        - Restart nginx

    - name: Deploy nginx site config from template
      template:
        src: nginx-site.j2
        dest: "{{ nginx_config_dir }}/{{ inventory_hostname }}"
        mode: '0644'
      notify:
        - Test nginx config
        - Restart nginx

    - name: Enable site (symlink)
      file:
        src: "{{ nginx_config_dir }}/{{ inventory_hostname }}"
        dest: /etc/nginx/sites-enabled/{{ inventory_hostname }}
        state: link
      notify:
        - Test nginx config
        - Restart nginx

    - name: Disable default site
      file:
        path: /etc/nginx/sites-enabled/default
        state: absent
      notify:
        - Test nginx config
        - Restart nginx

    - name: Start and enable nginx
      systemd:
        name: nginx
        state: started
        enabled: yes

  handlers:
    - name: Test nginx config
      systemd:
        name: nginx
        state: started
      check_mode: yes

    - name: Restart nginx
      systemd:
        name: nginx
        state: restarted

? Разберемся с tasks

Task

Описание

Clean previous repos

Очищает проблемные репозитории APT

Update apt cache

Обновляет список пакето��

Install nginx

Устанавливает Nginx

Create web roots

Создает папки /var/www/pi1 и /var/www/pi2 с правами

Deploy custom index.html

Генерирует HTML из шаблона с динамическими данными

Deploy nginx site config

Создает конфиги /etc/nginx/sites-available/pi1 и pi2

Enable site (symlink)

Создает симлинк в sites-enabled/ для активации

Disable default site

Удаляет дефолтный сайт Nginx

Start and enable nginx

Запускает и включает автозапуск

Handlers:

  • Test nginx config — проверяет синтаксис nginx -t

  • Restart nginx — перезапускает (срабатывает только при изменениях)

Запуск плейбука

ansible-playbook -i inventory.ini nginx-advanced.yml

Вывод:

PLAY [Deploy Advanced Nginx with Jinja2] ****
TASK [Gathering Facts] ****
pi1 | SUCCESS
pi2 | SUCCESS
TASK [Clean previous repos] ****
pi1 | CHANGED
pi2 | CHANGED
... (остальные tasks)
PLAY RECAP ****
pi1 : ok=11 changed=10 unreachable=0 failed=0
pi2 : ok=11 changed=10 unreachable=0 failed=0

Результат в браузере

Адрес

Результат

http://100.94.xx

? Hello from pi1! Port: 80

http://100.74.xx

? Hello from pi2! Port: 8080

Каждая Pi имеет свою конфигурацию, свой порт, свою корневую папку. Всё автоматизировано!


День 4: Диагностика и ansible.cfg

Проблема: Спящий режим Raspberry Pi

На следующий день проверил доступность:

ansible servers -m ping -i inventory.ini

Результат:

pi2 | SUCCESS =&gt; {"changed": false, "ping": "pong"}
pi1 | FAILED - Host is not reachable

Причина: Raspberry Pi переходит в спящий режим, Orange Pi Lite остается активной (из-за работающего Nginx).

Решение: Подключился к malina через SSH в dwservice — "разбудил" систему, и она ответила.

Вторая проблема: Забытый ansible.cfg

Попытался управлять Nginx без -i inventory.ini:

ansible servers -m systemd -a "name=nginx"

Ошибка:

[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available
[WARNING]: Could not match supplied host pattern, ignoring: servers

Причина: Ansible не нашел файл inventory.ini — искал его в дефолтных местах, не нашел и использовал только localhost.

Решение: Создание ansible.cfg

Создал конфигурационный файл в корне проекта:

cat &gt; ansible.cfg &lt;&lt; 'EOF'
[defaults]
inventory = inventory.ini
host_key_checking = False
[privilege_escalation]
become = True
EOF

Параметры ansible.cfg

Параметр

Значение

Описание

inventory

inventory.ini

Путь к inventory (автоматический, без -i флага)

host_key_checking

False

Отключить проверку SSH host keys (удобно для lab/тестирования)

become

True

Автоматически использовать sudo (становиться root)

Результат после ansible.cfg

Было:

ansible servers -i inventory.ini -m ping
ansible-playbook -i inventory.ini nginx-advanced.yml
ansible servers -i inventory.ini -m systemd -a "name=nginx state=stopped"

Стало:

ansible servers -m ping
ansible-playbook nginx-advanced.yml
ansible servers -m systemd -a "name=nginx state=stopped"

Намного удобнее! Теперь не нужно писать флаг -i inventory.ini в каждой команде.

Финальные проверки

# Список хостов
ansible servers --list-hosts
# Пинг обеих машин
ansible servers -m ping
# Статус Nginx
ansible servers -m systemd -a "name=nginx"
# Тест конфигурации
ansible servers -m shell -a "nginx -t"
# Проверка сайтов
curl http://100.94.38.85           # pi1: порт 80
curl http://100.74.146.7:8080      # pi2: порт 8080

Итоговая архитектура

┌────────────────────────────────────────────────��────┐
│           Основная машина (Ubuntu)                  │
│  ├─ Ansible                                         │
│  ├─ inventory.ini                                   │
│  └─ ansible.cfg                                     │
└────────┬──────────────────────────────┬─────────────┘
         │ Tailscale VPN                │
         │                               │
    ┌────▼────┐                    ┌────▼────┐
    │   pi1   │                    │   pi2   │
    │ (RPi)   │                    │ (OPi)   │
    │         │                    │         │
    │ Nginx   │                    │ Nginx   │
    │ Port 80 │                    │ Port 80→8080
    │         │                    │         │
    │ Index   │                    │ Index   │
    │ HTML    │                    │ HTML    │
    └─────────┘                    └─────────┘

Репозиторий

Весь код доступен на GitHub:

git clone https://github.com/doub1ess/ansible-lab
cd ansible-lab
ansible-playbook nginx-with-jinja.yml

Если у вас есть замечания, вопросы или советы по Ansible — пишите в комментариях. Буду благодарен за фидбек.

Комментарии (0)