730 lines
23 KiB
Markdown
730 lines
23 KiB
Markdown
# ITAM Linux 운영 배포 가이드
|
|
|
|
## 1. 문서 목적
|
|
|
|
이 문서는 현재 ITAM 저장소를 기준으로 Linux 환경에서 운영 배포하는 방법을 정리한 가이드다.
|
|
|
|
핵심 전제는 아래와 같다.
|
|
|
|
1. 저장소 구조를 크게 재편하지 않는다.
|
|
2. 현재 워크스페이스 기준 파일 구조를 그대로 활용한다.
|
|
3. `docker-compose.test.yaml`과 `docker-compose.prod.yaml` 모두 현재 저장소 루트 기준으로 동작한다.
|
|
4. DB는 Docker 내부가 아니라 외부 MySQL을 사용한다.
|
|
|
|
즉, 이 문서는 `/srv/itam` 같은 별도 운영 디렉터리 구조를 강제하는 문서가 아니라, 현재 저장소 구조를 기준으로 운영 전환하는 방법을 설명한다.
|
|
|
|
---
|
|
|
|
## 2. 현재 운영 관련 파일
|
|
|
|
현재 저장소에서 운영 전환과 직접 관련된 파일은 아래와 같다.
|
|
|
|
1. `docker-compose.yaml`
|
|
2. `docker-compose.test.yaml`
|
|
3. `docker-compose.prod.yaml`
|
|
4. `Dockerfile.frontend.prod`
|
|
5. `Dockerfile.backend.prod`
|
|
6. `docker/nginx/default.conf`
|
|
7. `docker/frontend/default.conf`
|
|
8. `.env.example`
|
|
9. `server.js`
|
|
|
|
각 파일의 역할은 아래와 같다.
|
|
|
|
1. `docker-compose.yaml`: 개발 재현용 구성
|
|
2. `docker-compose.test.yaml`: 운영형 Dockerfile과 reverse proxy 구조를 로컬에서 검증하는 테스트용 구성
|
|
3. `docker-compose.prod.yaml`: 현재 저장소 기준 운영용 구성
|
|
4. `Dockerfile.frontend.prod`: 프런트 정적 빌드 및 Nginx 서빙 이미지 정의
|
|
5. `Dockerfile.backend.prod`: 백엔드 API 운영 이미지 정의
|
|
6. `docker/nginx/default.conf`: reverse proxy 설정
|
|
7. `docker/frontend/default.conf`: frontend 컨테이너 내부 정적 파일 서빙 설정
|
|
8. `.env.example`: 운영/테스트 환경변수 템플릿
|
|
9. `server.js`: `/health`, `/ready` 엔드포인트 포함
|
|
|
|
---
|
|
|
|
## 3. 현재 기준 운영 아키텍처
|
|
|
|
현재 구조에서의 요청 흐름은 아래와 같다.
|
|
|
|
1. 외부 요청은 Nginx 컨테이너로 들어온다.
|
|
2. `/` 요청은 frontend 컨테이너로 전달된다.
|
|
3. `/api/`와 `/uploads/` 요청은 backend 컨테이너로 전달된다.
|
|
4. backend는 외부 MySQL에 연결한다.
|
|
5. `uploads`, `map_config.json`, `.env`, 로그는 현재 저장소 기준 상대 경로를 사용한다.
|
|
|
|
```mermaid
|
|
flowchart LR
|
|
U["User Browser"] --> RP["Reverse Proxy Nginx 80"]
|
|
RP -->|root| FE["Frontend Container Nginx Static 80"]
|
|
RP -->|api| BE["Backend Container Node 3000"]
|
|
RP -->|uploads| BE
|
|
BE --> DB["External MySQL 3306"]
|
|
BE --> UP["./uploads"]
|
|
BE --> CFG["./map_config.json"]
|
|
BE --> ENV["./.env"]
|
|
linkStyle default stroke:#d32f2f,stroke-width:2px;
|
|
```
|
|
|
|
현재 저장소 기준 파일/컨테이너 관계는 아래와 같다.
|
|
|
|
```mermaid
|
|
flowchart TB
|
|
subgraph REPO["Current Repository Root"]
|
|
ENV[".env"]
|
|
UP["uploads/"]
|
|
MAP["map_config.json"]
|
|
LOGS["logs/nginx/"]
|
|
CONF["docker/nginx/default.conf"]
|
|
end
|
|
|
|
subgraph CTR["Containers"]
|
|
NGINX["itam-nginx"]
|
|
FRONT["itam-frontend"]
|
|
BACK["itam-backend"]
|
|
end
|
|
|
|
DB["External MySQL"]
|
|
|
|
CONF --> NGINX
|
|
LOGS --> NGINX
|
|
ENV --> BACK
|
|
UP --> BACK
|
|
MAP --> BACK
|
|
NGINX --> FRONT
|
|
NGINX --> BACK
|
|
BACK --> DB
|
|
linkStyle default stroke:#d32f2f,stroke-width:2px;
|
|
```
|
|
|
|
---
|
|
|
|
## 4. 개발용, 테스트용, 운영용 차이
|
|
|
|
### 4.1 `docker-compose.yaml`
|
|
|
|
용도:
|
|
|
|
1. 개발 재현
|
|
2. 소스 수정과 빠른 확인
|
|
3. Vite dev server 기반 실행
|
|
|
|
특징:
|
|
|
|
1. bind mount 중심
|
|
2. 프런트는 개발 서버 기반
|
|
3. 운영 배포보다는 개발 생산성에 초점
|
|
|
|
### 4.2 `docker-compose.test.yaml`
|
|
|
|
용도:
|
|
|
|
1. 운영형 Dockerfile 테스트
|
|
2. reverse proxy 동작 테스트
|
|
3. 로컬/WSL에서 8080 포트 검증
|
|
|
|
특징:
|
|
|
|
1. frontend/backend를 `build`로 생성
|
|
2. nginx는 8080 포트로 노출
|
|
3. 현재 저장소 상대 경로를 그대로 사용
|
|
|
|
### 4.3 `docker-compose.prod.yaml`
|
|
|
|
용도:
|
|
|
|
1. 현재 저장소 구조 기준 운영 배포
|
|
2. 운영 모드 환경변수와 health check 사용
|
|
3. 현재 구조를 바꾸지 않고 운영 전환
|
|
|
|
특징:
|
|
|
|
1. frontend/backend를 `build + image` 방식으로 정의
|
|
2. `.env`, `uploads`, `map_config.json`, `logs/nginx`를 현재 저장소 기준 상대 경로로 사용
|
|
3. nginx는 80 포트를 사용
|
|
4. backend는 `NODE_ENV=production`으로 실행
|
|
|
|
---
|
|
|
|
## 5. 운영 파일 구조 기준
|
|
|
|
현재 운영 배포는 저장소 루트를 기준으로 아래 구조를 전제로 한다.
|
|
|
|
```text
|
|
itam/
|
|
.env
|
|
.env.example
|
|
docker-compose.prod.yaml
|
|
docker-compose.test.yaml
|
|
Dockerfile.frontend.prod
|
|
Dockerfile.backend.prod
|
|
map_config.json
|
|
uploads/
|
|
logs/
|
|
nginx/
|
|
docker/
|
|
nginx/
|
|
default.conf
|
|
frontend/
|
|
default.conf
|
|
```
|
|
|
|
운영에서 실제로 중요한 경로는 아래 네 가지다.
|
|
|
|
1. `./.env`
|
|
2. `./uploads`
|
|
3. `./map_config.json`
|
|
4. `./logs/nginx`
|
|
|
|
즉, 현재 구조를 유지하려면 이 경로들이 항상 함께 관리되어야 한다.
|
|
|
|
---
|
|
|
|
## 6. 운영 환경변수 정책
|
|
|
|
현재 기준 운영 환경변수는 저장소 루트의 `.env`를 사용한다.
|
|
|
|
`.env.example` 기준 예시는 아래와 같다.
|
|
|
|
```env
|
|
DB_HOST=172.16.8.151
|
|
DB_PORT=3306
|
|
DB_USER=itam_admin
|
|
DB_PASS=change-this
|
|
DB_NAME=itam
|
|
|
|
NODE_ENV=production
|
|
PORT=3000
|
|
LOG_LEVEL=info
|
|
```
|
|
|
|
운영 원칙은 아래와 같다.
|
|
|
|
1. 실제 운영 비밀번호가 들어간 `.env`는 Git에 올리지 않는다.
|
|
2. `.env.example`은 템플릿으로만 사용한다.
|
|
3. 운영 서버에서는 `.env` 파일 권한을 제한한다.
|
|
4. 운영 DB 계정과 개발 DB 계정은 분리한다.
|
|
|
|
권장 권한 예시는 아래와 같다.
|
|
|
|
```bash
|
|
chmod 600 .env
|
|
```
|
|
|
|
---
|
|
|
|
## 7. Frontend 운영 이미지 기준
|
|
|
|
`Dockerfile.frontend.prod`는 multi-stage build를 사용한다.
|
|
|
|
구성은 아래와 같다.
|
|
|
|
1. builder 단계에서 `npm ci` 수행
|
|
2. `npm run build` 수행
|
|
3. 결과물을 Nginx 이미지에 복사
|
|
4. `docker/frontend/default.conf`로 정적 파일 서빙
|
|
|
|
운영 관점에서의 장점은 아래와 같다.
|
|
|
|
1. runtime 이미지에 build 결과물만 포함된다.
|
|
2. dev server 없이 정적 파일만 제공한다.
|
|
3. frontend 컨테이너 자체도 health check 가능하다.
|
|
|
|
---
|
|
|
|
## 8. Backend 운영 이미지 기준
|
|
|
|
`Dockerfile.backend.prod`는 아래 기준으로 작성되어 있다.
|
|
|
|
1. `NODE_ENV=production`
|
|
2. production dependency만 설치
|
|
3. `appuser` 비루트 사용자 사용
|
|
4. `dumb-init` 사용
|
|
5. `/health` health check 사용
|
|
|
|
backend 컨테이너는 아래 자원을 사용한다.
|
|
|
|
1. `./.env`
|
|
2. `./uploads`
|
|
3. `./map_config.json`
|
|
4. 외부 MySQL
|
|
|
|
---
|
|
|
|
## 9. Reverse Proxy 기준
|
|
|
|
`docker/nginx/default.conf`는 현재 아래처럼 동작한다.
|
|
|
|
1. `/` -> `frontend:80`
|
|
2. `/api/` -> `backend:3000`
|
|
3. `/uploads/` -> `backend:3000`
|
|
|
|
추가로 아래 설정을 포함한다.
|
|
|
|
1. 기본 보안 헤더
|
|
2. access/error 로그
|
|
3. gzip 설정
|
|
4. health endpoint
|
|
|
|
중요한 점은, 현재 운영 기준에서 외부 사용자가 직접 frontend 컨테이너에 붙는 것이 아니라 반드시 nginx를 통해 들어간다는 점이다.
|
|
|
|
---
|
|
|
|
## 10. 현재 기준 운영 배포 절차
|
|
|
|
### 10.1 사전 점검
|
|
|
|
아래 항목을 먼저 확인한다.
|
|
|
|
1. `.env` 파일 존재 여부
|
|
2. `uploads/` 디렉터리 존재 여부
|
|
3. `logs/nginx/` 디렉터리 존재 여부
|
|
4. `map_config.json` 존재 여부
|
|
5. 외부 DB 접근 가능 여부
|
|
|
|
예시:
|
|
|
|
```bash
|
|
ls -la .env
|
|
ls -la uploads
|
|
ls -la logs/nginx
|
|
ls -la map_config.json
|
|
```
|
|
|
|
### 10.2 Compose 검증
|
|
|
|
```bash
|
|
docker compose -f docker-compose.prod.yaml config
|
|
```
|
|
|
|
### 10.3 운영 기동
|
|
|
|
```bash
|
|
docker compose -f docker-compose.prod.yaml up -d --build
|
|
docker compose -f docker-compose.prod.yaml ps
|
|
```
|
|
|
|
### 10.4 운영 중지
|
|
|
|
```bash
|
|
docker compose -f docker-compose.prod.yaml down
|
|
```
|
|
|
|
### 10.5 운영/배포 분기 흐름
|
|
|
|
현재 운영 반영은 자동 push 배포가 아니라, Gitea에 올라간 커밋을 기준으로 수동 workflow를 실행하는 방식이다.
|
|
|
|
즉 아래 원칙으로 이해하면 된다.
|
|
|
|
1. 로컬 수정본을 서버에 직접 복사하지 않는다.
|
|
2. 반드시 Gitea에 올라간 커밋을 기준으로 배포한다.
|
|
3. 운영 반영은 `.gitea/workflows/itam_production_deploy.yml` 수동 실행으로 진행한다.
|
|
4. 실패 후 재배포는 실패 지점에 따라 수정 위치가 달라진다.
|
|
|
|
운영 반영은 크게 세 상황으로 나뉜다.
|
|
|
|
1. 최초 운영 서버 구축 후 첫 배포
|
|
2. 코드 수정 후 일반 재배포
|
|
3. 배포 실패 또는 검증 실패 후 재배포
|
|
|
|
```mermaid
|
|
flowchart TD
|
|
START["배포 필요 발생"] --> CASE{"어떤 상황인가?"}
|
|
|
|
CASE -->|초기 구축| INIT["초기 운영 배포 준비"]
|
|
CASE -->|수정 반영| CHANGE["수정 후 재배포 준비"]
|
|
CASE -->|실패 후 재시도| RETRY["실패 원인 분석 후 재배포 준비"]
|
|
|
|
INIT --> INIT1["운영 서버 Docker / compose 확인"]
|
|
INIT1 --> INIT2["Gitea Variables / Secrets 등록"]
|
|
INIT2 --> INIT3["map_config.json / uploads 초기 데이터 준비"]
|
|
INIT3 --> MANUAL["Gitea에서 수동 배포 workflow 실행"]
|
|
|
|
CHANGE --> CHANGE1["로컬 수정 및 테스트"]
|
|
CHANGE1 --> CHANGE2["Gitea 커밋 / push"]
|
|
CHANGE2 --> CHANGE3["Code Check / Docker Build Check 통과"]
|
|
CHANGE3 --> MANUAL
|
|
|
|
RETRY --> RETRY1{"어디서 실패했는가?"}
|
|
RETRY1 -->|코드 체크 실패| FIX1["코드 또는 설정 수정"]
|
|
RETRY1 -->|배포 단계 실패| FIX2["서버 / 변수 / 권한 / 네트워크 수정"]
|
|
RETRY1 -->|Smoke Check 실패| FIX3["앱 기동 상태 / 프록시 / DB 상태 수정"]
|
|
FIX1 --> CHANGE2
|
|
FIX2 --> MANUAL
|
|
FIX3 --> MANUAL
|
|
|
|
MANUAL --> BACKUP["기존 운영 상태가 있으면 배포 전 백업"]
|
|
BACKUP --> DEPLOY["운영 서버 반영 수행"]
|
|
DEPLOY --> RESULT{"최종 검증 통과?"}
|
|
RESULT -->|예| DONE["운영 반영 완료"]
|
|
RESULT -->|아니오| RETRY
|
|
linkStyle default stroke:#d32f2f,stroke-width:2px;
|
|
```
|
|
|
|
### 10.6 최초 운영 배포 플로우
|
|
|
|
최초 배포에서는 코드보다 운영 환경 준비가 먼저다.
|
|
|
|
순서는 아래와 같다.
|
|
|
|
1. 운영 서버에 Docker Engine과 `docker compose`를 설치한다.
|
|
2. 운영 서버에서 Gitea 저장소에 접근 가능한 SSH 키를 준비한다.
|
|
3. Gitea repository Variables / Secrets를 등록한다.
|
|
4. `PROD_DEPLOY_PATH` 경로를 확정한다.
|
|
5. `PROD_BACKUP_ROOT` 경로를 `PROD_DEPLOY_PATH` 바깥으로 확정한다.
|
|
6. `map_config.json`, `uploads/` 초기 데이터를 준비한다.
|
|
7. Gitea에서 `itam_production_deploy.yml`을 수동 실행한다.
|
|
8. 배포 후 `docker compose ps`, `/health`, `/`, `/ready`를 확인한다.
|
|
|
|
즉 최초 배포는 아래 순서다.
|
|
|
|
```text
|
|
서버 준비 완료
|
|
-> Gitea 변수 / 시크릿 등록 완료
|
|
-> 백업 경로 확정 완료
|
|
-> 초기 데이터 준비 완료
|
|
-> 수동 배포 실행
|
|
```
|
|
|
|
### 10.7 수정 후 일반 재배포 플로우
|
|
|
|
일반적인 수정 반영은 아래 흐름이다.
|
|
|
|
1. 개발자가 로컬에서 코드 또는 설정을 수정한다.
|
|
2. 로컬에서 필요한 테스트를 수행한다.
|
|
3. 변경사항을 Gitea에 커밋 후 push 한다.
|
|
4. `itam_code_check.yml`이 빌드와 compose 문법을 검사한다.
|
|
5. `itam_docker_build_check.yml`이 운영용 이미지 빌드 가능 여부를 검사한다.
|
|
6. 두 검증이 통과하면 운영자가 Gitea에서 `itam_production_deploy.yml`을 수동 실행한다.
|
|
7. 기존 운영 상태가 있으면 배포 전 백업을 먼저 수행한다.
|
|
8. 운영 서버가 최신 커밋으로 동기화되고 컨테이너가 다시 올라온다.
|
|
9. smoke check 통과 여부를 확인한다.
|
|
|
|
```mermaid
|
|
flowchart LR
|
|
DEV["로컬 수정"] --> TEST["로컬 확인"]
|
|
TEST --> PUSH["커밋 / push"]
|
|
PUSH --> CODE["ITAM Code Check"]
|
|
CODE --> BUILD["ITAM Docker Build Check"]
|
|
BUILD --> GATE{"검증 통과?"}
|
|
GATE -->|예| RUN["Gitea에서 수동 배포 실행"]
|
|
GATE -->|아니오| FIX["로컬 수정 후 재커밋"]
|
|
FIX --> PUSH
|
|
RUN --> BACKUP["배포 전 백업"]
|
|
BACKUP --> PROD["운영 서버 배포"]
|
|
PROD --> SMOKE{"Smoke Check 통과?"}
|
|
SMOKE -->|예| OK["배포 완료"]
|
|
SMOKE -->|아니오| FIXDEPLOY["원인 수정 후 재배포"]
|
|
FIXDEPLOY --> RUN
|
|
linkStyle default stroke:#d32f2f,stroke-width:2px;
|
|
```
|
|
|
|
### 10.8 수동 배포 workflow 내부 실행 순서
|
|
|
|
Gitea에서 `itam_production_deploy.yml`을 수동 실행하면 내부적으로는 아래 순서로 진행된다.
|
|
|
|
1. SSH agent를 설정한다.
|
|
2. 필수 Variables / Secrets가 모두 있는지 확인한다.
|
|
3. 운영용 `.env.deploy` 파일을 생성한다.
|
|
4. 운영 서버에 접속한다.
|
|
5. `PROD_DEPLOY_PATH`를 생성한다.
|
|
6. 기존 운영 상태가 있으면 `make predeploy-backup`을 실행한다.
|
|
7. 저장소를 clone 또는 fetch 한다.
|
|
8. 선택한 브랜치의 최신 커밋으로 checkout, reset, clean 한다.
|
|
9. `uploads`, `logs/nginx` 디렉토리를 준비한다.
|
|
10. `.env.deploy`를 서버의 `.env`로 복사한다.
|
|
11. `docker compose -f docker-compose.prod.yaml config`를 수행한다.
|
|
12. `docker compose -f docker-compose.prod.yaml up -d --build`를 수행한다.
|
|
13. `docker compose ps`를 확인한다.
|
|
14. `/health`, `/`, backend `/ready` smoke check를 수행한다.
|
|
|
|
```mermaid
|
|
flowchart TD
|
|
A["수동 배포 시작"] --> B["SSH agent 설정"]
|
|
B --> C["Variables / Secrets 검증"]
|
|
C --> D{"필수 값 누락 여부"}
|
|
D -->|예| E["즉시 실패 후 설정 보완"]
|
|
D -->|아니오| F[".env.deploy 생성"]
|
|
F --> G["운영 서버 SSH 접속"]
|
|
G --> H["배포 경로 생성"]
|
|
H --> I["기존 운영 상태가 있으면 make predeploy-backup"]
|
|
I --> J["git clone 또는 fetch"]
|
|
J --> K["지정 브랜치 checkout / reset / clean"]
|
|
K --> L["uploads / logs/nginx 준비"]
|
|
L --> M[".env 업로드 및 권한 설정"]
|
|
M --> N["compose config 검증"]
|
|
N --> O{"compose config 성공?"}
|
|
O -->|아니오| P["설정 수정 후 재실행"]
|
|
O -->|예| Q["compose up -d --build"]
|
|
Q --> R["docker compose ps 확인"]
|
|
R --> S["/health, /, /ready smoke check"]
|
|
S --> T{"smoke check 성공?"}
|
|
T -->|예| U["운영 배포 완료"]
|
|
T -->|아니오| V["원인 분석 후 재배포"]
|
|
linkStyle default stroke:#d32f2f,stroke-width:2px;
|
|
```
|
|
|
|
### 10.9 실패 후 검증 및 재배포 플로우
|
|
|
|
실패가 났다고 해서 같은 방식으로 바로 다시 배포하면 안 된다.
|
|
|
|
실패 지점별 판단은 아래처럼 나눈다.
|
|
|
|
1. Code Check 실패: TypeScript, build, compose 문법 문제를 먼저 수정한다.
|
|
2. Docker Build Check 실패: Dockerfile, 정적 자산 복사, 운영 빌드 컨텍스트 문제를 수정한다.
|
|
3. Deploy 단계 실패: SSH, Gitea 변수, 서버 권한, 경로, 백업 경로, git 접근, Docker 권한을 수정한다.
|
|
4. Smoke Check 실패: Nginx 프록시, backend readiness, 외부 DB 연결, 앱 런타임 오류를 수정한다.
|
|
|
|
즉 재배포 전 판단 기준은 아래와 같다.
|
|
|
|
```text
|
|
CI 실패 -> 로컬 코드 / 설정 수정 후 재커밋
|
|
배포 실패 -> 서버 환경 또는 배포 설정 수정 후 수동 재실행
|
|
Smoke Check 실패 -> 앱 / 프록시 / DB 상태 수정 후 수동 재실행
|
|
```
|
|
|
|
운영 관점에서는 아래 순서를 지키는 것이 안전하다.
|
|
|
|
1. 실패 지점 확인
|
|
2. 원인 수정
|
|
3. 같은 실패가 다시 나는지 좁은 범위로 재검증
|
|
4. 그 다음에만 수동 배포 재실행
|
|
|
|
---
|
|
|
|
## 11. 테스트 배포 절차
|
|
|
|
운영형 구성을 먼저 검증하려면 아래처럼 진행한다.
|
|
|
|
```bash
|
|
docker compose -f docker-compose.test.yaml up -d --build
|
|
docker compose -f docker-compose.test.yaml ps
|
|
```
|
|
|
|
접속 기준은 아래와 같다.
|
|
|
|
1. `http://localhost:8080` -> nginx reverse proxy
|
|
2. `http://localhost:3000/health` -> backend health 확인
|
|
|
|
테스트용은 운영과 매우 유사하지만, 외부 노출 포트와 일부 실행 목적이 다르다.
|
|
|
|
---
|
|
|
|
## 12. Health Check 및 상태 판정
|
|
|
|
backend에는 아래 두 엔드포인트가 있다.
|
|
|
|
1. `/health`
|
|
2. `/ready`
|
|
|
|
판정 기준은 아래와 같다.
|
|
|
|
1. `/health = 200`, `/ready = 200`: 정상 서비스 가능 상태
|
|
2. `/health = 200`, `/ready = 503`: 프로세스는 살아 있으나 DB 또는 외부 의존성 미준비
|
|
|
|
확인 예시는 아래와 같다.
|
|
|
|
```bash
|
|
curl http://localhost:3000/health
|
|
curl http://localhost:3000/ready
|
|
```
|
|
|
|
---
|
|
|
|
## 13. 운영 점검 체크리스트
|
|
|
|
### 13.1 애플리케이션
|
|
|
|
1. `docker compose -f docker-compose.prod.yaml config` 통과 여부
|
|
2. frontend 이미지 빌드 성공 여부
|
|
3. backend 이미지 빌드 성공 여부
|
|
4. 모든 컨테이너가 `Up` 상태인지
|
|
5. 메인 화면이 정상 렌더링되는지
|
|
6. 데이터 조회가 정상 동작하는지
|
|
|
|
### 13.2 데이터 및 파일
|
|
|
|
1. `uploads/`에 쓰기 가능한지
|
|
2. `map_config.json`을 backend가 읽을 수 있는지
|
|
3. `.env` 파일 권한이 적절한지
|
|
4. `logs/nginx/`에 로그가 쌓이는지
|
|
|
|
### 13.3 네트워크
|
|
|
|
1. 외부 DB 접근 가능한지
|
|
2. nginx에서 backend upstream 연결이 되는지
|
|
3. `/api` 요청이 정상 응답하는지
|
|
|
|
---
|
|
|
|
## 14. 로그 및 장애 대응
|
|
|
|
기본 점검 명령은 아래와 같다.
|
|
|
|
```bash
|
|
docker compose -f docker-compose.prod.yaml ps
|
|
docker compose -f docker-compose.prod.yaml logs --tail=200 nginx
|
|
docker compose -f docker-compose.prod.yaml logs --tail=200 backend
|
|
docker compose -f docker-compose.prod.yaml logs --tail=200 frontend
|
|
```
|
|
|
|
장애 확인 순서는 아래가 좋다.
|
|
|
|
1. 컨테이너가 살아 있는지
|
|
2. nginx가 frontend/backend로 프록시하는지
|
|
3. backend가 DB에 붙는지
|
|
4. 업로드/설정 파일 권한 문제는 없는지
|
|
|
|
추가 확인 예시는 아래와 같다.
|
|
|
|
```bash
|
|
curl -I http://localhost/
|
|
curl http://localhost:3000/health
|
|
curl http://localhost:3000/ready
|
|
```
|
|
|
|
---
|
|
|
|
## 15. 백업 기준
|
|
|
|
현재 구조 기준 최소 백업 대상은 아래와 같다.
|
|
|
|
1. `.env`
|
|
2. `uploads/`
|
|
3. `map_config.json`
|
|
4. 외부 MySQL 데이터베이스
|
|
|
|
현재 저장소에는 운영 백업을 직접 실행할 수 있도록 `Makefile`과 `scripts/backup.sh`가 추가되어 있다.
|
|
|
|
기본 원칙은 아래와 같다.
|
|
|
|
1. DB dump와 런타임 파일 백업을 분리해서 실행할 수 있어야 한다.
|
|
2. 기본 백업 산출물은 `backups/` 디렉터리에 쌓는다.
|
|
3. DB 접속 정보는 `.env`를 기준으로 읽는다.
|
|
4. 오래된 백업 파일은 보존 주기에 따라 정리한다.
|
|
|
|
백업 실행 흐름은 아래와 같다.
|
|
|
|
```mermaid
|
|
flowchart TD
|
|
START["운영 백업 실행"] --> TARGET{"무엇을 백업할 것인가?"}
|
|
TARGET -->|DB| DB["make db-dump"]
|
|
TARGET -->|운영 파일| FILES["make files-backup"]
|
|
TARGET -->|전체| FULL["make full-backup"]
|
|
DB --> OUT1["backups/db/*.sql.gz"]
|
|
FILES --> OUT2["backups/files/*.tar.gz"]
|
|
FULL --> OUT1
|
|
FULL --> OUT2
|
|
OUT1 --> CLEAN["make cleanup-backups"]
|
|
OUT2 --> CLEAN
|
|
linkStyle default stroke:#d32f2f,stroke-width:2px;
|
|
```
|
|
|
|
### 15.1 Make 명령 기준
|
|
|
|
사용 가능한 기본 명령은 아래와 같다.
|
|
|
|
```bash
|
|
make db-dump
|
|
make files-backup
|
|
make full-backup
|
|
make cleanup-backups
|
|
```
|
|
|
|
각 명령의 역할은 아래와 같다.
|
|
|
|
1. `make db-dump`: `.env` 기준 MySQL dump를 `backups/db/`에 `.sql.gz`로 저장
|
|
2. `make files-backup`: `.env`, `uploads/`, `map_config.json`을 `backups/files/`에 `.tar.gz`로 저장
|
|
3. `make full-backup`: DB dump와 파일 백업을 한 번에 수행
|
|
4. `make cleanup-backups`: 기본 14일이 지난 백업 파일 정리
|
|
|
|
### 15.2 실행 예시
|
|
|
|
가장 단순한 전체 백업 예시는 아래와 같다.
|
|
|
|
```bash
|
|
make full-backup
|
|
```
|
|
|
|
DB dump만 별도로 실행하려면 아래처럼 사용한다.
|
|
|
|
```bash
|
|
make db-dump
|
|
```
|
|
|
|
운영 파일만 묶으려면 아래처럼 사용한다.
|
|
|
|
```bash
|
|
make files-backup
|
|
```
|
|
|
|
보존 주기를 30일로 바꿔 정리하려면 아래처럼 사용한다.
|
|
|
|
```bash
|
|
make cleanup-backups RETENTION_DAYS=30
|
|
```
|
|
|
|
백업 경로를 별도 디스크나 마운트 경로로 바꾸려면 아래처럼 사용한다.
|
|
|
|
```bash
|
|
make full-backup BACKUP_ROOT=/opt/itam-backups
|
|
```
|
|
|
|
### 15.3 현재 스크립트가 실제로 백업하는 대상
|
|
|
|
현재 `scripts/backup.sh`는 아래 규칙으로 동작한다.
|
|
|
|
1. `.env` 파일에서 `DB_HOST`, `DB_PORT`, `DB_USER`, `DB_PASS`, `DB_NAME`을 읽는다.
|
|
2. `mysqldump --single-transaction --quick --routines --triggers` 옵션으로 dump를 생성한다.
|
|
3. DB dump는 gzip 압축본으로 저장한다.
|
|
4. 파일 백업은 `.env`, `uploads/`, `map_config.json` 중 실제로 존재하는 항목만 묶는다.
|
|
5. 백업 정리는 `find ... -mtime` 기준으로 수행한다.
|
|
|
|
즉 현재 스크립트는 운영 서버 또는 백업 서버에서 바로 실행 가능한 최소 백업 도구로 보면 된다.
|
|
|
|
### 15.4 운영 사용 권장 방식
|
|
|
|
운영에서는 아래 방식이 가장 현실적이다.
|
|
|
|
1. 매일 새벽 cron 또는 systemd timer로 `make full-backup` 실행
|
|
2. 백업 완료 후 `make cleanup-backups` 실행
|
|
3. `backups/` 또는 별도 `BACKUP_ROOT` 경로를 NAS 또는 외부 백업 스토리지로 추가 복사
|
|
4. 최소 월 1회 restore 테스트 수행
|
|
|
|
가장 단순한 예시는 아래와 같다.
|
|
|
|
```bash
|
|
make full-backup BACKUP_ROOT=/opt/itam-backups
|
|
make cleanup-backups BACKUP_ROOT=/opt/itam-backups RETENTION_DAYS=30
|
|
```
|
|
|
|
DB 백업 자체는 여전히 DB 서버 정책과 함께 관리하는 것이 가장 안전하지만, 현재 저장소 기준 운영 자동화 진입점은 위 `make` 명령으로 통일해도 된다.
|
|
|
|
---
|
|
|
|
## 16. 롤백 기준
|
|
|
|
현재 구조에서는 가장 단순한 롤백 방식이 아래와 같다.
|
|
|
|
1. 이전 정상 커밋 또는 파일 상태 확보
|
|
2. 이미지 재빌드 또는 이전 이미지 재사용
|
|
3. `docker compose -f docker-compose.prod.yaml up -d --build` 재실행
|
|
4. `/health`, `/ready`, 메인 화면, 핵심 API 재검증
|
|
|
|
즉, 현재 구조에서는 별도 디렉터리 재배치보다 현재 저장소 상태 관리와 compose 재기동이 롤백의 중심이 된다.
|
|
|
|
---
|
|
|
|
## 17. 결론
|
|
|
|
현재 ITAM 저장소는 별도 `/srv/itam` 구조로 옮기지 않아도, 지금 파일 구조를 유지한 채 운영형 배포 흐름으로 전환할 수 있다.
|
|
|
|
정리하면 아래와 같다.
|
|
|
|
1. test와 prod 모두 현재 저장소 구조 기준으로 통일한다.
|
|
2. `.env`, `uploads`, `map_config.json`, `logs/nginx`를 운영 핵심 경로로 본다.
|
|
3. reverse proxy는 현재 `docker/nginx/default.conf`를 기준으로 운영한다.
|
|
4. backend는 production 모드, health check, 외부 DB 연결 구조를 유지한다.
|
|
5. 큰 구조 변경 없이도 운영 전환이 가능하다.
|
|
|
|
남은 작업은 TLS, 로그 로테이션, CI/CD, 보안 점검을 현재 구조 기준으로 계속 보강하는 것이다. |