Skip to content
This repository has been archived by the owner on Mar 18, 2024. It is now read-only.

Latest commit

 

History

History
executable file
·
434 lines (350 loc) · 13.4 KB

configuration.md

File metadata and controls

executable file
·
434 lines (350 loc) · 13.4 KB

Configuration

Deployment Strategies

There are a lot of different ways to deploy a Laravel application. This bundle provides two deployers that implement different strategies:

  • Default Deployer, it's the same zero-downtime strategy implemented by tools like Capistrano, Capifony and Deployer PHP.
  • Custom Deployer, it's a strategy that assumes nothing about how you want to deploy your application. It's similar to Python's Fabric tool, so it's just an SSH toolkit instead of a deployer.

There are plans to add more deployment strategies in the future. Open issues to ask for new strategies or vote for the existing issues so we can make better decisions about what to implement next.

Configuration Files

Artisan Deployer uses plain PHP files to configure the deployment process. In other words, you don't have to learn any special syntax and you won't face any of the problems and constraints imposed by XML, JSON and YAML files.

The first time you run the deploy command in a Laravel application, an initial config file is created for you. Go ahead, run deploy and open the generated config file. This is what you'll see:

// config/deploy_prod.php
use Bencagri\Artisan\Deployer\Deployer\DefaultDeployer;

return new class extends DefaultDeployer
{
    public function configure()
    {
        return $this->getConfigBuilder()
            ->server('user@hostname')
            ->deployDir('/var/www/laravel-demo')
            ->repositoryUrl('[email protected]:my-username/laravel-demo.git')
            ->repositoryBranch('master')
        ;
    }
};

The configuration file must return a PHP class extending the base class that corresponds to the deployment strategy used by your application. In PHP 7 this is easy because you can create anonymous classes (new class extends ...).

Then, configure the deployment process using the config builder object given to you in the configure() method. Each config builder is unique for the deployment strategy, so your IDE will only autocomplete the options available.

That's the best part of using a PHP config file. You don't have to read any docs, you don't have to learn any syntax, you don't have to memorize any option, you don't have to check deprecated/new options. All the available and updated config options are given to you by the IDE autocompletion. Simple, smart, and convenient.

Common Configuration Options

Most config options depend on the strategy used, but there are some options common to all of them.

SSH Agent Forwarding

SSH agent forwarding allows remote servers to use your local SSH keys. This lets remote servers fool other services and make them believe that is your local machine which is accessing those services.

This option is enabled by default, but some people consider it harmful, so you can disable it as follows:

public function configure()
{
    return $this->getConfigBuilder()
        ->useSshAgentForwarding(false)
        // ...
    ;
}

Server Configuration

This is the most important option and it defines the SSH connection credentials for all the servers involved in the deployment process. For simple applications where you only have one server, you'll define something like this:

public function configure()
{
    return $this->getConfigBuilder()
        ->server('user@hostname')
        // ...
    ;
}

The value of the server() option can be any string used to connect to the server via SSH (anything that you may type in the ssh ... console command):

// hostname (IP) and no user ('root' will be used)
->server('123.123.123.123')

// user + hostname
->server('[email protected]')

// user + host name + custom SSH port (default port: 22)
->server('[email protected]:22123')

// no user or hostname/IP (credentials will be read from ~/.ssh/config file)
->server('production')

Multiple Servers

If your application is deployed to several servers, add the server() option for each of those servers:

public function configure()
{
    return $this->getConfigBuilder()
        ->server('deployer@hostname1')
        ->server('deployer@hostname2')
        // ...
    ;
}

Server Roles

By default, all configured servers are treated as the server where the Laravel app is deployed. However, for complex apps you may have servers with different responsibilities (workers, database servers, etc.).

These responsibilities are called roles. There is one reserved role called app which is applied by default to all servers. You can define as many custom roles as needed passing an array with the role names as the second argument of the server() option:

public function configure()
{
    return $this->getConfigBuilder()
        ->server('deployer@hostname1') // this server uses the default 'app' role
        ->server('deployer@hostname2', ['workers', 'worker-1'])
        ->server('deployer@hostname3', ['workers', 'worker-2'])
        ->server('deployer@hostname4', ['database'])
        // ...
    ;
}

Later, these role names will let you run some deployment commands on specific servers. When using custom roles, don't forget to add the app role to those servers where the Laravel applications is deployed. For example, if you use the blue/green deployment strategy, add the app role in addition to the blue and green ones:

public function configure()
{
    return $this->getConfigBuilder()
        ->server('deployer@hostname1', ['app', 'blue'])
        ->server('deployer@hostname2', ['app', 'green'])
        // ...
    ;
}

Server Properties

These properties are custom configuration options defined for a particular server. You can define them as an associative array passed as the third argument of the server() option. Later you'll see how to use these properties inside the commands executed on any server:

public function configure()
{
    return $this->getConfigBuilder()
        ->server('deployer@hostname1', ['app'], ['token' => '...'])
        ->server('deployer@hostname2', ['database'], ['use-lock' => false])
        // ...
    ;
}

Common Hooks

The commands executed during a deployment and their order depends on the deployer used. However, all deployers include hooks that let you execute your own commands before, after or in the middle of that deployment flow. Technically these hooks are methods of the PHP class used to define the deployment.

Each deployer defines its own hooks, but all of them define these common hooks:

use Bencagri\Artisan\Deployer\Deployer\DefaultDeployer;

return new class extends DefaultDeployer
{
    public function configure()
    {
        // ...
    }


    public function beforeStartingDeploy()
    {
        // Deployment hasn't started yet, so here you can execute commands
        // to prepare the application or the remote servers
    }

    public function beforeFinishingDeploy()
    {
        // Deployment has finished but the deployer hasn't finished its
        // execution yet. Here you can run some checks in the deployed app
        // or send notifications.
    }

    public function beforeCancelingDeploy()
    {
        // An error happened during the deployment and remote servers are
        // going to be reverted to their original state. Here you can perform
        // clean ups or send notifications about the error.
    }


    public function beforeStartingRollback()
    {
        // Rollback hasn't started yet, so here you can execute commands
        // to prepare the application or the remote servers.
    }

    public function beforeCancelingRollback()
    {
        // An error happened during the rollback and remote servers are
        // going to be reverted to their original state. Here you can perform
        // clean ups or send notifications about the error.
    }

    public function beforeFinishingRollback()
    {
        // Rollback has finished but the deployer hasn't finished its
        // execution yet. Here you can run some checks in the reverted app
        // or send notifications.
    }
};

Common Methods

In addition to the common config options and hooks, every deployer has access to some common methods that are useful to deploy and roll back the application.

runLocal() Method

Executes the given shell command on the local computer. The working directory of the command is set to the local project root directory, so you don't have to add a cd command before the command:

public function beforeStartingDeploy()
{
    $this->runLocal('./vendor/bin/simple-phpunit');
    // equivalent to the following:
    // $this->runLocal('cd /path/to/project && ./vendor/bin/simple-phpunit');
}

If the deployer allows to configure the Laravel environment, it is automatically defined as an env var before executing the command:

public function beforeStartingDeploy()
{
    $this->runLocal('./php artisan app:optimize-for-deploy');
    // equivalent to the following:
    // $this->runLocal('APP_ENV=prod ./php artisan app:optimize-for-deploy');
}

If you need to change the Laravel environment for some command, add the --env option to the command, because it has preference over the env vars:

public function beforeStartingDeploy()
{
    $this->runLocal('./php artisan app:optimize-for-deploy --env=dev');
    // equivalent to the following (--env=dev wins over APP_ENV=prod):
    // $this->runLocal('APP_ENV=prod ./php artisan app:optimize-for-deploy --env=dev');
}

The runLocal() method returns an immutable object of type TaskCompleted which contains the command exit code, the full command output and other utilities:

public function beforeStartingDeploy()
{
    $result = $this->runLocal('./php artisan app:optimize-for-deploy');
    if (!$result->isSuccessful()) {
        $this->notify($result->getOutput());
    }
}

runRemote() Method

Executes the given shell command on one or more remote servers. By default, remote commands are executed only on the servers with the role app. Pass an array of role names to execute the command on the servers that contain any of those roles:

public function beforeFinishingDeploy()
{
    $this->runRemote('./php artisan app:generate-xml-sitemap');
    $this->runRemote('/user/deployer/backup.sh', ['database']);
    $this->runRemote('/user/deployer/scripts/check.sh', ['app', 'workers']);
}

The working directory of the command is set to the remote project root directory, so you don't have to add a cd command to that directory:

public function beforeFinishingDeploy()
{
    $this->runRemote('./php artisan app:generate-xml-sitemap');
    // equivalent to the following:
    // $this->runRemote('cd /path/to/project && ./php artisan app:generate-xml-sitemap');
}

If the deployer allows to configure the Laravel environment, it is automatically defined as an env var before executing the command:

public function beforeFinishingDeploy()
{
    $this->runRemote('./php artisan migrate');
    // equivalent to the following:
    // $this->runRemote('APP_ENV=prod ./php artisan migrate');
}

If you need to change the Laravel environment for some command, add the --env option to the command, because it has preference over the env vars:

public function beforeFinishingDeploy()
{
    $this->runRemote('./php artisan app:generate-xml-sitemap --env=dev');
    // equivalent to the following (--env=dev wins over APP_ENV=prod):
    // $this->runRemote('APP_ENV=prod ./php artisan app:generate-xml-sitemap --env=dev');
}

The runRemote() method returns an array of immutable objects of type TaskCompleted which contains the exit code, the full command output and other utilities for the execution of the command on each server:

public function beforeFinishingDeploy()
{
    $results = $this->runRemote('./php artisan app:generate-xml-sitemap');
    foreach ($results as $result) {
        $this->notify(sprintf('%d sitemaps on %s server', $result->getOutput(), $result->getServer()));
    }
}

log() Method

This method appends the given message to the log file generated for each deployment. If the deployment/rollback is run with the -v option, these messages are displayed on the screen too:

public function beforeFinishingDeploy()
{
    $this->log('Generating the Google XML Sitemap');
    $this->runRemote('./php artisan app:generate-xml-sitemap');
}

Command Properties

If you defined custom properties when configuring a server, you can use those inside a shell command with the {{ property-name }} syntax. For example, if you defined these servers:

public function configure()
{
    return $this->getConfigBuilder()
        ->server('deployer@hostname1', ['app'], ['token' => '...'])
        ->server('deployer@hostname2', ['database'], ['use-lock' => false])
        // ...
    ;
}

Those properties can be part of any command run on those servers:

public function beforeFinishingDeploy()
{
    $this->runRemote('./php artisan app:generate-xml-sitemap --token={{ token }}');
    $this->runRemote('/user/deployer/backup.sh --lock-tables={{ use-lock }}', ['database']);
}