Laravel Envoy and Dynamic Resolution of AWS Server IPs

The issue is I have two main clients with two different AWS accounts, and they run the same web app, with slight variations, as well as a number of unique web apps. We also run lots of workers which may start up and shut down according to demand, and auto-scaling groups that do similar things.

Deploying to these has been a challenge, though I have been using fabric with awsfabrictasks to parse a search term for a server name (all servers are tagged appropriately), and cycle through each running server and deploy to each. It’s worked, but it’s always felt ugly having a non-PHP dependency in there.

So here’s the Envoy.blade.php equivalent. It needs improvement, but it does work:

@include('Envoy.target.php')

@setup

// Create the EC2Client
$params = [
    'region' => 'us-east-1',
    'version' => '2015-10-01',
];
// Use the local credentials if profile is provided
if (isset($profile)) {
    $params['profile'] = sprintf('profile %s', $profile);
}
$client = new \Aws\Ec2\Ec2Client($params);

// Set up filters for the instances
$filter = [
    'Filters' => [
        [
            'Name' => 'tag:Name',
            'Values' => [
                $target
            ]
        ],
        [
            'Name' => 'instance-state-name',
            'Values' => [
                'running'
            ]
        ]
    ]
];
$response = $client->describeInstances($filter);

// Parse the list of instances
$reservations = $response->get('Reservations');
foreach ($reservations as $reservation) {
    foreach ($reservation['Instances'] as $instance) {
        $servers[] = $instance['PrivateIpAddress'];
    }
}

// Inject to the container
$__container->servers(['servers' => $servers]);
@endsetup

@task('deploy', ['on' => 'servers'])
    cd {{ $webroot }}
    php artisan down
    git pull --no-edit
    composer install --no-dev -o
    php artisan up
    php artisan queue:restart
@endtask

@task('update-composer', ['on' => 'servers'])
    sudo composer self-update
@endtask

AWS IAM is used on the servers for various tasks so –profile should not be used when deploying from an AWS instance. For local deployment the credential files are at ~/.aws/credentials under a different [profile XXX] for each client, so –profile is required.

Also, SSH traffic for each VPC is routed through gateways that are only open to SSH from my IP and I route everything for the 172.*.*.* VPC subnets through the gateways, so the instance PrivateIpAddress is used here, whereas it’s probable that the PublicIpAddress should be used instead for most setups.

The actually deployment is quite simple, but anything can be done here. I’m planning to change to releases that can be rolled-back, but that’s something for another day.

The file Envoy.target.php provides the search term for the server (as $target):

<?php
// Set app webroot
$webroot = '/var/www/project';

// Set the search terms allowed for servers running this app
$target = '*MyProject*';

This isn’t very interesting, but for a standalone project it’s fine. It means the main Envoy.blade.php can be shared amongst projects, and different Envoy.target.php files can be created for more complex projects across a variety of named servers. I have one that has about 50 different servers that have different roles and so have different names. I want to keep the targets the same as I had for fabric so for now it’s something like:

<?php
// Set app webroot
$webroot = '/var/www/project';

// Set the search terms allowed for servers running this app
switch($target) {
    case 'xyz-all':
        $target = '*XYZ*';
        break;
    case 'xyz-web':
        $target = '*XYZ Web*';
        break;
    case 'xyz-worker':
        $target = '*XYZ Worker*';
        break;
    case 'xyz-db':
        $target = '*XYZ DB*';
        break;
    case 'abc-worker':
        $target = '*ABC Worker*';
        break;
    case 'abc-db':
        $target = '*ABC DB*';
        break;
    case 'abc-book':
        $target = '*ABC Book Scripts*';
        break;
    case 'abc-book-worker':
        $target = '*ABC Book Worker*';
        break;
    case 'abc-app':
        $target = '*ABC App Scripts*';
        break;
    case 'abc-app-worker':
        $target = '*ABC App Worker*';
        break;
    case 'abc-api':
        $target = '*ABC API*';
        break;
    default:
        $target = '*ABC Dev*';
}

This allows for options like ‘envoy run deploy –profile=xyz –target=xyz-worker’ when I don’t want to push a small code change that affects one group of servers to all of them.

This took a few hours today and it doesn’t look like there’s anything similar out there, so hopefully this will help someone out.

Leave a Comment


NOTE - You can use these HTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>