На главную

DevOps и серверы

Плейбук Ansible

Плейбук Ansible для настройки сервера с нуля: базовые пакеты, пользователь с ключом и запретом входа root, Docker, nginx с сайтом, приложение как systemd-юнит, файрвол и сертификат — вместе с инвентарём и ansible.cfg.

Что настраиваем

Пусто — возьмём ~/.ssh/id_ed25519.pub при запуске

playbook.yml

---
- name: Настройка app
  hosts: web
  become: true
  vars:
    app_name: app
    app_port: 3000
    deploy_user: deploy

  tasks:
    - name: Обновить список пакетов
      ansible.builtin.apt:
        update_cache: true
        cache_valid_time: 3600

    - name: Поставить базовые пакеты
      ansible.builtin.apt:
        name:
          - curl
          - git
          - htop
          - unzip
          - ca-certificates
        state: present

    - name: Часовой пояс
      community.general.timezone:
        name: Europe/Moscow

    - name: Автоматические обновления безопасности
      ansible.builtin.apt:
        name: unattended-upgrades
        state: present

    - name: Пользователь deploy
      ansible.builtin.user:
        name: deploy
        groups: sudo
        shell: /bin/bash
        append: true

    - name: Ключ для deploy
      ansible.posix.authorized_key:
        user: deploy
        key: "{{ lookup('file', '~/.ssh/id_ed25519.pub') }}"

    - name: sudo без пароля
      ansible.builtin.copy:
        dest: /etc/sudoers.d/deploy
        content: "deploy ALL=(ALL) NOPASSWD:ALL\n"
        mode: "0440"
        validate: visudo -cf %s

    - name: Запретить вход root по SSH и пароли
      ansible.builtin.lineinfile:
        path: /etc/ssh/sshd_config
        regexp: "{{ item.re }}"
        line: "{{ item.line }}"
        validate: sshd -t -f %s
      loop:
        - { re: '^#?PermitRootLogin', line: 'PermitRootLogin no' }
        - { re: '^#?PasswordAuthentication', line: 'PasswordAuthentication no' }
      notify: Перезапустить sshd

    - name: nginx
      ansible.builtin.apt:
        name: nginx
        state: present

    - name: Конфиг сайта
      ansible.builtin.copy:
        dest: /etc/nginx/sites-available/example.com
        mode: "0644"
        content: |
          server {
              listen 80;
              server_name example.com;
              location / {
                  proxy_pass http://127.0.0.1:3000;
                  proxy_set_header Host $host;
                  proxy_set_header X-Real-IP $remote_addr;
                  proxy_set_header X-Forwarded-Proto $scheme;
              }
          }
      notify: Перезапустить nginx

    - name: Включить сайт
      ansible.builtin.file:
        src: /etc/nginx/sites-available/example.com
        dest: /etc/nginx/sites-enabled/example.com
        state: link
      notify: Перезапустить nginx

    - name: ufw
      ansible.builtin.apt:
        name: ufw
        state: present

    - name: Разрешить SSH, HTTP и HTTPS
      community.general.ufw:
        rule: allow
        port: "{{ item }}"
        proto: tcp
      loop: ["22", "80", "443"]

    - name: Включить файрвол
      community.general.ufw:
        state: enabled
        policy: deny

  handlers:
    - name: Перезапустить sshd
      ansible.builtin.systemd:
        name: ssh
        state: restarted

    - name: Перезапустить nginx
      ansible.builtin.systemd:
        name: nginx
        state: reloaded

inventory.ini

[web]
server1 ansible_host=203.0.113.10 ansible_user=root

[web:vars]
ansible_python_interpreter=/usr/bin/python3

ansible.cfg

[defaults]
inventory = inventory.ini
host_key_checking = True
retry_files_enabled = False
stdout_callback = yaml

[ssh_connection]
pipelining = True

Как запускать

ansible-galaxy collection install community.general ansible.posix
ansible-playbook -i inventory.ini playbook.yml --check --diff
# убедились, что меняется только нужное — и запускаем без --check
ansible-playbook -i inventory.ini playbook.yml