From dbcfe6569211ab3f5608e2cfe0c1c6835378f9b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B6=8C=ED=98=81=EC=84=B1?= Date: Thu, 26 Feb 2026 14:39:50 +0900 Subject: [PATCH] =?UTF-8?q?docs:MNG=20storage/logs=20=EC=8B=AC=EB=A7=81?= =?UTF-8?q?=ED=81=AC=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20E-Sign=20PDF=20?= =?UTF-8?q?=ED=8A=B8=EB=9F=AC=EB=B8=94=EC=8A=88=ED=8C=85=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 05-deployment: MNG Jenkinsfile/수동배포 storage/logs 심링크 방식 반영 - 08-troubleshooting: 전자계약 PDF 서명 합성 오류 진단/조치 가이드 추가 - 08-troubleshooting: MNG 500 에러 섹션 로그 경로 shared로 업데이트 Co-Authored-By: Claude Opus 4.6 --- deploys/ops-manual/05-deployment.md | 17 +-- deploys/ops-manual/08-troubleshooting.md | 136 +++++++++++++++++++++-- 2 files changed, 134 insertions(+), 19 deletions(-) diff --git a/deploys/ops-manual/05-deployment.md b/deploys/ops-manual/05-deployment.md index 0bd560f..eb9d2ab 100644 --- a/deploys/ops-manual/05-deployment.md +++ b/deploys/ops-manual/05-deployment.md @@ -609,9 +609,10 @@ ls -1dt */ | tail -n +4 | xargs rm -rf 2>/dev/null || true API와 동일한 releases/shared 구조. 차이점: npm build 추가, Queue Worker 재시작 불필요. -> **참고: storage/logs 권한** -> MNG는 `storage/logs`를 shared로 심링크하지 않고 릴리즈 디렉토리에 `mkdir`로 생성한다. -> Jenkinsfile에서 `sudo chown -R www-data:webservice storage/logs`로 권한을 설정한다. (2026-02-25 적용) +> **참고: storage/logs 심링크 (2026-02-26 변경)** +> MNG는 storage/logs를 shared로 심링크하여 배포 간 로그를 영속 보존한다. +> 이전에는 `mkdir`로 릴리즈 디렉토리에 생성하여 배포마다 로그가 유실되었음. +> 변경: `ln -sfn /home/webservice/mng/shared/storage/logs storage/logs` ### Jenkinsfile (mng/Jenkinsfile) @@ -655,9 +656,10 @@ 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 && + mkdir -p bootstrap/cache storage/framework/{views,cache/data,sessions} && ln -sfn /home/webservice/mng/shared/.env .env && ln -sfn /home/webservice/mng/shared/storage/app storage/app && + ln -sfn /home/webservice/mng/shared/storage/logs storage/logs && composer install --no-dev --optimize-autoloader --no-interaction && npm install --prefer-offline && npm run build && @@ -712,11 +714,12 @@ RELEASE_ID=$(date +%Y%m%d_%H%M%S) cd /home/webservice/mng/releases git clone --depth 1 --branch main https://git.sam.it.kr/SamProject/sam-manage.git $RELEASE_ID -ln -sfn /home/webservice/mng/shared/storage /home/webservice/mng/releases/$RELEASE_ID/storage ln -sfn /home/webservice/mng/shared/.env /home/webservice/mng/releases/$RELEASE_ID/.env +ln -sfn /home/webservice/mng/shared/storage/app /home/webservice/mng/releases/$RELEASE_ID/storage/app +ln -sfn /home/webservice/mng/shared/storage/logs /home/webservice/mng/releases/$RELEASE_ID/storage/logs cd /home/webservice/mng/releases/$RELEASE_ID -mkdir -p bootstrap/cache storage/framework/{views,cache/data,sessions} storage/logs +mkdir -p bootstrap/cache storage/framework/{views,cache/data,sessions} composer install --no-dev --optimize-autoloader --no-interaction # Vite 빌드 (Blade + Tailwind) @@ -915,7 +918,7 @@ sudo supervisorctl status # 에러 로그 sudo tail -20 /var/log/nginx/api.sam.it.kr.error.log sudo tail -20 /home/webservice/api/shared/storage/logs/laravel.log -sudo tail -20 /home/webservice/mng/current/storage/logs/laravel.log +sudo tail -20 /home/webservice/mng/shared/storage/logs/laravel.log # HTTP 응답 확인 curl -sI https://api.sam.it.kr diff --git a/deploys/ops-manual/08-troubleshooting.md b/deploys/ops-manual/08-troubleshooting.md index 3b2b27e..7c92783 100644 --- a/deploys/ops-manual/08-troubleshooting.md +++ b/deploys/ops-manual/08-troubleshooting.md @@ -102,6 +102,39 @@ sudo mysql -e "SET GLOBAL max_connections = 150;" --- +### [개발] MySQL 8.4 인증 플러그인 오류 + +**증상:** `SQLSTATE[HY000] [2054] The server requested authentication method unknown to the client` + +**원인:** MySQL 8.4에서 `mysql_native_password` 플러그인이 기본 비활성화됨. 레거시 PHP(5130 등)의 mysqlnd가 `caching_sha2_password`를 지원하지 못함. + +**조치:** + +```bash +# 1. mysqld.cnf에 플러그인 활성화 추가 +sudo vi /etc/mysql/mysql.conf.d/mysqld.cnf +# [mysqld] 섹션에 추가: +# mysql_native_password=ON + +# 2. MySQL 재시작 +sudo systemctl restart mysql + +# 3. 레거시 PHP용 계정 인증 방식 변경 +mysql -u debian-sys-maint -p'비밀번호' -e " +ALTER USER '계정'@'localhost' IDENTIFIED WITH mysql_native_password BY '비밀번호'; +FLUSH PRIVILEGES;" +``` + +**실제 사례 (2026-02-25):** + +1. 5130 레거시 사이트 로그인 시 2054 에러 발생 +2. `/etc/mysql/mysql.conf.d/mysqld.cnf`에 `mysql_native_password=ON` 추가 후 MySQL 재시작 +3. `codebridge`, `pro`, `chandj` 계정을 `mysql_native_password`로 변경하여 해결 + +**참고:** debian-sys-maint 비밀번호는 `/etc/mysql/debian.cnf`에서 확인 가능. + +--- + ### Redis 메모리 부족 **증상:** "OOM command not allowed" 메시지. @@ -262,16 +295,18 @@ sudo chmod -R 775 /home/webservice/api/current/bootstrap/cache **증상:** mng.codebridge-x.com 특정 페이지에서 500 에러. Laravel 로그에 기록 없음. -**배경:** MNG Jenkinsfile 배포 시 `storage/logs`는 shared로 심링크되지 않고 릴리즈 디렉토리에 직접 생성됨. -따라서 `hskwon:hskwon` 소유로 생성되어 `www-data`(PHP-FPM)가 로그를 쓸 수 없음. +**배경:** 2026-02-26 이후 MNG `storage/logs`는 shared로 심링크됨. 이전에는 릴리즈 디렉토리에 직접 생성되어 배포마다 로그가 유실되었음. **진단 순서:** ```bash -# 1. 실제 로그 위치 확인 (shared가 아닌 current) -ls -la /home/webservice/mng/current/storage/logs/laravel.log +# 1. 로그 심링크 확인 +ls -la /home/webservice/mng/current/storage/logs +# → shared/storage/logs 심링크인지 확인 + +# 2. 로그 파일 소유자 확인 +ls -la /home/webservice/mng/shared/storage/logs/laravel.log -# 2. 로그 파일 소유자 확인 — hskwon이면 www-data가 쓸 수 없음 # 3. nginx 접근 로그에서 500 확인 sudo tail -20 /var/log/nginx/mng.codebridge-x.com.access.log | grep " 500 " ``` @@ -279,12 +314,16 @@ sudo tail -20 /var/log/nginx/mng.codebridge-x.com.access.log | grep " 500 " **조치:** ```bash -# 로그 권한 수정 (배포마다 필요) -sudo chown www-data:webservice /home/webservice/mng/current/storage/logs/ -sudo chown www-data:webservice /home/webservice/mng/current/storage/logs/laravel.log +# 로그 심링크가 아닌 경우 (이전 배포 방식) +rm -rf /home/webservice/mng/current/storage/logs +ln -sfn /home/webservice/mng/shared/storage/logs /home/webservice/mng/current/storage/logs -# 로그 확인 후 실제 에러 파악 -cat /home/webservice/mng/current/storage/logs/laravel.log +# shared 로그 권한 수정 +sudo chown www-data:webservice /home/webservice/mng/shared/storage/logs/ +sudo chown www-data:webservice /home/webservice/mng/shared/storage/logs/laravel.log + +# 로그 확인 +cat /home/webservice/mng/shared/storage/logs/laravel.log ``` **실제 사례 (2026-02-25):** @@ -296,12 +335,85 @@ cat /home/webservice/mng/current/storage/logs/laravel.log 5. 최종 해결: `sudo apt install php8.4-soap && sudo systemctl restart php8.4-fpm` **교훈:** -- MNG 로그는 `current/storage/logs/`에 있음 (shared 아님) -- 500 에러인데 로그가 비어있으면 **파일 권한 문제**를 먼저 의심 +- MNG 로그는 `shared/storage/logs/`에 있음 (2026-02-26~) +- 500 에러인데 로그가 비어있으면 **심링크 여부 → 파일 권한** 순서로 확인 - PHP 확장 누락은 artisan tinker로 확인 가능: `php artisan tinker --execute="new SoapClient('test');"` --- +### MNG 전자계약(E-Sign) PDF 서명 합성 오류 + +**증상:** 전자계약 완료 후 다운로드한 PDF에 서명/도장/텍스트가 적용되지 않음. DB에서 `signed_file_path`가 null. + +**진단:** + +```bash +# 1. 완료됐지만 signed_file_path 없는 계약 확인 +cd /home/webservice/mng/current && php artisan tinker --execute=" +\$contracts = App\Models\ESign\EsignContract::withoutGlobalScopes() + ->where('status', 'completed')->whereNull('signed_file_path') + ->get(['id','tenant_id','status','completed_at']); +echo \$contracts->toJson(JSON_PRETTY_PRINT); +" + +# 2. 서명 이미지 파일 존재 확인 +sudo ls -la /home/webservice/mng/shared/storage/app/private/esign/*/signatures/ + +# 3. signed 디렉토리 존재 및 권한 확인 +ls -la /home/webservice/mng/shared/storage/app/private/esign/*/signed/ + +# 4. 로그 확인 +grep -i "서명\|esign\|pdf" /home/webservice/mng/shared/storage/logs/laravel.log | tail -20 + +# 5. 한글 폰트 확인 +ls -la /usr/share/fonts/truetype/nanum/NanumGothic.ttf +``` + +**조치 (수동 PDF 재합성):** + +```bash +cd /home/webservice/mng/current && sudo -u www-data php artisan tinker --execute=" +try { + \$contract = App\Models\ESign\EsignContract::withoutGlobalScopes()->find(<계약ID>); + \$pdfService = new App\Services\ESign\PdfSignatureService; + \$result = \$pdfService->mergeSignatures(\$contract); + echo 'SUCCESS: ' . \$result; +} catch (\Throwable \$e) { + echo 'ERROR: ' . \$e->getMessage(); +} +" +``` + +**주의:** 반드시 `sudo -u www-data`로 실행해야 서명 이미지 파일 접근 가능. + +**주요 원인 및 해결:** + +| 원인 | 진단 방법 | 해결 | +|------|----------|------| +| `signed/` 디렉토리 미존재 | `ls esign/*/signed/` | `sudo -u www-data mkdir -p esign/{tenant_id}/signed` | +| `signatures/` 권한 부족 | `stat esign/*/signatures/` | `sudo chmod 2775 esign/*/signatures/` | +| 로그 유실로 에러 추적 불가 | `ls -la current/storage/logs` | `storage/logs` → shared 심링크 확인 | +| 한글 폰트 미설치 | `ls /usr/share/fonts/truetype/nanum/` | `sudo apt install fonts-nanum` | +| FPDI/TCPDF 미설치 | `composer show setasign/fpdi` | `composer install` | + +**esign 디렉토리 권한 기준:** + +```bash +# 모든 esign 하위 디렉토리: www-data:webservice 2775 +sudo chown -R www-data:webservice /home/webservice/mng/shared/storage/app/private/esign/ +sudo chmod -R 2775 /home/webservice/mng/shared/storage/app/private/esign/ +``` + +**실제 사례 (2026-02-26):** + +1. 계약 #17이 `completed`인데 `signed_file_path`가 null +2. 원인: `signatures/` 디렉토리 권한 `2700` (www-data만 접근 가능), `signed/` 디렉토리 미존재 +3. 추가 원인: `storage/logs`가 릴리즈 디렉토리에 있어 이전 배포 로그 유실 +4. 조치: 권한 `2775`로 수정 + `sudo -u www-data`로 수동 재합성 + storage/logs 심링크 적용 +5. 결과: 409KB signed PDF 생성 (원본 265KB + 서명 이미지 144KB) + +--- + ## 공통 장애 ### 디스크 공간 부족