플랫폼

파일시스템과 영구 스토리지 마운트

HOME=/app 규칙, /home/container 심링크, 휘발성 컨테이너 루트와 /app 영구 볼륨의 차이, 의존성 자동 설치(.deps.hash)와 백업 제외 규칙 안내.

업데이트: 2026-10-08

일반적인 클라우드 PaaS(Heroku, Render)나 컨테이너 서비스(AWS ECS, Cloud Run)를 사용할 때 개발자들을 가장 당혹스럽게 만드는 문제는 **"서버가 재배포되거나 재부팅되면 로컬에 저장해 둔 SQLite 파일이나 수집 데이터가 깨끗이 사라진다"**는 점입니다.

DashCloud는 프로세스가 가동되는 샌드박스 내부의 /app 경로를 독립된 초고속 NVMe 영구 물리 블록 볼륨에 영구 마운트합니다.

새로운 코드를 배포하거나 서버를 재부팅해도 내 데이터가 100% 안전하게 유지되는 파일시스템 아키텍처와 대시보드 파일 관리자 활용법을 상세히 다룹니다.


HOME은 /app 입니다 (Wings 호환)

기존 Pterodactyl/Wings 환경과의 호환을 위해 컨테이너의 홈 디렉터리(HOME)는 /app으로 설정됩니다. 또한 Wings 경로를 그대로 쓰는 코드를 위해 /home/container가 /app으로 연결된 심링크가 제공됩니다.

  • ~, Path.home(), os.path.expanduser("~") → 모두 /app
  • /home/container → /app (심링크)
  • 프로세스 작업 디렉터리(cwd)도 /app이므로 상대 경로는 자동으로 영구 볼륨에 저장됩니다

휘발성 디렉터리와 영구 디렉터리의 생명주기 비교

컨테이너 내부의 디렉터리는 위치에 따라 생명주기가 완전히 다릅니다:

/ (루트 파일시스템 - 휘발성 영역)
├─ bin/
├─ usr/
├─ tmp/                  # [휘발성] 재부팅 시 초기화되는 임시 캐시 디렉터리
├─ home/container -> /app    # [심링크] Wings 호환 경로
└─ app/                  # [★ 물리 볼륨 영구 마운트 영역, HOME]
   ├─ main.py            # 소스 코드 (새 배포 시 덮어쓰기)
   ├─ requirements.txt
   ├─ .local/            # [영구] pip 의존성 설치 위치 (PYTHONPATH)
   ├─ node_modules/      # [영구] npm 의존성
   ├─ .deps.hash         # 의존성 변경 감지 마커 (내부용)
   └─ data/              # [★ 영구 보존 데이터 영역]
      ├─ bot.db          # [영구 보존] SQLite 유저 데이터베이스
      ├─ session.json    # [영구 보존] 크롤러 로그인 인증 세션
      ├─ grid_state.json # [영구 보존] 퀀트 활성 주문 상태 장부
      └─ exports/        # [영구 보존] 수집된 일일 CSV 엑셀 파일
디렉터리 위치스토리지 종류재배포 시 데이터 유지 여부권장 활용처
/app 및 /app/data독립 물리 영구 볼륨100% 안전 보존 (유지됨 ✅)SQLite DB, 세션 쿠키, CSV 로그, 영구 상태
/tmp컨테이너 임시 메모리 램디스크재부팅 시 완전 초기화 (삭제됨 ❌)1회성 임시 이미지 처리, 임시 다운로드 청크
/var, /usr컨테이너 기본 OS 이미지새 빌드 시 새 OS로 교체 (초기화)시스템 기본 라이브러리 및 패키지
상대 경로(./data/...) 사용 시 자동 영구화

파이썬이나 노드 스크립트에서 data/bot.db처럼 현재 디렉터리 하위의 상대 경로로 파일을 생성하면, 자동으로 /app/data/bot.db 물리 볼륨에 저장되므로 별도의 복잡한 마운트 경로를 신경 쓰지 않아도 됩니다. HOME=/app이므로 ~/data/bot.db도 동일합니다.


의존성 자동 설치와 .deps.hash

배포/재시작 시 런타임 이미지가 매니페스트 변경을 감지해 의존성을 자동 설치합니다.

  • Python: requirements.txt 변경 시 pip install --user(→ /app/.local) 실행, PYTHONPATH 자동 설정
  • Node.js: package-lock.json(없으면 package.json) 변경 시 npm ci/npm install 실행 (→ /app/node_modules)
  • 설치가 성공하면 매니페스트 해시가 .deps.hash 에 기록되어, 다음 재시작부터는 재설치를 건너뜁니다. 설치가 중단된 경우(크래시 등)는 다음 부팅에서 자동으로 복구·재시도합니다
  • 백업 복원 후에는 의존성이 자동으로 다시 설치됩니다 (백업에 의존성 트리를 포함하지 않기 때문)
설치 중 메모리 한도

의존성 설치는 별도의 일회성 설치 컨테이너(RAM/CPU 무제한)에서 실행되므로, 128MB 같은 저사양 봇에서도 빌드 컴파일이 OOM으로 죽지 않습니다.


백업 제외 규칙

백업(로컬 tar.gz / GDrive / restic)은 아래 항목을 의도적으로 제외합니다. 재설치로 복구 가능한 항목이거나 런타임 내부 상태이기 때문입니다.

제외 항목범위이유
node_modules, .venv, venv, __pycache__, .git하위 모든 위치재설치/재클론 가능
.cache, .npm프로젝트 루트만패키지 캐시 (중첩된 동명 디렉터리는 유저 데이터로 보존)
.deps.hash, .node_modules.bak*, .node_modules.lock프로젝트 루트만런타임 내부 상태

즉, data/ 하위의 파일·SQLite DB·수집 데이터는 백업에 포함되지만, 위 목록은 복원되지 않고 복원 직후 자동 재설치로 다시 생성됩니다.


새 버전 배포 시의 파일시스템 병합 원리

Git 푸시나 대시보드 업로드를 통해 새로운 코드를 배포할 때, DashCloud는 다음과 같이 파일을 병합합니다:

  1. 소스 코드 업데이트: 새로 업로드된 main.py나 requirements.txt는 새로운 버전으로 교체됩니다.
  2. 영구 데이터 보존: 사용자의 봇이 런타임 중에 /app/data/ 디렉터리에 기록해 둔 SQLite 데이터베이스(bot.db)나 CSV 파일은 새 배포에 포함되지 않은 파일이더라도 절대 삭제되지 않고 그대로 유지됩니다.
  3. 의존성만 재설치: 매니페스트가 바뀐 경우 .deps.hash 비교로 필요한 경우에만 설치가 다시 실행됩니다.

대시보드 웹 파일 관리자 활용

개발자는 복잡한 SFTP 클라이언트(FileZilla 등) 없이도, DashCloud 웹 대시보드에서 브라우저만으로 서버 내부의 파일을 직접 열람하고 조작할 수 있습니다.

주요 기능

  • 원클릭 파일 다운로드: /app/data/bot.db나 수집된 CSV 파일을 클릭하면 즉시 내 PC로 다운로드됩니다.
  • 웹 텍스트 에디터: 설정 파일(config.json)이나 .env 파일을 웹 브라우저에서 직접 열어 수정하고 저장할 수 있습니다.
  • 드래그앤드롭 업로드: 로컬 PC에 있던 초기 SQLite DB 파일 등을 브라우저 화면에 끌어다 놓아 바로 올릴 수 있습니다.
  • 압축/압축해제, 복사/이동/이름변경/삭제: 대용량 디렉터리는 페이지 단위로 불러옵니다 (기본 300개, "더 보기"로 확장).

잔여 디스크 용량 모니터링 (파이썬 코드)

크롤러가 대량의 스크린샷이나 시계열 데이터를 수집할 때 디스크가 가득 차면(Disk Full) 쓰기 작업이 실패합니다. 봇 내부에서 잔여 디스크 용량을 사전에 체크하는 방어 코드입니다.

check_disk.py
import shutil
import logging
 
logger = logging.getLogger("DiskMonitor")
 
def check_disk_usage(path: str = "/app"):
    """잔여 디스크 용량 측정 (GB 단위)"""
    total, used, free = shutil.disk_usage(path)
    
    total_gb = total / (1024 ** 3)
    used_gb = used / (1024 ** 3)
    free_gb = free / (1024 ** 3)
    used_percent = (used / total) * 100
 
    logger.info(f"디스크 용량: 총 {total_gb:.1f}GB 중 {used_gb:.1f}GB 사용 ({used_percent:.1f}%), 잔여: {free_gb:.1f}GB")
 
    # 잔여 공간이 200MB 미만이면 경고 발송
    if free_gb < 0.2:
        logger.critical("⚠️ 영구 볼륨 디스크 용량이 200MB 미만으로 위험 수준입니다!")
        # 관리자 텔레그램으로 경고 발송

관련 블로그


다음 추천 문서