v1.12.27 NAS 실행 yaml 및 명시적 이미지 빌드 분리
This commit is contained in:
@@ -192,10 +192,10 @@ cp .env.example .env.production
|
||||
# Docker 내부 앱에서 PostgreSQL에 접근할 때는 sori-studio-db:5432 사용
|
||||
|
||||
# Docker CLI 빌드 및 실행
|
||||
sudo docker compose -p sori-studio -f docker-compose.yml --env-file .env.production up -d --build
|
||||
sudo docker compose -p sori-studio -f docker-compose.build.yml --env-file .env.production up -d --build
|
||||
|
||||
# UGREEN NAS Docker 앱처럼 --env-file 옵션을 넣기 어려운 GUI 배포에서는
|
||||
# docker-compose.yml과 .env.production을 같은 프로젝트 위치에 두고 Compose 빌드를 실행한다.
|
||||
# 이미지를 사전 빌드한 뒤 docker-compose.yaml과 .env.production을 같은 프로젝트 위치에서 사용한다.
|
||||
# 앱과 DB 컨테이너는 env_file 설정으로 .env.production을 직접 읽는다.
|
||||
|
||||
# 운영 DB 마이그레이션 상태 확인
|
||||
@@ -210,34 +210,58 @@ sudo sh scripts/migrate-production-db.sh migrate
|
||||
|
||||
### Compose 프로젝트명과 네트워크 일치 (v1.12.26)
|
||||
|
||||
운영 기본 프로젝트명은 `sori-studio`다. `docker-compose.yml`의 최상위 `name`은 `${COMPOSE_PROJECT_NAME:-sori-studio}`를 사용하고 `.env.production`에도 `COMPOSE_PROJECT_NAME=sori-studio`를 설정한다. CLI `-p`가 환경 변수보다 우선하므로 아래 NAS 명령은 기존 운영 프로젝트를 명시한다. DB 볼륨 이름은 프로젝트명과 독립적으로 기존 `POSTGRES_VOLUME_NAME`/외부 볼륨 설정을 유지한다.
|
||||
운영 기본 프로젝트명은 `sori-studio`다. `docker-compose.build.yml`의 최상위 `name`은 `${COMPOSE_PROJECT_NAME:-sori-studio}`를 사용하고 `.env.production`에도 `COMPOSE_PROJECT_NAME=sori-studio`를 설정한다. CLI `-p`가 환경 변수보다 우선하므로 아래 NAS 명령은 기존 운영 프로젝트를 명시한다. DB 볼륨 이름은 프로젝트명과 독립적으로 기존 `POSTGRES_VOLUME_NAME`/외부 볼륨 설정을 유지한다.
|
||||
|
||||
`Pool overlaps`와 `soristudio_sori-studio-network` 생성 실패가 함께 나오고 기존 `sori-studio_sori-studio-network`가 같은 대역을 쓰고 있다면 프로젝트명 불일치다. 기존 네트워크 삭제나 다른 대역 변경 전에 프로젝트명을 맞춘다. `docker-compose.yml`과 `docker-compose.yaml`이 둘 다 있어도 `-f docker-compose.yml`로 저장소 파일을 명시한다. 운영 마이그레이션·썸네일 백필 스크립트도 같은 파일을 명시한다.
|
||||
`Pool overlaps`와 `soristudio_sori-studio-network` 생성 실패가 함께 나오고 기존 `sori-studio_sori-studio-network`가 같은 대역을 쓰고 있다면 프로젝트명 불일치다. 기존 네트워크 삭제나 다른 대역 변경 전에 프로젝트명을 맞춘다. 실행용 `docker-compose.yaml`과 빌드용 `docker-compose.build.yml`을 구분하고 CLI는 `-f`로 목적에 맞는 파일을 선택한다. 운영 마이그레이션·썸네일 백필 스크립트는 실행용 yaml을 명시한다.
|
||||
|
||||
이미 빌드가 성공한 직후 네트워크 생성만 실패했다면 다음 명령으로 해당 이미지를 실행한다.
|
||||
|
||||
```bash
|
||||
sudo docker compose -p sori-studio -f docker-compose.yml --env-file .env.production up -d --no-build --pull never
|
||||
sudo docker compose -p sori-studio -f docker-compose.yml --env-file .env.production ps
|
||||
sudo docker compose -p sori-studio -f docker-compose.build.yml --env-file .env.production up -d --no-build --pull never
|
||||
sudo docker compose -p sori-studio -f docker-compose.build.yml --env-file .env.production ps
|
||||
```
|
||||
|
||||
### 운영 업데이트 (Docker 앱 우선)
|
||||
### 운영 업데이트 (Docker 앱: 로컬 빌드 후 재배포, v1.12.27)
|
||||
|
||||
기본 운영 경로는 터미널에서 소스만 `git pull`하고, UGREEN Docker 앱의 기존 `sori-studio` 프로젝트에서 재배포하는 방식이다. 같은 Docker 엔진을 쓰므로 GUI와 CLI 자체가 충돌하는 것은 아니며, 프로젝트 이름과 구성 파일이 다를 때 별도 네트워크 생성 충돌이 발생한다.
|
||||
제공된 NAS 로그에서는 Docker 앱 재배포가 소스 빌드 없이 `sori-studio` 원격 pull을 시도했고, pull 오류 뒤 컨테이너 재생성을 표시했다. 기존 `git pull → 앱 재배포`만으로 새 코드를 빌드할 수 있다는 안내는 이 환경에 적용하지 않는다. 현재 경로는 빌드와 앱 실행을 분리한다.
|
||||
|
||||
1. 프로젝트의 실제 빌드 폴더에서 `git pull`한다. GUI 프로젝트의 구성 파일 경로·빌드 context가 이 폴더를 가리켜야 한다.
|
||||
2. Docker 앱 → `프로젝트` → 기존 `sori-studio` → `Compose 구성`을 연다.
|
||||
3. 앱이 관리하는 구성을 저장소의 최신 `docker-compose.yml`과 맞춘다. `.yaml`과 `.yml`이 함께 있는 NAS에서는 앱의 구성 파일 경로가 어느 파일인지 확인한다. 앱이 별도 사본을 저장하면 최신 `.yml` 내용을 Compose 편집기에 반영하고 저장한다. 기존 운영 `.env.production`은 유지한다.
|
||||
4. 같은 화면의 `재배포`를 실행한다. `image: sori-studio:latest`, `build.context: .`, `pull_policy: build`가 있어야 소스 기반 재빌드를 요청한다. 컨테이너 시작/중지 토글은 코드 빌드를 수행하는 배포 절차와 다르다.
|
||||
5. 배포 로그에서 Dockerfile의 `npm run build` 단계가 실행됐는지와 새 컨테이너의 실행 상태를 확인한다. GUI 버전별 실제 재빌드 동작은 NAS에서 확인해야 한다. Docker 앱이 빌드 정책을 무시하면 아래 CLI 경로를 사용한다.
|
||||
1. 프로젝트의 실제 소스 폴더에서 다음 명령으로 최신 소스를 받고 로컬 이미지를 만든다. 빌드 스크립트는 기존 컨테이너·네트워크·DB를 재생성하지 않고 `docker compose build sori-studio`만 실행한다. `.env.production`이 없으면 `ENV_FILE`을 명시해야 하며, 빌드 실패 시 재배포로 진행하지 않는다.
|
||||
|
||||
v1.12.24~26에는 새 DB 마이그레이션 파일이 없으므로 이 버전 범위만 업데이트할 때는 DB 마이그레이션 실행이 필요하지 않다. 이후 `db/migrations`가 변경되는 배포에서는 아래 스크립트로 미적용 SQL을 처리한다. 운영자 요청에 따라 소스 동기화 이후 앱 재배포를 우선하며 GUI에 없는 자동 Git 동기화·자동 마이그레이션을 가정하지 않는다.
|
||||
처음 한 번은 NAS 앱이 만들었던 비추적 `docker-compose.yaml`을 백업한다. 기존 파일이 Git 관리 중이면 이동하지 않는다. 이는 새로 추적하는 동일 이름 파일 때문에 `git pull`이 중단되는 것을 피하기 위한 절차다.
|
||||
|
||||
근거: [UGREEN Docker/Compose 안내](https://ai.ugreen.com/blogs/knowledge/docker-docker-compose-ugreen-nas), [Compose build 정책](https://docs.docker.com/reference/compose-file/services/#pull_policy).
|
||||
```bash
|
||||
if [ -f docker-compose.yaml ] && ! git ls-files --error-unmatch docker-compose.yaml >/dev/null 2>&1; then
|
||||
mv docker-compose.yaml "docker-compose.yaml.backup.$(date +%Y%m%d-%H%M%S)"
|
||||
fi
|
||||
git pull
|
||||
sudo sh scripts/build-production-image.sh
|
||||
```
|
||||
|
||||
이후에는 백업 분기 없이 `git pull`과 빌드 스크립트를 실행한다.
|
||||
|
||||
2. Docker 앱 → `프로젝트` → 기존 `sori-studio` → `Compose 구성`에서 저장소의 **`docker-compose.yaml` 내용**을 반영한다. 처음 한 번 GUI가 관리하는 구성에 적용하고 이후 같은 프로젝트를 사용한다. `docker-compose.yaml`은 앱이 기준으로 사용하는 독립 실행용 파일이며 빌드용 `.yml` 뒤에 병합하는 override가 아니다. 빌드 폴더와 같은 위치에서 상대 업로드·마이그레이션 경로를 사용해야 한다. `.env.production`과 기존 DB 볼륨을 유지한다.
|
||||
3. 이 구성은 앱 서비스의 `build`를 제거하고 `image: sori-studio:latest`, `pull_policy: never`를 사용한다. NAS에 미리 만든 로컬 이미지만 실행한다. 이미지가 없으면 실패하며 Docker Hub에서 해당 앱 이미지를 찾지 않는다. DB 이미지는 기존 `postgres:16-alpine`을 사용한다.
|
||||
4. `재배포`를 실행한다. 이미지 다시 다운로드·업데이트 같은 옵션이 있다면 앱 로컬 이미지에 대해 해제한다. GUI가 명시적인 pull을 강제하면 Compose 정책보다 우선할 수 있으므로, 같은 `pull access denied`가 재발할 때는 실제 재배포 옵션과 사용 중인 Compose 사본을 확인해야 한다. 현재 GUI의 옵션 제공 여부·정확한 명칭은 확인되지 않았다.
|
||||
5. 이미지를 먼저 빌드했으므로 앱 재배포 자체가 짧게 끝나는 것은 정상이다. 필요하면 실행 중인 앱 이미지 ID와 빌드한 이미지 ID를 비교해 새 이미지 적용을 확인한다.
|
||||
|
||||
```bash
|
||||
sudo docker image inspect sori-studio:latest --format '{{.Id}}'
|
||||
sudo docker inspect sori-studio --format '{{.Image}}'
|
||||
```
|
||||
|
||||
위 두 값이 같아야 방금 빌드한 이미지로 실행 중이다. GUI가 로컬 이미지 배포를 강제 pull로 막는 경우의 CLI 보조 실행은 다음과 같다.
|
||||
|
||||
```bash
|
||||
sudo docker compose -p sori-studio -f docker-compose.yaml --env-file .env.production up -d --pull never
|
||||
```
|
||||
|
||||
이후 업데이트도 `git pull → 이미지 빌드 스크립트 → 앱 재배포` 순서다. 앱만으로 모든 과정을 수행하려면 별도 이미지 빌드·저장소 배포 자동화가 필요하며 현재 구성에는 포함하지 않는다. v1.12.24~27에는 새 DB 마이그레이션 파일이 없으므로 이 버전 범위만 업데이트할 때는 DB 마이그레이션 실행이 필요하지 않다.
|
||||
|
||||
근거: [Compose 이미지 빌드](https://docs.docker.com/reference/cli/docker/compose/build/), [Compose pull 정책](https://docs.docker.com/reference/compose-file/services/#pull_policy). NAS 앱의 실제 옵션·새 이미지 반영은 운영 화면에서 추가 확인해야 한다.
|
||||
|
||||
### 운영 업데이트 (CLI 보조 경로)
|
||||
|
||||
Docker 앱 대신 CLI로 배포해야 할 때는 아래처럼 기존 프로젝트명·Compose 파일을 명시한다. 최초 설치 절차와 달리 `git clone`은 하지 않는다.
|
||||
기본 Compose로 빌드와 배포를 한 번에 CLI에서 처리하려면 아래처럼 기존 프로젝트명·Compose 파일을 명시한다. 최초 설치 절차와 달리 `git clone`은 하지 않는다.
|
||||
|
||||
```bash
|
||||
# 프로젝트 루트로 이동 (경로는 NAS 환경에 맞게 조정)
|
||||
@@ -251,7 +275,7 @@ sudo sh scripts/migrate-production-db.sh status
|
||||
sudo sh scripts/migrate-production-db.sh migrate
|
||||
|
||||
# 앱 이미지 재빌드 후 컨테이너 재기동
|
||||
sudo docker compose -p sori-studio -f docker-compose.yml --env-file .env.production up -d --build
|
||||
sudo docker compose -p sori-studio -f docker-compose.build.yml --env-file .env.production up -d --build
|
||||
```
|
||||
|
||||
| 단계 | 설명 |
|
||||
@@ -265,17 +289,17 @@ sudo docker compose -p sori-studio -f docker-compose.yml --env-file .env.product
|
||||
- `.env.production`은 Git에 포함하지 않는다. `git pull`로 덮어쓰이지 않는다. 값을 바꿀 때만 파일을 직접 수정한다.
|
||||
- `public/uploads/` 업로드 파일은 Docker 볼륨(`./public/uploads`)에 있으므로, **이미지 파일만 추가·수정한 경우** 앱 재빌드 없이도 URL로 바로 보인다.
|
||||
- 로컬에서 미리 확인하려면 `npm run verify` 후 NAS에서 위 명령을 실행하면 된다.
|
||||
- UGREEN Docker 앱의 재배포가 너무 빨리 끝나고 앱 빌드 로그가 보이지 않으면 기존 이미지를 재사용한 것이다. 현재 Compose는 `sori-studio:latest`와 `pull_policy: build`로 앱 이미지를 다시 빌드하도록 선언하지만, NAS Docker 앱 버전이 이 값을 무시하면 터미널에서 `sudo docker compose -p sori-studio -f docker-compose.yml --env-file .env.production up -d --build`를 실행해야 한다.
|
||||
- 제공된 UGREEN 재배포 로그에서는 소스 빌드가 실행되지 않았다. Docker 앱에는 로컬 이미지를 사전 빌드한 뒤 `docker-compose.yaml`로 재배포하는 절차를 사용한다. 기본 `docker-compose.build.yml`은 CLI 빌드·배포용으로 유지한다.
|
||||
|
||||
컨테이너만 재시작하고 이미지는 그대로 두려면(환경 변수만 바꾼 경우 등):
|
||||
|
||||
```bash
|
||||
sudo docker compose -p sori-studio -f docker-compose.yml --env-file .env.production up -d --no-build
|
||||
sudo docker compose -p sori-studio -f docker-compose.build.yml --env-file .env.production up -d --no-build
|
||||
```
|
||||
|
||||
### UGREEN NAS Docker 앱 Buildx 확인
|
||||
|
||||
UGREEN NAS Docker 앱에서 `Docker Compose requires buildx plugin to be installed` 경고가 뜨면 NAS의 Docker CLI 플러그인 경로에 Buildx가 설치되지 않은 상태다. Buildx는 프로젝트별 파일이 아니라 NAS Docker 환경 단위 플러그인이므로 한 번 설치한 뒤 기본 `docker-compose.yml`로 빌드한다.
|
||||
UGREEN NAS Docker 앱에서 `Docker Compose requires buildx plugin to be installed` 경고가 뜨면 NAS의 Docker CLI 플러그인 경로에 Buildx가 설치되지 않은 상태다. Buildx는 프로젝트별 파일이 아니라 NAS Docker 환경 단위 플러그인이므로 한 번 설치한 뒤 기본 `docker-compose.build.yml`로 빌드한다.
|
||||
|
||||
```bash
|
||||
sudo docker buildx version
|
||||
@@ -338,7 +362,7 @@ NAS에 Docker 컨테이너가 많이 실행 중이면 `could not find an availab
|
||||
|
||||
```bash
|
||||
DOCKER_SUBNET=10.250.51.0/24
|
||||
sudo docker compose -p sori-studio -f docker-compose.yml --env-file .env.production up -d --build
|
||||
sudo docker compose -p sori-studio -f docker-compose.build.yml --env-file .env.production up -d --build
|
||||
```
|
||||
|
||||
### 포트
|
||||
@@ -496,7 +520,7 @@ git diff -- . ':!package-lock.json'
|
||||
|
||||
`Error response from daemon: Container … is restarting, wait until the container is running`은 **프로세스가 곧바로 종료**되어 `restart: unless-stopped`가 반복 시도하는 상태다. 원인은 로그에 나온다.
|
||||
|
||||
1. **어느 서비스인지 확인** (`docker-compose.yml` 기준 이름은 `sori-studio`, `sori-studio-db`).
|
||||
1. **어느 서비스인지 확인** (`docker-compose.build.yml` 기준 이름은 `sori-studio`, `sori-studio-db`).
|
||||
|
||||
```bash
|
||||
docker ps -a --filter "name=sori-studio"
|
||||
@@ -512,8 +536,8 @@ docker logs sori-studio-db --tail 150
|
||||
Compose로 올렸다면:
|
||||
|
||||
```bash
|
||||
sudo docker compose -p sori-studio -f docker-compose.yml --env-file .env.production logs sori-studio --tail 200
|
||||
sudo docker compose -p sori-studio -f docker-compose.yml --env-file .env.production logs sori-studio-db --tail 200
|
||||
sudo docker compose -p sori-studio -f docker-compose.build.yml --env-file .env.production logs sori-studio --tail 200
|
||||
sudo docker compose -p sori-studio -f docker-compose.build.yml --env-file .env.production logs sori-studio-db --tail 200
|
||||
```
|
||||
|
||||
3. **자주 나오는 원인**
|
||||
@@ -522,11 +546,11 @@ sudo docker compose -p sori-studio -f docker-compose.yml --env-file .env.product
|
||||
- **`sori-studio-db`**: 이미 초기화된 볼륨과 다른 `POSTGRES_PASSWORD`로 다시 올린 경우, `docker-entrypoint-initdb.d` 마이그레이션 SQL 오류, 디스크/권한 문제.
|
||||
- **`sori-studio-db` 로그에 `ls: can't open '/docker-entrypoint-initdb.d/': Permission denied`**: 아래 **NAS·호스트에서 `db/migrations` 권한** 절차를 확인한다.
|
||||
|
||||
4. 로그를 고친 뒤에는 `sudo docker compose -p sori-studio -f docker-compose.yml --env-file .env.production up -d`로 다시 올리고, `docker ps`에서 `Up` 상태인지 확인한다.
|
||||
4. 로그를 고친 뒤에는 `sudo docker compose -p sori-studio -f docker-compose.build.yml --env-file .env.production up -d`로 다시 올리고, `docker ps`에서 `Up` 상태인지 확인한다.
|
||||
|
||||
### NAS·호스트에서 `db/migrations` 권한
|
||||
|
||||
`docker-compose.yml`은 `./db/migrations`를 Postgres 이미지의 `/docker-entrypoint-initdb.d`에 **읽기 전용**으로 붙인다. 공식 엔트리포인트는 이 디렉터리를 `ls`로 읽는데, NAS(UGREEN 등)나 SSH로 복사한 트리에서 **폴더·파일이 700/600만 허용**이거나 **상위 디렉터리에 실행(x) 비트가 없으면** 컨테이너 안 `postgres` 사용자가 경로를 통과하지 못해 `Permission denied`가 반복되고 DB 컨테이너가 재시작 루프에 들어갈 수 있다.
|
||||
`docker-compose.build.yml`은 `./db/migrations`를 Postgres 이미지의 `/docker-entrypoint-initdb.d`에 **읽기 전용**으로 붙인다. 공식 엔트리포인트는 이 디렉터리를 `ls`로 읽는데, NAS(UGREEN 등)나 SSH로 복사한 트리에서 **폴더·파일이 700/600만 허용**이거나 **상위 디렉터리에 실행(x) 비트가 없으면** 컨테이너 안 `postgres` 사용자가 경로를 통과하지 못해 `Permission denied`가 반복되고 DB 컨테이너가 재시작 루프에 들어갈 수 있다.
|
||||
|
||||
프로젝트 루트( `docker compose` 를 실행하는 디렉터리)에서 SSH로 다음을 적용한다. **비밀번호는 바꾸지 않으며**, 읽기·디렉터리 통과만 연다.
|
||||
|
||||
@@ -541,7 +565,7 @@ sudo chmod a+x . db db/migrations
|
||||
그다음 DB 컨테이너만 재시작한다.
|
||||
|
||||
```bash
|
||||
sudo docker compose -p sori-studio -f docker-compose.yml --env-file .env.production restart sori-studio-db
|
||||
sudo docker compose -p sori-studio -f docker-compose.build.yml --env-file .env.production restart sori-studio-db
|
||||
```
|
||||
|
||||
여전히 동일하면 프로젝트가 **SMB 공유 폴더 위**에만 있지 않은지 확인한다. Docker 데몬이 네이티브 경로(ext4 등)의 디렉터리를 마운트할 때 권한이 더 예측 가능하다.
|
||||
@@ -577,7 +601,7 @@ Homepage 위젯 연동은 `docs/homepage-widget.md`를 참고한다. Docker에
|
||||
- 게시물 Export ZIP 산출물은 `public/uploads/exports/YYYY/MM/{jobId}/` 아래 생성되며, 관리자 다운로드 API를 통해 내려받는다.
|
||||
- 완료·실패한 게시물 Export 작업을 관리자 화면에서 삭제하면 연결된 ZIP 파일도 함께 삭제된다.
|
||||
- `public/uploads/`는 Git에 포함하지 않는다.
|
||||
- NAS 운영에서는 `docker-compose.yml`의 `./public/uploads:/app/public/uploads` 볼륨으로 업로드 파일을 유지한다.
|
||||
- NAS 운영에서는 `docker-compose.build.yml`의 `./public/uploads:/app/public/uploads` 볼륨으로 업로드 파일을 유지한다.
|
||||
- 운영 빌드에서는 `/uploads/**` 서버 라우트가 `.output/public`이 아니라 `/app/public/uploads` 볼륨의 실제 파일을 직접 제공한다.
|
||||
- `/uploads/**` 응답은 `ETag`와 `Last-Modified`를 제공한다. 고유 URL로 저장되는 원본·시스템·회원 썸네일 파일은 1년 immutable 캐시를 사용하고, 같은 URL로 재생성될 수 있는 게시물 `thumbs` 파생 파일은 1시간 캐시와 `stale-while-revalidate`를 사용한다.
|
||||
- `MAX_FILE_SIZE`, `MAX_VIDEO_FILE_SIZE`, `MAX_AUDIO_FILE_SIZE`, `MAX_DOCUMENT_FILE_SIZE` 환경 변수로 관리자 미디어 업로드 파일별 최대 크기를 제한한다. 1회 업로드 최대 파일 수와 전체 용량은 관리자 Settings > POST 설정 > 업로드 제한에서 조절한다. 리버스 프록시(Nginx 등)를 쓰면 `client_max_body_size`가 앱 한도보다 작지 않은지 함께 확인한다.
|
||||
|
||||
Reference in New Issue
Block a user