For laravel 5.3+.
This can be used with Elastic Beanstalk Multi Docker Container.
- Database is automatically configured. No configuration needed what so ever. (RDS)
- Cache is setup on a shared redis (elasticache) server .
- S3 bucket is automatically created for your app, so you can store uploaded files!
- Cron that runs the scheduled laravel tasks.
- Migrations are run on each deploy.
- Queue tasks are automatically handeled by AWS SQS. Supervisord keeps laravel worker up to handle jobs.
- AWS Elastic Beanstalk will handle deployments.
- AWS Elastic Load Balancer with Auto scaling . Meaning you can have multiple EC2 instances running your app.
- AWS ElastiCache configured as the cache (redis) for laravel
- AWS RDS for the database (mysql is default)
- AWS S3 for storing uploaded assets (e.g. images).
- AWS SQS for queues.
- AWS Cloudfront created for you to use. Env variable
CDN
refers to the CDN address, just use it right away for your assets.
Optional things that can easily be configured ( Take a look at the Q / A section ).
- AWS SNS to send SMS via AWS SNS.
- AWS SES to send emails.
You have two options:
- (Option 1.) Have the laravel app inside a
web
folder in the root of this repo. recommended. - (Option 2.) Have the laravel app at a separate git repository, and configure so that on deploy it pulls the sources for your app via git.
With option 1. you also can benefit of beanstalk app rollbacks etc. Recommended.
With option 1 you have the benefit to rollback to previous app releases. Which is a big part of beanstalk.
git clone https://github.com/peec/laravel-aws ~/my-deploy-root
cd ~/my-deploy-root
laravel new myapp
# Note: change the app folder to "web".
mv myapp web
Directory structure should now be:
- .ebextensions/
- web/
This is your app location
- .gitignore
- .ebignore
- Dockerrun.aws.json
- README.md
How to work with laravel and version control
You will work on your laravel app like you normally do inside the web
folder. The web folder can be versioncontrolled in git like normal laravel projects is.
When you push changes with eb deploy
, elastic beanstalk will push up the web folder as-is, so don't forget to push nessecary changes and branch correctly inside the web folder before pushing eb deploy
.
Your web-app should probably be in some private repository e.g. on github or bitbucket. The deployment process will try
to clone this address, so ssh keys needs to be created and placed in server_env/deploykeys
.
ssh-keygen -t rsa
cp -R ~/.ssh/id_rsa* server_env/deploykeys/
Add the keys to your favorite private git hosting company control panel, E.g. github profile or bitbucket profile.
.ebextensions/options.config
APP_GIT_REPOSITORY: "git@bitbucket.org:zzz/yyy.git"
APP_GIT_BRANCH: "master"
mkdir web
eb deploy
cp .ebextensions/options.config.dist .ebextensions/options.config
Configure these environment variables.
Variable | Description |
---|---|
AWS_ACCESS_KEY_ID | Get this via AWS IAM (optional) |
AWS_SECRET_ACCESS_KEY | Get this via AWS IAM (optional) |
GITHUB_OAUTH_TOKEN | Token to get composer deps. You can get this in the github profile settings. |
APP_KEY | Set it to the laravel app key found in web/.env dir. (laravel dev copy). |
Add this ( IAM -> Roles -> aws-elasticbeanstalk-ec2-role -> Inline Policies -> Create Role Policy -> Custom Policy ) and here is what you add:
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"ec2:Describe*",
"cloudformation:Describe*",
"elasticbeanstalk:Describe*"
],
"Effect": "Allow",
"Resource": "*"
}
]
}
First we need to install some dependencies that laravel requires (also explained in laravel docs):
cd web
composer require league/flysystem-aws-s3-v3 ~1.0
composer require aws/aws-sdk-php ~3.0
composer require predis/predis
The configuration of this deployment will automatically add a number of new environment variables available to use by laravel.
Lets configure the files needed with env()
calls where needed.
app/config/filesystems.php
....
'default' => env('IS_ON_AWS', false) ? 's3' : 'local',
....
's3' => [
'driver' => 's3',
'key' => env('MEDIA_S3_ACCESS_KEY'),
'secret' => env('MEDIA_S3_SECRET_KEY'),
'region' => env('MEDIA_S3_REGION'),
'bucket' => env('MEDIA_S3_BUCKET'),
],
....
app/config/database.php
....
'redis' => [
'client' => 'phpredis',
'cluster' => false,
'default' => [
'host' => env('REDIS_HOST', '127.0.0.1'),
'password' => env('REDIS_PASSWORD', null),
'port' => env('REDIS_PORT', 6379),
'database' => env('REDIS_DATABASE', 0),
'read_timeout' => 60,
],
],
....
app/config/app.php
....
// REMOVE THIS LINE
'Redis' => Illuminate\Support\Facades\Redis::class,
....
app/config/services.php
....
'ses' => [
'key' => env('SES_KEY'),
'secret' => env('SES_SECRET'),
'region' => env('SES_REGION', 'eu-west-1'),
],
....
app/config/queue.php
....
'sqs' => [
'driver' => 'sqs',
'key' => env('SQS_KEY'),
'secret' => env('SQS_SECRET'),
'prefix' => null,
'queue' => env('SQS_QUEUE'),
'region' => env('SQS_REGION'),
],
....
The Multi-container Docker platform requires additional ECS permissions.
For more information see: https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/create_deploy_docker_ecstutorial.html#create_deploy_docker_ecstutorial_role
http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/eb-cli3-install.html
Lets run eb init
to start the new beanstalk app. Follow the guide below.
cd ~/my-deploy-root
eb init
Select a default region
.....
4) eu-west-1 : EU (Ireland)
....
(default is 3): 4
Select an application to use
1) xxxx
2) yyyy
3) [ Create new Application ]
(default is 3): 3
Enter Application Name
(default is "myapp"):
Application myapp has been created.
It appears you are using Multi-container Docker. Is this correct?
(y/n): y
Select a platform version.
1) Multi-container Docker 1.11.2 (Generic)
2) Multi-container Docker 1.11.1 (Generic)
3) Multi-container Docker 1.9.1 (Generic)
4) Multi-container Docker 1.6.2 (Generic)
(default is 1): 1
Do you want to set up SSH for your instances?
(y/n): y
Select a keypair.
1) xxxx
2) yyyy
3) zzzz
4) [ Create new KeyPair ]
(default is 4): 1
And to upload files, use laravel's "cloud" disk.
Lets create a new environment for the production env.
Adjust things according to needs.
Note: make sure the user executing the eb create command has privileges to create IAM users.
cd ~/my-deploy-root
eb create --database --database.engine mysql --database.instance db.t2.micro --database.size 10 -i t2.micro --database.username ebroot --database.password mydbpassword321 myapp-prod
CHANGES
- Change database.password to your needs
- Change myapp-prod to e.g. yourapp-prod or e.g. yourapp-staging, a naming convention is great here.
Now wait for a while (30 minutes or so), all resources like ElastiCache, RDS, EC2 etc must be created. This can take some minutes.
Open up the EB console to check status.
eb console
Lets open the app in the browser.
eb open
You should see the laravel welcome page.
Make some changes in e.g. the default welcome page view.
And deploy the changes:
eb deploy
# log into the EC2
eb ssh
# List all the docker containers.
sudo docker ps
# Get the "CONTAINER ID" of the "peec/laravel-docker-aws" and run this (replace XXXXXXXXX with the CONTAINER ID ):
# Lets run bash for one of the containers.....
sudo docker exec -i -t XXXXXXXXX /bin/bash
# List the laravel files
ls -al
# See the automatically generated environment variables:
cat .env
# all is fine, go out of the container
exit
# And go out of the EC2
exit
Just remove the file .ebextensions/cdn.config
.
git add --all
git commit -m "remove cdn"
eb deploy
Just remove the file .ebextensions/elasticache.config
. Beanstalk will skip creating the elasticache resource. Standard cache will be used instead.
git add --all
git commit -m "remove elasticache"
eb deploy
- Go to the AWS web console
- Find the Elastic Beanstalk service and click into your environment.
- Add laravel specific environment variables under "Configuration -> Software Configuration".
Go to the AWS web console and add these environment variables under "Configuration -> Software Configuration":
- MAIL_DRIVER:
ses
- SES_KEY:
YOUR_AWS_ACCESS_KEY
- SES_SECRET:
YOUR_AWS_SECRET_KEY
By default the region is region
inside the laravel/config/services.php ("ses" section) so you need to edit this in your app to your needs and redeploy.
Just install this custom notification service provider:
https://github.com/peec/aws-laravel-notification
Configure the keys as you want.
- Load balancer required
- EC2 required
- Elasticache
- Cloudfront
- S3
As you can see, the monthly costs can be 100$ easily.
The pricing depends on how you configure .ebextensions folder.
If you have followed the configuration steps explained, it's just as you would do normally:
Route::post('/avatars', function (){
request()->file('avatar')->store('avatars');
return back();
});
Try the following:
php artisan make:job TestQueue
app/Jobs/TestQueue.php
class TestQueue implements ShouldQueue
{
...
public function handle()
{
Log::info('SQS run the job....' . date('r'));
}
....
}
Create a route
Route::get('/queue-test-job', function (){
$job = (new \App\Jobs\TestQueue())
->delay(\Carbon\Carbon::now()->addMinute(1));
dispatch($job);
return back();
});
Deploy
eb deploy
Visit /queue-test-job
in the production url. Wait a minute and download log files from AWS Web console. You should see 'SQS run the job...'.
In the aws web console (beanstalk environment) you can download log files to troubleshoot (via Logs -> Request logs -> Last 100 lines)
The Error:
An error occurred (AuthFailure) when calling the DescribeInstances operation: Authorization header or parameters are not formatted correctly.
configure-instance-resources:ERROR: Could not get json from aws ec2 describe-instances
Solution:
Go to Dockerrun.aws.json and configure AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY . Also the keys associated to a specific key set must have access to describe resources.. See Custom policies
above.
Redeploy eb deploy
.