# Gitea Actions Workflow - vexplor 자동 배포 # # 환경 변수: # - GITEA_DOMAIN: g.wace.me # - HARBOR_REGISTRY: harbor.wace.me # - K8S_NAMESPACE: vexplor # # 필수 Secrets (Repository Settings > Secrets): # - HARBOR_USERNAME: Harbor 사용자명 # - HARBOR_PASSWORD: Harbor 비밀번호 # - K8S_SSH_KEY: base64로 인코딩된 SSH 비밀키 (쿠버네티스 서버 접속용) # # Application Secrets: # - k8s/vexplor-secret.yaml 파일에서 관리 name: Deploy vexplor on: push: branches: - main - master paths: - "backend-node/**" - "frontend/**" - "docker/**" - "k8s/**" - ".gitea/workflows/deploy.yml" workflow_dispatch: env: GITEA_DOMAIN: g.wace.me HARBOR_REGISTRY: localhost:5001 HARBOR_REGISTRY_K8S: harbor.wace.me HARBOR_REGISTRY_EXTERNAL: harbor.wace.me HARBOR_PROJECT: speefox_vexplor K8S_NAMESPACE: vexplor # 쿠버네티스 서버 SSH 접속 정보 K8S_SSH_HOST: 112.168.212.142 K8S_SSH_PORT: 22 K8S_SSH_USER: wace # Frontend 빌드 환경 변수 NEXT_PUBLIC_API_URL: "https://api.vexplor.com/api" NEXT_PUBLIC_ENV: "production" INTERNAL_API_URL: "http://vexplor-backend-service:3001" # Frontend 설정 FRONTEND_IMAGE_NAME: vexplor-frontend FRONTEND_DEPLOYMENT_NAME: vexplor-frontend FRONTEND_CONTAINER_NAME: vexplor-frontend FRONTEND_BUILD_CONTEXT: frontend FRONTEND_DOCKERFILE_PATH: docker/deploy/frontend.Dockerfile # Backend 설정 BACKEND_IMAGE_NAME: vexplor-backend BACKEND_DEPLOYMENT_NAME: vexplor-backend BACKEND_CONTAINER_NAME: vexplor-backend BACKEND_BUILD_CONTEXT: backend-node BACKEND_DOCKERFILE_PATH: docker/deploy/backend.Dockerfile jobs: build-and-deploy: runs-on: ubuntu-24.04 steps: # 작업 디렉토리 정리 - name: Clean workspace run: | echo "작업 디렉토리 정리..." cd /workspace rm -rf source mkdir -p source echo "정리 완료" # 필수 도구 설치 - name: Install required tools run: | echo "필수 도구 설치 중..." apt-get update -qq apt-get install -y git curl ca-certificates gnupg openssh-client # Docker 클라이언트 설치 install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc chmod a+r /etc/apt/keyrings/docker.asc echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \ $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null apt-get update -qq apt-get install -y docker-ce-cli echo "설치 완료:" git --version ssh -V docker --version export DOCKER_HOST=unix:///var/run/docker.sock docker version || echo "소켓 연결 대기 중..." # 저장소 체크아웃 - name: Checkout code run: | echo "저장소 체크아웃..." cd /workspace/source git clone --depth 1 --branch ${GITHUB_REF_NAME} \ https://oauth2:${{ github.token }}@${GITEA_DOMAIN}/${GITHUB_REPOSITORY}.git . echo "체크아웃 완료" git log -1 --oneline # 빌드 환경 설정 - name: Set up build environment run: | IMAGE_TAG="v$(date +%Y%m%d-%H%M%S)-${GITHUB_SHA::7}" echo "IMAGE_TAG=${IMAGE_TAG}" >> $GITHUB_ENV # Frontend 이미지 echo "FRONTEND_FULL_IMAGE=${HARBOR_REGISTRY}/${HARBOR_PROJECT}/${FRONTEND_IMAGE_NAME}" >> $GITHUB_ENV echo "FRONTEND_FULL_IMAGE_K8S=${HARBOR_REGISTRY_K8S}/${HARBOR_PROJECT}/${FRONTEND_IMAGE_NAME}" >> $GITHUB_ENV # Backend 이미지 echo "BACKEND_FULL_IMAGE=${HARBOR_REGISTRY}/${HARBOR_PROJECT}/${BACKEND_IMAGE_NAME}" >> $GITHUB_ENV echo "BACKEND_FULL_IMAGE_K8S=${HARBOR_REGISTRY_K8S}/${HARBOR_PROJECT}/${BACKEND_IMAGE_NAME}" >> $GITHUB_ENV echo "빌드 태그: ${IMAGE_TAG}" # Harbor 로그인 - name: Login to Harbor env: HARBOR_USER: ${{ secrets.HARBOR_USERNAME }} HARBOR_PASS: ${{ secrets.HARBOR_PASSWORD }} run: | echo "Harbor 로그인..." export DOCKER_HOST=unix:///var/run/docker.sock echo "${HARBOR_PASS}" | docker login ${HARBOR_REGISTRY} \ --username ${HARBOR_USER} \ --password-stdin echo "Harbor 로그인 완료" # Backend 빌드 및 푸시 - name: Build and Push Backend image run: | echo "Backend 이미지 빌드 및 푸시..." export DOCKER_HOST=unix:///var/run/docker.sock cd /workspace/source docker build \ -t ${BACKEND_FULL_IMAGE}:${IMAGE_TAG} \ -t ${BACKEND_FULL_IMAGE}:latest \ -f ${BACKEND_DOCKERFILE_PATH} \ ${BACKEND_BUILD_CONTEXT} docker push ${BACKEND_FULL_IMAGE}:${IMAGE_TAG} docker push ${BACKEND_FULL_IMAGE}:latest echo "Backend 푸시 완료" # Frontend 빌드 및 푸시 - name: Build and Push Frontend image run: | echo "Frontend 이미지 빌드 및 푸시..." export DOCKER_HOST=unix:///var/run/docker.sock cd /workspace/source echo "빌드 환경 변수:" echo " NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL}" echo " NEXT_PUBLIC_ENV=${NEXT_PUBLIC_ENV}" docker build \ -t ${FRONTEND_FULL_IMAGE}:${IMAGE_TAG} \ -t ${FRONTEND_FULL_IMAGE}:latest \ -f ${FRONTEND_DOCKERFILE_PATH} \ --build-arg NEXT_PUBLIC_API_URL="${NEXT_PUBLIC_API_URL}" \ ${FRONTEND_BUILD_CONTEXT} docker push ${FRONTEND_FULL_IMAGE}:${IMAGE_TAG} docker push ${FRONTEND_FULL_IMAGE}:latest echo "Frontend 푸시 완료" # SSH 키 설정 (쿠버네티스 서버 접속용) - name: Setup SSH Key env: SSH_KEY_CONTENT: ${{ secrets.K8S_SSH_KEY }} run: | echo "SSH 키 설정..." if [ -z "${SSH_KEY_CONTENT}" ]; then echo "K8S_SSH_KEY secret이 설정되지 않았습니다!" exit 1 fi mkdir -p ~/.ssh echo "${SSH_KEY_CONTENT}" | base64 -d > ~/.ssh/id_rsa chmod 600 ~/.ssh/id_rsa # known_hosts에 쿠버네티스 서버 추가 ssh-keyscan -p ${K8S_SSH_PORT} ${K8S_SSH_HOST} >> ~/.ssh/known_hosts 2>/dev/null # SSH 연결 테스트 echo "SSH 연결 테스트..." ssh -o StrictHostKeyChecking=no -p ${K8S_SSH_PORT} ${K8S_SSH_USER}@${K8S_SSH_HOST} "echo 'SSH 연결 성공'" echo "SSH 키 설정 완료" # k8s 매니페스트 파일을 쿠버네티스 서버로 전송 - name: Transfer k8s manifests run: | echo "k8s 매니페스트 파일 전송..." cd /workspace/source # 쿠버네티스 서버에 디렉토리 생성 ssh -p ${K8S_SSH_PORT} ${K8S_SSH_USER}@${K8S_SSH_HOST} "mkdir -p ~/vexplor-deploy/k8s" # k8s 파일 전송 scp -P ${K8S_SSH_PORT} -r k8s/* ${K8S_SSH_USER}@${K8S_SSH_HOST}:~/vexplor-deploy/k8s/ echo "매니페스트 파일 전송 완료" # Kubernetes 배포 (SSH를 통해 원격 실행) - name: Deploy to Kubernetes env: HARBOR_USER: ${{ secrets.HARBOR_USERNAME }} HARBOR_PASS: ${{ secrets.HARBOR_PASSWORD }} run: | echo "Kubernetes 배포 시작 (SSH 원격 실행)..." # SSH를 통해 쿠버네티스 서버에서 kubectl 명령 실행 ssh -p ${K8S_SSH_PORT} ${K8S_SSH_USER}@${K8S_SSH_HOST} << 'DEPLOY_SCRIPT' set -e cd ~/vexplor-deploy echo "네임스페이스 확인..." kubectl apply -f k8s/namespace.yaml echo "ConfigMap 적용..." kubectl apply -f k8s/vexplor-config.yaml -n vexplor # Secret 적용 (존재하는 경우에만) if [ -f k8s/vexplor-secret.yaml ]; then echo "Secret 적용..." kubectl apply -f k8s/vexplor-secret.yaml -n vexplor fi echo "네임스페이스 및 ConfigMap 적용 완료" DEPLOY_SCRIPT # Harbor Registry Secret 생성 (별도로 처리 - 환경변수 사용) echo "Harbor Registry Secret 확인..." ssh -p ${K8S_SSH_PORT} ${K8S_SSH_USER}@${K8S_SSH_HOST} "kubectl get secret harbor-registry -n vexplor" > /dev/null 2>&1 || \ ssh -p ${K8S_SSH_PORT} ${K8S_SSH_USER}@${K8S_SSH_HOST} "kubectl create secret docker-registry harbor-registry \ --docker-server=${HARBOR_REGISTRY_K8S} \ --docker-username=${HARBOR_USER} \ --docker-password='${HARBOR_PASS}' \ -n vexplor" echo "Harbor Registry Secret 확인 완료" # Backend 배포 echo "Backend 배포..." ssh -p ${K8S_SSH_PORT} ${K8S_SSH_USER}@${K8S_SSH_HOST} << BACKEND_DEPLOY set -e cd ~/vexplor-deploy kubectl apply -f k8s/vexplor-backend-deployment.yaml -n vexplor echo "Backend 이미지 업데이트..." kubectl set image deployment/${BACKEND_DEPLOYMENT_NAME} \ ${BACKEND_CONTAINER_NAME}=${HARBOR_REGISTRY_K8S}/${HARBOR_PROJECT}/${BACKEND_IMAGE_NAME}:latest \ -n vexplor || true kubectl rollout restart deployment/${BACKEND_DEPLOYMENT_NAME} -n vexplor echo "Backend Rolling Update 진행 중..." kubectl rollout status deployment/${BACKEND_DEPLOYMENT_NAME} -n vexplor --timeout=5m echo "Backend 배포 완료" BACKEND_DEPLOY # Frontend 배포 echo "Frontend 배포..." ssh -p ${K8S_SSH_PORT} ${K8S_SSH_USER}@${K8S_SSH_HOST} << FRONTEND_DEPLOY set -e cd ~/vexplor-deploy kubectl apply -f k8s/vexplor-frontend-deployment.yaml -n vexplor echo "Frontend 이미지 업데이트..." kubectl set image deployment/${FRONTEND_DEPLOYMENT_NAME} \ ${FRONTEND_CONTAINER_NAME}=${HARBOR_REGISTRY_K8S}/${HARBOR_PROJECT}/${FRONTEND_IMAGE_NAME}:latest \ -n vexplor || true kubectl rollout restart deployment/${FRONTEND_DEPLOYMENT_NAME} -n vexplor echo "Frontend Rolling Update 진행 중..." kubectl rollout status deployment/${FRONTEND_DEPLOYMENT_NAME} -n vexplor --timeout=5m echo "Frontend 배포 완료" FRONTEND_DEPLOY # Ingress 배포 echo "Ingress 배포..." ssh -p ${K8S_SSH_PORT} ${K8S_SSH_USER}@${K8S_SSH_HOST} "cd ~/vexplor-deploy && kubectl apply -f k8s/vexplor-ingress.yaml -n vexplor" echo "전체 배포 완료!" # 배포 검증 - name: Verify deployment run: | echo "배포 검증..." ssh -p ${K8S_SSH_PORT} ${K8S_SSH_USER}@${K8S_SSH_HOST} << 'VERIFY_SCRIPT' echo "" echo "Backend 상태:" kubectl get deployment vexplor-backend -n vexplor kubectl get pods -l app=vexplor-backend -n vexplor echo "" echo "Frontend 상태:" kubectl get deployment vexplor-frontend -n vexplor kubectl get pods -l app=vexplor-frontend -n vexplor echo "" echo "Services:" kubectl get svc -n vexplor echo "" echo "Ingress:" kubectl get ingress -n vexplor echo "" echo "검증 완료" VERIFY_SCRIPT # 배포 요약 - name: Deployment summary if: success() run: | echo "==========================================" echo "배포가 성공적으로 완료되었습니다!" echo "==========================================" echo "빌드 버전: ${IMAGE_TAG}" echo "Frontend: https://v1.vexplor.com" echo "Backend API: https://api.vexplor.com" echo "==========================================" # 실패 시 롤백 - name: Rollback on failure if: failure() run: | echo "배포 실패! 이전 버전으로 롤백..." ssh -p ${K8S_SSH_PORT} ${K8S_SSH_USER}@${K8S_SSH_HOST} "kubectl rollout undo deployment/vexplor-backend -n vexplor" || true ssh -p ${K8S_SSH_PORT} ${K8S_SSH_USER}@${K8S_SSH_HOST} "kubectl rollout undo deployment/vexplor-frontend -n vexplor" || true # Harbor 로그아웃 - name: Logout from Harbor if: always() run: | export DOCKER_HOST=unix:///var/run/docker.sock docker logout ${HARBOR_REGISTRY} || true