Skip to content

Commit 1f3ed5e

Browse files
authored
Merge pull request #80 from AET-DevOps25/dev
Sync dev with main
2 parents 619df45 + 5fba823 commit 1f3ed5e

136 files changed

Lines changed: 18188 additions & 1183 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
# ============================================================================
2+
# Build and Deploy to Kubernetes Workflow
3+
# ============================================================================
4+
#
5+
# This workflow builds Docker images for all microservices and deploys them
6+
# to a Kubernetes cluster using Helm charts with proper secret management.
7+
#
8+
# TRIGGERS:
9+
# - Manual trigger (workflow_dispatch): Run manually from GitHub Actions UI
10+
# - Push to dev/main branches: Automatically builds and deploys on code changes
11+
#
12+
# SERVICES BUILT:
13+
# - client: Vue.js frontend application
14+
# - auth-service: Spring Boot authentication service with JWT
15+
# - quiz-service: Spring Boot quiz management service
16+
# - flashcard-service: Spring Boot flashcard management service
17+
# - genai-service: FastAPI service for AI-powered features
18+
#
19+
# DEPLOYMENT TARGET:
20+
# - Kubernetes cluster with Helm
21+
# - Namespace: dev
22+
# - Domain: team-deployaas.student.k8s.aet.cit.tum.de
23+
#
24+
# REQUIRED SECRETS:
25+
# - POSTGRES_PASSWORD: Database password
26+
# - JWT_SECRET: JWT signing secret (32+ characters)
27+
# - GENAI_API_KEY: API key for GenAI service
28+
# - GRAFANA_PASSWORD: Grafana admin password
29+
# - KUBE_CONFIG: Base64-encoded Kubernetes config file
30+
#
31+
# SWAGGER ENDPOINTS (after deployment):
32+
# - Auth Service: https://team-deployaas.student.k8s.aet.cit.tum.de/auth/swagger-ui
33+
# - Quiz Service: https://team-deployaas.student.k8s.aet.cit.tum.de/quiz/swagger-ui
34+
# - Flashcard Service: https://team-deployaas.student.k8s.aet.cit.tum.de/flashcard/swagger-ui
35+
# - GenAI Service: https://team-deployaas.student.k8s.aet.tum.de/api/genai/docs
36+
# ============================================================================
37+
38+
name: Build and Deploy to Kubernetes
39+
40+
on:
41+
# Manual trigger - can be run from GitHub Actions UI
42+
workflow_dispatch:
43+
# Automatic trigger on push to main development branches
44+
push:
45+
branches: [dev, main]
46+
47+
jobs:
48+
# ============================================================================
49+
# BUILD JOB: Build and push Docker images for all microservices
50+
# ============================================================================
51+
build:
52+
name: Build & Push Docker Images
53+
runs-on: ubuntu-latest
54+
55+
# Matrix strategy builds all services in parallel for efficiency
56+
strategy:
57+
matrix:
58+
include:
59+
# Vue.js frontend with Nginx
60+
- service: client
61+
context: frontend/client-vue
62+
dockerfile: frontend/client-vue/Dockerfile
63+
# Spring Boot authentication service
64+
- service: auth-service
65+
context: auth-service
66+
dockerfile: auth-service/Dockerfile
67+
# Spring Boot quiz management service
68+
- service: quiz-service
69+
context: quiz-service
70+
dockerfile: quiz-service/Dockerfile
71+
# Spring Boot flashcard management service
72+
- service: flashcard-service
73+
context: flashcard-service
74+
dockerfile: flashcard-service/Dockerfile
75+
# FastAPI AI service
76+
- service: genai-service
77+
context: genai-service
78+
dockerfile: genai-service/Dockerfile
79+
80+
steps:
81+
# Checkout source code
82+
- uses: actions/checkout@v4
83+
84+
# Login to GitHub Container Registry using automatic token
85+
- name: Log in to GitHub Container Registry
86+
uses: docker/login-action@v3
87+
with:
88+
registry: ghcr.io
89+
username: ${{ github.actor }}
90+
password: ${{ secrets.GITHUB_TOKEN }}
91+
92+
# Set up Docker Buildx for multi-platform builds
93+
- uses: docker/setup-buildx-action@v3
94+
95+
# Generate image metadata (tags, labels)
96+
- id: meta
97+
uses: docker/metadata-action@v5
98+
with:
99+
images: ghcr.io/${{ github.repository }}/${{ matrix.service }}
100+
tags: |
101+
type=raw,value=latest # Always tag as 'latest'
102+
type=ref,event=branch # Tag with branch name
103+
type=sha # Tag with git commit SHA
104+
105+
# Build and push Docker image for current service
106+
- name: Build & Push ${{ matrix.service }}
107+
uses: docker/build-push-action@v5
108+
with:
109+
context: ./${{ matrix.context }} # Build context (service directory)
110+
file: ./${{ matrix.dockerfile }} # Dockerfile path
111+
push: true # Push to registry
112+
tags: ${{ steps.meta.outputs.tags }} # Use generated tags
113+
labels: ${{ steps.meta.outputs.labels }}
114+
platforms: linux/amd64,linux/arm64 # Multi-platform build
115+
cache-from: type=gha # Use GitHub Actions cache
116+
cache-to: type=gha,mode=max # Cache layers for faster builds
117+
118+
# ============================================================================
119+
# DEPLOY JOB: Deploy to Kubernetes using Helm with secrets management
120+
# ============================================================================
121+
deploy:
122+
name: Deploy to Kubernetes
123+
runs-on: ubuntu-latest
124+
needs: build # Wait for build job to complete
125+
environment: kubernetes # Deployment environment (for protection rules)
126+
127+
steps:
128+
# Checkout source code for Helm charts
129+
- uses: actions/checkout@v4
130+
131+
# Install kubectl for Kubernetes cluster management
132+
- name: Setup Kubectl
133+
uses: azure/setup-kubectl@v3
134+
with:
135+
version: 'latest'
136+
137+
# Install Helm for package management
138+
- name: Setup Helm
139+
uses: azure/setup-helm@v3
140+
with:
141+
version: '3.12.0'
142+
143+
# Configure Kubernetes cluster access
144+
- name: Configure Kubernetes
145+
run: |
146+
mkdir -p ~/.kube
147+
# Decode base64-encoded kubeconfig from secrets
148+
echo "${{ secrets.KUBE_CONFIG }}" | base64 -d > ~/.kube/config
149+
chmod 600 ~/.kube/config
150+
151+
# Set context to student cluster
152+
kubectl config use-context student
153+
154+
# Add Bitnami repository for PostgreSQL dependency
155+
- name: Add Bitnami Helm Repository
156+
run: |
157+
helm repo add bitnami https://charts.bitnami.com/bitnami
158+
helm repo update
159+
160+
# ============================================================================
161+
# HELM DEPLOYMENT: Deploy application with secure secrets management
162+
# ============================================================================
163+
#
164+
# Uses values-secure.yaml which references Kubernetes secrets created
165+
# from GitHub Actions secrets. This ensures no sensitive data is stored
166+
# in version control.
167+
#
168+
# Deployed services:
169+
# - PostgreSQL database (Bitnami chart)
170+
# - All microservices with Swagger UI enabled
171+
# - Prometheus + Grafana monitoring stack
172+
# - Nginx ingress with Let's Encrypt TLS
173+
# ============================================================================
174+
175+
- name: Deploy with Helm
176+
run: |
177+
cd helm/study-assistant
178+
179+
# Update Helm chart dependencies (PostgreSQL)
180+
helm dependency update
181+
182+
# Deploy/upgrade the complete application stack
183+
# Uses values-secure.yaml for production-ready configuration
184+
helm upgrade --install study-assistant . \
185+
--namespace=dev \
186+
--timeout=10m \
187+
--wait \
188+
--values values-secure.yaml \
189+
--set secrets.postgresPassword="${{ secrets.POSTGRES_PASSWORD }}" \
190+
--set secrets.jwtSecret="${{ secrets.JWT_SECRET }}" \
191+
--set secrets.genaiApiKey="${{ secrets.GENAI_API_KEY }}" \
192+
--set secrets.grafanaPassword="${{ secrets.GRAFANA_PASSWORD }}"
193+
194+
- name: Verify Deployment
195+
run: |
196+
echo "Checking deployment status..."
197+
kubectl get pods -n dev -l app.kubernetes.io/instance=study-assistant
198+
kubectl get svc -n dev -l app.kubernetes.io/instance=study-assistant
199+
kubectl get ingress -n dev -l app.kubernetes.io/instance=study-assistant
200+
201+
# Wait for pods to be ready
202+
kubectl wait --for=condition=ready pod -l app.kubernetes.io/instance=study-assistant -n dev --timeout=300s
203+
204+
- name: Get Application URL
205+
run: |
206+
INGRESS_HOST=$(kubectl get ingress -n dev -l app.kubernetes.io/instance=study-assistant -o jsonpath='{.items[0].spec.rules[0].host}' 2>/dev/null || echo "No ingress found")
207+
if [ "$INGRESS_HOST" != "No ingress found" ]; then
208+
echo "🚀 Application deployed successfully!"
209+
echo "📱 Application URL: https://$INGRESS_HOST"
210+
else
211+
echo "⚠️ Application deployed but no ingress URL found"
212+
fi

.github/workflows/build_docker.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
name: Build Docker Images
22

33
on:
4-
push:
4+
workflow_run:
5+
workflows: ["Run Tests"]
6+
types:
7+
- completed
8+
59

610
jobs:
711
build:
12+
if: ${{ github.event.workflow_run.conclusion == 'success' && (github.event.workflow_run.head_branch == 'main' || github.event.workflow_run.head_branch == 'dev') }}
813
name: Build & Push Docker Images
914
runs-on: ubuntu-latest
1015
environment: AWS
@@ -61,7 +66,6 @@ jobs:
6166
REACT_APP_QUIZ_HOST=quiz.${{ vars.EC2_PUBLIC_IP }}.nip.io
6267
REACT_APP_FLASHCARD_HOST=flashcard.${{ vars.EC2_PUBLIC_IP }}.nip.io
6368
FRONTEND_ORIGIN=https://client.${{ vars.EC2_PUBLIC_IP }}.nip.io
64-
6569
- name: Debug IP injection
6670
if: matrix.service == 'client'
6771
run: |
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
name: Deploy to AWS with Terraform + Ansible
2+
3+
on:
4+
push:
5+
branches: [dev, ansible-terraform, main]
6+
workflow_dispatch:
7+
8+
env:
9+
AWS_REGION: us-east-1
10+
11+
jobs:
12+
deploy:
13+
name: Deploy Infrastructure and Application
14+
runs-on: ubuntu-latest
15+
environment: AWS
16+
17+
steps:
18+
- name: Checkout code
19+
uses: actions/checkout@v4
20+
21+
- name: Configure AWS credentials
22+
uses: aws-actions/configure-aws-credentials@v4
23+
with:
24+
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
25+
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
26+
aws-session-token: ${{ secrets.AWS_SESSION_TOKEN }}
27+
aws-region: ${{ env.AWS_REGION }}
28+
29+
- name: Setup Terraform
30+
uses: hashicorp/setup-terraform@v3
31+
with:
32+
terraform_version: 1.6.0
33+
terraform_wrapper: false
34+
35+
- name: Get current IP for security group
36+
id: get-ip
37+
run: |
38+
CURRENT_IP=$(curl -s ipv4.icanhazip.com)
39+
echo "current_ip=${CURRENT_IP}/32" >> $GITHUB_OUTPUT
40+
41+
- name: Setup SSH keys for Terraform
42+
run: |
43+
mkdir -p ~/.ssh
44+
# Use your local devops key for both deployment and local access
45+
echo "${{ secrets.LOCAL_SSH_PRIVATE_KEY }}" > ~/.ssh/devops.pem
46+
chmod 600 ~/.ssh/devops.pem
47+
ssh-keygen -y -f ~/.ssh/devops.pem > infrastructure/devops.pub
48+
49+
- name: Terraform Init
50+
working-directory: infrastructure
51+
run: terraform init
52+
53+
- name: Terraform Plan
54+
working-directory: infrastructure
55+
run: |
56+
# Build terraform plan command with required variables
57+
PLAN_CMD="terraform plan \
58+
-var=\"my_ip=${{ steps.get-ip.outputs.current_ip }}\" \
59+
-var=\"ssh_key=devops.pub\" \
60+
-var=\"ssh_private_key=~/.ssh/devops.pem\""
61+
62+
# Add local IP if provided
63+
if [ -n "${{ secrets.LOCAL_IP }}" ]; then
64+
PLAN_CMD="$PLAN_CMD -var=\"local_ip=${{ secrets.LOCAL_IP }}\""
65+
fi
66+
67+
# Add output file
68+
PLAN_CMD="$PLAN_CMD -out=tfplan"
69+
70+
# Execute the plan
71+
eval $PLAN_CMD
72+
73+
- name: Terraform Apply
74+
working-directory: infrastructure
75+
run: terraform apply -auto-approve tfplan
76+
77+
- name: Get EC2 Instance IP
78+
id: get-instance-ip
79+
working-directory: infrastructure
80+
run: |
81+
INSTANCE_IP=$(terraform output -raw server-ip)
82+
echo "instance_ip=${INSTANCE_IP}" >> $GITHUB_OUTPUT
83+
84+
- name: Update Ansible inventory
85+
run: |
86+
echo "${{ steps.get-instance-ip.outputs.instance_ip }} ansible_user=ec2-user ansible_ssh_private_key_file=~/.ssh/devops.pem" > infrastructure/ansible/inventory.ini
87+
88+
- name: Install Ansible
89+
run: |
90+
sudo apt-get update
91+
sudo apt-get install -y ansible
92+
93+
- name: Wait for EC2 instance to be ready
94+
run: |
95+
timeout 300 bash -c 'until ssh -o ConnectTimeout=5 -o StrictHostKeyChecking=no -i ~/.ssh/devops.pem ec2-user@${{ steps.get-instance-ip.outputs.instance_ip }} "echo ready"; do sleep 10; done'
96+
97+
- name: Run initial server setup
98+
working-directory: infrastructure/ansible
99+
run: |
100+
# Build ansible command with optional local SSH key
101+
ANSIBLE_CMD="ansible-playbook -i inventory.ini playbook-simple.yml"
102+
103+
# Add local SSH public key if provided (same key used for deployment)
104+
if [ -n "${{ secrets.LOCAL_SSH_PRIVATE_KEY }}" ]; then
105+
LOCAL_PUB_KEY=$(ssh-keygen -y -f ~/.ssh/devops.pem)
106+
ANSIBLE_CMD="$ANSIBLE_CMD -e \"local_ssh_public_key=$LOCAL_PUB_KEY\""
107+
fi
108+
109+
# Execute the command
110+
eval $ANSIBLE_CMD
111+
112+
- name: Run application deployment
113+
working-directory: infrastructure/ansible
114+
run: |
115+
ansible-playbook -i inventory.ini playbook-deploy.yml \
116+
-e "openai_api_key=${{ secrets.OPENAI_API_KEY }}" \
117+
-v
118+
119+
- name: Display deployment info
120+
run: |
121+
echo "🎉 Deployment successful!"
122+
echo "Frontend: http://${{ steps.get-instance-ip.outputs.instance_ip }}:3000"
123+
echo "Quiz Service: http://${{ steps.get-instance-ip.outputs.instance_ip }}:8081"
124+
echo "Flashcard Service: http://${{ steps.get-instance-ip.outputs.instance_ip }}:8082"
125+
echo "Auth Service: http://${{ steps.get-instance-ip.outputs.instance_ip }}:8083"
126+
echo "GenAI Service: http://${{ steps.get-instance-ip.outputs.instance_ip }}:5001"
127+
echo "PgAdmin: http://${{ steps.get-instance-ip.outputs.instance_ip }}:8080"
128+
echo "Prometheus: http://${{ steps.get-instance-ip.outputs.instance_ip }}:9090"
129+
echo "Grafana: http://${{ steps.get-instance-ip.outputs.instance_ip }}:3001"
130+
echo "Alertmanager: http://${{ steps.get-instance-ip.outputs.instance_ip }}:9093"

0 commit comments

Comments
 (0)