Docker Compose 로 설치
설정 파일(/etc/geo-mlops/app.env)을 만들고 docker compose 로 스택을 띄운 뒤, 헬스체크로 확인합니다
Geo-MLOps 는 소스 디렉터리의 docker-compose.yml 하나로 뜹니다. 앱 이미지는 같은 디렉터리의 Dockerfile 로 그 자리에서 빌드되고, Postgres·RustFS·Prometheus·Alertmanager·패키지 캐시가 함께 올라옵니다. 이 페이지에서는 소스가 /opt/geo-mlops 에 있다고 가정합니다.
1. 설정 파일 만들기
앱의 비밀 설정은 소스 디렉터리 밖의 /etc/geo-mlops/app.env 에 둡니다. 소스를 새로 받거나 자동 배포가 소스 디렉터리를 정리해도 이 파일은 지워지지 않습니다. 형식은 GEO_MLOPS_키=값 한 줄씩입니다.
-
디렉터리와 파일을 만들고 권한을 좁힙니다.
sudo install -d -m 0750 /etc/geo-mlops sudo install -m 0600 /dev/null /etc/geo-mlops/app.env -
서명 키·pepper·웹훅 토큰으로 쓸 난수를 서로 다르게 세 개 만듭니다.
openssl rand -hex 32 # GEO_MLOPS_AUTH_SECRET 용 openssl rand -hex 32 # GEO_MLOPS_EDGE_TOKEN_PEPPER 용 openssl rand -hex 32 # GEO_MLOPS_ALERTMANAGER_WEBHOOK_TOKEN 용 -
sudoedit /etc/geo-mlops/app.env로 아래 내용을 넣습니다.<your-secret>자리에 방금 만든 값을 넣고, 주소는 실제 도메인으로 바꿉니다.
# --- 서명 키 (설치 직후 반드시 바꾼다) ---------------------------------
GEO_MLOPS_AUTH_SECRET=<your-secret>
GEO_MLOPS_EDGE_TOKEN_PEPPER=<your-secret>
# --- 첫 전역 관리자 ----------------------------------------------------
GEO_MLOPS_RBAC_BOOTSTRAP_ADMIN_EMAIL=admin@example.com
GEO_MLOPS_RBAC_BOOTSTRAP_ADMIN_PASSWORD=<your-secret>
# --- 개발용 시드 끄기 (운영 설치에서는 반드시 비운다) ---------------------
GEO_MLOPS_RBAC_BOOTSTRAP_TENANTS=
GEO_MLOPS_RBAC_BOOTSTRAP_ROLE_PASSWORD=
# --- Alertmanager 웹훅 공유 비밀 ----------------------------------------
GEO_MLOPS_ALERTMANAGER_WEBHOOK_TOKEN=<your-secret>
# --- 접속 주소 ----------------------------------------------------------
# HTTPS 로 접속하면 true(기본값). 사내망에서 http:// 로만 쓴다면 false.
GEO_MLOPS_AUTH_COOKIE_SECURE=true
# 웹 화면이 다른 도메인에서 API 를 부를 때, 그 화면의 주소
GEO_MLOPS_CORS_ALLOW_ORIGINS=https://mlops.example.com
# MLflow 가 받아 줄 Host 헤더 (아래 설명 참고)
GEO_MLOPS_MLFLOW_ALLOWED_HOSTS=api.mlops.example.com,localhost:*,127.0.0.1:*,10.*
각 줄의 뜻과 바꾸지 않으면 무엇이 위험한지는 필수 보안 설정에서 자세히 다룹니다. 여기서 꼭 알아 둘 것만 적습니다.
RBAC_BOOTSTRAP_TENANTS와RBAC_BOOTSTRAP_ROLE_PASSWORD를 비워 두는 이유: 기본값은 개발용입니다. 비우지 않으면 첫 기동 때 개발용 테넌트와 역할별 데모 계정(비밀번호가 기본값)이 만들어집니다. 시드는 기동할 때마다 돌기 때문에, 나중에 그 테넌트를 지워도 다음 재시작 때 다시 생깁니다. 첫 기동 전에 비워 두세요. 테넌트는 설치 뒤 화면에서 만듭니다.MLFLOW_ALLOWED_HOSTS: 내장 MLflow 는 DNS 리바인딩(주소를 속여 브라우저가 내부 서버에 요청하게 만드는 공격)을 막으려고Host헤더를 검사합니다. 모르는 호스트에는 403 을 돌려줍니다. 비워 두면 localhost 와 사설 IP(10.*, 192.168.* 등)만 통과합니다. 값을 지정하면 이 기본 목록이 통째로 대체되므로, 도메인과 함께localhost:*,127.0.0.1:*과 빌드 파드가 쓰는10.*도 넣어야 합니다. 도메인으로 MLflow 를 쓰지 않는다면 이 줄은 빼도 됩니다.
2. Alertmanager 웹훅 맞추기
monitoring/alertmanager.yml 은 Alertmanager 가 경보를 앱으로 되돌려 보내는 주소를 담고 있습니다. 앱은 GEO_MLOPS_ALERTMANAGER_WEBHOOK_TOKEN 이 비어 있으면 웹훅을 503 으로 거절하고, 값이 틀리면 401 을 돌려줍니다. 두 곳을 고칩니다.
url의 포트가 앱 포트 10000 인지 확인합니다. 배포본에:8000으로 적혀 있으면:10000으로 고칩니다.- 주석 처리된
http_config.headers블록을 풀고,app.env에 넣은 것과 같은 값을 넣습니다.
receivers:
- name: geo-mlops-webhook
webhook_configs:
- url: http://host.docker.internal:10000/api/v1/integrations/alertmanager/webhook
send_resolved: true
http_config:
headers:
X-Webhook-Token: <your-secret> # GEO_MLOPS_ALERTMANAGER_WEBHOOK_TOKEN 과 같은 값
3. 스택 띄우기
-
이미지를 빌드하고 모든 서비스를 띄웁니다. 첫 빌드는 인터넷에서 패키지를 받으므로 몇 분 걸립니다.
docker compose -f /opt/geo-mlops/docker-compose.yml up -d --build --remove-orphans -
앱 컨테이너가
healthy가 될 때까지 기다립니다(첫 기동은 30초 남짓).docker inspect --format='{{.State.Health.Status}}' geo-mlops-app docker compose -f /opt/geo-mlops/docker-compose.yml ps -
두 헬스체크를 부릅니다. 둘 다 인증 없이 열려 있습니다.
curl -s http://localhost:10000/api/v1/health # {"status":"ok","version":"…"} curl -s http://localhost:10000/api/v1/readyz # {"status":"ok","database":"up","object-store":"up","prometheus":"up","alertmanager":"up"}
/api/v1/health는 프로세스가 살아 있는지만 봅니다(의존 서비스를 건드리지 않음). Compose 의 헬스체크가 이것을 씁니다./api/v1/readyz는 Postgres·오브젝트 저장소·Prometheus·Alertmanager 에 실제로 붙어 봅니다. 하나라도down이면 전체가degraded입니다. 로드밸런서의 준비 확인에는 이쪽을 씁니다.
서버가 떠 있으면 http://<서버>:10000/docs 에서 API 문서를 볼 수 있습니다.
4. 첫 기동에서 저절로 일어나는 일
따로 명령을 칠 필요가 없습니다. 앱 컨테이너의 start-uvicorn.sh 와 앱 시작 과정이 다음을 합니다.
| 순서 | 하는 일 | 로그에서 확인 |
|---|---|---|
| 1 | DB 마이그레이션 alembic upgrade head (실패하면 서버가 뜨지 않음) | Running upgrade … |
| 2 | 부트스트랩 관리자 계정 생성 + 전역 관리자 권한 부여 | created bootstrap admin account admin@example.com |
| 3 | 공유 빌드 이미지 자리(학습 런타임·스테이저·서빙 빌더) 생성 | (따로 없음) |
| 4 | 테넌트별 MLflow 워크스페이스 준비, 끊긴 작업 정리 | (따로 없음) |
docker logs geo-mlops-app 2>&1 | grep -E "upgrade|bootstrap|seeded"
부트스트랩 관리자는 계정이 없을 때만 만들어집니다. 이미 있는 계정의 비밀번호는 바꾸지 않습니다. 시드가 실패해도 서버는 뜨고, 실패 내용은 startup seeding failed 로 로그에 남습니다.
5. 앱 컨테이너의 메모리 상한
앱 컨테이너는 기본 16 GB 로 제한됩니다(넘으면 커널이 앱만 강제로 끝내고, Compose 의 restart 설정이 다시 띄웁니다). GPU 가 시스템 메모리를 함께 쓰는 장비에서는 학습 파드 몫을 남기려는 장치입니다. 바꾸려면 Compose 를 부를 때 셸 변수로 줍니다.
GEO_APP_MEM_LIMIT=24g docker compose -f /opt/geo-mlops/docker-compose.yml up -d
6. 웹 화면 올리기
웹 화면(프론트엔드)은 앱과 따로 배포하는 정적 파일 묶음입니다. 빌드할 때 API 주소를 번들 안에 넣습니다(VITE_API_BASE_URL). 두 가지 배치 중 하나를 고릅니다.
| 배치 | 빌드 값 | 앱 설정 |
|---|---|---|
같은 도메인: 웹 서버가 정적 파일을 서빙하고 /api·/auth·/users·/mlflow·/v2 를 10000 으로 넘김 | VITE_API_BASE_URL= (비움) | CORS 설정 불필요 |
다른 도메인(예: 화면 https://mlops.example.com, API https://api.mlops.example.com) | VITE_API_BASE_URL=https://api.mlops.example.com | GEO_MLOPS_CORS_ALLOW_ORIGINS=https://mlops.example.com |
# 프론트엔드 소스 디렉터리에서
npm ci
VITE_API_BASE_URL=https://api.mlops.example.com npm run build # 결과: dist/
dist/ 를 정적 호스팅(웹 서버, CDN 등)에 올립니다. 화면 주소(/training, /system-health …)는 모두 브라우저 쪽 라우팅이므로, 없는 경로는 index.html 을 돌려주도록(SPA fallback) 설정해야 새로고침해도 404 가 나지 않습니다.
7. 부속 서비스의 기본 계정
Compose 파일의 Postgres(mlflow/mlflow)와 RustFS(rustfsadmin/rustfsadmin) 계정은 파일에 고정되어 있고, 포트(5433·9000·9001)는 서버의 모든 주소에 열립니다. 방화벽으로 이 포트들을 서버 밖에서 막으세요. 계정을 바꾸려면 Compose 파일과 app.env 의 해당 설정(GEO_MLOPS_DATABASE_URL, GEO_MLOPS_MLFLOW_BACKEND_URI, GEO_MLOPS_MLFLOW_S3_ACCESS_KEY_ID, GEO_MLOPS_MLFLOW_S3_SECRET_ACCESS_KEY)과 백업 설정을 함께 바꿔야 합니다.
자주 쓰는 명령
C="docker compose -f /opt/geo-mlops/docker-compose.yml"
$C ps # 상태
$C logs -f --tail=100 app # 앱 로그
$C up -d app # app.env 를 고친 뒤: 설정을 다시 읽어 앱만 재생성
$C down # 멈춤 (데이터 유지)
다음: k3s·GPU 준비