Развёртывание сложного PHP-приложения с помощью Ansible в Ubuntu 14.04

В первом руководстве данной серии вы узнали, как развернуть простое приложение PHP с помощью Ansible в Ubuntu 14.04.

В этом руководстве речь пойдет о развертывании более сложного приложения. Вы узнаете, как настроить SSH-ключи для поддержки инструментов для разработки и публикации кода, настроить системный брандмауэр, подготовить и настроить базу данных (включая пароль), планировщик задач (crons) и демон очереди. В результате вы получите полностью работающий сервер приложений PHP с вышеупомянутой расширенной конфигурацией.

Как и в первом руководстве, здесь в качестве PHP-приложения используется фреймворк Laravel.

Требования

Это руководство является продолжением первого мануала этой серии, потому обязательно выполните Развёртывание простого PHP-приложения с помощью Ansible в Ubuntu 14.04.

1: Изменение репозитория приложения

Обновите репозитоирй Git.

Поскольку установка Laravel по умолчанию не требует расширенных функций, которые вы вскоре настроите, вы должны заменить стандартный репозиторий пользовательским образцом репозитория с кодом отладки. Мы будем использовать репозитоирй, который находится по адресу https://github.com/do-community/do-ansible-adv-php.

Перейдите в каталог ansible-php:

cd ~/ansible-php/

Отредактируйте этот плейбук:

nano php.yml

Найдите задачу Clone git repository и откорректируйте е таким образом:

- name: Clone git repository
git: >
dest=/var/www/laravel
repo=https://github.com/do-community/do-ansible-adv-php
update=yes
version=example
sudo: yes
sudo_user: www-data
register: cloned

Сохраните и закройте плейбук.

Затем откройте свой сервер в браузере:

http://your_server_ip/

Вы должны увидеть сообщение:

could not find driver

Это значит, что репозиторий приложения был успешно изменен: приложение не может подключиться к БД.

2: Настройка SSH-ключей для разработки

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

Ansible отлично подходит для поддержания настройки серверов и приложений, но для изменения кода на сервере и удаленного запуска команд приложения часто используются такие инструменты, как Envoy и Rocketeer . большинство подобных инструментов требует подключение SSH, которое может напрямую обращаться к приложению. Это означает, что вам нужно настроить SSH-ключи для пользователя www-data.

Вам понадобится файл открытого ключа пользователя, который будет загружать код. Обычно это файл ~/.ssh/id_rsa.pub. Скопируйте его в ansible-php:

cp ~/.ssh/id_rsa.pub ~/ansible-php/deploykey.pub

Используйте модуль Ansible authorized_key для установки открытого ключа в /var/www/.ssh/authorized_keys. Это позволит средствам развертывания подключаться к приложению. В конфигурации следует указать, где находится ключ и какому пользователю нужен этот ключ (www-data в данном случае).

- name: Copy public key into /var/www
authorized_key: user=www-data key="{{ lookup('file', 'deploykey.pub') }}"

Нам также необходимо установить оболочку пользователя www-data, чтобы вы могли войти в систему. В противном случае SSH разрешит соединение, но пользователь не получит доступа к оболочке. Это можно сделать с помощью модуля user и установить оболочку /bin/bash (или любую другую оболочку).

- name: Set www-data user shell
user: name=www-data shell=/bin/bash

Снова откройте плейбук:

nano php.yml

Добавьте следующую задачу:

. . .
- name: Configure nginx
template: src=nginx.conf dest=/etc/nginx/sites-available/default
notify:
- restart php5-fpm
- restart nginx
- name: Copy public key into /var/www

authorized_key: user=www-data key="{{ lookup('file', 'deploykey.pub') }}"


- name: Set www-data user shell


user: name=www-data shell=/bin/bash

handlers:
. . .

Сохраните и закройте файл.

ansible-playbook php.yml --ask-sudo-pass

После этого вы сможете подключиться по SSH к пользователю www-data.

ssh www-data@your_server_ip

Если вам удалось подключиться к пользователю, значит, все работает. Чтобы выйти, введите logout или нажмите CTRL+D.

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

3: Настройка брандмауэра

Теперь можно настроить брандмауэр. Он будет блокировать все, кроме HTTP и SSH.

По умолчанию Ubuntu 14.04 поставляется с брандмауэром UFW (Uncomplicated Firewall). Ansible предлагает модуль ufw для работы с этим брандмауэром. Он имеет ряд полезных функций и прост в работе. Он идеально подходит для автономных веб-серверов, которым требуется только несколько портов. В данном случае нужно открыть порт 80 (HTTP) и 22 (SSH). Вам также может понадобиться порт 443 для HTTPS.

Модуль ufw имеет несколько различных параметров, которые выполняют разные задачи. Вы должны выполнить следующие задачи:

  • Включить UFW и запретить весь входящий трафик по умолчанию.
  • Открыть порт SSH, но ограничить его скорость, чтобы предотвратить атаки brute force.
  • Открыть порт HTTP.

Это можно сделать так:

- name: Enable UFW
ufw: direction=incoming policy=deny state=enabled
- name: UFW limit SSH
ufw: rule=limit port=ssh
- name: UFW open HTTP
ufw: rule=allow port=http

Откройте плейбук в редакторе:

nano php.yml

Добавьте перечисленные выше задачи:

. . .
- name: Copy public key into /var/www
authorized_key: user=www-data key="{{ lookup('file', 'deploykey.pub') }}"
- name: Set www-data user shell
user: name=www-data shell=/bin/bash
- name: Enable UFW

ufw: direction=incoming policy=deny state=enabled


- name: UFW limit SSH


ufw: rule=limit port=ssh


- name: UFW open HTTP


ufw: rule=allow port=http

handlers:
. . .

Сохраните и закройте файл.

ansible-playbook php.yml --ask-sudo-pass

после этого сервер будет поддерживать соединения SSH и HTTP; другие порты будут заблокированы.

Вы можете проверить состояние UFW с помощью команды:

ansible php --sudo --ask-sudo-pass -m shell -a "ufw status verbose"

Рассмотрим команду по частям:

  • ansible: запускает задачу Ansible вне плейбука.
  • php: запускает задачу для хоста в этой группе.
  • —sudo: запускает команду как sudo.
  • —ask-sudo-pass: запрашивает пароль для запуска sudo.
  • -m shell: запускает модуль shell.
  • -a «ufw status verbose»: опции модуля. Команда оболочки будет запущена без опций «ключ=значение».

В выводе вы увидите:

your_server_ip | success | rc=0 >>
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip
To                         Action      From
--                         ------      ----
22                         LIMIT IN    Anywhere
80                         ALLOW IN    Anywhere
22 (v6)                    LIMIT IN    Anywhere (v6)
80 (v6)                    ALLOW IN    Anywhere (v6)

4: Установка пакетов MySQL

Теперь нужно установить MySQL для приложения.

Сначала нужно добавить необходимые пакеты MySQL в задачу install packages в начале плейбука. Нужны пакеты mysql-server, mysql-client и php5-mysql. Чтобы Ansible мог общаться с MySQL, также вам понадобится python-mysqldb.

После добавления пакетов нужно перезапустить nginx и php5-fpm, чтобы новые пакеты начали работать. В противном случае MySQL не подключится к PHP.

Ansible позволяет переписать любую задачу в плейбуке, и изменения вступят в силу сразу после запуска плейбука.

Откройте файл php.yml:

nano php.yml

Найдите задачу install packages и добавьте в нее перечисленные выше пакеты:

. . .
- name: install packages
apt: name={{ item }} update_cache=yes state=latest
with_items:
- git
- mcrypt
- nginx
- php5-cli
- php5-curl
- php5-fpm
- php5-intl
- php5-json
- php5-mcrypt
- php5-sqlite
- sqlite3
- mysql-server

- mysql-client


- php5-mysql


- python-mysqldb


notify:


- restart php5-fpm


- restart nginx

. . .

Сохраните и закройте файл.

ansible-playbook php.yml --ask-sudo-pass

5: Настройка базы данных MySQL

Теперь нужно подготовить БД MySQL дял приложения.

Ansible взаимодействует с MySQL с помощью модулей с префиксом mysql_ (например mysql_db, mysql_user). Модуль mysql_db позволяет создать БД с определенным именем.

- name: Create MySQL DB
mysql_db: name=laravel state=present

Также для работы нужен валидный пользователь БД, с помощью которого приложение подключится к БД. Вы можете сгенерировать пароль локально и сохранить его в плейбуке Ansible, но это небезопасно. Есть более надежный способ сделать это.

Сгенерируйте пароль с помощью Ansible на самом сервере и используйте его непосредственно там, где это необходимо. Чтобы сгенерировать 32-символьный пароль, обратитесь к инструменту командной строки makepasswd. Поскольку makepasswd не поставляется по умолчанию в Ubuntu, нужно также добавить эту утилиту в список пакетов.

Также нужно чтобы Ansible сохранил вывод команды (то есть пароль). Однако, поскольку Ansible не знает, запустилась ли команда оболочки shell, пусть при запуске этой команды будет создан файл. Ansible проверит, существует ли файл, и если это так, Ansible предположит, что команда уже запущена и не будет запускать ее снова.

Задача выглядит так:

- name: Generate DB password
shell: makepasswd --chars=32
args:
creates: /var/www/laravel/.dbpw
register: dbpwd

затем нужно создать пользователя БД MySQL и указать его пароль. Для этого существует модуль mysql_user. Используйте параметр stdout в переменной, которую вы определили во время генерирования пароля, чтобы получить исходный вывод команды оболочки, например: dbpwd.stdout.

Команда mysql_user принимает имя пользователя и привилегии. Создайте пользователя laravel и передайте ему все права на таблицу laravel. Задача должна запускаться только после изменения переменной dbpwd (она запускается при генерировании пароля).

Задача выглядит так:

- name: Create MySQL User
mysql_user: name=laravel password={{ dbpwd.stdout }} priv=laravel.*:ALL state=present
when: dbpwd.changed

Обновите плейбук:

nano php.yml

Найдите задачу install packages и добавьте в нее пакет makepasswd.

. . .
- name: install packages
apt: name={{ item }} update_cache=yes state=latest
with_items:
- git
- mcrypt
- nginx
- php5-cli
- php5-curl
- php5-fpm
- php5-intl
- php5-json
- php5-mcrypt
- php5-sqlite
- sqlite3
- mysql-server
- mysql-client
- php5-mysql
- python-mysqldb
- makepasswd
notify:
- restart php5-fpm
- restart nginx
. . .

Затем добавьте в конец файла задачи для генерирования пароля, создания БД MySQL и пользователя для этой БД.

. . .
- name: UFW limit SSH
ufw: rule=limit port=ssh
- name: UFW open HTTP
ufw: rule=allow port=http
- name: Create MySQL DB

mysql_db: name=laravel state=present


- name: Generate DB password


shell: makepasswd --chars=32


args:


creates: /var/www/laravel/.dbpw


register: dbpwd


- name: Create MySQL User


mysql_user: name=laravel password={{ dbpwd.stdout }} priv=laravel.*:ALL state=present


when: dbpwd.changed

handlers:
. . .

Пока что не запускайте плейбук!

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

6: Настройка взаимодействия приложения PHP и базы данных

Теперь нужно добавить пароль БД MySQL в файл .env приложения.

Обновите файл .env и добавьте в него учетные данные новой БД. По умолчанию файл .env для Laravel содержит такие строки:

DB_HOST=localhost
DB_DATABASE=homestead
DB_USERNAME=homestead
DB_PASSWORD=secret

Строку DB_HOST можно оставить как есть, но три других нужно обновить. для этого используйте:

- name: set DB_DATABASE
lineinfile: dest=/var/www/laravel/.env regexp='^DB_DATABASE=' line=DB_DATABASE=laravel
- name: set DB_USERNAME
lineinfile: dest=/var/www/laravel/.env regexp='^DB_USERNAME=' line=DB_USERNAME=laravel
- name: set DB_PASSWORD
lineinfile: dest=/var/www/laravel/.env regexp='^DB_PASSWORD=' line=DB_PASSWORD={{ dbpwd.stdout }}
when: dbpwd.changed

Как и в задаче по созданию пользователя MySQL, здесь переменная сгенерированного пароля (dbpwd.stdout) поможет заполнить файл нужными данными. Опция when запустит задачу только после изменения dbpwd.

Поскольку файл .env уже существовал на момент добавления задачи для генерации пароля, вам нужно будет сохранить пароль в другом файле. Задача может проверить наличие этого файла. Параметры sudo и sudo_user создадут файл как пользователь www-data.

- name: Save dbpw file
lineinfile: dest=/var/www/laravel/.dbpw line="{{ dbpwd.stdout }}" create=yes state=present
sudo: yes
sudo_user: www-data
when: dbpwd.changed

Откройте php.yml:

nano php.yml

Добавьте в плейбук перечисленные выше задачи:

. . .
- name: Create MySQL User
mysql_user: name=laravel password={{ dbpwd.stdout }} priv=laravel.*:ALL state=present
when: dbpwd.changed
- name: set DB_DATABASE

lineinfile: dest=/var/www/laravel/.env regexp='^DB_DATABASE=' line=DB_DATABASE=laravel


- name: set DB_USERNAME


lineinfile: dest=/var/www/laravel/.env regexp='^DB_USERNAME=' line=DB_USERNAME=laravel


- name: set DB_PASSWORD


lineinfile: dest=/var/www/laravel/.env regexp='^DB_PASSWORD=' line=DB_PASSWORD={{ dbpwd.stdout }}


when: dbpwd.changed


- name: Save dbpw file


lineinfile: dest=/var/www/laravel/.dbpw line="{{ dbpwd.stdout }}" create=yes state=present


sudo: yes


sudo_user: www-data


when: dbpwd.changed

handlers:
. . .

Не запускайте плейбук!

7: Перемещение базы данных

Теперь нужно настроить миграцию БД, чтобы подготовить таблицы.

В Laravel это делается путем запуска команды migrate (php artisan migrate —force) в каталоге Laravel. Обратите внимание, мы добавили флаг -force, потому что это требует среда production.

Задача Ansible выглядит так:

- name: Run artisan migrate
shell: php /var/www/laravel/artisan migrate --force
sudo: yes
sudo_user: www-data
when: dbpwd.changed

Обновите плейбук:

nano php.yml

Добавьте в него эту задачу:

. . .
- name: Save dbpw file
lineinfile: dest=/var/www/laravel/.dbpw line="{{ dbpwd.stdout }}" create=yes   state=present
sudo: yes
sudo_user: www-data
when: dbpwd.changed
- name: Run artisan migrate

shell: php /var/www/laravel/artisan migrate --force


sudo: yes


sudo_user: www-data


when: dbpwd.changed

handlers:
. . .

Теперь можно сохранить и запустить его:

ansible-playbook php.yml --ask-sudo-pass

После этого обновите страницу браузера. Вы увидите сообщения:

Queue: NO
Cron: NO

БД настроена правильно. Теперь нужно настроить cron и демон очередей.

8: Настройка cron

Задачи cron – это команды, которые выполняются по установленному расписанию и могут выполнить любое количество задач для приложения, (например, обновлять электронную почту). Демон cron выполняет задачи, которые нужно выполнять периодически, без вмешательства пользователя вручную. Задачи cron могут выполняться так часто, как вам нужно.

По умолчанию Laravel предоставляет команду Artisan schedule:run, которая запускается каждую минуту и выполняет определенные запланированные задачи в приложении. Значит, нужно добавить только одну задачу cron, если приложение использует эту функцию.

У Ansible есть модуль cron, который предлагает множество опций.

  • job: команда, которую нужно выполнить. Нужна, если state=present.
  • minute, hour, day, month и weekday: время, когда нужно запустить задачу (минута, час, день и день недели соответственно).
  • special_time (reboot, yearly, annually, monthly, weekly, daily, hourly): специальные определители времени.

По умолчанию модуль создаст задачу, которая будет запускаться каждую минуту.

- name: Laravel Scheduler
cron: >
job="run-one php /var/www/laravel/artisan schedule:run 1>> /dev/null 2>&1"
state=present
user=www-data
name="php artisan schedule:run"

Команда run-one является вспомогательной командой Ubuntu, которая запускает указанную команду всего один раз. Это значит, что если предыдущая команда schedule:run до сих пор запущена, она не будет запущена снова. Это позволит избежать ситуаций, в которых cron попадает в цикл и запускает несколько экземпляров одной и той же команды.

Откройте плейбук:

nano php.yml

Добавьте в него задачу:

. . .
- name: Run artisan migrate
shell: php /var/www/laravel/artisan migrate --force
sudo: yes
sudo_user: www-data
when: dbpwd.changed
- name: Laravel Scheduler

cron: >


job="run-one php /var/www/laravel/artisan schedule:run 1>> /dev/null 2>&1"


state=present


user=www-data


name="php artisan schedule:run"

handlers:
. . .

Сохраните и запустите плейбук:

ansible-playbook php.yml --ask-sudo-pass

Обновите страницу браузера и вы увидите такое сообщение:

Queue: NO
Cron: YES

Это значит, что cron теперь работает. Осталось настроить демон очередей.

9: Настройка демона очередей

Также Laravel предоставляет команду Artisan queue:work —daemon.

Обработчики очередей похожи на задачи cron, поскольку они выполняют задачи в фоновом режиме. Разница заключается в том, что приложение передает задания в очередь либо посредством действий, выполняемых пользователем, либо с помощью запланированных в cron задач. Задания очереди выполняются обработчиком по одному и по требованию. Задачи в очереди обычно используются для выполнения длительных операций, например, для отправки электронной почты или вызова API для внешних сервисов.

В отличие от schedule:run эту команду не нужно запускать каждую минуту. Вместо этого она должна постоянно работать как демон в фоновом режиме. Популярный способ сделать это – использовать сторонний пакет, например supervisord, но для этого метода требуется понимание того, как этот пакет работает. Существует гораздо более простой способ реализовать его с помощью команды cron и run-one.

Создайте запись cron, чтобы запустить демон обработки очереди, и используйте run-one для его запуска. Теперь cron начнет процесс при первом запуске, а все последующие прогоны cron будут игнорироваться, пока демон работает.

Как только обработчик очередей остановится, run-one снова запустит его. Это невероятно простой метод, который избавляет вас от необходимости изучать настройку сторонних инструментов.

Задача выглядит так:

- name: Laravel Queue Worker
cron: >
job="run-one php /var/www/laravel/artisan queue:work --daemon --sleep=30 --delay=60 --tries=3 1>> /dev/null 2>&1"
state=present
user=www-data
name="Laravel Queue Worker"

Откройте php.yml:

nano php.yml

Добавьте в него задачу:

. . .
- name: Laravel Scheduler
cron: >
job="run-one php /var/www/laravel/artisan schedule:run 1>> /dev/null 2>&1"
state=present
user=www-data
name="php artisan schedule:run"
- name: Laravel Queue Worker

cron: >


job="run-one php /var/www/laravel/artisan queue:work --daemon --sleep=30 --delay=60 --tries=3 1>> /dev/null 2>&1"


state=present


user=www-data


name="Laravel Queue Worker"

handlers:
. . .

Сохраните и запустите плейбук:

ansible-playbook php.yml --ask-sudo-pass

Обновите страницу браузера:

Queue: YES
Cron: YES

Демон очередей работает в фоновом режиме. Задача cron, которую вы добавили раннее, вносит задание в очередь. Это обновляет базу данных.

Теперь у вас есть рабочее приложения Laravel, которое поддерживается с помощью cron и демона очередей.

Tags: , , ,