Как развернуть расширенное PHP-приложение, используя Ansible в Ubuntu 14.04

Вступление

Это руководство является вторым в серии о развертывании приложений PHP с использованием Ansible в Ubuntu 14.04. Https://www.digitalocean.com/community/tutorials/how-to-deploy-a-basic-php-application-using-ansible-on-ubuntu-14-04[first учебник] охватывает основные шаги для развертывания приложение, и является отправной точкой для шагов, описанных в этом руководстве.

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

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

Предпосылки

Это руководство следует непосредственно из конца the первый учебник в серии, а также все настройки и файлы, сгенерированные для этого учебника. Если вы еще не завершили этот учебник, пожалуйста, сначала сделайте это, прежде чем продолжить этот учебник.

Шаг 1 - Переключение хранилища приложений

На этом шаге мы обновим репозиторий Git до слегка настроенного репозитория примеров.

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

Если вы этого еще не сделали, замените каталоги на + ansible-php + из предыдущего урока.

cd ~/ansible-php/

Откройте нашу существующую книгу для редактирования.

nano php.yml

Найдите и обновите задачу «Clone git repository», чтобы она выглядела следующим образом.

Обновлено Ansible задача

- name: Clone git repository
 git: >
   dest=/var/www/laravel
   repo=
   update=

 sudo: yes
 sudo_user: www-data
 register: cloned

Сохраните и запустите playbook.

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

Когда он завершит работу, зайдите на свой сервер в веб-браузере (т.е. + Http: /// +). Вы должны увидеть сообщение «Не удалось найти драйвер» *.

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

Шаг 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 / авторизованный_keys +, что позволит инструментам развертывания подключаться и получать доступ к нашему приложению. Конфигурация должна знать только, где находится ключ, используя поиск, и для пользователя должен быть установлен ключ (в нашем случае + www-data +).

Новое Ansible задание

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

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

Новое Ansible задание

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

Теперь откройте книгу для редактирования, чтобы добавить новые задания.

nano php.yml

Добавьте вышеперечисленные задачи в вашу + php.yml + playbook; конец файла должен соответствовать следующему. Дополнения выделены красным.

Обновлен php.yml

. . .

 - name: Configure nginx
   template: src=nginx.conf dest=/etc/nginx/sites-available/default
   notify:
     - restart php5-fpm
     - restart nginx







 handlers:

. . .

Сохраните и запустите playbook.

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

Когда Ansible завершит работу, вы сможете использовать SSH с пользователем + www-data.

ssh www-data@

Если вы успешно вошли в систему, это работает! Теперь вы можете выйти из системы, введя + logout + или нажав * CTRL + D *.

Нам не нужно будет использовать это соединение для каких-либо других шагов в этом руководстве, но будет полезно, если вы настраиваете другие инструменты, как указано выше, или для общей отладки и обслуживания приложений по мере необходимости.

Шаг 3 - Настройка брандмауэра

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

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

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

  1. Включить UFW и запретить весь входящий трафик по умолчанию.

  2. Откройте порт SSH, но ограничьте скорость, чтобы предотвратить атаки методом перебора.

  3. Откройте порт HTTP.

Это можно сделать с помощью следующих задач, соответственно.

Новые Ansible задачи

- 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

Как и прежде, откройте файл + php.yml + для редактирования.

nano php.yml

Добавьте вышеупомянутые задачи в playbook; конец файла должен соответствовать следующему.

Обновлен 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










 handlers:

. . .

Сохраните и запустите playbook.

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

Когда это успешно завершится, вы все равно сможете подключиться через SSH (используя Ansible) или HTTP к вашему серверу; другие порты теперь будут заблокированы.

Вы можете проверить состояние UFW в любое время, выполнив эту команду:

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

Разбиваем команду Ansible выше:

  • + ansible +: запустить необработанное задание Ansible без книги воспроизведения.

  • + php: запустить задачу против хостов в этой группе.

  • + - sudo: Запустите команду как` + sudo`.

  • + - ask-sudo-pass +: запрашивать пароль + sudo +.

  • + -m shell: запустить модуль` + shell`.

  • + -a" ufw status verbose "+: опции для передачи в модуль. Поскольку это команда + shell +, мы передаем необработанную команду (т.е. + ufw status verbose) прямо без каких-либо опций` + key = value`.

Это должно вернуть что-то вроде этого.

Вывод состояния UFW

| 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 установлен на нашем сервере, просто добавив необходимые пакеты в задачу установки пакетов в верхней части нашей книги игр. Нам нужны следующие пакеты: + mysql-server,` + mysql-client` и + php5-mysql. Нам также понадобится + python-mysqldb +, чтобы Ansible мог взаимодействовать с MySQL.

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

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

Как и прежде, откройте файл + php.yml + для редактирования.

nano php.yml

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

Обновлен php.yml

. . .

- 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








. . .

Сохраните и запустите playbook:

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

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

На этом этапе мы создадим базу данных MySQL для нашего приложения.

Ansible может напрямую общаться с MySQL, используя предустановленные модули + mysql_ + (например, + mysql_db +, + mysql_user +). Модуль + mysql_db + обеспечивает способ обеспечения существования базы данных с определенным именем, поэтому мы можем использовать такую ​​задачу для создания базы данных.

Новое Ansible задание

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

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

Мы сгенерируем пароль с помощью Ansible на самом сервере и будем использовать его там, где это необходимо. Чтобы сгенерировать пароль, мы будем использовать инструмент командной строки + makepasswd + и запросим пароль из 32 символов. Поскольку + 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 + имеет changed, что будет только тогда, когда будет запущена задача генерации пароля.

Задача должна выглядеть так:

Новое Ansible задание

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

Собрав все это вместе, откройте файл + php.yml + для редактирования, чтобы мы могли добавить вышеуказанные задачи.

nano php.yml

Во-первых, найдите задачу + install packages + и обновите ее, чтобы включить пакет + makepasswd +.

Обновлен php.yml

. . .

- 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

. . .

Затем добавьте генерацию пароля, создание базы данных MySQL и задачи создания пользователя внизу.

Обновлен php.yml

. . .

 - name: UFW limit SSH
   ufw: rule=limit port=ssh

 - name: UFW open HTTP
   ufw: rule=allow port=http














 handlers:

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

Шаг 6 - Настройка приложения PHP для базы данных

На этом этапе мы сохраним пароль базы данных MySQL в файле + .env + для приложения.

Как мы это делали в предыдущем уроке, мы обновим файл + .env +, чтобы включить в него вновь созданные учетные данные базы данных. По умолчанию файл Laravel + .env + содержит следующие строки:

Laravel .env file

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

Мы можем оставить строку + DB_HOST + как есть, но обновим остальные три, используя следующие задачи, которые очень похожи на задачи, которые мы использовали в предыдущем уроке для установки + APP_ENV + и + APP_DEBUG +.

Новые Ansible задачи

- 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 +, чтобы сообщить Ansible, что нужно создать файл как + www-data +.

Новое Ansible задание

- 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

Добавьте вышеупомянутые задачи в playbook; конец файла должен соответствовать следующему.

Обновлен php.yml

. . .

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

















 handlers:

. . .
  • Опять же, пока не запускайте playbook! * У нас есть еще один шаг, прежде чем мы сможем запустить playbook.

Шаг 7 - Миграция базы данных

На этом шаге мы запустим миграцию базы данных, чтобы настроить таблицы базы данных.

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

Задача Ansible для выполнения этого выглядит следующим образом.

Новое Ansible задание

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

Теперь пришло время обновить наш playbook. Откройте файл + php.yml + для редактирования.

nano php.yml

Добавьте вышеупомянутые задачи в playbook; конец файла должен соответствовать следующему.

Обновлен 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







 handlers:

. . .

Наконец, мы можем сохранить и запустить playbook.

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

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

your_server_ip /»> Http: ///

Queue: NO
Cron: NO

Это означает, что база данных настроена правильно и работает должным образом, но мы еще не настроили задачи cron или демон очереди.

Шаг 8 - Настройка задач cron

На этом шаге мы настроим любые задачи cron, которые необходимо настроить.

Задачи Cron - это команды, которые запускаются по заданному расписанию и могут использоваться для выполнения любого количества задач для вашего приложения, таких как выполнение задач обслуживания или рассылка обновлений активности электронной почты - по сути, всего, что необходимо делать периодически без вмешательства пользователя вручную. Задачи Cron могут выполняться так часто, как каждую минуту, или так редко, как вам требуется.

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

Ansible имеет модуль + cron + с рядом различных опций, которые переводят непосредственно в разные опции, которые вы можете настроить через cron:

  • + job +: команда для выполнения. Требуется, если состояние = присутствует.

  • «+ минута», «+ час», «+ день», «+ месяц» и «+ день недели +»: минута, час, день, месяц или день недели, когда должно выполняться задание, соответственно.

  • + special_time + (+ перезагрузка +, + ежегодно +, + ежегодно +, + ежемесячно +, + еженедельно +, + ежедневно +, + ежечасно +): псевдоним для указания специального времени.

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

Новое Ansible задание

- 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 блокируется в цикле, и со временем все больше и больше экземпляров одной и той же задачи запускаются до тех пор, пока на сервере не закончатся ресурсы.

Как и прежде, откройте файл + php.yml + для редактирования.

nano php.yml

Добавьте вышеупомянутое задание в playbook; конец файла должен соответствовать следующему.

Обновлен php.yml

. . .

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








 handlers:

. . .

Сохраните и запустите playbook:

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

Теперь обновите страницу в вашем браузере. Через минуту он обновится, чтобы выглядеть так.

your_server_ip /»> Http: ///

Queue: NO
Cron:

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

Шаг 9 - Настройка демона очереди

Как и команда Artisan + schedule: run + из шага 8, Laravel также поставляется с работником очереди, который можно запустить с помощью команды Artisan + queue: work --daemon +. На этом шаге мы настроим работника демона очереди для Laravel.

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

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

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

Учитывая все это, мы создадим еще одну задачу cron для запуска нашего работника очереди.

Новое Ansible задание

- 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

Добавьте вышеупомянутое задание в playbook; конец файла должен соответствовать следующему:

Обновлен 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"








 handlers:
. . .

Сохраните и запустите playbook:

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

Как и раньше, обновите страницу в своем браузере. Через минуту он обновится и будет выглядеть так:

your_server_ip /»> Http: ///

Queue:
Cron: YES

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

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

Заключение

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

Мы не использовали ни одной SSH-команды как часть этого руководства (кроме проверки входа пользователя в систему + www-data +), и все, включая пароль пользователя MySQL, было настроено автоматически. После выполнения этого руководства ваше приложение готово к работе и поддерживает инструменты для принудительного обновления кода.

Related