Сегодня про автоматизацию начальной настройки 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.
❗️ Осторожно
Дорогие читатели, прошу обратить ваше внимание на тот факт, что материалы на моём сайте носят рекомендательный характер. Все действия вы выполняете на свой страх и риск. Всегда делайте бэкапы важных файлов, перед настройкой виртуалок делайте снапшоты, а перед внесением изменений на продуктивных серверах всегда проверяйте их на тестовом стенде. Благодарю за понимание.
Иван Чёрный
Предисловие: в чем суть?
Все просто — данный плейбук является автоматизацией моих прошлых статей:
- Начальная настройка Linux сервера на примере Debian
- Neovim – Установка и настройка редактора кода с элементами IDE всего в несколько команд
- ZSH – Интерактивная командная оболочка для Linux + Oh-My-Zsh
- bat, exa – подсветка синтаксиса стандартного вывода в терминале Linux (cat, less, tail и ls)
- Tmux – установка и кастомизация + Nord theme
В каждой из статей довольно много подробностей и рутинных действий. Поэтому идея автоматизировать процесс родилась сама собой. В этой заметке я расскажу про мою Ansible role, которая сводит количество выполняемых действий из статей выше — к одной команде. Если конечно соблюдены необходимые условия😉.
Коротко перечислю, что делает playbook:
1) Конфигурация сервера:
- Замена пароля
root
пользователя - Установка временнОй зоны (timezone)
- Установка
hostname
- Добавление хостов в файл
hosts
- Настройка локализации
- Обновление и установка пакетов
- Конфигурация SWAP (если его нет) в файле
- Ограничение количества хранимых системных журналов
journald
- Конфигурация SSH сервера
- Отключение использования IPv6 в системе и в фаерволе UFW
- Открытие заданного списка сетевых портов в UFW
черт, а я тут ufw пользуюсь..😳
2) Настройка окружения пользователя:
- Создание нового пользователя с указанием пароля, оболочки и списка групп
- Добавление публичного SSH ключа авторизации в
~/.ssh
- Установка пакетов пользователя (отдельный список)
- Скачивание и применение конфигурации для Zsh, Neovim, Tmux
Если вам не нужно выполнять все перечисленные действия, то плейбук подразумевает выборочную конфигурацию с помощью указания флагов (см. ниже) или через теги задач.
Подготовка
Для запуска плейбука нам понадобятся:
- Целевые Linux сервер(ы) с Debian/Ubuntu на борту
- Возможностью удаленного подключения к этим серверам по SSH
- Установленный и настроенный Ansible на управляющей машине-клиенте
Если у вас еще нет такого набора, то возможно найдете полезными эти мои материалы:
- Пример установки сервера Debian 12
- SSH – Безопасное подключение к удалённым хостам: введение
- 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-512server_ssh_port
— новый порт сервера SSHserver_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
Рабочий запуск
ВНИМАНИЕ❗️ Данный playbook меняет номер порта демона sshd и отключает парольный доступ к серверу по SSH. Крайне рекомендуется запускать необкатанные плейбуки в тестовых средах. Все действия вы выполняете на свой страх и риск. Я вас предупредил)
Теперь запускаем плейбук, который внесет реальные изменения на хосты:
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, уведомления о новых материалах на сайте приходят туда в день публикации. А если появились вопросы – приглашаю в наш дружелюбный Вороний чат 🚶♀️🐧🚶🐧🚶♂️🐧.
Всех благ!
Полезные материалы
- Начальная настройка Linux сервера на примере Debian
- Neovim – Установка и настройка редактора кода с элементами IDE всего в несколько команд
- ZSH – Интерактивная командная оболочка для Linux + Oh-My-Zsh
- bat, exa – подсветка синтаксиса стандартного вывода в терминале Linux (cat, less, tail и ls)
- Tmux – установка и кастомизация + Nord theme
- Пример установки сервера Debian 12
- SSH – Безопасное подключение к удалённым хостам: введение
- Ansible – система управления конфигурациями: знакомство
- Идемпотентность | Wiki
Комментарии
📝 Примечание
Иван Чёрный:
Полезный комментарий из чата:
Спасибо Вам за то, что делитесь своим опытом! Статья понравилась.
Единственное, на чём хотелось бы сделать акцент (Указание своих значений переменных):
Я бы порекомендовал всё таки переменные лучше определять в ./default/main.yml. При использовании переменных ./vars/main.yml вы теряете в большинстве случаев возможность переопределять переменные роли (https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_variables.html#understanding-variable-precedence), в том числе и через внешние файлы group_vars, host_vars (https://docs.ansible.com/ansible/latest/inventory_guide/intro_inventory.html#organizing-host-and-group-variables). Только —extra-vars наивысший приоритет и позволит при запуске плейбука переопределить значение переменной в роли.
В group_vars, host_vars можно описать все необходимые переменные для целых групп серверов или хоста, в том числе и секретные данные (пароли, логины и т.д.), при этом эти файлы можно спокойно зашифровать Ansible Vault’ом.
В целом, вы можете в ./default/main.yml определить сами переменные для использования в роли, без значений или с примерами необходимых значений и уже через внешние файлы подкидывать нужные значения, для всех ролей. Это ладно если парочка ролей, а если их с несколько десятков, которые могут содержать как общие, так и индивидуальные переменные, в том числе и секретные.