Deploy Laravel to AWS Beanstalk with GitLab CI (including testing, and staging/production branches)

mjsarfatti

Manuele J Sarfatti

Posted on September 18, 2019

Deploy Laravel to AWS Beanstalk with GitLab CI (including testing, and staging/production branches)

Photo by Dewang Gupta on Unsplash

This is a tutorial the story of how one night I set out to streamline my deployment process and found myself hours later at 4am building new docker images. Hopefully it contains valuable information for those of you looking into setting up CI/CD for your Laravel app, so that you won't have to stay up all night like me.

What we (I) want(ed) to accomplish

Quite simply, that on every push to master or staging phpunit tests are run automatically.

Additionally, that if all tests pass on the branch staging the app is automatically deployed to Elastic Beanstalk.

I left the master branch deployment to "manual" because I don't want an accidental push to deploy a new feature in production.

Requirements

I'm trying to keep this concise and to the point, and for this reason it's expected that you already have the following ready (if you don't, don't worry, bookmark this article and come back later):

  1. A working Laravel app in your local environment, with its git repository on GitLab, and two branches: master and staging (all pretty standard so far)
  2. Two configured and running environments on AWS Elastic Beanstalk (you probably want them inside the same Beanstalk Application, I found this guide very useful for this part)
  3. A IAM user in your AWS Account with Elastic Beanstalk Full Access permissions (we will need the Access/Secret Keys of this user, make sure you note them down somewhere)
  4. That you have installed the EB CLI and ran eb init

0. A bad default

If you've run eb init you'll notice that you now have a folder named .elasticbeanstalk. This folder is essential for GitLab, but for some reason the command also added a few lines in your .gitignore effectively making it impossible for GitLab to do its job. If you see something like this in your .gitignore:

# Elastic Beanstalk Files
.elasticbeanstalk/*
!.elasticbeanstalk/*.cfg.yml
!.elasticbeanstalk/*.global.yml

go ahead and delete those lines.

1. Getting started

Let's start from the end of the process, just to spice things up a little bit.

After every deployment of a Laravel application (and this is true for any server setup, not just EB) we need to make sure that our remote (gitignored) /vendor folder has all the needed packages, and that the structure of the remote database is up to date. This is done by running two commands: $ composer install and $ php artisan migrate.

Additionally, in our specific setup we need to tell EB that our app is served from the folder /public, and since SSL is morally mandatory by now we also probably want to tell Apache to redirect all http requests to https.

This is a basic setup that should work well for 98% of the projects.

Now, we could ssh into the remote machine and run a few commands, but fortunately Elastic Beanstalk provides us with Configuration Files (a set of YAML files in a .ebextensions folder that instruct the remote server on AWS to run certain commands right before and right after deployment) that suit our CI/CD workflow perfectly.

You are welcome to explore the official documentation , but again fortunately yours truly went ahead and published the needed files on GitHub for your peruse.

In the root of your project run:

$ svn export https://github.com/mjsarfatti/laravel-eb-gitlab-ci/trunk/.ebextensions

This will create a .ebextensions folder, with four files inside. Feel free to inspect the files, and note that I went ahead and added a couple of goodies in there.

2. The GitLab YAML

To instruct GitLab on how and what to run during deployment we need to create and commit a .gitlab-ci.yml file. How GitLab CI works is out of the scope of this tutorial, but the file itself is pretty self-explanatory.

Add it to your project:

$ svn export https://github.com/mjsarfatti/laravel-eb-gitlab-ci/trunk/.gitlab-ci.yml

Then open up this file, and make sure to set the variable MYSQL_DATABASE to whatever your testing database name is (you'll probably find it in your phpunit.xml file).

A useful bit to know 🎁

At times you may want to deploy even if tests fail (for example if you are testing the deployment itself). Or you want to push a simple edit (like a typo in your README, or in a warning message) and you don't want to use precious CI minutes for useless testing.

Our .gitlab-ci.yml has a friendly rule that will skip testing if the commit message contains the phrase [skip tests] (or a variation such as [skip test] or [skip-tests].

3. Setting up GitLab CI for success

Log in to your GitLab account, and in your project's Settings > CI / CD add two variables, one named AWS_ACCESS_KEY_ID with, you got it, your IAM user access-key-id, and one named AWS_SECRET_ACCESS_KEY with... well you know what.

And that's it 🥳

Push away and enjoy your well deserved peace of mind.

Oh, I was almost forgetting

So how is it that I spent the night building docker images, you ask?

Well, it all started when I discovered these AMAZING Docker images by Eduardo Bizarro preconfigured with PHP, Composer, Node and Yarn and available for PHP 7.1, 7.2 and 7.3: https://github.com/edbizarro/gitlab-ci-pipeline-php/

Crazy, I thought (it was still only 11pm), I'll just include those in my .gitlab-ci.yml and call it a night.

LOL.

The images don't include eb (why would they). So I tried to install it. But eb needs pip, and pip needs a few weird Python packages. Just apt-get, right? Wrong.

The images don't run on the root user (I guess it's a sane default?), which means I can't install any package during CI. I knew the way to go would be to fork the project, dive into their Dockerfile and customize it to my needs. But I've barely ever used Docker before, I didn't feel like embarking on a new adventure that late at night (funny, uh?).

So I fought, and I fought, and I fought. I tried all 3 Linux distributions they come with, and I tried all different methods to install eb, including installing all the tools in a local folder. Nothing worked.

So now it's 3am, and there I am with a simple choice: should I go to bed and rest or is this now the fight of my life?

You can imagine what happened.

I opened up https://hub.docker.com/, studied Bizarro's repo, studied the getting started and, lo and behold, an hour later I had:

  • successfully reconfigured Bizarro's images to install EBCli, AWSCli and CLI53 (because why not)
  • published the new image to Docker
  • successfully used my first ever new Docker image to deploy my Laravel app to Elastic Beanstalk 🎉🎉🎉

So if you notice in our .gitlab-ci.yml file we are using image: mjsarfatti/gitlab-ci-pipeline-php-aws:latest.

Please note: this is a PHP 7.2 Debian-based image. You can't install packages. It's not thoroughly tested. But it works.

Big thanks to

As always, I would be nowhere near where I am if it wasn't for the material, software and tutorials that accompanied me last night. In no particular order, big thanks to:

Comment below if this guide has been useful, and if you face any problem!

-- EDIT --
Added rennokki/laravel-aws-eb link

💖 💪 🙅 🚩
mjsarfatti
Manuele J Sarfatti

Posted on September 18, 2019

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related