본문으로 건너뛰기

Notebook ↔ Git ↔ MLflow 사용자 가이드

GenD 의 JupyterLab / VS Code 환경은 내부 Gitea 와 자동 통합되어 있습니다. 본 가이드는 사용자 매뉴얼 없이도 git clone → 작업 → push 까지 마칠 수 있도록 흐름을 정리합니다. 운영자 가이드는 Git Service Operator Guide 를 참조하세요.

무엇이 자동으로 되어 있나

JupyterHub 가 여러분의 single-user 서버를 띄울 때, lifecycle hook 이 gend-git-bootstrap.sh 를 자동 실행해 다음을 시드합니다 (수동 개입 불필요):

  1. ~/.gitconfig — author identity (Keycloak preferred_username)
  2. Keycloak ↔ Gitea 사용자 매핑 (POST /api/v1/git/users/sync)
  3. 60 분짜리 단명 PAT 발급 (POST /api/v1/git/tokens)
  4. ~/.git-credentials — HTTPS push/pull 자동 인증

/proc/<jupyter-pid>/cwd 첫 번째 터미널에서 cat ~/.gitconfig 로 확인 가능합니다. 실패 시 [gend-git-bootstrap] FAIL — ... 로그가 stderr 에 남고, 사용자는 수동으로 Gitea UI → Settings → Applications 에서 PAT 를 발급해 대체할 수 있습니다.

빠른 시작

1. 새 노트북 리포 만들기

JupyterLab 의 git extension 메뉴 또는 터미널에서:

# JupyterLab Git tab → "Initialize a Repository" 클릭하면 GenD 가 자동으로
# <your-login>/notebook-<sid> 리포 (Gitea native owner/repo 형식) 를 만들고
# clone URL + PAT 를 반환합니다.

# CLI 로 동일 동작:
curl -fsS -H "Authorization: Bearer $GEND_API_JWT" \
-H 'Content-Type: application/json' \
-X POST "$GEND_API_URL/api/v1/git/notebooks/$(uuidgen)/init" \
-d '{"description":"my analysis notebook"}' | jq

응답 예:

{
"repo_full_name": "alice/notebook-abc-123",
"clone_url": "http://alice:pat-...@gitea-http.gend.svc.cluster.local:3000/alice/notebook-abc-123.git",
"pat": "pat-...",
"pat_expires_at": "2026-05-26T07:00:00Z",
"default_branch": "main"
}

clone_url 에 이미 PAT 가 박혀 있어 git clone <clone_url> 1 줄로 완료됩니다.

2. 기존 repo 가져오기

# repo_full_name 은 Gitea native owner/repo (예: bob/shared-analytics).
# 문서 호환을 위해 users/<login>/<repo> · groups/<org>/<repo> 도 허용 —
# 서버에서 prefix 를 strip 한 뒤 동일하게 처리.
curl -fsS -H "Authorization: Bearer $GEND_API_JWT" \
-H 'Content-Type: application/json' \
-X POST "$GEND_API_URL/api/v1/git/notebooks/$(uuidgen)/clone" \
-d '{"repo_full_name":"bob/shared-analytics"}' | jq -r .clone_url \
| xargs -I{} git clone {}

3. 일반적인 작업

cd notebook-abc-123
jupyter lab . # 노트북 편집
git add notebook.ipynb
git commit -m "feat: add EDA"
git push origin main
# → Gitea webhook 이 gend-api 로 push 이벤트 전송
# → mlflow.source.git.commit 태그 자동 갱신 (관련 run 이 있는 경우)
# → notebook_commit_link 행 생성 (감사 추적용)

MLflow Run 과 commit SHA 연결

분석 노트북이 mlflow.start_run() 로 run 을 생성할 때 git commit SHA 를 미리 태깅하면 gend-api webhook 이 push 시점에 그 run 에 자동으로 mlflow.source.git.commit 태그를 갱신합니다.

import mlflow, subprocess

# 현재 작업 directory 의 HEAD commit (pre-commit hook 또는 노트북 첫 셀에 둠)
commit = subprocess.check_output(["git", "rev-parse", "HEAD"]).decode().strip()

with mlflow.start_run() as run:
mlflow.set_tag("mlflow.source.git.commit", commit)
mlflow.log_metric("rmse", 0.42)
# …

이후 push 가 발생하면 MLflow UI 의 그 run 페이지에서 commit SHA 가 deep-link 로 표시되고, 반대로 GenD UI 의 commit 페이지 (GET /api/v1/git/commits/...) 에서도 매칭된 run 목록을 확인할 수 있습니다.

VS Code (code-server) 사용자

VS Code profile 도 동일한 gend-git-bootstrap.sh 가 실행됩니다. 좌측 Source Control panel 에서 Initialize Repository → 동일하게 동작.

차이점: VS Code 는 native git extension 이 ~/.git-credentials 를 자동으로 읽으므로 PAT 입력 다이얼로그가 절대 뜨지 않습니다 (JupyterLab git extension 도 동일).

트러블슈팅

증상원인 / 해결
git push 가 username/password 묻는다~/.git-credentials 누락 또는 만료. 터미널에서 cat ~/.git-credentials 확인. 없거나 60 분 경과면 새 PAT: curl -X POST $GEND_API_URL/api/v1/git/tokens -H "Authorization: Bearer $GEND_API_JWT" -d '{}'
409 Conflict from notebooks/initKeycloak↔Gitea 사용자 sync 가 안 됨. POST /api/v1/git/users/sync 먼저 호출 (bootstrap 이 실패한 경우)
notebook.ipynb 변경했는데 MLflow tag 가 안 붙는다(a) feature branch push 는 M1 에서 무시 — main 으로 push (b) mlflow.start_run() 안에서 set_tag("mlflow.source.git.commit", ...) 가 호출됐는지 확인
Gitea UI 접근https://gend.genon.ai/git (Keycloak SSO 자동 로그인)
60 분 만료 시점 push 가 실패bootstrap 은 매 spawn 시점에만 PAT 발급. JupyterHub cull (1h idle) 후 새 spawn 이면 자동 갱신. 같은 spawn 안에서는 위 curl 로 수동 갱신.

보안 / 정책

  • 외부 GitHub.com clone/push 는 차단 (네트워크 정책). 내부 Gitea 만 사용.
  • PAT 평문은 응답 1 회 노출만. DB 는 SHA-256 hash 만 보관.
  • PAT TTL 최대 60 분 — 정책 cap 으로 422 반환.
  • webhook 은 HMAC-SHA256 검증 (X-Gitea-Signature).
  • 모든 git API 호출은 gend-audit 로그 (OpenSearch) 에 HMAC 체인으로 기록됨.

관련 문서