October 10, 2019

How to deploy Serverless applications using Github Actions

In this article I'll show you how to deploy Serverless applications using Github Actions. Github Actions allow you to test, build and deploy your applications in easy way.

Why Github Actions?

Previously I worked a lot with Laravel Framework and used Envoyer for all my deployments. I wanted to find a similar tool for Serverless Framework, so I made a list of important features I'd like to see in a deployment tool:

  • Easy to use;
  • Not too expensive;
  • Ability to setup for client's projects;
  • Ability to manage environment variables (like passwords and secret api keys);
  • Should work with Github;
  • Flexible configuration;

I've tested a number of tools and decided to go with Github Actions for the following reasons:

  • Its provided by Github;
  • Its completely free;
  • Its pretty flexible;
  • I can easily configure custom steps for each project using YAML configuration file;
  • Github provides a secure way to store environment variables;

So, how deployment process looks like?

In most cases deployment of Serverless application consists of number of steps:

  1. Checkout a repository.
  2. Configure container (install Node, NPM, Serverless Framework, etc).
  3. Connect serverless to provider (AWS/Azure/Cloudflare/etc).
  4. Create environment file with all our secret variables.
  5. Install NPM Dependencies.
  6. Deploy application.
  7. Optional - Build and deploy frontend assets to S3.

Just for example, let's setup automatic deployment to development environment on push to master and deployment to production on version tag release (e.g. v1.0.0, v1.2.0, etc).

Let's do some demo

For demo purposes we'll be using a Node.js application hosted on AWS. You may easily adopt the workflow to work with other languages and service providers.

Demo application

First, let's create a simple application using Serverless framework using the following command:

serverless create --template aws-nodejs

Then let's create a env.yml file to store secret variables. This file should never be pushed to our git repository as it contains private api keys and other sensitive information. However, it's a good idea to store in git repository a copy of env file (e.g. sample.env.yml) with some placeholder data, so it will be easier to create an actual env.yml file.

In our case, let's store appName value in our env.yml file:

prod:
  appName: "Serverless App"

default:
  appName: "DEV Serverless App"

As you can see, for each application environment we'll be using different values. So when application will be deployed to production, the app name will be "Serverless App", and when application will be deployed to development environment, the app name will be "DEV Serverless App".

Then, let's modify our serverless.yml file:

service: gh-actions-demo

custom:
  defaultStage: dev
  environment: ${file(env.yml):${self:provider.stage}, file(env.yml):default}

provider:
  name: aws
  runtime: nodejs10.x
  stage: ${opt:stage, self:custom.defaultStage}
  environment:
    appName: ${self:custom.environment.appName}


functions:
  index:
    handler: handler.index
    events:
      - http:
          path: /
          method: get

A few notes regarding the file above:

In custom section we define default stage which will be used on application deployment, and loaded variables from env.yml file depending on application stage.

In provider section we added a stage key (if no stage is passed via command line - default stage from custom section will be used by default). And created appName environment variable, so we'll be able to use it in our Lambda functions. The value of that variable is defined in our env.yml file and passed from custom section.

And finally, we defined index function in handler.js with the following content:

'use strict';

module.exports.index = async event => {
  let html = `
    <h1>Welcome to ${process.env.appName}</h1>
    <p>Stage: ${event.requestContext.stage}</p>`;

  return {
    statusCode: 200,
    headers: {
        'Content-Type': 'text/html',
    },
    body: html
  };

};

As you can see, this function outputs simple text. When deployed to production it will show "Welcome to Serverless App. Stage: prod" and when deployed to dev it will show "Welcome to DEV Serverless App. Stage: dev".

Create Github Actions

There are few ways to create Github Actions: you may go to your Github Repository, click Actions and add a new workflow. Or you can define a workflow yaml files and store them in .github/workflows in your application.

In our case we'll create a two workflows. One of them will be used to deploy application to dev environment on each push to master:

And the second workflow will be used to deploy application to prod environment every time a new tag with specific pattern is released:

How to store secrets on Github

To store secret environment variables, go Settings of your Github repository and choose Secrets from the left sidebar.

First, we need to store AWS Key and AWS Secret in order to be able to deploy our application using Serverless Framework.

Second, we need to store secret variables listed in our env.yml file. We can do a little trick here: rather than storing each secret environment variable separately, we can store the content of entire env.yml file as show on the screenshot below:

Github Actions Secrets

And then use that content to create env.yml file during our workflow process:

- name: Create env file
  run: |
    cat > env.yml << EOF
    ${{ secrets.ENV }}
    EOF

Please note: all secrets stored in Github are encrypted. You will not be able to view or edit them once created. So if you need to update any secret key, you need to delete old variable first, and then create a new variable with the same name and updated value.

Managing Github Actions

You can see the status of all triggered Github Actions:

List of all triggered Github Actions

To see the details of specific action, just click on it:

Details of completed action

And finally, you can see URL of your deployed application by expanding "Deploy Lambda functions" step:

URL of the deployed application

That's all! As you can see, Github Actions make deployment process of Serverless applications really easy.

© 2023 [maxico.dev] — All rights reserved.