Building Scalable GO Application With Docker, AWS, and GitHub Actions

Building Scalable GO Application With Docker, AWS, and GitHub Actions

Part 3: How to Deploy Go Apps to AWS EC2 with GitHub Actions

Introduction

  • In the last part, we successfully put the Go application in a Docker container. Now, we need to deploy it so others can access it.

  • First, we need to upload our Docker application to Docker Hub. We'll start with that and then set up a GitHub workflow action to help us deploy our application to AWS EC2.

  • You might wonder why we use GitHub Actions. In real-world projects, things change often, and you can't manually deploy the application every time. To automate this, we use a tool like GitHub Actions.

  • Let's begin with Docker Hub, and then we'll create the workflow file.


Upload Go Application to DockerHub

    • DockerHub is like GitHub but for Docker images. It's an online place where developers can find and share Docker images.

      * You make your custom image on your computer and push it to DockerHub for others to use.

      * As developers, we just need to pull these images using the command docker pull, and we're ready to go.

      * Now, let's upload our Go application to DockerHub.Step 1: Login to Dockerhub

  • First, we need to log into the docker hub in order to upload.

  • Run the below command.

docker login

  • This takes you to the browser where you can log in or sign up. Once you do that, you'll see a message confirming your login was successful.

    • Once you are logged in, go to hub.docker.com

Create Repository

  • Just like GitHub needs a repository to store code, Docker Hub needs a repository to store Docker images. Go to the Repositories tab in the Navbar.

  • And create a public repository. I already have one, so I won't create it again.

Push Image

  • Once you've created the repository, you can push the image we made using the docker push command. But first, we need to tag our local image as latest, as shown below.
docker tag anonymous-go dhruvnakum/anonymous-go:latest
  • And now let’s push it
 docker push dhruvnakum/anonymous-go:latest
  • Replace dhruvnakum with your username, and if you named the repository differently, use that name instead.

  • After you run this, the image will be successfully pushed to the repository we just created.

  • Now, let's create a workflow to automate deployment.


Creating the GitHub Actions Workflow

  • To automate deployment, we will set up a GitHub Actions workflow.

Create the Workflow File

  • In the project, create a new file at .github/workflows/cicd.yml.

Define the CI/CD Pipeline

name: Deploy Go Application

on:
  push:
    branches: 
      - ec2

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Source
        uses: actions/checkout@v4
      - name: Create .env file
        run: echo "PORT=${{ secrets.PORT }}" >> .env
      - name: Login to docker hub
        run: sudo docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
      - name: Build docker image
        run: sudo docker build -t dhruvnakum/anonymous-go .
      - name: Push image to docker hub
        run: sudo docker push dhruvnakum/anonymous-go:latest
  deploy:
    needs: build
    runs-on: self-hosted
    steps:
      - name: Delete old anonymous-go container
        run: sudo docker rm -f anonymous-go  
      - name: Pull new anonymous-go docker image
        run: sudo docker pull dhruvnakum/anonymous-go
      - name: Delete old mongo container
        run: sudo docker rm -f mongo-demo
      - name: Pull new mongo docker image
        run: sudo docker pull mongo:8.0
      - name: Delete old network
        run: sudo docker network rm anonymous-go-nw
      - name: Create networks
        run: sudo docker network create anonymous-go-nw
      - name: Run mongo container
        run: sudo docker run --name mongo-demo --network anonymous-go-nw -d mongo:8.0
      - name: Run docker container
        run: |
          sudo docker run --name anonymous-go \
          --network anonymous-go-nw \
          -p 3000:3000 \
          -e MONGODB_URI="mongodb://mongo-demo:27017" \
          -e JWT_SECRET="secret" \
          -e REDIS_SECRET="secret" \
          -e REDIS_ADDR="redis-11068.c261.us-east-1-4.ec2.redns.redis-cloud.com:11068" \
          -e REDIS_PASSWORD="DM4iBq2pTYNQkweBZmwqGKyDYrj872M8" \
          -e PORT=3000 \
          -d dhruvnakum/anonymous-go:latest

Let me explain what's happening here:

  • First, we named the workflow Deploy to Go Application.

  • To be safe, I created a new branch called ec2 and pushed all the code to it. We want the workflow to run automatically whenever this branch is updated. Here's how we do it:

on:
  push:
    branches: 
      - ec2
  • We have two jobs: Build and Deploy.

  • In the Build job, we download our project's code, build a Docker image from it, and push it to Docker Hub.

  • In the Deploy job, we pull the Docker image of our project and the Mongo image from Docker Hub, run them inside one container as we did before, and finally, we run that container.


Build Job

  • We want this job to run on the latest Ubuntu operating system.
jobs:
  build:
    runs-on: ubuntu-latest
  • Now we want to download our project’s code from GitHub inside this OS, so we write this:
name: Checkout source code
uses: actions/checkout@v2
  • Since we have environment variables in our code, we need to make them available here. To do this, we must add these secrets in GitHub Action Secrets. Go to the project's settings, and under secrets and variables, add these secrets.

  • Now, to get this from there, we create .env inside this OS and push everything:
- name: Create .env file
  run: echo "PORT=${{ secrets.PORT }}" >> .env
  • Now that we have all the secrets. We Login to the docker:
- name: Login to docker hub
  run: sudo docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
  • Then we build the docker image of our project:
- name: Build docker image
  run: sudo docker build -t dhruvnakum/anonymous-go .
  • Finally, we push the image to the docker hub:
- name: Push image to docker hub
  run: sudo docker push dhruvnakum/anonymous-go:latest

Deploy Job

  • In this job, everything we write will run on the AWS EC2 instance provided by GitHub.

  • We want this job to start after the build job finishes successfully.

deploy:
    needs: build
  • In this job, we first want to delete all the old containers and pull the latest ones. Once that is done, we run the container using docker run command.
deploy:
    needs: build
    runs-on: self-hosted
    steps:
      - name: Delete old anonymous-go container
        run: sudo docker rm -f anonymous-go  
      - name: Pull new anonymous-go docker image
        run: sudo docker pull dhruvnakum/anonymous-go
      - name: Delete old mongo container
        run: sudo docker rm -f mongo-demo
      - name: Pull new mongo docker image
        run: sudo docker pull mongo:8.0
      - name: Delete old network
        run: sudo docker network rm anonymous-go-nw
      - name: Create networks
        run: sudo docker network create anonymous-go-nw
      - name: Run mongo container
        run: sudo docker run --name mongo-demo --network anonymous-go-nw -d mongo:8.0
      - name: Run docker container
        run: |
          sudo docker run --name anonymous-go \
          --network anonymous-go-nw \
          -p 3000:3000 \
          -e MONGODB_URI="mongodb://mongo-demo:27017" \
          -e JWT_SECRET="secret" \
          -e REDIS_SECRET="secret" \
          -e REDIS_ADDR="redis-11068.c261.us-east-1-4.ec2.redns.redis-cloud.com:11068" \
          -e REDIS_PASSWORD="DM4iBq2pTYNQkweBZmwqGKyDYrj872M8" \
          -e PORT=3000 \
          -d dhruvnakum/anonymous-go:latest

Before we move ahead I forgot to change the URL in the main from localhost to 0.0.0.0 . So make sure you have this inside main.go at the end.

log.Fatal(r.Run("0.0.0.0:" + os.Getenv("PORT")))

Setting Up AWS EC2 Instance

  • Before deploying the project, we need to set up an EC2 instance.

  • To do this, log in to the AWS Management Console and go to the EC2 dashboard.

  • Use this link: https://aws.amazon.com/ec2/

  • It might ask for your card details, but don't worry; they won't charge you unless you exceed the limit, which you won't be charged for for this purpose.

  • Search for EC2 and click on it.

  • Now click on the “Launch Instance.”

  • Now, give the instance name and select the Ubuntu image

  • Also, select Key pair. If you don’t have one. You can create it by clicking on the Create new key pair button.

  • Once it is done, click on the Launch Instance button on the right

  • Since our app is running on port 3000, go to the Security Group settings and allow inbound traffic on port 3000 to make sure the application is accessible.

  • To do that, click on the Instance ID

  • Then click on the Security tab and there click on Security Group

  • Click on Edit inbound rules and then add custom TCP with Port range 3000 as shown below and save the rules.

  • Now, we are ready to run the instance.

Setting up Docker inside EC2 Instance,

  • Our instance is ready. First, we need to make sure Docker is installed. To do this, let's connect to the instance. Click the Connect button after selecting the instance.

  • Once you do that you will see a window like this:

  • To confirm whether docker is there or not, run docker command, you will most likely see this:

  • Now, to install everything, run the below commands

sudo apt-get update && sudo apt-get install docker.io -y && sudo systemct| start docker && sudo chmod 666 /var/run/docker.sock && sudo systemcti enable docker && docker --version
  • Once it is done, run docker version to see if it’s installed correctly.

Register a Self-Hosted GitHub Runner

  • A self-hosted runner is simply a computer or server that you own (like an AWS EC2 instance) that runs GitHub Actions instead of using GitHub’s default machines.

  • To set up, go to the project settings, and inside Actions, you will see this runner tab.

  • Click on `New self-hosted runner:

  • Select Linux

  • And run all the commands you see there inside your EC2.

  • Once you run the last step ./run.sh command, you will see a runner with the status idle :

  • But there is one problem, that runner does not run on detached mode in our instance, If you kill it by pressing CTRL+C, you wont be able to run it.

  • To resolve this issue, just run the below command

sudo ./svc.sh install
sudo ./svc.sh start
  • Now, your running is running in the background, and you can perform other operations inside your instance.

  • Finally, we are done with it. Now, we can test our application.

Testing Your Application

  • To test, commit to the ec2 branch that we created. Once you do that, Go to the Actions tab, and there you will see both build deploy services are running.

  • Once it is done, you will see both marked as green ticks, which means that everything went well and the docker is successfully up inside the ec2.

  • To test, go to the instance we created and find the Public IPv4 address. Copy it, paste it into Postman, and run any endpoint in your application.

  • As you can see it’s working now.

a man is sitting at a table with his arms outstretched in front of a crowd with the words let 's celebrate !!

  • Phew! I know that seems like a lot at first. But once you follow these steps, you'll get comfortable with it.

Wrapping Up

  • If you've made it to the end of this guide, you truly deserve a big thank you. So, thank you for sticking with it and reading all the way through. I hope you've gained a lot of valuable insights from this single blog post, covering everything from creating a GitHub Workflow to understanding Actions, setting up a Runner, and working with AWS EC2.

  • Keep diving deeper into these topics and explore other related concepts, as they are essential for cloud development in today's fast-paced tech world. Mastering these skills will undoubtedly enhance your ability to build and deploy applications efficiently.

  • I look forward to sharing more knowledge with you in the next blog post! Until then, keep learning and experimenting with new technologies.

Reference: https://www.youtube.com/watch?v=sSAWMr_-Co4&t=409s

Did you find this article valuable?

Support Dhruv Nakum by becoming a sponsor. Any amount is appreciated!