Important Note

The methodology explained here is outdated, please read Deploying Symfony2 Apps via Scalarium: Improved Methodology for an updated version.

About

This article describes how to use the cloud-based cluster-management platform Scalarium in order to automatically mass-deploy Symfony2 applications with a MySQL database backend onto clusters of Amazon EC2 virtual machines by creating a special Symfony2 environment, using a custom Chef recipe, and making use of Doctrine migrations.

Target audience

Current or soon-to-be Scalarium customers, devops interested in deploying Symfony2 applications using centralized configuration management tools like Chef or Puppet, PHP developers.

Prerequisites

In order to get your Symfony2 application running on Amazon AWS, you need an AWS account, a Scalarium account (correctly set up with your AWS credentials), and a Github account to host your Symfony2 application and your custom Chef recipes. This tutorial assumes that you are already familiar with managing EC2 clusters with Scalarium. It’s tested to work with Symfony 2.0.4 on Ubuntu 11.04 instances.

Overview

The above diagram is here to illustrate how the different parts are working together in order to deploy a running application. The virtual machines hosting our Symfony2 application and the database are EC2 instances running in the Amazon AWS cloud. We will use Scalarium to define the layout of our cluster (called a “cloud” in Scalarium), to configure the application which will be deployed onto the application servers within this cluster, and to configure the custom Chef recipes which need to be applied to our application server instances in order to finalize the deployment and setup of our application.

Looking at the diagram, we could say that Scalarium manages, Amazon runs, and Github provides – Scalarium uses what Github provides in order to run the application on Amazon.

Because Scalarium already provides the setup logic and recipes that allow us to set up basic Apache/PHP application servers and MySQL database servers on AWS, we will only need to provide what is needed in order to finalize the deployment and setup of our specific Symfony2 application – setting up a basic Ubuntu server, installing software packages like PHP, Apache, MySQL, and checking out our Symfony2 application code from Github is all done by Scalarium without the need to provide additional means.

The example application

It makes sense to follow this tutorial using your own Symfony2 application. However, an example application hosted at Github.com → MyHammer → ScalariumExampleSymfony2Application is used to illustrate the process – you are free to use this repository to deploy the application to your Scalarium cloud. The commit history of this repository also shows step by step which changes need to be applied to a Symfony2 application to prepare it for running on a Scalarium cloud.

Setting up a Scalarium cloud for our application

Our very first step is to set up a new cloud in Scalarium which manages the EC2 instances, server roles, application configuration and custom Chef cookbooks needed to deploy and run our Symfony2 application.

In order to do so, click on Add Cloud in the upper right corner of the main Scalarium administration interface. Choose whatever name you like (I will stick with “Symfony”), define a region that makes sense for you (Europe in my case), and choose “Ubuntu 11.04″ as the default operating system. Please choose “Role Dependent” as the Hostname Theme, because this will make it much easier to follow my explanations whenever I’m talking about specific machine instances.

Assuming that you have already set up Amazon AWS in your Scalarium account, please choose the AWS credentials and SSH keys accordingly.

You now have an empty cloud. Let’s define these three things in order to host our application:

  • An application
  • Custom Cookbooks
  • An application server and a database server role

Let’s start with our application. Later, we are going to make some modifications to our application that are necessary for hosting it with Scalarium, but we can already configure it into our cloud. Clicking on Add Application gives us a dialogue in which we can configure where EC2 instances will be able to pull our application from upon bootup or application deployment.

I’m going to choose “SymfonyExample” as the name. Please choose “PHP” as the Application Type. If you are going to use the sample application that resides on Github, please choose a Repository Type of “Git”, and enter the following Repository URL: “git://github.com/MyHammer/ScalariumExampleSymfony2Application.git”. All other parameters are not important for now – however, if you are configuring your own private repository, you will need to provide a deploy SSH key.

Although this is sufficient to deploy the code from Github onto our instances, it’s not enough to actually get a running application. Additional steps are necessary after the code has been pulled from Github, and those steps need to be performed using a custom Chef recipe. We are going to write this recipe later, but we can already configure the cookbook that contains the recipe into our cloud. On the cloud overview page, select Manage Cookbooks from the Actions dropdown.

In the new dialogue, check Enable custom cookbooks, and configure as follows:

  • Repository Type: “Git”
  • Repository URL: “git://github.com/MyHammer/ScalariumExampleSymfony2ChefRecipes.git”
Deploy SSH Key and Branch / Revision don’t need to be filled out.

As a last step, our cloud needs two machine roles: one for our application servers, and one for our database server. We will start with the application server role. On the cloud overview page, select Add role, and choose the PHP Application Server role.

Once you have added it, an additional configuration is necessary: In Custom Recipes, please add the recipe “symfony2::deploy” for the Scalarium actions configure and deploy. This makes sure that our recipe is run everytime our application is deployed to an instance, or whenever the cloud changes.

Last but not least, create an additional role MySQL Master. Additional configuration is not needed here. If you like, you can already add and boot an instance for this role.

Modifying the Symfony2 application

Our Symfony2 application doesn’t need to be re-programmed in any way in order to run via Scalarium – however, its configuration needs to be specifically modified. You can either modify your existing “prod” environment configuration, or you may want to create a new environment called “scalarium”, which is what I did for the example application.

The nice thing about Scalarium and Symfony2 is that the former provides a very nice PHP interface to the layout of the current cluster, and the latter provides an easy way to make use of this interface.

This allows us to dynamically configure our application’s database setup, avoiding the need to hard code e.g. the IP address of our MySQL master into the application. Here is a step by step description on how to achieve this:

First, create a new and empty file /app/config/config_scalarium.yml. This is going to be the main configuration file for the new “scalarium” environment. Fill the file with the following content: imports: - { resource: parameters_scalarium.php } - { resource: config.yml } This makes our new environment use 99% of it’s settings based on those from the “prod” environment – however, an additional file is imported, /app/config/parameters_scalarium.php – this is where we are going to dynamically define our database settings using the power of Scalarium.

Let’s fill /app/config/parameters_scalarium.php with life: <?php include_once(__DIR__ . '/../../scalarium.php'); $scalarium = new Scalarium(); $container->setParameter('database_driver', 'pdo_mysql'); $container->setParameter('database_host', $scalarium->db->host); $container->setParameter('database_port', '3306'); $container->setParameter('database_name', $scalarium->db->database); $container->setParameter('database_user', $scalarium->db->username); $container->setParameter('database_password', $scalarium->db->password); $container->setParameter('database_path', null); unset($scalarium); What happens here is that the database parameter placeholders like %database_host%, which are used in /app/config/config.yml to configure the database for our application, are programmatically defined based on the current state of the Scalarium cloud. Leveraging the power of the scalarium.php script, which is available in the root directory of our application per default (a symlink is created whenever our application is deployed on an instance), this makes our database config dynamic and therefore always up to date.

The creators of Scalarium point out that using scalarium.php is just a convenience. The full-stack alternative would be to write a Chef recipe that generates the configuration file and register this recipe on the configure and deploy events. Inside the Chef recipe Scalarium provides you with all necessary information like which hosts exist in the cloud and what their configuration is (e.g. IP addresses, roles, etc.). See http://support.scalarium.com/kb/custom-instance-setup for further information on how to use the Scalarium cloud structure information in your own recipes.

One important thing to consider here is that the settings in /app/config/parameters.ini overwrite settings with the same name in /app/config/parameters_scalarium.php – we therefore need to remove all lines containing “%database” from parameters.ini!

The last step is to create a Frontend Controller for our new “scalarium” environment at /web/app_scalarium.php: <?php require_once __DIR__.'/../app/bootstrap.php.cache'; require_once __DIR__.'/../app/AppKernel.php'; //require_once __DIR__.'/../app/AppCache.php'; use Symfony\Component\HttpFoundation\Request; $kernel = new AppKernel('scalarium', false); $kernel->loadClassCache(); //$kernel = new AppCache($kernel); $kernel->handle(Request::createFromGlobals())->send();

You can look at a visual before-and-after comparison of all these changes on Github:

Putting the parts together

Our cloud now has everything in place it needs. Its roles are defined, our application servers know where to pull our application from, instances of role “PHP Application Server” are configured to use our custom Symfony2 deploy recipe, and our application has been modified to make use of the dynamic cloud configuration in a new environment called “scalarium”.

Let’s take a moment to look at our custom Chef recipe.

What this recipe does is it checks for each application that is going to be deployed if it’s actually a Symfony2 application, and if yes, it starts by updating the vendors (or installing them from scratch if none exist yet), clears the application’s cache, applies database migrations (if any), makes sure that cache and log files and directories are read and writable for those users that need it, installs and configures an Apache vhost file for the app, and disables the default vhost.

Ok, let’s deploy our application! All you need to do is to add an instance of role PHP Application Server and boot it up. Everything else happens automatically:

  • Scalarium requests an EC2 instance from Amazon AWS which is booted with a fresh Ubuntu 11.04 image
  • Scalarium sets this machine up with some basic packages like PHP, Apache etc., and executes its basic setup routines (which are Chef recipes, too)
  • Our Symfony2 application is pulled from Github and deployed to /srv/www/symfonyexample/current
  • The instance executes our custom Chef recipe symfony2::deploy which installs the vendors, clears the app cache, applies the database migrations, installs the web assets, sets users and permissions on cache and log files, and configures an Apache vhost for our application

Once this is done, you can browse to the public IP of the booted application server instance, which will display the Symfony2 welcome page.

If you would like to be informed on updates to this post, just follow @manuelkiessling