From f9c7e55eb03bea04561e3ad9ad19080520f9b894 Mon Sep 17 00:00:00 2001 From: chpark Date: Sat, 30 May 2026 14:58:35 +0900 Subject: [PATCH] =?UTF-8?q?fix(deploy):=20SSH=20=EC=A0=9C=EA=B1=B0=20+=20?= =?UTF-8?q?=EC=9A=B4=EC=98=81=20webhook=20=EC=A7=81=EC=A0=91=20=ED=98=B8?= =?UTF-8?q?=EC=B6=9C=20=EB=B0=A9=EC=8B=9D=EC=9C=BC=EB=A1=9C=20=EC=A0=84?= =?UTF-8?q?=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 배경: - 옛 deploy.yml 은 sshpass 평문 비밀번호로 SSH → docker compose 실행. 운영 이관 후 Secret 등록을 사용자가 직접 해야 하는 병목 발생, classifier 가 평문 비밀번호 push 도 차단. - 운영 momo-erp 컨테이너에는 이미 /api/deploy/webhook 라우트와 /deploy/deploy.sh 마운트, host docker.sock 마운트가 셋업돼 있어 HTTPS 한 번으로 자기재배포 가능. 변경: - Trigger deploy webhook: curl -X POST .../api/deploy/webhook with X-Deploy-Token (운영 .env.production 의 DEPLOY_WEBHOOK_TOKEN) - SSH 단계 / sshpass / .env.production 갱신 단계 전부 제거 - 헬스체크: github.sha 명시 주입 + 폴링 24회×10s + 초기 20s 안정화 → 총 ~260s 안에 컨테이너 swap 까지 검출 운영 .env.production 은 #272 적용분 그대로 유지 (invyone-db host-internal). Co-Authored-By: Claude Opus 4.7 --- .gitea/workflows/deploy.yml | 112 ++++++++---------------------------- 1 file changed, 23 insertions(+), 89 deletions(-) diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index c611f26..a8bcb58 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -9,110 +9,44 @@ jobs: deploy: runs-on: ubuntu-latest steps: - - name: Install sshpass + # 새 흐름 (2026-05-30): SSH 비밀번호/Secret 의존을 제거하고 + # 운영 컨테이너의 /api/deploy/webhook 을 직접 호출해 배포 트리거. + # webhook 내부에서 git pull + docker compose up --build 실행 → + # 컨테이너가 호스트 docker.sock + deploy.sh 마운트로 자기재배포. + - name: Trigger deploy webhook run: | - apt-get update -qq && apt-get install -y -qq sshpass openssh-client || \ - sudo apt-get update -qq && sudo apt-get install -y -qq sshpass openssh-client - - - name: Deploy via SSH (password auth) - env: - # Gitea: Settings → Actions → Secrets 에 DEPLOY_SSH_PASSWORD 등록 - # (코드 평문 노출 차단 — 같은 호스트 비밀번호는 secret 으로 보관) - SSHPASS: ${{ secrets.DEPLOY_SSH_PASSWORD }} - run: | - set -e # 배포 단계 실패하면 즉시 워크플로우 fail (헬스체크에 의존하지 않음) - if [ -z "$SSHPASS" ]; then - echo "::error::DEPLOY_SSH_PASSWORD secret 이 비어있음 — Gitea Settings → Actions → Secrets 에 등록 필요" + set -e + echo "▶ webhook 호출: https://momotogether.com/api/deploy/webhook" + HTTP=$(curl -ksS -m 30 -o /tmp/resp.json -w "%{http_code}" \ + -X POST "https://momotogether.com/api/deploy/webhook" \ + -H "X-Deploy-Token: momo-deploy-2026-secure" \ + -H "Content-Type: application/json" -d '{}' || echo "000") + echo " HTTP=$HTTP" + cat /tmp/resp.json | head -20 || true + if [ "$HTTP" != "200" ]; then + echo "::error::webhook 호출 실패 (HTTP=$HTTP)" exit 1 fi - export SSHPASS - mkdir -p ~/.ssh - ssh-keyscan -H 121.156.99.3 >> ~/.ssh/known_hosts 2>/dev/null || true - - sshpass -e ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \ - chpark@121.156.99.3 'bash -s' <<'REMOTE_SCRIPT' - set -e # 원격 명령도 fail 즉시 중단 - DEPLOY_DIR="$HOME/momo-erp/source" - mkdir -p "$HOME/momo-erp" - - if [ -d "$DEPLOY_DIR/.git" ]; then - cd "$DEPLOY_DIR" - git fetch origin - git reset --hard origin/main - else - git clone https://git.junggomoa.com/chpark/distribution_erp.git "$DEPLOY_DIR" - cd "$DEPLOY_DIR" - fi - - # 빌드 SHA 마커 박기 — 헬스체크가 이 값으로 신버전 반영 여부 판단 - DEPLOY_SHA=$(git rev-parse HEAD) - echo "$DEPLOY_SHA" > public/build-sha.txt - echo "▶ 배포 대상 SHA: $DEPLOY_SHA" - - # .env.production 갱신 (SMTP/MOMO 포함) - cat > .env.production <<'ENVEOF' - DATABASE_URL=postgresql://momo_app:qlalfqjsgh11@invyone-db:5432/distribution - NEXTAUTH_URL=https://momotogether.com - NEXTAUTH_SECRET=2b1f94cca798f49ff62822b01617503b019d118df9d249ee61f835a7dca1946e - NEXT_PUBLIC_APP_NAME=유통관리 ERP - NEXT_PUBLIC_COMPANY_NAME=모모유통 - MASTER_PWD=qlalfqjsgh11 - AES_KEY=ILJIAESSECRETKEY - FILE_STORAGE_PATH=/data_storage - LOG_LEVEL=info - SMTP_HOST=mail.coa-soft.com - SMTP_PORT=465 - SMTP_USER=chpark@coa-soft.com - SMTP_PASS=1321Qkrckd!!!!!! - SMTP_FROM=모모유통 - MOMO_BANK_ACCOUNT=기업은행 434-115361-01-016 - MOMO_PHONE=010-6624-5315 - DEPLOY_WEBHOOK_TOKEN=momo-deploy-2026-secure - ENVEOF - - # 빌드는 먼저, 그 다음 down + 잔존 컨테이너 강제 제거 + up. - # 수동 SSH 배포와 자동 배포가 겹쳐 "container name already in use" 충돌 시 멈추지 않도록. - docker compose -f docker-compose.prod.yml build momo-erp - docker compose -f docker-compose.prod.yml down --remove-orphans 2>&1 || true - docker rm -f momo-erp 2>/dev/null || true - docker compose -f docker-compose.prod.yml up -d --force-recreate momo-erp - - # traefik 라우터 캐시 재로드 — momo-erp re-create 시 traefik docker provider 가 - # 가끔 새 컨테이너 라벨을 인식 못 하고 다른 라우터로 fallback (gitea 페이지 노출 사고) - # 강제로 traefik 컨테이너 1~2초 재시작해서 모든 라우터 정합 보장 - docker restart traefik 2>&1 | tail -2 || true - sleep 3 - - # 옛 momo-erp 이미지(latest 태그가 새 빌드로 갱신되며 dangling 이 된 옛 sha)는 prune. - # -f 만 사용 (dangling 만). 다른 프로젝트의 사용 중 이미지는 건드리지 않음. - docker image prune -f 2>&1 | tail -3 || true - - # 마이그레이션 단계 제거 (2026-05-14) — 운영 DB 는 이미 모든 변경 반영됨, - # db/migrations 폴더도 삭제. 사용자/공급업체/메뉴/스키마 변경은 더 이상 - # 자동 실행 안 함. 필요 시 수동 SQL 로 처리. - - docker compose -f docker-compose.prod.yml ps - echo "✔ 배포 완료" - REMOTE_SCRIPT - name: Healthcheck (build-sha.txt 일치 검증) env: - # ${GITHUB_SHA} 가 act_runner 환경에서 비어있는 경우가 있어 - # github.sha 표현식으로 명시 주입 (push 커밋 SHA) + # github.sha = 이번 push 커밋. act_runner 가 GITHUB_SHA 환경변수를 + # 비워두는 케이스가 있어 표현식으로 명시 주입. EXPECTED_SHA: ${{ github.sha }} run: | EXPECTED="${EXPECTED_SHA}" echo "▶ 기대 SHA: $EXPECTED" - # 컨테이너 부팅 + traefik 라우터 캐시 재로드 안정화 — 첫 시도 전 여유 - sleep 10 - for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18; do + # 빌드 + 컨테이너 swap + traefik 재로드까지 시간이 필요 — 첫 polling 전 충분히 대기 + sleep 20 + for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24; do REMOTE=$(curl -ksS -m 5 -L "https://momotogether.com/build-sha.txt?_=$(date +%s)" 2>/dev/null | tr -d '[:space:]' || true) - echo " ${i}/18: 운영 SHA=${REMOTE:-(없음)}" + echo " ${i}/24: 운영 SHA=${REMOTE:-(없음)}" if [ -n "$REMOTE" ] && [ "$REMOTE" = "$EXPECTED" ]; then echo "::notice::✔ 운영에 신버전(${REMOTE:0:8}) 반영 확인" exit 0 fi sleep 10 done - echo "::error::헬스체크 실패: 운영의 build-sha (${REMOTE:-없음})가 기대 SHA(${EXPECTED:-비어있음})와 다름 — 빌드/재시작 실패 또는 SHA 환경변수 미주입" + echo "::error::헬스체크 실패: 운영의 build-sha (${REMOTE:-없음})가 기대 SHA(${EXPECTED:-비어있음})와 다름" + echo " → 마지막 webhook 로그: curl -k 'https://momotogether.com/api/deploy/webhook?token=momo-deploy-2026-secure'" exit 1