From 15265ebfc9eec6c7a2b9fc4efacfab919bed2481 Mon Sep 17 00:00:00 2001 From: Johngreen Date: Mon, 22 Dec 2025 17:39:26 +0900 Subject: [PATCH] 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. --- .gitea/workflows/deploy.yml | 165 ++++++++++++++++++++---------------- 1 file changed, 94 insertions(+), 71 deletions(-) diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index e307079e..ec48126e 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -8,7 +8,7 @@ # 필수 Secrets (Repository Settings > Secrets): # - HARBOR_USERNAME: Harbor 사용자명 # - HARBOR_PASSWORD: Harbor 비밀번호 -# - KUBECONFIG: base64로 인코딩된 Kubernetes config +# - K8S_SSH_KEY: base64로 인코딩된 SSH 비밀키 (쿠버네티스 서버 접속용) # # Application Secrets: # - k8s/vexplor-secret.yaml 파일에서 관리 @@ -31,10 +31,15 @@ on: env: GITEA_DOMAIN: g.wace.me HARBOR_REGISTRY: localhost:5001 - HARBOR_REGISTRY_K8S: 192.168.1.100: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" @@ -74,12 +79,7 @@ jobs: run: | echo "필수 도구 설치 중..." apt-get update -qq - apt-get install -y git curl ca-certificates gnupg - - # kubectl 설치 - curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" - chmod +x kubectl - mv kubectl /usr/local/bin/ + apt-get install -y git curl ca-certificates gnupg openssh-client # Docker 클라이언트 설치 install -m 0755 -d /etc/apt/keyrings @@ -94,7 +94,7 @@ jobs: echo "설치 완료:" git --version - kubectl version --client + ssh -V docker --version export DOCKER_HOST=unix:///var/run/docker.sock @@ -180,100 +180,121 @@ jobs: docker push ${FRONTEND_FULL_IMAGE}:latest echo "Frontend 푸시 완료" - # Kubernetes 설정 - - name: Setup Kubernetes config + # SSH 키 설정 (쿠버네티스 서버 접속용) + - name: Setup SSH Key env: - KUBECONFIG_CONTENT: ${{ secrets.KUBECONFIG }} + SSH_KEY_CONTENT: ${{ secrets.K8S_SSH_KEY }} run: | - echo "Kubernetes 설정..." + echo "SSH 키 설정..." - if [ -z "${KUBECONFIG_CONTENT}" ]; then - echo "KUBECONFIG secret이 설정되지 않았습니다!" + if [ -z "${SSH_KEY_CONTENT}" ]; then + echo "K8S_SSH_KEY secret이 설정되지 않았습니다!" exit 1 fi - mkdir -p ~/.kube - echo "${KUBECONFIG_CONTENT}" | base64 -d > ~/.kube/config - chmod 600 ~/.kube/config + mkdir -p ~/.ssh + echo "${SSH_KEY_CONTENT}" | base64 -d > ~/.ssh/id_rsa + chmod 600 ~/.ssh/id_rsa - if [ ! -s ~/.kube/config ]; then - echo "kubeconfig 파일이 비어있습니다" - exit 1 - fi + # known_hosts에 쿠버네티스 서버 추가 + ssh-keyscan -p ${K8S_SSH_PORT} ${K8S_SSH_HOST} >> ~/.ssh/known_hosts 2>/dev/null - echo "kubeconfig 파일 생성 완료" - kubectl cluster-info > /dev/null 2>&1 && echo "Kubernetes 클러스터 연결 성공" + # SSH 연결 테스트 + echo "SSH 연결 테스트..." + ssh -o StrictHostKeyChecking=no -p ${K8S_SSH_PORT} ${K8S_SSH_USER}@${K8S_SSH_HOST} "echo 'SSH 연결 성공'" + echo "SSH 키 설정 완료" - # Kubernetes 배포 - - name: Deploy to Kubernetes + # k8s 매니페스트 파일을 쿠버네티스 서버로 전송 + - name: Transfer k8s manifests run: | - echo "Kubernetes 배포 시작..." - + 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 - # ConfigMap 적용 echo "ConfigMap 적용..." - kubectl apply -f k8s/vexplor-config.yaml -n ${K8S_NAMESPACE} + 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 ${K8S_NAMESPACE} + kubectl apply -f k8s/vexplor-secret.yaml -n vexplor fi - # Harbor Registry Secret 생성 (없을 때만) + echo "네임스페이스 및 ConfigMap 적용 완료" + DEPLOY_SCRIPT + + # Harbor Registry Secret 생성 (별도로 처리 - 환경변수 사용) echo "Harbor Registry Secret 확인..." - if ! kubectl get secret harbor-registry -n ${K8S_NAMESPACE} > /dev/null 2>&1; then - echo "Harbor Registry Secret 생성 중..." - kubectl create secret docker-registry harbor-registry \ - --docker-server=${HARBOR_REGISTRY_K8S} \ - --docker-username=${{ secrets.HARBOR_USERNAME }} \ - --docker-password=${{ secrets.HARBOR_PASSWORD }} \ - -n ${K8S_NAMESPACE} - echo "Harbor Registry Secret 생성 완료" - else - echo "Harbor Registry Secret 이미 존재" - fi + 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 배포..." - kubectl apply -f k8s/vexplor-backend-deployment.yaml -n ${K8S_NAMESPACE} + 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 - if kubectl get deployment ${BACKEND_DEPLOYMENT_NAME} -n ${K8S_NAMESPACE} > /dev/null 2>&1; then - echo "Backend 이미지 업데이트..." - kubectl set image deployment/${BACKEND_DEPLOYMENT_NAME} \ - ${BACKEND_CONTAINER_NAME}=${BACKEND_FULL_IMAGE_K8S}:latest \ - -n ${K8S_NAMESPACE} - kubectl rollout restart deployment/${BACKEND_DEPLOYMENT_NAME} -n ${K8S_NAMESPACE} - fi + 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 ${K8S_NAMESPACE} --timeout=5m + kubectl rollout status deployment/${BACKEND_DEPLOYMENT_NAME} -n vexplor --timeout=5m echo "Backend 배포 완료" + BACKEND_DEPLOY # Frontend 배포 echo "Frontend 배포..." - kubectl apply -f k8s/vexplor-frontend-deployment.yaml -n ${K8S_NAMESPACE} + 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 - if kubectl get deployment ${FRONTEND_DEPLOYMENT_NAME} -n ${K8S_NAMESPACE} > /dev/null 2>&1; then - echo "Frontend 이미지 업데이트..." - kubectl set image deployment/${FRONTEND_DEPLOYMENT_NAME} \ - ${FRONTEND_CONTAINER_NAME}=${FRONTEND_FULL_IMAGE_K8S}:latest \ - -n ${K8S_NAMESPACE} - kubectl rollout restart deployment/${FRONTEND_DEPLOYMENT_NAME} -n ${K8S_NAMESPACE} - fi + 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 ${K8S_NAMESPACE} --timeout=5m + kubectl rollout status deployment/${FRONTEND_DEPLOYMENT_NAME} -n vexplor --timeout=5m echo "Frontend 배포 완료" + FRONTEND_DEPLOY # Ingress 배포 echo "Ingress 배포..." - kubectl apply -f k8s/vexplor-ingress.yaml -n ${K8S_NAMESPACE} + ssh -p ${K8S_SSH_PORT} ${K8S_SSH_USER}@${K8S_SSH_HOST} "cd ~/vexplor-deploy && kubectl apply -f k8s/vexplor-ingress.yaml -n vexplor" echo "전체 배포 완료!" @@ -281,22 +302,24 @@ jobs: - name: Verify deployment run: | echo "배포 검증..." + ssh -p ${K8S_SSH_PORT} ${K8S_SSH_USER}@${K8S_SSH_HOST} << 'VERIFY_SCRIPT' echo "" echo "Backend 상태:" - kubectl get deployment ${BACKEND_DEPLOYMENT_NAME} -n ${K8S_NAMESPACE} - kubectl get pods -l app=${BACKEND_DEPLOYMENT_NAME} -n ${K8S_NAMESPACE} + kubectl get deployment vexplor-backend -n vexplor + kubectl get pods -l app=vexplor-backend -n vexplor echo "" echo "Frontend 상태:" - kubectl get deployment ${FRONTEND_DEPLOYMENT_NAME} -n ${K8S_NAMESPACE} - kubectl get pods -l app=${FRONTEND_DEPLOYMENT_NAME} -n ${K8S_NAMESPACE} + kubectl get deployment vexplor-frontend -n vexplor + kubectl get pods -l app=vexplor-frontend -n vexplor echo "" echo "Services:" - kubectl get svc -n ${K8S_NAMESPACE} + kubectl get svc -n vexplor echo "" echo "Ingress:" - kubectl get ingress -n ${K8S_NAMESPACE} + kubectl get ingress -n vexplor echo "" echo "검증 완료" + VERIFY_SCRIPT # 배포 요약 - name: Deployment summary @@ -315,8 +338,8 @@ jobs: if: failure() run: | echo "배포 실패! 이전 버전으로 롤백..." - kubectl rollout undo deployment/${BACKEND_DEPLOYMENT_NAME} -n ${K8S_NAMESPACE} || true - kubectl rollout undo deployment/${FRONTEND_DEPLOYMENT_NAME} -n ${K8S_NAMESPACE} || true + 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 -- 2.49.1