A hands-on DevOps project demonstrating how to build, containerize, and deploy a Flask web app using GitHub Actions, Docker, and AWS (ECR + EC2) — with SonarCloud and Snyk integration.
This project implements a simple counter service that:
- GET / → shows current count
- POST / → increments count
- Persists state in
/data/counter.txt - Builds & deploys automatically via GitHub Actions
| Component | Technology |
|---|---|
| Language | Python + Flask + Gunicorn |
| CI/CD | GitHub Actions |
| Cloud | AWS ECR + EC2 |
| Containerization | Docker + Docker Compose |
| Code Quality & Security | SonarCloud + Snyk |
- Accounts: GitHub, AWS, SonarCloud, Snyk
- Tools:
git,docker,python3,aws-cli - AWS IAM User: ECR + EC2 access
git clone https://github.com/Ayaan49/python-aws-cicd-pipeline.git
cd python-aws-cicd-pipeline
python3 -m venv venv
source venv/bin/activate
pip3 install flask
pip3 freeze > requirements.txt
python counter-app.pycurl localhost:8080/
curl -X POST localhost:8080/docker build -t "counter-app-local:1.0.0" .
docker run -p 8080:8080 counter-app-local:1.0.0aws ecr create-repository --repository-name python-aws-cicd-pipeline --region <your-region>Allow inbound ports 22 (SSH) and 80 (HTTP).
Install Docker & AWS CLI on EC2:
# Install aws cli
sudo apt-get install -y awscli
# Update packages
sudo apt-get update -y
# Install dependencies
sudo apt-get install -y ca-certificates curl gnupg lsb-release
# Add Docker’s official GPG key
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
# Add Docker repo
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# Install Docker + Compose plugin
sudo apt-get update -y
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
# (Optional) Allow ubuntu user to run docker without sudo
sudo usermod -aG docker ubuntu
- Workflow file:
.github/workflows/ci-cd.yml - Triggers: pushes to
development(change tomainwhen ready)
- SonarCloud scan (code quality)
- Determine next semantic version (tag)
- Build Docker image & push to ECR
- Snyk scan (image vulnerabilities)
- SSH to EC2 and deploy with Docker Compose
| Secret | Description |
|---|---|
AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY |
IAM credentials |
AWS_REGION |
e.g., us-east-1 |
SONAR_TOKEN |
SonarCloud token |
SNYK_TOKEN |
Snyk API token |
EC2_PEM_KEY |
EC2 private key contents (single line) |
EC2_HOST, EC2_USER |
EC2 Public IP & username (e.g., ubuntu) |
ACCESS_TOKEN_2 |
GitHub Personal Access Token for releases |
version: '2.4' # The last version of Docker Compose file format that directly supports mem_limit and cpus
services:
counter-service:
container_name: py-aws-cicd
image: "${ECR_REGISTRY}/${ECR_REPOSITORY}:${IMAGE_TAG}"
volumes:
- ./data:/data
ports:
- "80:8080"
restart: always
mem_limit: 256M
cpus: 0.6cd ~/docker
sudo chmod -R 777 ./data
docker compose up -d --force-recreateOpen in browser:
http://<EC2-Public-IP>/
Increment counter:
curl -X POST http://<EC2-Public-IP>/