NH

Deploy Flask app with Github Actions

Cover Image for Deploy Flask app with Github Actions
Nasrul Hasan
Nasrul Hasan

Github Action

Github action is a popular ci cd tool which is used to automate your Github workflow.

Github Actions is not just a ci cd tool. we can automate other actions on our repository, which makes this tool very powerful. Many large companies are shifting from other ci cd to github actions because of its pre defined Actions in marketplace and easy to maintain yaml syntax.

Prerequisite

  • An EC2 instance to deploy

  • Dockerhub account

  • Github account

  • IDE (visual studio, atom etc)

  • Terraform (optional)

Now open IDE and create project folder structure like this.

GA CI CD structure

Keep in mind we can only write ci cd yaml files inside .github > workflows folder. Other names are optional,

Infrastructure provisioning

1. For infrastructure you can create EC2 instance manually and follow step ahead or you can create terraform main.tf file inside terraform folder to provision server. you can find the code to provision ec2 in this blog section Provisioning of EC2 instance. Just copy paste the code from this section. and in script folder use this code in your env.sh file

 #!/bin/bash
sudo apt-get update
sudo apt-get install docker.io -y
sudo systemctl start docker && sudo systemctl enable docker
sudo usermod -aG docker ubuntu

this script will install docker in your ec2 instance.

2. Now we have to add public key in authorized_key folder to this ec2 instance. For this we will create ssh keys in our local machine. if you dont know how to do this please follow section Setting Up SSH Keys and Passwordless Login in my blog.

3. Now add private ssh key in github secrets. For this go to settings in your code repo. In Dashboard go to secrets and variable > Actions > New repository secret and add your key with name of your choice. i will use EC2_SSH_KEY.

4. In Environment secrets create environment with name prod and add docker login credentials with name DOCKERHUB_USERNAME and DOCKERHUB_PASSWORD

5. in variable section add your ec2 instance public ip address wuth name EC2_SERVER_IP

For your reference it should look like this

GH secrets and variable

Writing CI CD pipeline for Flask

Inside .github/workflows we will write our deployment file.

Inside server we will store our flask code and inside scripts we will write bash script which we will execute in ec2 through our pipeline.

Now create a ec2-deploy.yaml file insiode workflows folder and use the code below.

 name: Docker image deployment to EC2
on:
  # push:
  #   branches: main
  workflow_dispatch:
    #
jobs:
  build-and-push-docker-image:
    name: build-and-push-docker-image-to-dockerhub
    runs-on: ubuntu-latest
    environment:
      name: prod
    env:
     user: ${{secrets.DOCKERHUB_USERNAME}}
     pass: ${{secrets.DOCKERHUB_PASSWORD}}
    steps:
      - uses: actions/checkout@v2
#
      - name: build docker
        run: cd server && docker build -t flask-simple-app .
#
      - name: docker tag
        run: docker tag flask-simple-app $user/flask-simple-app:${{github.run_number}}
#
      - name: docker login
        run: docker login -u $user -p $pass
#
      - name: docker push
        run: docker push $user/flask-simple-app:${{github.run_number}}
#
      - name: clean docker image
        run: docker rmi $user/flask-simple-app:${{github.run_number}} && docker rmi flask-simple-app
#
      - name: successfull
        run: echo " Job is successfull broooo... "
  #
  deploy-to-ec2:
    name: Deploy Docker images to EC2 instance
    runs-on: ubuntu-latest
    needs: build-and-push-docker-image
    environment:
      name: prod
    env:
      sshKey: ${{secrets.EC2_SSH_KEY}}
      user: ${{secrets.DOCKERHUB_USERNAME}}
      pass: ${{secrets.DOCKERHUB_PASSWORD}}
      serverIp: ${{ vars.EC2_SERVER_IP }}
    steps:
      - uses: actions/checkout@v2
      - name: Copy deploy.sh to EC2 instance
        run: |
          mkdir -p ~/.ssh
          echo "$sshKey" > ~/.ssh/id_rsa
          chmod 600 ~/.ssh/id_rsa
          eval "$(ssh-agent -s)"
          ssh-add ~/.ssh/id_rsa
          scp -o StrictHostKeyChecking=no ./scripts/deploy.sh ubuntu@$serverIp:/home/ubuntu/deploy.sh
#
      - name: Execute deploy.sh on EC2 instance
        run: |
          ssh -o StrictHostKeyChecking=no ubuntu@$serverIp "bash /home/ubuntu/deploy.sh ${{github.run_number}} $user"

in the last step you can see we are using deploy.sh script to run our application on EC2 instance. So inside deploy.sh you can use this code

 #!/bin/bash
# check if there is already a existing container with t he same label
existing_container=$(docker ps -q -f label=flask-app)
if [ -n "$existing_container" ]; then
    # stopping old running container
    docker stop $existing_container
fi
# removing old images from server
docker images prune -a -f 
# capturing run number and user from argument of workflow
run_number="$1"
docker_user="$2"
echo "============= RUN NUMBER ==========" $run_number
echo "============= user ==========" $docker_user
# deploying container
docker run -p 3000:3000 -d -l flask-app $docker_user/flask-simple-app:$run_number

Now your deployment pipeline is completed. Now to run the pipeline trigger it from Actions tab in your Github.

Actions tab in github

#Deployment#Flask#Python