From 25a1034d11ee3526bcfdb1fb767511d479de4ee0 Mon Sep 17 00:00:00 2001 From: "jade04342@gmail.com" Date: Fri, 1 May 2026 21:11:52 +0900 Subject: [PATCH] feat: add opt-in noVNC GUI support --- Dockerfile | 14 +++++++++ README.md | 47 +++++++++++++++++++++++++--- entrypoint.sh | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 144 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index 9b09888..53ea72a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,6 +5,10 @@ # 변경한 사람: 이소은 # tensorflow version 2.13.0-gpu -> 2.18.0-gpu +##### decs:260501##### +# 변경한 사람: 임준영 +# Xfce, TigerVNC, noVNC 기반 GUI 접속 지원 추가 + ########################### version history end ########################## # TensorFlow 2.18.0 GPU 공식 이미지 사용 (CUDA 12.3, cuDNN 8.9 포함, Ubuntu 22.04 기반) @@ -30,6 +34,13 @@ vim \ wget \ curl \ ssh \ +dbus-x11 \ +xfce4 \ +xfce4-terminal \ +tigervnc-standalone-server \ +tigervnc-common \ +novnc \ +websockify \ software-properties-common # motd install @@ -78,6 +89,9 @@ RUN mkdir /jupyter_config \ # entrypoint.sh 복사 COPY entrypoint.sh / +# noVNC 접속 포트. VNC 서버(5901)는 localhost에만 바인딩합니다. +EXPOSE 6080 + # SSHD 서버를 실행하고, entrypoint 파일을 start/restart 시 마다 실행, dev/null에 entrypoint 로그를 저장 RUN chmod +x /entrypoint.sh ENTRYPOINT ["bash", "/entrypoint.sh"] diff --git a/README.md b/README.md index e189b75..95b1ad5 100644 --- a/README.md +++ b/README.md @@ -3,10 +3,18 @@ | 이미지 태그 (Image Tag) | TensorFlow 버전 | CUDA / cuDNN | 베이스 OS (Base OS) | 주요 변경사항 및 설명 | | :--- | :--- | :--- | :--- | :--- | -| `dguailab/decs:latest`
`dguailab/decs:260201` | **2.18.0** | CUDA 12.5
cuDNN 8.9 | Ubuntu 22.04 | **(최신)** 의도되지 않은 MOTD 출력 방지 버그 수정 | -| `dguailab/decs:251023` | **2.18.0** | CUDA 12.5
cuDNN 8.9 | Ubuntu 22.04 | **(이전 안정 버전)** Jupyter Notebook 버전 변경으로 인한 오류 해결 버전 | -| `dguailab/decs:251002` | **2.18.0** | CUDA 12.5
cuDNN 8.9 | Ubuntu 22.04 | TensorFlow 2.18.0 업그레이드, 최신 GPU 환경 지원 | -| `dguailab/decs:250926` | **2.13.0** | CUDA 11.8
cuDNN 8.6 | Ubuntu 20.04 | **(이전 안정 버전)** TensorFlow 2.13.0 기반의 안정화 버전 | +| `dguailab/decs:latest`
`dguailab/decs:260501` | **2.18.0** | CUDA 12.5
cuDNN 8.9 | Ubuntu 22.04 | **(최신)** Xfce + TigerVNC + noVNC 기반 브라우저 GUI 접속 지원 추가 | +| `dguailab/decs:260427` | **2.18.0** | CUDA 12.5
cuDNN 8.9 | Ubuntu 22.04 | 기존 사용자 컨테이너 변경 시 `~/.bashrc`의 conda initialize 블록 갱신, 경고 주석 영문화 및 인덴트 수정 | +| `dguailab/decs:260403` | **2.18.0** | CUDA 12.5
cuDNN 8.9 | Ubuntu 22.04 | 계정/권한 검증 강화, config-server 주입 기반 sudo 권한 축소, Jupyter 및 컨테이너 유지 프로세스 비-root 실행, gosu 추가 | +| `dguailab/decs:260201` | **2.18.0** | CUDA 12.5
cuDNN 8.9 | Ubuntu 22.04 | 의도되지 않은 MOTD 출력 방지 버그 수정 | +| `dguailab/decs:251023` | **2.18.0** | CUDA 12.5
cuDNN 8.9 | Ubuntu 22.04 | **(이전 안정 버전)** Jupyter Notebook 설정 파일 생성/경로 오류 해결, 랜덤 토큰 저장 방식 반영 | +| `dguailab/decs:251002` | **2.18.0** | CUDA 12.5
cuDNN 8.9 | Ubuntu 22.04 | TensorFlow 2.18.0 업그레이드, 최신 GPU 환경 지원, Docker Hub 자동 빌드/푸시 워크플로 도입 | +| `dguailab/decs:250926` | **2.13.0** | CUDA 11.8
cuDNN 8.6 | Ubuntu 20.04 | **(이전 안정 버전)** 사용자 홈 디렉토리 소유권 설정 순서 수정, 이슈/PR 템플릿 추가 | +| `dguailab/decs:250428` | **2.13.0** | CUDA 11.8
cuDNN 8.6 | Ubuntu 20.04 | Jupyter Lab 설정 파일을 사용자 홈의 `.jupyter` 경로에서 생성하고 사용하도록 수정 | +| `dguailab/decs:250416` | **2.13.0** | CUDA 11.8
cuDNN 8.6 | Ubuntu 20.04 | Jupyter Notebook 설정 파일 생성 로직 수정, 10자리 랜덤 토큰 생성 및 파일 저장 추가 | +| `dguailab/decs:250323` | **2.13.0** | CUDA 11.8
cuDNN 8.6 | Ubuntu 20.04 | `useradd` 옵션 보완, MOTD 공지 추가, TensorFlow ASCII 출력 제거 | +| `dguailab/decs:250309` | **2.13.0** | CUDA 11.8
cuDNN 8.6 | Ubuntu 20.04 | ldconfig permission denied 오류 방지, MOTD/Slack 공지 반영, Conda 최신화 반영 | +| `dguailab/decs:250218` | **2.13.0** | CUDA 11.8
cuDNN 8.6 | Ubuntu 20.04 | Anaconda3 2024.10-1 설치로 Conda 24.9.2 업그레이드 | ## ⚙️ 사용 방법 필요한 버전의 이미지를 Docker Hub에서 pull 받아 사용합니다. @@ -28,6 +36,37 @@ docker pull dguailab/decs:251002 docker pull dguailab/decs:250926 ``` +### GUI 접속(noVNC) +`260501` 버전부터 브라우저 기반 GUI 접속을 지원합니다. GUI 접속은 기본 비활성화 상태이며, 컨테이너 생성 시 `ENABLE_VNC=true` 환경변수를 전달한 경우에만 시작됩니다. + +- 컨테이너 내부 noVNC 포트: `6080` +- 컨테이너 내부 VNC 포트: `localhost:5901` 전용 +- 접속 URL: `http://서버주소:외부포트/vnc.html` +- VNC 비밀번호 저장 위치: `/home/$USER_ID/decs_jupyter_lab/vnc_password.txt` + +직접 `docker run`을 사용할 때는 다음처럼 환경변수와 포트를 함께 지정합니다. + +``` +-e ENABLE_VNC=true -p 외부포트:6080 +``` + +운영 스크립트(`~/uid`)에서는 `--enable_vnc true`를 주면 컨테이너 내부 `6080` 포트를 자동으로 추가하고, Docker 실행 환경변수에 `ENABLE_VNC=true`를 함께 전달합니다. FARM2 예시는 외부 포트 범위 `9100~9199` 중 하나가 `6080`에 연결되는 방식입니다. + +``` +--enable_vnc true +``` + +관련 환경변수: + +| 환경변수 | 기본값 | 설명 | +| :--- | :--- | :--- | +| `ENABLE_VNC` | `false` | `true`로 설정한 경우에만 VNC/noVNC를 시작합니다. | +| `VNC_PASSWORD` | 랜덤 8자리 | 지정하지 않으면 자동 생성 후 `vnc_password.txt`에 저장합니다. | +| `VNC_RESOLUTION` | `1920x1080` | VNC 화면 해상도입니다. | +| `VNC_DEPTH` | `24` | VNC 색상 깊이입니다. | +| `VNC_DISPLAY` | `1` | VNC display 번호입니다. 기본 VNC 포트는 `5901`입니다. | +| `NOVNC_PORT` | `6080` | noVNC가 컨테이너 내부에서 listen할 포트입니다. | + # ⚒️ 빌드 자동화 이 Docker 이미지는 GitHub Actions를 통해 자동으로 빌드 및 배포됩니다. diff --git a/entrypoint.sh b/entrypoint.sh index b03bcea..cf3d799 100644 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -1,5 +1,89 @@ #!/bin/bash +start_novnc() { + case "${ENABLE_VNC:-false}" in + true|TRUE|1|yes|YES|on|ON) ;; + *) + echo "VNC/noVNC disabled. Set ENABLE_VNC=true to enable it." + return 0 + ;; + esac + + local user_home="/home/$USER_ID" + local vnc_dir="$user_home/.vnc" + local vnc_display="${VNC_DISPLAY:-1}" + local vnc_resolution="${VNC_RESOLUTION:-1920x1080}" + local vnc_depth="${VNC_DEPTH:-24}" + local novnc_port="${NOVNC_PORT:-6080}" + local vnc_password_file="$user_home/decs_jupyter_lab/vnc_password.txt" + local vnc_password + + vnc_display="${vnc_display#:}" + if ! [[ "$vnc_display" =~ ^[0-9]+$ && "$novnc_port" =~ ^[0-9]+$ && "$vnc_depth" =~ ^[0-9]+$ ]]; then + echo "Invalid VNC configuration. Check VNC_DISPLAY, NOVNC_PORT, and VNC_DEPTH." + return 1 + fi + local vnc_port=$((5900 + vnc_display)) + + if ! command -v vncserver >/dev/null 2>&1 || ! command -v websockify >/dev/null 2>&1; then + echo "VNC/noVNC packages are not installed. Skipping GUI startup." + return 0 + fi + + mkdir -p "$vnc_dir" "$user_home/decs_jupyter_lab" /tmp/.X11-unix /tmp/.ICE-unix + chown root:root /tmp/.X11-unix /tmp/.ICE-unix + chmod 1777 /tmp/.X11-unix /tmp/.ICE-unix + + if [[ -n "${VNC_PASSWORD:-}" ]]; then + vnc_password="$VNC_PASSWORD" + elif [[ -s "$vnc_password_file" ]]; then + vnc_password=$(tr -d '\r\n' < "$vnc_password_file" | head -c 8) + else + vnc_password=$(tr -dc A-Za-z0-9 "$vnc_password_file" + chmod 600 "$vnc_password_file" + + printf "%s\n" "$vnc_password" | vncpasswd -f > "$vnc_dir/passwd" + chmod 600 "$vnc_dir/passwd" + + cat > "$vnc_dir/xstartup" <<'EOF' +#!/bin/sh +unset SESSION_MANAGER +unset DBUS_SESSION_BUS_ADDRESS +export XDG_SESSION_TYPE=x11 +export XKL_XMODMAP_DISABLE=1 +xrdb "$HOME/.Xresources" 2>/dev/null || true +exec dbus-launch --exit-with-session startxfce4 +EOF + chmod +x "$vnc_dir/xstartup" + chown -R "$USER_ID:$USER_GROUP" "$vnc_dir" "$vnc_password_file" + + sudo -u "$USER_ID" env HOME="$user_home" USER="$USER_ID" \ + vncserver -kill ":$vnc_display" >/tmp/vnc-kill.log 2>&1 || true + + echo "trying TigerVNC on localhost:$vnc_port..." + if ! sudo -u "$USER_ID" env HOME="$user_home" USER="$USER_ID" \ + vncserver -localhost yes ":$vnc_display" -geometry "$vnc_resolution" -depth "$vnc_depth" >/tmp/vncserver.log 2>&1; then + echo "TigerVNC startup failed. See /tmp/vncserver.log." + cat /tmp/vncserver.log + return 1 + fi + echo "TigerVNC listening on localhost:$vnc_port" + + pkill -f "websockify.*$novnc_port" >/dev/null 2>&1 || true + echo "trying noVNC on 0.0.0.0:$novnc_port..." + nohup websockify --web=/usr/share/novnc "0.0.0.0:$novnc_port" "localhost:$vnc_port" >/tmp/novnc.log 2>&1 & + echo "noVNC listening on port $novnc_port. VNC password saved to $vnc_password_file" +} + sudo apt update sudo apt install -y auditd @@ -129,6 +213,9 @@ echo "trying jupyter lab..." nohup /opt/anaconda3/bin/jupyter lab --NotebookApp.token=$TOKEN --config=/home/$USER_ID/.jupyter/jupyter_notebook_config.py >/dev/null 2>&1 & echo "jupyter lab listening!" +# noVNC 기동. 외부에서는 컨테이너의 6080 포트를 매핑해서 접속합니다. +start_novnc || echo "VNC/noVNC startup failed." + # ldconfig permission 오류 방지 # bash.bashrc에서 ldconfig 명령어 삭제 후 명령어 실행 및 결과 출력 sed -i '/ldconfig/d' /etc/bash.bashrc