Deploy Flask app with Github Actions
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.
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
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.