Tag: elastic beanstalk


Automating the Deployment of Encrypted Web Services with the AWS SDK for PHP (Part 2)

by Joseph Fontes | on | in PHP | Permalink | Comments |  Share

In the first post of this series, we focused on how to use Amazon Route 53 for domain registration and use Amazon Certificate Manager (ACM) to create SSL certificates. With our newly registered domain available for use, we can proceed to deploy and configure the services we need to host the www.dev-null.link website across an encrypted connection. Once complete, the infrastructure configuration will reflect the diagrams below.

Diagram 1

Diagram 2

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

The first diagram shows the use of Route 53 to route traffic between AWS Elastic Beanstalk environments across multiple regions. The second example adds Amazon CloudFront support to the design.

AWS Elastic Beanstalk

Our first step is to create the Elastic Beanstalk application, which will provide the necessary infrastructure to host our website. The following is the order of the methods used for the AWS Elastic Beanstalk deployment:

  • createApplication
  • createApplicationVersion
  • createConfigurationTemplate
  • createEnvironment

We start by creating the Elastic Beanstalk application.

$ebCreateApplicationData = [ 'ApplicationName' => "DevNullDemo",
            'Description' => "Demo application for ACM deployment" ];

$ebCreateApplicationResult = $ebClient->createApplication($ebCreateApplicationData);

print_r($ebCreateApplicationResult);

Result

…
[Application] => Array
	(
	[ApplicationName] => DevNullDemo
	[Description] => Demo application for ACM deployment
…

Now we’ll create the initial version of the application and name it DevNullDemo. You can use the application archive of your choice, although a simple PHP demo site is available here.

$ebCreateAppVersionData = [ 'ApplicationName' => "DevNullDemo",
                            'VersionLabel' => 'v1',
                            'Description' => 'Initial Create',
                            'SourceBundle' => [ 'S3Bucket' => 'pub-materials',
                                                'S3Key' => 'Sample-App.zip' ] ];

$ebCreateAppVersionResult = $ebClient->createApplicationVersion($ebCreateAppVersionData);

print_r($ebCreateAppVersionResult);

Result

…
            [ApplicationVersion] => Array
                (
                    [ApplicationName] => DevNullDemo
                    [Description] => Initial Create
                    [VersionLabel] => v1
                    [SourceBundle] => Array
                        (
                            [S3Bucket] => pub-materials
                            [S3Key] => Sample-App.zip
                        )
                    [Status] => UNPROCESSED
                )
…

Next, we need to create an Elastic Beanstalk configuration template. This template requires the selection of an Elastic Beanstalk solution stack prior to calling the method, createConfigurationTemplate. The solution stack is the platform you choose to run your application within Elastic Beanstalk. You can find a list of available solution stack choices by using the listAvailableSolutionStacks method.

$ebSolutionStacks = $ebClient->listAvailableSolutionStacks();
print_r($ebSolutionStacks);

Result

…
            [SolutionStacks] => Array
                (
                    [0] => 64bit Windows Server Core 2012 R2 v1.2.0 running IIS 8.5
…
                    [5] => 64bit Amazon Linux 2016.03 v2.1.6 running Java 7
…
                    [9] => 64bit Amazon Linux 2014.03 v1.1.0 running Node.js
                    [14] => 64bit Amazon Linux 2016.03 v2.1.7 running PHP 7.0
                    [15] => 64bit Amazon Linux 2015.09 v2.0.6 running PHP 5.6
                    [16] => 64bit Amazon Linux 2015.09 v2.0.4 running PHP 5.6
…
                    [14] => Array
                        (
                            [SolutionStackName] => 64bit Amazon Linux 2016.03 v2.1.7 running PHP 7.0
                            [PermittedFileTypes] => Array
                                (
                                    [0] => zip
                                )

                        )
…

For our demonstration, we’ll use the, 64bit Amazon Linux 2016.03 v2.1.7 running PHP 7.0 solution stack.

$ebConfigTemplateData = [ 'ApplicationName' => "DevNullDemo",
                          'TemplateName' => 'DevNullDemoTemplate',
                          'SolutionStackName' => '64bit Amazon Linux 2016.03 v2.1.7 running PHP 7.0',
                          'Description' => 'EB Environment template for blog deployment.' ];

$ebConfigTemplateCreateResult = $ebClient->createConfigurationTemplate($ebConfigTemplateData);

print_r($ebConfigTemplateCreateResult);

Result

…
        (
            [SolutionStackName] => 64bit Amazon Linux 2016.03 v2.1.7 running PHP 7.0
            [ApplicationName] => DevNullDemo
            [TemplateName] => DevNullDemoTemplate
            [Description] => EB Environment template for blog deployment.
…

Now we can create and start the infrastructure by using the createEnvironment method. The following example sets additional options such as instance type and ACM SSL certificate. You need to replace the [CERTIFICATEARN] value with the AWS ACM certificate ARN created in part 1 of this series. You can also find this value by using the AWS ACM listCertificates method. For these examples, we’ve created a certificate across multiple regions for the host name eb.dev-null.link, in addition to the previously created www.dev-null.link certificate.

$ebCreateEnvData = [ 'ApplicationName' => "DevNullDemo",
                        'EnvironmentName' => "DevNullEnv",
                        'Description' => "Demo environment for ACM EB deployment.",
                        'TemplateName' => "DevNullDemoTemplate",
                        'VersionLabel' => 'v1',
                        'OptionSettings' => [

                                [ 'Namespace' => 'aws:elb:listener:443',
                                  'OptionName' => 'ListenerProtocol',
                                  'Value' => 'HTTPS' ],

                                [ 'Namespace' => 'aws:elb:listener:443',
                                  'OptionName' => 'SSLCertificateId',
                                  'Value' => '[CERTIFICATEARN]' ],

                                [ 'Namespace' => 'aws:elb:listener:443',
                                  'OptionName' => 'InstancePort',
                                  'Value' => '80' ],

                                [ 'Namespace' => 'aws:elb:listener:443',
                                  'OptionName' => 'InstanceProtocol',
                                  'Value' => 'HTTP' ],

                                [ 'Namespace' => 'aws:autoscaling:launchconfiguration',
                                  'OptionName' => 'InstanceType',
                                  'Value' => 't2.nano' ],

                                ],
                        'Tier' => [ 'Name' => 'WebServer',
                                    'Type' => 'Standard',
                                    'Version' => ' ' ],
                        ];

$ebCreateEnvData = $ebClient->createEnvironment($ebCreateEnvData);

print_r($ebCreateEnvData);

Result

…
            [EnvironmentName] => DevNullEnv
            [EnvironmentId] => e-fnvhjptdjd
            [ApplicationName] => DevNullDemo
            [VersionLabel] => v1
            [SolutionStackName] => 64bit Amazon Linux 2016.03 v2.1.7 running PHP 7.0
            [Description] => Demo environment for ACM EB deployment.

            [Status] => Launching
            [Health] => Grey
            [Tier] => Array
                (
                    [Name] => WebServer
                    [Type] => Standard
                    [Version] =>
                )
…

As the results show, the current status of our environment is Launching. We can periodically check the status with the describeEnvironments method.

$ebDescEnvResult = $ebClient->describeEnvironments();

foreach($ebDescEnvResult['Environments'] as $ebEnvList) {
        print "Name:\t".$ebEnvList['EnvironmentName']."\n";
        print "ID:\t".$ebEnvList['EnvironmentId']."\n";
        print "CNAME:\t".$ebEnvList['CNAME']."\n";
        print "Status:\t".$ebEnvList['Status']."\n\n";
}

Result

Name:  	DevNullEnv
ID:    	[ID]
CNAME: 	DevNullEnv.[ID].[Region].elasticbeanstalk.com
Status:	Ready

When the environment has a status of Ready, we can proceed to create the necessary DNS records. You can also check that the site is functional by pasting the CNAME value into a web browser. Be sure to record this CNAME value so you can use it later.

Demo App

You will want to repeat this process across additional AWS Regions to demonstrate latency-based DNS resolution.

Amazon Route 53

Our next step is to create a Route 53 hosted zone. This hosted zone will define a domain name (or subdomain) for which we are authoritative and, thus, allowed to create DNS records. We’ll start with the createHostedZone method.

$route53Client = $sdk->createRoute53();

$route53Data = [ 'Name' => "dev-null.link",
            'CallerReference' => "BLOGPOSTREF001",
            'HostedZoneConfig' => [ 'Comment' => "AWS SDK sample dev-null.link" ] ];

$route53Result = $route53Client->createHostedZone($route53Data);

Result

            [HostedZone] => Array
                (
                    [Id] => /hostedzone/[Amazon Route 53 Zone ID]
                    [Name] => dev-null.link.
                    [CallerReference] => BLOGPOSTREF001
                    [Config] => Array
                        (
                            [Comment] => AWS SDK sample dev-null.link
                            [PrivateZone] =>
                        )

                    [ResourceRecordSetCount] => 2
                )
…
            [DelegationSet] => Array
                (
                    [NameServers] => Array
                        (
                            [0] => ns-999.awsdns-60.net
		…
)
                )

You should copy the ID of the hosted zone from this result. You can also find a list of all hosted zone ID values by using the listHostedZones method.

$route53ListResult = $route53Client->listHostedZones();

foreach($route53ListResult['HostedZones'] as $zoneItem) {
        print "Name:\t".substr($zoneItem['Name'],0,-1)."\n";
        print "ID:\t".$zoneItem['Id']."\n\n";
}

Result

Name:  	dev-null.link
ID:    	/hostedzone/[Amazon Route 53 Zone ID]

We’ll now create a new DNS entry so that our website is visible via web browser with the eb.dev-null.link host name. For this, we need to use the CNAME value from our Elastic Beanstalk application.

$currentDate = date("r");
$hostedZoneId = "/hostedzone/[Amazon Route 53 Zone ID]";
$subDomain = "eb.dev-null.link";
$ebCname = "DevNullEnv.[EB ID].[Region].elasticbeanstalk.com";

$recordComment = "Created $subDomain record on $currentDate";

$route53RecordData = [ 'HostedZoneId' => $hostedZoneId,
                    'ChangeBatch' => [ 'Comment' => $recordComment,
                    'Changes' => [
                                       [ 'Action' => 'CREATE',
                                         'ResourceRecordSet' => [ 'Name' => $subDomain,
                                                                  'Type' => 'CNAME',
                                                                  'TTL' => 60,
                                                                  'ResourceRecords' => [ [ 'Value' => $ebCname ] ] ]
] ] ] ];

$route53ChangeResult = $route53Client->changeResourceRecordSets($route53RecordData);

print_r($route53ChangeResult);

Result

…
            [ChangeInfo] => Array
                (
                    [Id] => /change/[ChangeInfo ID]
                    [Status] => PENDING
…
                    [Comment] => Created test.jf.unicorn.rentals record on Thu, 15 Sep 2016 12:25:07 -0700
                )
…

As we can see from the result, the status of the change is PENDING. We can check the status with the getChange method using the value of the ChangeInfo ID.

$route53ChangeData = [ 'Id' => "/change/[ChangeInfo ID]" ];
$route53ChangeResult = $route53Client->getChange($route53ChangeData);
print_r($route53ChangeResult);

Result

…
            [ChangeInfo] => Array
                (
                    [Id] => /change/<ChangeInfo ID>
                    [Status] => INSYNC
…
                    [Comment] => Created test.jf.unicorn.rentals record on Thu, 15 Sep 2016 12:25:07 -0700
                )
…

Now that our change has the status of INSYNC, we can view the secure URL in our browser window with the URL https://eb.dev-null.link/.
Demo App

Deploying Across Multiple Regions

This deployment is now serving our website across an encrypted connection in a single AWS Region. For those who would like to use multiple regions, we can expand our current configuration. An AWS ACM certificate is needed in each AWS Region used for the deployment. Because we’ll be using CloudFront, we have to ensure that a certificate is created in the us-east-1 region because CloudFront will source the available AWS ACM certificates from there. You can reference the previous blog post for instructions on creating an AWS ACM certificate in additional regions. Next, run the Elastic Beanstalk creation methods shown earlier in each additional region where you want to deploy the application. Be sure to record the CNAME value for each environment.

After we have all of the necessary Elastic Beanstalk environments running, we need to delete the Route 53 resource record for eb.dev-null.link so that we can replace it with a latency-based record set.

$recordComment = "Deleted $subDomain record on $currentDate";

$route53RecordData = [ 'HostedZoneId' => $hostedZoneId,
                    'ChangeBatch' => [ 'Comment' => $recordComment,
                    'Changes' => [
                                       [ 'Action' => 'DELETE',
                                         'ResourceRecordSet' => [ 'Name' => $subDomain,
                                                                  'Type' => 'CNAME',
                                                                  'TTL' => 60,
                                                                  'ResourceRecords' => [ [ 'Value' => $ebCname ] ] ]
] ] ] ];

$route53ChangeResult = $route53Client->changeResourceRecordSets($route53RecordData);

You might notice that the method and instructions to delete the record are almost identical to the instructions used to create the record. They even use the same method, changeResourceRecordSets.

Result

…
            [ChangeInfo] => Array
                (
                    [Id] => /change/[ChangeInfo ID]
                    [Status] => PENDING

                    [Comment] => Deleted www.dev-null.link record on Thu, 15 Sep 2016 14:58:51 -0700
                )
…

Our next step is to add the latency-based routing rules. This example provides the CNAME of the Elastic Beanstalk environment via the describeEnvironments method.

$currentDate = date("r");
$hostedZoneId = "HOSTED ZONE ID";

$ebDescEnvData = [ 'EnvironmentNames' => [ 'DevNullEnvProd' ] ];
$ebDescEnvResult = $ebClient->describeEnvironments($ebDescEnvData);

$ebCname = $ebDescEnvResult['Environments'][0]['CNAME'];

$recordComment = "Created www record on $currentDate";

$route53RecordData = [ 'HostedZoneId' => $hostedZoneId,
                    'ChangeBatch' => [ 'Comment' => $recordComment,
                    'Changes' => [
                                       [ 'Action' => 'CREATE',
                                         'ResourceRecordSet' => [
                                                'Name' => "eb.dev-null.link",
                                                'Type' => 'CNAME',
                                                'TTL' => 60,
                                                'Region' => $region,
                                                'SetIdentifier' => str_replace("-","",$region),
                                                'ResourceRecords' => [ [ 'Value' => $ebCname ], ],
                                                ],
                                        ],
                                ],
                        ],
                ];

$route53ChangeResult = $route53Client->changeResourceRecordSets($route53RecordData);

Result

…
            [ChangeInfo] => Array
                (
                    [Id] => /change/[ChangeInfo ID]
                    [Status] => PENDING
…

Demo App

In the Route 53 console, we can see there are two CNAMEs that now resolve the host name, eb.dev-null.link. When a user visits the website, the URL returned will correspond to the lower of the record latencies.

Amazon CloudFront

To enhance the user experience, we’ll now configure and deploy a content delivery network solution named Amazon CloudFront. This AWS service provides content delivery acceleration for the media provided with our web application. Each CloudFront deployment is composed of a CloudFront distribution with each distribution having one or more origins. An origin defines the mapping of a URL to a particular destination. The host name, www.dev-null.link, will resolve to our CloudFront distribution, which will then serve pages from the backend eb.dev-null.link load-balanced site. We first create our distribution with the createDistribution method. The value of $cfCreateDistData can be found in this Github Gist.

$cfCreateDistResult = $cfClient->createDistribution($cfCreateDistData);
print_r($cfCreateDistResult);

Result

Aws\Result Object
(
    [data:Aws\Result:private] => Array
        (
            [Distribution] => Array
                (
                    	[Id] => [CF ID Value]
[DomainName] => [CloudFront ID].cloudfront.net
…

Once complete, we need to save the value of DomainName from the result returned. Next, we’ll create a new Route 53 CNAME record that points www.dev-null.link to our CloudFront distribution.

$subDomain = "www.dev-null.link";
$cfCname = "[CloudFront ID].cloudfront.net";

$recordComment = "Created $subDomain record on $currentDate";

$route53RecordData = [ 'HostedZoneId' => $hostedZoneId,
                    'ChangeBatch' => [ 'Comment' => $recordComment,
                    'Changes' => [
                                       [ 'Action' => 'CREATE',
                                         'ResourceRecordSet' => [ 'Name' => $subDomain,
                                                                  'Type' => 'CNAME',
                                                                  'TTL' => 60,
                                                                  'ResourceRecords' => [ [ 'Value' => $cfCname ] ] ]
] ] ] ];

$route53ChangeResult = $route53Client->changeResourceRecordSets($route53RecordData);

Result

Aws\Result Object
(
    [data:Aws\Result:private] => Array
        (
            [ChangeInfo] => Array
                (
[Status] => PENDING
…

Once complete, we can test the new deployment by navigating to the URL (https://www.dev-null.link/) in a web browser.

Conclusion

With our infrastructure configuration completed, we now have a globally load balanced web application that’s accessible via encrypted communications. We’ve shown we can use the AWS SDK for PHP to automate these deployments, which provides the agility to reproduce these environments for customers on demand. Next, we’ll continue this series by reviewing deployments that use Amazon S3 for static content hosting, and the AWS Application Load Balancer and Elastic Load Balancing with Amazon EC2 instance deployments.

Reduce Composer Issues on Elastic Beanstalk

by Jeremy Lindblom | on | in PHP | Permalink | Comments |  Share

During the past couple of months, we’ve had a few reports from customers where they have experienced PHP application deployment failures on AWS Elastic Beanstalk related to parsing exceptions being thrown by Composer. In case you have recently run into the issue yourself, we would like to briefly describe why it is happening and how you can circumvent the issue.

The issue

The issue occurs when a project or its dependencies expresses its requirements using newer Composer syntax features like the carat (^) operator. For users of the AWS SDK for PHP, the error looks something like this:

[RuntimeException] Could not load package aws/aws-sdk-php in http://packagist.org:
[UnexpectedValueException] Could not parse version constraint ^5.3: Invalid version string "^5.3"

We also observed the issue with some versions of the Laravel framework and a few other libraries. The issue comes up when you are using older Elastic Beanstalk stacks with your applications. The older stacks have an old version of Composer included on the underlying Amazon Machine Image (AMI) that does not support some of the latest Composer features like the carat (^) and new OR (||) syntax.

The solution

There are 3 different ways to solve this issue.

  1. Upgrade your application to use the latest Elastic Beanstalk solution stack. The latest solution stacks for PHP have a more recent version of Composer that supports the new syntax features.
  2. Use Elastic Beanstalk configuration files (.ebextension). You can create a file ending in .config inside your .ebextension directory that allows you to perform a Compose self-update command before installing your dependencies. For example, name the file 01composer.config and add the following configuration:

    commands:
      01updateComposer:
        command: export COMPOSER_HOME=/root && /usr/bin/composer.phar self-update
    
    option_settings:
      - namespace: aws:elasticbeanstalk:application:environment
        option_name: COMPOSER_HOME
        value: /root
  3. Install your dependencies locally. One way to avoid issues with Composer during deployment is to bypass the whole Composer workflow entirely by creating deployments of your application with the dependencies pre-installed.

The conclusion

We hope that this short blog post will be helpful if you happen to run into this issue. If this article does not solve your problem or you are running into other issues, please contact AWS Support or ask for help on the Elastic Beanstalk forum.

Deploying Ruby Applications to AWS Elastic Beanstalk with Git

by Loren Segal | on | in Ruby | Permalink | Comments |  Share

With the release of Ruby container support on AWS Elastic Beanstalk, it is now possible to deploy Rails (or any Rack-based) applications onto the AWS infrastructure in just a few easy steps. The container is backed by Amazon EC2 and automatically provisions the machines to use Phusion Passenger (with nginx under the hood). You can run your own local database with your application, or better yet, you can have Elastic Beanstalk automatically set you up with a MySQL database backed by Amazon Relational Database Service (RDS). All of the machines sit behind a load balancer with auto-scaling support automatically configured so you don’t even have to think about ops. Scaling is built-in.

But enough with the awesome features. Let’s deploy an app!

Setting up the application

Before we can deploy an application to Elastic Beanstalk, we first have to create one. Let’s generate an empty Rails skeleton and set it up against a Git repository. We will be using the repository later to deploy our app.

$ rails new fooapp
$ cd fooapp
$ git init && git add -A && git commit -m "Initial commit"

Initializing our Elastic Beanstalk environment

At this point we have an application, but we have not yet configured it to deploy to Elastic Beanstalk. In order to do that, we need to install the AWS Elastic Beanstalk command line tool. This package provides us a binary, eb, which we will use to manage and deploy our applications.

Note that you do need to have Python (2.7 or 3.0) installed in order to use the CLI tools. If you are on OS X or a modern Linux OS, you should already have this installed. If not, visit Python’s website for download information. In order to verify if you have Python on your machine, you can type python --version

We can now initialize our application with the CLI tool by typing eb init. The tool will ask us for some information about our deployment such as our AWS credentials, the deployment region, and the solution stack we want to deploy. For our case, we want to use the "64bit Amazon Linux running Ruby 1.9.3" stack. The tool also asks if we want to setup an RDS database instance for our environment. Let’s answer "no" on that one for now.

$ eb init
To get your AWS Access Key ID and Secret Access Key, 
  visit "https://aws-portal.amazon.com/gp/aws/securityCredentials".
Enter your AWS Access Key ID: ****
Enter your AWS Secret Access Key: ****
Select an AWS Elastic Beanstalk service region.
Available service regions are:
2) US West (Oregon)
...
Select:  (1 to 6): 2
Enter an AWS Elastic Beanstalk application name (auto-generated value is "fooapp"): 
Enter an AWS Elastic Beanstalk environment name (auto-generated value is "fooapp-env"): 
Select a solution stack.
Available solution stacks are:
...
10) 64bit Amazon Linux running Ruby 1.9.3
Select:  (1 to 10): 10
Create an RDS DB Instance? [y/n]: n         

Note that this command will create a .elasticbeanstalk directory in our project and add it to our .gitignore. This directory contains the private environment configuration data provided above, as well as other customized settings. We then commit this change to our .gitignore with the following command:

$ git add .gitignore && git commit -m "Ignore .elasticbeanstalk from Git"

You can see more examples of setting up a project with the eb tool on the Amazon Web Services Blog.

Starting the environment

Now that our environment is configured, we simply need to start it. Starting the environment can take a couple of minutes. During this time, Elastic Beanstalk is spawning a set of EC2 instances, setting up a load balancer, and establishing health monitoring. Fortunately we only have to do this once per environment. Once Elastic Beanstalk is done, we can start the environment with the following command:

$ eb start
Starting application "fooapp".
Waiting for environment "fooapp-env" to launch.
... 
Application is available at "fooapp-env-djr9ywimiu.elasticbeanstalk.com".

After a little bit of waiting, our environment should be live. We can use the environment in a web browser with the URL provided above:

AWS Elastic Beanstalk Ruby Container Welcome Page

You can now also type eb status to see the status of your environment:

$ eb status
URL : fooapp-env-djr9ywimiu.elasticbeanstalk.com
Status  : Ready
Health  : Green

Deploying with Git

The eb init command we ran above actually did some nifty things behind the scenes. The most notable thing is that it setup a Git subcommand named aws.push that can push our code to the environment. This means that whenever we need to deploy our app, we just have to type git aws.push. Since our environment is running, and our app is ready, let’s do that right now!

$ git aws.push

If everything succeeds, you should have initiated a deploy to your environment. Typing eb status should now tell us that a deploy is in progress:

$ eb status
URL : fooapp-env-djr9ywimiu.elasticbeanstalk.com
Status  : Updating
Health  : Grey

Eventually, the status will be green, and we will be able to access our new Rails application:

Rails Welcome Page

Notice that gems listed in our Gemfile were automatically installed, assets were precompiled, and, if we had any, database migrations would have been run too. Migrations and asset compilation happen by default for any Rails 3.x application.

All we have to do now is update our application, commit to our Git repository, and execute the command git aws.push to push our new changes to deploy new versions of the app. Migrations will be run, and our assets will get updated.

Customizing your environment with .ebextensions

To run commands and drop environment variables onto your system via your app during a deployment, you can add a special .config file to the .ebextensions directory in your project root. The config file is formatted as YAML, and is documented on the Customizing and Configuring AWS Elastic Beanstalk Environments documentation page.

For reference, here is a sample .ebextensions/redmine.config that runs some extra hooks for installing Redmine on Elastic Beanstalk. Note that the environment variables are not necessary; BUNDLE_WITHOUT and RACK_ENV already default to the values below. We’re just setting them as an example of some config we might want to perform.

# Sample configuration for Redmine Rails installation

option_settings:
  - option_name: BUNDLE_WITHOUT
    value: "test:development"
  - option_name: RACK_ENV
    value: production

packages:
  yum:
    git: []

container_commands:
  01_secret:
    command: rake generate_secret_token

Skipping migrations and asset compilation

I mentioned you can set environment variables using the option_settings key in the .ebextensions/*.config files. Two specific variables you might want to set are:

  • RAILS_SKIP_MIGRATIONS — set this to true if you don’t want Elastic Beanstalk to run migrations on your behalf. You may want to manage migrations yourself.
  • RAILS_SKIP_ASSET_COMPILATION — set this to true if you don’t use asset compilation in your Rails app. Note, however, that your Elastic Beanstalk container is already configured with a JS runtime to properly handle asset compilation, you do not have to do anything fancy like install the therubyracer gem. It should just work.

Setting up Amazon RDS

The final-but-very-optional step for using the Ruby solution stack in Elastic Beanstalk is to hook up an RDS Instance to your application. If you are running any kind of real world application, you most likely have some database. If your application uses a relational database, you can take advantage of Amazon RDS for access to a MySQL (or Oracle) database.

Fortunately, the setup for this is extremely simple. Remember when we said "no" to creating an RDS DB Instance when we ran eb init? Well, we can initialize another application in the exact same way, but instead of no, we can say yes. We will be prompted to provide a password for this database, but everything else will be directly configured by Elastic Beanstalk on our behalf.

How do we configure it with our Rails app, you ask? Easy. All of the DB information will be passed to our application as environment variables. In Rails, this means we can setup our application to use RDS with a simple database.yml snippet:

production:
  adapter: mysql2
  encoding: utf8
  database: <%= ENV['RDS_DB_NAME'] %>
  username: <%= ENV['RDS_USERNAME'] %>
  password: <%= ENV['RDS_PASSWORD'] %>
  host: <%= ENV['RDS_HOSTNAME'] %>
  port: <%= ENV['RDS_PORT'] %>

The only other thing we have to do is add mysql2 to our Gemfile. It would look something like:

gem 'mysql2'

And RDS should now be fully configured for our Rails application.

Testing locally with Passenger

Because we use Passenger in your container, it’s really easy to test your deployment locally in a very similar webserver environment. If you don’t already have passenger installed on your local machine, you can set it up and initialize your server with:

$ gem install passenger
$ passenger start

A few quick notes if you’ve never deployed to a Passenger server before:

  • Passenger relies on a config.ru Rack file in your root application directory by convention.
  • Passenger also relies on a public/ directory for your static assets located in your application root.

Passenger needs both of these paths to exist in order to detect your Rack/Rails application. In a Rails 3.x app, these paths already exist, but if you are rolling your own low-level Rack application, you need to make sure that they are created. Once they have been setup, your application is ready to be tested as if it were deployed to Elastic Beanstalk. You can consult the Phusion Passenger documentation for more information about how to run your application on this snappy webserver.

Conclusion

So that’s it. We just went through the nuts and bolts of deploying a Ruby application to AWS Elastic Beanstalk. Although there is a lot here to read through, the steps themselves are pretty simple. In short, you can deploy an application with a few commands:

  1. Run eb init on your Git-backed application
  2. Run eb start to load up your environment
  3. Run git aws.push to deploy your code to Elastic Beanstalk
  4. Profit!

Setting up RDS is just a matter of using the configuration values provided to your application through standard environment variables. And of course if you need to dig deeper and customize settings, you can do so by adding a .ebextensions/config.yml file.

There is of course plenty you can do with an Elastic Beanstalk application, and there are plenty more things to talk about with the Ruby solution stack, but hopefully this helps you get a Rack-based application up and running in just a few minutes!