vendredi 7 octobre 2011

Deploying a Symfony2 project using Capistrano

Here is a tutorial to deploy a Symfony2 application on a remote server through github thanks to Capifony.

Thanks a lot to @qpleple for the help !

Get the stuff

Just in case you start from scratch on a mac OS X :
  1. Install Xcode from the Application Disc (install everything, including UNIX developer tools)
  2. Download & install Macports
  3. Install/update Ruby through macports :
    sudo port install ruby
  4. Install/update RubyGems
    sudo gem update --system
  5. Install Capifony
    sudo gem install capifony


Configure Capifony

Setup the capifony project as explained here.
Here is a sample (and more comprehensive) deploy.rb file (developed by @qpleple, annotated and reorganised by me) :
# ==============================================================================
#                                CONFIGURATION
# ==============================================================================

# ------------------------------------------------------------------------------
# 1. Servers configuration
# ------------------------------------------------------------------------------
# IP Adress of the servers to push the code to
set :domain,      "12.34.56.78"
set :application, "JohnDoeAppication"
set :serverName,  domain

# Front servers, nginx, HTTP server, Apache/etc
role :web,        domain
# Web server, where the code will go
role :app,        domain
# Database server       
role :db,         domain, :primary => true

set :deploy_to,   "/home/johndoe"

# ------------------------------------------------------------------------------
# 2. Security configuration
# ------------------------------------------------------------------------------
# Security : specification of the specific ssh key of the server
ssh_options[:keys] = %w(/Users/johndoe/.ssh/johndoe.pem)
ssh_options[:forward_agent] = true

# ------------------------------------------------------------------------------
# 3. Github and SCM configuration
# ------------------------------------------------------------------------------
# Repository path
set :repository,  "git@github.com:johndoe/Symfony.git"
# SCM type
set :scm,         :git
# Fetch only master branch
set :branch, "master"
# Use local keys to pull from github
set :scm_verbose, false
# Will keep a local git repo : only diffs will be uploaded
set :deploy_via, :remote_cache
# The number of releases which will remain on the server
set  :keep_releases,  3
# Automates the backup process
after "deploy:update", "deploy:cleanup" 

# ------------------------------------------------------------------------------
# 4. Project configuration
# ------------------------------------------------------------------------------
# Set some paths to be shared between versions : images, config..
set :shared_files,    ["app/config/parameters.ini"]
set :shared_children, [app_path + "/logs", web_path + "/uploads", "vendor"]
# Don't check vendors after each deploy : it has to be done manually
set :update_vendors, false

# ------------------------------------------------------------------------------
# 5. Capifony parameters
# ------------------------------------------------------------------------------
set :user,       "root"
set :use_sudo,   false
set :app_path,    "app"


after "deploy" do
    # Make app.php the front controller (and not app_dev.php)
    # in order not to have to change the htaccess manualyy everytime
    run "sed -i 's/app_dev/app/' #{deploy_to}/current/web/.htaccess"
end


# ==============================================================================
#                                     TASKS
# ==============================================================================

# ------------------------------------------------------------------------------
# Automated connection to the server
# ------------------------------------------------------------------------------

task :ssh do
    # To ssh into the prod server
    system("chmod 600 #{ssh_options[:keys]}")
    system("ssh -A -i #{ssh_options[:keys]} root@#{serverName}")
end

# ------------------------------------------------------------------------------
# First deploy
# ------------------------------------------------------------------------------

task :firstdeploy do
    # Make task fail if it is not the first deploy
    run "[ -d #{deploy_to} ] && exit 1 || echo 'ok'"
    
    # Install missing packages:
    # git-core: to get the code from github
    # php5-mysql: to make php able to talk to mysql (mysqli, pdo_mysql)
    # php5-sqlite: required by the profiler
    # php5-curl: CURL library
    run "apt-get install --yes git-core php5-mysql php5-sqlite php5-curl"
    
    # Activate url rewriting apache module
    run "a2enmod rewrite"
    
    # Create directory arborescence : creation of a shared folder, etc...
    deploy.setup
    
    # Permissions
    # Create new permission group
    run "egrep -i '^www-pub' /etc/group > /dev/null || groupadd www-pub"
    # Add the www-data user to the www-pub group
    run "usermod -a -G www-pub www-data"
    # Add the root user to the www-pub group
    run "usermod -a -G www-pub root"
    # Transfers the ownership of Home to the www-pub group
    run "chown -R root:www-pub #{deploy_to}"
    # Setup permissions for files and folders
    run "find #{deploy_to} -type d -exec chmod 2775 {} ;"
    run "find #{deploy_to} -type f -exec chmod 0664 {} ;"
    
    # Symlink from apache root dir to app web dir
    # run "rm -rf /var/www"
    # run "ln -s #{deploy_to}/current/web /var/www"

    set :mysql_password, Capistrano::CLI.ui.ask("Enter MySQL database password: ")

    # File parameters.ini
    template = <<-EOF
[parameters]
    database_driver="pdo_mysql"
    database_host="localhost"
    database_name="JohnDoeDatabase"
    database_user="root"
    database_password = #{mysql_password}
    mailer_transport="smtp"
    mailer_host="localhost"
    mailer_user=""
    mailer_password=""
    locale="en"
    secret            = #{Capistrano::CLI.ui.ask("Secret token: ")}
EOF
    run "mkdir -p #{shared_path}/app/config"
    put ERB.new(template).result(binding), "#{shared_path}/app/config/parameters.ini"

    # Tell Capistrano to install vendors 
    set :update_vendors, true
    
    # Make a new release, fetch code, install vendors
    deploy.default
    
    # Set mysql root password
    run "mysqladmin -u root password #{mysql_password} || echo 'password already set'"
    # Create database
    run "echo 'CREATE DATABASE JohnDoeDatabase CHARACTER SET utf8 COLLATE utf8_general_ci' | mysql -p#{mysql_password}"
    # Create schema
    symfony.doctrine.schema.create
    
end

# ------------------------------------------------------------------------------
# Clear distant cache
# ------------------------------------------------------------------------------

task :cc do
    run "rm -rf #{deploy_to}/current/app/cache"
end


Setup your server

You first have to make your server able to connect to the github repository : github has to be added to the know hosts list of the server. In order to do so :
  1. Log into server in command line with the handy shortcut defined in deploy.rb :
    cap ssh 
  2. Connect to github
    ssh git@github.com
  3. Quit server


Setup your computer

When you will try to push code to the server, the github server will follow the ssh challenge to the computer from which you are pushing. You thus have to have your id_rsa identity loaded in the ssh-agent of your computer at this time. You can display what are the currently loaded identities with :
ssh-add -l
If the key is missing :
ssh-add ~/.ssh/id_rsa
This has to be done after every reboot.


Deploy

Launch first deploy :
cap firstdeploy

If it crashes at some point :
  1. Remove everything on the server :
    cap ssh
    rm -rf /home/johndoe
  2. Reinitialize the database
    echo "drop database DatabaseName;" | mysql -u root



You are done !

Some stuff is probably missing, do not hesitate to comment below.


References

1 commentaire: