Пишем ansible playbook для начальной настройки Linux сервера

Пишем ansible playbook для начальной настройки Linux сервера

Приветствую!

Сегодня про автоматизацию начальной настройки Linux сервера посредством Ansible🎺. От установки locales, timezone, параметров SSH сервера… до создания и настройки окружения нового пользователя👨‍💻: Oh-My-Zsh, Neovim и Tmux. Будет интересно😉.

Подписывайтесь на наш телеграм @r4ven_me📱, чтобы не пропустить новые публикации на сайте😉. А если есть вопросы или желание пообщаться по тематике – заглядывайте в Вороний чат @r4ven_me_chat🧐.

Для примеров из статьи в качестве управляющего хоста использовалась система с Linux Mint 22. Ansible playbook разработан под Deb based дистрибутивы и протестирован на Debian 12 и Ubuntu 24.

Предисловие: в чем суть?

Все просто — данный плейбук является автоматизацией моих прошлых статей:

В каждой из статей довольно много подробностей и рутинных действий. Поэтому идея автоматизировать процесс родилась сама собой. В этой заметке я расскажу про мою Ansible role, которая сводит количество выполняемых действий из статей выше — к одной команде. Если конечно соблюдены необходимые условия😉.

Коротко перечислю, что делает playbook:

1) Конфигурация сервера:

  • Замена пароля root пользователя
  • Установка временнОй зоны (timezone)
  • Установка hostname
  • Добавление хостов в файл hosts
  • Настройка локализации
  • Обновление и установка пакетов
  • Конфигурация SWAP (если его нет) в файле
  • Ограничение количества хранимых системных журналов journald
  • Конфигурация SSH сервера
  • Отключение использования IPv6 в системе и в фаерволе UFW
  • Открытие заданного списка сетевых портов в UFW
черт, а я тут ufw пользуюсь..😳

2) Настройка окружения пользователя:

  • Создание нового пользователя с указанием пароля, оболочки и списка групп
  • Добавление публичного SSH ключа авторизации в ~/.ssh
  • Установка пакетов пользователя (отдельный список)
  • Скачивание и применение конфигурации для Zsh, Neovim, Tmux

Если вам не нужно выполнять все перечисленные действия, то плейбук подразумевает выборочную конфигурацию с помощью указания флагов (см. ниже) или через теги задач.

Подготовка

Для запуска плейбука нам понадобятся:

  1. Целевые Linux сервер(ы) с Debian/Ubuntu на борту
  2. Возможностью удаленного подключения к этим серверам по SSH
  3. Установленный и настроенный Ansible на управляющей машине-клиенте

Если у вас еще нет такого набора, то возможно найдете полезными эти мои материалы:

Последнюю статью крайне рекомендую, если вы новичок в Ansible👆.

Если же вам лень читать длинные полотна и вы просто хотите поскорее приступить к автоматизации, то необходимый минимум, это: файлы ansible.cfg и inventory.yml.

Под спойлером короткая инструкция🙃

Открываем терминал и выполняем:

# устанавливаем ansible
sudo apt update && sudo apt install -y ansible

# создаем директории Ansible
mkdir -p ~/ansible/{playbooks,roles}

# создаем конфиг файл
nvim ~/ansible/ansible.cfg
[defaults]
home = ~/ansible/
inventory = ~/ansible/inventory.yml
playbook_dir = ~/ansible/playbooks/
roles_path = ~/ansible/roles/
host_key_checking = False
# создаем файл инвентаризации
nvim ~/ansible/inventory.yml
all:
  hosts:
    debian12:
      ansible_host: 192.168.122.136
      ansible_port: 22
      ansible_user: root
      ansible_ssh_private_key_file: ~/.ssh/id_rsa
      # ansible_password: "Pa$$w0rD"
    ubuntu24:
      ansible_host: 192.168.122.11
      ansible_port: 22
      ansible_user: root
      ansible_ssh_private_key_file: ~/.ssh/id_rsa
      # ansible_become_password: "Pa$$w0rD"

Подставьте свои значения в файл инвентаризации.

Если же у вас все готово, то проверяем наличие модулей сообщества:

ansible-galaxy collection list community.general

При их отсутствии выполните установку:

ansible-galaxy collection install community.general

С подготовкой наконец-то закончили🤯.

Скачивание и разбор роли Ansible

Далее мы скачаем файлы проекта из моего репозитория на GitHub (понадобится утилита git) и скопируем нужную роль в рабочую директорию ansible, в моем случае это ~/ansible:

~ — это alias домашней директории пользователя.

# установка git
sudo apt install -y git

# клонирование репозитория во временную папку
git clone https://github.com/r4ven-me/ansible /tmp/ansible

# копирование роли в рабочую папку
cp -r /tmp/ansible/roles/linux-base-config ~/ansible/roles/

# очистка
rm -rf /tmp/ansible

# переход в рабочую папку
cd ~/ansible/

Состав роли выглядит так:

ls -l --tree --sort=type ./roles/linux-base-config

Понравился вывод команды ls? Можете с легкостью настроить такое у себя по статье: bat, exa – подсветка синтаксиса стандартного вывода в терминале Linux (cat, less, tail и ls).

Ниже приведены спойлеры для каждого файла роли, под которыми указано его содержимое на момент написания статьи с комментариями на русском языке👇.

Если вы не хотите особо вникать во все файлы, то изучите только defaults/main.yml, содержащий переменные, которые необходимо отредактировать в соответствии с вашими предпочтениями.

defaults/main.yml — файл с переменными по умолчанию

---
### Переменные по умолчанию

# --- ФЛАГИ --- #
server_config: false
server_reboot: false
user_config: false

# --- ПОЛЬЗОВАТЕЛЬ --- #
user_name: "ivan"
user_home: "/home/{{ user_name }}"
# use 'mkpasswd --method=sha-512' command to generate password hash (whois pkg)
user_password: "$6$gKi8acqF5AxMpHKK$mSaoEkOH8WMjvMxEH2/azDr08ypzu1OGIUaprYhwsoGFAG69S917bCZ4SRP1I5.iRpk4.Mw6pLHo/mklYPA7g0"
user_groups:
  - "users"
  - "adm"
  - "sudo"
user_shell: "/usr/bin/zsh"
user_ssh_pubkey: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINQdlmVhGAz7z3NrJ6/goVtpeVf+xEjeZfZWfA55whZO ivan@r4ven-me"
user_pkgs:
  - "zsh"
  - "neovim"
  - "tmux"
  - "fzf"
  - "git"
  - "curl"
  - "shellcheck"
  - "pylint"
  - "yamllint"
  - "bat"
  - "{{ 'exa' if ansible_facts['distribution'] == 'Debian' and ansible_facts['distribution_major_version'] <= '12' else 'eza' }}"

# --- СЕРВЕР --- #
server_root_password: "$6$jGYWmPob7wv9CQgB$KZPaEoXI39GPQp4pi08T1e9d6CT9BqNqGixBJ.CXn1ArUQQ1Lnnlr0lD7pFnVw7r3/5syDebNbK7HkSGLTHX8."
server_timezone: "Europe/Moscow"
server_hostname: "{{ inventory_hostname }}"
server_hosts:
  - "127.0.0.1 {{ server_hostname }}"
  - "127.0.1.1 {{ server_hostname }}"
server_locales:
  - "en_US.UTF-8"
  - "ru_RU.UTF-8"
server_swap_size: "2048"
server_journald_params:
  - {key: "SystemMaxUse", value: "800M"}
  - {key: "MaxFileSec", value: "2week"}
server_ssh_port: "2222"
server_ssh_params:
  - {key: "Port", value: "{{ server_ssh_port }}"}
  - {key: "AddressFamily", value: "inet"}
  - {key: "PermitEmptyPasswords", value: "no"}
  - {key: "PermitRootLogin", value: "no"}
  - {key: "PubkeyAuthentication", value: "yes"}
  - {key: "PasswordAuthentication", value: "no"}
  - {key: "AllowUsers", value: "{{ user_name }}"}
server_ufw_ports:
  - "22"
  - "{{ server_ssh_port }}"
  - "443"
  - "80"
  - "53"
server_pkgs:
  - "sudo"
  - "locales"
  - "ufw"
  - "software-properties-common"
  - "ca-certificates"
  - "net-tools"
  - "dnsutils"
  - "mtr-tiny"
  - "nload"
  - "ncat"
  - "wget"
  - "curl"
  - "git"
  - "mc"
  - "neovim"
  - "tmux"

handlers/main.yml — файл задач-обработчиков

---
### Обработчики

- name: Enable swap on startup  # Включает swap при загрузке системы
  ansible.builtin.lineinfile:
    path: "/etc/fstab"  # Файл конфигурации монтирования
    regexp: "^/swap"  # Проверяет наличие строки, начинающейся с /swap
    line: '/swap none swap sw 0 0'  # Добавляет строку для монтирования swap
    state: present  # Убеждается, что строка присутствует в файле

- name: Reload sysctl  # Применяет настройки sysctl
  ansible.builtin.shell: "sysctl -p"
  ignore_errors: true  # Игнорирует ошибки при выполнении

- name: Restart journald service  # Перезапускает systemd-journald
  ansible.builtin.systemd_service:
    name: systemd-journald  # Имя службы
    state: restarted  # Перезапуск службы

- name: Restart ufw service  # Перезапускает UFW (брандмауэр)
  ansible.builtin.systemd_service:
    name: ufw  # Имя службы
    state: restarted  # Перезапуск службы
    enabled: true  # Включает службу при загрузке

- name: Restart sshd service  # Перезапускает SSH-сервер (имя определяется в зависимости от ОС)
  ansible.builtin.systemd_service:
    name: >-
      {%- if ansible_facts['distribution'] == 'Debian' -%}
        sshd
      {%- elif ansible_facts['distribution'] == 'Ubuntu' -%}
        ssh
      {%- else -%}
        ssh
      {%- endif -%}
    daemon_reload: true  # Перечитывает конфиг systemd при изменениях
    state: restarted  # Перезапуск службы

- name: Reboot system  # Перезагружает систему
  ansible.builtin.shell: "sleep 5 && reboot"
  async: 1  # Запускает команду асинхронно
  poll: 0  # Не ожидает завершения команды

- name: Delete ufw rule for default ssh port  # Удаляет правило UFW для порта 22
  community.general.ufw:
    rule: allow  # Тип правила (разрешить)
    port: 22  # Порт, который удаляется
    proto: tcp  # Протокол (TCP)
    delete: true  # Удаление правила
  when: server_config == true and ansible_facts['user_id'] == "root" # Условия выполнения

- name: Wait for online  # Ожидает, пока сервер станет доступным
  ansible.builtin.wait_for_connection:
    connect_timeout: 15  # Тайм-аут на подключение
    sleep: 5  # Пауза перед следующей попыткой
    delay: 40  # Задержка перед началом ожидания
    timeout: 300  # Общее время ожидания
  become: false # Выполнение от непривилегированного пользователя
  vars:
    ansible_port: "{{ server_ssh_port }}" # Обновление значения порта подключения
    ansible_user: "{{ user_name }}" # Обновление значения пользователя подключения

meta/main.yml — метаданные о роли

---
### Метаинформация о роли

galaxy_info:
  author: Ivan Cherniy  # Автор роли
  description: Basic configuration for linux server  # Описание роли
  company: r4ven.me  # Название компании или проекта

  license: GPL-2.0  # Лицензия, по которой распространяется роль

  min_ansible_version: 2.1  # Минимальная версия Ansible для работы роли

  platforms:  # Поддерживаемые платформы
    - name: Debian
      versions:
        - 12  # Поддерживаемая версия Debian
    - name: Ubuntu
      versions:
        - 24  # Поддерживаемая версия Ubuntu

  galaxy_tags: []  # Теги для поиска в Ansible Galaxy (не заданы)

dependencies: []  # Зависимости (если есть другие роли, которые требуется установить)

tasks/main.yml — основной файл задач

---
### Файл задач для базовой настройки сервера

- name: Check flags  # Проверяет, установлен ли хотя бы один из флагов
  ansible.builtin.debug:
    msg: "At least one of the flags should be set to true:
      server_config | user_config | server_reboot"  # Выводит сообщение, если флаги не установлены
  changed_when: true  # Помечает задачу как изменённую (для корректного триггера обработчиков)
  when: (server_config and user_config and server_reboot) != true  # Проверяет, что хотя бы один флаг установлен
  tags:
    - check_flags  # Метка для запуска этой задачи отдельно

- import_tasks: server_config.yml  # Импорт задач настройки сервера
  when: server_config == true and ansible_facts['user_id'] == "root" # Выполняется, если server_config установлен в true и пользователь root
  tags:
    - server_config  # Метка для удобного запуска этой части задач

- import_tasks: user_config.yml  # Импорт задач настройки пользователя
  when: user_config == true  # Выполняется, если user_config установлен в true
  tags:
    - user_config  # Метка для запуска этой части задач

- name: Init reboot handlers  # Инициализирует обработчики для перезагрузки сервера
  ansible.builtin.debug:
    msg: "Running handlers..."  # Выводит сообщение о запуске обработчиков
  notify:  # Запускает обработчики при изменениях
    - Reboot system  # Перезагружает систему
    - Delete ufw rule for default ssh port  # Удаляет правило UFW для стандартного порта SSH
    - Wait for online  # Ждёт, пока сервер снова станет доступным
  when: server_reboot == true  # Выполняется, если server_reboot установлен в true
  changed_when: true  # Помечает задачу как изменённую
  tags:
    - reboot  # Метка для запуска этой части задач

tasks/server_config.yml — файл задач по настройке сервера

---
### Конфигурация сервера

- name: Change root password  # Устанавливает пароль для root
  ansible.builtin.user:
    name: "root"  # Имя пользователя
    password: "{{ server_root_password }}"  # Новый пароль
  tags:
    - root  # Метка для фильтрации задач

- name: Set timezone  # Устанавливает часовой пояс
  community.general.timezone:
    name: "{{ server_timezone }}"  # Часовой пояс из переменной
  tags:
    - timezone

- name: Change hostname  # Изменяет имя хоста
  ansible.builtin.lineinfile:
    path: "/etc/hostname"  # Файл с именем хоста
    regexp: "^.*$"  # Заменяет всю строку
    line: "{{ server_hostname }}"  # Новое имя хоста
  tags:
    - hostname

- name: Set hosts  # Добавляет записи в /etc/hosts
  ansible.builtin.lineinfile:
    path: "/etc/hosts"  # Файл hosts
    line: "{{ item }}"  # Строка с записью
    state: present  # Убеждается, что строка присутствует
  loop: "{{ server_hosts }}"  # Перебирает список хостов
  tags:
    - hosts

- name: Config locales  # Устанавливает локали
  community.general.locale_gen:
    name: "{{ item }}"  # Название локали
    state: present  # Убеждается, что локаль установлена
  loop: "{{ server_locales }}"  # Перебирает список локалей
  tags:
    - locales

- name: Update system and install pkgs  # Обновляет систему и устанавливает пакеты
  block:
    - name: Cache update and system upgrade  # Обновляет кэш пакетов и выполняет обновление
      ansible.builtin.apt:
        upgrade: true  # Выполняет обновление системы
        update_cache: true  # Обновляет кэш пакетов

    - name: Install pkgs  # Устанавливает необходимые пакеты
      ansible.builtin.apt:
        name: "{{ server_pkgs }}"  # Список пакетов
        state: present  # Убеждается, что пакеты установлены
  when: ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu'  # Выполняется только для Debian и Ubuntu
  tags:
    - pkgs

- name: Config SWAP  # Настраивает файл подкачки
  block:
    - name: Checking if swap partition exists  # Проверяет, существует ли swap
      ansible.builtin.shell: "swapon -s | grep -q /"
      register: swap_status  # Сохраняет статус выполнения команды
      ignore_errors: true  # Игнорирует ошибки
      changed_when: false  # Не помечает задачу как изменённую
      failed_when: false  # Не считает ошибку критической

    - name: Make swap file  # Создаёт файл подкачки
      ansible.builtin.command: "{{ item }}"
      loop:
        - "dd if=/dev/zero of=/swap bs=1M count={{ server_swap_size }}"  # Создаёт файл
        - "chmod 600 /swap"  # Устанавливает права доступа
        - "mkswap /swap"  # Форматирует файл подкачки
        - "swapon /swap"  # Включает swap
      when: swap_status.rc != 0  # Выполняется, если swap отсутствует
      notify: Enable swap on startup  # Запускает задачу включения swap при загрузке
  tags:
    - swap

- name: Disable ipv6 in system  # Отключает IPv6 в системе
  ansible.builtin.lineinfile:
    path: "/etc/sysctl.conf"  # Файл конфигурации sysctl
    regexp: "^{{ item.key }}"  # Ищет строку по ключу
    line: "{{ item.key }} = {{ item.value }}"  # Добавляет или заменяет строку
    state: present  # Убеждается, что строка присутствует
    create: true  # Создаёт файл, если он отсутствует
  loop:
    - {key: "net.ipv6.conf.all.disable_ipv6", value: "1"}
    - {key: "net.ipv6.conf.default.disable_ipv6", value: "1"}
    - {key: "net.ipv6.conf.lo.disable_ipv6", value: "1"}
  notify: Reload sysctl  # Перезапускает sysctl для применения изменений
  tags:
    - ipv6

- name: Disable ipv6 in UFW  # Отключает IPv6 в UFW (брандмауэре)
  ansible.builtin.lineinfile:
    path: /etc/default/ufw  # Файл конфигурации UFW
    regexp: "^IPV6=yes"  # Ищет строку с включённым IPv6
    line: "IPV6=no"  # Отключает IPv6
  tags:
    - ipv6

- name: Allow ports from list in UFW  # Разрешает список портов в UFW
  community.general.ufw:
    rule: allow  # Разрешает соединения
    port: "{{ item }}"  # Номер порта
    proto: tcp  # Протокол TCP
    state: enabled  # Включает UFW
  loop: "{{ server_ufw_ports }}"  # Перебирает список портов
  notify:
    - Restart ufw service  # Перезапускает UFW
  tags:
    - ufw

- name: Config journald  # Настраивает systemd-journald
  ansible.builtin.lineinfile:
    path: /etc/systemd/journald.conf  # Файл конфигурации
    regexp: "^{{ item.key }}="  # Ищет строку с параметром
    line: "{{ item.key }}={{ item.value }}"  # Добавляет или заменяет параметр
    backup: true  # Создаёт резервную копию файла перед изменением
  loop: "{{ server_journald_params }}"  # Перебирает параметры конфигурации
  notify: Restart journald service  # Перезапускает journald после изменений
  tags:
    - journald

- name: Config SSH daemon  # Настраивает SSH-сервер
  ansible.builtin.lineinfile:
    path: /etc/ssh/sshd_config  # Файл конфигурации SSH
    regexp: "^{{ item.key }}"  # Ищет параметр по ключу
    line: "{{ item.key }} {{ item.value }}"  # Добавляет или изменяет параметр
    validate: "/usr/sbin/sshd -t -f %s"  # Проверяет конфигурацию перед сохранением
    backup: true  # Создаёт резервную копию файла
  loop: "{{ server_ssh_params }}"  # Перебирает параметры SSH
  notify:
    - Restart sshd service  # Перезапускает SSH после изменений
  tags:
    - sshd

tasks/user_config.yml — файл задач по настройке пользователя

---
### Конфигурация пользователя

- name: Create user with shell, groups and password  # Создаёт пользователя с оболочкой, группами и паролем
  ansible.builtin.user:
    name: "{{ user_name }}"  # Имя пользователя
    shell: "{{ user_shell }}"  # Оболочка пользователя (например, /bin/zsh)
    groups: "{{ user_groups }}"  # Список групп, в которые добавляется пользователь
    append: true  # Добавляет пользователя в группы, не заменяя текущие
    password: "{{ user_password }}"  # Устанавливает пароль
  when: ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu'  # Выполняется только для Debian/Ubuntu
  tags:
    - user  # Метка для удобного запуска

- name: Create ssh directory  # Создаёт каталог .ssh для пользователя
  ansible.builtin.file:
    path: "{{ user_home }}/.ssh/"  # Путь к каталогу
    state: directory  # Убеждается, что это директория
    owner: "{{ user_name }}"  # Назначает владельца
    group: "{{ user_name }}"  # Назначает группу
    mode: "0700"  # Устанавливает права доступа (только владелец)
  tags:
    - ssh

- name: Add user ssh public key  # Добавляет SSH-ключ пользователя
  ansible.builtin.lineinfile:
    path: "{{ user_home }}/.ssh/authorized_keys"  # Файл с авторизованными ключами
    line: "{{ user_ssh_pubkey }}"  # Публичный ключ SSH
    create: true  # Создаёт файл, если он отсутствует
    owner: "{{ user_name }}"  # Назначает владельца
    group: "{{ user_name }}"  # Назначает группу
    mode: "0600"  # Устанавливает права (чтение и запись только владельцу)
  tags:
    - ssh

- name: Install user pkgs  # Устанавливает пакеты для пользователя
  ansible.builtin.apt:
    name: "{{ user_pkgs }}"  # Список пакетов для установки
    state: present  # Убеждается, что пакеты установлены
    update_cache: true  # Обновляет кэш перед установкой
  when: ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu'  # Только для Debian/Ubuntu
  tags:
    - user_pkgs

- name: Create dots dir  # Создаёт каталоги для dot-файлов
  ansible.builtin.file:
    path: "{{ item }}"  # Путь к создаваемой директории
    state: directory  # Убеждается, что это директория
    owner: "{{ user_name }}"  # Назначает владельца
    group: "{{ user_name }}"  # Назначает группу
    recurse: true  # Рекурсивно создаёт вложенные каталоги
  loop:
    - "{{ user_home }}/.local/share/nvim/site/autoload/"  # Папка для nvim-плагинов
    - "{{ user_home }}/.config/nvim/"  # Папка для конфигурации Neovim
    - "{{ user_home }}/.config/tmux/"  # Папка для конфигурации tmux
  tags:
    - dots

- name: Download dot files  # Загружает dot-файлы из репозитория
  ansible.builtin.shell:
    cmd: "sudo -u {{ user_name }}
          curl -fsSL https://raw.githubusercontent.com/{{ item.key }}
          -o {{ user_home }}/{{ item.value }}"  # Скачивает файл и сохраняет в указанное место
  loop:
    - {key: "r4ven-me/dots/main/.zshrc", value: ".zshrc"}  # Конфигурация Zsh
    - {key: "junegunn/vim-plug/master/plug.vim", value: ".local/share/nvim/site/autoload/plug.vim"}  # Установщик плагинов Neovim
    - {key: "r4ven-me/dots/main/.config/nvim/init.vim", value: ".config/nvim/init.vim"}  # Основной конфиг Neovim
    - {key: "r4ven-me/dots/main/.config/nvim/plugins.vim", value: ".config/nvim/plugins.vim"}  # Список плагинов Neovim
    - {key: "r4ven-me/dots/main/.config/tmux/tmux.conf", value: ".config/tmux/tmux.conf"}  # Конфигурация tmux
  tags:
    - dots

- name: Apply config for dot files  # Применяет настройки dot-файлов
  ansible.builtin.shell: "sudo -u {{ user_name }} {{ item }}"
  loop:
    - "zsh -c 'source {{ user_home }}/.zshrc'"  # Загружает конфигурацию Zsh
    - "nvim -e -c 'PlugInstall' -c 'qall!'"  # Устанавливает плагины Neovim
    - "git clone https://github.com/tmux-plugins/tpm {{ user_home }}/.config/tmux/plugins/tpm"  # Загружает менеджер плагинов tmux
    - "{{ user_home }}/.config/tmux/plugins/tpm/bin/install_plugins"  # Устанавливает плагины tmux
  ignore_errors: true  # Игнорирует ошибки
  changed_when: true  # Помечает задачу как изменённую
  failed_when: false  # Не считает ошибку критической
  no_log: true  # Скрывает вывод (например, для паролей)
  tags:
    - dots

tests/test.yml — файл тестовых задач

---
### Тестовые задачи

- name: Test playbook  # Тестовый playbook
  vars:
    ansible_become_password: "password"  # Пароль для sudo
    user_pkgs:  # Список пакетов для установки
      - "zsh"
      - "tmux"
      - "neovim"
  hosts: localhost  # Запуск на локальном хосте
  # remote_user: root  # (Закомментировано) Пользователь для удалённого подключения
  gather_facts: true  # Сбор фактов о системе
  become: true  # Выполнение с правами суперпользователя (sudo)
  force_handlers: true  # Принудительное выполнение обработчиков
  # roles:
  #   - role: ../server-base-config  # (Закомментировано) Подключение роли
  connection: local  # Использование локального соединения (без SSH)

  tasks:
    - name: Start debug  # Отладочный вывод переменных
      debug:
        msg:
          - "{{ ansible_distribution }}"  # Выводит дистрибутив системы
          - "{{ user_pkgs }}"  # Выводит список пакетов
      tags:
        - debug  # Метка для запуска отладки отдельно

tests/inventory.yml — файл инвентаризации для тестов

---
### Тестовый инвентаризация

all:  # Глобальная группа хостов
  hosts:
    localhost:  # Определяет локальный хост
      ansible_host: localhost  # Использует локальный адрес
      # ansible_port: 22  # (Закомментировано) Порт SSH, по умолчанию 22
      # ansible_user: user  # (Закомментировано) Имя пользователя для подключения
      # ansible_ssh_private_key_file: ~/.ssh/id_ed25519  # (Закомментировано) Путь к приватному SSH-ключу

vars/main.yml — файл с переменными

Файл с переменными, которые переопределяют оные в файле default/main.yml. Про него поговорим далее.

README.md — файл ридми

Файл с кратким руководством в формате markdown (в процессе).

server_base_config.yml — файл инициализирующего плейбука

---
### Playbook для инициализации роли

- name: Basic server configuration  # Основная конфигурация сервера
  hosts: all  # Применение на всех хостах из инвентаря
  # remote_user: root  # (Закомментировано) Пользователь для удалённого подключения
  gather_facts: true  # Сбор фактов о системе
  become: true  # Выполнение с правами суперпользователя (sudo)
  force_handlers: true  # Принудительное выполнение обработчиков
  roles:
    - role: ../server-base-config  # Запуск роли

Указание своих значений переменных

Перед запуском роли Ansible необходимо указать свои значения для обязательных переменных✍️.

Сделать это можно в двух местах: в ./default/main.yml и ./vars/main.yml. Первый, понятно по названию, содержит переменные по умолчанию, а второй переопределяет значения переменных из первого. Я рекомендую вам оставить ./default/main.yml в качестве шаблона, а свои значения задать в ./vars/main.yml.

Тут следует учитывать иерархию переопределения переменных. Смотрите комментарий под постом.

Открываем его на редактирование:

nvim ./roles/linux-base-config/vars/main.yml

Вы можете раскоментировать и изменить любые значения, я укажи лишь обязательные.

Флаги (true, false):

  • server_config — если true, выполняет пул задач для конфигурации сервера;
  • server_reboot — если true, перезапускает сервер после завершения всех задач;
  • user_config — если true, выполняет пул задач по настройке окружения пользователя.

Должен быть установлен хотя бы один флаг в true.

Переменные:

  • user_name — имя нового пользователя
  • user_password — пароль пользователя в виде хэш строки SHA-512 (как получить см. ниже)
  • user_ssh_pubkey — публичный SSH ключ (как получить см. ниже)
  • server_root_password — пароль пользователя root, также SHA-512
  • server_ssh_port — новый порт сервера SSH
  • server_ufw_ports — список tcp портов фаервола UFW, которые нужно открыть

В списке портов UFW обязательно укажите текущий SSH порт, чтобы не потерять доступ к серверу во время настройки.

Понравился мой конфиг Neovim? Можете с легкостью создать аналогичный по статье: Neovim – Установка и настройка редактора кода с элементами IDE всего в несколько команд.

Пароли пользователей вы можете указать явно (ansible все равно сделает из них строку SHA-512 на сервере), но так делать не рекомендуется☝️. Безопаснее всего указать их в виде хэш строки. К слову, аналогичным образом в файле /etc/shadow хранятся пароли локальных пользователей Linux.

Пароль в виде строки SHA-512

Чтобы получить hash пароля в SHA-512 воспользуйтесь утилитой mkpasswd (из пакета whois) или openssl (из пакета openssl):

mkpasswd --method=sha-512

# или

openssl passwd -6

Генерация публичного SSH ключа

Если у вас уже есть SSH ключи, просто скопируйте публичный и вставьте в соответствующую переменную: user_ssh_pubkey. Для генерации нового SSH ключа воспользуйтесь командой:

# генерация нового ключа
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519

# вывод публичного ключа
cat ~/.ssh/id_ed25519.pub

Подробнее про SSH ключи я рассказывал в прошлых статьях тут и тут.

Пример запуска playbook на двух хостах

В моей инвентаризацие Ansible есть два хоста с именами debian12 и ubuntu24. На их примере я и покажу, как запустить Playbook. Чтобы удостовериться в корректности написанных задач ansible, всегда рекомендуется прогонять плейбуки в режиме dry run🏃. Только учтите, что не все модули Ansible его поддерживают, например предусмотреть результат кастомных shell невозможно🤷‍♂️.

Запуск в режиме dry run

Вот команда запуска плейбука в режиме проверки (--check) для отдельных хостов из файла inventory (ключ -l или --limit + хосты через запятую):

ansible-playbook ./roles/linux-base-config/linux_base_config.yml \
    --limit debian12,ubuntu24 --check

Часть задач будет пропущена.

Все задачи в данной роли имеют теги. Чтобы выполнить пул по тегу(ам) используйте ключ -t или --tags:

ansible-playbook ./roles/linux-base-config/linux_base_config.yml \
    --limit debian12,ubuntu24 --check --tags sshd

С помощью тегов можно также исключать задачи: --skip-tags tag1,tag2.

Чтобы просмотреть список доступных тегов роли выполните:

ansible-playbook ./roles/linux-base-config/linux_base_config.yml --list-tags

Рабочий запуск

Теперь запускаем плейбук, который внесет реальные изменения на хосты:

ansible-playbook ./roles/linux-base-config/linux_base_config.yml \
    --limit debian12,ubuntu24

Готово👌. Если какие-то из задач завершились ошибкой😢, изучите вывод, при необходимости поправьте файлы роли и запустите плейбук заново (или обратитесь в наш чат за помощью). Помните, что ansible работает по принципу идемпотентности, т.е. приводит систему к состоянию. Чаще всего, если задача выполнилась успешно, то при следующем запуске она отработает без внесения изменений.

В конце своей работы (при наличии соответствующего флага) ansible отправит команду перезапуска системы (reboot) на удаленные хосты и удалит из списка открытых в фаерволе портов 22-й. В случае успеха плейбук должен положительно завершиться задачей “Wait for online”.

После отработки проверяем подключение к хостам уже под другими реквизитами:

Все отлично!

Если необходимо просто выполнить настройку окружения пользователя, например root, сделать это можно так:

ansible-playbook ./roles/linux-base-config/linux_base_config.yml \
    --limit debian12,ubuntu24 -t dots -e 'user_name=root' -e 'user_home=/root'

Дополнительно: установка powerline шрифта для GUI сеанса

Если вы выполняли настройку окружения пользователя, то для корректной отрисовки иконок в вашем терминале во время графической сессии — необходимо использовать специальный мноноширный иконочный powerline шрифт🤯, например, из проекта Nerd fonts.

Мои читатели знают, что я предпочитаю шрифт Hack☝️. Вот простой пример, как его можно установить:

Обратите вниманием, что для выполнения команд потребуются права sudo. Либо установите шрифты только для текущего пользователя в директории ~/.local/share/fonts.

# создаем директорию шрифта
sudo mkdir /usr/share/fonts/Hack

# скачиваем архив со шрифтами
curl -fsSLO \
    $(curl -s https://api.github.com/repos/ryanoasis/nerd-fonts/releases/latest \
    | grep browser_download_url \
    | grep 'Hack.zip' \
    | cut -d  '"' -f 4)

# распаковываем архив, коприруем шрифты в систем
sudo unzip ./Hack.zip -d /usr/share/fonts/Hack/ && rm -f ./Hack.zip

В команде curl используется механизм подстановки командной строки. Т.е. основной команде на скачивание: curl -fsSLO передается аргумент, который является результатом выполнения другой команды внутри конструкции $(command), выполняющейся предварительно. В итоге основная команда получит прямой URL на zip файл последнего релиза шрифта Hack из GitHub. Команда универсальна.

После установки шрифта, активируйте его в настройках вашего терминала🛠.

В Gnome-terminal это делается так:

Послесловие

Вот так автоматизация сокращает количество ручной работы в десятки раз.

Спасибо, что читаете 😊. Еще раз напомню, что актуальные версии файлов из статьи доступны в моем репозитории на GitHub.

Обязательно подписывайтесь на наш телеграм канал: @r4ven_me, уведомления о новых материалах на сайте приходят туда в день публикации. А если появились вопросы – приглашаю в наш дружелюбный Вороний чат 🚶‍♀️🐧🚶🐧🚶‍♂️🐧.

Всех благ!

Полезные материалы

Подписаться
Уведомить о
1 Комментарий
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии