JupyterHub로 다중 사용자 환경에서 Jupyter Notebook을 효율적으로 활용하는 방법
Frontmatter 2024-09-24 · 5 tags
---
title: JupyterHub로 다중 사용자 환경에서 Jupyter Notebook을 효율적으로 활용하는 방법
author: 봉정근
date:
modified:
category: 📗 Docs
tags: [Docs, python, ubuntu, install, ssl]
---
JupyterHub는 다중 사용자 환경에서 Jupyter Notebook을 효율적으로 관리할 수 있는 서버로, 사용자별 독립된 환경을 제공한다. Ubuntu 서버에 JupyterHub를 설치하고 설정하는 과정에는 pip 설치, systemd 서비스 등록, Nginx reverse proxy 구성 등이 포함된다. PAM 인증을 통해 사용자 관리를 하며, SSL 적용으로 보안을 강화할 수 있다. 각 사용자의 리소스 제한 설정과 WebSocket 지원도 중요하다.
연구실이나 팀에서 여러 사람이 동시에 Jupyter Notebook을 사용해야 하는 상황이 생긴다. 각자 서버에 SSH로 접속해서 jupyter notebook을 띄우면 포트 충돌에 환경 꼬임까지... 관리하는 입장에서 상당히 골치 아프다.
JupyterHub는 이런 문제를 해결해주는 멀티 유저 Jupyter 서버다. 중앙에서 사용자 관리를 하면서, 각 사용자별로 독립된 Jupyter Notebook/Lab 환경을 제공한다. TensorFlow나 PyTorch 같은 ML 프레임워크를 사용하는 환경에서 특히 유용하다.
이번 글에서는 Ubuntu 서버에서 JupyterHub를 설치하고, systemd 서비스로 등록하고, Nginx reverse proxy까지 구성하는 전체 과정을 정리한다.
JupyterHub는 멀티 유저를 위한 Jupyter 서버다. 단일 서버에서 여러 사용자에게 각각 독립된 Jupyter Notebook 또는 JupyterLab 환경을 제공한다.
💡
JupyterHub의 핵심 구성 요소
Hub: 사용자 인증과 세션 관리를 담당하는 중앙 프로세스
Proxy: 사용자 요청을 적절한 Notebook 서버로 라우팅. configurable-http-proxy(Node.js 기반)를 사용한다
Spawner: 각 사용자별 Notebook 서버를 생성하는 컴포넌트
Authenticator: 사용자 인증을 처리. 기본적으로 PAM(Linux 시스템 계정) 인증을 사용한다
일반 Jupyter Notebook과의 차이를 정리하면 이렇다:
구분
Jupyter Notebook/Lab
JupyterHub
사용자 수
단일 사용자
멀티 유저
인증
토큰 기반
PAM, OAuth, LDAP 등
관리
각자 실행/관리
중앙에서 통합 관리
환경 분리
없음
사용자별 독립 환경
포트
사용자별 다른 포트
단일 포트(8000)로 통합
여기서 중요한 건 Proxy다. JupyterHub는 사용자가 로그인하면 그 사용자 전용 Notebook 서버를 Spawner를 통해 생성하고, Proxy가 해당 서버로 요청을 리다이렉트하는 구조다. 그래서 configurable-http-proxy가 반드시 설치되어야 JupyterHub가 작동한다.
# configurable-http-proxy 설치 (JupyterHub의 프록시 역할)
sudo npm install -g configurable-http-proxy
# JupyterHub 및 JupyterLab 설치
sudo pip install jupyterhub jupyterlab notebook
JupyterHub는 사용자 요청을 각 Notebook 서버로 리다이렉트하는 방식으로 동작하므로, configurable-http-proxy가 반드시 먼저 설치되어야 한다. 이걸 빠뜨리면 Hub가 실행은 되는데 아무것도 안 된다
Hub만 설치하면 기본으로 Jupyter Notebook(클래식) 인터페이스로 실행된다. JupyterLab의 모던한 UI를 사용하고 싶다면 jupyterlab도 같이 설치해주자.
설치 확인
bash
# JupyterHub 버전 확인
jupyterhub --version
# configurable-http-proxy 확인
configurable-http-proxy --version
정상적으로 버전이 출력되면 설치 완료다. 🎉
⚙️ 2단계: JupyterHub 설정
설정 파일 생성
JupyterHub 기본 설정 파일을 생성하고, /etc/jupyterhub/ 디렉토리로 이동시킨다:
bash
# 기본 설정 파일 생성
sudo jupyterhub --generate-config
# 설정 디렉토리 생성 및 파일 이동
sudo mkdir -p /etc/jupyterhub
sudo mv jupyterhub_config.py /etc/jupyterhub/
💡
왜 /etc/jupyterhub/에 두는가?
--generate-config를 실행하면 현재 디렉토리에 설정 파일이 생성된다. 하지만 systemd 서비스로 운영할 것이기 때문에, 시스템 설정 파일이 모여있는 /etc/ 하위에 두는 것이 관리상 편하다.
주요 설정 항목
설정 파일을 열어서 편집한다:
bash
sudo vi /etc/jupyterhub/jupyterhub_config.py
아래 설정을 추가한다:
python
# 바인딩 주소 - 모든 인터페이스에서 접근 허용
c.JupyterHub.ip = '0.0.0.0'
# 포트 설정 (기본: 8000)
c.JupyterHub.port = 18000
# 기본 인터페이스를 JupyterLab으로 변경
c.Spawner.default_url = '/lab'
# 각 사용자의 Notebook 작업 디렉토리
c.Spawner.notebook_dir = '~/jupyter'
# 관리자 계정 설정
c.Authenticator.admin_users = {'root', 'your-username'}
# 모든 시스템 사용자 로그인 허용
c.Authenticator.allow_all = True
기본 포트는 8000번이나, Portainer 등 다른 서비스가 이미 사용 중일 수 있으므로 18000번으로 변경했다. default_url을 /lab으로 설정해야 JupyterLab 인터페이스로 기본 실행이 바뀐다.
📝
설정 항목 상세 해설
c.JupyterHub.ip = '0.0.0.0': 모든 네트워크 인터페이스에서 접근을 허용한다. Nginx reverse proxy를 사용할 경우 127.0.0.1로 변경하여 로컬에서만 접근하도록 제한하는 것이 보안상 좋다
c.JupyterHub.port = 18000: 원하는 포트 번호로 설정. 방화벽에서 해당 포트를 열어야 한다
c.Spawner.default_url = '/lab': 이 설정이 없으면 클래식 Notebook으로 열린다
c.Spawner.notebook_dir = '~/jupyter': 각 사용자의 홈 디렉토리 아래 jupyter 폴더를 작업 공간으로 지정한다
c.Authenticator.admin_users: 관리자 페이지(/hub/admin)에 접근할 수 있는 사용자 목록
c.Authenticator.allow_all = True: 서버에 존재하는 모든 시스템 사용자가 로그인할 수 있다. 특정 사용자만 허용하려면 c.Authenticator.allowed_users = {'user1', 'user2'}를 사용하자
⚠️
보안 주의사항
JupyterHub는 기본적으로 PAM 인증을 사용한다. 즉, 서버의 Linux 시스템 계정과 비밀번호로 로그인한다. 따라서 반드시 sudo 권한으로 실행해야 다른 사용자의 Notebook 서버를 생성할 수 있다. 외부에 직접 노출하는 경우 반드시 HTTPS를 적용해야 비밀번호가 평문으로 전송되는 것을 방지할 수 있다.
추가 유용한 설정
필요에 따라 아래 설정도 추가할 수 있다:
python
# 로그아웃 시 서버 자동 종료
c.JupyterHub.shutdown_on_logout = True
# Notebook 서버 시작 타임아웃 (초)
c.Spawner.start_timeout = 60
# 각 사용자에게 할당할 메모리 제한
c.Spawner.mem_limit = '4G'
# 각 사용자에게 할당할 CPU 제한
c.Spawner.cpu_limit = 2
💡
리소스 제한이 중요한 이유
ML 작업을 하다 보면 한 사용자가 메모리를 독점해서 서버 전체가 먹통이 되는 경우가 있다. mem_limit과 cpu_limit을 적절히 설정해두면 이런 사태를 예방할 수 있다. 누군가 for 루프 안에서 DataFrame을 무한 복사하는 걸 막으려면 필수다
Restart=on-failure: 비정상 종료 시 자동 재시작. RestartSec=10으로 10초 대기 후 재시작한다
서비스 등록 및 시작
bash
# systemd 데몬 리로드
sudo systemctl daemon-reload
# 부팅 시 자동 시작 활성화
sudo systemctl enable jupyterhub.service
# 서비스 시작
sudo systemctl start jupyterhub.service
# 상태 확인
sudo systemctl status jupyterhub.service
✅
서비스 상태가 active (running)이면 성공이다! 🎉
이제부터는 설정 파일을 수정한 후 아래 명령어로 간편하게 재시작할 수 있다:
sudo systemctl restart jupyterhub.service
로그 확인이 필요한 경우:
bash
# 실시간 로그 확인
sudo journalctl -u jupyterhub -f
# 최근 100줄 로그 확인
sudo journalctl -u jupyterhub -n 100
👥 5단계: 사용자 관리
JupyterHub는 기본적으로 Linux PAM 인증을 사용한다. 즉, 서버에 시스템 사용자를 추가하면 바로 JupyterHub에 로그인할 수 있다.
새 사용자 추가
bash
# 시스템 사용자 추가
sudo adduser newuser
# Jupyter 작업 디렉토리 생성 (자동 생성되지 않는 경우)
sudo mkdir -p /home/newuser/jupyter
sudo chown newuser:newuser /home/newuser/jupyter
관리자 패널
관리자 계정으로 로그인한 후, http://{ip}:18000/hub/admin에 접속하면 관리자 패널을 사용할 수 있다.
기능
설명
서버 시작/중지
특정 사용자의 Notebook 서버를 수동으로 시작하거나 중지
사용자 추가/삭제
JupyterHub 내에서 사용자 관리 (시스템 계정은 별도 생성 필요)
서버 접근
다른 사용자의 서버에 접근하여 디버깅
토큰 관리
API 토큰 발급 및 관리
💡
관리자 권한 설정
jupyterhub_config.py에서 관리자 사용자를 설정할 수 있다:
python
# 관리자 사용자 지정
c.Authenticator.admin_users = {'admin', 'your-username'}
# 관리자가 다른 사용자 서버에 접근 허용 (디버깅에 유용)
c.JupyterHub.admin_access = True
⚠️
주의:admin_users 설정은 권한을 부여할 때만 사용된다. 한번 부여된 관리자 권한은 이 설정에서 사용자를 제거해도 자동으로 해제되지 않는다. 관리자 권한을 회수하려면 관리자 패널이나 API를 통해 직접 변경해야 한다.
Conda 환경별 커널 연동
사용자마다 다른 Conda 환경을 Jupyter 커널로 등록하여 사용할 수 있다. 예를 들어 TensorFlow 전용 환경을 만들어서 커널로 등록하려면:
bash
# Conda 환경 생성
conda create -n tf python=3.10
# 환경 활성화
conda activate tf
# TensorFlow 설치
pip install tensorflow
# ipykernel 설치 및 커널 등록
pip install ipykernel
python -m ipykernel install --user --name tf --display-name "Python (TensorFlow)"
이렇게 등록하면 JupyterLab에서 새 Notebook을 만들 때 커널을 선택할 수 있다.
🌐 6단계: Nginx Reverse Proxy
JupyterHub를 직접 외부에 노출하는 것보다 Nginx를 앞단에 두고 reverse proxy로 구성하는 것을 강력히 권장한다. 이유는 다음과 같다:
SSL/TLS 종료: Nginx에서 HTTPS를 처리하여 보안 통신 확보
포트 통합: 443 포트로 깔끔하게 접근 가능
WebSocket 지원: JupyterHub는 커널 통신에 WebSocket을 사용하므로, 프록시에서 이를 명시적으로 지원해야 한다
JupyterHub는 커널과의 통신에 WebSocket을 사용한다. proxy_set_header Upgrade와 Connection 설정이 없으면 Notebook을 열어도 커널 연결이 실패하여 코드 실행이 불가능하다. 이거 때문에 "커널이 안 잡힌다"고 삽질하는 사람이 정말 많다
핵심 설정을 하나씩 살펴보면:
map $http_upgrade $connection_upgrade
이 블록은 WebSocket 프록시를 위한 변수 매핑이다. 클라이언트가 WebSocket 업그레이드를 요청하면(Upgrade: websocket) Connection 헤더를 upgrade로 설정하고, 일반 HTTP 요청이면 close로 설정한다.
# 사이트 활성화
sudo ln -s /etc/nginx/sites-available/jupyterhub /etc/nginx/sites-enabled/
# 설정 문법 검사 (필수!)
sudo nginx -t
# Nginx 재시작
sudo systemctl restart nginx
✅
nginx -t에서 syntax is ok가 나오면 정상이다. 이 단계를 건너뛰고 바로 재시작했다가 문법 오류로 Nginx가 죽으면... 서버의 모든 웹 서비스가 한 번에 날아간다. 꼭 검증하자! 🙏
이제 https://jupyter.yourdomain.com으로 접속하면 JupyterHub를 HTTPS로 안전하게 사용할 수 있다.
🔧 트러블슈팅
configurable-http-proxy를 찾을 수 없는 경우
bash
# npm 글로벌 설치 경로 확인
npm config get prefix
# 결과가 /usr/local이 아닌 경우, 심볼릭 링크 생성
sudo ln -s $(npm config get prefix)/bin/configurable-http-proxy /usr/local/bin/