From 9adcab2048c4416a8d1b1f0bc93c92bbc9deccc4 Mon Sep 17 00:00:00 2001 From: djeon Date: Mon, 27 Oct 2025 17:31:03 +0900 Subject: [PATCH] add ci/cd --- .github/workflows/backend-cicd_ArgoCD.yaml | 254 +++++++++++ deploy/k8s/backend/README.md | 420 +++++++++++++++++++ deploy/k8s/backend/configmap.yaml | 20 + deploy/k8s/backend/create-secrets.sh | 103 +++++ deploy/k8s/backend/deploy.sh | 133 ++++++ deploy/k8s/backend/meeting-service.yaml | 112 +++++ deploy/k8s/backend/namespace.yaml | 9 + deploy/k8s/backend/notification-service.yaml | 129 ++++++ deploy/k8s/backend/secret-template.yaml | 36 ++ deploy/k8s/backend/undeploy.sh | 89 ++++ deploy/k8s/backend/user-service.yaml | 102 +++++ 11 files changed, 1407 insertions(+) create mode 100644 .github/workflows/backend-cicd_ArgoCD.yaml create mode 100644 deploy/k8s/backend/README.md create mode 100644 deploy/k8s/backend/configmap.yaml create mode 100755 deploy/k8s/backend/create-secrets.sh create mode 100755 deploy/k8s/backend/deploy.sh create mode 100644 deploy/k8s/backend/meeting-service.yaml create mode 100644 deploy/k8s/backend/namespace.yaml create mode 100644 deploy/k8s/backend/notification-service.yaml create mode 100644 deploy/k8s/backend/secret-template.yaml create mode 100755 deploy/k8s/backend/undeploy.sh create mode 100644 deploy/k8s/backend/user-service.yaml diff --git a/.github/workflows/backend-cicd_ArgoCD.yaml b/.github/workflows/backend-cicd_ArgoCD.yaml new file mode 100644 index 0000000..5ca97ed --- /dev/null +++ b/.github/workflows/backend-cicd_ArgoCD.yaml @@ -0,0 +1,254 @@ +name: Backend Services CI/CD + +on: + push: + branches: [ main, develop ] + paths: + - 'user/**' + - 'meeting/**' + - 'stt/**' + - 'ai/**' + - 'notification/**' + - 'common/**' + - '.github/**' + pull_request: + branches: [ main ] + workflow_dispatch: + inputs: + ENVIRONMENT: + description: 'Target environment' + required: true + default: 'dev' + type: choice + options: + - dev + - staging + - prod + SKIP_SONARQUBE: + description: 'Skip SonarQube Analysis' + required: false + default: 'true' + type: choice + options: + - 'true' + - 'false' + +env: + REGISTRY: acrdigitalgarage02.azurecr.io + IMAGE_ORG: hgzero + RESOURCE_GROUP: rg-digitalgarage-02 + AKS_CLUSTER: aks-digitalgarage-02 + NAMESPACE: hgzero + +jobs: + build: + name: Build and Test + runs-on: ubuntu-latest + outputs: + image_tag: ${{ steps.set_outputs.outputs.image_tag }} + environment: ${{ steps.set_outputs.outputs.environment }} + + steps: + - name: Check out code + uses: actions/checkout@v4 + + - name: Set up JDK 21 + uses: actions/setup-java@v3 + with: + java-version: '21' + distribution: 'temurin' + cache: 'gradle' + + - name: Determine environment + id: determine_env + run: | + # Use input parameter or default to 'dev' + ENVIRONMENT="${{ github.event.inputs.ENVIRONMENT || 'dev' }}" + echo "environment=$ENVIRONMENT" >> $GITHUB_OUTPUT + + - name: Load environment variables + id: env_vars + run: | + ENV=${{ steps.determine_env.outputs.environment }} + + # Initialize variables with defaults + REGISTRY="acrdigitalgarage02.azurecr.io" + IMAGE_ORG="hgzero" + RESOURCE_GROUP="rg-digitalgarage-02" + AKS_CLUSTER="aks-digitalgarage-02" + NAMESPACE="hgzero" + + # Read environment variables from .github/config file + if [[ -f ".github/config/deploy_env_vars_${ENV}" ]]; then + while IFS= read -r line || [[ -n "$line" ]]; do + # Skip comments and empty lines + [[ "$line" =~ ^#.*$ ]] && continue + [[ -z "$line" ]] && continue + + # Extract key-value pairs + key=$(echo "$line" | cut -d '=' -f1) + value=$(echo "$line" | cut -d '=' -f2-) + + # Override defaults if found in config + case "$key" in + "resource_group") RESOURCE_GROUP="$value" ;; + "cluster_name") AKS_CLUSTER="$value" ;; + esac + done < ".github/config/deploy_env_vars_${ENV}" + fi + + # Export for other jobs + echo "REGISTRY=$REGISTRY" >> $GITHUB_ENV + echo "IMAGE_ORG=$IMAGE_ORG" >> $GITHUB_ENV + echo "RESOURCE_GROUP=$RESOURCE_GROUP" >> $GITHUB_ENV + echo "AKS_CLUSTER=$AKS_CLUSTER" >> $GITHUB_ENV + + - name: Grant execute permission for gradlew + run: chmod +x gradlew + + - name: Build with Gradle + run: | + ./gradlew build -x test + + - name: SonarQube Analysis & Quality Gate + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }} + run: | + # Check if SonarQube should be skipped + SKIP_SONARQUBE="${{ github.event.inputs.SKIP_SONARQUBE || 'true' }}" + + if [[ "$SKIP_SONARQUBE" == "true" ]]; then + echo "⏭️ Skipping SonarQube Analysis (SKIP_SONARQUBE=$SKIP_SONARQUBE)" + exit 0 + fi + + # Define services array + services=(user meeting stt ai notification) + + # Run tests, coverage reports, and SonarQube analysis for each service + for service in "${services[@]}"; do + ./gradlew :$service:test :$service:jacocoTestReport :$service:sonar \ + -Dsonar.projectKey=hgzero-$service-${{ steps.determine_env.outputs.environment }} \ + -Dsonar.projectName=hgzero-$service-${{ steps.determine_env.outputs.environment }} \ + -Dsonar.host.url=$SONAR_HOST_URL \ + -Dsonar.token=$SONAR_TOKEN \ + -Dsonar.java.binaries=build/classes/java/main \ + -Dsonar.coverage.jacoco.xmlReportPaths=build/reports/jacoco/test/jacocoTestReport.xml \ + -Dsonar.exclusions=**/config/**,**/entity/**,**/dto/**,**/*Application.class,**/exception/** + done + + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: app-builds + path: | + user/build/libs/*.jar + meeting/build/libs/*.jar + stt/build/libs/*.jar + ai/build/libs/*.jar + notification/build/libs/*.jar + + - name: Set outputs + id: set_outputs + run: | + # Generate timestamp for image tag + IMAGE_TAG=$(date +%Y%m%d%H%M%S) + echo "image_tag=$IMAGE_TAG" >> $GITHUB_OUTPUT + echo "environment=${{ steps.determine_env.outputs.environment }}" >> $GITHUB_OUTPUT + + release: + name: Build and Push Docker Images + needs: build + runs-on: ubuntu-latest + + steps: + - name: Check out code + uses: actions/checkout@v4 + + - name: Download build artifacts + uses: actions/download-artifact@v4 + with: + name: app-builds + + - name: Set environment variables from build job + run: | + echo "REGISTRY=${{ env.REGISTRY }}" >> $GITHUB_ENV + echo "IMAGE_ORG=${{ env.IMAGE_ORG }}" >> $GITHUB_ENV + echo "ENVIRONMENT=${{ needs.build.outputs.environment }}" >> $GITHUB_ENV + echo "IMAGE_TAG=${{ needs.build.outputs.image_tag }}" >> $GITHUB_ENV + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Docker Hub (prevent rate limit) + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_PASSWORD }} + + - name: Login to Azure Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ secrets.ACR_USERNAME }} + password: ${{ secrets.ACR_PASSWORD }} + + - name: Build and push Docker images for all services + run: | + # Define services array + services=(user meeting stt ai notification) + + # Build and push each service image + for service in "${services[@]}"; do + echo "Building and pushing $service..." + docker build \ + --build-arg BUILD_LIB_DIR="$service/build/libs" \ + --build-arg ARTIFACTORY_FILE="$service.jar" \ + -f deployment/container/Dockerfile-backend \ + -t ${{ env.REGISTRY }}/${{ env.IMAGE_ORG }}/$service:${{ needs.build.outputs.environment }}-${{ needs.build.outputs.image_tag }} . + + docker push ${{ env.REGISTRY }}/${{ env.IMAGE_ORG }}/$service:${{ needs.build.outputs.environment }}-${{ needs.build.outputs.image_tag }} + done + + update-manifest: + name: Update Manifest Repository + needs: [build, release] + runs-on: ubuntu-latest + + steps: + - name: Set image tag environment variable + run: | + echo "IMAGE_TAG=${{ needs.build.outputs.image_tag }}" >> $GITHUB_ENV + echo "ENVIRONMENT=${{ needs.build.outputs.environment }}" >> $GITHUB_ENV + + - name: Update Manifest Repository + run: | + # 매니페스트 레포지토리 클론 + REPO_URL=$(echo "https://github.com/hjmoons/hgzero-manifest.git" | sed 's|https://||') + git clone https://${{ secrets.GIT_USERNAME }}:${{ secrets.GIT_PASSWORD }}@${REPO_URL} manifest-repo + cd manifest-repo + + # Kustomize 설치 + curl -s "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" | bash + sudo mv kustomize /usr/local/bin/ + + # 매니페스트 업데이트 + cd hgzero/kustomize/overlays/${{ env.ENVIRONMENT }} + + # 각 서비스별 이미지 태그 업데이트 + services="user meeting stt ai notification" + for service in $services; do + kustomize edit set image acrdigitalgarage02.azurecr.io/hgzero/$service:${{ env.ENVIRONMENT }}-${{ env.IMAGE_TAG }} + done + + # Git 설정 및 푸시 + cd ../../../.. + git config user.name "GitHub Actions" + git config user.email "actions@github.com" + git add . + git commit -m "🚀 Update hgzero ${{ env.ENVIRONMENT }} images to ${{ env.ENVIRONMENT }}-${{ env.IMAGE_TAG }}" + git push origin main + + echo "✅ 매니페스트 업데이트 완료. ArgoCD가 자동으로 배포합니다." diff --git a/deploy/k8s/backend/README.md b/deploy/k8s/backend/README.md new file mode 100644 index 0000000..b835e5d --- /dev/null +++ b/deploy/k8s/backend/README.md @@ -0,0 +1,420 @@ +# HGZero 백엔드 서비스 Kubernetes 배포 가이드 + +## 개요 + +이 가이드는 HGZero 백엔드 서비스(User, Meeting, Notification)를 Azure Kubernetes Service(AKS)에 배포하는 방법을 설명합니다. + +## 배포 환경 + +- **ACR(Azure Container Registry)**: acrdigitalgarage02 +- **AKS(Azure Kubernetes Service)**: aks-digitalgarage-02 +- **네임스페이스**: hgzero +- **리소스 그룹**: rg-digitalgarage-02 + +## 서비스 구성 + +### 1. User Service +- **포트**: 8080 +- **이미지**: acrdigitalgarage02.azurecr.io/hgzero/user-service:latest +- **기능**: 사용자 인증 및 관리, LDAP 연동 + +### 2. Meeting Service +- **포트**: 8081 (HTTP), 8082 (WebSocket) +- **이미지**: acrdigitalgarage02.azurecr.io/hgzero/meeting-service:latest +- **기능**: 회의 관리, WebSocket 통신 + +### 3. Notification Service +- **포트**: 8082 +- **이미지**: acrdigitalgarage02.azurecr.io/hgzero/notification-service:latest +- **기능**: 알림 전송, 이메일 발송 + +## 리소스 할당 + +각 서비스는 다음과 같은 리소스를 사용합니다: + +- **Requests**: + - CPU: 256m + - Memory: 256Mi +- **Limits**: + - CPU: 1024m + - Memory: 1024Mi +- **Replicas**: 1 (각 서비스) + +## 사전 요구사항 + +1. **Azure CLI 설치** + ```bash + # macOS + brew install azure-cli + + # Windows + # Download from https://aka.ms/installazurecliwindows + + # Linux + curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash + ``` + +2. **kubectl 설치** + ```bash + # macOS + brew install kubectl + + # Windows + # Download from https://kubernetes.io/docs/tasks/tools/install-kubectl-windows/ + + # Linux + curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" + sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl + ``` + +3. **Azure 로그인** + ```bash + az login + ``` + +4. **ACR 로그인** + ```bash + az acr login --name acrdigitalgarage02 + ``` + +## 배포 파일 구조 + +``` +deploy/k8s/backend/ +├── namespace.yaml # 네임스페이스 정의 +├── configmap.yaml # ConfigMap (Redis, Mail 설정) +├── secret-template.yaml # Secret 템플릿 (민감 정보) +├── user-service.yaml # User Service 배포 매니페스트 +├── meeting-service.yaml # Meeting Service 배포 매니페스트 +├── notification-service.yaml # Notification Service 배포 매니페스트 +├── deploy.sh # 배포 스크립트 +├── undeploy.sh # 배포 해제 스크립트 +├── create-secrets.sh # Secret 생성 스크립트 +└── README.md # 이 문서 +``` + +## 배포 절차 + +### 1단계: AKS 클러스터 접근 설정 + +```bash +# AKS credentials 가져오기 +az aks get-credentials \ + --resource-group rg-digitalgarage-02 \ + --name aks-digitalgarage-02 \ + --overwrite-existing + +# 클러스터 연결 확인 +kubectl cluster-info +``` + +### 2단계: 네임스페이스 생성 + +```bash +kubectl apply -f namespace.yaml +``` + +### 3단계: ConfigMap 생성 + +```bash +kubectl apply -f configmap.yaml +``` + +### 4단계: Secret 생성 + +옵션 A: 대화형 스크립트 사용 (권장) +```bash +./create-secrets.sh +``` + +옵션 B: 직접 생성 +```bash +# Database Secret +kubectl create secret generic db-secret \ + --from-literal=host= \ + --from-literal=username= \ + --from-literal=password= \ + --namespace=hgzero + +# Azure Secret +kubectl create secret generic azure-secret \ + --from-literal=eventhub-connection-string= \ + --from-literal=blob-connection-string= \ + --namespace=hgzero + +# Mail Secret +kubectl create secret generic mail-secret \ + --from-literal=username= \ + --from-literal=password= \ + --namespace=hgzero +``` + +### 5단계: 서비스 배포 + +옵션 A: 자동 배포 스크립트 사용 (권장) +```bash +./deploy.sh +``` + +옵션 B: 수동 배포 +```bash +# ACR 연동 설정 +az aks update \ + -n aks-digitalgarage-02 \ + -g rg-digitalgarage-02 \ + --attach-acr acrdigitalgarage02 + +# 서비스 배포 +kubectl apply -f user-service.yaml +kubectl apply -f notification-service.yaml +kubectl apply -f meeting-service.yaml +``` + +### 6단계: 배포 상태 확인 + +```bash +# Deployment 상태 확인 +kubectl get deployments -n hgzero + +# Pod 상태 확인 +kubectl get pods -n hgzero + +# Service 확인 +kubectl get services -n hgzero + +# 상세 정보 확인 +kubectl describe deployment user-service -n hgzero +``` + +## 배포 확인 및 테스트 + +### Pod 로그 확인 + +```bash +# User Service 로그 +kubectl logs -f deployment/user-service -n hgzero + +# Meeting Service 로그 +kubectl logs -f deployment/meeting-service -n hgzero + +# Notification Service 로그 +kubectl logs -f deployment/notification-service -n hgzero +``` + +### Health Check + +```bash +# User Service health check +kubectl port-forward svc/user-service 8080:8080 -n hgzero +curl http://localhost:8080/actuator/health + +# Meeting Service health check +kubectl port-forward svc/meeting-service 8081:8081 -n hgzero +curl http://localhost:8081/actuator/health + +# Notification Service health check +kubectl port-forward svc/notification-service 8082:8082 -n hgzero +curl http://localhost:8082/actuator/health +``` + +### Pod 내부 접속 + +```bash +# Pod 내부로 접속하여 디버깅 +kubectl exec -it deployment/user-service -n hgzero -- /bin/sh +``` + +## 배포 해제 + +### 전체 서비스 삭제 + +```bash +./undeploy.sh +``` + +### 개별 서비스 삭제 + +```bash +kubectl delete -f user-service.yaml +kubectl delete -f meeting-service.yaml +kubectl delete -f notification-service.yaml +``` + +### Secret 및 ConfigMap 삭제 + +```bash +kubectl delete configmap redis-config mail-config -n hgzero +kubectl delete secret db-secret azure-secret mail-secret -n hgzero +``` + +### 네임스페이스 삭제 + +```bash +kubectl delete namespace hgzero +``` + +## 트러블슈팅 + +### Pod가 시작되지 않는 경우 + +```bash +# Pod 상태 확인 +kubectl get pods -n hgzero + +# Pod 상세 정보 확인 +kubectl describe pod -n hgzero + +# Pod 로그 확인 +kubectl logs -n hgzero + +# 이전 컨테이너 로그 확인 (재시작된 경우) +kubectl logs -n hgzero --previous +``` + +### 이미지 Pull 오류 + +```bash +# ACR 연동 상태 확인 +az aks show -n aks-digitalgarage-02 -g rg-digitalgarage-02 --query "servicePrincipalProfile" + +# ACR 재연동 +az aks update -n aks-digitalgarage-02 -g rg-digitalgarage-02 --attach-acr acrdigitalgarage02 + +# ImagePullSecret 확인 +kubectl get serviceaccount default -n hgzero -o yaml +``` + +### Secret 관련 오류 + +```bash +# Secret 존재 확인 +kubectl get secrets -n hgzero + +# Secret 내용 확인 +kubectl get secret db-secret -n hgzero -o yaml + +# Secret 재생성 +kubectl delete secret db-secret -n hgzero +./create-secrets.sh +``` + +### 네트워크 연결 문제 + +```bash +# Service Endpoint 확인 +kubectl get endpoints -n hgzero + +# DNS 확인 +kubectl run -it --rm debug --image=busybox --restart=Never -- nslookup user-service.hgzero.svc.cluster.local + +# 네트워크 정책 확인 +kubectl get networkpolicies -n hgzero +``` + +### 리소스 부족 문제 + +```bash +# 노드 리소스 사용량 확인 +kubectl top nodes + +# Pod 리소스 사용량 확인 +kubectl top pods -n hgzero + +# 리소스 제한 조정 (필요 시) +kubectl edit deployment user-service -n hgzero +``` + +## 업데이트 및 롤링 배포 + +### 이미지 업데이트 + +```bash +# 새 이미지 태그로 업데이트 +kubectl set image deployment/user-service \ + user-service=acrdigitalgarage02.azurecr.io/hgzero/user-service:v2.0.0 \ + -n hgzero + +# 롤아웃 상태 확인 +kubectl rollout status deployment/user-service -n hgzero + +# 롤아웃 히스토리 확인 +kubectl rollout history deployment/user-service -n hgzero +``` + +### 롤백 + +```bash +# 이전 버전으로 롤백 +kubectl rollout undo deployment/user-service -n hgzero + +# 특정 revision으로 롤백 +kubectl rollout undo deployment/user-service --to-revision=2 -n hgzero +``` + +## 모니터링 + +### Kubernetes Dashboard + +```bash +# Dashboard 접근 +az aks browse -n aks-digitalgarage-02 -g rg-digitalgarage-02 +``` + +### 리소스 모니터링 + +```bash +# 실시간 리소스 사용량 모니터링 +watch kubectl top pods -n hgzero + +# 이벤트 모니터링 +kubectl get events -n hgzero --sort-by='.lastTimestamp' +``` + +## 보안 권장사항 + +1. **Secret 관리** + - Secret은 절대 Git에 커밋하지 마세요 + - Azure Key Vault 통합을 고려하세요 + - Secret rotation 정책을 수립하세요 + +2. **네트워크 보안** + - Network Policy를 활용하여 Pod 간 통신을 제한하세요 + - Service Mesh 도입을 고려하세요 + +3. **이미지 보안** + - 정기적으로 이미지 스캔을 수행하세요 + - 최소 권한 원칙을 적용하세요 + +## 성능 최적화 + +1. **Auto Scaling 설정** + ```bash + kubectl autoscale deployment user-service \ + --cpu-percent=70 \ + --min=1 \ + --max=5 \ + -n hgzero + ``` + +2. **리소스 조정** + - 실제 사용량에 따라 requests/limits를 조정하세요 + - HPA(Horizontal Pod Autoscaler) 설정을 고려하세요 + +## 참고 자료 + +- [Azure Kubernetes Service 문서](https://docs.microsoft.com/azure/aks/) +- [Kubernetes 공식 문서](https://kubernetes.io/docs/) +- [kubectl 치트시트](https://kubernetes.io/docs/reference/kubectl/cheatsheet/) + +## 지원 및 문의 + +문제 발생 시 다음 정보를 수집하여 보고해주세요: + +```bash +# 진단 정보 수집 +kubectl get all -n hgzero > hgzero-resources.txt +kubectl describe pods -n hgzero > hgzero-pods-detail.txt +kubectl logs deployment/user-service -n hgzero > user-service.log +kubectl logs deployment/meeting-service -n hgzero > meeting-service.log +kubectl logs deployment/notification-service -n hgzero > notification-service.log +``` diff --git a/deploy/k8s/backend/configmap.yaml b/deploy/k8s/backend/configmap.yaml new file mode 100644 index 0000000..10e0500 --- /dev/null +++ b/deploy/k8s/backend/configmap.yaml @@ -0,0 +1,20 @@ +--- +# Redis Configuration +apiVersion: v1 +kind: ConfigMap +metadata: + name: redis-config + namespace: hgzero +data: + host: "redis-service.hgzero.svc.cluster.local" + port: "6379" +--- +# Mail Configuration +apiVersion: v1 +kind: ConfigMap +metadata: + name: mail-config + namespace: hgzero +data: + host: "smtp.gmail.com" + port: "587" diff --git a/deploy/k8s/backend/create-secrets.sh b/deploy/k8s/backend/create-secrets.sh new file mode 100755 index 0000000..8123127 --- /dev/null +++ b/deploy/k8s/backend/create-secrets.sh @@ -0,0 +1,103 @@ +#!/bin/bash + +# HGZero Backend Services Secrets Creation Script +# This script helps create Kubernetes secrets for the backend services + +set -e + +# Color codes for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Configuration +NAMESPACE="hgzero" + +echo -e "${GREEN}======================================${NC}" +echo -e "${GREEN}HGZero Secrets Creation${NC}" +echo -e "${GREEN}======================================${NC}" + +# Check if kubectl is installed +if ! command -v kubectl &> /dev/null; then + echo -e "${RED}Error: kubectl is not installed${NC}" + exit 1 +fi + +# Verify connection to cluster +echo -e "${YELLOW}Verifying connection to Kubernetes cluster...${NC}" +if ! kubectl cluster-info &> /dev/null; then + echo -e "${RED}Error: Cannot connect to Kubernetes cluster${NC}" + exit 1 +fi + +# Check if namespace exists +if ! kubectl get namespace ${NAMESPACE} &> /dev/null; then + echo -e "${RED}Error: Namespace '${NAMESPACE}' does not exist${NC}" + echo -e "${YELLOW}Please run deploy.sh first to create the namespace${NC}" + exit 1 +fi + +# Function to prompt for secret value +prompt_secret() { + local prompt_text=$1 + local secret_value + echo -n -e "${YELLOW}${prompt_text}: ${NC}" + read -s secret_value + echo "" + echo -n "$secret_value" +} + +# Create Database Secret +echo -e "${GREEN}Creating Database Secret...${NC}" +DB_HOST=$(prompt_secret "Enter Database Host") +DB_USERNAME=$(prompt_secret "Enter Database Username") +DB_PASSWORD=$(prompt_secret "Enter Database Password") + +kubectl create secret generic db-secret \ + --from-literal=host="${DB_HOST}" \ + --from-literal=username="${DB_USERNAME}" \ + --from-literal=password="${DB_PASSWORD}" \ + --namespace=${NAMESPACE} \ + --dry-run=client -o yaml | kubectl apply -f - + +echo -e "${GREEN}✓ Database secret created${NC}" +echo "" + +# Create Azure Secret +echo -e "${GREEN}Creating Azure Secret...${NC}" +EVENTHUB_CONN=$(prompt_secret "Enter EventHub Connection String") +BLOB_CONN=$(prompt_secret "Enter Blob Storage Connection String") + +kubectl create secret generic azure-secret \ + --from-literal=eventhub-connection-string="${EVENTHUB_CONN}" \ + --from-literal=blob-connection-string="${BLOB_CONN}" \ + --namespace=${NAMESPACE} \ + --dry-run=client -o yaml | kubectl apply -f - + +echo -e "${GREEN}✓ Azure secret created${NC}" +echo "" + +# Create Mail Secret +echo -e "${GREEN}Creating Mail Secret...${NC}" +MAIL_USERNAME=$(prompt_secret "Enter Mail Username") +MAIL_PASSWORD=$(prompt_secret "Enter Mail Password") + +kubectl create secret generic mail-secret \ + --from-literal=username="${MAIL_USERNAME}" \ + --from-literal=password="${MAIL_PASSWORD}" \ + --namespace=${NAMESPACE} \ + --dry-run=client -o yaml | kubectl apply -f - + +echo -e "${GREEN}✓ Mail secret created${NC}" +echo "" + +# Verify secrets +echo -e "${GREEN}======================================${NC}" +echo -e "${GREEN}Secrets Created Successfully${NC}" +echo -e "${GREEN}======================================${NC}" +kubectl get secrets -n ${NAMESPACE} + +echo "" +echo -e "${YELLOW}Note: Secrets are stored in Kubernetes and can be viewed with:${NC}" +echo -e " kubectl get secret -n ${NAMESPACE} -o yaml" diff --git a/deploy/k8s/backend/deploy.sh b/deploy/k8s/backend/deploy.sh new file mode 100755 index 0000000..50a579a --- /dev/null +++ b/deploy/k8s/backend/deploy.sh @@ -0,0 +1,133 @@ +#!/bin/bash + +# HGZero Backend Services Kubernetes Deployment Script +# Azure Container Registry: acrdigitalgarage02 +# Azure Kubernetes Service: aks-digitalgarage-02 +# Namespace: hgzero + +set -e + +# Color codes for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Configuration +ACR_NAME="acrdigitalgarage02" +AKS_NAME="aks-digitalgarage-02" +RESOURCE_GROUP="rg-digitalgarage-02" +NAMESPACE="hgzero" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +echo -e "${GREEN}======================================${NC}" +echo -e "${GREEN}HGZero Backend Services Deployment${NC}" +echo -e "${GREEN}======================================${NC}" + +# Check if kubectl is installed +if ! command -v kubectl &> /dev/null; then + echo -e "${RED}Error: kubectl is not installed${NC}" + exit 1 +fi + +# Check if Azure CLI is installed +if ! command -v az &> /dev/null; then + echo -e "${RED}Error: Azure CLI is not installed${NC}" + exit 1 +fi + +# Login to Azure (if not already logged in) +echo -e "${YELLOW}Checking Azure login status...${NC}" +if ! az account show &> /dev/null; then + echo -e "${YELLOW}Please login to Azure...${NC}" + az login +fi + +# Get AKS credentials +echo -e "${YELLOW}Getting AKS credentials...${NC}" +az aks get-credentials --resource-group ${RESOURCE_GROUP} --name ${AKS_NAME} --overwrite-existing + +# Verify connection to cluster +echo -e "${YELLOW}Verifying connection to Kubernetes cluster...${NC}" +if ! kubectl cluster-info &> /dev/null; then + echo -e "${RED}Error: Cannot connect to Kubernetes cluster${NC}" + exit 1 +fi +echo -e "${GREEN}✓ Successfully connected to ${AKS_NAME}${NC}" + +# Create namespace if it doesn't exist +echo -e "${YELLOW}Creating namespace '${NAMESPACE}'...${NC}" +kubectl apply -f ${SCRIPT_DIR}/namespace.yaml +echo -e "${GREEN}✓ Namespace created/verified${NC}" + +# Apply ConfigMaps +echo -e "${YELLOW}Applying ConfigMaps...${NC}" +kubectl apply -f ${SCRIPT_DIR}/configmap.yaml +echo -e "${GREEN}✓ ConfigMaps applied${NC}" + +# Check if secrets exist +echo -e "${YELLOW}Checking for secrets...${NC}" +if ! kubectl get secret db-secret -n ${NAMESPACE} &> /dev/null || \ + ! kubectl get secret azure-secret -n ${NAMESPACE} &> /dev/null || \ + ! kubectl get secret mail-secret -n ${NAMESPACE} &> /dev/null; then + echo -e "${RED}Warning: One or more secrets are missing!${NC}" + echo -e "${YELLOW}Please create secrets using secret-template.yaml as reference${NC}" + echo -e "${YELLOW}Example:${NC}" + echo -e " kubectl create secret generic db-secret -n ${NAMESPACE} \\" + echo -e " --from-literal=host= \\" + echo -e " --from-literal=username= \\" + echo -e " --from-literal=password=" + echo "" + read -p "Do you want to continue without secrets? (y/N): " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + echo -e "${RED}Deployment cancelled${NC}" + exit 1 + fi +fi + +# Configure ACR integration +echo -e "${YELLOW}Configuring ACR integration...${NC}" +az aks update -n ${AKS_NAME} -g ${RESOURCE_GROUP} --attach-acr ${ACR_NAME} +echo -e "${GREEN}✓ ACR integration configured${NC}" + +# Deploy services +echo -e "${YELLOW}Deploying User Service...${NC}" +kubectl apply -f ${SCRIPT_DIR}/user-service.yaml +echo -e "${GREEN}✓ User Service deployed${NC}" + +echo -e "${YELLOW}Deploying Notification Service...${NC}" +kubectl apply -f ${SCRIPT_DIR}/notification-service.yaml +echo -e "${GREEN}✓ Notification Service deployed${NC}" + +echo -e "${YELLOW}Deploying Meeting Service...${NC}" +kubectl apply -f ${SCRIPT_DIR}/meeting-service.yaml +echo -e "${GREEN}✓ Meeting Service deployed${NC}" + +# Wait for deployments to be ready +echo -e "${YELLOW}Waiting for deployments to be ready...${NC}" +kubectl wait --for=condition=available --timeout=300s \ + deployment/user-service \ + deployment/notification-service \ + deployment/meeting-service \ + -n ${NAMESPACE} + +# Show deployment status +echo -e "${GREEN}======================================${NC}" +echo -e "${GREEN}Deployment Status${NC}" +echo -e "${GREEN}======================================${NC}" +kubectl get deployments -n ${NAMESPACE} +echo "" +kubectl get pods -n ${NAMESPACE} +echo "" +kubectl get services -n ${NAMESPACE} + +echo -e "${GREEN}======================================${NC}" +echo -e "${GREEN}Deployment Completed Successfully!${NC}" +echo -e "${GREEN}======================================${NC}" +echo "" +echo -e "${YELLOW}Useful commands:${NC}" +echo -e " View logs: kubectl logs -f deployment/ -n ${NAMESPACE}" +echo -e " View pods: kubectl get pods -n ${NAMESPACE}" +echo -e " Describe pod: kubectl describe pod -n ${NAMESPACE}" +echo -e " Port forward: kubectl port-forward svc/ : -n ${NAMESPACE}" diff --git a/deploy/k8s/backend/meeting-service.yaml b/deploy/k8s/backend/meeting-service.yaml new file mode 100644 index 0000000..2ad0472 --- /dev/null +++ b/deploy/k8s/backend/meeting-service.yaml @@ -0,0 +1,112 @@ +--- +# Meeting Service Deployment +apiVersion: apps/v1 +kind: Deployment +metadata: + name: meeting-service + namespace: hgzero + labels: + app: meeting-service + tier: backend +spec: + replicas: 1 + selector: + matchLabels: + app: meeting-service + template: + metadata: + labels: + app: meeting-service + tier: backend + spec: + containers: + - name: meeting-service + image: acrdigitalgarage02.azurecr.io/hgzero/meeting-service:latest + imagePullPolicy: Always + ports: + - containerPort: 8081 + name: http + - containerPort: 8082 + name: websocket + env: + - name: SPRING_PROFILES_ACTIVE + value: "prod" + - name: SERVER_PORT + value: "8081" + - name: DB_HOST + valueFrom: + secretKeyRef: + name: db-secret + key: host + - name: DB_USERNAME + valueFrom: + secretKeyRef: + name: db-secret + key: username + - name: DB_PASSWORD + valueFrom: + secretKeyRef: + name: db-secret + key: password + - name: REDIS_HOST + valueFrom: + configMapKeyRef: + name: redis-config + key: host + - name: REDIS_PORT + valueFrom: + configMapKeyRef: + name: redis-config + key: port + - name: AZURE_EVENTHUB_CONNECTION_STRING + valueFrom: + secretKeyRef: + name: azure-secret + key: eventhub-connection-string + - name: NOTIFICATION_SERVICE_URL + value: "http://notification-service:8082" + resources: + requests: + cpu: 256m + memory: 256Mi + limits: + cpu: 1024m + memory: 1024Mi + livenessProbe: + httpGet: + path: /actuator/health/liveness + port: 8081 + initialDelaySeconds: 60 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /actuator/health/readiness + port: 8081 + initialDelaySeconds: 30 + periodSeconds: 5 + timeoutSeconds: 3 + failureThreshold: 3 +--- +# Meeting Service Service +apiVersion: v1 +kind: Service +metadata: + name: meeting-service + namespace: hgzero + labels: + app: meeting-service +spec: + type: ClusterIP + ports: + - port: 8081 + targetPort: 8081 + protocol: TCP + name: http + - port: 8082 + targetPort: 8082 + protocol: TCP + name: websocket + selector: + app: meeting-service diff --git a/deploy/k8s/backend/namespace.yaml b/deploy/k8s/backend/namespace.yaml new file mode 100644 index 0000000..c277997 --- /dev/null +++ b/deploy/k8s/backend/namespace.yaml @@ -0,0 +1,9 @@ +--- +# Namespace for HGZero application +apiVersion: v1 +kind: Namespace +metadata: + name: hgzero + labels: + name: hgzero + environment: production diff --git a/deploy/k8s/backend/notification-service.yaml b/deploy/k8s/backend/notification-service.yaml new file mode 100644 index 0000000..8d56964 --- /dev/null +++ b/deploy/k8s/backend/notification-service.yaml @@ -0,0 +1,129 @@ +--- +# Notification Service Deployment +apiVersion: apps/v1 +kind: Deployment +metadata: + name: notification-service + namespace: hgzero + labels: + app: notification-service + tier: backend +spec: + replicas: 1 + selector: + matchLabels: + app: notification-service + template: + metadata: + labels: + app: notification-service + tier: backend + spec: + containers: + - name: notification-service + image: acrdigitalgarage02.azurecr.io/hgzero/notification-service:latest + imagePullPolicy: Always + ports: + - containerPort: 8082 + name: http + env: + - name: SPRING_PROFILES_ACTIVE + value: "prod" + - name: SERVER_PORT + value: "8082" + - name: DB_HOST + valueFrom: + secretKeyRef: + name: db-secret + key: host + - name: DB_USERNAME + valueFrom: + secretKeyRef: + name: db-secret + key: username + - name: DB_PASSWORD + valueFrom: + secretKeyRef: + name: db-secret + key: password + - name: REDIS_HOST + valueFrom: + configMapKeyRef: + name: redis-config + key: host + - name: REDIS_PORT + valueFrom: + configMapKeyRef: + name: redis-config + key: port + - name: AZURE_EVENTHUB_CONNECTION_STRING + valueFrom: + secretKeyRef: + name: azure-secret + key: eventhub-connection-string + - name: AZURE_BLOB_CONNECTION_STRING + valueFrom: + secretKeyRef: + name: azure-secret + key: blob-connection-string + - name: MAIL_HOST + valueFrom: + configMapKeyRef: + name: mail-config + key: host + - name: MAIL_PORT + valueFrom: + configMapKeyRef: + name: mail-config + key: port + - name: MAIL_USERNAME + valueFrom: + secretKeyRef: + name: mail-secret + key: username + - name: MAIL_PASSWORD + valueFrom: + secretKeyRef: + name: mail-secret + key: password + resources: + requests: + cpu: 256m + memory: 256Mi + limits: + cpu: 1024m + memory: 1024Mi + livenessProbe: + httpGet: + path: /actuator/health/liveness + port: 8082 + initialDelaySeconds: 60 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /actuator/health/readiness + port: 8082 + initialDelaySeconds: 30 + periodSeconds: 5 + timeoutSeconds: 3 + failureThreshold: 3 +--- +# Notification Service Service +apiVersion: v1 +kind: Service +metadata: + name: notification-service + namespace: hgzero + labels: + app: notification-service +spec: + type: ClusterIP + ports: + - port: 8082 + targetPort: 8082 + protocol: TCP + name: http + selector: + app: notification-service diff --git a/deploy/k8s/backend/secret-template.yaml b/deploy/k8s/backend/secret-template.yaml new file mode 100644 index 0000000..6ace0e8 --- /dev/null +++ b/deploy/k8s/backend/secret-template.yaml @@ -0,0 +1,36 @@ +--- +# Database Secret Template +# Note: Replace base64 encoded values with your actual credentials +# To encode: echo -n 'your-value' | base64 +apiVersion: v1 +kind: Secret +metadata: + name: db-secret + namespace: hgzero +type: Opaque +data: + host: + username: + password: +--- +# Azure Secret Template +apiVersion: v1 +kind: Secret +metadata: + name: azure-secret + namespace: hgzero +type: Opaque +data: + eventhub-connection-string: + blob-connection-string: +--- +# Mail Secret Template +apiVersion: v1 +kind: Secret +metadata: + name: mail-secret + namespace: hgzero +type: Opaque +data: + username: + password: diff --git a/deploy/k8s/backend/undeploy.sh b/deploy/k8s/backend/undeploy.sh new file mode 100755 index 0000000..76f07a8 --- /dev/null +++ b/deploy/k8s/backend/undeploy.sh @@ -0,0 +1,89 @@ +#!/bin/bash + +# HGZero Backend Services Kubernetes Undeployment Script + +set -e + +# Color codes for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Configuration +NAMESPACE="hgzero" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +echo -e "${YELLOW}======================================${NC}" +echo -e "${YELLOW}HGZero Backend Services Undeployment${NC}" +echo -e "${YELLOW}======================================${NC}" + +# Check if kubectl is installed +if ! command -v kubectl &> /dev/null; then + echo -e "${RED}Error: kubectl is not installed${NC}" + exit 1 +fi + +# Verify connection to cluster +echo -e "${YELLOW}Verifying connection to Kubernetes cluster...${NC}" +if ! kubectl cluster-info &> /dev/null; then + echo -e "${RED}Error: Cannot connect to Kubernetes cluster${NC}" + exit 1 +fi + +# Check if namespace exists +if ! kubectl get namespace ${NAMESPACE} &> /dev/null; then + echo -e "${YELLOW}Namespace '${NAMESPACE}' does not exist. Nothing to undeploy.${NC}" + exit 0 +fi + +echo -e "${YELLOW}This will delete all services in namespace '${NAMESPACE}'${NC}" +read -p "Are you sure you want to continue? (y/N): " -n 1 -r +echo +if [[ ! $REPLY =~ ^[Yy]$ ]]; then + echo -e "${YELLOW}Undeployment cancelled${NC}" + exit 0 +fi + +# Delete services +echo -e "${YELLOW}Deleting Meeting Service...${NC}" +kubectl delete -f ${SCRIPT_DIR}/meeting-service.yaml --ignore-not-found=true +echo -e "${GREEN}✓ Meeting Service deleted${NC}" + +echo -e "${YELLOW}Deleting Notification Service...${NC}" +kubectl delete -f ${SCRIPT_DIR}/notification-service.yaml --ignore-not-found=true +echo -e "${GREEN}✓ Notification Service deleted${NC}" + +echo -e "${YELLOW}Deleting User Service...${NC}" +kubectl delete -f ${SCRIPT_DIR}/user-service.yaml --ignore-not-found=true +echo -e "${GREEN}✓ User Service deleted${NC}" + +# Delete ConfigMaps +echo -e "${YELLOW}Deleting ConfigMaps...${NC}" +kubectl delete -f ${SCRIPT_DIR}/configmap.yaml --ignore-not-found=true +echo -e "${GREEN}✓ ConfigMaps deleted${NC}" + +# Ask about secrets deletion +echo "" +echo -e "${YELLOW}Do you want to delete secrets as well?${NC}" +echo -e "${RED}Warning: This will delete all database and Azure credentials${NC}" +read -p "Delete secrets? (y/N): " -n 1 -r +echo +if [[ $REPLY =~ ^[Yy]$ ]]; then + kubectl delete secret db-secret azure-secret mail-secret -n ${NAMESPACE} --ignore-not-found=true + echo -e "${GREEN}✓ Secrets deleted${NC}" +fi + +# Ask about namespace deletion +echo "" +echo -e "${YELLOW}Do you want to delete the namespace '${NAMESPACE}'?${NC}" +read -p "Delete namespace? (y/N): " -n 1 -r +echo +if [[ $REPLY =~ ^[Yy]$ ]]; then + kubectl delete namespace ${NAMESPACE} + echo -e "${GREEN}✓ Namespace deleted${NC}" +fi + +echo -e "${GREEN}======================================${NC}" +echo -e "${GREEN}Undeployment Completed${NC}" +echo -e "${GREEN}======================================${NC}" diff --git a/deploy/k8s/backend/user-service.yaml b/deploy/k8s/backend/user-service.yaml new file mode 100644 index 0000000..d5f6267 --- /dev/null +++ b/deploy/k8s/backend/user-service.yaml @@ -0,0 +1,102 @@ +--- +# User Service Deployment +apiVersion: apps/v1 +kind: Deployment +metadata: + name: user-service + namespace: hgzero + labels: + app: user-service + tier: backend +spec: + replicas: 1 + selector: + matchLabels: + app: user-service + template: + metadata: + labels: + app: user-service + tier: backend + spec: + containers: + - name: user-service + image: acrdigitalgarage02.azurecr.io/hgzero/user-service:latest + imagePullPolicy: Always + ports: + - containerPort: 8080 + name: http + env: + - name: SPRING_PROFILES_ACTIVE + value: "prod" + - name: DB_HOST + valueFrom: + secretKeyRef: + name: db-secret + key: host + - name: DB_USERNAME + valueFrom: + secretKeyRef: + name: db-secret + key: username + - name: DB_PASSWORD + valueFrom: + secretKeyRef: + name: db-secret + key: password + - name: REDIS_HOST + valueFrom: + configMapKeyRef: + name: redis-config + key: host + - name: REDIS_PORT + valueFrom: + configMapKeyRef: + name: redis-config + key: port + - name: AZURE_EVENTHUB_CONNECTION_STRING + valueFrom: + secretKeyRef: + name: azure-secret + key: eventhub-connection-string + resources: + requests: + cpu: 256m + memory: 256Mi + limits: + cpu: 1024m + memory: 1024Mi + livenessProbe: + httpGet: + path: /actuator/health/liveness + port: 8080 + initialDelaySeconds: 60 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /actuator/health/readiness + port: 8080 + initialDelaySeconds: 30 + periodSeconds: 5 + timeoutSeconds: 3 + failureThreshold: 3 +--- +# User Service Service +apiVersion: v1 +kind: Service +metadata: + name: user-service + namespace: hgzero + labels: + app: user-service +spec: + type: ClusterIP + ports: + - port: 8080 + targetPort: 8080 + protocol: TCP + name: http + selector: + app: user-service