18 Mar 2015 · Software Engineering

    Deploying PHP Applications with Rocketeer and Docker

    11 min read
    Contents

    Deploying web applications is an integral part of modern web development. From this need, many tools have emerged to make the process as easy as possible.

    Rocketeer is heavily influenced by Capistrano and Laravel’s principle of elegant code. It strives to make the deployment process accessible for everyone. We’ll see if it manages to do that. Rocketeer can also be used to deploy applications which are not written in PHP, so it is quite a versatile tool.

    We’ll deploy a fresh Laravel installation to a running Docker instance. This enables us to focus on setting up and using Rocketeer, while the Docker instance will act as a great environment to experiment with and an application server requiring zero configuration.

    Requirements

    A presumption is that you’re using a Linux environment for following along. This tutorial is written using Ubuntu 14.04 LTS, so you may need to adjust a few commands if you’re running a different Linux distribution.

    Installing Rocketeer

    Rocketeer can be installed in multiple ways, depending on your needs. The most straightforward way is to install it globally, so that it’s available regardless of your project’s configuration. A tight integration with Laravel is also available.

    $ wget http://rocketeer.autopergamene.eu/versions/rocketeer.phar
    $ chmod +x rocketeer.phar
    $ sudo mv rocketeer.phar /usr/local/bin/rocketeer

    Installation with Composer

    To install it as a global dependency with Composer, execute the following line:

    $ composer global require anahkiasen/rocketeer

    Setting Up the Application Server

    Rocketeer Deployment

    Using Your Own Server

    If you already have a fully configured server capable of running a Laravel application, you can skip the step of running a Docker instance. The things you will need are:

    • Knowledge to configure nginx for the example application
    • The IP address of the server
    • A new user on the server called rocketeer.

    Using Docker

    With using a Docker container as the deployment target, we have a disposable environment, which we can re-use or modify if anything goes wrong without messing up our local machine. The Docker image used in this tutorial is based on a slightly modified version of fideloper/docker-nginx-php.

    The information we need to provide Rocketeer includes the IP address of the server where we will deploy our application. This is a good time to fire up a Docker instance and get it’s address.

    $ docker run -ti -P -d ervin/webapp /sbin/my_init
    88dde8da5bc9d29e9aa6305c9a1346aa243f10f716ebeb9d96626e0c8465b409

    Copy the first few characters from the container ID, and use it as the last parameter for the next command.

    $ docker inspect -f "{{ .NetworkSettings.IPAddress  }}" 88dde8da
    172.17.0.3

    The last thing to do is to enable our local machine to connect to the server through SSH. A user named rocketeer with a password rocketeer was created on the server beforehand. We will use this user to perform our deploys.

    $ ssh-copy-id -i ~/.ssh/id_rsa.pub rocketeer@172.17.0.3
    
    INFO: attempting to log in with the new key(s), to filter out any that are already installed
    INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
    rocketeer@172.17.0.3's password: rocketeer
    
    Number of key(s) added: 1
    
    Now try logging into the machine, with:   "ssh 'rocketeer@172.17.0.3'"
    and check to make sure that only the key(s) you wanted were added

    When ssh-copy-id is successfully executed, our local machine is permanently authorized to access the application server through the SSH protocol.

    After this little detour and with the IP address in our hand, we can continue with setting up Rocketeer.

    Configuring Rocketeer

    Before getting into tweaking Rocketeer, let’s set up our project. If you don’t want to go through the process of creating a Laravel project and pushing it on GitHub, feel free to clone laravel-base-app.

    $ git clone https://github.com/ervinb/laravel-base-app.git webapp

    Enter the webapp directory which we created, and we’re ready to go. Rocketeer provides a wizard which helps you to configure the basics.

    $ rocketeer ignite
    
    No connections have been set, please create one: (production) staging
    No host is set for [staging/0], please provide one: 172.17.0.3
    No username is set for [staging/0], please provide one: rocketeer
    No password or SSH key is set for [staging/0], which would you use? (key) [key/password] <Enter>
    Please enter the full path to your key (/home/ervin/.ssh/id_rsa) <Enter>
    If a keyphrase is required, provide it: <Enter>
    No repository is set for [repository], please provide one:https://github.com/ervinb/laravel-base-app.git
    No username is set for [repository], please provide one: <Enter>
    No password is set for [repository], please provide one: <Enter>
    staging/0 | Ignite (Creates Rocketeer's configuration)
    What is your application's name ? (webapp) <Enter>
    The Rocketeer configuration was created at webapp/.rocketeer

    Rocketeer made good guesses for the most part and the only things we had to provide were the stage, the deploy server’s IP address, the repository and the user which is used on the deploy server. The configuration files are located in the .rocketeer folder in our project’s root.

    | webapp
    | .rocketeer
    | -- config.php
    | -- hooks.php
    | -- paths.php
    | -- remote.php
    | -- scm.php
    | -- stages.php
    | -- events.php
    | -- tasks.php
    | ...

    Let’s change the path where our code will live on the server. Open your favorite text editor and edit ./rocketeer/remote.php. The irrelevant content is not shown in the example below.

    <?php
    
    return [
        ...
        // Folders
        ////////////////////////////////////////////////////////////////////
    
        // The root directory where your applications will be deployed
        // This path *needs* to start at the root, ie. start with a /
        'root_directory' => '/home/rocketeer/',
        ...
    ];

    Next, verify that everything has been configured correctly.

    $ rocketeer check

    Deploying with Rocketeer

    At this point, we are actually ready to deploy for the very first time.

    $ rocketeer deploy

    This command will connect to the server, take care of the project’s dependencies and pull the project’s code from GitHub. This is the so called “pull deployment” when the code is fetched from a source code manager like GitHub. There’s also a paradigm called “push deployment” which includes building the project locally and pushing the package to the server. The latter is useful when the code has to be compiled to a binary before deployment.

    ...
    |=> Sharing file /home/rocketeer/webapp/releases/20150317101849//logs
    |=> Sharing file /home/rocketeer/webapp/releases/20150317101849//sessions
    $ rm -rf /home/rocketeer/webapp/current
    $ ln -s /home/rocketeer/webapp/releases/20150317101849 /home/rocketeer/webapp/current-temp
    $ mv -Tf /home/rocketeer/webapp/current-temp /home/rocketeer/webapp/current
    |=> Successfully deployed release 20150317101849
    | Cleanup (Clean up old releases from the server)
      |=> No releases to prune from the server
      Execution time: 215.5437s
      Saved logs to /home/ervin/webapp/.rocketeer/logs/staging--20150317.log

    Now, if you navigate to the IP address of the Docker instance in your browser, you should be greeted with Laravel’s welcome page. The deployment succeeded!

    Digging Deeper

    Naturally, we would like to have a bit more control over our deployment process, so let’s see what Rocketeer has to offer. We have a rough idea of what Rocketeer does: it places our code on a server. Let’s briefly glance over what’s under the hood.

    Folder Structure on the Server

    The folder structure of the application we’ve deployed to the Docker instance looks like this.

    | webapp
    | - current -> /home/rocketeer/webapp/releases/20150313121122/
    | - releases/
    | - shared/
    | - state.json
    • current points to the latest release of the application which is served when an incoming request comes.
    • releases contains a number of versions of the application, including the current one.
    • shared is used for storing configuration files which are shared between releases. This includes database configurations, API keys for third-party services used by the applications, and other files which are not version controlled by nature.

    This approach for the folder structure is influenced by Capistrano.

    Stages and Connections

    We can differentiate our deploys with connections. Connections provide us the means to define multiple servers for deployment. A common practice is to have staging servers and production servers. The staging servers are used by the developers for testing purposes, and production servers are where the application is accessible to the public. These different servers are configured in the connections array in .rocketeer/config.php.

    <?php
    
    return [
    ...
    // The default remote connection(s) to execute tasks on
    'default' => ['staging'],
    
    // The various connections you defined
    // You can leave all of this empty or remove it entirely if you don't want
    // to track files with credentials : Rocketeer will prompt you for your credentials
    // and store them locally
    'connections' => [
      'staging' => [
        'host'      => '172.17.0.3',
        'username'  => 'rocketeer',
        'password'  => '',
        'key'       => '/home/ervin/.ssh/id_rsa',
        'keyphrase' => '',
        'agent'     => '',
        'db_role'   => true,
      ],
    ],
    ...

    In our case, we only have one connection called staging. Stages on the other hand can be used to add another level of abstraction to the connection, which is useful if, for example, we want to have multiple testing environments on our staging server. They can be configured in the .rocketeer/stages.php file.

    Tasks

    Every command which is offered with Rocketeer’s command line interface is a pre-defined Task behind the scenes. Depending on their complexity, there are three types of tasks:

    • A single command which is executed in the current release of the application folder
    • A closure exposing Rocketeers helpers
    • A class which extends Rocketeer\Abstracts\AbstractTask. This is how custom Tasks are made.

    Tasks emit events, which can be used as triggers for starting other tasks. The most commonly used events are before and after. Tasks can be chained together using events in the .rocketeer/hooks.php file.

    <?php
    'after' => array(
        'setup' => array(
    
          // Commands
          'composer install',
    
          // Tasks classes
          'Rocketeer\Tasks\Cleanup',
    
          // Closures
          function($task) {
                $tests = $task->runForCurrentRelease('phpunit');
          }
    ?>

    Strategies

    We touched on the topic of stages earlier. They provide different contexts for our deployments. Following the same principle, we can add alternative behaviors to our tasks. The task will accomplish the same goal, but in a different context.

    $ rocketeer strategies
    
    +--------------+----------------+-------------------------------------------------------------+
    | Strategy     | Implementation | Description                                                 |
    +--------------+----------------+-------------------------------------------------------------+
    | check        | Php            | Checks if the server is ready to receive a PHP application  |
    | check        | Ruby           | Checks if the server is ready to receive a Ruby application |
    | check        | Node           | Checks if the server is ready to receive a Node application |
    | deploy       | Clone          | Clones a fresh instance of the repository by SCM            |
    

    In the above example, you can see that the check task has three different strategies. The mapping between tasks and strategies can be altered in the .rocketeer/strategies.php file. Opening it reveals which Tasks are triggered by the deploy and update commands among others. Strategies are not limited to the built-in ones. You can define your own strategies by extending the Rocketeer\Abstracts\AbstractStrategy class.

    Plugins

    Rocketeer’s functionalities can be broadened with plugins. They also provide a way to package your commonly used tasks so that they can be reused across projects.

    There is an interesting set of plugins for sending notifications to services like Slack, [Campfire] (https://campfirenow.com/) and HipChat. This is useful if you have a continuous deployment pipeline setup with a service like [Semaphore] (https://semaphoreci.com/) where the deployment happens automatically. You will get a message about every deploy right in your chatroom. Let’s see how to install the Slack plugin for example:

    rocketeer plugin:install anahkiasen/rocketeer-slack
    

    After the plugin is installed, we need to enable it in .rocketeer/config.php. The plugins array should contain a string describing the class of the plugin.

    <?php
    
    return [
      ...
      // The plugins to load
      'plugins' => [
        'Rocketeer\Plugins\Slack\RocketeerSlack',
      ],
      ...

    The next step is to configure the installed plugin. Publish the plugin’s configuration file with the following command:

    rocketeer plugin:config anahkiasen/rocketeer-slack

    The command will create a file .rocketeer/plugins/rocketeers/rocketeer-slack/config.php in which we enter the details about the Slack room which will receive the deploy messages. Finally, add 'Rocketeer\Plugins\Hipchat\RocketeerHipchatServiceProvider', as a provider to app/config/app.php as described on the plugin’s GitHub page.

    Wrapping it Up

    We saw how to deploy a simple PHP application to a Docker instance with only minimal amount of configuration. After a brief overview of Rocketeer’s features like Tasks and Strategies, we installed a plugin which notifies a Slack room when a deploy happens. Rocketeer has some advanced features which can be further explored on its documentation page. Along with the detailed description of all the features, you’ll also find some helpful examples.

    Leave a Reply

    Your email address will not be published. Required fields are marked *

    Avatar
    Writen by: