A comprehensive Todo application demonstrating modern DevOps practices. Features microservice architecture, multi-stage deployment, GitOps, CI/CD pipelines, and multiple deployment strategies.
- Project Overview
- Architecture
- Repository Structure
- Step-by-Step Setup Guide
- Deployment Strategy Comparison
- Pipeline Workflow Summary
- Configuration
- Development Workflow
- Contributing
This project can progress through 6 different stages:
- π³ Docker Compose - Development environment
- βΈοΈ Kubernetes - Container orchestration
- β΅ Helm - Package management + multi-environment
- π§ Kustomize - Helm alternative, overlay pattern
- π Jenkins - CI/CD pipeline
- πββοΈ ArgoCD - GitOps deployment
This project is a comprehensive infrastructure example that simulates real-world DevOps scenarios. It includes the following technologies and methodologies:
- Frontend: React 19 + Vite + TailwindCSS web interface
- User Service: FastAPI user management (auth, JWT, bcrypt)
- Todo Service: FastAPI todo operations with user authorization
- Database: SQLite (persistent volumes in K8s)
- Container Registry: GitHub Container Registry (ghcr.io)
- Container: Docker & Docker Compose
- Orchestration: Kubernetes (Minikube)
- Package Manager: Helm Charts
- Configuration Management: Kustomize
- CI/CD: Jenkins with Shared Libraries
- GitOps: ArgoCD (App of Apps pattern)
- Code Quality: Pre-commit hooks, Hadolint, SonarQube
- Security: Trivy vulnerability scanning
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β LOAD BALANCER β
β (Ingress/Service) β
βββββββββββββββββββββββ¬ββββββββββββββββββββββββ¬ββββββββββββββββββββ
β β
βββββββββββββββββββββββ΄ββββββββββββββββββββββ β
β FRONTEND β β
β (React App) β β
β Port: 3000 β β
βββββββββββββββββββββββ¬ββββββββββββββββββββββ β
β β
βΌ βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β BACKEND SERVICES β
βββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββββββββ€
β USER SERVICE β TODO SERVICE β
β (FastAPI) β (FastAPI) β
β Port: 8001 β Port: 8002 β
β - Authentication β - Todo CRUD β
β - User Management β - User Authorization β
β - JWT Tokens β - Service Communication β
βββββββββββββββββββββββ΄ββββββββββββββββββββββββββββββββββββββββββββ
This project consists of three main repositories:
βββ π³ docker-compose.yml # Development environment
βββ π³ docker-compose.test.yml # Test environment
βββ π frontend2/frontend/ # Frontend (React)
βββ π user-service/ # User service (FastAPI)
βββ π todo-service/ # Todo service (FastAPI)
βββ π k8s/ # Vanilla Kubernetes manifests
βββ π helm-charts/ # Helm chart definitions
βββ π kustomize/ # Kustomize overlays
βββ π Jenkinsfile # CI/CD pipeline definition
βββ π requirements.txt # Python dependencies
βββ π jenkins-values.yaml # Jenkins Helm values
βββ π .pre-commit-config.yaml # Code quality hooks
βββ π sonarqube-values.yaml # SonarQube Helm values
βββ π vars/ # Jenkins Shared Library functions
β βββ π buildAllServices.groovy # Parallel service build
β βββ π runUnitTests.groovy # Test execution
β βββ π argoDeployStaging.groovy # ArgoCD staging deploy
β βββ π argoDeployProduction.groovy # ArgoCD production deploy
β βββ ... (other shared functions)
βββ π src/com/company/jenkins/ # Utils and helper classes
β βββ π Utils.groovy # Jenkins utility functions
βββ π examples/ # Example pipeline files
βββ π Jenkinsfile-simple # Simple Jenkinsfile example
βββ π argocd-manifests/ # ArgoCD Application definitions
βββ π root-application.yaml # App of Apps root
βββ π environments/ # Environment-specific apps
βββ π staging.yaml # Staging application
βββ π production.yaml # Production application
This section shows how to set up the project step by step with different technologies. Each stage builds upon the previous one and adds new technologies.
- Docker & Docker Compose
- Git
- (For later stages) Minikube, kubectl, Helm, ArgoCD CLI
This is the simplest stage. No additional setup required.
# Clone the project
git clone <repo-url>
cd jenkins-shared-library2/local_devops_infrastructure
# Start the application
docker compose up -d
# Follow logs
docker compose logs -f
# Check status
docker compose ps# Run test services
docker compose -f docker-compose.test.yml up --build
# Follow tests
docker compose -f docker-compose.test.yml logs -f
# Clean test images
docker compose -f docker-compose.test.yml down --rmi all- Frontend: http://localhost:3000
- User Service: http://localhost:8001
- Todo Service: http://localhost:8002
- API Docs: http://localhost:8001/docs and http://localhost:8002/docs
- Endpoints:
/register,/login,/users/{id},/admin/users - Features: JWT authentication, bcrypt password hashing, SQLite database
- Health Check:
/health
- Endpoints:
/todos(CRUD),/admin/todos - Features: JWT validation, user-specific todos, SQLite database
- Dependencies: User Service for authentication
- Health Check:
/health
- Technology: React 19 + Vite + TailwindCSS
- Features: Modern UI, responsive design, API integration
- Build: Production-optimized with Vite
# Stop services
docker compose down
# Clean volumes as well
docker compose down -v
# Remove images too
docker compose down --rmi allIn this stage, we'll transition to Kubernetes using Minikube.
# Install Minikube (if not already installed)
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
sudo install minikube-linux-amd64 /usr/local/bin/minikube# Start Minikube
minikube start
# Enable ingress addon (required for Nginx Ingress Controller)
minikube addons enable ingress
# Point Docker environment to Minikube
# This allows Docker builds to run directly in Minikube
eval $(minikube -p minikube docker-env)# Create secret for GitHub Container Registry
# This secret is required to pull private images
kubectl create secret docker-registry github-registry-secret \
--docker-server=ghcr.io \
--docker-username=<GITHUB_USERNAME> \
--docker-password=<GITHUB_TOKEN> \
-n todo-appNote: Your GitHub Token must have packages scope.
# Apply all Kubernetes manifests
kubectl apply -f k8s/
# Check pod status
kubectl get pods -n todo-app
# Check service status
kubectl get services -n todo-appImportant Note: If you're adding new namespaces, you need to add rolebindings for those namespaces in k8s/jenkins-rbac.yaml. Otherwise, Jenkins agents cannot access those namespaces.
# Get Minikube IP
MINIKUBE_IP=$(minikube ip)
# Add to hosts file
echo "$MINIKUBE_IP todo-app.local" | sudo tee -a /etc/hostsApplication access: http://todo-app.local
# Check pod logs
kubectl logs -f deployment/user-service -n todo-app
kubectl logs -f deployment/todo-service -n todo-app
kubectl logs -f deployment/frontend -n todo-app
# Check ingress status
kubectl get ingress -n todo-app
kubectl describe ingress todo-app-ingress -n todo-appIn this stage, we'll package the deployment using Helm and add multi-environment support.
# Install Helm (if not already installed)
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bashHelm values require more complex secret management:
# Get Docker config in base64 format
cat ~/.docker/config.json | base64 | tr -d '\n'
# Save this value as `github-registry-dockerconfig` credential in Jenkins# Basic Helm deployment
helm upgrade --install todo-app helm-charts/helm-todo-app \
--namespace todo-app \
--create-namespace \
--wait# Create secret for staging namespace
kubectl create secret docker-registry github-registry-secret \
--namespace=staging \
--docker-server=ghcr.io \
--docker-username=<GITHUB_USERNAME> \
--docker-password=<GITHUB_TOKEN>
# Deploy staging environment
helm upgrade --install todo-app-staging helm-charts/helm-todo-app \
--namespace staging \
--create-namespace \
-f helm-charts/helm-todo-app/values-staging.yaml \
--wait# Create secret for production namespace
kubectl create secret docker-registry github-registry-secret \
--namespace=production \
--docker-server=ghcr.io \
--docker-username=<GITHUB_USERNAME> \
--docker-password=<GITHUB_TOKEN>
# Deploy production environment
helm upgrade --install todo-app-prod helm-charts/helm-todo-app \
--namespace production \
--create-namespace \
-f helm-charts/helm-todo-app/values-prod.yaml \
--wait
# Add production hosts entry
echo "$(minikube ip) prod.todo-app.local" | sudo tee -a /etc/hosts# List all releases
helm list --all-namespaces
# Check release status
helm status todo-app -n todo-app
# Test template (for debugging)
helm template todo-app helm-charts/helm-todo-app
# Remove release
helm uninstall todo-app -n todo-appKustomize can be used as an alternative to Helm. It uses base configuration + overlay pattern.
# Install Kustomize
sudo snap install kustomize# Deploy base configuration
kubectl apply -k kustomize/base/
# Check resources
kubectl get all -n todo-app# Deploy staging overlay
kubectl apply -k kustomize/overlays/staging/
# Check staging resources
kubectl get all -n staging# Deploy production overlay
kubectl apply -k kustomize/overlays/production/
# Check production resources
kubectl get all -n production# View build output (without applying)
kustomize build kustomize/base/
kustomize build kustomize/overlays/staging/
# Remove staging
kubectl delete -k kustomize/overlays/staging/
# Remove production
kubectl delete -k kustomize/overlays/production/Note: When using Kustomize, you need to manually create secrets:
# Create secret for each namespace
kubectl create secret docker-registry github-registry-secret \
--namespace=staging \
--docker-server=ghcr.io \
--docker-username=<GITHUB_USERNAME> \
--docker-password=<GITHUB_TOKEN>In this stage, we'll set up automatic CI/CD pipeline with Jenkins.
Install Jenkins with Helm:
# Create Jenkins namespace
kubectl create namespace jenkins
# Create Jenkins admin secret
kubectl create secret generic jenkins-admin-secret -n jenkins \
--from-literal=jenkins-admin-user='admin' \
--from-literal=jenkins-admin-password='YourStrongPassword123!'
# Install Jenkins with Helm
helm repo add jenkins https://charts.jenkins.io
helm repo update
helm install jenkins jenkins/jenkins -f jenkins-values.yaml -n jenkins --create-namespace
# Add JNLP port to Jenkins service (required for agent connection)
kubectl edit svc jenkins -n jenkins
# Add the following port:
# - name: jnlp
# port: 50000
# protocol: TCP
# targetPort: 50000Install the following plugins in Jenkins:
- Kubernetes Credentials Provider (for using Kubernetes secrets)
- Basic Branch Build Strategies (required for tag builds)
- SonarQube Scanner (for code quality analysis)
You can install SonarQube in two ways:
# Run SonarQube with Docker
docker pull sonarqube
docker run -d --name sonarqube -p 9000:9000 sonarqube
# Access SonarQube: http://192.168.49.1:9000
# Default: admin/adminSonarQube Configuration:
- Login to SonarQube
- Create new project:
- Project key:
Local-DevOps-Infrastructure - Display name:
Local DevOps Infrastructure
- Project key:
- Create token: Administration > Security > Users > Tokens
- Create webhook: Administration > Configuration > Webhooks
- URL:
http://jenkins.jenkins.svc.cluster.local:8080/sonarqube-webhook/
- URL:
# Add SonarQube Helm repository
helm repo add sonarqube https://SonarSource.github.io/helm-chart-sonarqube
helm repo update
# Install SonarQube
helm install sonarqube sonarqube/sonarqube -f sonarqube-values.yaml -n sonarqube --create-namespace
# Add to hosts file
echo "$(minikube ip) sonarqube.local" | sudo tee -a /etc/hosts
# Access SonarQube: http://sonarqube.localJenkins SonarQube Integration:
-
Manage Jenkins > Tools > SonarQube Scanner installations:
- Name:
SonarQube-Scanner - Install automatically: Yes
- Name:
-
Manage Jenkins > Configure System > SonarQube servers:
- Name:
sq1 - Server URL:
http://sonarqube.local(Helm) orhttp://192.168.49.1:9000(Docker) - Authentication token: Token obtained from SonarQube
- Name:
-
Credentials > Global > Add Credential:
- Kind: Secret text
- ID:
sonarqube-token - Secret: SonarQube token value
Add the following in Jenkins > Manage Jenkins > Credentials:
# For GitHub registry (ID: github-registry)
# Username: <GITHUB_USERNAME>
# Password: <GITHUB_TOKEN> (packages scope)
# For GitHub webhook (ID: github-webhook)
# Username: <GITHUB_USERNAME>
# Password: <GITHUB_TOKEN> (repo, hook scopes)
# For Docker config (ID: github-registry-dockerconfig)
cat ~/.docker/config.json | base64 | tr -d '\n'
# Save this output as "Secret text"Manage Jenkins > Configure System:
# Global Pipeline Libraries
Name: todo-app-shared-library
Default version: master
Retrieval method: Modern SCM
Source Code Management: Git
Project Repository: <YOUR_SHARED_LIBRARY_REPO>
# Global properties (Environment variables)
ARGOCD_SERVER: argocd.todo-app.local
# SonarQube Servers (if using)
Name: sq1
Server URL: http://sonarqube.localManage Jenkins > Tools:
# SonarQube Scanner installations
Name: SonarQube-Scanner
Install automatically: YesManage Jenkins > Clouds > Add Kubernetes:
Kubernetes URL: https://kubernetes.default.svc
Kubernetes Namespace: jenkins
Credentials: kubernetes service account# Create kubeconfig file (if needed)
kubectl config view --raw --minify > kubeconfig.yaml
# Add as "Secret file" in Jenkins
# However, this is not needed with modern Kubernetes pluginCreate a Multibranch Pipeline job in Jenkins:
# Branch Sources
GitHub
Owner: <YOUR_GITHUB_USERNAME>
Repository: local-devops-infrastructure
Credentials: github-webhook
# Build Configuration
Script Path: Jenkinsfile
# Scan Repository Triggers
Periodically if not otherwise run: Yes
Interval: 1 minute
# Build Strategies
Regular branches: Any
Tags: Tags matching a pattern v*In this final stage, we'll set up GitOps workflow with ArgoCD.
# Create ArgoCD namespace
kubectl create namespace argocd
# Install ArgoCD
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
# Install ArgoCD ingress
kubectl apply -f k8s/argocd-ingress.yaml
# Add to hosts file
echo "$(minikube ip) argocd.todo-app.local" | sudo tee -a /etc/hosts# Get admin password
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d
# Access ArgoCD via web: https://argocd.todo-app.local
# Username: admin
# Password: password obtained above# Download ArgoCD CLI
curl -SL -o argocd-linux-amd64 https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64
sudo install -m 555 argocd-linux-amd64 /usr/local/bin/argocd
rm argocd-linux-amd64
# Login to ArgoCD
argocd login argocd.todo-app.local --insecure --grpc-web
# Create API token (required for Jenkins)
kubectl patch configmap/argocd-cm --type merge -p '{"data":{"accounts.admin":"apiKey"}}' -n argocd
argocd account generate-tokenCreate secrets for ArgoCD to pull images:
# For staging
kubectl create secret docker-registry github-registry-secret \
--namespace=staging \
--docker-server=ghcr.io \
--docker-username=<GITHUB_USERNAME> \
--docker-password=<GITHUB_TOKEN>
# For production
kubectl create secret docker-registry github-registry-secret \
--namespace=production \
--docker-server=ghcr.io \
--docker-username=<GITHUB_USERNAME> \
--docker-password=<GITHUB_TOKEN>Important Note: The imagePullSecret creation feature in your Helm chart should be disabled because ArgoCD doesn't have access to Jenkins credentials. We created the secrets manually.
Add credentials for ArgoCD access in Jenkins:
# Credentials > Global > Add Credential
# ID: argocd-username, Value: admin
# ID: argocd-password, Value: <ARGOCD_ADMIN_PASSWORD># Deploy GitOps root application
kubectl apply -f todo-app-gitops/argocd-manifests/root-application.yaml -n argocdThis command starts the App of Apps pattern and automatically creates staging/production applications.
Now you can test the complete GitOps workflow:
# Create feature branch
git checkout -b feature/test-pipeline
git push origin feature/test-pipeline
# Jenkins will run build + test
# Merge to master
git checkout master
git merge feature/test-pipeline
git push origin master
# Jenkins will deploy to staging
# Create production tag
git tag v1.0.0
git push origin v1.0.0
# Jenkins will deploy to production# Remove finalizers to clean all applications
kubectl patch application staging-todo-app -n argocd -p '{"metadata":{"finalizers":null}}' --type=merge
kubectl patch application production-todo-app -n argocd -p '{"metadata":{"finalizers":null}}' --type=merge
kubectl patch application root-app -n argocd -p '{"metadata":{"finalizers":null}}' --type=merge
# Clean namespaces
kubectl delete all --all -n staging
kubectl delete all --all -n productionbuildAllServices()- Parallel service buildsrunUnitTests()- Parallel test executionpushToRegistry()- Docker registry pushdeployWithHelm()- Helm deploymentargoDeployStaging()- ArgoCD staging syncargoDeployProduction()- ArgoCD production syncrunHadolint()- Dockerfile lintingrunTrivyScan()- Security scanning
- Feature Branch β Build + Test + Analysis
- Master Branch β Registry Push + Staging Deploy
- Git Tag (v)* β Production Deploy
The pipeline is fully configurable via the config map in Jenkinsfile:
def config = [
appName: 'todo-app',
services: [
[name: 'user-service', dockerfile: 'user-service/Dockerfile'],
[name: 'todo-service', dockerfile: 'todo-service/Dockerfile'],
[name: 'frontend', dockerfile: 'frontend2/frontend/Dockerfile', context: 'frontend2/frontend/']
],
// Registry and deployment settings
registry: 'ghcr.io',
username: 'keremar',
// Choose your deployment strategy
// helmReleaseName: 'todo-app', // For Helm
// argoCdStagingAppName: 'staging-todo-app', // For GitOps
]| Strategy | Best For | Pros | Cons |
|---|---|---|---|
| Docker Compose | Local development, testing | Quick setup, simple | Not production-ready |
| K8s Manifests | Learning, simple deployments | Full control, transparent | Verbose, hard to manage |
| Helm | Complex apps, multi-env | Templating, packaging | Learning curve, complexity |
| Kustomize | Environment variants | Declarative, patch-based | Limited templating |
| GitOps/ArgoCD | Production, compliance | Git-based, audit trail | Complex setup, git dependency |
# Install pre-commit
pip install pre-commit
# Activate hooks
pre-commit install
# Run on all files
pre-commit run --all-filesYou need to define the following credentials in Jenkins:
github-registry: For GitHub Container Registrygithub-webhook: For GitHub webhook (repo + hook scopes)argocd-username: ArgoCD usernameargocd-password: ArgoCD passwordsonarqube-token: SonarQube token (if using)
Define the following environment variable in Jenkins:
ARGOCD_SERVER: argocd.todo-app.local
# Start development environment
docker compose up -d
# Make code changes
# Run tests
docker compose -f docker-compose.test.yml run --rm user-service-test
docker compose -f docker-compose.test.yml run --rm todo-service-test
# Check pre-commit hooks
pre-commit run --all-files# Create feature branch
git checkout -b feature/new-feature
# Push and create PR
git push origin feature/new-feature
# Jenkins automatically runs:
# build β test β security scan β staging deploy (on merge to master)# Create release tag
git tag v1.0.0
git push origin v1.0.0
# Jenkins automatically performs production deployment
# (skips build/test stages)Before starting, verify:
- β Docker and Docker Compose installed
- β Kubernetes cluster (minikube) running
- β kubectl configured
- β Helm installed (for Helm deployments)
- β Jenkins accessible with required plugins
- Fork this repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Create a Pull Request
This project is licensed under the MIT License.
Project Owner: Kerem AR
- GitHub: @KeremAR
Note: This project is for educational and demonstration purposes. Review security settings before using in production.