> ## Content Index
> Fetch the complete content index at: https://blog.mebooks.co.nz/llms.txt
> Use this file to discover other available public pages before exploring further.

# Automating Elastic Beanstalk
- URL: https://blog.mebooks.co.nz/automating-elastic-beanstalk/
- Published: 2016-04-02T03:25:00.000Z
- Updated: 2017-10-01T05:16:46.000Z
- Author: Jason Darwin
- Tags: #Import 2026-09-23 10:11

## Good practice and scalability

In this post we're going to demonstrate running Docker containers using Amazon's Elastic Beanstalk.

When I was figuring out to use Docker containers on Amazon's Elastic Beanstalk, I seemed to find plenty of articles detailing how to manually setup Elastic Beanstalk using the AWS dashboard, but I really wanted a solution that was automatable / repeatable.

Specifically, with this project we want to be able to:

- Install and use Docker on Mac OSX (though other host platforms are fine)
- Build a custom `apache-php5` Docker image using a `Dockerfile`, fully provisioned and ready for our app
- Build a `php-app` Docker image containing our custom app on top of this `apache-php5` Docker image
- Push our Docker images to hub.docker.com
- Run up our application locally from the Docker images using Elastic Beanstalk's `eb local run`
- Run up our application remotely on AWS using Elastic Beanstalk's `eb create` / `eb deploy`
- Do all of the above in an automated fashion, using a `Makefile`

We want to follow good practice, and this involves splitting our database and application into separate containers. This will then alow us to scale up application containers and database containers independently.

For the local install, we'll deploy both the application and database container on the same host, but when deploying for production, we'll use RDS to serve our database, and an EC2 instance dedicated to each application container.

Presuming that our application is PHP heavy but database light (say, because it only uses the database for login / personal profile storage), we can probably get away with a single  
database container / RDS instance, while scaling up more EC2 instances of our application container when demand increases.

Our app will do nothing more arduous than displaying the information generated by `phpinfo()` (on the `php-app` container) and show that it can connect to a MySQL database (on the `mysql` container if local or the using RDS if deployed on AWS).

However, you should easily be able to build out a much more complicated PHP application after  
reading through these notes.

Note that we build an `apache-php5` Docker image first, and then another `php-app` image containing our app on top of this image. This allows us to re-use the existing apache-php5 Docker image whenever our app changes, and thus speeds up our builds.

All code for this project can be found at [https://github.com/jcdarwin/local-elasticbeanstalk-php-demo](https://github.com/jcdarwin/local-elasticbeanstalk-php-demo?ref=blog.mebooks.co.nz)

## Requirements

We're doing this on OSX, but it should work on typical linux variants.  
We're presuming that you've got installed:

- [homebrew](http://brew.sh/?ref=blog.mebooks.co.nz) (Mac OSX package manager)
- GNU make
- a public/private key pair, [created and registered on AWS](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-key-pairs.html?ref=blog.mebooks.co.nz),  
and stored locally, e.g.:

```bash
~/.ssh/aws-eb
~/.ssh/aws-eb.pub

```

We're also presuming that you're reasonably comfortable with the principles behind Docker, and have probably already had some exposure to AWS.

## Configuration

We'll use a local shell script, `.env_local`, not versiond in git, to store our custom / sensitive details and implement them as environment variables.  
This should be the only place we have to regularly make changes, and is used by our `Makefile` when running build steps:

```bash
#!/bin/bash

export EB_APP=local-elasticbeanstalk
export EB_ENVIRONMENT=dev-$EB_APP
export EB_SCALE_MIN=2

export DOCKER_MACHINE=default
export DOCKER_USER=mebooks
export DOCKER_EMAIL=jcdarwin@gmail.com
export DOCKER_PASSWORD=bailter

DOCKERED=eval "$(docker-machine env $DOCKER_MACHINE)"

```

Set execute permissions on this file:

```bash
chmod a+g .env_local

```

Note that running the `.env_local` loads the environment so we can use docker (once installed) from the local OSX terminal, and does the same as:

```bash
eval "$(docker-machine env default)"

```

## Installation

We use `docker-machine` on OSX via the [Docker ToolBox](https://www.docker.com/products/docker-toolbox?ref=blog.mebooks.co.nz).

The easiest way to do this is:

```bash
brew cask install dockertoolbox

```

As we're using Max OSX, this means that we'll end up with a Virtualbox Linux VM that will be used to run Docker.

Start our `docker-machine`:

```bash
docker-machine start default

```

### Setup docker so we can use it from our current shell

Port-forward in `VirtualBox`, so we can access port 80 transparently:

```bash
VBoxManage list vms
VBoxManage modifyvm "defaut" --natpf1 "guestnginx,tcp,,80,,80"

```

Once installed, we can see that `docker` is at version 10.1:

```bash
docker -v
$ Docker version 1.10.2, build c3959b1

```

If we apply our environment variables:

```bash
. ./.env_local

```

we should then see our environment values based on `docker-machine config default` plus any extra that we've added in `.env_local`:

```bash
env | grep DOCKER

    DOCKER_PASSWORD=WHATEVER
    DOCKER_HOST=tcp://192.168.99.101:2376
    DOCKER_MACHINE_NAME=default
    DOCKER_TLS_VERIFY=1
    DOCKER_MACHINE=default
    DOCKER_USER=mebooks
    DOCKER_CERT_PATH=/Users/jasondarwin/.docker/machine/machines/default
    DOCKER_EMAIL=jcdarwin@gmail.com

```

and we should be able to access docker directly

```bash
docker info

```

### Install `awsebcli`

We install the `awsebcli` using `homebrew`:

```bash
brew install awsebcli

```

However, [there's a problem with the version compatibility check](https://forums.aws.amazon.com/thread.jspa?threadID=225425&ref=blog.mebooks.co.nz), meaning that awsebcli thinks that docker 1.10.2 is < docker 1.6, and we receive the following message:

```bash
"You must install Docker version 1.6.0 to continue. If you are using Mac OS X, ensure you have boot2docker version 1.6.0. Currently, "eb local" does not support Windows."

```

To rectify this, currently we must edit `/usr/local/Cellar/aws-elasticbeanstalk/3.7.3/libexec/lib/python2.7/site-packages/ebcli/containers/compat.py` as follows:

```python
def supported_docker_installed():
    """
    Return whether proper Docker version is installed.
    :return: bool
    """

    try:
        #return commands.version() >= SUPPORTED_DOCKER_V
        return True
    # OSError = Not installed
    # CommandError = docker versions less than 1.5 give exit code 1
    # with 'docker --version'.
    except (OSError, CommandError):
        return False

```

### Install Composer

```bash
curl -sS https://getcomposer.org/installer | sudo php -- --install-dir=/usr/local/bin --filename=composer

```

Use composer to install the dependencies for our `php-app`

```bash
cd php-app
composer install

```

## App environment variables

We need to make certain environment variables available to our PHP scripts, particularly  
those to do with connecting to our MySQL container.

To do this, we create a `php-app/.env` file, with placeholders for the expected  
environment variables:

```php
# php-app/.env
# The variables below are replaced during container startup by init.sh

# If we're using a local mysql container, the MYSQL variables are populated
#DB_HOST="${MYSQLDB_PORT_3306_TCP_ADDR}"
#DB_DATABASE="${MYSQLDB_ENV_MYSQL_DATABASE}"
#DB_PASSWORD="${MYSQLDB_ENV_MYSQL_ROOT_PASSWORD}"
#DB_USERNAME="${MYSQLDB_ENV_MYSQL_USERNAME}"

# If we;re using an AWS RDS instance, the RDS  variables are populated
#DB_HOST="${RDS_HOSTNAME}"
#DB_DATABASE="${RDS_DB_NAME}"
#DB_PASSWORD="${RDS_PASSWORD}"
#DB_USERNAME="${RDS_USERNAME}"

```

We then use our `init.sh` script to read the environment variables during the initialisation of our `php-app` container, and replace the placeholders in `php-app/.env` with the environment variables values:

```bash
# Make a copy of our .env file, as we don't want to pollute the original
cp /var/www/html/.env /tmp/

# Update the app configuration to make the service environment
# variables available.
function setEnvironmentVariable() {
    if [ -z "$2" ]; then
        echo "Environment variable '$1' not set."
        return
    fi

    # Check whether variable already exists
    if grep -q "\${$1}" /tmp/.env; then
        # Reset variable
        sed -i "s/\${$1}/$2/g" /tmp/.env
    fi
}

# Grep for variables that look like MySQL (for local deployments)
# or RDS (for remote deployments).
for _curVar in `env | grep 'MYSQL\|RDS' | awk -F = '{print $1}'`;do
    # awk has split them by the equals sign
    # Pass the name and value to our function
    setEnvironmentVariable ${_curVar} ${!_curVar}
done

# Now that /tmp/.env is populated, we can start/restart apache
# and let our PHP scripts access them.
service apache2 restart

```

## Create our Makefile

We use a Makefile to make our builds slightly easier:

```bash
include .env_local
BASE_IMAGE=mebooks/apache-php5
APP_IMAGE=mebooks/php-app
APP=php-app
VERSION=`git describe --tags`
CORE_VERSION=HEAD

all: build-base prepare

base: build-base push-base

app: prepare-app build-app push-app

environment: create-environment

#
# Our base image tasks
#
build-base:
    docker build -t $(BASE_IMAGE):$(VERSION) docker/base

push-base:
    docker login --username=$(DOCKER_USER) --email=$(DOCKER_EMAIL) --password=$(DOCKER_PASSWORD)
    docker push $(BASE_IMAGE)

#
# Our app image tasks
#
prepare-app:
    # Update Dockerrun.aws.json with the current image version
    sed -i '' "s~${APP_IMAGE}\:[^\"]*~${APP_IMAGE}\:$(VERSION)~g" Dockerrun.aws.json
    git archive --format tgz HEAD $(APP) > docker/app/$(APP).tgz

build-app:
    docker build -t $(APP_IMAGE):$(VERSION) docker/app

push-app:
    docker login --username=$(DOCKER_USER) --email=$(DOCKER_EMAIL) --password=$(DOCKER_PASSWORD)
    docker push $(APP_IMAGE)

#
# Our Elastic Beanstalk tasks
#
create-environment:
    eb create -v \
        --cfg $(EB_APP) \
        --scale $(EB_SCALE_MIN) \
        --cname $(EB_ENVIRONMENT) \
        $(EB_ENVIRONMENT)

```

## Build the docker images

Commit and tag our changes, e.g.:

```bash
    git tag 2.3.0

```

The use of `git describe --tags` in the `Makefile` means that we'll be using the latest `git tag` to tag our Docker images.  
Note that, if you've made commits since your last `git tag`, we'll end up using a tag value which is a combination of the tag and the last commit hash:

```bash
git describe --tags
    2.4.0-9-g8215032

```

### Build our `mebooks/apache-php5` base Docker image

```bash
    # Create the `mebooks/apache-php5` Docker image
    make base

    # Check that the image was created
    docker images

```

Edit `docker/app/Dockerfile` and ensure that our `php-app` is refering to the same version as that that we just built:

```bash
    FROM mebooks/apache-php5:2.3.0

```

Our `mebooks/apache-php5` Docker image should only need updating when we want to update the packages in the distribution, such as when there are security vulnerabilities.

### Build our app docker image

Note that our app docker image wil be tagged with the current version of the repo and our `Dockerrun.aws.json` file will be updated accordingly.

```bash
# Create our php-app image and push it to hub.docker.com
make app

# Check that we updated the image version in our `Dockerrun.aws.json`
cat Dockerrun.aws.json | grep 'Name'
    "Name": "mebooks/php-app:2.4.0-8-gb0eef33",

```

As we've just tagged our `Dockerrun.aws.json` file with the current version of the repo,  
we need to commit changes, otherwise the `eb create` / `eb deploy` (which makes use of `git archive`) will use the previous version of the `Dockerrun.aws.json` file.

Note that, as `eb create` / `eb deploy` uses `git archive` to create a zipfile of our application for deployment, this means that:

- we can use `.gitignore` to specify files that shouldn't be under version control
- we can use `.gitattributes` to specify files that should be under version control but should be deployed in the app bundle.

`eb create` / `eb deploy` can also make use of a `.ebignore` file.

### Check that our containers function as expected

```bash
# Set our environment variable
VERSION=`git describe --tags` && echo $VERSION

# Start just the PHP container
docker run -tid -p 80:80 \
    --name=php-app \
    mebooks/php-app:${VERSION}

# Start both containers linked
docker run -p 3306:3306 \
    -e MYSQL_USERNAME=root \
    -e MYSQL_ROOT_PASSWORD=password \
    -e MYSQL_DATABASE=my_db \
    -d \
    --name mysqlserver \
    mysql

docker run -tid -p 80:80 \
    --name=php-app \
    --link mysqlserver:mysqldb \
    mebooks/php-app:${VERSION}

```

We should now be able to see the PHP Info details at the address reported by `docker-machine ip`, e.g.:

```bash
docker-machine ip
    http://192.168.99.101/

```

If we've started the `mysql` container, we should also be able to see a simple example of connecting to our MySQL database at [http://192.168.99.101/mysql.php](http://192.168.99.101/mysql.php?ref=blog.mebooks.co.nz)

## Push our images to hub.docker

Although our `Makefile` handles the pushing of our containers to hub.docker, we'll cover it here as we need to know about AWS using the associated config file to gain access to pull our images from hub.docker.

Login to our docker account:

```bash
# Enter the username, password and email when prompted
docker login

# Alternatively, specify username, email and password
# If any of these parameters are not supplied, you'll be prompted for them
docker login --username=mebooks --email=jcdarwin@gmail.com --password=WHATEVER

```

The login will create a config file at `~/.docker/config.json`.

AWS currently uses an older format of the docker config for authentication to hub.docker,  
so we need to change our current `~/.docker/config.json` to the required format by removing the `auths` wrapper:

```json
# ~/.docker/config.json
{
    "auths": {
        "https://index.docker.io/v1/": {
            "auth": "WHATEVER",
            "email": "jcdarwin@gmail.com"
        }
    }
}

```

becomes:

```json
# ~/.docker/.dockercfg.json
{
        "https://index.docker.io/v1/": {
            "auth": "WHATEVER",
            "email": "jcdarwin@gmail.com"
        }
}

```

We now need to push this to a suitable s3 bucket so Elastic Beanstalk can use it to download our images from our private repository.

```bash
s3cmd put ~/.docker/.dockercfg.json s3://elasticbeanstalk-ap-southeast-2-<aws_account_id>

```

Pushing our images to hub.docker is as simple as:

```bash
docker push mebooks/apache-php5
docker push mebooks/php-app

```

Note that the `make app` task automatically does the `docker push mebooks/php-app`, while the `make base` task automatically does the `docker push mebooks/apache-php5`,

Once pushed, we should be able to see our images on hub.docker:

```
https://hub.docker.com/r/mebooks/php-app/tags/
https://hub.docker.com/r/mebooks/apache-php5/tags/

```

### Create our ECR respositories

Alternatively, in this section we'll look at using Amazon's Elastic Container Registry (ECR) as a place to store our images instead of using hub.docker.

Authenticate Docker to an Amazon ECR registry with [get-login](http://docs.aws.amazon.com/AmazonECR/latest/userguide/ECR%5FAWSCLI.html?ref=blog.mebooks.co.nz):

```bash
aws ecr --profile oregon get-login

docker login -u AWS -p BIGLONGNUMBERAPPEARSHERE -e none https://<aws_account_id>.dkr.ecr.us-west-2.amazonaws.com

```

Note that https://<aws\_account\_id>.dkr.ecr.us-west-2.amazonaws.com is the URL for our container registry.

Copy and paste the docker login command into a terminal to authenticate your Docker CLI to the registry. This command provides an authorization token that is valid for the specified registry for 12 hours.

Create our repositories:

```bash
aws ecr create-repository --profile oregon --repository-name mebooks/apache-php5

    registryId: <aws_account_id>
    repositoryArn: arn:aws:ecr:us-west-2:<aws_account_id>:repository/mebooks/apache-php5
    repositoryName: mebooks/apache-php5

aws ecr create-repository --profile oregon --repository-name mebooks/php-app

    registryId: <aws_account_id>
    repositoryArn: arn:aws:ecr:us-west-2:<aws_account_id>:repository/mebooks/php-app
    repositoryName: mebooks/php-app

```

Tag our images and push them

```bash
docker tag mebooks/apache-php5:latest <aws_account_id>.dkr.ecr.us-west-2.amazonaws.com/mebooks/apache-php5:latest

docker push <aws_account_id>.dkr.ecr.us-west-2.amazonaws.com/mebooks/apache-php5:latest

docker tag mebooks/php-app:2.4.0-8-gb0eef33 <aws_account_id>.dkr.ecr.us-west-2.amazonaws.com/mebooks/php-app:2.4.0-8-gb0eef33

docker push <aws_account_id>.dkr.ecr.us-west-2.amazonaws.com/mebooks/php-app:2.4.0-8-gb0eef33

```

## Create our `Dockerrun.aws.json` (multi-container version)

We can use a multi-container `Dockerrun.aws.json` if we want to use `eb local run` to spin up both our `php-app` container and an associated `mysql` container.

```json
{
    "AWSEBDockerrunVersion": 2,
    "containerDefinitions": [
        {
            "name": "mysql",
            "image": "mysql:5.6",
            "essential": true,
            "portMappings": [
                {
                    "hostPort": 3306,
                    "containerPort": 3306
                }
            ],
            "environment": [
                {
                    "name": "MYSQL_USERNAME",
                    "value": "root"
                },
                {
                    "name": "MYSQL_PASSWORD",
                    "value": "password"
                },
                {
                    "name": "MYSQL_DB_NAME",
                    "value": "my_db"
                }
            ]
        },
        {
            "name": "php-app",
            "image": "mebooks/php-app",
            "essential": true,
            "memory": 128,
            "portMappings": [
                {
                    "hostPort": 80,
                    "containerPort": 80
                }
            ],
            "links": [
                "mysql"
            ]
        }
    ]
}

```

Note that we specify a (very) weak `MYSQL_ROOT_PASSWORD` in our `Dockerrun.aws.json` — you'll want to change this and ideally not have it under version control.

## Run our containers locally

Finally, use `eb local` to create our docker containers locally:

```bash
eb local run

```

In a second terminal:

```bash
eb local status
docker ps

```

## Accessing the containers locally

Find the appropriate container id, and start a bash shell on it:

```bash
docker ps

$ CONTAINER ID        IMAGE
$ 832af3ff45d8        mebooks/apache-php5:latest
$ fc6a9553583f        mysql:5.6

# Access our PHP container
docker exec -it 832af3ff45d8 bash

```

Alternatively, we can use `eb` to find the details, including the human-readable container names:

```bash
eb local status

$ Platform: 64bit Amazon Linux 2015.09 v2.0.8 running Multi-container Docker 1.9.1 (Generic)
$ Container name: elasticbeanstalk_mysql_1
$ Container ip: 127.0.0.1
$ Container running: True
$ Exposed host port(s): 3306
$ Full local URL(s): 127.0.0.1:3306

$ Container name: elasticbeanstalk_phpapache_1
$ Container ip: 127.0.0.1
$ Container running: True
$ Exposed host port(s): 80
$ Full local URL(s): 127.0.0.1:80

```

Access our PHP container:

```bash
docker exec -it elasticbeanstalk_phpapache_1 bash

# check our apache config
apachectl configtest

# view our apache config
cat /etc/apache2/sites-enabled/vhost.conf

# Find the ip of our MySQL container
env | grep MYSQL_1_PORT_3306_TCP_ADDR

$ ELASTICBEANSTALK_MYSQL_1_PORT_3306_TCP_ADDR=172.17.0.2
$ MYSQL_1_PORT_3306_TCP_ADDR=172.17.0.2

# Login to mysql
mysql -u root -h 172.17.0.2  -p

```

Access our MySQL container:

```bash
docker exec -it elasticbeanstalk_mysql_1 bash

# Display our databases -- we should see my_db
mysql -u root -p -e "show databases;"

```

## Create our `Dockerrun.aws.json` (single container version)

Although the multi-container version above is useful for testing locally, in production we'll be using RDS to host MySQL, so we'll use a single container `Dockerrun.aws.json` to deploy our `php-app` container, and use an Amazon RDS instance for our database.

```json
{
    "AWSEBDockerrunVersion": 1,
    "Image": {
        "Name": "mebooks/php-app:2.4.0-8-gb0eef33",
        "Update": "true"
    },
    "Authentication": {
        "Bucket": "elasticbeanstalk-ap-southeast-2-<aws_account_id>",
        "Key": ".dockercfg.json"
    },
    "Ports": [
        {
            "ContainerPort": "80"
        }
    ],
    "Logging": "/var/log/apache2"
}

```

## Initialise our Elastic Beanstalk app

Now that we're happy with the running the containers up locally, we need to initialise our `.elasticbeanstalk/config.yml` so we can deploy our containers to AWS:

```bash
    eb init

```

More info about the `eb cli` tool is to be found on the [Amazon site](http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/eb-cli3-getting-started.html?ref=blog.mebooks.co.nz).

We'll need to customise our configuration, so create a standard config as a starting point:

```bash
# This creates `.elasticbeanstalk/local-elasticbeanstalk.env.yml`
eb config

```

Ensure we have settings as we want, e.g. the `MinSize` for autoscaling:

```bash
  aws:autoscaling:asg:
    Availability Zones: Any
    Cooldown: '360'
    Custom Availability Zones: 'ap-southeast-2'
    MaxSize: '4'
    MinSize: '2'

```

## Create our Elastic Beanstalk application environment

Refer [https://github.com/hopsoft/relay/wiki/How-to-Deploy-Docker-apps-to-Elastic-Beanstalk](https://github.com/hopsoft/relay/wiki/How-to-Deploy-Docker-apps-to-Elastic-Beanstalk?ref=blog.mebooks.co.nz)

```bash
# Ensure `.env_local` specifies the correct `EB_ENVIRONMENT`:
export EB_ENVIRONMENT=dev-local-elasticbeanstalk

# Create our new environment on Elastic Beanstalk
make environment

```

`make environment` effectively runs something like the following, depending on  
your environment variable settings in `.env_local`:

```bash
eb create -v \
    --scale 2
    --cname dev-local-elasticbeanstalk \
    dev-local-elasticbeanstalk

```

We should then see output at our terminal like the following:

```bash
    INFO: Creating new application version using project code
    WARNING: You have uncommitted changes.
    INFO: Getting version label from git with git-describe
    Creating application version archive "app-8215-160402_175219".
    INFO: creating zip using git archive HEAD
    INFO: git archive output: Dockerrun.aws.json
    php-app/
    php-app/.env
    php-app/composer.json
    php-app/composer.lock
    php-app/public/
    php-app/public/index.php
    php-app/public/mysql.php
    INFO: Uploading archive to s3 location: local-elasticbeanstalk-php-demo/app-8215-160402_175219.zip
    Uploading local-elasticbeanstalk-php-demo/app-8215-160402_175219.zip to S3. This may take a while.
    Upload Complete.
    INFO: Creating AppVersion app-8215-160402_175219
    INFO: Creating new environment
    Environment details for: dev-local-elasticbeanstalk
      Application name: local-elasticbeanstalk-php-demo
      Region: ap-southeast-2
      Deployed Version: app-8215-160402_175219
      Environment ID: e-myq9pzyjxg
      Platform: 64bit Amazon Linux 2015.09 v2.0.8 running Docker 1.9.1
      Tier: WebServer-Standard
      CNAME: dev-local-elasticbeanstalk.ap-southeast-2.elasticbeanstalk.com
      Updated: 2016-04-02 04:52:24.369000+00:00
    Printing Status:
    INFO: createEnvironment is starting.
    INFO: Using elasticbeanstalk-ap-southeast-2-<aws_account_id> as Amazon S3 storage bucket for environment data.
     -- Events -- (safe to Ctrl+C)

# CTRL-C when prompted, and follow progress:
eb status

```

If we want to reference an existing config, we can do this during creation:

```bash
    eb create -v --cfg local-elasticbeanstalk

```

Note that `eb create` will use the settings from `.gitattributes` to `export-ignore` files in the zip file that it creates and uploads to s3.  
As such, we have to be careful that the `Dockerrun.aws.json` file is included in the root level of our zip file.

```bash
Creating application version archive "app-4bbe-160402_140739".
Uploading local-elasticbeanstalk-php-demo/app-4bbe-160402_140739.zip to S3. This may take a while.

```

If we wish, we can easily download this zip file and inspect the contents:

```bash
s3cmd get s3://elasticbeanstalk-ap-southeast-2-<aws_account_id>/local-elasticbeanstalk-php-demo/app-4bbe-160402_140739.zip

```

Once created (and even during creation), we will see our envionment in our AWS dashboard:  
[https://ap-southeast-2.console.aws.amazon.com/elasticbeanstalk/home](https://ap-southeast-2.console.aws.amazon.com/elasticbeanstalk/home?ref=blog.mebooks.co.nz)

## RDS

We now need to create our [RDS instance](https://aws.amazon.com/rds/?ref=blog.mebooks.co.nz).  
For this project we'll use:

- RDS MySQL
- T2 Small
- Multi-AZ
- Allocated storage: 5GB
- DB Instance Identifier: mebooks-mysql-dbinstance
- Master Username: root

Using `Multi-AZ` ensures that we mitigate the chance of failure by having our main RDS instance in one availability zone, and a RDS failover in another availability zone.  
Ensure that the security group used for the RDS instance allows connections to and from port 3306, and that the EC2 instances will be also using this security group.

Importantly, we should ensure that our RDS instance is private, but set to use the same VPC as our EC2 instances.

Once setup, we should be able to connect from an EC2 instance in the same group as follows (depending on the actual assigned RDS endpoint):

```bash
mysql -h mebooks-mysql-dbinstance.criieggarwwz.ap-southeast-2.rds.amazonaws.com -u root -p

```

As per [this answer](http://serverfault.com/a/540916?ref=blog.mebooks.co.nz), there's currently no easy way to automate the attachment of an RDS database existing outside of an Elastic Beanstalk environment to the environment during creation.  
Amazon seem to assume that you'll be wanting to create an RDS instance inside the Elastic Beanstalk environment, which means that your database becomes less independent, and may disappear should anything go wrong with your Elastic Beanstalk environment.

Instead, if you want the RDS instance to exist outside of the environment you can simply provide the connection parameters as environment variables via the EB Console: Configuration -> Web Layer -> Software Configuration:

```bash
RDS_HOSTNAME: mebooks-mysql-dbinstance.criieggarwwz.ap-southeast-2.rds.amazonaws.com
RDS_DB_NAME : my_db
RDS_USERNAME: root
RDS_PASSWORD: WHATEVER

```

These environment variables can then be [accessed from inside your PHP app](http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/create%5Fdeploy%5FPHP.rds.html?ref=blog.mebooks.co.nz#create%5Fdeploy%5FPHP.rds.newDB).

This does mean that our application will not be able to connect to our database on the initial deployment during the `eb create`, as we won't have yet had the chance to set these RDS  
environment variables in the EB dashboard.

Note that with this method, as the `RDS_PASSWORD` will be in plain sight on the AWS dashboard, it's probably wise to change this regularly. When you make changes to your environment variables on the EB dashboard and click `apply`, Elastic Beanstalk will then re-provision your existing EC2 instances with the new values.

## Accessing our application

To see our application in the browser, we can use:

```bash
# opens a browser tab with something like:
# http://dev-local-elasticbeanstalk.ap-southeast-2.elasticbeanstalk.com/
eb open

```

We should see our PHP Info page, and be able to see our database connection details at `/mysql.php`.

Once our Elastic Beanstalk cluster is running , we can ssh into our load balancer, and then telnet to our instances:

```bash
# once sshed into the load balancer, you'll be presented with a choice of the
# EC2 instances if there are more than one.
eb ssh
sudo -s
yum install telnet
telnet mebooks-mysql-dbinstance.criieggarwwz.ap-southeast-2.rds.amazonaws.com

```

Once we know the public DNS of our ec2 instance, we can also directly ssh in as `ec2-user`:

```bash
ssh -i ~/.ssh/aws-eb  ec2-user@ec2-52-62-4-141.ap-southeast-2.compute.amazonaws.com

# Once logged in, elevate to root to be able to acces docker:
sudo -s

```

## Redeploying

Presuming that we're only updating our custom `php-app` code, successive deployments should be a matter of the following:

```bash
# create the image and push it to hub.docker
make app

```

As `make app` tagged our `Dockerrun.aws.json` file with the current version of the repo, we need to commit the changes, otherwise the `eb create` / `eb deploy` (which uses `git archive`) will use the previously-committed version of the `Dockerrun.aws.json` file.

```bash
git add Dockerrun.aws.json
git commit -m "Updated Docker image tag in Dockerrun.aws.json"

eb deploy -v local-elasticbeanstalk

```

## Terminating

Once we've finished with a particular environment, we can terminate it:

```bash
eb terminate local-elasticbeanstalk

```

## Cleaning up

Once we're finished, we can remove our containers locally, either by id or by name

```bash
docker rm 832af3ff45d8 fc6a9553583f

```

If we're finished with our image, we can delete it:

```bash
docker rmi mebooks/php-app
docker rmi mebooks/apache-php5

```

## Summary

Hopefuly the above is well-enough detailed to help you get to grips with Elastic Beanstalk.

It's a great mechanism for easily deploying a scalable Docker-based application, and all the better when we can automate the creation and deployment of our environments and applications.

In case using a `Makefile` as we did here becomes a bit limiting, you may want to look at using something like Ansible to automate the creation and deployment tasks, and there's  
a great example of this at [https://github.com/hsingh/ansible-elastic-beanstalk](https://github.com/hsingh/ansible-elastic-beanstalk?ref=blog.mebooks.co.nz).

Elastic Beankstalk is a very handy service, but should you want to create a more enterprise-level cloud application, you might also want to look at something like RedHat's [OpenShift Origin](https://www.openshift.org/?ref=blog.mebooks.co.nz).

However, being a developer rather than operations, I found the method outlined above  
suited me, as it was more Dev-ops (with a big 'D') rather than dev-Ops (with a big 'O').

## Further reading

In putting together the above, I found these posts of help:

- [http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/create\_deploy\_docker-eblocal.html](http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/create%5Fdeploy%5Fdocker-eblocal.html?ref=blog.mebooks.co.nz)
- [http://victorlin.me/posts/2014/11/26/running-docker-with-aws-elastic-beanstalk](http://victorlin.me/posts/2014/11/26/running-docker-with-aws-elastic-beanstalk?ref=blog.mebooks.co.nz)
- [https://github.com/hopsoft/relay/wiki/How-to-Deploy-Docker-apps-to-Elastic-Beanstalk](https://github.com/hopsoft/relay/wiki/How-to-Deploy-Docker-apps-to-Elastic-Beanstalk?ref=blog.mebooks.co.nz)
- [http://www.sitepoint.com/docker-and-dockerfiles-made-easy/](http://www.sitepoint.com/docker-and-dockerfiles-made-easy/?ref=blog.mebooks.co.nz)
- [http://www.michaelgallego.fr/blog/2015/07/18/using-elastic-beanstalk-multi-container-with-php/](http://www.michaelgallego.fr/blog/2015/07/18/using-elastic-beanstalk-multi-container-with-php/?ref=blog.mebooks.co.nz)