GitLab CI Deployment for PHP Applications to AWS Elastic Beanstalk-Automated QA/Test Environments

How to deploy customisable PHP Applications to AWS Elastic Beanstalk on GitLab and make it possible to deploy on any branches as standalone application version.

Ismail Atkurt
Voices of PlusDental
11 min readOct 25, 2020

--

Introduction

The purpose of this article is to assist people who want to make use of GitLab CI pipelines and deploy PHP applications to AWS Elastic Beanstalk environment seamlessly. Although it is not intended to do live/production deployment via this guide, it is still very possible to achieve.

Initially I was using this approach only for my personal projects but then in our company, it is needed to create QA environments for separate tickets we implement and serve them on isolated environments. So that every person in QA team would have chance to create a new environment on any branch they wish and test it in an isolated environment.

Final Environment URL that we want to achieve will look like https://jira-123.eba-nh2qkppi.eu-central-1.elasticbeanstalk.com/ however if you configure your CNAME in Elastic Beanstalk then you can have more readable domain names.

To sum up; at the end of this article, you will have a system such that anyone can create or update an environment in AWS Elastic Beanstalk on any branches you have in GitLab. The sweetest part of it, it ain't gonna cost much and will be very fast and convenient ;)

Requirements

  • An account on GitLab. You should be able to push your projects/commits to your own private repositories and set CI variables. To achieve this your SSH key must be added to GitLab account. If you don’t have it yet please follow the steps here: https://docs.gitlab.com/ee/gitlab-basics/create-your-ssh-keys.html
  • An account on AWS (Free Tier would be enough for this sample project)
  • If you would like to see the app working on your local, you will need to install Docker engine, if not then you can skip the part Run PHP app on local section.
  • NO COST! By the help of free GitLab account and AWS free tier you don’t need to pay anything to follow this guide.

Let's Create a simple PHP project

Slim Framework is used to create our sample PHP application. Basically there is one controller named UserController consists of 2 methods; index and show which return all users and single user with given ID, respectively. These 2 methods stand there just to demonstrate our Nginx configuration will successfully handle different routes on AWS environment.

Sample PHP Project folder structure

Repository can be found here publicly and here is the screenshot of basic structure. Since it is out of scope of this article, I will skip explaining how Slim and controller etc work in our Sample PHP App.

Next step is to fork base repository and then pull it to our local;

  • Fork the repository to your user on GitLab.
  • Locate your terminal to a proper place on your system and execute git clone as written below by changing url to your forked repository.
cd /to/proper/location/for/projectsgit clone git@gitlab.com:ismail-atkurt/gitlab-ci-deployment-to-aws-elastic-beanstalk.gitcd gitlab-ci-deployment-to-aws-elastic-beanstalk

Run PHP app on local

Skip this step if you have no intention to run sample application on your local.

You will see docker directory and docker-compose.yaml files as well. Those are just helpers for our local environment in case we wanna see it live. They will never be used while deploying the app or after on AWS environment.

Let’s start with the local environment and make sure the app is working properly. Assuming your terminal is inside the project root directory;

docker-compose up --build

That should take just a couple of minutes and once it is finished just visit http://localhost/users on your browser. You are expected to see list of users in JSON format as below.

{“1”:{“first_name”:”Mike”,”last_name”:”Litoris”},”2":{“first_name”:”Peter”,”last_name”:”Ennis”},”3":{“first_name”:”Thomas”,”last_name”:”Fister”}}

http://localhost/users/2

{“first_name”:”Peter”,”last_name”:”Ennis”}

Looks alright!

Now we know the app worked fine and it is time to move to setting up our AWS Key/ID and GitLab CI variables.

Create AWS Key/ID in IAM

For the sake of security we better add a new user to AWS IAM and create an access key/id from that user.

Creating AWS IAM User
  • Click on Create Group
Creating New Group on IAM
  • Search for AWSElasticBeanstalkFullAccess permission and select, enter your Group Name such as GitLabCIDeployment
Naming group and setting permission
  • You may skip Tags section and finalise creating user
  • Download provided CSV file or write down your Access Key and ID to somewhere secure. Because you will not be able to access Secret KEY later on.
Download Access ID/Key after creation

Create AWS Elastic Beanstalk Application

In this article we will use AWS application name as SamplePHPApplication.

Select Platform on AWS Elastic Beanstalk Create Application page
  • By default Application code section is selected as Sample application, just leave it as it is.
  • Click on Configure more options button at the bottom.
  • Be sure Presets is selected as Single instance (Free Tier eligible)
Select Single instance (Free Tier eligible) for Presets
  • Click Edit button in Software configurations.
  • Set Document root as /public and make sure Nginx is selected as Proxy server and Memory limit equals 256M.
  • Click on Create app at the bottom and that's all for our EB Application.
  • By default AWS will create an initial environment. Let's wait on this app creation logs page for couple minutes. Once it is done go to Environments page.
  • Terminate the environment which create by AWS as default
  • All necessary steps are done now and we can move on to setting up GitLab.

Setting up GitLab CI Variables

Now we need to add our Key and Secret(some calls it key/id or id/secret) into our GitLab settings so that it will be exposed/accessible on CI runner and then we can use it while deploying with `eb deploy` command. Open your repository on GitLab and go to CI Settings page.

  • Click on Add Variable button and make sure Protected Variable is not selected then add AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY naming convention is up to you but we will be following these variable names throughout the article.

Configuring AWS specific files

By the help of .ebextensions we have flexibility to configure almost everything on our environments (EC2 instances). Let's investigate our ebextensions configuration files.

01_setup_swap.config & setup_swap.sh files are placed to ensure our libraries will be included successfully. For PHP projects EB will run composer together with your composer.json file. Depending on your external libraries included in composer file, it may take too much resource on memory and eventually exhausts/fails. To prevent this having swap configuration would help. Let's go through the files!

  • There is a file in .ebextensions directory, named as 01_setup_swap.config the contents will tell AWS to execute setup_swap.sh (which we will create it in the next step) shell script on EC2 instance. All configuration files we have in .ebextensions directory will be executed in order based on the numbers we prefix. In this case 01 and it will be executed as the first config file.
container_commands:
01setup_swap:
command: "bash .ebextensions/setup_swap.sh"
  • Next file in is setup_swap.sh in .ebextensions directory, and it will take care of our swap configuration on EC2 instance.

02_install_php_redis.config file will take care of installing PHP Redis extension.

We will install only 1 extension on our EC2 instance just to examine how to install any extension that you may need for your projects. If you don't need any extension, you may just remove the file safely.

03_install_ssl_certificate.config, this file was especially added to the article and project. Back in days, I have spent quite time to install free SSL certificates for my environments. Thus I hope it will help someone out there and will save the day :)

We will not cover the contents of this file since it might be long and out of scope. If you have no intention to install SSL certificates just remove the file and configurations in nginx.conf file (we will discuss it in next steps.)

For the folks who want to keep SSL certificate creation;

  • Replace email address with your address.
  • Replace Elastic Beanstalk subdomain (in our sample it is written as eba-nh2qkppi.eu-central-1.elasticbeanstalk.com) with yours. But be careful not to remove `{\”Ref\”: \”AWSEBEnvironmentName\” }`. that refers to specific Environment Name for our first initial Environment it is Samplephpapplication-env. You can find your own subdomain to replace on AWS Environment page.

Region Specific Configuration File

env-eu-central-1.config file is executed only for EU-Central-1 region. And as an example, we only have document_root: /public and memory_limit: 256M

Configure Application Config file

You will notice a file named config.yaml in .elasticbeanstalk directory. There are 2 things you need to make sure about the contents of ;

  • application_name should be set as Samplephpapplication or whatever you have named your Elastic Beanstalk application.
  • default_region should be set as whatever region you prefer. However for this article/project it is configured as eu-central-1

Nginx Configuration file

Another crucial file is nginx.conf and placed in .platform/nginx/ and includes possible configurations for our Nginx reverse proxy.

If you have already decided to use SSL certificates then you can just keep the file unchanged and skip this section.

If you won't use SSL certificates;

Change

listen 80;
listen 443 ssl default_server;

into

listen 80 default_server;

and also locate the config block as below and remove them.

ssl_certificate      /etc/letsencrypt/live/certificates/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/certificates/privkey.pem;
ssl_session_timeout 5m;
ssl_protocols TLSv1.1 TLSv1.2;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
ssl_prefer_server_ciphers on;
if ($ssl_protocol = "") {
rewrite ^ https://$host$request_uri? permanent;
}

GitLab CI file

Ideally you don't need to change anything in GitLab CI file, named as .gitlab-ci.yml. However based on your preferences you may want to change couple things.

  • CI script takes first 8 characters of branch names. For instance if you have your current branch name as JIRA-123-adding-some-cool-feature then script takes jira-123 and create environment in AWS EB with this name. That means your environment URL will have jira-123 as subdomain. So if you want to change number of characters to be taken find line below and modify 1–8 accordingly.
ELASTIC_BEANSTALK_ENVIRONMENT_NAME=$(echo $CI_COMMIT_REF_NAME| cut -b 1-8)
  • If you branch name is less than 4 characters, AWS will not accept it as an environment name. In such cases script will add a prefix to branch name. Let's say your branch name is dev and script will add prefix sample-application- . Your final environment name and URL will be sample-application-dev. To change it; find the line below and apply the necessary changes as you wish.
- if [[ $STRLENGTH < 4 ]]; then ELASTIC_BEANSTALK_ENVIRONMENT_NAME=$(echo "sample-application-${ELASTIC_BEANSTALK_ENVIRONMENT_NAME}"); fi
  • If you have no plan to use .env files for your PHP project then just find and remove the line below. Additionally you can safely delete .env.example file in the project.
- envsubst < .env.example > .env
  • In case if you deleted these then feel free to remove installation of gettext-base package since it is not needed anymore. It basically takes environment variables that are provided by GitLab project settings to the runner and do the replacement in .env.example and creates the new environment file .env for PHP usage.
- apt-get install zip unzip libzip-dev git gettext-base python3-pip -y
  • There is final line that you may want to change. The line below just prints new or updated Environment URL to pipeline. It is currently using my own AWS Application Subdomain, so better to change it to yours.
- echo "https://$ELASTIC_BEANSTALK_ENVIRONMENT_NAME.eba-nh2qkppi.eu-central-1.elasticbeanstalk.com"
  • Note that; if the environment/branch already created on AWS EB Application, then script will use the same environment and update it instead of terminating and creating a new one.
  • Finally we have 1 more stage in CI script. It is terminateEnv. After you created your environment you can terminate it easily in GitLab CI Pipelines without logging into AWS Console.

Time to create our first Environment

  • Assuming you already pulled the repository and made all changes we have discussed above steps.
  • Go to your terminal and navigate to project root and execute the lines below.
- git checkout -b JIRA-123-my-initial-deployment
- git add .
- git commit -m "Initial Deployment"
- git push origin JIRA-123-my-initial-deployment
  • Now go to your project on GitLab and open CI Pipelines page and you are supposed to see below screens one by one.
Click on blue button with text "running"
Initialisation of script
Successful deployment message

Finally we have our new environment running on https://jira-123.eba-nh2qkppi.eu-central-1.elasticbeanstalk.com/ as totally isolated and together with free SSL Certificate installed.

Conclusion

First of all, just to remind the purpose of this article; once the steps are followed you will have a GitLab CI Pipeline to deploy any branches in your project to isolated EC2 environments on AWS Elastic Beanstalk. That can be used either to have different versions of your application or to enable QA teams to have easy and convenient way of creating isolated test environments. In other words, you will have Automated QA/Test Environments for your PHP Projects on AWS Elastic Beanstalk Application.

I have followed all the steps in the article by myself from scratch and ensured it works fine. Hoping it will work all good for you guys as well :) In time, depending on comments, there would be revisions and fixes for sure. But let's hope the best and it is gonna work at first try for anyone..

Feel free to contact me via Twitter, E-Mail or any other platform…

--

--