Jupyterlab 4 업그레이드 주의 사항.

Jupyter 3.x 를 사용중에 Jupyter 4.0 출시를 했다고 해서 가상환경을 만들고 jupyterlab 4.0 을 설치했었다. 조금 더 UI 가 명확해 보이고 괜잖았는데 한글 입력시 글가가 깨져서 입력되거나 혹은 현재 편집중인 Cell 의 위치가 달라져 보이는 이상한 증상이 있어서 4.0 업그레이드 버전 사용을 안했는데 이유는 Node.js 버전 문제로 보인다.

Node.js 버전

기존에 jupyterlab 3.6 에서 Node.js 14 버전을 사용하고 있었다. Jupyterlab 4.x 버전의 주피터 확장 모듈 빌드도 문제가 없었는데 위에 언급한 이상한 현상이 발생해서 Jupyterlab 4.x 가상환경에서 Node.Js 버전을 18버전으로 업그레이후 후에는 문제없이 사용중이다.

Jupyter 기반 환경에서 Multiprocess 실행시 print 출력 차이.

jupyter notebook에서 실행하면 차일드 프로세스로 실행한 print() 로 출력하는 결과를 확인할 수 업다. print 는 std out 에 출력하도록 되어 있다. 자식 프로세스에서 print 를 호출하면 spawn 을 사용해서 호출되어서 출력되지 않는다.

차일드 프로세스 print 문제

아래 코드는 아주 간단하게 메인 프로세스에서 새 프로세스를 생성하고 실행한다.

1
2
3
4
5
6
7
8
9
from multiprocessing import Process

def func(msg):
print(msg)

if __name__ == "__main__":
proc = Process(target=func, args=('Hello multiproess',))
proc.start()
proc.join()

이 코드는 소스로 쉘에서 실행하거나 Web 기반의 jupyterlab 에서 실행하면 아무 문제이 print 문이 잘 작동하고 출력 결과가 나온다.

1
2
>>> ! python mutiprocess1.py
Hello multiproess

print 의 Multiprocessing 에서 문제는 링크 참고1 을 읽어보기를 권한다. 이 글에서는 테스트한 내용을 정리만 했다.

원인

아래 링크 참고1 기사에서 설명을 자세히 하고 있다. 보통 파이썬에서 차일드 프로세스를 시작할 때는 'fork', 'spawn', 'forkserver' 방법이 있다고 한다. 그런데 spawn 으로 차일드 프로세스를 실행하면 std io 출력 버퍼가 자동으로 비워지지 않는다(print는 기본으로 flush가 되지 않는다). 그러다 보니 차일드 프로세스가 종료 하면서 자동으로 gabage collection에 의해 버퍼에 남아 있는 메시지가 사라지는 것이다.

해결 방법

  1. 메시지 버퍼가 사라지기 전에 flush 를 한다.
  2. fork 기반의 프로세스를 생성한다.

1. flush 사용

  1. 메시지 버퍼가 사라지기 전에 flush 를 한다.

이를 해결하기 위해 모든 print 의 출력은 즉시 flush 되도록 flush=True 옵션을 사용할 수 있다고 한다.

1
2
def func(msg):
print(msg, flush=Yes)

이 방법은 VS code 현재 jupyter 확장에서는 효과가 없다.

flush 란?

std io 의 버퍼 처리 흐름은 글 파일 I/O 버퍼링 을 살펴보고,

python에서 print 같은 표준 출력의 flush에 대해서 이글 의 그림을 참고한다.

2. fork 기반의 프로세스를 생성한다.

지원되는 프로세스 시작 방법은 아래 같이 확인이 가능하다.

1
2
3
4
import multiprocessing as mp

mp.get_start_method() # 현재 start 메서드
mp.get_all_start_methods() # 모든 start 메서드

아래 같이 start method 를 fork 로 강제 사용할 수 있다.

1
2
3
import multiprocessing as mp

mp.set_start_method('fork') # 현재 start 메서드

브라우저 기반의 jupyter 실행환경은 'fork', 'spawn', 'forkserver' 를 모두 가능하다. 하지만 현재 VS code 의 jupyter 확장은 에서는 spawn 만 지원하고 있다.

fork, spawn

링크 침고2 에 있는 글에서 Fork, Spawn 에 대한 설명을 읽어보자.

Forking

  • 포크한 부모 프로세스의 이미지를 그래도 사용한다.

Spwaning

  • 포크한 부모 프로세스와 다르게 새 이미지로 갱신한다.

sleep 으로 지연하면?

VS code 환경에서 아래 같이 sleep 으로 지연을 주어 봤지만 해결이 안된다.

1
2
3
4
5
6
7
8
def func(msg):
print(msg, flush=True)
time.sleep(1)

if __name__ == "__main__":
proc = mp.Process(target=func, args=('Hello multiproess',))
proc.start()
proc.join()

아래 같이 join 전에 sleep 으로 지연

1
2
3
4
5
6
7
8
9
def func(msg):
print(msg, flush=True)
time.sleep(1)

if __name__ == "__main__":
proc = mp.Process(target=func, args=('Hello multiproess',))
proc.start()
time.sleep(1)
proc.join()

결론

현재 테스트한 VS Code 의 jupyter 확장 버전은 아래 같다.

  • Windows VS code, Jupyter Extension
    • v2023.6.1101941928
    • v2023.7.1001901100

만약 VS code 에서 Multiprocess 디버깅시 다른 logger 라이브러리 등을 이용해야 할 것 같다.


참고

참고1 : How to print() from a Child Process in Python

참고2 : fork, vfork 그리고 posix_spawn 이야기

참고3 : foring, spawning 이미지

VSCode 에서 languageserver 확인

VS Code에서 Jupyterlab 3.x를 사용하는데 classic jupyter notebook 에서 가능하던 content assistance 기능이 작동을 하지 않았다.

상황

Jupyterlab, classic jupyter notebook 에서 가능하던 content assistance 기능으로 속성/모듈/함수/도움말 등의 지원이 가능했다.

  • Jupyterlab에서 노트북 셀의 tab & shift tab
그림>

그런데 Visual studio code 에서 .ipynb 노트북 파일을 열어 사용하면 이런 기능이 안되고 있어서 궁금했다.

구글 검색으로 스택오버플로우에 올라온 기사를 보니 settings.json 을 보면 python language server 설정을 해야 한다고 한다. 현재는 Default 상태로 선택되어 있어서 그렇다고 한다. 그래서 Pylance 를 지정했다.

languageserver 를 지정하고 재시작한 후 살펴보니 잘 된다.

Language Server extension 이란?

language-server-extension-guide 에서 설명한 바로는

여러 프로그래밍 언어에 대해서 강력한 편집 경험을 제공하고자 한다.

아래 그림에서 languageserver 의 효과를 설명하고 있다. 왼쪽 같이 LSP 가 없으면 각 편집기에서 직접 언어 엔진을 가동해야 한다. 그런데 오른쪽 깥이 LSP를 사용하면 하나의 서버를 통해서 여러 편집기에서 언어의 특성을 활용할 수 있다는 설명 이다.

Jupyterlab 의 extension 을 설치하고 build 시 npm 에러.

jupyterlab 의 확장 모듈을 다운받아 설치시 nodejs 실행 환경이 제공되야 한다.

jupyterlab 확장 build error

jupyterlab 의 확장 모듈을 다운받아 설치하다 보면 npm 과 연계한 프로그램이 많다. 아래 같은 nodejs 관련 에러를 발생한다.

1
2
3
4
5
6
daddy> jupyter lab build
...
...
An error occurred.
RuntimeError: npm dependencies failed to install
See the log file for details: C:\Users\USERID\AppData\Local\Temp\jupyterlab-debug-4kr1lkn1.log

결론으로 현재 jupyterlab 을 실행하는 환경에서

  1. nodejs 버전이 설치되어 있고
  2. jupyterlab extensions가 활성화 되어 있다면

해당 로그 파일을 확인해 보면 대부분 nodejs 버전이 일치하지 않아서 아래 같은 로그를 기록하고 있어 보인다.

1
2
error readable-stream@4.3.0: The engine "node" is incompatible with this module. Expected version "^12.22.0 || ^14.17.0 || >=16.0.0". Got "14.15.0"
error Found incompatible module.

nodejs 버전을 업그레이드 하고 빌드를 수행하니 잘 된다.

Python 에서 secret 키 값 이용하기 - json

크롤링, 데이터 베이스 연결등에 사용자 인증번호를 사용하는데 코드 자체에 아이디/비밀번호를 입력해서 사용하면 유출의 염려가 있으므로 이를 외부 파일에 두고 관리하고자 한다.

여러가지 방법이 있겠지만 여기서는 json 을 사용한다.

key:value 형식의 properties, 윈도우 ini 같은 형식을 지원하는 configparser 모듈도 좋은 선택 같다.

json 이용 키 관리

외부에 아래 형식의 secret 파일을 생성한다.

  • 디렉토리: .api_keys/ 폴더 사용
  • 파일: secret_keys.json

secret_keys.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{   "naver.com" : {
"userid": "UR_ID",
"password" :"votmdnjem" },
"apple.net": {
"userid": "UR_ID",
"password" : "votmdnjem" },
"api.twitter.com": {
"CONSUMER_KEY" : "unJgTOc0aZkk7NoX4LlD5g",
"CONSUMER_SECRET":"uHdmctDcPM66s9OasrKnr2x3pu88",
"ACCESS_TOKEN" : "98948674-J9auHiBiOGoWUJOzRafp",
"ACCESS_TOKEN_SECRET" : "kKgT9tlSY2rCIAbWJrCEEiTsR37" },
"my_database" : {
"userid" : "UR_ID",
"password" : "votmdnjem"}
}

파일 사용

json 모듈을 사용해서 해당 파일을 읽으면 키:값 형식으로 참조해서 사용할 수 있다.

1
2
3
4
import json

with open('../../.api_keys/secret_keys.json') as f:
secrets = json.loads(f.read())

이렇게 읽어들인 json 은 dict 같이 사용할 수 있다.

1
DB_USER, DB_PW = secrets['my_database']['userid'], secrets['my_database']['password']

Jupyterlab 에서 password 생성해 systemd 서비스 이용

Jupyter Lab - systemd 운영

jupyterlab 을 Itel 기반의 Ubuntu 시스템에 설치하고 시스템 서비스로 등록하는 과정.

  1. Ubuntu 18.04
  2. node.js
  3. Anaconda 2020

Restart your shell so the path changes take effect. You can now begin using pyenv.

1
exec "$SHELL"

Anaconda 배포본 설치 (Ubuntu)

우분투용 다운로드

https://www.anaconda.com/products/individual#linux

다움로드안 쉘 스크립트를 시작한다.

1
bash ~/Downloads/Anaconda3-2020.02-Linux-x86_64.sh

To control whether or not each shell session has the base environment activated or not, run conda config –set auto_activate_base False or True. To run conda from anywhere without having the base environment activated by default, use conda config –set auto_activate_base False. This only works if you have run conda init first.

Anaconda 를 설치하면 대부분의 패키지가 내장되어 있다.

설치후 최신 conda 환경 base를 갱신해 준다.

1
conda update -n base -c defaults conda

nvm으로 Node.js 설치

nvm 설치

1
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.37.0/install.sh | bash

스크립트로 설치를 완료하면 쉘 시작시 nvm 환경을 구성하기 위해서아래 스크립이 자동으로 현재 쉘 스크립트 파일 끝에 추가된다.

만약 아래 스크립이 추가 안되면 아래 스크립트를 (~/.bash_profile, ~/.zshrc, ~/.profile, or ~/.bashrc).)에 추가한다.

1
2
export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm

node.js 설치

1
2
3
4
5
6
~$ nvm ls-remote
v14.0.0
v14.1.0
v14.14.0
v14.15.0 (LTS: Fermium)
v14.15.1 (Latest LTS: Fermium)

lts 최신 버전 14.0을 설치한다.

1
2
3
4
5
6
7
8
~$ nvm install --lts 14.15.1
Downloading and installing node v14.15.1...
Downloading https://nodejs.org/dist/v14.15.1/node-v14.15.1-linux-x64.tar.xz...
######################################################################### 100.0%
Computing checksum with sha256sum
Checksums matched!
Now using node v14.15.1 (npm v6.14.8)
Creating default alias: default -> lts/* (-> v14.15.1)

node 버전을 확인

1
2
~$ node --version
v14.15.1

Conda 에서 jupyterlab 설치

Anaconda 나 Miniconda 를 설치하고 가상환경을 하나 생성한다.

1
2
3
4
$ conda create -n tf2 jupyterlab numpy scipy matplotlib tensorflow

$ conda activate tf2
(tf2) ~$

JupyterLab 실행

가상환경에서 jupyterlab 을 외부에서 접속 가능하도록 실행하자.

1
(base) $ jupyter-lab --no-browser --ip=* --port=8888 ~/Jupyter-Notebook/

LabConfig 디렉토리

1
2
3
4
5
6
7
8
9
10
11
12
13
$ jupyter --paths
config:
/home/qkboo/.jupyter
/home/qkboo/anaconda3/envs/py3/etc/jupyter
/usr/local/etc/jupyter
/etc/jupyter
data:
/home/qkboo/.local/share/jupyter
/home/qkboo/anaconda3/envs/py3/share/jupyter
/usr/local/share/jupyter
/usr/share/jupyter
runtime:
/home/qkboo/.local/share/jupyter/runtime

JupyterLab 환경 설정

jupyter 의 config 파일을 통해서 인증과 구성을 하자.

  • jupyterlab 3.x 버전: jupyter_server_config.py
  • jupyterlab 2.x 버전: jupyter_notebook_config.py

Jupyter Lab 에서 설정 파일을 생성한다. 다음 명령으로 각각 $HOME/.jupyter/ 위치에 jupyter_server_config.py 파일이 생성된다.

1
2
(tf2)$ jupyter lab --generate-config
Writing default config to: /home/qkboo/.jupyter/jupyter_server_config.py

패스워드 사용

jupyter_notebook_config.py 설정 파일에 비밀번호를 추가하려면 비밀번호를 생성해야 한다. 아래 명령으로 생성한다.

1
2
3
4
(tf2)$ jupyter lab password
Enter password:
Verify password:
[NotebookPasswordApp] Wrote hashed password to $HOME\.jupyter\jupyter_server_config.json

jupyter_server_config.json 파일로 암호가 생성된다. 생성한 암호를 jupyter_server_config.py 파일의 c.ServerApp.password 항목에 입력해 준다

1
2
3
4
5
[jupyter_server_config.py]

c.ServerApp.password = 'sha1:*********' # 외부 접속시 사용할 비밀번호
c.ServerApp.ip = '*' # 어디서든 접속 가능
c.ServerApp.port = 8888 # 접속에 사용할 포트

Jupyter Lab 3.x 버전

1
2
3
4
5
6
7
8
9
c.ServerApp.base_url = '/notebook'
c.ServerApp.enable_mathjax = True
c.ServerApp.password = ''
c.ServerApp.ip = '*'
c.ServerApp.port = 8888
c.ServerApp.port_retries = 10
c.ServerApp.open_browser = False

c.ServerApp.tornado_settings = {"websocket_max_message_size": 400 * 1024 * 1024}

Jupyter Notebook, Jupyterlab 2.x 버전:

1
2
3
4
5
6
7
8
9
c.NotebookApp.base_url = '/notebook'
c.NotebookApp.enable_mathjax = True
c.NotebookApp.password = ''
c.NotebookApp.ip = '*'
c.NotebookApp.port = 8888
c.NotebookApp.port_retries = 10
c.NotebookApp.open_browser = False

c.NotebookApp.tornado_settings = {"websocket_max_message_size": 400 * 1024 * 1024}

systemd 구성

nodejs, jupyter lab 을 시스템 시작 서비스로 등록한다. node.js 경로, jupyterlab 을 위한 시작 환경이 필요하다.

systemd의 unit 위치는 OS 마다 조금 다른 것 같다.

여기서는 /etc/systemd/system 밑에 jupyter.service 라는 유닛 파일에 아래 같이 파이썬 환경을 포함해 작성한다.

가상환경을 사용하고 있으므로 systemd unit의 ExecStart 의 python도 가상환경 위치로 지정해 주어야 한다.

유닛 파일: /etc/systemd/system/jupyter.service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[Unit]
Description=My Jupyter-Notebook

[Service]
Type=simple
PIDFile=/run/jupyter-notebook.pid
# nodejs path
Environment="PATH=/home/qkboo/.nvm/versions/node/v12.18.0/bin/:/usr/local/bin:/ usr/local/sbin:/usr/bin:/bin:/usr/sbin:/sbin"

# anaconda: tf2
ExecStart=/home/qkboo/anaconda3/envs/tf2/bin/jupyter-lab --config /home/qkboo/Home/mybook_config.py
User=qkboo
Group=qkboo
WorkingDirectory=/home/qkboo/Home/Jupyter-Notebook/
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target

유닛 파일 등록과 시작

jupyter.service 파일을 등록한다.

1
$ sudo systemctl enable jupyter

등록한 유닛 파일을 확인해 보자.

1
2
3
$ sudo systemctl list-unit-files | grep jupyter
jupyter_book.service disabled enabled
jupyter.service enabled enabled

그리고 데몬을 리로드 한다.

1
2
$ sudo systemctl daemon-reload
$ sudo systemctl start jupyter

실행한 서비스를 종료하려면

1
$ sudo systemctl stop jupyter

실행 상태를 확인한다.

1
$ sudo systemctl status jupyter

유닛 파일 등록 해지

1
2
$ sudo systemctl disable jupyter
Removed /etc/systemd/system/multi-user.target.wants/jupyter.service.

서비스 상태

해당 서비스 상태 확인

1
$ sudo systemctl status jupyter

구동에 실패한 서비스 보기

1
$ sudo systemctl list-units --state=failed

enabled 상태인 서비스 목록

1
$ sudo systemctl list-units --state=enabled