Follow

Follow
Automatic Deployment to DigitalOcean With Github Actions

Automatic Deployment to DigitalOcean With Github Actions

Code Memoirs's photo
Code Memoirs
Ā·Apr 29, 2021Ā·

8 min read

Software development is a continuous process, with regular code updates from time to time. Whether from adding new features, fixing bugs or just performing general chores like updating project dependencies now and then. One effective approach to easily push and preview these local development changes to a staging or production environment is to create a continuous integration and deployment workflow. In the next few steps, I will teach you how to automatically deploy your project from GitHub to DigitalOcean using GitHub Actions.

Prerequisites:
ā€
Table of Contents:

Continuous Integration and Deployment

Continuous Integration (CI) is the automated processing of building and testing code changes made in a feature branch before it is merged into the main branch if the tests pass. This practice if done correctly prevents the introduction of defects into the codebase. Continuous Deployment (CD) is the automated processing of building, testing and deploying code pushed or merged to the main branch to a production or staging environment. If the process of deployment is not automated, it is referred to as Continuous Delivery. Continuous Integration and Deployment (CI/CD) provides rapid feedback, fast defects identification and seamless deployment of new features.

Continuous Integration and Deployment Pipeline Continuous Integration and Deployment Pipeline

About GitHub Actions

GitHub Actions are event-driven features provided to automate software development workflows. Actions are composed of several components such as workflows, events, jobs, steps, actions and runners. Workflows are triggered when certain events occur or according to a schedule. Events are activities that trigger a workflow. Examples of such events are pushing commits to a repository, performing a pull request or setting a cronjob. You can find the complete list of events that can trigger workflow here. A job is a set of steps that execute on the same runner. You can run several jobs in a workflow, either sequentially or in parallel by default.

A runner is a server that hosts the GitHub Actions runner application installed. The Actions runner application can be hosted by GitHub in a virtual hosting environment or could be self-hosted. Every job has a composition of shell commands or steps made of standalone commands called actions.

Components of a GitHub Actions Workflow Components of a GitHub Actions Workflow

Continuous Integration With GitHub Actions

Let's set up a continuous integration workflow to manage a project into feature branches. For this example, we will be using a basic [NestJS] application. NestJS is a progressive framework for building efficient, scalable Node.js web applications.

Create Your NestJS App

In your local development environment, clone the NestJS TypeScript starter project with Git.

git clone https://github.com/nestjs/typescript-starter.git my_server
cd my_server
npm install

Create A GitHub Repository

Log in to GitHub and create a new private or public repository to host the codebase. When the repository has been created, push the local project to the repository from the terminal.

git remote rm origin
git remote add origin https://github.com/username/repo_name.git
git branch -M main
git push -u origin main

Add Integration Workflow

All GitHub Actions are placed under the .github > workflows directory in the project root folder. Let's create the continuous integration workflow to manage automated build and testing before merging to the codebase. Workflows are in YAML file format. You can pick any name you want for your workflow file except action.yml to avoid linting conflicts.

mkdir -p .github/workflows
cd .github/workflows
touch integration.yml

Create the Integration Workflow

  • Add a workflow to build and test code commits on a GitHub hosted Ubuntu runner when a pull request event is triggered on the main branch. Push the addition to GitHub.
  • name: Continuous Integration
    
    on:
      pull_request:
        branches:
          - main
    
    jobs:
      merge_pull_request:
        runs-on: ubuntu-latest
        steps:
          - name: Checkout main branch
            uses: actions/checkout@v2
    
          - name: Use Node.js
            uses: actions/setup-node@v1
            with:
              node-version: '14.x'
    
          - name: Install dependencies
            run: npm ci
    
          - name: Test application
            run: npm test
    
          - name: Build application
            run: npm run build
    

    Continuous Integration Workflow

  • Create a new branch.
  • git checkout -b feature-test
    
  • In the feature-test branch, make some code changes to src > app.service.ts and src > app.controller.spec, for example, change 'World' to 'Universe' and push the commit.
  • import { Injectable } from '@nestjs/common';
    
    @Injectable()
    export class AppService {
      getHello(): string {
        return 'Hello Universe!';
      }
    }
    

    src/app.service.ts

    import { Test, TestingModule } from '@nestjs/testing';
    import { AppController } from './app.controller';
    import { AppService } from './app.service';
    
    describe('AppController', () => {
      let app: TestingModule;
    
      beforeAll(async () => {
        app = await Test.createTestingModule({
          controllers: [AppController],
          providers: [AppService],
        }).compile();
      });
    
      describe('getHello', () => {
        it('should return "Hello Universe!"', () => {
          const appController = app.get<AppController>(AppController);
          expect(appController.getHello()).toBe('Hello Universe!');
        });
      });
    });
    

    src/app.controller.spec.ts

  • Trigger a pull request event to try out the integration workflow. Then head over to the Actions tab of the repository to view all the workflow runs. Click on a workflow run to get more details about that run.
  • Workflow Runs Workflow Runs

    Workflow Run Details Workflow Run Details

    You can then merge the pull request if all the tests passed and other metrics like code quality and coverage have been met.

    Setting Up Your Droplet

    With your DigitalOcean account created and logged in, let's create a droplet to run the Ubuntu 20.04 LTS server. To quickly do that, use the DigitalOcean 1 Click Ubuntu server installation. On the setup page, you can choose your preferred server configuration covering the image, usage plan (amount of RAM, storage spaces, CPU cores and bandwidth), datacenter region and authentication (SSH preferably).

    Droplet Server Configuration Droplet Server Configuration

    After you have confirmed your preferred server configuration and chosen a hostname for your droplet, click the Create Droplet button at the end of the page to create and initialise your droplet. You should be presented with the following screen when the droplet is ready. Note the IP address of the server.

    Ubuntu 20.04 Server On a Droplet Ubuntu 20.04 Server On a Droplet

    To gain access to the Ubuntu server, connect securely using SSH via a terminal with the user as root and the IP address.

    ssh root@your_ip_address
    

    If you cannot connect via SSH due to "Error Permission denied (publickey)", take a look at this community question. If everything works fine, you should be logged in to your Droplet as the root user.

    Continuous Deployment to DigitalOcean

    With the deployment server set up and running, let's add a deployment workflow to push changes to the server automatically.

    Project Setup

    If you don't have a project to work with, you can clone the deployment-starter branch of this basic Nest.js project, then create a new repository on GitHub and push your project to your repository.

    git clone -b deployment-starter https://github.com/krizten/digitalocean-actions.git
    cd digitalocean-actions && npm install
    git remote rm origin
    git remote add origin https://github.com/username/repo_name.git
    git branch -M main
    git push -u origin main
    

    Configure Self-hosted Runner

    For deployment, we will be using the self-hosted runner option in the deployment workflow. For this example, the runner will be at a repository level hosted on the DigitalOcean Droplet that was set up in the previous section. Go to the Settings > Actions section of the GitHub repository, then click the Add runner button.

    Self-hosted Runners Self-hosted Runners

    Since we have Ubuntu installed on the Droplet, select Linux as the operating system and x64 as the architecture.

    Self-hosted Runner Configuration Self-hosted Runner Configuration

    To integrate the self-hosted runner into the Ubuntu server, log in to the Droplet via the terminal and follow the Download and Configure instructions provided in your Settings > Actions page.

  • Log in as the root user and create a sudo user.
  • ssh root@ip_address
    adduser newusername sudo
    sudo su - newusername
    

  • Download and install the latest runner package.
  • mkdir actions-runner && cd actions-runner
    curl -o actions-runner-linux-x64-2.278.0.tar.gz -L https://github.com/actions/runner/releases/download/v2.278.0/actions-runner-linux-x64-2.278.0.tar.gz
    tar xzf ./actions-runner-linux-x64-2.278.0.tar.gz
    

  • Configure the runner and start the runner as a Linux service.
  • ./config.sh --url https://github.com/username/repo_name --token your_token
    sudo ./svc.sh install
    sudo ./svc.sh start
    

  • Set up the project dependencies (Node.js and PM2 Process Manager).
  • cd ~
    curl -sL https://deb.nodesource.com/setup_14.x | sudo -E bash -
    sudo apt install nodejs
    sudo npm install pm2 -g
    

    Add Deployment Workflow

  • In your local development, add a deployment workflow using a self-hosted runner to automatically build on the DigitalOcean Droplet and push the addition to GitHub.
  • name: Continuous Deployment
    
    on:
      push:
        branches:
          - main
    
    jobs:
      deployment:
        runs-on: self-hosted
        steps:
          - name: Checkout main branch
            uses: actions/checkout@v2
    
          - name: Setup Node.js
            uses: actions/setup-node@v1
            with:
              node-version: '14.x'
    
          - name: Install dependencies
            run: npm ci
    
          - name: Test application
            run: npm test
    
          - name: Build application
            run: npm run build
    

    Deployment Workflow

    deployment-workflow.png

  • Log in to the Droplet via the terminal using the sudo user created earlier, navigate to the project root directory and start the application using the PM2 process manager.
  • cd /actions-runner/_work/digitalocean-actions/digitalocean-actions
    pm2 start dist/main.js --name MyAppName
    

  • Complete the deployment workflow by including a step to restart the application using PM2.
  • - name: Restart server application
       run: pm2 restart MyAppName
    

    Restart Step In Deployment Workflow

    At this point, the project should be accessible on the public IPv4 address http://ip_address:3000. You may want to configure a domain name and incorporate SSL certificates, but these are out of the scope of this tutorial.

    project-1.png

    Conclusion

    GitHub Actions features an easy way to automate tasks within your software development life cycle. With GitHub Actions, you can manage the codebase, review code commits and handle continuous deployment to platforms like DigitalOcean all from GitHub.

    If you need a deeper dive into GitHub Actions, head over to the official docs and learn how to integrate it to match your use case. You can also check DigitalOcean if you are looking for a good cloud infrastructure provider using our referral link.

    Resources

    If you have any questions or feedback to share, please don't hesitate to comment below. Don't forget to subscribe and share if you enjoyed reading this article.

    Ā 
    Share this