주피터 노트북과 스파크는 워낙 잘 알려진 오픈소스이므로 이에 대한 설명은 굳이 언급하지 않으려 한다. 여기서는 k8s 상에 주피터를 어떻게 잘 설치할 수 있으며, 주피터 상에서 스파크 executor 를 어떻게 실행할수 있는지, 단순히 혼자서 쓰는게 아닌 여러명이 어떻게 공간을 분리(멀티테넌트)하여 제한된 리소스로 사용하도록 As a service 형태로 제공할수 있는지 설명한다. (보안측면에서는 자세히 다루지 않는다. k8s에서 networkPolicy 등 세밀한 설정이 필요하겠지만 그건 필요에 따라 레벨이 달라지기 때문이기도 하고, 자칫하다가는 주피터 노트북의 기본 동작 조차 안되는게 있을 수도 있다)
또한 스파크의 배포형상에 대해서도 논하지 않는다. 주피터 노트북에서는 cluster 모드 보다는 client 모드를 주로 쓰기 때문에 이를 토대로 하며 master-worker 방식이 아닌 executor 파드를 직접 올리는 방식으로 진행한다.(형상에 대해서는 다른 블로그에 많이 나와 있으니 구글링으로 참고하기 바란다)
참고로 필자는 경험많은 데이터 엔지니어가 아니다. 데이터 엔지니어에게 데이터플랫폼을 제공하는 클라우드 엔지니어의 입장에서 이 글을 작성하였다. 특히나 k8s 를 기반으로 하는 클라우드네이티브 스타일을 기준으로 하였다.
Jupyterhub 차트
https://github.com/jupyterhub/zero-to-jupyterhub-k8s
주피터헙을 k8s에 설치하는데 있어 가장 잘 알려진 배포 프로젝트이다. Helm을 10년 가까이 사용해본 경험에도 불구하고, 이렇게 난해한 차트는 처음본것 같다.
초고난이도로 생각되며 많은 패턴과 트릭을 배울 수 있으니 시간이 된다면 꼭 차트를 분석해보도록 하자.
Values 분석
Value가 상당히 많은데, 그럼에도 불구하고 문서화가 정말 잘되어 있다.
https://github.com/jupyterhub/zero-to-jupyterhub-k8s/blob/main/jupyterhub/values.schema.yaml
이 스키마 파일을 참고하면 각 value에 대한 자세한 예시와 사용법을 이해할 수 있다.
필자는 몇가지 목표를 세워서 이 차트를 분석하고 사용해보았다.
- 인증/권한 기능 제공
- 사용자별 리소스 컨트롤: Limit, 비사용 노트북/Spark Exeuctor 파드 자동 종료
- 다양한 커널 지원
- 손쉬운 Spark 설정
- 사용자별 스파트UI/히스토리서버
- 사용자별 자유로운 Executor 사용: 리소스/이미지
위의 목표를 달성하기 위해서 노트북/스파크 컨테이너 이미지를 직접 생성하는 과정이 필요하다.
참고로, 다음의 Dockerfile을 기반으로 수정하였다.
- 노트북: https://github.com/jupyter/docker-stacks/tree/main/images/pyspark-notebook
- 스파크: https://github.com/apache/spark-docker
인증/권한 기능
주피터헙 차트를 설정하면 인증은 붙어 있지 않기 때문에 아무나 로그인을 할수 있다. 이것을 ldap등 과 연동하는 방법도 있겠지만, 필자가 생각하기에는 공통으로 사용하는 계정(팀단위)도 주로 있을 거라 자체인증 플러그인을 붙여보았다.
가장 잘 알려진 jupyterhub-nativeauthenticator 를 적용하였다.
먼저 pyspark Dockerfile에 jupyterhub-nativeauthenticator 설치 추가가 필요하다.
RUN pip3 install jupyterhub-nativeauthenticator
그 다음에 차트 value에 해당 템플릿으로 로그인/관리 화면을 교체하는 과정이 필요하다.
chart.hub.extraConfig에 다음을 추가한다.
set_admin_password: |
import os, nativeauthenticator
c.JupyterHub.template_paths = [f"{os.path.dirname(nativeauthenticator.__file__)}/templates/"]
extraConfig은 jupyterhub config에 추가적인 설정을 포함하기 위한 value이다. extraConfig은 고급 설정을 위한 아주 중요한 value중 하나라 생각한다. 차트를 제대로 다루려면 이 부분을 많이 봐야한다. 해당 기본 파일이 어떻게 되어 있는지는 다음을 참고하면 된다.
https://github.com/jupyterhub/zero-to-jupyterhub-k8s/blob/main/jupyterhub/files/hub/jupyterhub_config.py
그 외에 chart 에도 다음의 설정이 필요하다.
singleuser:
serviceAccountName: "{username}" # kubespawner의 템플릿값이다. 고쳐야하는 값이 아니고 이대로 그냥 쓴다.
hub:
config:
JupyterHub:
authenticator_class: native
admin_users:
- admin
NativeAuthenticator:
open_signup: false
minimum_password_length: 8
Authenticator:
admin_users:
- admin
참고로, 해당 admin 도 처음에는 sign up을 해야한다. 그러고나서 admin 으로 로그인할 수 있다.
다른 사용자인 경우는 sign up을 한 뒤, admin이 인가를 해주면 그 때부터 사용할 수 있다.
사용자를 인가하기 전에 admin이 사용자의 환경을 셋팅해보도록 하자
사용자별 리소스 컨트롤
앞서 말했듯이 사용자들이 executor 파드를 자유롭게 사용할 수 있다. 이 의미는 몇개의 파드를 쓸지, cpu/memory는 얼마나할지, 그리고 어떤 버전의 스파크 이미지를 쓸지를 말한다. 그러므로 하나의 k8s 클러스터에 안정적인 리소스를 제공하기 위해서는 사용자별 리소스를 제한할 필요가 있다.
필자는 단순히 k8s에서의 기술을 사용하여 이를 해결하였다.
리소스 제한
필자는 다음과 같은 형상으로 주피터 노트북 파드와 executor 파드가 배포되도록 하였다.
주피터 노트북이 driver 역할을 하며, 기본적으로 노트북 driver 파드의 리소스는 고정되어 있다.(extraConfig을 활용하면 사용자별로 다르게 설정할 수 있기는하다)
- 네임스페이스: 사용자별 네임스페이스를 생성한다. 단 사용자 이름이 파드나 네임스페이스 이름 생성 규정이 맞지 않을 수 있기 때문에 주피터헙에서는 username 을 escape처리하여 사용한다. 참고로 위의 thomas.e 는 thomas-2ee 로 변경되서 사용된다는 점이다. 주피터헙에서 사용하는 방식은 slug로 시작하는 자체함수를 사용하고 있지만, 필자는 해당 함수보다는 escapism을 이용해서 네임스페이스를 생성하였다.
- 롤: 해당 네임스페이스에서의 어떤 권한을 줄 것인가를 정해야한다. 즉, 주피터 노트북 파드마다 각 네임스페이스를 컨트롤할수 있는 계정을 jupyterhub 네임스페이스에 생성하고, 롤을 부여해야 한다.
- apiGroups: [""]
resources: ["pods", "services", "configmaps", "secrets", "persistentvolumeclaims"]
verbs: ["get", "list", "watch", "create", "patch", "update", "delete", "deletecollection"]
- apiGroups: [""]
resources: ["pods", "services", "configmaps", "secrets"]
verbs: ["get", "list", "watch", "create", "patch", "update", "delete", "deletecollection"]
다 필요하지는 않은데, 기본적으로 pods에 대한 verb는 다 넣도록 하자(특히 deletecollection 을 누락하면 파드가 정리가 안된다)
롤바인딩은 jupyterhub 네임스페이스에 있는 유저 어카운트(serviceAccount) 를 해당 네임스페이스의 롤과 바인딩해야한다. 이 점 유의하기 바란다.
- 리소스쿼타: 실질적으로 사용자 리소스를 제한하는 설정이다. 아래를 참고해서 설정하면 된다.
apiVersion: v1
kind: ResourceQuota
metadata:
name: {username} # 유저이름에 따라 바꿔서 사용
namespace: {user namespace} # 유저 네임스페이스에 따라 바꿔서 사용. 유저이름과 동일하게 할것을 추천
spec:
hard:
requests.cpu: 10
requests.memory: 20Gi
limits.cpu: 10
limits.memory: 20Gi
비사용 노트북 자동 종료
보통 사용자들이 노트북을 사용하고나서 executor를 비롯해서 파드를 종료하지 않는다. 그렇기 때문에 불필요한 idle 리소스를 자동으로 정리해줘야하는데, 주피터헙 차트에서는 culler(https://github.com/jupyterhub/jupyterhub-idle-culler) 을 이용해 이미 기본적으로 이 기능을 지원하고 있다.
chart.cull 로 설정할 수 있다. 다음과 같이 기본값으로 되어있다.
cull:
enabled: true
users: false # --cull-users
adminUsers: true # --cull-admin-users
removeNamedServers: false # --remove-named-servers
timeout: 3600 # --timeout
every: 600 # --cull-every
concurrency: 10 # --concurrency
maxAge: 0 # --max-age
위 설정대로 보면 1시간 동안 노트북을 사용하지 않으면 종료시켜 버린다. driver 역할을 하는 노트북 파드가 종료되기 때문에 이어서 executor파드들도 정리가 된다. 참고로 idle을 판단하는 기준은 주피터헙 api 에서 last_activity (datetime) 을 가지고 한다.
필자가 테스트 했을때 last_activity는 다음의 경우에 갱신/비갱신 된다.
- 브라우저에 계속 탭을 열어놨을때(다른탭을 사용해도 괜찮다. 1분마다) 갱신된다.
- 네트워크가 끊어졌을때는 갱신되지 않는다.
보통 평일 기준으로 작업하다가 어떤 잡을 돌려놓고 퇴근한뒤 그 결과를 보는 경우가 있으므로 timeout을 1시간이 아닌 하루로 하는게 적당할 것 같다.
한가지 팁을 말하지면, 퇴근을 하느라 네트웍이 오랫동안 끊어진 경우 결과가 노트북 output에 갱신되지 않는다. 그래서 결과를 볼수가 없는데, 다음날 노트북 탭(닫지 않고 그대로 열어둔 상태의)에 접근하면 Restart/Dismiss 팝업이 나오는데, 이 때 절대 버튼을 클릭하지 않도록 한다. 팝업 다른 곳을 클릭해서 팝업을 지우고 브라우저 탭을 리프레시하면 노트북 output에 결과가 나온다.
다만 기본적으로 노트북의 Output 버퍼값이 크지 않으므로 이를 늘려놓는게 좋다. 다음의 설정을 chart.singleuser.extraFiles에 추가하도록 하자
singleuser:
extraFiles:
singleUserConfig:
mountPath: /usr/local/etc/jupyter/jupyter_server_config.json
data:
ServerApp:
iopub_data_rate_limit: 100000000
다양한 커널 지원
Spark를 k8s에 돌리는 가장 큰 이유중 하나일 것이다. 하둡을 기반으로 할 때는 스파크 버전도 신경써야하지만 k8s에서는 다양한 버전의 스파크를 운용할 수 있다.(이 얘기는 사실 하둡 생태계에 대한 경험이 부족해 약간 틀린 얘기일 수도 있다. 적어도 하둡2에서는 하둡과 스파크 버전이 맞아야 하는 것으로 알고 있다)
기본적으로 주피터헙에서는 프로필이란걸 지원하고 있어서 프로필마다 다른 노트북 이미지를 사용 할 수 있다. 다음은 프로필 예시다.
singleuser:
profileList:
- display_name: "spark3.5.2-iceberg1.5.2-python3.11-scala2.12-java17"
default: true
kubespawner_override:
image: "pyspark-notebook:spark3.5.2-iceberg1.5.2-python3.11-scala2.12-java17"
env:
EXECUTOR_IMAGE: "spark:3.5.2-iceberg1.5.2-python3.11-scala2.12-java17"
주피터헙은 kubespawner(https://github.com/jupyterhub/kubespawner)라는 자체 파드컨트롤 오퍼레이터를 제작했는데, 보통 CRD를 통한 operator 패턴으로 역할의 분리에 익숙한 필자에게는 다소 원초적인 방법으로 느껴졌다.
여하튼, kubespawner_override 로 다양한 노드북 이미지 설정이 가능하다.
필자인 경우 각 노트북과 스파크 버전을 맞추기 힘들었는데, 그 이유는 공식 dockerhub 등에서 제공하는 노트북과 스파크간의 버전이 잘 맞지 않기 때문이다. 기본적으로 스파크를 잘 동작시키려면, 자바/파이썬/스파크/하둡 등의 버전이 동일해야 하는데, 노트북과 스파크가 동일 관리가 되는게 아니라서 조금씩 버전이 다르다. 그렇기 때문에 위에서 언급한 Dockerfile을 기준으로 직접 생성해야 한다.
손쉬운 스파크 설정
사용성을 위해서 가장 신경써야할 부분이었다. 일반적으로 스파크 세션생성을 위해 설정을 하려면, 상당히 많은 설정이 필요하다. 사용자가 일일히 하기에는 불편한 점이 많다. 이런 설정은 PYSPARK_SUBMIT_ARGS 환경변수를 셋팅해 줌으로 어느정도 해결이 가능하다.
- 마스터 설정: --master k8s://https://kubernetes.default.svc
- 로그 설정: --conf spark.eventLog.enabled=true --conf spark.eventLog.compress=true --conf spark.eventLog.dir=.spark-logs --spark.eventLog.dir=file://home/jovyan/.spark-logs
그 외에 다른 설정들도 고정되어 들어가는게 있으면 위 환경변수에 설정해주면 된다. 주의할 점은 기본 환경변수에 덮어 쓰는거라서 마지막에 꼭 pyspark-shell 을 추가해줘야 한다는 점이다. (PYSPARK_SUBMIT_ARGS="--master ~~ --conf ~~ pyspark-shell")
위 환경변수는 chart.singleuser.extraEnv 에 추가하면 된다.
설정된 환경변수를 이용해 새로운 환경변수를 만드는 일도 필요한데, 위의 value에 추가하지 말고 cmd를 이용하거나 k8s postStart hook 을 이용해야 한다.
singleuser:
cmd:
- "/bin/bash"
- "-c"
- |
export PYSPARK_SUBMIT_ARGS="\"${PYSPARK_SUBMIT_ARGS} --conf spark.kubernetes.namespace=${ESCAPED_USER} --conf spark.driver.host=$(hostname -i) --conf spark.ui.proxyBase=${JUPYTERHUB_SERVICE_PREFIX}spark/ui --conf spark.ui.proxyRedirectUri=/ --conf spark.eventLog.dir=file://${HOME}/${SPARK_LOG_DIR_NAME} --conf spark.kubernetes.container.image=${EXECUTOR_IMAGE} pyspark-shell\""
SPARK_HISTORY_OPTS="-Dspark.ui.proxyBase=${JUPYTERHUB_SERVICE_PREFIX}spark/history-ui -Dspark.ui.proxyRedirectUri=/ -Dspark.history.fs.logDirectory=file://${HOME}/${SPARK_LOG_DIR_NAME} -Dspark.eventLog.compress=true" SPARK_LOG_DIR="${HOME}/${SPARK_LOG_DIR_NAME}/history" ${SPARK_HOME}/sbin/start-history-server.sh
jupyterhub-singleuser
extraEnv 에 설정한 PYSPARK_SUBMIT_ARGS 에 추가로 설정하는 예시다. 사용자가 사용할 수 있는 네임스페이스는 사용자 이름으로 정해져 있기 때문에 미리 설정해두는 것이 가능하다. 또한 driver host인 경우 파드의 ip(k8s 서비스가 아님) 설정이 필요한데 $(hostname -i) 를 사용할수 있기 때문에 사용자가 일일히 ip를 찾아 설정하지 않아도 된다. executor 파드 이미지도 미리 설정해두면 좋다.
만약에 postStart로 하게 된다면 환경변수를 파일로 저장한 후에 cmd에서 다시 로드해야 한다는 점을 유의하자.
이렇게 설정해두면, 사용자가 스파크 세션 생성시에 위의 설정들은 자동 설정 되므로 무척 편리해진다. 좀 더 복잡한 설정을 고려한다면, 별도의 파이선 라이브러리를 제공하는 게 필요해보인다. (aws boto lib와 같은)
사용자별 스파크UI / 히스토리 서버
스파크에는 스파크 잡 상태를 확인하는 스파크UI 사용이 필수다. 또한 이미 실행되어 종료된 잡의 히스토리를 보기위해서는 히스토리 서버도 별도의 프로세스로 띄어져야 한다.
바로 위 PYSPARK_SUBMIT_ARGS 예제에서 히스토리 서버 설정 및 실행 코드도 같이 포함되어 있는걸 눈치챘을 것이다.
SPARK_HISTORY_OPTS="-Dspark.ui.proxyBase=${JUPYTERHUB_SERVICE_PREFIX}spark/history-ui -Dspark.ui.proxyRedirectUri=/ -Dspark.history.fs.logDirectory=file://${HOME}/${SPARK_LOG_DIR_NAME} -Dspark.eventLog.compress=true" SPARK_LOG_DIR="${HOME}/${SPARK_LOG_DIR_NAME}/history" ${SPARK_HOME}/sbin/start-history-server.sh
스파크 UI는 스파크 세션을 생성하면 자동으로 생성이 되는데, 문제는 브라우저로 어떻게 접근하냐 이다. 주피터헙 차트를 배포하면 주피터헙 파드외에 프락시 파드가 하나 띄어져 있는걸 알수 있다.
이 프락시 파드를 통해서 각 사용자 노트북에도 접근할수 있는 방식인데, 이 프락시는 주피터헙에 의해서 컨트롤이 된다. 그래서 프락시의 정확한 이름이 configurable-http-proxy(chp) 이다.
스파크 UI와 히스토리 서버에 접근하려면 이 외에 추가적으로 jupyter_server_proxy 플러그인을 노트북에 설치해야 한다. 그러면 이 플러그인이 스파크UI는 4040포트로, 히스토리서버는 18080포트로 접근가능하도록 해준다.
RUN pip3 install jupyter-server-proxy \
&& jupyter server extension enable --sys-prefix jupyter_server_proxy
그러면 사용자는 다음과 같이 접근이 가능하다.
스파크UI: https://jupyterhub/user/thomas.e/proxy/4040
스파크 히스토리 서버: https://jupyterhub/user/thomas.e/proxy/18080
이게 끝이 아니다. 프락시되어 루트 path 가 달라지기 때문에 페이지가 다 깨져서 보이거나 제대로 안보이게 된다(executor 탭 등).
이를 해결하려면 다음의 두 설정이 필수이다.
-Dspark.ui.proxyBase=${JUPYTERHUB_SERVICE_PREFIX}spark/ui -Dspark.ui.proxyRedirectUri=/
근데 설정을 보면 proxy path가 위에서 말한 proxy/4040 형태와 다른걸 알수 있다. 필자는 proxy/4040 조차 불편하다고 생각되어 좀 더 직관적인 방법을 추가로 적용해보았다.
- 스파크UI: https://jupyterhub/user/thomas.e/spark/ui (proxyBase: ${JUPYTERHUB_SERVICE_PREFIX}spark/ui)
- 스파크 히스토리 서버: https://jupyterhub/user/thomas.e/spark/history-ui (proxyBase: ${JUPYTERHUB_SERVICE_PREFIX}spark/history-ui)
단순히 설정만 해주면 안된다. 실제 해당 주소를 입력했을때 proxy/로 매핑해주는 것을 해줘야 하며, 필자의 환경에서는 ingress-nginx 설정을 통해서 해결 했다. 이 설정은 chart.ingress.annotations에 해주면 된다(ingress-nginx를 사용하지 않으면 proxy path를 그대로 쓰거나 해당 Ingress controller의 설정 방식을 찾아봐야한다).
참고로 해당 패스명은 그대로 사용할 것을 권한다. spark ui 프론트엔드 코드상 특정 키워드에 대해 path 처리 버그가 있는 걸로 보이기 때문이다.
ingress:
enabled: true
annotations:
nginx.ingress.kubernetes.io/use-regex: "true"
nginx.ingress.kubernetes.io/server-snippet: |
rewrite ^/user/(.+)/spark/ui(.*) /user/$1/proxy/4040$2 last;
rewrite ^/user/(.+)/spark/history-ui(.*) /user/$1/proxy/18080$2 last;
TMI. ingress-nginx 서버스니펫 이용시에는 메트릭 지표가 정상적으로 나타나지 않을 수 있다. 그 이유는 보통 ingress 가 생성되면 nginx.conf 에 설정값이 추가되는데 이때 자동으로 메트릭 지표 수집 코드가 포함된다. 하지만 서버스니펫은 말그대로 자동 설정하는 부분을 패스할 수 있다. 그렇기 때문에 넣고 싶다면 ingress-nginx lua 플러그인중 하나인 monitor.lua를 살펴보고 monitor.call()을 호출해야한다.
이렇게 설정하면 사용자별로 UI와 히스토리 서버를 사용할 수 있다. 또한 인증까지 처리해주기 때문에 보안기능도 제공할 수 있다.
사용자별 자유로운 Executor 사용
사실 위의 설정들을 통해서 이미 사용자별 executor 파드를 자유로이 사용할 수 있다. 예시만을 언급한다.
from pyspark.sql import SparkSession
spark = (
SparkSession.builder
.appName("default dev")
.config("spark.executor.instances", "2") # executor 개수
.config("spark.kubernetes.executor.limit.cores", 1) # resourcequota 적용했을때 반드시 기입해줘야 한다.
.config("spark.executor.cores", "1") # executor 당 cpu
.config("spark.executor.memory", "1g") # executor 당 memory
# 기타 설정들~~
.getOrCreate()
)
참고로, memory 를 1g로 했지만 실제 사용할 수 있는 메모리는 Executor.memory(위 설정값의 절반)+max(10%, 384Mi)(spark.executor.memoryOverhead) 으로 설정된다.
executor 가 종료되는 조건은 다음과 같다.
- 사용자가 spark.stop()을 명시적으로 노트북에서 호출 했을때
- 사용자가 노트북 파드를 명시적으로 종료했을때
- 사용자가 커널을 리셋했을때
- cull에 의해 idle timeout 처리되어 강제로 노트북 파드가 종료됐을때
PersistentVolume
주피터헙 차트 설정으로 손쉽게 PV를 유저별로 제공할 수 있다. 이 부분에 대해서 구체적으로 언급할 생각은 없는데, 그 이유는 워낙 클러스터 환경별로 PV 지원 방식이 다르기 때문이다. PV가 정상적으로 붙지 않는 Pending 현상을 보통 마주할수 있지만, 이 부분은 클러스터 형상과 기타 인프란 지원 여부에 따라 해결 방법이 달라진다.
한가지 팁이자면, 사용자간 자료 공유를 위해서 shared 디렉토리를 nfs pv(ReadWriteMany)를 이용해 추가해보는 것도 사용성을 향상시키는 좋은 방법 중 하나가 아닐까 한다.
Spark Cluster 모드
앞서서 client 모드에 대해서만 설명한다고 하긴 했지만, 사실 cluster 모드도 사용할수 있으며, 크게 다른건 없다. 그냥 터미널에서 spark-submit 을 하고 --deploy-mode cluster 옵션을 추가하고, driver 리소스 스펙에 대해서만 명시하면 driver 도 별도의 파드로 생성된다. 다만 애플리케이션 파일을 외부 리모트에 저장하고 써야하기 때문에 이는 주피터 노트북에서는 굳이 할 필요가 없다고 생각이 되기 때문이다. 또한 결과도 driver 파드에만 콘솔로그로 표시되기 때문에 노트북에 나타나지 않는다.
그 외의 팁
- chart.proxy.secretToken: 주피터헙과 proxy간의 설정을 주고 받기위한 보안 토큰에 해당되는데, 이 값을 주지 않으면 배포시마다 랜덤으로 생성된다. 보안상 랜덤이 좋을거 같지만, 문제는 주피터헙과 프락시 파드가 전부 재실행되기 때문에 재배포시 노트북을 사용중이 유저가 있으면 세션이 종료되면서 재로그인해야하는 번거로움이 발생한다. 그렇기 때문에 고정된 값을 사용하는걸 추천한다. 고정하면 프락시는 재시작하지 않는다.
- chart.hub.cookieSecret: 쿠키 시크릿에 해당하는데, 이것도 설정하지 않으면 배포시마다 랜덤하게 바뀐다. 고정해야 사용자가 재로그인을 하지 않는다.
- 노트북 파드(singleuser)의 설정을 동적으로 하고 싶으면 chart.hub.extraConfig을 잘 활용해보길 바란다. Hub에서 kubespawner를 이용해 노트북 파드를 생성하는데, pre_spawn_hook을 이용하여 세밀한 컨트롤이 가능하다. kubespawner에서 사용하는 템플릿 필드이 있다. 이 것도 잘 활용해볼 수 있다(https://jupyterhub-kubespawner.readthedocs.io/en/latest/templates.html)
- 사내 네트웍이라서 노트북에 프록시 설정을 적용하고 싶다면 chart.singleuser.extraEnv에 프록시 환경변수를 설정하면 된다.
참고자료
https://github.com/jupyterhub/zero-to-jupyterhub-k8s
https://spark.apache.org/docs/latest/running-on-kubernetes.html
https://jupyterhub.readthedocs.io/en/stable/index.html
https://github.com/jupyterhub/kubespawner
https://github.com/jupyter/docker-stacks
https://github.com/apache/spark-docker
https://github.com/jupyterhub/jupyterhub-idle-culler
'Development' 카테고리의 다른 글
모든 워커노드에서 특정 컨테이너 이미지 삭제 방법 (0) | 2024.11.06 |
---|---|
GPT와 개발 (0) | 2024.11.02 |
Prometheus TSDB 분석 (0) | 2024.01.28 |
nvidia dcgm-exporter(gpu-exporter) (0) | 2024.01.18 |
운영 자동화와 최적화 (0) | 2024.01.15 |