From 4e2182055b475159cfc89f7da9d80533ab521070 Mon Sep 17 00:00:00 2001 From: djeon Date: Wed, 29 Oct 2025 22:42:07 +0900 Subject: [PATCH] feat: add rag ci/cd --- .github/config/deploy_env_vars_rag_dev | 7 + .github/config/deploy_env_vars_rag_prod | 7 + .github/config/deploy_env_vars_rag_staging | 7 + .github/workflows/rag-cicd_ArgoCD.yaml | 221 +++++++++++++++++++++ deployment/container/Dockerfile-rag | 57 ++++++ 5 files changed, 299 insertions(+) create mode 100644 .github/config/deploy_env_vars_rag_dev create mode 100644 .github/config/deploy_env_vars_rag_prod create mode 100644 .github/config/deploy_env_vars_rag_staging create mode 100644 .github/workflows/rag-cicd_ArgoCD.yaml create mode 100644 deployment/container/Dockerfile-rag diff --git a/.github/config/deploy_env_vars_rag_dev b/.github/config/deploy_env_vars_rag_dev new file mode 100644 index 0000000..32a5d38 --- /dev/null +++ b/.github/config/deploy_env_vars_rag_dev @@ -0,0 +1,7 @@ +# Azure Resource Configuration +resource_group=rg-digitalgarage-02 +cluster_name=aks-digitalgarage-02 + +# RAG Service Configuration +python_version=3.11 +app_port=8088 diff --git a/.github/config/deploy_env_vars_rag_prod b/.github/config/deploy_env_vars_rag_prod new file mode 100644 index 0000000..d9d4519 --- /dev/null +++ b/.github/config/deploy_env_vars_rag_prod @@ -0,0 +1,7 @@ +# Azure Resource Configuration +resource_group=rg-digitalgarage-prod +cluster_name=aks-digitalgarage-prod + +# RAG Service Configuration +python_version=3.11 +app_port=8088 diff --git a/.github/config/deploy_env_vars_rag_staging b/.github/config/deploy_env_vars_rag_staging new file mode 100644 index 0000000..8f01907 --- /dev/null +++ b/.github/config/deploy_env_vars_rag_staging @@ -0,0 +1,7 @@ +# Azure Resource Configuration +resource_group=rg-digitalgarage-staging +cluster_name=aks-digitalgarage-staging + +# RAG Service Configuration +python_version=3.11 +app_port=8088 diff --git a/.github/workflows/rag-cicd_ArgoCD.yaml b/.github/workflows/rag-cicd_ArgoCD.yaml new file mode 100644 index 0000000..b5d7c5b --- /dev/null +++ b/.github/workflows/rag-cicd_ArgoCD.yaml @@ -0,0 +1,221 @@ +name: RAG Service CI/CD + +on: + push: + branches: [ main, develop ] + paths: + - 'rag/**' + - '.github/workflows/rag-cicd_ArgoCD.yaml' + pull_request: + branches: [ main ] + workflow_dispatch: + inputs: + ENVIRONMENT: + description: 'Target environment' + required: true + default: 'dev' + type: choice + options: + - dev + - staging + - prod + SKIP_TESTS: + description: 'Skip Tests' + required: false + default: 'false' + type: choice + options: + - 'true' + - 'false' + +env: + REGISTRY: acrdigitalgarage02.azurecr.io + IMAGE_ORG: hgzero + SERVICE_NAME: rag + 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 Python 3.11 + uses: actions/setup-python@v4 + with: + python-version: '3.11' + cache: 'pip' + cache-dependency-path: 'rag/requirements.txt' + + - name: Determine environment + id: determine_env + run: | + 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 }} + + REGISTRY="acrdigitalgarage02.azurecr.io" + IMAGE_ORG="hgzero" + RESOURCE_GROUP="rg-digitalgarage-02" + AKS_CLUSTER="aks-digitalgarage-02" + NAMESPACE="hgzero" + + if [[ -f ".github/config/deploy_env_vars_rag_${ENV}" ]]; then + while IFS= read -r line || [[ -n "$line" ]]; do + [[ "$line" =~ ^#.*$ ]] && continue + [[ -z "$line" ]] && continue + + key=$(echo "$line" | cut -d '=' -f1) + value=$(echo "$line" | cut -d '=' -f2-) + + case "$key" in + "resource_group") RESOURCE_GROUP="$value" ;; + "cluster_name") AKS_CLUSTER="$value" ;; + esac + done < ".github/config/deploy_env_vars_rag_${ENV}" + fi + + 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: Install dependencies + run: | + cd rag + python -m pip install --upgrade pip + pip install -r requirements.txt + + - name: Run Tests + env: + SKIP_TESTS: ${{ github.event.inputs.SKIP_TESTS || 'false' }} + run: | + if [[ "$SKIP_TESTS" == "true" ]]; then + echo "⏭️ Skipping Tests (SKIP_TESTS=$SKIP_TESTS)" + exit 0 + fi + + cd rag + # Run pytest with coverage + pytest tests/ --cov=src --cov-report=xml --cov-report=html + + echo "✅ Tests completed successfully" + + - name: Code Quality Check + run: | + cd rag + # Run linters + flake8 src/ --max-line-length=120 --exclude=__pycache__ + black --check src/ + mypy src/ --ignore-missing-imports + + - name: Upload test results + if: always() + uses: actions/upload-artifact@v4 + with: + name: test-results + path: | + rag/htmlcov/ + rag/coverage.xml + + - 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 Image + needs: build + runs-on: ubuntu-latest + + steps: + - name: Check out code + uses: actions/checkout@v4 + + - name: Set environment variables from build job + run: | + echo "REGISTRY=${{ env.REGISTRY }}" >> $GITHUB_ENV + echo "IMAGE_ORG=${{ env.IMAGE_ORG }}" >> $GITHUB_ENV + echo "SERVICE_NAME=${{ env.SERVICE_NAME }}" >> $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 image + run: | + echo "Building and pushing RAG service..." + docker build \ + -f deployment/container/Dockerfile-rag \ + -t ${{ env.REGISTRY }}/${{ env.IMAGE_ORG }}/${{ env.SERVICE_NAME }}:${{ needs.build.outputs.environment }}-${{ needs.build.outputs.image_tag }} \ + rag/ + + docker push ${{ env.REGISTRY }}/${{ env.IMAGE_ORG }}/${{ env.SERVICE_NAME }}:${{ needs.build.outputs.environment }}-${{ needs.build.outputs.image_tag }} + + echo "✅ Docker image pushed successfully" + + 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-rag/kustomize/overlays/${{ env.ENVIRONMENT }} + + # RAG 서비스 이미지 태그 업데이트 + kustomize edit set image acrdigitalgarage02.azurecr.io/hgzero/rag:${{ env.ENVIRONMENT }}-${{ env.IMAGE_TAG }} + + # Git 설정 및 푸시 + cd ../../../.. + git config user.name "GitHub Actions" + git config user.email "actions@github.com" + git add . + git commit -m "🚀 Update RAG ${{ env.ENVIRONMENT }} image to ${{ env.ENVIRONMENT }}-${{ env.IMAGE_TAG }}" + git push origin main + + echo "✅ 매니페스트 업데이트 완료. ArgoCD가 자동으로 배포합니다." diff --git a/deployment/container/Dockerfile-rag b/deployment/container/Dockerfile-rag new file mode 100644 index 0000000..cdd7965 --- /dev/null +++ b/deployment/container/Dockerfile-rag @@ -0,0 +1,57 @@ +# Build stage +FROM python:3.11-slim AS builder + +WORKDIR /app + +# Install system dependencies +RUN apt-get update && apt-get install -y \ + gcc \ + g++ \ + make \ + libpq-dev \ + && rm -rf /var/lib/apt/lists/* + +# Copy requirements and install dependencies +COPY requirements.txt . +RUN pip install --no-cache-dir --user -r requirements.txt + +# Run stage +FROM python:3.11-slim + +ENV USERNAME=k8s +ENV ARTIFACTORY_HOME=/home/${USERNAME} +ENV PYTHONUNBUFFERED=1 +ENV PYTHONDONTWRITEBYTECODE=1 + +# Install runtime dependencies +RUN apt-get update && apt-get install -y \ + libpq5 \ + && rm -rf /var/lib/apt/lists/* + +# Add a non-root user +RUN adduser --system --group ${USERNAME} && \ + mkdir -p ${ARTIFACTORY_HOME} && \ + chown ${USERNAME}:${USERNAME} ${ARTIFACTORY_HOME} + +WORKDIR ${ARTIFACTORY_HOME} + +# Copy Python dependencies from builder +COPY --from=builder /root/.local /home/${USERNAME}/.local + +# Copy application code +COPY --chown=${USERNAME}:${USERNAME} . . + +# Update PATH to include user's local bin +ENV PATH=/home/${USERNAME}/.local/bin:$PATH + +USER ${USERNAME} + +# Expose port +EXPOSE 8088 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \ + CMD python -c "import requests; requests.get('http://localhost:8088/health')" || exit 1 + +# Run the application +CMD ["uvicorn", "src.api.main:app", "--host", "0.0.0.0", "--port", "8088"] \ No newline at end of file