Деплой Symfony2 приложения на Heroku Cloud

Heroku Cloud Symfony2 application deployment

Во время работы над небольшим домашним проектом на Symfony2 у меня возникла потребность задеплоить его на стейджинг для тестирования API извне. Понятно, что для этого нужно иметь хостинг для сайта, т.е. либо уже располагать им, либо купить. Еще есть альтернатива в виде облачных сервисов, на которых можно разместить сайт бесплатно, но с ограниченными ресурсами. Подобных сервисов сейчас есть множество, и они конкурируют между собой. Я почему-то склонился к Heroku Cloud, я о нем слышал уже давно, и тут в нужный момент он всплыл в моей памяти. На другие я пока не смотрел, просто решил в этот раз разобраться именно с Heroku. Благо, здесь таки оказалась опция бесплатного размещения сайта, и я начал искать всю необходимую информацию о том, что из мебч представляет работа Symfony2 на Хероку.

Вот кое что из того, что я нашел и что мне помогло:

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

Подготовка

Для начал нужно завести себе аккаунт на Heroku и скачать Heroku Toolbelt под свою платформу. С помощью него можно будет управлять своими удаленным проектами через консоль. Например, чтоб установить Heroku под Debian/Ubuntu, нужно всего лишь выполнить эту команду:

$ wget -qO- https://toolbelt.heroku.com/install-ubuntu.sh | sh

Теперь нужно залогиниться через консоль, выполнив heroku login. Вводим тот e-mail и пароль, с которым регистрировались на сайте.

Enter your Heroku credentials.
Email: test@examle.com
Password (typing will be hidden):
Authentication successful.

Запустим из консоли команду heroku help, чтоб посмотреть список доступных команд Heroku для консоли. Список неполный, так как в нем не показываются алиасы. Например, команда heroku create является алиасом для heroku apps:create.

Создание Heroku приложения

Создавать новое приложение на Heroku можно двумя способами:

  • из консоли на вашем рабочем компьютере;
  • через веб интерфейс Heroku Dashboard.

Создание приложения из консоли

Heroku разворачивает PHP-проекты только с помощью композера, т.е. в вашем проекте должен быть валидный composer.json и к нему же composer.lock. Если просто выполнить heroku create без каких либо параметров, то в вашем облаке создастся первое приложение. Пока что оно ничего не делает, это просто заглушка, которую еще нужно конфигурировать. Название у него будет случайное, например pure-savannah-3520. После создания имя приложения можно поменять (если новое имя еще никем не занято) вот таким образом: heroku rename your-nice-name --app pure-savannah-3520. Команда heroku rename — это, опять же, алиас к heroku apps:rename. А вообще правильнее указывать имя при создании приложения heroku create my-blog, но оно должно быть свободно. Команду heroku create можно выполнять в любой директории, она просто создает в вашем аккаунте в облаке новое приложение. Но толку от этого будет мало. Мы ведь хотим задеплоить наш проект, поэтому эту команду стоит выполнять в папке проекта, в которой инициализирован Git. Помимо создания проекта heroku create проверяет, инициализирован ли Git в текущей директории, и, если да, то добавляет к нему еще свой удаленный репозиторий.

$ mkdir my-abracadabra
$ cd my-abracadabra/
$ git init
Initialized empty Git repository in /home/fresh/Desktop/my-abracadabra/.git/
 
$ heroku create my-abracadabra
Creating my-abracadabra... done, stack is cedar-14
https://my-abracadabra.herokuapp.com/ | https://git.heroku.com/my-abracadabra.git
Git remote heroku added
 
$ git remote -v
heroku  https://git.heroku.com/my-abracadabra.git (fetch)
heroku  https://git.heroku.com/my-abracadabra.git (push)

Собственно, у вас два варианта: либо вы создаете новый проект, инициализируете в нем Git, и подключаете Heroku, либо подключаете Heroku к рабочему проекту с настроенным гитом. Еще есть вариант, когда вы создали проект на одной машине, а хотите его подключить на другой. Или же вы создали проект через веб-интерфейс и хотите добавить его через консоль. В этом случае вам нужно просто добавить удаленный Git репозиторий Heroku к вашему проекту. Адрес репозитория может быть таким: https://git.heroku.com/your-nice-name.git. Например, если проект назвали my-abracadabra, то нужно выполнить:

$ git remote add heroku https://git.heroku.com/my-abracadabra.git

или

$ heroku git:remote -a my-abracadabra

Создание приложения через личный кабинет

На этой страничке https://dashboard.heroku.com/new для создания нового приложения достаточно указать его название и месторасположение сервера.

deploy to heroku

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

Running Symfony2 on Heroku

Конфигурация процессов

В корневой директории проекта нужно создать файл с названием Procfile, а в него записать следующее:

web: vendor/bin/heroku-php-apache2 web/

Эта команда во время деплоя укажет Heroku, что нужно создать web-воркера, который будет слушать HTTP-запросы, а корень сайта (в данном случае Symfony) находится в директории web. Также в команде мы прописали, что нужно запустить PHP в связке с веб-сервером Apache. Если нужен Nginx, то команда будет выглядеть так:

web: vendor/bin/heroku-php-nginx

Можно также попробовать развернуть проект на HHVM:

web: vendor/bin/heroku-hhvm-nginx
web: vendor/bin/heroku-hhvm-apache2

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

Подготовка окружения для Symfony

Важно знать, что Heroku во время деплоя проекта на PHP запускает composer с такими параметрами:

$ composer install --no-dev --prefer-dist --optimize-autoloader --no-interaction

Это означает, что все, что вы подключили в require-dev вашего composer.json файла, не будет установлено.

По умолчанию все консольные команды запускаются в dev окружении. Нам нужно перевести Symfony в prod режим на этапе выполнения скриптов композера, так как там выполняются некоторые необходимые для старта команды (дампятся ассеты, очищается кеш). Это можно сделать с помощью переменной окружения SYMFONY_ENV. Если она установлена, то ее значение будет указывать на окружение, которое необходимо для Symfony. Делается это так:

$ heroku config:set SYMFONY_ENV=prod

Команду нужно запустить только один раз перед первым деплоем.

И теперь деплой

Деплой на Heroku осуществляется путем отправки коммитов на удаленный репозиторий.

$ git push heroku master

После выполнения этой команды в консоли вы увидите весь процесс деплоя. Если он завершился успешно, то выполнив heroku open, в браузере откроется страничка вашего сайта, например, https://my-abracadabra.herokuapp.com/. По умолчанию адрес вашего приложения будет в виде поддомена к herokuapp.com, но можно привязать любой другой домен.

Просмотр логов

Для того, чтобы смотреть логи через консоль Heroku, нужно немного изменить конфиг продакшен окружения для Symfony. В файле app/config/config_prod.yml нужно заменить path: "%kernel.logs_dir%/%kernel.environment%.log" на path: "php://stderr".

monolog:
    handlers:
        nested:
            path: "php://stderr"

Теперь логи можно просмотреть так:

$ heroku logs --num 10

Или следить за логами в режиме онлайн:

$ heroku logs --tail

Запуск консольных команд Symfony

Чтоб получить доступ к bash вашего облака, нужно выполнить команду heroku run bash.

После чего можно запускать доступные команды из вашего Symfony-приложения:

$ heroku run bash
Running `bash` attached to terminal... up, run.1516
~ $ app/console doctrine:schema:validate
[Mapping]  OK - The mapping files are correct.
[Database] FAIL - The database schema is not in sync with the current mapping file.
~ $ app/console doctrine:schema:create
ATTENTION: This operation should not be executed in a production environment.
 
Creating database schema...
Database schema created successfully!
~ $ exit
exit

Для команд не нужно указывать окружение через опцию --env=prod, так как продакшен окружение для Symfony мы уже настроили через переменную окружения.

Также можно запустить PHP интерпретатор из консольной команды:

$ heroku run "php -a"
Running `php -a` attached to terminal... up, run.3436
Interactive shell
 
php > echo "Hello World";
Hello World
php > exit

Для выхода не забудьте написать exit.

Управление параметрами конфигурации

Когда мы выполняем composer update на локальной машине и в файле parameters.yml.dist появились новые параметры, которые у нас еще не установлены, то консоль Symfony в интерактивном режиме попросит эти параметры добавить. На Heroku такой возможности нет, так как composer запускается с опцией --no-interaction. Поэтому нам нужно эти параметры как-то передать. Самый удобный способ, который предлагает и Symfony, и Heroku — это записать их в переменные окружения, а в PHP их оттуда считать. Можно записать их с помощью команды:

$ heroku config:set DATABASE_PASSWORD=secret_password
Setting config vars and restarting my-abracadabra... done, v32
DATABASE_PASSWORD: secret_password
$ heroku config:set DATABASE_USER=secret_user
Setting config vars and restarting my-abracadabra... done, v33
DATABASE_USER: secret_user

Проверить, что мы установили, можно так:

$ heroku config
=== my-abracadabra Config Vars
DATABASE_PASSWORD:          secret_password
DATABASE_USER:              secret_user

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

Деплой Symfony2 приложения на Heroku Cloud

А еще можно это сделать красиво в composer.json с помощью скрипта для композера Incenteev/ParameterHandler. Он прописан в composer.json Symfony из коробки, нам ничего не нужно делать, кроме как использовать его фичу по маппингу переменных окружения в параметры приложения.

{
    "extra": {
        "incenteev-parameters": {
            "env-map": {
                "my_first_param": "MY_FIRST_PARAM",
                "my_second_param": "MY_SECOND_PARAM"
            }
        }
    }
}

Находим секцию extra в файле composer.json и доводим ее до такого вида.

{
    "extra": {
        "symfony-app-dir": "app",
        "symfony-web-dir": "web",
        "incenteev-parameters": {
            "file": "app/config/parameters.yml",
            "env-map": {
                "database_name": "SYMFONY__DATABASE_NAME",
                "database_user": "SYMFONY__DATABASE_USER",
                "database_host": "SYMFONY__DATABASE_HOST",
                "database_password": "SYMFONY__DATABASE_PASSWORD"
            }
        }
    }
}

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

Подключение базы данных к проекту

Базу данных, как и любой другой инструмент, который вам понадобится, нужно искать в аддонах, на странице https://addons.heroku.com/. На странице аддона будет указана команда, с помощью которой его можно установить. Устанавливаем PostgreSQL:

$ heroku addons:add heroku-postgresql
Adding heroku-postgresql on my-abracadabra... done, v9 (free)
Attached as HEROKU_POSTGRESQL_IVORY_URL
Database has been created and is available
 ! This database is empty. If upgrading, you can transfer
 ! data from another database with pgbackups:restore.
Use `heroku addons:docs heroku-postgresql` to view documentation.

MySQL устанавливается через аддон ClearDB MySQL Database:

$ heroku addons:add cleardb
Adding cleardb on my-abracadabra... done, v10 (free)
Use `heroku addons:docs cleardb` to view documentation.

Во время подключения аддона к нашему проекту его параметры подключения записываются также в переменные среды:

$ heroku config
=== my-abracadabra Config Vars
CLEARDB_DATABASE_URL:        mysql://b9153ad7c7a263:a7f8d514@us-cdbr-iron-east-01.cleardb.net/heroku_ab3bbc34931b570?reconnect=true
HEROKU_POSTGRESQL_IVORY_URL: postgres://ylcarfkdjbxouy:tEDQdicyUIcnCk7-xNn9SZZCHZ@ec2-107-20-229-112.compute-1.amazonaws.com:5432/db7sm6in86mtir

Если распарсить урл, то получим и хост, и название базы, и юзера, и пароль. Чтобы это все автоматически считывалось и записывалось в наши параметры приложения на этапе деплоя нужно написать скрипт для композера и повесить его на событие pre-install-cmd. Для этого где-то в проекте, например в неймспейсе AppBundle\Composer создаем такой вот класс:

<?php
namespace AppBundle\Composer;
 
use Composer\Script\Event;
 
class HerokuEnvironment
{
    /**
     * Populate Heroku environment
     *
     * @param Event $event Event
     */
    public static function populateEnvironment(Event $event)
    {
        $url = getenv('CLEARDB_DATABASE_URL'); // Если MySQL
        // $url = getenv('HEROKU_POSTGRESQL_IVORY_URL'); Если установили PostgreSQL
 
        if ($url) {
            $url = parse_url($url);
            putenv("SYMFONY__DATABASE_HOST={$url['host']}");
            putenv("SYMFONY__DATABASE_USER={$url['user']}");
            putenv("SYMFONY__DATABASE_PASSWORD={$url['pass']}");
 
            $db = substr($url['path'], 1);
            putenv("SYMFONY__DATABASE_NAME={$db}");
        }
 
        $io = $event->getIO();
        $io->write('CLEARDB_DATABASE_URL=' . getenv('CLEARDB_DATABASE_URL'));
    }
}

Теперь подключаем этот скрипт в composer.json:

{
    "scripts": {
        "pre-install-cmd": [
            "AppBundle\\Composer\\HerokuEnvironment::populateEnvironment"
        ]
    }
}

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

Бесплатная версия PostgreSQL рассчитана на 10 000 записей в базе данных. Бесплатная ClearDB ограничена 5 мегабайтами.

Дополнительные настройки через composer.json

В файле composer.json в секции extra для Heroku можно передать дополнительные настройки среды. Например:

{
    "extra": {
        "heroku": {
            "framework": "symfony2",
            "document-root": "web",
            "php-config": [
                "date.timezone=Europe/Kiev",
                "display_errors=off",
                "short_open_tag=off"
            ]
        }
    }
}

Скрипт php app/check.php покажет вам, что у вас осталось не настроенного для полноценной работы Symfony. Параметры для PHP указываются в секции php-config. Также для Symfony требуется, чтобы были включены некоторые модули PHP. Для того, чтобы Heroku включил их в момент создания web-воркера, нужно перечислить их в require секции composer.json.

{
    "require": {
        "ext-intl": "*",
        "ext-mbstring": "*"
    }
}

Нотификации об успешном деплое

Есть аддон Deploy Hooks, с помощью которого одной консольной командой можно настроить отсылку уведомлений после успешного деплоя:

$ heroku addons:add deployhooks:email \
>     --recipient=me@example.com \
>     --subject="My abracadabra application has been deployed" \
>     --body="{{user}} has deployed my abracadabra to Heroku"
Adding deployhooks:email on my-abracadabra... done, v11 (free)
Use `heroku addons:docs deployhooks` to view documentation.

Можно настроить также другие каналы нотификаций.

Связка с GitHub

Если ваш удаленный репозиторий размещен на GitHub, тогда в личном кабинете можно его подключить к автоматическим деплоям при каждом комите в мастер (либо в другую ветку).

deploy symfony2 on heroku cloud

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

Если же вы не хотите настраивать автоматический деплой на хероку, тогда вам придется пушить два раза. Один раз, чтоб отправить изменения в свой удаленный репозиторий; второй — чтоб отправить изменения на Heroku и запустить деплой:

$ git push origin master
$ git push heroku master

Файл app.json и деплой на symfony 2 с помощью хероку open source проектов в один клик

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

Файл конфигурации для Heroku называется app.json, он должен лежать в корневой папке проекта. Детально о всех конфигах написано тут. Вот пример файла:

{
    "name": "Name will be shown during the creation of a new app",
    "description": "Description will be shown during the creation of a new app",
    "keywords": ["something", "to", "describe", "app"],
    "website": "https://homepage-of-project.com",
    "success_url": "/homepage-or-smth",
    "repository": "https://github.com/your-nickname/your-repository",
    "logo": "https://exampe.com/this-image-will-be-shown-during-the-creation-of-new-app.webp",
    "addons": [
        "cleardb"
    ],
    "env": {
        "SYMFONY_ENV": {
            "value": "prod"
        }
    },
    "scripts": {
        "postdeploy": "php app/console doctrine:migration:migrate"
    }
}

Можно устанавливать переменные окружения прямо из конфига. Переводим Symfony в prod режим:

{
    "env": {
        "SYMFONY_ENV": {
            "value": "prod"
        }
    }
}

Если нужно выполнить несколько команд после деплоя:

{
    "scripts": {
        "postdeploy": "php app/console doctrine:migration:migrate; ls -la; sleep 5"
    }
}

Думаю, все знают о популярном приложении TodoMVC, которое пишут как туториал для каждого javascript-фреймворка. Вот есть отличный пример того, как можно развернуть веб-приложение Symfony Angular TodoMVC в один клик. Тут TodoMVC работает на Angular в связке с Symfony. Не забудьте, что вы должны быть зарегистрированы на Heroku, чтоб задеплоить. Найдите такую кнопку в README указанного репозитория и просто кликните.

Деплой Symfony2 приложения на Heroku Cloud

Вас перенаправит в ваш dashboard на страницу создания нового приложения. В URL указан параметр template, в котором указан путь к файлу app.json, с которого стянуты настройки приложения. В дашборде это выглядит так:

Деплой Symfony2 приложения на Heroku Cloud

Все, что указано в app.json, отобразится на этой странице: имя, описание, лого. Как видим, тут сразу же подключается аддон для PostgreSQL, и это благодаря одной опции:

{
  "addons": [
    "heroku-postgresql:hobby-dev"
  ]
}

После клика на «Deploy for Free» начнется процесс деплоя. Пара секунд, и вы открываете рабочий сайт. При условии, что в мастере лежит рабочий код :). Вот еще дополнительная информация по конфигурированию деплоя в один клик.

Аддоны для популярных задач

Если вам нужно добавить задачи для крона, для этого есть аддон Heroku Scheduler. Работа с ним детально описана в документации.

Платформа Heroku сама не предоставляет возможности отсылать имейлы. Для этого нужен внешний SMTP-сервер. Но в списке аддонов в категории Email/SMS можно найти уже интегрированные сервисы.

Заключение

Это тот минимум, который вам необходим для старта работы с Heroku. Инструменты, которые вам могут понадобиться и о которых тут не было сказано, можно попробовать поискать в списке аддонов. Самые популярные тулзы должны там быть. Заслуживает ли Heroku быть полноценным продакшен сервером? Пока сложно сказать, еще мало опыта работы с ним. Но как стейджинг с быстрым процессом развертывания — вполне сойдет. Так же он подойдет для опенсоурс проектов, которые можно развернуть в один клик.