Развёртывание сложного PHP-приложения с помощью Ansible в Ubuntu 14.04
PHP, Ubuntu | Комментировать запись
В первом руководстве данной серии вы узнали, как развернуть простое приложение 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: Ansible, Laravel, PHP, Ubuntu 14.04