Previously on Jobeet
With the configuration of the cache system yesterday, the Jobeet website is ready to be deployed on the production servers.
During twenty-two days, we have developed Jobeet on a development machine, and for most of you, it probably means your local machine; except if you develop on the production server directly, which is of course a very bad idea. Now, it is time to move the website to the production servers.
Today, we will see what need to be done before going to production, what kind of deploying strategies you can use, and also the tools you need for a successful deployment.
Preparing the Production Server
Before deploying the project to production, we need to be sure the production server is well-configured. You can re-read day 1, where we explained how to configure the web server.
In this section, we assume that you have already installed the web server, the database server, and PHP 5.2.4 or later.
If you don't have an SSH access to the web server, skip the part where you need to have access to the command line.
Server Configuration
First, you need to check that PHP is installed with all the needed extensions
and is correctly configured. As for day 1, we will use the
check_configuration.php
script provided with symfony. As we won't install
symfony on the production server, download the file directly from the symfony
website:
http://trac.symfony-project.org/browser/branches/1.2/data/bin/check_configuration.php?format=raw
Copy the file to the web root directory and run it from your browser and from the command line:
$ php check_configuration.php
Fix any fatal error the script finds and repeat the process until everything works fine in both environments.
PHP Accelerator
For the production server, you probably want the best performance possible. Installing a PHP accelerator will give you the best improvement for your money.
From Wikipedia: A PHP accelerator work by caching the compiled bytecode of PHP scripts to avoid the overhead of parsing and compiling source code on each request.
APC is one of the most popular one, and it is quite simple to install it:
$ pecl install APC
Depending on your Operating System, you will also be able to install it with the OS native package manager.
Take some time to learn how to configure APC.
The symfony Libraries
Embedding symfony
One of the great strength of symfony is that a project is self-contained. All the files needed for the project to work are under the main root project directory. And you can move around the project in another directory without changing anything in the project itself as symfony only uses relative paths. It means that the directory on the production servers does not have to be the same as the one on your development machine.
The only absolute path that can possibly be found is in the
config/ProjectConfiguration.class.php
file; but we took care of it during
day 1. Check that it actually contains a relative path to the symfony core
autoloader:
// config/ProjectConfiguration.class.php require_once dirname(__FILE__).'/../lib/vendor/symfony/lib/autoload/sfCoreAutoload.class.php';
Upgrading symfony
Even if everything is self-contained in a single directory, upgrading symfony to a newer release is nonetheless insanely easy.
You will want to upgrade symfony to the latest minor release from time to time, as we constantly fix bugs and possibly security issues. The good news is that all symfony versions are maintained for at least a year and during the maintenance period, we never ever add new features, even the smallest one. So, it is always fast, safe, and secure to upgrade from one minor release to another.
Upgrading symfony is as simple as changing the content of the
lib/vendor/symfony/
directory. If you have installed symfony with the
archive, remove the current files and replace them with the newest ones.
If you use Subversion for your project, you can also link your project to the latest symfony 1.2 tag:
$ svn propedit svn:externals lib/vendor/
# symfony http://svn.symfony-project.com/tags/RELEASE_1_2_1/
Upgrading symfony is then as simple as changing the tag to the latest symfony version.
You can also use the 1.2 branch to have fixes in real-time:
$ svn propedit svn:externals lib/vendor/
# symfony http://svn.symfony-project.com/branches/1.2/
Now, each time you do an svn up
, you will have the latest symfony 1.2
version.
When upgrading to a new version, you are advised to always clear the cache, especially in the production environment:
$ php symfony cc
If you also have an FTP access to the production server, you can simulate a
symfony cc
by simply removing all the files and directories under thecache/
directory.
You can even test a new symfony version without replacing the existing
one. If you just want to test a new release, and want to be able to rollback
easily, install symfony in another directory (lib/vendor/symfony_test
for
instance), change the path in the ProjectConfiguration
class, clear the
cache, and you are done. Rollbacking is as simple as removing the directory,
and change back the path in ProjectConfiguration
.
Tweaking the Configuration
Database Configuration
Most of the time, the production database has different credentials than the local one. Thanks to the symfony environments, it is quite simple to have a different configuration for the production database:
$ php symfony configure:database "mysql:host=localhost;dbname=prod_dbname" prod_user prod_pass
You can also edit the databases.yml
configuration file directly.
Assets
As Jobeet uses plugins that embed assets, symfony created relative symbolic
links in the web/
directory. The plugin:publish-assets
task regenerates or
create them if you install plugins without the plugin:install
task:
$ php symfony plugin:publish-assets
Customizing Error Pages
Before going to production, it is better to customize default symfony pages, like the "Page Not Found" page, or the default exception page.
We have already configured the error page for the YAML
format during day 16,
by creating an error.yaml.php
and an exception.yaml.php
files in the
config/error/
directory. The error.yaml.php
file is used by symfony when
in the prod
environment, whereas exception.yaml.php
is used in the dev
environment.
So, to customize the default exception page for the HTML format, create two
files: config/error/error.html.php
and config/error/exception.html.php
.
The 404
page (page not found) can be customized by changing the
error_404_module
and error_404_action
settings:
# apps/frontend/config/settings.yml all: .actions: error_404_module: default error_404_action: error404
Customizing the Directory Structure
To better structure and standardize your code, symfony has a default directory structure with pre-defined names. But sometimes, you don't have the choice but to change the structure because of some external constraints.
Configuring the directory names can be done in the
config/ProjectConfiguration.class.php
class.
The Web Root Directory
On some web hosts, you cannot change the web root directory name. Let's say
that on your web host, it is named public_html/
instead of web/
:
// config/ProjectConfiguration.class.php class ProjectConfiguration extends sfProjectConfiguration { public function setup() { $this->setWebDir($this->getRootDir().'/public_html'); } }
The setWebDir()
method takes the absolute path of the web root directory.
If you also move this directory elsewhere, don't forget to edit the
controller scripts to check that the path to the ProjectConfiguration
file
is still valid:
require_once(dirname(__FILE__).'/../config/ProjectConfiguration.class.php');
The Cache and Log Directory
The symfony framework only write in two directories: cache/
and log/
. For
security reasons, some web host do not set write permissions in the main
directory. If this is the case, you can move these directories elsewhere on the
filesystem:
// config/ProjectConfiguration.class.php class ProjectConfiguration extends sfProjectConfiguration { public function setup() { $this->setCacheDir('/tmp/symfony_cache'); $this->setLogDir('/tmp/symfony_logs'); } }
As for the setWebDir()
method, setCacheDir()
and setLogDir()
takes an
absolute path to the cache
and log
directories respectively.
The Factories
During the Jobeet tutorial, we talked about symfony core objects like
sfUser
, sfRequest
, sfResponse
, sfI18N
, sfRouting
, and so on. These
objects are automatically created, configured, and managed by the symfony
framework. They are always accessible from the sfContext
object, and like
many things in the framework, they are configurable via a configuration file:
factories.yml
. This file is configurable by environment.
When the sfContext
initializes the core factories, it reads the
factories.yml
file for the class names (class
) and the parameters
(param
) to pass to the constructor:
response: class: sfWebResponse param: send_http_headers: false
In the previous snippet, to create the response factory, symfony instantiates
a sfWebResponse
object and pass the send_http_headers
option as an
parameter.
Begin able to customize the factories means that you can use a custom class for symfony core objects instead of the default one. You can also change the default behavior of these classes by changing the parameters send to them.
Let's see some classic customizations you might want to do.
Cookie Name
To handle the user session, symfony uses a cookie. This cookie has a default
name of symfony
, which can be changed in factories.yml
. Under the all
key, add the following configuration to change the cookie name to jobeet
:
# apps/frontend/config/factories.yml storage: class: sfSessionStorage param: session_name: jobeet
Session Storage
The default session storage class is sfSessionStorage
. It uses the
filesystem to store the session information. If you have several web servers,
you would want to store the sessions in a central place, like a database
table:
# apps/frontend/config/factories.yml storage: class: sfPDOSessionStorage param: session_name: jobeet db_table: session database: propel db_id_col: id db_data_col: data db_time_col: time
Session Timeout
By default, the user session timeout if 1800
seconds. This can be changed by
editing the user
entry:
# apps/frontend/config/factories.yml user: class: myUser param: timeout: 1800
Logging
By default, there is no logging in the prod
environment because the logger
class name is sfNoLogger
:
# apps/frontend/config/factories.yml prod: logger: class: sfNoLogger param: level: err loggers: ~
You can for instance enable logging on the filesystem by changing the logger
class name to sfFileLogger
:
# apps/frontend/config/factories.yml logger: class: sfFileLogger param: level: error file: %SF_LOG_DIR%/%SF_APP%_%SF_ENVIRONMENT%.log
In the
factories.yml
configuration file,%XXX%
strings are replaced with their corresponding value from thesfConfig
object. So,%SF_APP%
in a configuration file is equivalent tosfConfig::get('sf_app')
in PHP code. This notation can also be used in theapp.yml
configuration file. It is very useful when you need to reference a path in a configuration file without hardcoding the path (SF_ROOT_DIR
,SF_WEB_DIR
, ...).
Deploying
What to deploy?
When deploying the Jobeet website to the production server, we need to be careful not to deploy unneeded files, or override files uploaded by our users, like the company logos.
In a symfony project, there are three directories to exclude from the
transfer: cache/
, log/
, and web/uploads/
. Everything else can be
transfered as is.
But for security reasons, you also don't want to transfer the "non-production"
front controllers, like the frontend_dev.php
and frontend_cache.php
scripts.
Deploying Strategies
In this section, we will assume that you have full control over the production server(s). If you can only access the server with a FTP account, the only deployment solution possible is to transfer all files every time you deploy.
The simplest way to deploy your website is to use the built-in project:deploy
task. It uses SSH
and rsync
to connect and transfer the files from a server
to another one.
The servers are to be configured in the config/properties.ini
configuration
file:
# config/properties.ini [production] host=www.symfony-project.com port=22 user=jobeet dir=/var/www/jobeet/ type=rsync pass=
To deploy to the newly configured production
server, use the
project:deploy
task:
$ php symfony project:deploy production
If you run this command, symfony will only simulate the transfer. To actually
deploy the website, add the --go
option:
$ php symfony project:deploy production --go
Even if you can provide the SSH password in the
properties.ini
file, it is better to configure your server with a SSH key to allow password-less connections.
By default, symfony won't transfer the directories we have talked about in the
previous section, nor it will transfer the dev
front controller script.
That's because the project:deploy
task exclude files and directories are
configured in the config/rsync_exclude.txt
file:
# config/rsync_exclude.txt
.svn
/web/uploads/*
/cache/*
/log/*
/web/*_dev.php
For Jobeet, we need to add the frontend_cache.php
file:
# config/rsync_exclude.txt
.svn
/web/uploads/*
/cache/*
/log/*
/web/*_dev.php
/web/frontend_cache.php
You can also create a
config/rsync_include.txt
file to force some files or directories to be transfered.
Even if the project:deploy
task is very flexible, you might want to
customize it even further. As deploying can be very different based on your
server configuration and topology , don't hesitate to extend the default task.
Each time you deploy a website to production, don't forget to at least clear the configuration cache on the production server:
$ php symfony cc --type=config
If you have changed some routes, you will also need to clear the routing cache:
$ php symfony cc --type=routing
Clearing the cache selectively allows to keep some parts of cache like the template one.
See you Tomorrow
The deployment of a project is the very last step of a symfony development life-cycle. It does not mean that you are done. This is quite the contrary. A website is something that has a life by itself. You will probably have to fix bugs and you will also want to add new features over time. But thanks to the symfony structure, and the tools at your disposal, upgrading your website is going to be simple, fast, and safe.
Tomorrow is already the last day of the Jobeet tutorial. It will be time to stand back a little, and have a look at what you learned during the twenty-three days of Jobeet.
Don't miss the last installment of Jobeet.
why isn't jobeet online @ www.jobeet.org ?
If you only have access by FTP you don't have to transfer everything each time. You can still using small tools like ftpsync.pl(http://ftpsync.sourceforge.net/) or weex(http://weex.sourceforge.net/) to transfer only what has changed.
@zero0x: Try http://localhost/ ;)
ha-ha
I thought it would be published by now..
I really wonder why the properties.ini file is in the ini format and not in the yaml one... and also why we cannot pass several hosts to deploy the project (or idally a selection of apps) on a servers pool
For the initial deployment of 2000 or 3000 files of a typical project, I like to do an svn export and pack it in an archive that I upload and extract on the server. Then use the deploy task for updates to the project.
Hello i have this problem when i typing "symfony project:deploy production"
Problem executing command Warning: Permanently added 'www.altrano.com,209.239.114.12' (RSA) to the list of known hosts. rsync: connection unexpectedly closed (0 bytes received so far) [sender] rsync error: error in rsync protocol data stream (code 12) at io.c(454) [sender=2.6.9]
what is going on??