DEV Community

Cover image for Deploy a Docker-ized Laravel Application to AWS ECS with CodeBuild
Gbenga Oni
Gbenga Oni

Posted on • Edited on

Deploy a Docker-ized Laravel Application to AWS ECS with CodeBuild

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

  1. A Windows, Linux or Mac PC with Docker installed
  2. An AWS Account
  3. Laravel
  4. 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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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 .
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

The container will now run with accessibility on port 8001:

Laravel Docker running on port 8000

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 
Enter fullscreen mode Exit fullscreen mode

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 ....
Enter fullscreen mode Exit fullscreen mode

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.
console

Click on the Create Repository button.

console

Define the Repository name. I have named mine gbxnga/laravel-docker-aws and click Create Repository.

ECR 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
Enter fullscreen mode Exit fullscreen mode

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.

AWS CodeBuild

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.

Create a CodeBuild build project

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.

Specify CodeBuild source

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.

Event type

Scroll down to the Environment section

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.

Image

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.

Laravel AWS CodeBuild project

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.

Image

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.

Image

We will attach the Amazon managed AmazonEC2ContainerRegistryPowerUser policy to the service role.

Click on Attach Policies button.

Attach policies

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.

CodeBuild building a docker image

Scroll down and Click on tail logs to see the build logs

AWS CodeBuild process logs

Wait for the build process to complete. It make take a few minutes.

Image

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.

Image

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.

AWS Elastic Container Service (ECS)

Create Amazon ECS Cluster

Click on Clusters on the side menu.

AWS ECS Cluster

Click on the Create Cluster button

Create AWS ECS Cluster

Select EC2 Linux + Networking, and proceed to the next step.

Create AWS ECS Cluster

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.

AWS ECS creating a Cluster

Once the Cluster has been successfully created, click on the View Cluster button at the top to view the created cluster.

Image

Clicking on the ECS Instances tab, you can see the t2.micro instance provisioned in the cluster.

ECS Instances

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.

Image

Select EC2 and proceed to the next step.

Image

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.

Image

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

Image

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.

Image

Now that the task definition has been created, we need to run the task.

Click on Actions and select Run Task.

Image

Select EC2, leave other parameters as is and click Run Task.

Image

The task is now running on the laravel-docker-aws-cluster that contains the t2.micro instance.

Navigate to EC2 dashboard on AWS console.

Image

Copy the IP of the EC2 instance and paste it in your browser.

Laravel Docker running container on AWS ECS

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)

Collapse
 
taelkir profile image
Tom Stanley

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?

Collapse
 
imkrunal profile image
Krunal Shah

Did you found the solution? I am having the same issue

Collapse
 
taelkir profile image
Tom Stanley

No, sorry, ended up not using Docker and deploying my site through Laravel Vapor.

Thread Thread
 
imkrunal profile image
Krunal Shah

I spent 2 hours and figured out how to do it. I am using code pipelines. Here is the step you need to do.

  1. Disable the auto-deploy on push with code build.
  2. Add artifacts to buildspec.yml
  3. Create a service in ECS
  4. Create pipeline ( Source: Select Github or any version control, Build: Use code build you created from this tutorial, Deploy: Select your ECS )
Thread Thread
 
japhernandez profile image
John Piedrahita

Please, you share this knowledge with us in a guide.

Collapse
 
christopherarter profile image
Chris Arter

How do you manage secrets / ENV in the image?