Skip to content

Deploy with Elastic Beanstalk

JP Barbosa edited this page Jul 25, 2015 · 33 revisions

Deploy with Elastic Beanstalk

Add AWS SDK
composer require aws/aws-sdk-php-laravel
Create MySql database config to allow RDS env variables
nano config/database.php
        ...
        'mysql_eb' => [
            'driver'    => 'mysql',
            'host'      => env('RDS_HOSTNAME', 'localhost'),
            'database'  => env('RDS_DB_NAME', 'ebdb'),
            'username'  => env('RDS_USERNAME', 'ebroot'),
            'password'  => env('RDS_PASSWORD', ''),
            'charset'   => 'utf8',
            'collation' => 'utf8_unicode_ci',
            'prefix'    => '',
            'strict'    => false,
        ],
        ...
Change queue config to allow SQS env variables
nano config/queue.php
        ...
        'sqs' => [
            'driver' => 'sqs',
            'key'    => env('SQS_PUBLIC_KEY', 'your-public-key'),
            'secret' => env('SQS_SECRET_KEY', 'your-secret-key'),
            'queue'  => env('SQS_URL', 'your-queue-url'),
            'region' => env('SQS_REGION', 'us-east-1'),
        ],
        ...
Create PushedSqsQueue class to handle SQS
mkdir app/Queue
nano app/Queue/PushedSqsQueue.php
<?php

namespace App\Queue;

use App;
use Queue;
use Request;

class PushedSqsQueue
{

    public function marshal()
    {
        try {
            $this->createPushedSqsJob()->fire();
        } catch (Exception $e) {
            Log::error('[SQS-JOB] Error: ' . $e->getMessage());
            return response($e->getMessage(), 500);
        }
        return response('OK');
    }

    protected function createPushedSqsJob()
    {
        $queue = Queue::connection();
        $jobData = ['Body' => json_encode(Request::all())];
        return new PushedSqsJob(App::getFacadeRoot(), $queue->getSqs(), $queue, $jobData);
    }
}
Override SqsJob to not delete jobs because SQS already do that
nano app/Queue/PushedSqsJob.php
<?php

namespace App\Queue;

use Illuminate\Queue\Jobs\SqsJob;

class PushedSqsJob extends SqsJob
{
    public function delete()
    {
        $this->deleted = true;
    }
}
Add route for queue
nano app/Http/routes.php
use App\Queue\PushedSqsQueue;
...
Route::post('queue/receive', function () {
    return (new PushedSqsQueue)->marshal();
});
Temp disable VerifyCsrfToken
nano app/Http/Kernel.php
//\App\Http\Middleware\VerifyCsrfToken::class,
Create dir to store Elastic Beanstalk extensions
mkdir .ebextensions
Download Papertrail config and set version, files, hostname, host and port
wget https://gist.githubusercontent.com/leonsodhi/8eb28e06b2c35c136bf8/raw/6e6832c1328e04e4530e51cc6cf2d09b79d0563c/01papertrail.config -O .ebextensions/01-papertrail.config
nano .ebextensions/01-papertrail.config
sources:
  /home/ec2-user: https://github.com/papertrail/remote_syslog2/releases/download/v0.14/remote_syslog_linux_amd64.tar.gz

files:
  "/etc/log_files.yml":
    mode: "00644"
    owner: root
    group: root
    encoding: plain
    content: |
      files:
        - /var/log/aws-sqsd/default.log
        - /var/log/eb-activity.log
        - /var/log/eb-commandprocessor.log
        - /var/log/eb-version-deployment.log
        - /var/log/httpd/error_log
        - /var/log/messages
      hostname: laravel-apz
      destination:
        host: <YOUR-LOG-DESTINATION>
        port: <YOUR-PORT-NUMBER>
        protocol: tls
...
Basic config with static env variables
nano .ebextensions/02-option-settings.config
option_settings:
  - namespace: aws:elasticbeanstalk:container:php:phpini
    option_name: document_root
    value: '/public'
  - namespace: aws:elasticbeanstalk:container:php:phpini
    option_name: composer_options
    value: '--no-dev'
  - namespace: aws:elasticbeanstalk:sqsd
    option_name: HttpPath
    value: '/queue/receive'
  - option_name: APP_DEBUG
    value: false
  - option_name: APP_ENV
    value: production
  - option_name: APP_LOG
    value: syslog
  - option_name: CACHE_DRIVER
    value: file
  - option_name: DB_CONNECTION
    value: mysql_eb
  - option_name: MAIL_DRIVER
    value: smtp
  - option_name: MAIL_ADDRESS
    value: equipe@jp7.com.br
  - option_name: MAIL_NAME
    value: "Laravel Apz"
  - option_name: QUEUE_DRIVER
    value: sqs
  - option_name: REDIS_HOST
    value: AWS_REDIS_HOST
  - option_name: SESSION_DRIVER
    value: file
Pre build scripts
nano .ebextensions/03-install-nodejs.config
commands:
  01getNodeRepo:
    command: "curl --silent --location https://rpm.nodesource.com/setup | bash -"
  02installNode:
    command: "yum install -y nodejs"
  03updateNpm:
    command: "npm install npm -g"
  04enableSudo:
    command: "echo Defaults:root \\!requiretty >> /etc/sudoers"
Post build scripts
nano .ebextensions/04-deploy-script.config
container_commands:
  01artisanMigrate:
    command: "php artisan migrate --force"
  02npmInstall:
    command: "sudo npm install"
  03bowerInstall:
    command: "sudo ./node_modules/.bin/bower install --allow-root"
  04gulp:
    command: "sudo ./node_modules/.bin/gulp --production"
Install Elastic Beanstalk (EB) CLI and AWS CLI
brew install aws-elasticbeanstalk awscli
Init new EB application
eb init laravel-apz --platform php5.5
Add Elastic Beanstalk config to Git
git add .
git commit -m "Add config to deploy with Elastic Beanstalk"
Export APP_KEY
export RDS_PASSWORD=MY_RDS_PASSWORD
source .env
Create web instance
eb create laravel-apz-web \
   --cname laravel-apz \
   --instance_type t1.micro \
   --single \
   --instance_profile aws-elasticbeanstalk-ec2-role \
   --database \
   --database.username ebroot \
   --database.password $RDS_PASSWORD \
   --keyname KEYNAME \
   --envvars APP_KEY=$APP_KEY
Get RDS_HOSTNAME
export RDS_INSTANCE=`aws elasticbeanstalk describe-events \
       --application-name laravel-apz \
       --environment-name laravel-apz-web \
       --output text \
       | grep -m 1 'Created RDS database name' | awk '{print $9}'`
export RDS_HOSTNAME=`aws rds describe-db-instances \
       --db-instance-identifier $RDS_INSTANCE \
       --output text \
       | awk '$1 == "ENDPOINT"' | awk '{print $2}'`
Create worker instance
eb create laravel-apz-worker \
   --instance_type t1.micro \
   --single \
   --tier worker \
   --instance_profile aws-elasticbeanstalk-ec2-worker-role \
   --keyname KEYNAME \
   --envvars APP_KEY=$APP_KEY,RDS_HOSTNAME=$RDS_HOSTNAME,RDS_PASSWORD=$RDS_PASSWORD
Get worker SQS_URL, SQS_DEAD_LETTER_URL and AWS_ACCOUNT_ID
export SQS_URL=`aws elasticbeanstalk describe-environment-resources \
       --environment-name laravel-apz-worker \
       --output text \
       | awk '$2 == "WorkerQueue"' | awk '{print $3}'`
export SQS_NAME=`echo $SQS_URL | awk -F '/' '{print $5}'`
export SQS_USER=MY_IAM_SQS_USER
export SQS_DEAD_LETTER_URL=`aws elasticbeanstalk describe-environment-resources \
       --environment-name laravel-apz-worker \
       --output text \
       | awk '$2 == "WorkerDeadLetterQueue"' | awk '{print $3}'`
export SQS_DEAD_LETTER_NAME=`echo $SQS_DEAD_LETTER_URL | awk -F '/' '{print $5}'`
export AWS_ACCOUNT_ID=`echo $SQS_URL | awk -F '/' '{print $4}'`
Get worker security group and add it to web RDS
# eb config laravel-apz-worker
export EB_WORKER_SECURITY_GROUP=`aws elasticbeanstalk describe-configuration-settings \
       --application-name laravel-apz-jp \
       --environment-name laravel-apz-worker \
       --output text \
       | awk '$3 == "SecurityGroups"' | awk '{print $5}'`
export RDS_SECURITY_GROUP=`aws rds describe-db-instances \
       --db-instance-identifier $RDS_INSTANCE \
       --output text \
       | awk '$1 == "DBSECURITYGROUPS"' | awk '{print $2}'`
aws rds authorize-db-security-group-ingress \
    --db-security-group-name $RDS_SECURITY_GROUP \
    --ec2-security-group-owner-id $AWS_ACCOUNT_ID \
    --ec2-security-group-name $EB_WORKER_SECURITY_GROUP
Add permissions to SQS user in both SQS (queue and dead)
export SQS_POLICY_ID=my_uniq_label_for_sqs
aws sqs add-permission \
    --queue-url $SQS_URL \
    --label $SQS_POLICY_ID \
    --aws-account-ids "arn:aws:iam::$AWS_ACCOUNT_ID:user/$SQS_USER" \
    --actions "*"
aws sqs set-queue-attributes \
    --queue-url $SQS_URL \
    --cli-input-json '{"Attributes": {"Policy": "{\"Version\":\"2012-10-17\",\"Id\":\"arn:aws:sqs:us-east-1:'$AWS_ACCOUNT_ID':'$SQS_NAME'/SQSDefaultPolicy\",\"Statement\":[{\"Sid\":\"'$SQS_POLICY_ID'\",\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::'$AWS_ACCOUNT_ID':user/'$SQS_USER'\"},\"Action\":\"SQS:*\",\"Resource\":\"arn:aws:sqs:us-east-1:'$AWS_ACCOUNT_ID':'$SQS_NAME'\"}]}"}}'
aws sqs set-queue-attributes \
    --queue-url $SQS_DEAD_LETTER_URL \
    --cli-input-json '{"Attributes": {"Policy": "{\"Version\":\"2012-10-17\",\"Id\":\"arn:aws:sqs:us-east-1:'$AWS_ACCOUNT_ID':'$SQS_DEAD_LETTER_NAME'/SQSDefaultPolicy\",\"Statement\":[{\"Sid\":\"'$SQS_POLICY_ID'\",\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::'$AWS_ACCOUNT_ID':user/'$SQS_USER'\"},\"Action\":\"SQS:*\",\"Resource\":\"arn:aws:sqs:us-east-1:'$AWS_ACCOUNT_ID':'$SQS_DEAD_LETTER_NAME'\"}]}"}}'
Create and load environment variables for Elastic Beanstalk
cp .env .env.production.eb
nano .env.production.eb
...
APP_URL=http://laravel-apz.domain
HOST_NAME=laravel-apz.domain

SQS_PUBLIC_KEY=
SQS_SECRET_KEY=
SQS_URL=
SQS_REGION=
source .env.production.eb
Set web environment variables
eb setenv -e laravel-apz-web \
APP_URL=$APP_URL \
HOST_NAME=$HOST_NAME \
MAIL_HOST=$MAIL_HOST \
MAIL_PORT=$MAIL_PORT \
MAIL_USERNAME=$MAIL_USERNAME \
MAIL_PASSWORD=$MAIL_PASSWORD \
RECAPTCHA_PUBLIC_KEY=$RECAPTCHA_PUBLIC_KEY \
RECAPTCHA_PRIVATE_KEY=$RECAPTCHA_PRIVATE_KEY \
SQS_PUBLIC_KEY=$SQS_PUBLIC_KEY \
SQS_SECRET_KEY=$SQS_SECRET_KEY \
SQS_URL=$SQS_URL \
SQS_REGION=$SQS_REGION
Set worker environment variables
eb setenv -e laravel-apz-worker \
APP_URL=$APP_URL \
HOST_NAME=$HOST_NAME \
MAIL_HOST=$MAIL_HOST \
MAIL_PORT=$MAIL_PORT \
MAIL_USERNAME=$MAIL_USERNAME \
MAIL_PASSWORD=$MAIL_PASSWORD \
RECAPTCHA_PUBLIC_KEY=$RECAPTCHA_PUBLIC_KEY \
RECAPTCHA_PRIVATE_KEY=$RECAPTCHA_PRIVATE_KEY \
SQS_PUBLIC_KEY=$SQS_PUBLIC_KEY \
SQS_SECRET_KEY=$SQS_SECRET_KEY \
SQS_URL=$SQS_URL \
SQS_REGION=$SQS_REGION
Deploy
eb deploy laravel-apz-web
eb deploy laravel-apz-worker
Open
eb open laravel-apz-web
Clone this wiki locally