Files
vexplor/.gitea/workflows/deploy.yml
Johngreen 15265ebfc9 Refactor deploy workflow to use SSH for k8s operations
Replaces direct kubectl usage with SSH-based remote execution for Kubernetes deployment steps. Updates secrets and environment variables to use SSH key and connection info, and modifies manifest transfer and deployment verification to operate over SSH. This improves security and flexibility for remote Kubernetes server management.
2025-12-22 17:39:26 +09:00

351 lines
13 KiB
YAML

# 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