Мы продолжаем цикл обучающих статей для начинающих системных администраторов. В этом материале мы будем писать Ansible role для поднятия полноценного готового сервера. Отметим, что если вы являетесь опытным администратором, можете смело пропускать данный материал.
Любое написание ansible-роли сопровождается планом. В этот план необходимо будет включить все, что необходимо будет установить и настроить.
Вот наш план:
Пункт 1. Первоначальная настройка сервера.
Пункт 2. Установка LEMP.
Пункт 3. Права и пользователь.
Пункт 4. Настройка LEMP.
Пункт 5. Перенос кода площадки и БД.
Пункт 6. Тестирование.
Пункт 7. Итог.
Так как если рассмотреть все пункты в одной статье, то материал получается слишком объемным, было принято решение разделить его на две части. В первой части обсудим 1, 2 и 3 пункты плана. В следующей - 4, 5, 6, 7 пункты.
На текущий момент в директории /var/ansible, которую мы создали и использовали в предыдущей статье, имеются файлы:
hosts.txt - файл с ip адресами удаленных машин.
playbook.yml - playbook из первой статьи.
Генерируем роль, роль будет называться LEMP (название можете использовать любое).
ansible-galaxy init LEMP
Открываем playbook.yml. Добавляем:
- name: Install LEMP server
hosts: all
become: yes
roles:
- LEMP
name - название данного ansible проекта.
hosts - на каких хостах запускать.
become - использование sudo.
roles - подключаем созданную роль.
Сохраняем файл и выходим. Больше данный файл нам не понадобится.
Структура директорий в директории ansible:
.
├── hosts.txt
├── LEMP
│ ├── defaults
│ │ └── main.yml
│ ├── files
│ ├── handlers
│ │ └── main.yml
│ ├── meta
│ │ └── main.yml
│ ├── README.md
│ ├── tasks
│ │ ├── main.yml
│ ├── templates
│ ├── tests
│ │ ├── inventory
│ │ └── test.yml
│ └── vars
│ └── main.yml
└── playbook.yml
Пункт 1. Первоначальная настройка сервера.
В данном пункте необходимо настроить сервер для работы веб приложений. Выполнить установку default приложений, настроить дату, название сервера и локализацию.
Необходимо установить приложения и пакеты:
dirmngr mc iotop htop telnet tcpdump nmap curl console-cyrillic hexedit sudo zip unzip patch pwgen vim less parted subversion ntp bzip2 lsof strace mutt s-nail ncdu smartmontools tree dnsutils logrotate rsyslog
Необходимо настроить:
время
hostname
локализацию
Первое, что необходимо сделать, - зайти в директорию с заданиями по пути /var/ansible/LEMP/tasks/
.
cd /var/ansible/LEMP/tasks/
Создаем yml-файл для пункта 1. Это необходимо, чтобы в дальнейшем удобнее было администрировать ansible-роль. Потому что все будет разделено по своим файлам и загружаться только тогда, когда нам это необходимо. Для каждого пункта статьи мы будем создавать отдельный файл конфигурации.
Создаем yml файл. Название можно выбрать любое:
touch default_settings.yml
Заходим в главный файл конфигурации в заданиях ./LEMP/tasks/main.yml.
Необходимо подгрузить выше созданный файл конфигурации. Для этого используется модуль include_tasks и название файла:
- include_tasks: default_settings.yml
Сохраняем изменения в main.yml.
Теперь начинаем редактировать default_settings.yml
. Для установки нам потребуется использование shell модуль. Для начала необходимо обновить все репозитории. Добавляем:
---
- name: update repo.
shell: apt update
name - название задания.
shell - модуль, с помощью которого выполняется команда.
Сохраняем и запускаем из директории, где лежит playbook.yml (/var/ansible/):
ansible-playbook playbook.yml
PLAY [Install LEMP server] **************************************************************************************************************************************************************************************
TASK [Gathering Facts] ******************************************************************************************************************************************************************************************
ok: [ansible2]
TASK [test : update repo.] **************************************************************************************************************************************************************************************
changed: [ansible2]
PLAY RECAP ******************************************************************************************************************************************************************************************************
ansible2 : ok=2 changed=1 unreachable=0 failed=0
Как видим из вывода, ошибки не наблюдается.
Начинаем устанавливать приложения и пакеты.
Также будем использовать модуль shell, так как нужных нам утилит достаточно много и устанавливать все через модуль apt будет достаточно долго и проблематично.
В файл default_settings.yml добавляем:
- name: install default app.
shell:
cmd: "apt install -y dirmngr mc iotop htop telnet tcpdump nmap curl hexedit sudo zip unzip patch pwgen vim less parted subversion ntp bzip2 lsof strace mutt s-nail ncdu smartmontools tree dnsutils logrotate rsyslog"
cmd - ключ, в котором мы указываем команду.
Запускаем:
# ansible-playbook playbook.yml
PLAY [Install LEMP server] **************************************************************************************************************************************************************************************
TASK [Gathering Facts] ******************************************************************************************************************************************************************************************
ok: [ansible2]
TASK [test : update repo.] **************************************************************************************************************************************************************************************
changed: [ansible2]
TASK [test : install default app.] ******************************************************************************************************************************************************************************
changed: [ansible2]
PLAY RECAP ******************************************************************************************************************************************************************************************************
ansible2 : ok=3 changed=2 unreachable=0 failed=0
Все установлено.
Далее необходимо настроить время, локализацию и hostname.
Нам понадобятся команды:
Для установки времени:
timedatectl set-timezone Europe/Moscow
Для установки локализации:
locale-gen ru_RU.UTF-8
update-locale LANG=en_US.UTF-8 LC_TIME="ru_RU.UTF-8"
Для установки hostname:
hostnamectl set-hostname DOMAIN_NAME
Так как у нас используются плавающие переменные, мы добавим их в директорию vars. Чтобы в дальнейшем на других серверах нам было удобно их менять.
Плавающие переменные:
Europe/Moscow
ru_RU.UTF-8
en_US.UTF-8
DOMAIN_NAME
Открываем файл, где указываем переменные для всего проекта. Файл находится по адресу: /etc/ansible/test/vars/main.yml
Добавляем переменные:
DOMAIN_NAME: domain_name
locale1: ru_RU.UTF-8
locale2: en_US.UTF-8
time_zone: Europe/Moscow
В дальнейшем для вызова переменной необходимо будет использовать скобки {{переменная}}.
Идем в default_settings.yml и добавляем задания:
- name: time
shell:
cmd: "timedatectl set-timezone {{time_zone}}"
- name: locale settings
shell:
cmd: 'locale-gen {{locale1}} && update-locale LANG={{locale2}} LC_TIME="{{locale1}}"'
- name: hostname
shell:
cmd: "hostnamectl set-hostname {{DOMAIN_NAME}}"
Запускаем и проверяем.
ansible-playbook playbook.yml
PLAY [Install LEMP server] **************************************************************************************************************************************************************************************
TASK [Gathering Facts] ******************************************************************************************************************************************************************************************
ok: [ansible2]
TASK [test : update repo.] **************************************************************************************************************************************************************************************
changed: [ansible2]
TASK [test : install default app.] ******************************************************************************************************************************************************************************
changed: [ansible2]
TASK [test : time] **********************************************************************************************************************************************************************************************
changed: [ansible2]
TASK [test : locale settings] ***********************************************************************************************************************************************************************************
changed: [ansible2]
TASK [test : hostname] ******************************************************************************************************************************************************************************************
changed: [ansible2]
PLAY RECAP ******************************************************************************************************************************************************************************************************
ansible2 : ok=6 changed=5 unreachable=0 failed=0
Для проверки можно отправить команду с помощью модуля shell.
ansible all -m shell -a "date && cat /etc/hostname"
ansible2 | CHANGED | rc=0 >>
Пн июн 20 14:41:49 MSK 2022
domainname
Все работает. Пункт 1 завершён.
Итоговый файл default_settings.yml:
- name: update repo.
shell: apt update
- name: install default app.
shell:
cmd: "apt install -y dirmngr mc iotop htop telnet tcpdump nmap curl hexedit sudo zip unzip patch pwgen vim less parted subversion ntp bzip2 lsof strace mutt s-nail ncdu smartmontools tree dnsutils logrotate rsyslog"
- name: time
shell:
cmd: "timedatectl set-timezone {{time_zone}}"
- name: locale settings
shell:
cmd: 'locale-gen {{locale1}} && update-locale LANG={{locale2}} LC_TIME="{{locale1}}"'
- name: hostname
shell:
cmd: "hostnamectl set-hostname {{DOMAIN_NAME}}"
Пункт 2. Установка LEMP.
В данном пункте необходимо установить:
nginx apache2 mysql exim4
Для установки этих пакетов будем использовать модуль apt.
Нам понадобится разделить установку пакетов по разным yml файлам. Так как в дальнейшем будет удобно добавлять и изменять конфигурации для каждого пакета по отдельности.
Создаем 4 файла:
cd /var/ansible/LEMP/tasks
touch mysql_install.yml nginx_install.yml apache2_install.yml exim4_install.yml
Подключаем загрузку задач в main.yml:
#####install mysql
- include_tasks: mysql_install.yml
#####install nginx
- include_tasks: nginx_install.yml
#####install apache2
- include_tasks: apache2_install.yml
#####install exim4
- include_tasks: exim4_install.yml
Добавляем в файл nginx_install.yml:
- name: Install nginx
apt:
name: nginx
state: latest
- name - название задачи.
apt - модуль.
name - название пакета.
state - версия пакета; в данном случае последняя доступная версия.
Добавляем в файл apache2_install.yml:
- name: Install apache2
apt:
name: apache2
state: latest
Добавляем в файл exim4_install.yml:
- name: Install exim4
apt:
name: exim4
state: latest
При установке mysql необходимо будет подключить репозитории. Открываем mysql_install.yml. Используем модуль get_url для загрузки deb. файла с официального сайта.
- name: add mysql repo
get_url:
url: https://dev.mysql.com/get/mysql-apt-config_0.8.6-1_all.deb
dest: "/tmp"
mode: 0440
get_url - модуль загрузки файла по ссылке, аналог wget.
dest - место куда будет загружен файл.
mode - присваиваем права загруженному файлу.
Следующим действием необходимо установить скачанный репозиторий.
- name: install mysql repo
apt: "deb=/tmp/mysql-apt-config_0.8.6-1_all.deb"
become: true
Добавляем ключ репозитория и обновляем репозитории:
- name: add key mysql and update repo
shell: "apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 && apt update"
Устанавливаем python-mysqldb для дальнейшего взаимодействия.
- name: install python-mysqldb
apt:
name: python-mysqldb
state: present
update_cache: yes
Проверяем доступную версию и выводим на экран для проверки:
- name: check latest version of mysql 5.7
command: bash -c "apt-cache showpkg mysql-server|grep 5.7|head -1|cut -d' ' -f1"
register: latestmysql57
- debug: msg="{{ latestmysql57.stdout }}"
Устанавливаем:
- name: install mysql 57
apt:
name: mysql-server={{ latestmysql57.stdout }}
state: present
update_cache: yes
Запускаем:
ansible-playbook playbook.yml
TASK [LEMP : include_tasks] *************************************************************************************************************************************************************************************included: /var/ansible/LEMP/tasks/mysql_install.yml for ansible2
TASK [LEMP : add mysql repo] ************************************************************************************************************************************************************************************ok: [ansible2]
TASK [LEMP : install mysql repo] ********************************************************************************************************************************************************************************ok: [ansible2]
TASK [LEMP : add key mysql] *************************************************************************************************************************************************************************************changed: [ansible2]
TASK [LEMP : update repo] ***************************************************************************************************************************************************************************************changed: [ansible2]
TASK [LEMP : check latest version of mysql 5.7] *****************************************************************************************************************************************************************changed: [ansible2]
TASK [LEMP : debug] *********************************************************************************************************************************************************************************************ok: [ansible2] => {
"msg": "5.7.38-1debian10"
}
TASK [LEMP : install mysql 57] **********************************************************************************************************************************************************************************changed: [ansible2]
TASK [LEMP : include_tasks] *************************************************************************************************************************************************************************************included: /var/ansible/LEMP/tasks/nginx_install.yml for ansible2
TASK [LEMP : Install nginx] *************************************************************************************************************************************************************************************ok: [ansible2]
TASK [LEMP : include_tasks] *************************************************************************************************************************************************************************************included: /var/ansible/LEMP/tasks/apache2_install.yml for ansible2
TASK [LEMP : Install apache2] ***********************************************************************************************************************************************************************************ok: [ansible2]
TASK [LEMP : include_tasks] *************************************************************************************************************************************************************************************included: /var/ansible/LEMP/tasks/exim4_install.yml for ansible2
TASK [LEMP : Install exim4] *************************************************************************************************************************************************************************************ok: [ansible2]
Все необходимые нам 4 сервиса установлены.
Для проверки можно отправить запрос на удаленный сервер с помощью shell модуля :
ansible all -m shell -a "systemctl status nginx apache2 mysql exim4"
Пункт 3. Права и пользователь.
Необходимо:
Cоздать пользователя/домашнюю директорию.
Настроить права директории /var/www.
Создать БД и пользователя БД.
Создаем конфигурационный файл для данного пункта:
touch /var/ansible/LEMP/tasks/default_user_settings.yml
Подключаем конфигурацию в main-файле:
#####default user settings
- include_tasks: default_user_settings.yml
Создаем структуру директорий по аналогии с нашей первой статьей.
На данном этапе нам необходимо добавить переменные в /var/ansible/LEMP/vars, номер пользователя/группы в системе и пароль.
User_uid: 10000
Group_GID: 10000
user_password: password
Открываем default_user_settings.yml. Для добавление пользователя необходимо будет использовать модуль user и group. Добавляем группу:
- name: add group
group:
name: "{{ DOMAIN_NAME }}"
state: present
gid: "{{ Group_GID }}"
state - состояние группы, если группа будет имеется на сервер, то ansible не будет ее пересоздавать или пытаться изменить.
gid - номер группы в системе
"{{ DOMAIN_NAME }}" - переменная которую мы добавляли в Пункте 1.
Добавляем пользователя:
- name: add user
user:
name: "{{ DOMAIN_NAME }}"
password: "{{ user_password | password_hash('sha512') }}"
uid: "{{ User_uid }}"
group: "{{ DOMAIN_NAME }}"
state: present
update_password: on_create
home: "/var/www/{{ DOMAIN_NAME }}"
shell: /bin/bash
password: "{{ user_password | password_hash('sha512') }}" - так как ansible не может передавать не зашифрованный пароль, мы его шифруем с помощью password_hash('sha512').
update_password: on_create - означает, что пароль будет добавлен только один раз при первом выполнении данной команды, то есть он не будет перезаписан в случае повторного запуска роли.
home: "/var/www/{{ DOMAIN_NAME }}" - домашняя директория пользователя.
Таким образом, пользователь и группы созданы. Теперь необходимо создать структуру директорий. Нам поможет модуль file:
- name: create home directory
file:
path: "/var/www/{{ DOMAIN_NAME }}"
owner: "{{ DOMAIN_NAME }}"
group: "{{ DOMAIN_NAME }}"
mode: 0751
state: directory
path: "/var/www/{{ DOMAIN_NAME }}" - адрес директории
owner: "{{ DOMAIN_NAME }}" - Присваиваем директорию пользователю
group: "{{ DOMAIN_NAME }}" - Присваиваем директорию группе.
mode: 0751 - права директории
state: directory - означает, что создается именно директория а не файл.
Структура директорий:
.└── domain_name
├── data
├── log
│ ├── apache2
│ └── nginx
├── sess
├── tmp
└── upload
Следующим шагом необходимо создать базу данных и пользователя базы данных.
Так как настройка mysql ведется в определенном файле, будем использовать именно его (/var/ansible/LEMP/tasks/mysql_install.yml).
Во-первых, необходимо создать root пароль. Добавляем root пароль в переменные /var/ansible/LEMP/vars/main.yml.
mysql_root_password: password
Идем в /var/ansible/LEMP/tasks/mysql_install.yml. Добавляем:
- name: update mysql root password for all root accounts
become: true
mysql_user:
name: root
host: "{{ item }}".
password: "{{ mysql_root_password }}"
login_user: root
login_password: ''
check_implicit_admin: yes
priv: "*.*:ALL,GRANT"
state: present
with_items:
- 127.0.0.1
- ::1
- localhost
Используется модуль mysql_user.
name: root - имя пользователя которого используем в mysql.
host: "{{ item }}". - адреса хостов, на которых будет изменен пароль; все хосты указаны в with_items.
password: "{{ mysql_root_password }}" - новый пароль.
check_implicit_admin - производит проверку входа в mysql без пароля (если, например, пароль указан в .my.cnf в home директории); Если войти без пароля не удалось, будет использован пароль указанный в login_password.
login_user: root и login_password: '' пользователь и пароль, под которым мы заходим в mysql.
priv: "*.*:ALL,GRANT" - какие привилегии присваиваем пользователю.
state: present - присваиваем единоразово.
Далее добавляем базу данных. Добавляем переменные название базы данных и пользователя в /var/ansible/LEMP/vars/main.yml:
name_db: domain_name_db
user_db: domain_name_db
password_user_db: password
Идем обратно в /var/ansible/LEMP/tasks/mysql_install.yml. Добавляем базу данных:
- name: Create a new database with name 'DOMAIN_NAME_DB'
mysql_db:
login_user: root
login_password: "{{ mysql_root_password }}"
name: "{{name_db}}"
state: present
Используем модуль mysql_db.
name: "{{name_db}}" - название базы данных.
Добавляем пользователя и присваиваем права на базу данных.
- name: add user DOMAIN_NAME_USR
mysql_user:
login_user: root
login_password: "{{ mysql_root_password }}"
host: localhost
name: "{{user_db}}"
password: "{{password_user_db}}"
priv: '{{name_db}}.*:ALL,GRANT'
state: present
Пользователь базы данных и база данных созданы. Итоговые файлы в рамках данного пункта следующие:
- name: add group
group:
name: "{{ DOMAIN_NAME }}"
state: present
gid: "{{ Group_GID }}"
- name: add user
user:
name: "{{ DOMAIN_NAME }}"
password: "{{ user_password | password_hash('sha512') }}"
uid: "{{ User_uid }}"
group: "{{ DOMAIN_NAME }}"
state: present
update_password: on_create
home: "/var/www/{{ DOMAIN_NAME }}"
shell: /bin/bash
- name: create home directory
file:
path: "/var/www/{{ DOMAIN_NAME }}"
owner: "{{ DOMAIN_NAME }}"
group: "{{ DOMAIN_NAME }}"
mode: 0751
state: directory
- name: create other directory
file:
path: "/var/www/{{ DOMAIN_NAME }}/data"
owner: "{{ DOMAIN_NAME }}"
group: "{{ DOMAIN_NAME }}"
mode: 0755
state: directory
- name: create other directory
file:
path: "/var/www/{{ DOMAIN_NAME }}/log"
owner: "{{ DOMAIN_NAME }}"
group: "{{ DOMAIN_NAME }}"
mode: 0755
state: directory
- name: create other directory
file:
path: "/var/www/{{ DOMAIN_NAME }}/sess"
owner: "{{ DOMAIN_NAME }}"
group: "{{ DOMAIN_NAME }}"
mode: 0755
state: directory
- name: create other directory
file:
path: "/var/www/{{ DOMAIN_NAME }}/tmp"
owner: "{{ DOMAIN_NAME }}"
group: "{{ DOMAIN_NAME }}"
mode: 0755
state: directory
- name: create other directory
file:
path: "/var/www/{{ DOMAIN_NAME }}/upload"
owner: "{{ DOMAIN_NAME }}"
group: "{{ DOMAIN_NAME }}"
mode: 0755
state: directory
- name: create other directory
file:
path: "/var/www/{{ DOMAIN_NAME }}/log/apache2"
owner: "{{ DOMAIN_NAME }}"
group: "{{ DOMAIN_NAME }}"
mode: 0755
state: directory
- name: create other directory
file:
path: "/var/www/{{ DOMAIN_NAME }}/log/nginx"
owner: "{{ DOMAIN_NAME }}"
group: "{{ DOMAIN_NAME }}"
mode: 0755
state: directory
Файл mysql_install.yml:
- name: add mysql repo
get_url:
url: https://dev.mysql.com/get/mysql-apt-config_0.8.6-1_all.deb
dest: "/tmp"
mode: 0440
- name: install mysql repo
apt: "deb=/tmp/mysql-apt-config_0.8.6-1_all.deb"
become: true
- name: add key mysql and update repo
shell: "apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 && apt update"
- name: install python-mysqldb
apt:
name: python-mysqldb
state: present
update_cache: yes
- name: check latest version of mysql 5.7
command: bash -c "apt-cache showpkg mysql-server|grep 5.7|head -1|cut -d' ' -f1"
register: latestmysql57
- debug: msg="{{ latestmysql57.stdout }}"
- name: install mysql 57
apt:
name: mysql-server={{ latestmysql57.stdout }}
state: present
update_cache: yes
- name: update mysql root password for all root accounts
become: true
mysql_user:
name: root
host: "{{ item }}"
password: "{{ mysql_root_password }}"
login_user: root
login_password: 12345
check_implicit_admin: yes
priv: "*.*:ALL,GRANT"
state: present
with_items:
- 127.0.0.1
- ::1
- localhost
- name: Create a new database with name 'DOMAIN_NAME_DB'
mysql_db:
login_user: root
login_password: "{{ mysql_root_password }}"
name: "{{name_db}}"
state: present
- name: add user DOMAIN_NAME_USR
mysql_user:
login_user: root
login_password: "{{ mysql_root_password }}"
host: localhost
name: "{{user_db}}"
password: "{{password_user_db}}"
priv: '{{name_db}}.*:ALL,GRANT'
state: present
Файл переменных:
DOMAIN_NAME: domain_name
locale1: ru_RU.UTF-8
locale2: en_US.UTF-8
time_zone: Europe/Moscow
User_uid: 10000
Group_GID: 10000
user_password: password
mysql_root_password: password
name_db: domain_name_db
user_db: domain_name_usr
password_user_db: password
Итог
В этой статье мы с вами рассмотрели первую половину практической части серии обучающих статей по ansible. Мы использовали на практике полученные знания из первой статьи. В следующей статье мы рассмотрим остальные пункты, после чего у нас будет готова полноценная ansible-роль для быстрого развертывания простых проектов. Также мы выложим данную ansible-роль на github для ознакомления.
Если у вас остались вопросы, можете задавать их в комментариях. Ставьте лайки и подписывайтесь на нас - в дальнейшем будем публиковать еще больше обучающих статьей.
Также подписывайтесь на наш telegram-канал DevOps FM.
Рекомендации для чтения:
Комментарии (5)
stalker_by
22.06.2022 13:42+1А нельзя сразу нормально учить людей Configuration Management?
В добавок к первому комментарию, почему не объяснить сразу людям про: идемпотентность (которой ваш код не отягощён), Molecule и ansible-lint?!
Кстати да, ваш "код" ansible-lint видимо ненавидит...не показывайте ему ваш код, три новые статьи писать придется :D
Tamerlan666
22.06.2022 20:34+1Это что, прикол какой-то? Как напихать максимальное количество антипаттернов в ансибл-код?
tnt4brain
22.06.2022 21:51Видел стенд Nixys на DevOpsConf. Как-то это не вяжется с техническим уровнем статьи.
nikweter
Первая половина статьи - сплошной bashsible. Дальше нормально пошло, но почему сразу так не писать? Зачем столько shell и cmd?
mc2
DevOps эт не про думать, это про "х., х. и в прод".