Skip to main content

Magento 2 Cluster Deployments With Deployer

Mar 03 '17

You've completed building your gorgeous new Magento 2 commerce site (or maybe your small site has grown to be a big success), and now you need to deploy your site to a server cluster to handle the traffic from all of your satisfied customers.

Of course a fully managed hosting platform will generally provide tooling or processes for code deployments, but many customers prefer the flexibility and control of dedicated or self managed hardware. In this case, a deployment strategy needs to be designed to install and update the application on the production hardware (after, of course, QA and automated tests have completed on your staging environments).

There are many strategies to choose from of varying complexity, ranging from homebrew shell scripts to a full continuous integration system. Deployer falls somewhere in the middle, and is a good choice for controlled manual deployments of PHP applications. If you're familiar with tools like Capistrano then Deployer should be pretty easy to work with, as it offers the same functionality but is implemented and configured using PHP.

Server Configuration

Configuring the nodes and services of your cluster is beyond the scope of this article, but there are a couple of configuration steps to set up on the target servers to smooth the deployment process:

  • SSH KEY AUTH - You'll be connecting to all of your cluster nodes repeatedly with SSH to run deployments, so you'll want to setup key-based authentication so you can connect without typing passwords.
  • SSH KEY FORWARDING - Each deployment target will need to be able to clone the code repository from a remote server. If you're using a private GitHub repo you'll need to setup public key access to it and make sure your SSH client forwards that key to the deployment targets.

    Server Definitions

    After installing Deployer on your local development machine/VM, our first step is to layout a server definition file in YAML which contains information and variables about the cluster nodes. In this example we have two web heads and a master DB server:

    prod_appmaster:
      host: myserver1.example.com
      user: myuser
      identity_file: ~
      stage: production
      deploy_path: /var/www/mysite
      web_host: mystore.example.com
      os_user: myuser
      http_user: apache
      db_host: mydbserver.example.com
      db_name: mydb
      db_user: mydbuser
      db_password: mydbpass
    
    prod_appslv1:
      host: myserver2.example.com
      user: myuser
      identity_file: ~
      stage: production
      deploy_path: /var/www/mysite
      web_host: mystore.example.com
      os_user: myuser
      http_user: apache
      db_host: mydbserver.example.com
      db_name: mydb
      db_user: mydbuser
      db_password: mydbpass
      
    prod_dbmaster:
      host: mydbserver.example.com
      user: myuser
      identity_file: ~
      stage: production
      deploy_path: /home/myuser/mysite
      os_user: myuser
      http_user: apache
      db_host: localhost
      db_name: mydb
      db_user: mydbuser
      db_password: mydbpass

    Most of these settings are pretty self-explanatory, but further information is available in the Deployer documentation.

    Task Definitions

    The real heart of a deployment tool like Deployer is the task definitions you setup to run the tasks particular to your application setup procedure. Here we'll setup a task file called cluster_install.php for doing an initial install of or Magento environment. Typically a very similar task can be used for further updates by replacing the install tasks with one to run magento setup:upgrade and additional update commands.

    namespace Deployer;
    require 'recipe/common.php';
     
    // Configuration
    set('ssh_type', 'native');
    set('ssh_multiplexing', true);
    set('repository', 'repository.git');
    set('branch', 'master');
    set('base_unsecure_url', 'http://{{web_host}}');
    set('base_secure_url', 'https://{{web_host}}');
     
    set('shared_files', [
      'magento/app/etc/env.php',
      'magento/var/.maintenance.ip',
    ]);
     
    set('shared_dirs', [
      'magento/var/log',
      'magento/var/backups',
      'magento/pub/media',
    ]);
     
    set('writable_dirs', [
      'magento/var',
      'magento/pub/static',
      'magento/pub/media',
    ]);
     
    set('clear_paths', [
      'magento/var/generation/*',
      'magento/var/cache/*',
    ]);
     
    // Servers
    serverList('servers.yml');
     
    // Tasks
    desc('Installing vendors');
    task('deploy:vendors', function () {
      run('cd {{release_path}}/magento && {{env_vars}} {{bin/composer}} {{composer_options}}');
    })->onlyOn(['prod_appmaster', 'prod_appslv1']);
     
    desc('Clean / create DB');
    task('deploy:clean_db', function() {
      run('mysql -u{{db_user}} -p{{db_password}} -e "DROP DATABASE {{db_name}}"');
      run('mysql -u{{db_user}} -p{{db_password}} -e "CREATE DATABASE {{db_name}}"');
    })->onlyOn(['prod_dbmaster']);
     
    desc('Install Magento');
    task('deploy:install_magento', function () {
      run('chmod +x {{release_path}}/magento/bin/magento');
      run('{{release_path}}/magento/bin/magento setup:install --base-url={{base_unsecure_url}} --db-host={{db_host}} --db-name={{db_name}} --db-user={{db_user}} --db-password={{db_password}} --admin-firstname=Test --admin-lastname=User [email protected] --admin-user=testuser --admin-password=mypassword --language=en_US --currency=USD --timezone=America/New_York --cleanup-database --use-rewrites=1 --backend-frontname=admin_foo');
    })->onlyOn(['prod_appmaster', 'prod_appslv1']);
     
    desc('Data import and setup');
    task('deploy:data_import', function () {
      // Any additional data import / setup tasks here
    })->onlyOn(['prod_appmaster']);
     
    desc('Clean Magento Cache.');
    task('deploy:cache_clean', function() {
      cd('{{release_path}}/magento');
      run('bin/magento cache:clean');
    })->onlyOn(['prod_appmaster', 'prod_appslv1']);
     
    desc('Build indexes');
    task('deploy:build_indexes', function () {
      cd('{{release_path}}/magento');
      run('bin/magento indexer:reindex');
    })->onlyOn(['prod_appmaster']);
     
    desc('Set Magento production mode');
    task('deploy:set_production_mode', function() {
      run('{{release_path}}/magento/bin/magento deploy:mode:set production');
    })->onlyOn(['prod_appmaster', 'prod_appslv1']);
     
    desc('Deploy new installation.');
    task('deploy', [
      'deploy:prepare',
      'deploy:lock',
      'deploy:release',
      'deploy:clean_db',
      'deploy:update_code',
      'deploy:shared',
      'deploy:writable',
      'deploy:vendors',
      'deploy:clear_paths',
      'deploy:install_magento',
      'deploy:data_import',
      'deploy:build_indexes',
      'deploy:cache_clean',
      'deploy:set_production_mode',
      'deploy:symlink',
      'deploy:unlock',
      'cleanup',
      'success'
    ]);

    This is a basic task file for running a fresh Magento install. This file uses test/sample values and should be adjusted for your admin credentials and other information (or those can be injected as variables as well).

    Run The Deployment

    From a command line in the directory with your task / server files, you should now be able to run your deployment:

    dep -f=cluster_install.php deploy production

    If all goes well, your Magento site should now be setup and running properly on the cluster! Time to celebrate!

    In reality it will probably take a few tries to get everything setup and configured properly for your specific application. If a deployment fails for some reason halfway through you may need to connect to the server via SSH to determine what went wrong by running the offending command manually. Running Deployer in verbose mode (with flag -vvv) will display the exact commands as they are running, which is very handy for debugging. Deployer also uses a locking system to ensure two deployments aren't attempted simultaneously, so you may also need to manually "unlock" your deployments before you try again:

    dep -f=cluster_install.php deploy:unlock production

    Conclusion

    Deployer works quite well for manual management of Magento 2 application deployments, and is flexible enough to support many different custom use cases as well. Building solid and repeatable deployment tooling will pay off in the long run.

  • Read more about: