13년차의 서버실

서버, 인프라, 홈랩 기술 블로그

[태그:] 컨테이너 배포

  • [Cloud] Cloud Run 마이그레이션: 온프레미스 앱 전환 전략

    [Cloud] Cloud Run 마이그레이션: 온프레미스 앱 전환 전략

    [클라우드] Cloud Run 마이그레이션으로 온프레미스 앱 전환하기

    온프레미스에서 오래 돌리던 앱을 클라우드로 옮기는 일, 말은 쉬워 보여도 막상 들어가면 손이 꽤 갑니다. 특히 Cloud Run 마이그레이션은 서버 한 대만 없앤다고 끝나는 작업이 아니거든요. 프로세스가 어떻게 뜨는지, 파일을 어디에 쓰는지, 배치 작업은 어떻게 분리할지, 외부 트래픽은 어떤 방식으로 받을지까지 다시 봐야 합니다. 저도 홈랩과 운영 환경에서 비슷한 전환을 몇 번 해보니, 잘된 사례보다 중간에 막혔던 경험에서 더 많이 배우게 되더라고요. 이번 글에서는 온프레미스 클라우드 전환을 고민하시는 분들을 위해, 실제로 점검하는 순서대로 Cloud Run 도입 전략과 컨테이너 마이그레이션 포인트를 정리해보겠습니다.

    기존 VM 기반 운영에 익숙하셨다면 Cloud Run의 요청 기반 실행 모델이 처음엔 좀 낯설 수 있습니다. 저도 처음엔 감이 잘 안 왔는데, 구조를 한 번 이해하고 나니 운영 포인트가 오히려 단순해졌습니다. 핵심은 애플리케이션을 억지로 클라우드에 끼워 맞추는 게 아니라, Cloud Run이 안정적으로 실행할 수 있는 형태로 먼저 정리한 뒤 올리는 것입니다.

    Cloud Run 마이그레이션을 위한 온프레미스 클라우드 전환 아키텍처 다이어그램

    온프레미스 애플리케이션이 컨테이너 이미지로 패키징되고 Cloud Run, 데이터베이스, 로드밸런서로 연결되는 전체 흐름을 보여주는 개요 이미지입니다.

    1. 왜 Cloud Run 마이그레이션이 현실적인 선택인지

    쉽게 말해 Cloud Run은 컨테이너만 잘 만들면 실행 환경은 서비스가 관리해주는 플랫폼입니다. 서버 패치, 런타임 노드 관리, 자동 확장 같은 운영 부담을 꽤 덜 수 있죠. 그래서 기존 온프레미스 앱 중에서도 아래 조건에 맞는 서비스는 전환 효과가 큰 편입니다.

    • HTTP 또는 gRPC 기반으로 요청을 처리하는 웹 애플리케이션
    • 상태(state)를 메모리나 로컬 디스크가 아닌 외부 저장소로 분리할 수 있는 서비스
    • 트래픽 변동이 있어 자동 확장의 이점을 볼 수 있는 서비스
    • 배포 속도와 롤백 단순화가 중요한 팀 서비스

    반대로 항상 실행 중이어야 하는 장기 백그라운드 프로세스나, 로컬 파일시스템 의존도가 높은 구조라면 그대로 옮기기 어렵습니다. 이런 경우는 웹 서비스는 Cloud Run 서비스로, 배치성 작업은 Cloud Run Jobs 같은 다른 실행 방식으로 나눠 보는 편이 낫습니다. 결국 마이그레이션에서 제일 중요한 건 클라우드 기능을 많이 아는 게 아니라, 현재 앱이 어떤 전제 위에서 돌아가는지 정확히 파악하는 것입니다.

    2. Cloud Run 마이그레이션 전에 알아둘 개념 차이

    온프레미스에서는 보통 서버를 만들고, 프로세스를 띄워두고, 로그 위치를 정하고, 방화벽을 열고, 필요하면 리버스 프록시를 붙입니다. 그런데 Cloud Run은 접근 방식이 꽤 다릅니다.

    항목 온프레미스 앱 운영 Cloud Run 운영
    실행 단위 서버/VM 위 프로세스 컨테이너
    확장 방식 수동 증설 또는 별도 오토스케일링 요청량 기반 자동 확장
    상태 저장 로컬 디스크 활용 빈번 영속 데이터는 외부 저장소 권장
    배포 서버 접속 후 배포 또는 파이프라인 이미지 배포 중심
    네트워크 공개 방화벽, 프록시, 로드밸런서 직접 구성 Ingress 정책과 IAM으로 제어

    핵심은 두 가지입니다. 첫째, 애플리케이션은 지정된 포트에서 요청을 받아야 합니다. 둘째, 인스턴스의 영속 상태에 의존하면 안 됩니다. 로그는 stdout/stderr, 설정은 환경 변수, 민감한 값은 Secret Manager 같은 외부 비밀 저장소, 파일은 Cloud Storage 같은 외부 스토리지를 쓰는 방식이 잘 맞습니다.

    여기서 하나 짚고 갈 부분이 있습니다. Cloud Run 컨테이너 파일시스템은 쓰기가 가능하긴 하지만 영구 디스크가 아니라 인메모리 기반 임시 저장소라서, 인스턴스가 내려가면 데이터가 유지되지 않습니다. 임시 파일 정도는 괜찮지만 업로드 원본이나 세션 파일을 계속 거기에 두는 구조는 금방 문제를 만들더라고요.

    3. 마이그레이션 전에 꼭 하는 사전 점검

    실전 들어가기 전에 저는 항상 아래 체크리스트부터 봅니다. 이 단계 건너뛰면 뒤에서 더 오래 걸립니다.

    1. 프로세스 시작 방식 확인
      앱이 포그라운드로 실행되는지 확인합니다. 백그라운드로 포크되면 컨테이너 환경에서 예상과 다르게 동작할 수 있습니다.
    2. 포트 바인딩 방식 확인
      앱이 환경 변수로 주입된 포트를 사용할 수 있는지 봅니다. Cloud Run은 컨테이너에 PORT 환경 변수를 주입합니다.
    3. 세션/업로드 저장 위치 확인
      세션 파일, 업로드 파일, 캐시가 로컬 디스크에 쌓이면 장애 포인트가 됩니다.
    4. 환경 변수 정리
      DB 접속 정보, API 키, 운영 모드 값을 코드나 파일에 박아뒀다면 외부 설정으로 분리합니다.
    5. 헬스체크와 기동 시간 점검
      Cloud Run에서는 시작 프로브와 라이브니스 프로브를 설정할 수 있으니, 앱 기동 시간이 긴 편이면 같이 검토합니다.
    6. 장기 실행 작업 분리
      웹 요청 처리와 오래 걸리는 배치를 분리하지 않으면 타임아웃 때문에 고생할 수 있습니다. Cloud Run 서비스의 요청 타임아웃은 기본 5분이고 최대 60분까지 설정할 수 있습니다.

    혹시 지금 운영 중인 앱이 이런 상태이신가요? “서버 켜지면 systemd로 자동 실행되고, 로그는 /var/log 아래에 쌓이고, 업로드 파일은 /data에 저장한다.” 정말 흔한 구조입니다. 저도 많이 봤고 직접 운영도 했거든요. 그런데 이 구조를 그대로 온프레미스 클라우드 전환에 들이밀면 문제가 생기기 쉽습니다. 그래서 먼저 상태 의존성을 줄이는 게 중요합니다.

    4. 실전 구현 1: 앱을 컨테이너로 감싸기

    이제 실제 작업으로 들어가 보겠습니다. 예시는 Python Flask 기준이지만 Node.js, Go, Java도 원리는 같습니다. 중요한 건 프레임워크가 아니라 컨테이너 안에서 단일 프로세스로 뜨고, 지정 포트를 리스닝하는지입니다.

    4-1. 애플리케이션 코드에서 포트 환경 변수 받기

    import os
    from flask import Flask
    
    app = Flask(__name__)
    
    @app.route("/")
    def index():
        return {"status": "ok", "message": "hello from cloud run"}
    
    if __name__ == "__main__":
        port = int(os.environ.get("PORT", "8080"))
        app.run(host="0.0.0.0", port=port)

    여기서 중요한 포인트는 127.0.0.1이 아니라 0.0.0.0으로 바인딩해야 한다는 점입니다. Cloud Run은 컨테이너가 0.0.0.0에서 요청을 받기를 기대하거든요. 이거 한 번 놓치면 배포는 성공했는데 접속이 안 되는 전형적인 상황이 나옵니다. 저도 예전에 이걸로 로그만 한참 뒤졌습니다.

    4-2. Dockerfile 작성

    FROM python:3.11-slim
    
    WORKDIR /app
    
    COPY requirements.txt ./
    RUN pip install --no-cache-dir -r requirements.txt
    
    COPY . .
    
    ENV PORT=8080
    CMD ["python", "app.py"]

    프로덕션에서는 Gunicorn 같은 WSGI 서버를 두는 구성이 더 일반적입니다. 다만 여기서는 흐름을 설명하려고 단순 예시로 적었습니다. 실제 운영에서는 애플리케이션 특성에 맞는 프로세스 모델을 고르시면 됩니다.

    4-3. 로컬에서 컨테이너 실행 확인

    docker build -t my-onprem-app:local .
    docker run --rm -p 8080:8080 my-onprem-app:local
    curl http://localhost:8080/

    로컬에서 먼저 확인하는 이유는 단순합니다. Cloud Run 문제인지, 내 컨테이너 문제인지 먼저 구분해야 하거든요. 이 단계에서 응답이 안 오면 클라우드로 올려도 똑같이 막힙니다.

    5. Cloud Run 마이그레이션 배포 절차

    컨테이너가 정상 동작하면 이제 이미지를 레지스트리에 올리고 서비스를 배포합니다. 보통은 CI/CD에서 처리하지만, 처음 구조 검증할 때는 CLI로 한 번 직접 해보는 게 좋습니다. 해보면 서비스 계정, 리전, 이미지 태그 전략이 한 번에 정리되더라고요.

    PROJECT_ID="your-gcp-project"
    REGION="asia-northeast3"
    REPO="app-images"
    SERVICE="my-onprem-app"
    IMAGE="$REGION-docker.pkg.dev/$PROJECT_ID/$REPO/$SERVICE:manual-001"
    
    gcloud auth login
    gcloud config set project "$PROJECT_ID"
    gcloud artifacts repositories create "$REPO" \
      --repository-format=docker \
      --location="$REGION"
    
    gcloud auth configure-docker "$REGION-docker.pkg.dev"
    docker build -t "$IMAGE" .
    docker push "$IMAGE"
    
    gcloud run deploy "$SERVICE" \
      --image "$IMAGE" \
      --region "$REGION" \
      --allow-unauthenticated

    사내 서비스처럼 외부 공개가 필요 없다면 --allow-unauthenticated 대신 인증된 호출만 허용하는 구성을 검토하시면 됩니다. 이때는 Ingress 정책과 IAM을 같이 봐야 합니다. 인증을 막는 설정과 네트워크 진입 경로 설정은 역할이 다르니 묶어서 보는 게 좋습니다.

    Cloud Run 도입 과정의 컨테이너 마이그레이션 배포 흐름 이미지

    컨테이너 이미지가 Artifact Registry에 저장되고 Cloud Run 서비스로 배포되며 환경 변수와 시크릿이 주입되는 흐름을 설명하는 구성 이미지입니다.

    5-1. 환경 변수와 시크릿 분리

    운영하다 보면 제일 위험한 게 자격 증명을 이미지 안에 넣어버리는 겁니다. 이건 정말 피하셔야 합니다.

    gcloud run deploy "$SERVICE" \
      --image "$IMAGE" \
      --region "$REGION" \
      --set-env-vars APP_ENV=prod,LOG_LEVEL=info \
      --set-secrets DB_PASSWORD=db-password:latest

    설정값은 환경 변수로, 비밀번호나 토큰은 시크릿으로 나누는 습관을 들이면 운영이 훨씬 편해집니다. 나중에 키 교체할 때 체감이 확 옵니다. 이거 진짜 편하더라고요.

    5-2. 데이터베이스 연결은 어떻게 할까

    온프레미스 앱이 데이터베이스를 쓰고 있다면, 가장 먼저 볼 건 연결 방식입니다. 앱과 DB를 같은 서버에 붙여 놓았던 구조라면 분리가 필요합니다. 보통은 다음 두 방향 중 하나를 검토합니다.

    • 관리형 데이터베이스로 이전해 네트워크와 자격 증명을 정리한다
    • 기존 데이터베이스를 유지하되, 안전한 네트워크 경로와 연결 풀을 재설계한다

    중요한 건 애플리케이션 인스턴스 수가 늘어나도 DB 연결 수가 급격히 폭증하지 않게 하는 것입니다. Cloud Run은 빠르게 확장될 수 있어서, 연결 풀 값을 아무 생각 없이 잡으면 DB가 먼저 버거워질 수 있습니다.

    6. 운영 관점에서 꼭 챙겨야 할 설정

    배포가 됐다고 끝은 아닙니다. 실제로는 여기서부터 운영 품질이 갈립니다.

    6-1. 동시성 이해하기

    Cloud Run은 한 인스턴스가 여러 요청을 동시에 처리할 수 있습니다. CPU 바운드 작업인지, I/O 바운드 작업인지에 따라 적정값이 달라집니다. 기본 동시성은 콘솔 배포 기준 80이고, CLI나 Terraform으로 새 서비스를 만들 때는 vCPU 수에 따라 계산되는 기본값이 적용될 수 있습니다. 그래서 그냥 숫자 하나만 외워두기보다, 실제 지연 시간과 CPU 사용률을 보고 조정하는 편이 더 안전합니다.

    6-2. 타임아웃과 장기 작업 분리

    웹 요청 하나가 오래 걸린다면, 그 작업을 직접 처리하기보다 큐나 배치로 넘기는 게 낫습니다. 예를 들어 대용량 리포트 생성, 비디오 인코딩, 대량 동기화 작업은 웹 요청 경로에서 분리하는 편이 안전합니다. 서비스 타임아웃은 기본 5분이고 최대 60분까지 늘릴 수 있지만, 길게 준다고 구조 문제가 사라지진 않더라고요.

    6-3. 로그는 파일이 아니라 표준 출력으로

    온프레미스에선 로그 파일을 tail로 보던 습관이 강하죠. 그런데 컨테이너 환경에서는 애플리케이션 로그를 stdout/stderr로 내보내는 방식이 관리가 훨씬 쉽습니다. 로그 포맷도 가능하면 JSON 같은 구조화 로그로 맞춰두면 검색이 편합니다.

    6-4. 파일 업로드는 외부 스토리지로

    앱이 업로드 파일을 로컬 경로에 저장하고 있다면 구조를 바꾸는 게 좋습니다. Cloud Run의 로컬 파일시스템은 영속 저장소가 아니라 임시 저장소에 가깝기 때문입니다. 이 부분은 컨테이너 마이그레이션에서 자주 놓치는 항목 중 하나입니다.

    7. 실제로 많이 겪는 문제와 트러블슈팅

    이 섹션은 정말 경험담 위주입니다. 문서만 읽으면 잘 안 보이는데, 실제로 해보면 여기서 많이 막힙니다.

    • 문제 1: 배포는 성공했는데 접속이 안 됨
      원인으로는 포트 바인딩 오류, 0.0.0.0 미사용, 애플리케이션 시작 실패가 많습니다. 로그에서 startup 관련 메시지를 먼저 확인합니다.
    • 문제 2: 특정 요청에서만 500 에러 발생
      로컬 파일 경로에 쓰기 시도하는 경우가 많습니다. 업로드, 임시 파일, 세션 저장 위치를 다시 확인합니다.
    • 문제 3: 예상보다 응답이 느림
      콜드 스타트, 외부 DB 연결 지연, 동시성 설정 부적합을 의심합니다.
    • 문제 4: 갑자기 DB 연결이 부족해짐
      오토스케일링과 연결 풀 값이 충돌할 수 있습니다. 인스턴스 수와 DB 허용 연결 수를 같이 봐야 합니다.
    • 문제 5: 배치 작업이 중간에 끊김
      HTTP 요청 처리 경로에 오래 걸리는 작업을 넣은 경우가 많습니다. 작업 큐나 Cloud Run Jobs 기반으로 재설계하는 편이 낫습니다.

    제가 한 번은 기존 앱이 이미지 업로드 후 썸네일까지 같은 요청 안에서 만들고, 결과 파일도 로컬 디스크에 저장하는 구조를 본 적이 있습니다. 로컬 테스트에선 멀쩡했는데 클라우드에 올리니 간헐적으로 실패하더라고요. 처음엔 네트워크 문제인 줄 알았는데, 결국 저장소 구조가 원인이었습니다. 업로드 원본과 결과물을 외부 스토리지로 빼고 나서야 안정화됐습니다. 그때 진짜 숨이 좀 놓였죠.

    8. 검증 방법: 전환 후 무엇을 확인해야 하나요?

    배포가 끝난 뒤에는 기능만 보지 말고 운영 지표도 같이 봐야 합니다. 최소한 아래 항목은 확인하시는 걸 권장드립니다.

    1. 기능 검증
      핵심 API, 로그인, 업로드, 외부 연동을 실제 시나리오로 테스트합니다.
    2. 로그 검증
      오류 로그가 구조적으로 남는지, 요청 추적이 가능한지 확인합니다.
    3. 성능 검증
      트래픽이 몰릴 때 응답 시간이 급격히 늘지 않는지 봅니다.
    4. 확장 검증
      동시 요청 상황에서 인스턴스 확장이 의도대로 이뤄지는지 확인합니다.
    5. 운영 절차 검증
      재배포, 롤백, 설정 변경, 시크릿 교체가 얼마나 단순해졌는지 봅니다.
    SERVICE_URL="https://your-service-url"
    
    curl -i "$SERVICE_URL/"
    
    for i in $(seq 1 20); do
      curl -s "$SERVICE_URL/" &
    done
    wait

    초기 검증은 이렇게 단순하게 시작해도 충분합니다. 중요한 건 숫자 자체보다, 어떤 조건에서 앱이 흔들리는지 빨리 파악하는 것입니다. 운영은 결국 재현성과 관찰 가능성 싸움이거든요. 관련해서 이전 글의 컨테이너 이미지 최적화 내용이 있다면 여기와 함께 내부 링크로 연결해두는 것도 SEO와 체류 시간 측면에서 꽤 도움이 됩니다.

    Cloud Run 마이그레이션 이후 검증을 위한 운영 대시보드 이미지

    배포 이후 요청 성공률, 응답 시간, 로그 확인 과정을 한눈에 볼 수 있는 운영 검증용 대시보드 이미지입니다.

    9. 결과적으로 무엇이 좋아졌나

    모든 앱이 Cloud Run에 맞는 건 아닙니다. 이건 분명합니다. 하지만 웹 API, 내부 업무 서비스, 이벤트 기반 백엔드처럼 구조만 잘 맞추면 얻는 장점이 꽤 큽니다.

    • 배포 단위가 서버가 아니라 이미지가 되면서 재현성이 좋아집니다
    • 운영 환경 표준화가 쉬워집니다
    • 트래픽 변화 대응이 단순해집니다
    • 서버 패치와 런타임 관리 부담이 줄어듭니다
    • 개발팀과 인프라팀의 경계가 조금 더 명확해집니다
    전환 전 전환 후
    서버 접속 후 수동 점검 이미지 중심 배포와 설정 관리
    로컬 로그 파일 추적 중앙화된 로그 확인
    확장 시 서버 증설 고민 요청량 기반 확장 고려
    앱과 인프라 설정이 강하게 결합 컨테이너 기준으로 역할 분리

    물론 대가도 있습니다. 앱이 상태 비저장에 가깝게 움직이도록 구조를 손봐야 하고, 기존 운영 습관도 일부 바꿔야 합니다. 그런데 이 과정을 한 번 거치고 나면 이후 운영이 꽤 가벼워집니다. 저는 그게 가장 크게 느껴졌습니다.

    온프레미스 클라우드 전환과 Cloud Run 도입 비교 인포그래픽

    온프레미스 운영 방식과 Cloud Run 도입 이후의 배포, 확장, 로그 관리 차이를 요약한 비교 인포그래픽입니다.

    10. 마무리: 성공적인 Cloud Run 도입을 위한 정리

    Cloud Run 마이그레이션의 핵심은 기술 자체보다 애플리케이션 구조를 얼마나 잘 정리하느냐에 달려 있습니다. 서버를 옮기는 작업으로 보면 자꾸 꼬이고, 컨테이너가 요청을 안정적으로 처리하도록 재구성하는 작업으로 보면 길이 보입니다. 저도 처음엔 단순 배포 문제라고 생각했는데, 막상 해보니 설정 파일, 로그, 세션, 업로드, 배치 작업까지 전부 연결돼 있더라고요.

    정리하면 이렇습니다.

    1. 앱이 PORT 환경 변수로 실행되게 바꿉니다.
    2. 상태와 파일 저장을 외부로 분리합니다.
    3. 시크릿과 환경 변수를 이미지에서 분리합니다.
    4. 동시성, 타임아웃, DB 연결 수를 함께 봅니다.
    5. 배포보다 검증 자동화를 먼저 습관화합니다.

    혹시 지금 온프레미스 앱을 들고 계시고 어디서부터 손대야 할지 막막하셨다면, 위 순서대로만 체크해도 꽤 정리가 됩니다. 다음 글에서는 Cloud Run 앞단에 로드밸런서와 도메인, 그리고 내부 전용 서비스 노출 전략까지 이어서 다뤄보셔도 좋겠습니다. 이전 글이 있다면 여기서 자연스럽게 내부 링크를 걸어 주제 클러스터를 만드는 것도 추천드립니다.

    11. 자주 묻는 질문

    Q1. 기존 온프레미스 앱을 수정 없이 그대로 올릴 수 있나요?

    대부분은 어렵습니다. 특히 로컬 파일 저장, 백그라운드 데몬 방식, 고정 포트 전제는 수정이 필요할 가능성이 큽니다.

    Q2. Cloud Run 도입은 작은 팀에도 괜찮나요?

    오히려 작은 팀일수록 운영 단순화 효과를 크게 느끼는 경우가 많습니다. 다만 앱 구조가 맞아야 합니다.

    Q3. 모든 마이크로서비스에 적합한가요?

    아닙니다. 요청 기반 처리와 컨테이너 실행 모델에 잘 맞는 서비스부터 시작하시는 게 안전합니다.

    Q4. 마이그레이션 첫 단계는 무엇이 가장 중요할까요?

    현재 앱의 상태 의존성과 파일 저장 습관을 파악하는 겁니다. 이거 놓치면 뒤에서 계속 발목을 잡습니다.

  • [NAS] TrueNAS SCALE 앱 배포: Docker 컨테이너부터 관리까지

    NAS가 단순 저장소였던 시대는 끝났습니다

    홈랩을 운영하다 보면 어느 순간 이런 생각이 드시지 않나요? “NAS에 데이터만 넣어두기엔 너무 아깝다.” 저도 처음엔 그냥 파일 서버로 쓰다가, 어느 날 Plex를 띄워보고 나서 생각이 완전히 바뀌었거든요. 그 이후로 TrueNAS SCALE 위에서 십수 개의 서비스를 돌리고 있습니다.

    근데 솔직히 말씀드리면, 처음에 TrueNAS SCALE 앱 시스템을 접했을 때 꽤 헷갈렸어요. 버전마다 배포 방식이 달라지고, 한글 자료도 많지 않아서 삽질을 꽤 했거든요. 이번 글에서는 제가 직접 겪어온 경험을 바탕으로, TrueNAS SCALE에서 앱을 배포하고 관리하는 방법을 처음부터 끝까지 정리해 드리겠습니다.

    TrueNAS SCALE 위에서 여러 컨테이너 앱이 동작하는 홈랩 전체 아키텍처. 스토리지, 네트워킹, 앱 레이어가 통합된 모습입니다.

    TrueNAS SCALE 앱 시스템이 바뀐 이유

    여기서 중요한 역사 이야기를 하나 해야 할 것 같아요. TrueNAS SCALE의 앱 시스템은 버전을 거치면서 꽤 큰 변화가 있었거든요.

    초기 버전(Angelfish, Bluefin, Cobia 등)에서는 K3s(경량 쿠버네티스)를 기반으로 앱을 운영했습니다. 쿠버네티스를 쓴다니까 좋아 보이긴 했는데, 일반 홈랩 사용자 입장에서는 진입 장벽이 꽤 높았어요. Helm 차트 구조도 이해해야 하고, 리소스 관리가 복잡하다는 불만도 많았고요.

    그래서 iXsystems는 Electric Eel(24.10) 버전부터 TrueNAS SCALE 앱 시스템을 Docker Compose 기반으로 전면 바꿨습니다. 쿠버네티스를 걷어내고 훨씬 직관적인 Docker Compose 방식을 채택한 거죠. 솔직히 처음엔 “아니 왜 바꾸는 거야”라고 생각했는데, 써보니 훨씬 낫더라고요.

    • 이전(~Dragonfish): K3s + Helm Chart 기반, TrueCharts 커뮤니티 카탈로그 의존도 높음
    • 이후(Electric Eel~): Docker Compose 기반, 공식 카탈로그 + 커스텀 컨테이너 지원

    이 글은 Electric Eel 이후의 Docker Compose 기반 앱 시스템을 중심으로 설명합니다. 혹시 구버전 사용 중이시라면 업그레이드를 먼저 하시길 권장합니다.

    시작 전 준비사항 체크

    본격적으로 TrueNAS SCALE 앱 배포에 들어가기 전에 확인해야 할 것들이 있어요. 저도 이걸 안 하고 바로 들어갔다가 중간에 막혀서 처음부터 다시 한 경험이 있거든요 😅

    1. 풀(Pool) 설정 확인: 앱 데이터를 저장할 ZFS 풀이 생성되어 있어야 합니다. 저는 SSD 별도 풀을 만들어서 앱 전용으로 씁니다.
    2. Apps 전용 풀 지정: TrueNAS SCALE에서 Apps 탭으로 이동하면, 앱 데이터를 저장할 풀을 처음 한 번 지정하게 돼 있어요. 나중에 바꾸기 귀찮으니 처음에 잘 골라두세요.
    3. 네트워크 설정: 외부에서 접근이 필요한 앱이라면, 공유기 포트 포워딩이나 Reverse Proxy(리버스 프록시) 설정도 미리 생각해두면 좋습니다.
    4. TrueNAS SCALE 버전: 앞서 말씀드린 것처럼, 가능하면 Electric Eel(24.10) 이상으로 업그레이드하고 시작하세요.

    TrueNAS SCALE 앱 카탈로그에서 첫 번째 앱 설치하기

    준비가 됐다면 이제 실전입니다! TrueNAS SCALE 웹 UI 왼쪽 메뉴에서 Apps를 클릭하세요. 처음 접속하면 앱 풀(pool)을 선택하라고 나옵니다. 앞서 준비한 풀을 선택하면 돼요.

    앱 화면으로 들어오면 Discover Apps 버튼이 보입니다. 여기서 공식 카탈로그에 등록된 앱들을 검색하고 설치할 수 있어요. Plex, Nextcloud, Jellyfin, Home Assistant 같은 인기 홈랩 앱들이 대부분 등록되어 있습니다.

    예시로 Jellyfin(미디어 서버)을 설치해볼게요.

    1. Discover Apps에서 “Jellyfin” 검색
    2. 앱 카드 클릭 → Install 버튼 클릭
    3. 설정 화면에서 주요 항목 입력:
      • Application Name: jellyfin (기본값 그대로)
      • Network Configuration: 포트 번호 설정 (기본 8096)
      • Storage Configuration: 미디어 파일이 있는 데이터셋 경로 지정
    4. Install 클릭 후 배포 완료 대기

    설치가 완료되면 앱 목록에 Jellyfin이 나타나고, http://NAS-IP:8096으로 접속할 수 있어요. 드디어 됐다! 라는 느낌이 이때 오더라고요 🎉

    TrueNAS SCALE Apps 탭의 카탈로그 화면. Discover Apps에서 원하는 앱을 검색하고 GUI로 간편하게 설치할 수 있습니다.

    Docker 컨테이너 직접 배포하기 (Custom App)

    카탈로그에 없는 앱을 써야 할 때가 있죠. 저도 특정 오픈소스 툴을 올리고 싶은데 카탈로그에 없을 때, 직접 Docker 이미지를 지정해서 배포하는 방법을 씁니다. Docker on TrueNAS를 활용하는 방식이에요.

    Discover Apps 화면 오른쪽 위를 보면 Custom App 버튼이 있습니다. 이걸 누르면 Docker Compose 방식으로 직접 컨테이너를 정의할 수 있는 화면이 나와요.

    예시로 Uptime Kuma(서비스 모니터링 앱)를 커스텀 앱으로 배포해볼게요.

    # Custom App 설정에서 사용하는 Docker Compose 예시
    services:
      uptime-kuma:
        image: louislam/uptime-kuma:latest
        container_name: uptime-kuma
        ports:
          - "3001:3001"
        volumes:
          - /mnt/pool/apps/uptime-kuma:/app/data
        restart: unless-stopped

    TrueNAS SCALE의 Custom App UI에서는 이걸 GUI 폼으로 입력할 수 있어요. 직접 YAML을 쓰는 방식도 지원되고, 폼 형태로도 입력 가능합니다.

    핵심 입력 항목들은 이렇습니다:

    항목 설명 예시
    Image Repository Docker Hub 이미지 이름 louislam/uptime-kuma
    Image Tag 버전 태그 latest 또는 1.23.x
    Container Port 컨테이너 내부 포트 3001
    Node Port 호스트에서 접근할 포트 3001
    Host Path TrueNAS 데이터셋 경로 /mnt/pool/apps/uptime-kuma
    Mount Path 컨테이너 내부 마운트 경로 /app/data

    💡 팁: 볼륨 경로는 반드시 미리 ZFS 데이터셋으로 만들어두고 마운트하는 게 좋습니다. 그냥 디렉토리 경로를 쓰면 나중에 스냅샷 관리가 안 되거든요. 이거 처음에 몰라서 나중에 다 옮긴 적이 있습니다 ㅎㅎ.

    환경 변수와 네트워크 설정 팁

    컨테이너를 배포할 때 환경 변수(Environment Variable) 설정이 필요한 경우가 많아요. 예를 들어 데이터베이스 비밀번호, API 키 같은 것들이요.

    Custom App 설정 화면에서 Environment Variables 섹션을 찾으면 Key-Value 형태로 추가할 수 있습니다.

    # 환경 변수 예시 (Nextcloud 같은 경우)
    MYSQL_DATABASE=nextcloud
    MYSQL_USER=ncuser
    MYSQL_PASSWORD=your_secure_password
    NEXTCLOUD_ADMIN_USER=admin
    NEXTCLOUD_ADMIN_PASSWORD=admin_password

    네트워크 관련해서 한 가지 더 말씀드리면, TrueNAS SCALE에서 여러 앱이 서로 통신해야 할 때(예: 웹 앱 + DB 컨테이너) 같은 Docker 네트워크에 묶어야 해요. Custom App에서 Network Configuration 섹션에서 네트워크를 지정할 수 있습니다. 이 부분은 처음엔 좀 헷갈릴 수 있는데, 같은 앱 스택이라면 동일한 사용자 정의 네트워크를 사용하면 돼요.

    ⚠️ 트러블슈팅: 자주 겪는 문제들

    13년 동안 인프라 일을 하면서 깨달은 건, 문서대로 안 되는 게 정상이라는 거예요 ㅎㅎ. TrueNAS SCALE 앱 배포하면서 자주 만나는 문제들과 해결법을 공유합니다.

    문제 1: 앱이 Deploying 상태에서 멈춤

    가장 흔한 문제예요. 배포 눌렀는데 한참 동안 Deploying 상태에서 안 바뀌는 경우.

    원인 대부분은 이미지 풀(pull) 실패입니다. 이미지 이름이 틀렸거나, NAS가 인터넷에 제대로 연결 안 됐을 때 주로 발생해요.

    # TrueNAS Shell에서 컨테이너 로그 확인
    docker logs [컨테이너_이름]
    
    # 이미지가 제대로 받아졌는지 확인
    docker images | grep [앱_이름]

    문제 2: 볼륨 마운트 권한 오류

    앱은 뜨는데 데이터를 못 쓰는 경우가 있어요. 특히 컨테이너가 특정 UID로 실행되는데, 마운트한 경로의 소유자가 다를 때 발생합니다.

    # TrueNAS Shell에서 권한 확인 및 수정
    ls -la /mnt/pool/apps/uptime-kuma
    
    # 필요하다면 소유자 변경 (999는 예시 UID, 앱마다 다름)
    chown -R 999:999 /mnt/pool/apps/uptime-kuma

    💡 팁: Docker Hub의 해당 이미지 문서에 어떤 UID로 실행되는지 나와 있는 경우가 많아요. 미리 확인하고 데이터셋 권한을 맞춰두면 삽질을 줄일 수 있습니다.

    문제 3: 포트 충돌

    “이미 사용 중인 포트”라는 오류가 뜨는 경우요. 앱들이 기본 포트가 겹칠 때 발생합니다. 예를 들어 여러 웹 앱이 다 80번 포트를 쓰려 하면 충돌이 나죠.

    해결책은 단순합니다. 앱 설치 시 Node Port를 겹치지 않게 다른 번호로 바꿔주면 돼요. 저는 개인적으로 앱별로 포트 번호를 스프레드시트에 정리해두고 쓰고 있습니다. 처음엔 귀찮아 보여도, 앱이 10개 이상 넘어가면 이게 필수예요.

    TrueNAS SCALE 앱 관리 및 업데이트

    설치하고 끝이 아니죠. 앱 관리도 중요합니다. TrueNAS SCALE에서 설치된 앱의 업데이트는 Apps 탭 → Installed 화면에서 할 수 있어요.

    업데이트 가능한 앱이 있으면 배지(badge)가 표시되고, Update All 버튼으로 한 번에 업데이트도 가능합니다. 근데 저는 한 번에 다 올리기보다는, 중요한 앱은 하나씩 올리고 확인하는 편이에요. 한 번에 다 올렸다가 뭔가 깨지면 어디서 문제가 생긴 건지 찾기 어렵거든요.

    앱 데이터 백업은 ZFS 스냅샷을 활용하는 게 최고입니다. 앱 데이터를 담은 데이터셋에 정기 스냅샷을 걸어두면, 앱이 망가졌을 때 스냅샷으로 롤백이 가능해요. 이게 TrueNAS SCALE을 쓰는 가장 큰 이유 중 하나라고 생각합니다.

    TrueNAS SCALE Apps의 Installed 탭. 실행 중인 앱 상태, 업데이트 가능 여부, 리소스 사용량을 한눈에 확인할 수 있습니다.

    ✅ 실전 구성: 제 홈랩 앱 운영 사례

    참고로 현재 제가 TrueNAS SCALE 위에서 돌리고 있는 주요 앱들을 공유할게요. 비슷한 구성을 계획하시는 분들께 도움이 될 것 같아서요.

    앱 이름 용도 설치 방법
    Jellyfin 미디어 서버 (영화, 음악) 공식 카탈로그
    Nextcloud 개인 클라우드 스토리지 공식 카탈로그
    Home Assistant 스마트홈 허브 공식 카탈로그
    Uptime Kuma 서비스 모니터링 Custom App
    Vaultwarden 비밀번호 관리자 Custom App
    Nginx Proxy Manager 리버스 프록시 + SSL Custom App

    모두 ZFS 데이터셋에 데이터를 저장하고, 매일 새벽 3시에 스냅샷이 자동으로 찍히도록 설정해뒀습니다. 덕분에 앱 업데이트 실패나 설정 꼬임 같은 상황에서도 걱정 없이 복구할 수 있어요. 실제로 Home Assistant 업데이트 후 통합 설정이 날아갔을 때 스냅샷으로 5분 만에 복구한 적도 있습니다 😅

    현재 운영 중인 홈랩의 앱 구성 요약. TrueNAS SCALE 위에서 미디어, 클라우드, 모니터링, 보안 앱들이 유기적으로 연동되는 모습입니다.

    마무리: TrueNAS SCALE 앱 배포, 이렇게 시작하세요

    오늘 다룬 내용을 간단히 정리해볼게요.

    • ✅ TrueNAS SCALE은 Electric Eel(24.10)부터 Docker Compose 기반 앱 시스템으로 전환됨
    • ✅ 공식 카탈로그 앱은 GUI로 손쉽게 설치 가능
    • ✅ 카탈로그에 없는 앱은 Custom App 기능으로 Docker 이미지 직접 지정 배포
    • ✅ 볼륨은 반드시 ZFS 데이터셋으로 분리해서 관리할 것
    • ✅ 스냅샷 정책을 함께 설정해두면 백업/복구가 정말 편함

    처음 TrueNAS SCALE 앱 시스템을 접하면 설정 항목이 많아서 막막할 수 있어요. 근데 한 번 익숙해지면 정말 편합니다. 스토리지와 앱을 같은 플랫폼에서 관리한다는 게 이렇게 편할 줄 몰랐다는 생각이 드실 거예요.

    다음 글에서는 Nginx Proxy Manager와 Let’s Encrypt를 이용한 HTTPS 설정을 다룰 예정입니다. 외부에서 도메인으로 안전하게 접근하는 방법인데, 홈랩을 한 단계 업그레이드하고 싶은 분들께 유용할 거예요. 이전 글에서 다뤘던 ZFS 데이터셋 구성과 함께 보시면 더 도움이 됩니다.

    궁금한 점이 있으면 댓글로 남겨주세요. 같이 삽질하며 발전하는 홈랩 라이프, 응원합니다! 🎉