name: Backend Services CI/CD (Generic K8s) on: push: branches: [ main, develop ] paths: - 'api-gateway/**' - 'user-service/**' - 'bill-service/**' - 'product-service/**' - 'kos-mock/**' - 'common/**' - '.github/**' pull_request: branches: [ main ] env: # ============================================ # 변경: Azure ACR → Docker Hub # ============================================ REGISTRY: docker.io IMAGE_ORG: ${{ secrets.DOCKERHUB_USERNAME }} NAMESPACE: phonebill # SSH 터널링용 MINIKUBE_IP: "192.168.49.2" 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: | # workflow_dispatch 입력값 우선, 없으면 vars 사용 ENVIRONMENT="${{ vars.ENVIRONMENT || 'dev' }}" echo "environment=$ENVIRONMENT" >> $GITHUB_OUTPUT - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Build with Gradle run: | ./gradlew build -x test - name: SonarQube Analysis & Quality Gate if: ${{ vars.SKIP_SONARQUBE != 'true' }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }} run: | # Define services array services=(api-gateway user-service bill-service product-service kos-mock) # Run tests, coverage reports, and SonarQube analysis for each service for service in "${services[@]}"; do ./gradlew :$service:test :$service:jacocoTestReport :$service:sonar \ -Dsonar.projectKey=phonebill-$service-${{ steps.determine_env.outputs.environment }} \ -Dsonar.projectName=phonebill-$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: | api-gateway/build/libs/*.jar user-service/build/libs/*.jar bill-service/build/libs/*.jar product-service/build/libs/*.jar kos-mock/build/libs/*.jar - name: Set outputs id: set_outputs run: | 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 "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 # ============================================ # 변경: Docker Hub 로그인만 사용 # ============================================ - name: Login to Docker Hub uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_PASSWORD }} - name: Build and push Docker images for all services run: | # Define services array services=(api-gateway user-service bill-service product-service kos-mock) # 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 }}/${{ secrets.DOCKERHUB_USERNAME }}/$service:${{ needs.build.outputs.environment }}-${{ needs.build.outputs.image_tag }} \ -t ${{ env.REGISTRY }}/${{ secrets.DOCKERHUB_USERNAME }}/$service:${{ needs.build.outputs.environment }}-latest . docker push ${{ env.REGISTRY }}/${{ secrets.DOCKERHUB_USERNAME }}/$service:${{ needs.build.outputs.environment }}-${{ needs.build.outputs.image_tag }} docker push ${{ env.REGISTRY }}/${{ secrets.DOCKERHUB_USERNAME }}/$service:${{ needs.build.outputs.environment }}-latest done deploy: name: Deploy to Kubernetes needs: [build, release] runs-on: ubuntu-latest steps: - name: Check out code uses: actions/checkout@v4 - name: Set environment variables run: | echo "IMAGE_TAG=${{ needs.build.outputs.image_tag }}" >> $GITHUB_ENV echo "ENVIRONMENT=${{ needs.build.outputs.environment }}" >> $GITHUB_ENV # ============================================ # 변경: Azure CLI/Login 제거 → SSH 터널링 # ============================================ - name: Setup SSH key run: | mkdir -p ~/.ssh echo "${{ secrets.VM_SSH_KEY }}" > ~/.ssh/vm_key chmod 600 ~/.ssh/vm_key ssh-keyscan -H ${{ secrets.VM_IP }} >> ~/.ssh/known_hosts 2>/dev/null || true - name: Create SSH tunnel to Minikube run: | ssh -i ~/.ssh/vm_key \ -o StrictHostKeyChecking=no \ -o ServerAliveInterval=60 \ -L 8443:${{ env.MINIKUBE_IP }}:8443 \ ${{ secrets.VM_USER }}@${{ secrets.VM_IP }} -N & sleep 5 echo "✅ SSH tunnel established" # ============================================ # 변경: az aks get-credentials → KUBECONFIG Secret # ============================================ - name: Setup kubectl uses: azure/setup-kubectl@v3 - name: Configure kubectl via KUBECONFIG run: | mkdir -p $HOME/.kube echo "${{ secrets.KUBECONFIG }}" > $HOME/.kube/config chmod 600 $HOME/.kube/config # server 주소를 localhost:8443으로 변경 (SSH 터널 통해 접근) sed -i 's|server:.*|server: https://127.0.0.1:8443|g' $HOME/.kube/config - name: Verify cluster connection run: | kubectl cluster-info kubectl get nodes - name: Create namespace run: | kubectl create namespace ${{ env.NAMESPACE }} --dry-run=client -o yaml | kubectl apply -f - # ============================================ # 추가: Docker Hub pull secret 생성 # ============================================ - name: Create image pull secret run: | kubectl create secret docker-registry dockerhub-secret \ --docker-server=docker.io \ --docker-username=${{ secrets.DOCKERHUB_USERNAME }} \ --docker-password=${{ secrets.DOCKERHUB_PASSWORD }} \ --namespace=${{ env.NAMESPACE }} \ --dry-run=client -o yaml | kubectl apply -f - - name: Install Kustomize run: | curl -s "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" | bash sudo mv kustomize /usr/local/bin/ - name: Update Kustomize images and deploy run: | cd .github/kustomize/overlays/${{ env.ENVIRONMENT }} # ============================================ # 변경: 이미지 경로를 Docker Hub로 # ============================================ kustomize edit set image ${{ env.REGISTRY }}/${{ secrets.DOCKERHUB_USERNAME }}/api-gateway:${{ env.ENVIRONMENT }}-${{ env.IMAGE_TAG }} kustomize edit set image ${{ env.REGISTRY }}/${{ secrets.DOCKERHUB_USERNAME }}/user-service:${{ env.ENVIRONMENT }}-${{ env.IMAGE_TAG }} kustomize edit set image ${{ env.REGISTRY }}/${{ secrets.DOCKERHUB_USERNAME }}/bill-service:${{ env.ENVIRONMENT }}-${{ env.IMAGE_TAG }} kustomize edit set image ${{ env.REGISTRY }}/${{ secrets.DOCKERHUB_USERNAME }}/product-service:${{ env.ENVIRONMENT }}-${{ env.IMAGE_TAG }} kustomize edit set image ${{ env.REGISTRY }}/${{ secrets.DOCKERHUB_USERNAME }}/kos-mock:${{ env.ENVIRONMENT }}-${{ env.IMAGE_TAG }} kubectl apply -k . - name: Wait for deployments to be ready run: | echo "Waiting for deployments to be ready..." kubectl -n ${{ env.NAMESPACE }} wait --for=condition=available deployment/api-gateway --timeout=300s kubectl -n ${{ env.NAMESPACE }} wait --for=condition=available deployment/user-service --timeout=300s kubectl -n ${{ env.NAMESPACE }} wait --for=condition=available deployment/bill-service --timeout=300s kubectl -n ${{ env.NAMESPACE }} wait --for=condition=available deployment/product-service --timeout=300s kubectl -n ${{ env.NAMESPACE }} wait --for=condition=available deployment/kos-mock --timeout=300s - name: Show deployment status run: | kubectl -n ${{ env.NAMESPACE }} get pods -o wide kubectl -n ${{ env.NAMESPACE }} get svc # ============================================ # 추가: SSH 터널 정리 # ============================================ - name: Cleanup SSH tunnel if: always() run: | pkill -f "ssh.*8443" || true