Automated multi region deployments using Github Actions, Laravel Vapor and Cloudflare cover image

Automated multi region deployments using Github Actions, Laravel Vapor and Cloudflare

Tom Hatzer • December 13, 2020

laravel vapor cloudflare aws

ATTN: I'm writing this as a brain dump so some places may be missing information. If you find a mistake or would love to get more information, please get in touch with me on twitter. Thanks!

Today I would like to show you how to get automated multi region deployments working with Github Actions, Laravel Vapor and Cloudflare.

Ingredients

  • 1 Laravel Vapor account
  • 1 Cloudflare account
  • 1 AWS account
  • 1 Domain (also added to Vapor including certificates)
  • 1 Github account
  • 1 Source repository on Github
  • 4 tbsp love and affection
  • 2+ Regions

Difficulty

Low to mid

Duration

About 30 minutes to 1 hour

Temperature

Your body temperature and heart rate should stay at the usual levels.

Contents

  • Project setup in Vapor
  • Project setup for other regions in Vapor
  • Environment setup in Vapor
  • Vapor configuration files
  • Vapor API access token
  • Github repository secrets
  • Github action auto deploy
  • Cloudflare Load Balancer setup
  • Final step

Introduction

We will start with the base, a working Laravel Vapor account. I won't go into details as this is already covered in the docs here. After you've set up your Laravel Vapor account, let's create our project in the regions we want to deploy it.

In my case we will use the following regions for our project:

  • US North Virginia (us-east-1)
  • EU Frankfurt (eu-central-1)
  • ASIA Singapore (ap-southeast-1)

Steps

Step 1 - Project setup in Vapor

Log into Laravel Vapor, select your Team and in your Dashboard click on the Create Project button.

Fill the modal windows input fields with your desired project name and select the first region eg.

After creating your project, a modal window will appear with an example vapor.yml file. Copy the content from this window into a temporary file on your local disk as we will need this later on.

Laravel Vapor will create 2 environments for you: Production and Staging. For simplicity we will only keep production here and delete the Staging environment.

Step 2 - Project setup for other regions in Vapor

Repeat Step 1 for the other regions you have. In my case it would be eu-central-1 and ap-southeast-1.

Step 3 - Environment setup in Vapor

Now add/edit the environment variables (content of your .env file) for each environment in each project.

A single global database is a nice to have. AWS offers Aurora Global Databases with active/active sync, but they are pretty expensive when deployed in multiple regions. For 3 regions with high availability you would have to pay around 2.4k USD per month for a single global cluster.

You can also create databases in Laravel Vapor and use them for your project if you don't need active synchronisation.

Step 4 - Vapor configuration files

Create vapor-<region>.yml (replace <region> with your custom region) files with the content that you received earlier in the Vapor Dashboard after creating the projects.

In my case there would be 3 .yml files:

  • vapor-us-east-1.yml
  • vapor-eu-central-1.yml
  • vapor-ap-southeast-1.yml

Your files could look like this one here:

id: 1234
name: global-demo-project-ap-southeast-1
environments:
  production:
    domain: mydomain.com
    gateway-version: 2
    memory: 512
    cli-memory: 512
    cli-timeout: 15
    runtime: 'php-7.4'
    build:
      - 'COMPOSER_MIRROR_PATH_REPOS=1 composer install --no-ansi --no-dev --no-interaction --no-suggest --optimize-autoloader'
      - 'php artisan event:cache'
      - 'yarn && yarn prod && rm -rf node_modules'
    deploy:
      - 'php artisan migrate --force'

Please be aware that you have to enter your own id depending on your project.

Step 5 - Vapor API access token

Now create a Vapor API access token by clicking your name in the upper right corner of the Vapor Dashboard, selecting My Profile and then clicking on API below the Account Settings headline.

Enter your tokens name and submit the form by clicking on the Add button. A modal window will appear with your token. Copy the content from this window into a temporary file on your local disk as we will need this later on.

Step 6 - Github repository secrets

Head over to github.com and into the settings of your repository. Select Secrets from the menu and click the New repository secret button on the right side. (Can't find it? The github docs will guide you the way.)

Enter the following details:

  • Name: VAPOR_API_TOKEN
  • Value: Enter the token you copied/saved in Step 5 into this field

Now click on Add secret to store the secret inside your repository.

Step 7 - Github action auto deploy

Let's set up our automatic deployment using Github actions.

For this, we will create the file .github/workflows/multi-region-deployment.yml in the repository with the following content:

name: Laravel

on:
  push:
    branches: [ "main" ]

jobs:
  vapor:
    name: Check out, build and deploy using Vapor
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Setup PHP (w/ extensions) & Composer
        uses: shivammathur/setup-php@v2
        with:
          php-version: 7.4
          tools: pecl
          extensions: bcmath, bz2, ctype, curl, fileinfo, gd, gmp, iconv, intl, json, mbstring, mysqli, openssl, pdo, pdo_mysql, pdo_sqlite, sodium, sqlite3, tokenizer, xml, zip
          coverage: none

      - name: Obtain Composer cache directory
        id: composer-cache
        run: echo "::set-output name=dir::$(composer config cache-files-dir)"

      - name: Cache Composer dependencies
        uses: actions/cache@v1
        with:
          path: ${{ steps.composer-cache.outputs.dir }}
          # Use composer.json for key, if composer.lock is not committed.
          # key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}
          key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
          restore-keys: ${{ runner.os }}-composer-

      - name: Install Vapor CLI Globally
        run: composer global require laravel/vapor-cli

      - name: Install Composer dependencies
        run: composer install --no-progress --no-suggest --prefer-dist --optimize-autoloader

      - name: Obtain NPM Cache directory (used by Laravel Mix)
        id: node-cache-dir
        run: echo "::set-output name=dir::$(yarn cache dir)" # Use $(yarn cache dir) for yarn

      - name: Cache NPM dependencies (used by Laravel Mix)
        uses: actions/cache@v1
        with:
          path: ${{ steps.node-cache-dir.outputs.dir }}
          key: ${{ runner.os }}-node-${{ hashFiles('**/yarn.lock') }} # Use '**/yarn.lock' for yarn
          restore-keys: ${{ runner.os }}-node-

      - name: Rename vapor.yml file
        run: mv vapor-eu-central-1.yml vapor.yml

      - name: Deploy using Laravel Vapor
        env:
          VAPOR_API_TOKEN: ${{ secrets.VAPOR_API_TOKEN }}
        run: /home/runner/.composer/vendor/bin/vapor deploy production

      - name: Remove vapor.yml file
        run: rm vapor.yml

      - name: Rename vapor.yml file
        run: mv vapor-ap-southeast-1.yml vapor.yml

      - name: Deploy using Laravel Vapor
        env:
          VAPOR_API_TOKEN: ${{ secrets.VAPOR_API_TOKEN }}
        run: /home/runner/.composer/vendor/bin/vapor deploy production

      - name: Remove vapor.yml file
        run: rm vapor.yml

      - name: Rename vapor.yml file
        run: mv vapor-us-east-1.yml vapor.yml

      - name: Deploy using Laravel Vapor
        env:
          VAPOR_API_TOKEN: ${{ secrets.VAPOR_API_TOKEN }}
        run: /home/runner/.composer/vendor/bin/vapor deploy production

Quick explanation:

  • Checkout the repository code
  • Set up php 7.4 and composer
  • Obtain composer cache directory and cache dependencies
  • Install Vapor CLI
  • Install project dependencies using composer
  • Obtain npm/yarn cache directory and cache dependencies
  • Rename vapor.yml file and deploy to desired stage (3x)
  • Done!

After you've done this, Github should trigger your first deployment and set up your first environments on AWS.

Step 8 - Cloudflare Load Balancer setup

Like in the description we will be using Cloudflare for the regional load balancing. The whole process is also described in the Cloudflare docs including screenshots.

So first, you will have to point the nameservers of your domain to cloudflare. To do this, follow these steps:

  • Log into your Cloudflare account.
  • Add your domain if you haven't done this already.
  • Cloudflare will show you the nameservers you have to point your domain to. Set your nameservers for your domain accordingly.
  • Wait a little bit until Cloudflare was able to resolve your domain using their nameservers. This can take up to 48 hours, but most of the time it takes only a few minutes or more.
  • The first thing afterwards should be, setting your SSL settings to Full. Do not set it to Full (strict) as this will cause problems with monitoring for the load balancer.
  • Then click on the Traffic item in the top menu.
  • The submenu has the Load Balancing item for you available to select. Click on it.
  • Create a new Load Balancer of type Dynamic steering to be able to use Geo steering.
  • Set your Hostname for the Load Balancer. This can be a subdomain or your main domain (eg. www.mydomain.com or mydomain.com).
  • Activate Session Affinity as you may encounter problems with sessions or likewise if you use multiple regions.
  • Click on Next.
  • Now we are going to add the Origin Pools. These are the API Gateway domains you will find in your Vapor environments. For every region you have, add one Origin Pool.
    • Go back to your Vapor Dashboard, select the Production environment for the first region. I'll be using the domain directly without subdomain, so I'm copying the hostname without www like d-kx24nowri2.execute-api.ap-southeast-1.amazonaws.com. Pay attention to copy the correct hostname as this will otherwise cause problems later on.
    • Repeat the previous step for every region you have on your list. Also assign them a speaking name so you can differentiate them later on.
    • You can also add monitoring to your Origin Pools. We'll do this right below.
  • Click on Next.
  • Let's add a Health Check to our Origin Pools.
    • Type: https
    • Path: /
    • Method: GET
    • Expected Codes: 2xx
    • Header Name:
    • Host: mydomain.com
  • You can now add that Health Check to all of your Origin Pools.
  • Click on Next.
  • Now we're going to configure the Geo Routing:
    • Select the Region where you want to bind the Origin Pool to. Do this for every region that you want to support and every Origin Pool you have.
  • Click on Next.
  • Review your settings and check if your Origin Pools are Healthy. Everything should be green and Healthy. If not, you can send me a message on twitter if you need assistance.
  • Click on Done.

Step 9 - Final step

Now check your domain, you should be able to view your deployed website, delivered from the Origin Pool you selected for your region.

Notes

  • If there are parts of information missing, send me a quick message on twitter (link on top of this article).
  • Please use the docs of the specific products you are using if you need any further help or want to implement additional features.

References