Хабрахабр

Разворачиваем окружение для Java-приложения с помощью Ansible

За мной, за мной, читатель, и я проведу тебя в чарующий мир автоматизации разворачивания окружения на серверах под управлением Linux семейства RHEL.

Один из наших java-проектов вырос, стал совсем взрослым и сейчас занимает 4 контура:

Dev — контур для команды разработки,
Qa — контур для команды тестирования,
Stage — контур для демонстрации новых фич заказчику,
Production — боевой контур.

Каждый контур содержит два одинаковых сервера с идентичным набором компонентов окружения для нашего приложения:

linux Oracle — операционная система,
jdk — комплект приложений Java,
haproxy — proxy сервер,
nginx — веб-сервер для отдачи статики,
mysql — субд.

Перед командой эксплуатации встал резонный вопрос: как настроить управление окружением на восьми серверах и сохранить оптимистичное отношение к жизни.

После краткого сравнения систем управления конфигурациями был выбран Ansible. В его пользу сыграли простота, гибкость и отсутствие агентов на управляемых серверах.

Теперь немного расскажем об архитектуре ролей Ansible для проекта.

Для установки и первоначальной настройки каждого компонента окружения написана отдельная роль. Последующая кастомизация настроек каждого компонента окружения также выделена в дополнительную роль. Это позволило нам избежать дублирования общих стандартных ролей для остальных проектов.

Playbook c последовательностью выполняемых ролей для подключения новой ноды в контур выглядит так:

- hosts: - project_group user: username become: yes gather_facts: true roles: - rhel_install_new_server - rhel_install_java - rhel_install_haproxy - rhel_install_nginx - rhel_install_mysql

А теперь расскажем о каждой роли поподробнее.

Роль rhel_install_new_server

Выполняет общую настройку операционной системы и установку системных утилит.

Файл roles/rhel_install_new_server/tasks/main.yml

---
# tasks file for rhel_install_new_server # Проверяем и устанавливаем системные обновления:
- name: yum update yum: name: "*" state: latest update_cache: yes # Подключаем EPEL repository:
- name: install EPEL repository yum: name: https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm state: present update_cache: yes # Устанавливаем стандартные утилиты. Список пакетов задан в переменной tools_packages:
- name: install tools yum: name: '{{ item.name }}' state: present update_cache: yes with_items: '{{ tools_packages }}' # Выставляем автозагрузку для сетевого интерфейса по умолчанию:
- name: set autoload for default interface lineinfile: path: "/etc/sysconfig/network-scripts/ifcfg-{{ansible_default_ipv4.interface}}" regexp: '^ONBOOT=' line: 'ONBOOT="yes"' # Отключаем IPv6 для интерфейса по умолчанию:
- name: disable IPv6 for eth0 replace: path: "/etc/sysconfig/network-scripts/ifcfg-{{ansible_default_ipv4.interface}}" regexp: '^(IPV6.*=).*$' replace: '\1"no"' # Отключаем IPv6:
- name: disable IPv6 blockinfile: path: /etc/sysctl.d/disableipv6.conf create: yes marker: no block: | net.ipv6.conf.all.disable_ipv6 = 1 net.ipv6.conf.default.disable_ipv6 = 1 # Отключаем SELinux:
- name: disable SELinux selinux: state: disabled # Отключаем локальный firewall:
- name: disable firewall systemd: name: firewalld enabled: no state: stopped ignore_errors: yes # Выставляем часовой пояс. Временная зона задана в переменной timezone:
- name: set timezone to UTC timezone: name: '{{ timezone }}' # Выставляем синхронизацию времени в крон:
- name: set synchronize time in cron cron: name: "set synchronize time by ansible" minute: 1 job: "/usr/sbin/ntpdate -u pool.ntp.org >/dev/null 2>&1" # Чистим старые ядра:
- name: Remove old kernels shell: "rpm -q kernel | grep -v `uname -r` | grep -v `/sbin/grubby --default-kernel | sed -r 's#^/boot/vmlinuz-##'` | xargs rpm -e || true"

Файл с переменными roles/rhel_install_new_server/vars/main.yml

---
# vars file for rhel_install_new_server tools_packages: - name: vim - name: mc - name: less - name: sysstat - name: iotop - name: strace - name: traceroute - name: screen - name: rsync - name: curl - name: python - name: wget - name: zlib - name: unzip - name: bind-utils - name: ntp - name: ntpdate - name: telnet - name: nmap - name: tcpdump - name: logrotate - name: net-tools - name: bash-completion - name: yum-utils - name: mtr timezone: UTC

Роль rhel_install_java — установка Java

На нашем проекте используется пакет jdk версии 8u60. Дополнительно мы заранее скачали и положили в папку roles/rhel_install_java/files/ файлы JCE: US_export_policy.jar и local_policy.jar.

Файл с переменными roles/rhel_install_java/vars/main.yml

---
# vars file for roles/rhel_install_java java_dst_path: "/opt/dst/java"
download_url: "http://download.oracle.com/otn/java/jdk/8u60-b27/jdk-8u60-linux-x64.rpm"
java_cookie: "Cookie:' gpw_e24=http%3A%2F%2Fwww.oracle.com%2F; oraclelicense=accept-securebackup-cookie'"

Файл roles/rhel_install_java/tasks/main.yml

---
# tasks file for roles/rhel_install_java # Создадим папку для дистрибутивов:
- name: java | create java dst directory file: path: "{{ java_dst_path }}" state: directory recurse: yes owner: root group: root mode: 0755 # Скачиваем дистрибутив jdk:
- name: java | download java get_url: url: "{{ download_url }}" dest: "{{ java_dst_path }}" tmp_dest: "{{ java_dst_path }}" headers: "{{ java_cookie }}" validate_certs: no owner: root group: root mode: 0744 force: yes ignore_errors: True # Находим скачанный jdk в папке дистрибутивов и заносим имя файла в переменную:
- name: java | register java rpm find: paths: "{{ java_dst_path }}" patterns: "*.rpm" register: java_rpm # Проверим значение переменной:
- debug: msg={{ java_rpm.files.0.path }} # Установим скачанный пакет:
- name: java | install java rpm yum: name: "{{ java_rpm.files.0.path }}" state: present # Копируем Java Cryptography Extension: - name: java | copy JCE copy: src: files/{{ item }} dest: /usr/java/default/jre/lib/security/ owner: root group: root mode: 0644 backup: yes with_items: - US_export_policy.jar - local_policy.jar

Роль rhel_install_haproxy — установка haproxy

Файл roles/rhel_install_haproxy/tasks/main.yml

---
# tasks file for install_haproxy # Устанавливаем последнюю версию haproxy:
- name: install the latest version of haproxy yum: name: haproxy state: latest # делаем бэкап rsyslog.conf:
- name: backup rsyslog.conf copy: src: /etc/rsyslog.conf dest: /etc/rsyslog.conf_orig force: no remote_src: true # Включаем udp в rsyslog:
- name: format rsyslog | set UDP options blockinfile: path: /etc/rsyslog.conf block: | $ModLoad imudp $UDPServerAddress 127.0.0.1 $UDPServerRun 514 state: present insertafter: '^#\$UDPServerRun.*$' notify: - restart rsyslog # создаём файл конфигурации haproxy для rsyslog:
- name: create rsyslog for haproxy blockinfile: path: /etc/rsyslog.d/haproxy.conf content: | if $programname == 'haproxy' and $syslogseverity <= '4' then /var/log/haproxy/haproxy.out if $programname == 'haproxy' and $syslogseverity > '4' then /var/log/haproxy/haproxy.log & stop state: present create: yes notify: - restart rsyslog # делаем бэкап файла конфигурации logrotate:
- name: backup logrotate file haproxy copy: src: /etc/logrotate.d/haproxy dest: /etc/logrotate.d/haproxy_orig force: no remote_src: true # Копируем свой файл конфигурации logrotate для haproxy:
- name: copy logrotate file for haproxy copy: src: files/logrotate_haproxy dest: /etc/logrotate.d/haproxy force: yes # добавляем в автозагрузку и запускаем сервис:
- name: enable and start haproxy systemd: name: haproxy daemon_reload: yes enabled: yes state: started

Файл rhel_install_haproxy/handlers/main.yml

---
# handlers file for install_haproxy - name: restart rsyslog systemd: name: rsyslog state: restarted - name: reload haproxy systemd: haproxy: name state: reloaded

Роль rhel_install_nginx — установка nginx

Файл rhel_install_nginx/tasks/main.yml

---
# tasks file for roles/rhel_install_nginx # Подключаем официальный репозиторий nginx:
- name: add nginx repo yum_repository: name: nginx description: nginx official repo state: present baseurl: "http://nginx.org/packages/rhel/{{ansible_distribution_major_version}}/{{ansible_userspace_architecture}}/" gpgkey: http://nginx.org/keys/nginx_signing.key gpgcheck: yes enabled: yes # Устанавливаем nginx:
- name: install nginx yum: name: nginx state: present update_cache: yes # Создаём системные папки:
- name: create folders file: path: '/etc/nginx/{{ item }}' state: directory mode: 0755 with_items: - conf-available - sites-available - sites-enabled # Подключаем sites-enabled в конфиг nginx:
- name: enable sites-enabled dir lineinfile: dest: /etc/nginx/nginx.conf state: present insertafter: "(.*)include /etc/nginx/(.*)" line: " include /etc/nginx/sites-enabled/*.conf;" # Определяем существует ли default.conf:
- name: stat /etc/nginx/conf.d/default.conf stat: path=/etc/nginx/conf.d/default.conf register: defaultconf_stat # Если существует, перемещаем его:
- name: Move default.conf command: mv /etc/nginx/conf.d/default.conf /etc/nginx/sites-available/default.conf when: defaultconf_stat.stat.exists # Добавляем в автозагрузку и стартуем вебсервер:
- name: enable and started nginx systemd: name: nginx.service enabled: yes state: started

Роль rhel_install_mysql — установка mysql

Здесь пришлось помучиться с первичной авторизацией в mysql.

Файл roles/rhel_install_mysql/vars/main.yml

---
# vars file for roles/rhel_install_mysql mysql_repo_rpm: https://dev.mysql.com/get/mysql57-community-release-el7-11.noarch.rpm
mysql_packets: - mysql-community-server - mysql-community-common - MySQL-python mysql_log: /var/log/mysqld.log

Файл roles/rhel_install_mysql/handlers/main.yml

---
# handlers file for roles/rhel_install_percona_mysql - name: restarted mysql systemd: name: mysqld state: restarted

Файл roles/rhel_install_mysql/templates/root.cnf.j2

[client] user=root
password={{ mysql_root_password }}

Файл roles/rhel_install_mysql/tasks/main.yml

--
-
# tasks file for roles/rhel_install_mysql # Устанавливаем репозиторий yum:
- name: install mysql repo yum: name: "{{ mysql_repo_rpm }}" state: installed # Устанавливаем пакеты mysql:
- name: install mysql yum: name: "{{ mysql_packets }}" state: installed # Добавляем mysql в автозагрузку и запускаем его:
- name: enable mysql systemd: daemon_reload: yes name: mysqld.service enabled: yes state: started # Парсим лог файл mysql и получаем из него пароль рута, назначенный при установке и который нам необходимо сменить. Заносим пароль из файла лога в переменную mysql_root_temp_password.
- name: take default root password from mysql log file shell: > awk -F': ' '$0 ~ "temporary password"{print $2}' {{ mysql_log }} | tail -1 register: mysql_root_temp_password # Проверяем, что мы действительно получили временный пароль:
- name: check mysql_root_temp_password debug: msg={{ mysql_root_temp_password.stdout }} when: mysql_root_temp_password is defined # Назначаем временный пароль как действительный пароль для рута:
- name: Set temp root pass as root password set_fact: mysql_root_password: "{{ mysql_root_temp_password.stdout }}" when: mysql_root_temp_password is defined # Копируем шаблон с новым паролем рута:
- name: Copy the root credentials as .my.cnf file template: src: root.cnf.j2 dest: "~/.my.cnf" mode: 0600 # Проверяем, валиден ли текущий пароль рута:
- name: register password expire shell: mysql --defaults-file=~/.my.cnf -e "SELECT NOW();" register: password_expired ignore_errors: True - name: check password_expired debug: msg={{ password_expired.stdout }} when: password_expired is defined # Наконец-то устанавливаем постоянный пароль для рута на localhost:
- name: ALTER USER root@localhost shell: mysql --defaults-file=~/.my.cnf --connect-expired-password -e "ALTER USER root@localhost IDENTIFIED BY '{{ mysql_root_password }}';" when: password_expired.stdout.find("expired") != -1 # Устанавливаем постоянный пароль для остальных учётных записей рута:
- name: Update MySQL root password for all root accounts mysql_user: name: root host: "{{ item }}" password: "{{ mysql_root_password }}" state: present priv: "*.*:ALL,GRANT" with_items: - "{{ ansible_hostname }}" - 127.0.0.1 - ::1 - localhost when: mysql_root_temp_password is defined # Удалим анонимного пользователя:
- name: Ensure Anonymous user(s) are not in the database mysql_user: name: '' host: "{{ item }}" state: absent with_items: - localhost - "{{ ansible_hostname }}" # Удалим тестовую базу данных:
- name: Remove the test database mysql_db: name: test state: absent

На данном этапе мы получили «чистое» окружение, настройки которого необходимо кастомизировать под наш проект. Сделаем это с помощью дополнительной роли project_configuration.

Роль project_configuration — кастомизация окружения

Перед выполнением роли проверим, что все необходимые компоненты установлены в системе.

Файл roles/project_configuration/tasks/main.yml

######### check java - name: register installed java shell: java -version args: warn: no register: java_present failed_when: java_present.rc > 1 changed_when: no tags: check_installed - debug: msg: "{{ java_present.rc }}" tags: check_installed - fail: msg="Please install java first" when: java_present.rc == 1 tags: check_installed ######### check haproxy - name: register installed haproxy shell: rpm -q haproxy args: warn: no register: haproxy_present failed_when: haproxy_present.rc > 1 changed_when: no tags: check_installed - debug: msg: "{{ haproxy_present.rc }}" tags: check_installed - fail: msg="Please install haproxy first" when: haproxy_present.rc == 1 tags: check_installed - include: tasks/project_haproxy.yml when: haproxy_present.rc == 0 tags: check_installed ######### check nginx - name: register installed nginx shell: rpm -q nginx args: warn: no register: nginx_present failed_when: nginx_present.rc > 1 changed_when: no tags: check_installed - debug: msg: "{{ nginx_present.rc }}" tags: check_installed - fail: msg="Please install nginx first" when: nginx_present.rc == 1 tags: check_installed - include: tasks/project_nginx.yml when: nginx_present.rc == 0 tags: check_installed ######### check mysql - name: register installed mysql shell: rpm -q mysql57-community-release args: warn: no register: mysql_present failed_when: mysql_present.rc > 1 changed_when: no tags: check_installed - debug: msg: "{{ mysql_present.rc }}" tags: check_installed - fail: msg="Please install mysql first" when: mysql_present.rc == 1 tags: check_installed - include: tasks/project_mysql.yml when: mysql_present.rc == 0 tags: check_installed

При успешном выполнении проверок подключаются файлы:
tasks/project_haproxy.yml
tasks/project_nginx.yml
tasks/project_mysql.yml

В них мы прописываем установку конфигурационных файлов с помощью шаблонов, установку ssl сертификатов, заведение необходимых системных пользователей, создание баз данных и т.д.

Как результат: после всех манипуляций мы получили рабочее окружение для нашего приложения. Теперь можно приступать к процедуре деплоймента приложения, но это уже совсем другая история, которой мы поделимся в следующей статье.

Теги
Показать больше

Похожие статьи

Кнопка «Наверх»
Закрыть