From 4610ae128b39e6e01d2a3faeca4e6756c9e0adb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B6=8C=ED=98=81=EC=84=B1?= Date: Tue, 24 Feb 2026 23:53:56 +0900 Subject: [PATCH] =?UTF-8?q?docs:=20ops-manual=20=EB=B0=B0=ED=8F=AC=20?= =?UTF-8?q?=EA=B0=80=EC=9D=B4=EB=93=9C=20=ED=98=84=ED=96=89=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 05-deployment.md: Jenkinsfile 코드블록 전체 현행화 - React/API/MNG: slackSend + tokenCredentialId 추가 - API/MNG: mkdir-p bootstrap/cache, storage/framework 추가 - MNG: npm install --production=false → --prefer-offline - 수동배포 섹션: mkdir-p 추가, 단계 번호 재정렬 - 빌드 실패 트러블슈팅: Laravel 디렉토리 누락 항목 추가 - 07-monitoring.md: Contact Point TODO → 실제 설정 완료 내용 반영 Co-Authored-By: Claude Opus 4.6 --- deploys/ops-manual/05-deployment.md | 77 ++++++++++++++++++++++------- deploys/ops-manual/07-monitoring.md | 2 +- 2 files changed, 60 insertions(+), 19 deletions(-) diff --git a/deploys/ops-manual/05-deployment.md b/deploys/ops-manual/05-deployment.md index 888f9ce..e9582dc 100644 --- a/deploys/ops-manual/05-deployment.md +++ b/deploys/ops-manual/05-deployment.md @@ -206,7 +206,11 @@ pipeline { stages { stage('Checkout') { - steps { checkout scm } + steps { + slackSend channel: '#product_infra', color: '#439FE0', tokenCredentialId: 'slack-token', + message: "🚀 *react* 빌드 시작 (`${env.BRANCH_NAME}`)\n<${env.BUILD_URL}|빌드 #${env.BUILD_NUMBER}>" + checkout scm + } } stage('Prepare Env') { @@ -312,8 +316,14 @@ pipeline { } post { - success { echo '✅ react 배포 완료 (' + env.BRANCH_NAME + ')' } - failure { echo '❌ react 배포 실패 (' + env.BRANCH_NAME + ')' } + success { + slackSend channel: '#product_infra', color: 'good', tokenCredentialId: 'slack-token', + message: "✅ *react* 배포 성공 (`${env.BRANCH_NAME}`)\n<${env.BUILD_URL}|빌드 #${env.BUILD_NUMBER}>" + } + failure { + slackSend channel: '#product_infra', color: 'danger', tokenCredentialId: 'slack-token', + message: "❌ *react* 배포 실패 (`${env.BRANCH_NAME}`)\n<${env.BUILD_URL}|빌드 #${env.BUILD_NUMBER}>" + } } } ``` @@ -369,7 +379,11 @@ pipeline { stages { stage('Checkout') { - steps { checkout scm } + steps { + slackSend channel: '#product_infra', color: '#439FE0', tokenCredentialId: 'slack-token', + message: "🚀 *api* 빌드 시작 (`${env.BRANCH_NAME}`)\n<${env.BUILD_URL}|빌드 #${env.BUILD_NUMBER}>" + checkout scm + } } // ── main → 운영서버 Stage 배포 ── @@ -386,6 +400,7 @@ pipeline { . ${DEPLOY_USER}@211.117.60.189:/home/webservice/api-stage/releases/${RELEASE_ID}/ ssh ${DEPLOY_USER}@211.117.60.189 ' cd /home/webservice/api-stage/releases/${RELEASE_ID} && + mkdir -p bootstrap/cache storage/framework/{views,cache/data,sessions} storage/logs && ln -sfn /home/webservice/api-stage/shared/.env .env && ln -sfn /home/webservice/api-stage/shared/storage/app storage/app && composer install --no-dev --optimize-autoloader --no-interaction && @@ -427,6 +442,7 @@ pipeline { . ${DEPLOY_USER}@211.117.60.189:/home/webservice/api/releases/${RELEASE_ID}/ ssh ${DEPLOY_USER}@211.117.60.189 ' cd /home/webservice/api/releases/${RELEASE_ID} && + mkdir -p bootstrap/cache storage/framework/{views,cache/data,sessions} storage/logs && ln -sfn /home/webservice/api/shared/.env .env && ln -sfn /home/webservice/api/shared/storage/app storage/app && composer install --no-dev --optimize-autoloader --no-interaction && @@ -448,9 +464,13 @@ pipeline { } post { - success { echo "✅ api 배포 완료 (${env.BRANCH_NAME})" } + success { + slackSend channel: '#product_infra', color: 'good', tokenCredentialId: 'slack-token', + message: "✅ *api* 배포 성공 (`${env.BRANCH_NAME}`)\n<${env.BUILD_URL}|빌드 #${env.BUILD_NUMBER}>" + } failure { - echo "❌ api 배포 실패 (${env.BRANCH_NAME})" + slackSend channel: '#product_infra', color: 'danger', tokenCredentialId: 'slack-token', + message: "❌ *api* 배포 실패 (`${env.BRANCH_NAME}`)\n<${env.BUILD_URL}|빌드 #${env.BUILD_NUMBER}>" script { if (env.BRANCH_NAME == 'main') { sshagent(credentials: ['deploy-ssh-key']) { @@ -490,26 +510,29 @@ git clone --depth 1 --branch main https://git.sam.it.kr/SamProject/sam-api.git $ ln -sfn /home/webservice/api/shared/storage /home/webservice/api/releases/$RELEASE_ID/storage ln -sfn /home/webservice/api/shared/.env /home/webservice/api/releases/$RELEASE_ID/.env -# 3. 의존성 설치 +# 3. 필수 디렉토리 생성 (.gitignore에 의해 누락) cd /home/webservice/api/releases/$RELEASE_ID +mkdir -p bootstrap/cache storage/framework/{views,cache/data,sessions} storage/logs + +# 4. 의존성 설치 composer install --no-dev --optimize-autoloader --no-interaction -# 4. 캐시 생성 +# 5. 캐시 생성 php artisan config:cache php artisan route:cache php artisan view:cache -# 5. 마이그레이션 (필요시) +# 6. 마이그레이션 (필요시) php artisan migrate --force -# 6. 심링크 전환 (이 시점에 배포 적용) +# 7. 심링크 전환 (이 시점에 배포 적용) ln -sfn /home/webservice/api/releases/$RELEASE_ID /home/webservice/api/current -# 7. 서비스 리로드 +# 8. 서비스 리로드 sudo systemctl reload php8.4-fpm sudo supervisorctl restart sam-queue-worker:* -# 8. 오래된 릴리즈 정리 (최근 5개만 유지) +# 9. 오래된 릴리즈 정리 (최근 5개만 유지) cd /home/webservice/api/releases ls -1dt */ | tail -n +6 | xargs rm -rf 2>/dev/null || true ``` @@ -527,6 +550,7 @@ ln -sfn /home/webservice/api-stage/shared/storage /home/webservice/api-stage/rel ln -sfn /home/webservice/api-stage/shared/.env /home/webservice/api-stage/releases/$RELEASE_ID/.env cd /home/webservice/api-stage/releases/$RELEASE_ID +mkdir -p bootstrap/cache storage/framework/{views,cache/data,sessions} storage/logs composer install --no-dev --optimize-autoloader --no-interaction php artisan config:cache php artisan route:cache @@ -560,7 +584,11 @@ pipeline { stages { stage('Checkout') { - steps { checkout scm } + steps { + slackSend channel: '#product_infra', color: '#439FE0', tokenCredentialId: 'slack-token', + message: "🚀 *mng* 빌드 시작 (`${env.BRANCH_NAME}`)\n<${env.BUILD_URL}|빌드 #${env.BUILD_NUMBER}>" + checkout scm + } } // ── main → 운영서버 Production ── @@ -578,10 +606,11 @@ pipeline { . ${DEPLOY_USER}@211.117.60.189:/home/webservice/mng/releases/${RELEASE_ID}/ ssh ${DEPLOY_USER}@211.117.60.189 ' cd /home/webservice/mng/releases/${RELEASE_ID} && + mkdir -p bootstrap/cache storage/framework/{views,cache/data,sessions} storage/logs && ln -sfn /home/webservice/mng/shared/.env .env && ln -sfn /home/webservice/mng/shared/storage/app storage/app && composer install --no-dev --optimize-autoloader --no-interaction && - npm install --production=false && + npm install --prefer-offline && npm run build && php artisan config:cache && php artisan route:cache && @@ -600,9 +629,13 @@ pipeline { } post { - success { echo "✅ mng 배포 완료 (${env.BRANCH_NAME})" } + success { + slackSend channel: '#product_infra', color: 'good', tokenCredentialId: 'slack-token', + message: "✅ *mng* 배포 성공 (`${env.BRANCH_NAME}`)\n<${env.BUILD_URL}|빌드 #${env.BUILD_NUMBER}>" + } failure { - echo "❌ mng 배포 실패 (${env.BRANCH_NAME})" + slackSend channel: '#product_infra', color: 'danger', tokenCredentialId: 'slack-token', + message: "❌ *mng* 배포 실패 (`${env.BRANCH_NAME}`)\n<${env.BUILD_URL}|빌드 #${env.BUILD_NUMBER}>" script { if (env.BRANCH_NAME == 'main') { sshagent(credentials: ['deploy-ssh-key']) { @@ -634,10 +667,11 @@ ln -sfn /home/webservice/mng/shared/storage /home/webservice/mng/releases/$RELEA ln -sfn /home/webservice/mng/shared/.env /home/webservice/mng/releases/$RELEASE_ID/.env cd /home/webservice/mng/releases/$RELEASE_ID +mkdir -p bootstrap/cache storage/framework/{views,cache/data,sessions} storage/logs composer install --no-dev --optimize-autoloader --no-interaction # Vite 빌드 (Blade + Tailwind) -npm install --production=false +npm install --prefer-offline npm run build php artisan config:cache @@ -807,6 +841,7 @@ ssh sam-prod " ln -sfn /home/webservice/api/shared/storage /home/webservice/api/releases/${RELEASE_ID}/storage && ln -sfn /home/webservice/api/shared/.env /home/webservice/api/releases/${RELEASE_ID}/.env && cd /home/webservice/api/releases/${RELEASE_ID} && + mkdir -p bootstrap/cache storage/framework/{views,cache/data,sessions} storage/logs && composer install --no-dev --optimize-autoloader --no-interaction && php artisan config:cache && php artisan route:cache && @@ -874,4 +909,10 @@ ls -la /var/lib/jenkins/workspace/ 2. npm run build 실패 -- TypeScript 오류, 환경변수 누락 3. rsync 실패 -- SSH 키 문제, 디스크 공간 부족 4. composer install 실패 -- 네트워크, PHP 확장 누락 -5. SSH 연결 실패 -- known_hosts 변경, 키 만료 \ No newline at end of file +5. SSH 연결 실패 -- known_hosts 변경, 키 만료 +6. Laravel `package:discover` 실패 -- `bootstrap/cache/` 디렉토리 누락 (`.gitignore`에 포함) +7. Blade view 캐시 실패 -- `storage/framework/views/` 디렉토리 누락 +8. `Target class [request] does not exist` -- CLI 컨텍스트에서 `request()` 호출 (AppServiceProvider 확인) + +> **Laravel 배포 필수:** `mkdir -p bootstrap/cache storage/framework/{views,cache/data,sessions} storage/logs`를 +> `composer install` 전에 실행해야 함. `.gitignore`가 이 디렉토리들을 제외하므로 rsync/git clone 후 생성 필요. \ No newline at end of file diff --git a/deploys/ops-manual/07-monitoring.md b/deploys/ops-manual/07-monitoring.md index a2a2d75..8a01bea 100644 --- a/deploys/ops-manual/07-monitoring.md +++ b/deploys/ops-manual/07-monitoring.md @@ -167,7 +167,7 @@ node_filefd_allocated **알림 채널:** Grafana > Alerting > Contact points 에서 이메일, Slack 등 설정 -> **TODO:** Contact Point에 실제 이메일 주소 또는 Slack Webhook 설정 필요 (현재 기본 email placeholder) +**현재 설정:** SAM Slack Contact Point (Incoming Webhook) 연결 완료. Notification Policy에서 SAM Alerts 폴더의 알림이 Slack `#product_infra` 채널로 전송됨. ---