Containers have gradually become a wide-spread and widely accepted technology. They are a standard unit of software that encapsulates your code, together with the dependencies required to make them function. With containers, you can be certain your system will function in similar manner even in different environments.
A Docker container image is a lightweight, standalone, executable package of software that includes everything needed to run an application: code, runtime, system tools, system libraries and settings.
As cloud services have become an integral part of application deployment, the need to effectively build, deploy and maintain containers in the cloud has become imperative.
There are a number of services designed to help developers achieve this, one of which is AWS Elastic Container Service — A highly available and high performance container orchestration service. It allows you to run, scale and secure docker containers in AWS.
In this tutorial, you will learn how to docker-ize a Laravel application. You will also learn how to deploy the image built to EC2 with AWS Elastic Container Service.
We will use AWS CodeBuild — “a fully managed continuous integration service that compiles source code, runs tests, and produces software packages that are ready to deploy” — to build our container image whichwill be stored on AWS Elastic Container Registry(ECR), an Amazon managed docker container registry — think Docker hub.
Requirements
- A Windows, Linux or Mac PC with Docker installed
- An AWS Account
- Laravel
- A Github Account
Get Composer
Firstly, we need to download Composer, a PHP package manager. We will use Composer to install Laravel and other dependencies. If you don’t already have it installed, you can download it here.
Install Laravel
Create a fresh Laravel application using composer by running the following command.
composer create-project --prefer-dist laravel/laravel laravel-docker-aws
Docker-ize Laravel
Now that we Laravel installed, let's configure docker.
Create a Dockerfile
Create a Dockerfile
in the root of your Laravel application. A Dockerfile is a text document that contains all the commands a user could call on the command line to assemble an image.
FROM php:7.3.2-apache-stretch
LABEL maintainer="Gbenga Oni B. <onigbenga@yahoo.ca>" \
version="1.0"
COPY --chown=www-data:www-data . /srv/app
COPY .docker/vhost.conf /etc/apache2/sites-available/000-default.conf
WORKDIR /srv/app
RUN docker-php-ext-install mbstring pdo pdo_mysql \
&& a2enmod rewrite negotiation \
&& docker-php-ext-install opcache
Create a docker folder
Lets create a folder where all configuration files needed to build our docker image will be placed. Create a folder .docker
in Laravel root folder that will contain our Apache config file (vhost.conf
). The file will be copied into sites available folder on container at run time.
<VirtualHost *:80>
DocumentRoot /srv/app/public
<Directory "/srv/app/public">
AllowOverride all
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
The vhost.conf
file sets the document root for Apache to /src/app/public
, this way since our Laravel app will be copied to the /srv/app
folder, we can access it directly.
Build Laravel Application Image locally
Once you’ve created the Dockerfile
and the Apache configuration file in the .docker
folder. Our application is now ready to be built.
Navigate to the root folder of the Laravel app in the command line then run
docker build -t laravel-docker-aws .
The docker machine will now build the image. This may take some minutes.
Now that the laravel-docker-aws
image has been built, run the image with the following command:
docker run -it -p 8001:80 laravel-docker-aws
The container will now run with accessibility on port 8001:
Setup CodeBuild build spec file
According to AWS CodeBuild documentation, A build spec is a collection of build commands and related settings, in YAML format, that CodeBuild uses to run a build.
The buildspec.yml
file must be named as is and should be placed in the root of the project folder. However, you can give the buildspec file an alternate name or store it in a different location using the AWS CLI or AWS Cloudformation.
Create a buildspec.yml file and copy the code below into it.
version: 0.2
phases:
install:
runtime-versions:
php: 7.3
pre_build: # commands to be run before build
commands:
- echo Logging in to Amazon ECR....
- aws --version
# login to Elastic container registry
- eval $(aws ecr get-login --region us-east-1 --no-include-email | sed 's|https://||')
- REPOSITORY_URI=<YOUR ECR REPOSITORY URI HERE>
- IMAGE_TAG=<YOUR IMAGE TAG HERE>
build:
commands:
- echo Build started on `date`
- echo installing composer..
- composer install
- echo creating .env file..
- cp .env.example .env
- echo generating app key
- php artisan key:generate
- echo Building the Docker image...
- docker build -t $REPOSITORY_URI:latest .
- docker tag $REPOSITORY_URI:latest $REPOSITORY_URI:$IMAGE_TAG
post_build:
commands:
- echo Build completed on `date`
- echo Pushing the Docker image...
- docker image ls -a
- docker push $REPOSITORY_URI:$IMAGE_TAG
The build spec contains env, version, phases, artifacts, cache, etc. We will be using just version and phases.
The version which is required represents the build spec version. We will use version 2.0 — the recommended version. The phases
, also required, describes the command to be executed during the respective build phases.
For a more in-depth understanding of the build spec file, go here to read more.
In the install
phase, we specified a php 7.3
runtime version. The runtime-version parameter is required since we will be using the Ubuntu standard image 2.0
In the pre_build
phase, we check the aws cli version and also attempt to login to AWS Elastic Container Registry. we also define the registry repository URI and the image tag.
In the build
phase, we install composer, create the Laravel environment file and also generate an application key. Finally, we build the docker image and assign it a tag.
Note: make sure you specify the AWS region you’re working in in the pre_build
phase command that logs you into AWS ECR:
eval $(aws ecr get-login) --region us-east-1 ....
Lastly, in the post_build
phase, we push the docker image to the AWS ECR repository.
Our Laravel Application is now set for deployment.
Create an Elastic Container Registry Repository
Let's create an image registry where we will store the Laravel docker image.
Navigate the ECR dashboard on AWS Console.
Click on the Create Repository button.
Define the Repository name. I have named mine gbxnga/laravel-docker-aws
and click Create Repository.
Once that’s done, copy the URI of the repository and paste it in the pre_build
phase of the build spec file.
Also, Set the IMAGE_TAG = 1.0
pre_build:
commands:
...
- REPOSITORY_URI=650749741049.dkr.ecr.ap-southeast-2.amazonaws.com/gbxnga/laravel-docker-aws
- IMAGE_TAG=1.0
The ECR repository is now set. Container Images for our Laravel Application can now be stored on it.
Configure AWS CodeBuild
Let's configure CodeBuild on the AWS Console. Log in to the AWS Console and navigate to the CodeBuild dashboard.
You will probably see the above Get Started screen if you’re using CodeBuild for the first time in the current region.
Create a CodeBuild Project
Click on Create Project to create a build project. You will be taken to a build project creation page.
Give the project name. I have in this case named the build project laravel-docker-aws
. You can skip the Description
box as its optional.
Scroll down to the Source section below. This is where the source provider for the project will be specified.
Select Github in this case. Make sure you have pushed the Laravel project containing the dockerfile, docker folder and the build spec file we created.
Click on the OAuth button. You will be asked to authorize AWS CodeBuild with your GitHub account.
Once that's done, check “Repository in my GitHub account”, then select the repository you pushed the Laravel app to.
Scroll down to the Primary source webhook events section. Check the rebuild option then select the PUSH
Event Type. This tells CodeBuild to rebuild the image every time a PUSH is made to the selected GitHub repository.
Scroll down to the Environment section
Choose Managed image as Environment Image and also the Ubuntu Operating System. Select the Standard Runtime and the aws/codebuild/standard:2.0
Image. Check the Privileged box and also New Service Role.
Leave the Buildspec Section as is, but make sure the Use a buildspec file option is checked.
Leave every other section as is, scroll all the way down and click the Create build project button.
The CodeBuild project will now be created.
Create ECR access IAM Policy for CodeBuild
In the post build phase of the build spec file, we attempt to push the docker image to AWS ECR. To successfully achieve this, we need to authorize the necessary action for CodeBuild.
We will do this by creating an IAM Policy that grants full access to the AWS ECR repository we just created, then attach it to the service role that was automatically created for the code build project we just created.
Navigate to IAM on the AWS Console then click on Roles.
Look/Search for codebuild-laravel-docker-aws-service-role
role.
Note that its likely the default name given to the code build project service role may be different. But if you named your build project laravel-docker-aws, the service role name will most likely be the same as the above.
Click on the codebuild-laravel-docker-aws-service-role
role.
We will attach the Amazon managed AmazonEC2ContainerRegistryPowerUser policy to the service role.
Click on Attach Policies button.
Search for and select the AmazonEC2ContainerRegistryPowerUser
policy then click Attach Policy.
The policy will now be attached, granting CodeBuild power user access to the repository.
Build the Laravel Docker Image
Now that we have granted CodeBuild access to ECR, and we have configured our CodeBuild project, let’s initiate a build process for the Laravel docker image.
Navigate to the CodeBuild dashboard on AWS console. Click on the laravel-docker-aws
CodeBuild project, then click the Start Build button to initiate a build process.
Click on the build project to see more information on whats happening.
Scroll down and Click on tail logs to see the build logs
Wait for the build process to complete. It make take a few minutes.
CodeBuild has now successfully built and pushed our docker image to the ECR repository.
Navigate to ECR dashboard to see the list of images in the laravel-docker-aws repository
.
Copy out the Image URI. We will be needing it to setup ECS.
Setup AWS Elastic Container Service (ECS)
Now that we have created our docker image and pushed it to AWS ECR, we will make use of AWS ECS to run it.
Navigate to ECS dashboard on the AWS console.
Create Amazon ECS Cluster
Click on Clusters on the side menu.
Click on the Create Cluster button
Select EC2 Linux + Networking, and proceed to the next step.
Give a name to the cluster. I have, in this case, named it laravel-docker-aws-cluster
and select the t2.micro
EC2 instance type.
Leave every other parameter as is in the Instance configuration section and the Networking section.
Make sure that a new role is to be created in the Container instance IAM role section if you haven’t already created the ecsInstanceRolerole
.
Click the Create button at the bottom to initiate the creation process for the ECS Cluster.
Once the Cluster has been successfully created, click on the View Cluster button at the top to view the created cluster.
Clicking on the ECS Instances tab, you can see the t2.micro
instance provisioned in the cluster.
Create AWS ECS Task Definition
Task definitions specify the container information for your application, such as how many containers are part of your task, what resources they will use, how they are linked together, and which host ports they will use.
Click on Task Definitions on the side menu, then click the Create new Task Definition button.
Select EC2 and proceed to the next step.
Give the task definition a name. Leave the Task role and Network Mode parameters as is.
Scroll down to Container Definitions and click Add Container.
Specify a container name laravel-docker-aws-container
. Also specify image repository URI.
Make sure you specify the image tag(1.0 or the new tag if you chose to use another).
Specify a Memory limit of 128MiB
Scroll down to Port Mappings and specify the Host Port as 80
and Container Port as 80
. This means that port 80
on the EC2 instance will be mapped to port 80
on the Laravel docker container.
Leave every other thing as is, scroll down and click Add.
Once you have added the container, scroll down and click on Create to create the task definition.
Now that the task definition has been created, we need to run the task.
Click on Actions and select Run Task.
Select EC2, leave other parameters as is and click Run Task.
The task is now running on the laravel-docker-aws-cluster
that contains the t2.micro
instance.
Navigate to EC2 dashboard on AWS console.
Copy the IP of the EC2 instance and paste it in your browser.
Our Laravel docker container is now running.
That’s it. You’ve successfully deployed and run a docker image on EC2 with AWS ECS.
Conclusion
In this article, we set up docker for a Laravel application. We also implemented CodeBuild to build the docker image, pushing it to Elastic Container Registry. Finally, we setup Elastic Container Service to run the image on Amazon EC2.
Top comments (6)
Thanks for the tutorial! All works great.
Just one question - I've got the ECR image updating whenever I push a code change, but this doesn't seem to update the ECS cluster unless I stop the task and start a new one. Did I miss a step or is there something else I need to do to trigger the ECS instance to update with new code changes from the image?
Did you found the solution? I am having the same issue
No, sorry, ended up not using Docker and deploying my site through Laravel Vapor.
I spent 2 hours and figured out how to do it. I am using code pipelines. Here is the step you need to do.
Please, you share this knowledge with us in a guide.
How do you manage secrets / ENV in the image?