一個web編程界面送給愛寫代碼的你

image

背景:最近一直在研究jupyter相關(guān)的知識,jupyterhub jupyterlab jupyternotebooks等,經(jīng)過不斷的努(踩)力(坑)以及數(shù)十根頭發(fā)的代價,終于把jupyterhub成功部署在k8s里了寞埠!本篇文章內(nèi),只講述了如何把jupyterhub部署在k8s里,包括配置文件的講解(不包括docker環(huán)境下,docker環(huán)境下,詳見下篇文章)廢話不多說,直接來干貨冒萄!

集群環(huán)境 集群版本 搭建方式 helm版本
master v1.16.8 kubeadm v3.1.1
node1 v1.16.8 kubeadm v3.1.1
node2 v1.16.8 kubeadm v3.1.1

安裝helm

wget https://get.helm.sh/helm-v3.1.1-linux-amd64.tar.gz
tar xf helm-v3.1.1-linux-amd64.tar.gz
cp linux-amd64/helm /usr/local/bin/
helm version
  • 添加jupyterhub的repo源并更新
helm repo add jupyterhub https://jupyterhub.github.io/helm-chart/

helm repo update

安裝ldap

ldap可以安裝在k8s內(nèi)也可以安裝在VM中,看你需求而定
我這里是把ldap安裝到k8s中了,過程略!

安裝nfs-client-provisioner

安裝nfs-client-provisioner用來做動態(tài)存儲,即web頁面中寫完的代碼存儲位置
安裝nfs-client-provisioner過程略!

  • 將jupyterhub相關(guān)配置拉取到本地進(jìn)行編輯
helm pull jupyterhub/jupyterhub https://jupyterhub.github.io/helm-chart/ --untar && cd jupyterhub

更改配置文件

custom: {}

hub:
  service:
    type: ClusterIP
    annotations: {}
    ports:
      nodePort:
    loadBalancerIP:
  baseUrl: /
  cookieSecret:
  publicURL:
  initContainers: []
  uid: 1000
  fsGid: 1000
  nodeSelector: {}
  concurrentSpawnLimit: 64
  consecutiveFailureLimit: 5
  activeServerLimit:
  deploymentStrategy:
    ## type: Recreate
    ## - sqlite-pvc backed hubs require the Recreate deployment strategy as a
    ##   typical PVC storage can only be bound to one pod at the time.
    ## - JupyterHub isn't designed to support being run in parallell. More work
    ##   needs to be done in JupyterHub itself for a fully highly available (HA)
    ##   deployment of JupyterHub on k8s is to be possible.
    type: Recreate
  db:
    type: sqlite-pvc
    upgrade:
    pvc:
      annotations: {}
      selector: {}
      accessModes:
        - ReadWriteOnce
      storage: 1Gi
      subPath:
# 提前創(chuàng)建好的sc
      storageClassName: nfs-jupyterhub
    url:
    password:
  labels: {}
  annotations: {}
  extraConfig:
    jupyterlab: |
      c.Spawner.cmd = ['jupyter-labhub']
  extraConfigMap: {}
  extraEnv: {}
  extraContainers: []
  extraVolumes: []
  extraVolumeMounts: []
  image:
    name: jupyterhub/k8s-hub
    tag: '0.9.0'
    # pullSecrets:
    #   - secretName
  resources:
    requests:
      cpu: 200m
      memory: 512Mi
  services: {}
  imagePullSecret:
    enabled: false
    registry:
    username:
    email:
    password:
  pdb:
    enabled: true
    minAvailable: 1
  networkPolicy:
    enabled: false
    ingress: []
    egress:
      - to:
          - ipBlock:
              cidr: 0.0.0.0/0
  allowNamedServers: false
  namedServerLimitPerUser:
  authenticatePrometheus:
  redirectToServer:
  shutdownOnLogout:
  templatePaths: []
  templateVars: {}
  livenessProbe:
    enabled: false
    initialDelaySeconds: 30
    periodSeconds: 10
  readinessProbe:
    enabled: true
    initialDelaySeconds: 0
    periodSeconds: 50
  # existingSecret: existing-secret

rbac:
  enabled: true


proxy:
# 使用命令openssl rand -hex 32生成的十六進(jìn)制字符串粘貼到下面
  secretToken: 'e734c6d0d526140a7de392cf67ec7bc990625be1e8502da672b829df8ef34e91'
  deploymentStrategy:
    ## type: Recreate
    ## - JupyterHub's interaction with the CHP proxy becomes a lot more robust
    ##   with this configuration. To understand this, consider that JupyterHub
    ##   during startup will interact a lot with the k8s service to reach a
    ##   ready proxy pod. If the hub pod during a helm upgrade is restarting
    ##   directly while the proxy pod is making a rolling upgrade, the hub pod
    ##   could end up running a sequence of interactions with the old proxy pod
    ##   and finishing up the sequence of interactions with the new proxy pod.
    ##   As CHP proxy pods carry individual state this is very error prone. One
    ##   outcome when not using Recreate as a strategy has been that user pods
    ##   have been deleted by the hub pod because it considered them unreachable
    ##   as it only configured the old proxy pod but not the new before trying
    ##   to reach them.
    type: Recreate
    ## rollingUpdate:
    ## - WARNING:
    ##   This is required to be set explicitly blank! Without it being
    ##   explicitly blank, k8s will let eventual old values under rollingUpdate
    ##   remain and then the Deployment becomes invalid and a helm upgrade would
    ##   fail with an error like this:
    ##
    ##     UPGRADE FAILED
    ##     Error: Deployment.apps "proxy" is invalid: spec.strategy.rollingUpdate: Forbidden: may not be specified when strategy `type` is 'Recreate'
    ##     Error: UPGRADE FAILED: Deployment.apps "proxy" is invalid: spec.strategy.rollingUpdate: Forbidden: may not be specified when strategy `type` is 'Recreate'
    rollingUpdate:
  service:
    type: NodePort
    labels: {}
    annotations: {}
    nodePorts:
      http:
      https:
    loadBalancerIP:
    loadBalancerSourceRanges: []
  chp:
    image:
      name: jupyterhub/configurable-http-proxy
      tag: 4.2.1
    livenessProbe:
      enabled: true
      initialDelaySeconds: 30
      periodSeconds: 10
    readinessProbe:
      enabled: true
      initialDelaySeconds: 0
      periodSeconds: 10
    resources:
      requests:
        cpu: 200m
        memory: 512Mi
  traefik:
    image:
      name: traefik
      tag: v2.1
    hsts:
      maxAge: 15724800 # About 6 months
      includeSubdomains: false
    resources: {}
  secretSync:
    image:
      name: jupyterhub/k8s-secret-sync
      tag: '0.9.0'
    resources: {}
  labels: {}
  nodeSelector: {}
  pdb:
    enabled: true
    minAvailable: 1
  https:
    enabled: true
    type: letsencrypt
    #type: letsencrypt, manual, offload, secret
    letsencrypt:
      contactEmail: ''
      # Specify custom server here (https://acme-staging-v02.api.letsencrypt.org/directory) to hit staging LE
      acmeServer: ''
    manual:
      key:
      cert:
    secret:
      name: ''
      key: tls.key
      crt: tls.crt
    hosts: []
  networkPolicy:
    enabled: false
    ingress: []
    egress:
      - to:
          - ipBlock:
              cidr: 0.0.0.0/0


auth:
  type: ldap
  whitelist:
    users:
# 管理員用戶(不利于管理建議不要使用 在這里創(chuàng)建了用戶,ldap里是不會同步的,所以不建議使用)
  admin: 
    access: true
    users:
      - laoshi
  dummy:
    password:
# auth.ldap.server.address并且auth.ldap.dn.templates是必需的
  ldap:
    server: 
      address: 10.20.80.203
      port: 31712
    dn:
      templates:
        - 'uid={username},ou=AI,dc=lhws,dc=com'
#      search: {}
#      user: {}
    user: {}
  state:
    enabled: false
    cryptoKey:


singleuser:
  extraTolerations: []
  nodeSelector: {}
  extraNodeAffinity:
    required: []
    preferred: []
  extraPodAffinity:
    required: []
    preferred: []
  extraPodAntiAffinity:
    required: []
    preferred: []
  networkTools:
    image:
      name: jupyterhub/k8s-network-tools
      tag: '0.9.0'
  cloudMetadata:
    enabled: false
    ip: 169.254.169.254
  networkPolicy:
    enabled: false
    ingress: []
    egress:
    # Required egress is handled by other rules so it's safe to modify this
      - to:
          - ipBlock:
              cidr: 0.0.0.0/0
              except:
                - 169.254.169.254/32
  events: true
  extraAnnotations: {}
  extraLabels:
    hub.jupyter.org/network-access-hub: 'true'
  extraEnv: 
    EDITOR: "vim"
  lifecycleHooks:
# 我這里做了定制,將會在用戶登陸到web界面后看到的目錄
    postStart:
      exec:
        command:
          - "sh"
          - "-c"
          - >
            cp -r /code/ /home/jovyan;
            cp -r /dataset/ /home/jovyan;
            cp -r /test/ /home/jovyan;
            cp -r /train/ /home/jovyan
  initContainers: []
  extraContainers: []
  uid: 1000
  fsGid: 100
  serviceAccountName:
  storage:
    type: dynamic
    extraLabels: {}
    extraVolumes: []
    extraVolumeMounts: []
    static:
      pvcName:
      subPath: '{username}'
# 默認(rèn)請求數(shù)據(jù)卷大小
    capacity: 10Gi
    homeMountPath: /home/jovyan
    dynamic:
# 提前創(chuàng)建好的sc
      storageClass: nfs-jupyterhub
      pvcNameTemplate: claim-{username}{servername}
      volumeNameTemplate: volume-{username}{servername}
      storageAccessModes: [ReadWriteOnce]
  image:
# 默認(rèn)是jupyterhub/k8s-singleuser-sample我這里做了定制 下文會提到關(guān)于定制
    name: harbor.k8s.fit/jupyter/k8s-singleuser-sample
    tag: '1.0'
    pullPolicy: IfNotPresent
    # pullSecrets:
    #   - secretName
  imagePullSecret:
    enabled: false
    registry:
    username:
    email:
    password:
  startTimeout: 300
# 資源限制部分保證有0.5個cpu上限為0.5個cpu
  cpu:
    limit: .5
    guarantee: .5
# 資源限制部分保證有1G內(nèi)存夷恍,上限為1G內(nèi)存
  memory:
    limit: 1G
    guarantee: 1G
  extraResource:
    limits: {}
    guarantees: {}
  cmd: jupyterhub-singleuser
# 默認(rèn)是/lab即jupyterlab 可以換成/tree即jupyternotebooks
  defaultUrl: "/lab"
  extraPodConfig: {}


scheduling:
  userScheduler:
    enabled: true
    replicas: 2
    logLevel: 4
    ## policy:
    ## Allows you to provide custom YAML/JSON to render into a JSON policy.cfg,
    ## a configuration file for the kube-scheduler binary.
    ## NOTE: The kube-scheduler binary in the kube-scheduler image we are
    ## currently using may be version bumped. It would for example happen if we
    ## increase the lowest supported k8s version for the helm chart. At this
    ## point, the provided policy.cfg may require a change along with that due
    ## to breaking changes in the kube-scheduler binary.
    policy: {}
    image:
      name: gcr.io/google_containers/kube-scheduler-amd64
      tag: v1.13.12
    nodeSelector: {}
    pdb:
      enabled: true
      minAvailable: 1
    resources:
      requests:
        cpu: 50m
        memory: 256Mi
  podPriority:
    enabled: false
    globalDefault: false
    defaultPriority: 0
    userPlaceholderPriority: -10
  userPlaceholder:
    enabled: true
    replicas: 0
  corePods:
    nodeAffinity:
      matchNodePurpose: prefer
  userPods:
    nodeAffinity:
      matchNodePurpose: prefer


prePuller:
  hook:
# 默認(rèn)是true 將會在集群所有節(jié)點上pull該鏡像
    enabled: false
    image:
      name: jupyterhub/k8s-image-awaiter
      tag: '0.9.0'
  continuous:
    enabled: true
  extraImages: {}
  pause:
    image:
      name: gcr.io/google_containers/pause
      tag: '3.1'

# ingress相關(guān)配置 我這里沒有設(shè)置證書
# 前提是安裝了 Ingress Controller 否則不會正常運行
ingress:
  enabled: true
  annotations:
    kubernetes.io/ingress.class: "nginx"
  hosts:
    - jupyterhub.lhws.com
  pathSuffix: ''
  tls: []


cull:
  enabled: true
  users: false
  removeNamedServers: false
  timeout: 3600
  every: 600
  concurrency: 10
  maxAge: 0


debug:
  enabled: false

部署jupyterhub

helm install jhub -f values.yaml . -n jhub

#假如配置文件有變動 對其更新命令如下

helm upgrade jhub -f values.yaml . -n jhub

默認(rèn)會pull國外的源 手動把鏡像pull到調(diào)度的節(jié)點并改tag

docker pull registry.aliyuncs.com/google_containers/pause:3.1 &&
docker pull registry.aliyuncs.com/google_containers/kube-scheduler:v1.13.12 &&
docker tag registry.aliyuncs.com/google_containers/pause:3.1 gcr.io/google_containers/pause:3.1 &&
docker tag registry.aliyuncs.com/google_containers/kube-scheduler:v1.13.12 gcr.io/google_containers/kube-scheduler-amd64:v1.13.12

驗證

[root@master jupyterhub]# kubectl get pod,svc,ing -n jhub -o wide
NAME                                                        READY   STATUS    RESTARTS   AGE     IP              NODE    NOMINATED NODE   READINESS GATES
pod/continuous-image-puller-cvwsx                           1/1     Running   0          126m    10.244.38.147   node1   <none>           <none>
pod/continuous-image-puller-lss88                           1/1     Running   0          126m    10.244.11.21    node2   <none>           <none>
pod/hub-589bc9bfb7-28wlx                                    1/1     Running   0          17m     10.244.38.151   node1   <none>           <none>
pod/jupyter-laoshi                                          1/1     Running   0          16m     10.244.11.26    node2   <none>           <none>
pod/jupyter-lijian                                          1/1     Running   0          13m     10.244.11.27    node2   <none>           <none>
pod/jupyter-q                                               1/1     Running   0          13m     10.244.11.28    node2   <none>           <none>
pod/jupyter-s                                               1/1     Running   0          13m     10.244.11.29    node2   <none>           <none>
pod/jupyter-z                                               1/1     Running   0          13m     10.244.38.152   node1   <none>           <none>
pod/nfs-jupyterhub-nfs-client-provisioner-6cd98c87c-b2fkk   1/1     Running   0          5h18m   10.244.11.8     node2   <none>           <none>
pod/proxy-55bcd85458-hlqnk                                  1/1     Running   0          5h17m   10.244.38.138   node1   <none>           <none>
pod/user-scheduler-865b49c54-zpxw2                          1/1     Running   0          5h17m   10.244.11.10    node2   <none>           <none>
pod/user-scheduler-865b49c54-zwj9s                          1/1     Running   0          5h17m   10.244.38.139   node1   <none>           <none>

NAME                   TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                      AGE     SELECTOR
service/hub            ClusterIP   10.244.72.60     <none>        8081/TCP                     5h17m   app=jupyterhub,component=hub,release=jhub
service/proxy-api      ClusterIP   10.244.126.226   <none>        8001/TCP                     5h17m   app=jupyterhub,component=proxy,release=jhub
service/proxy-public   NodePort    10.244.125.153   <none>        443:30853/TCP,80:32688/TCP   5h17m   component=proxy,release=jhub

NAME                            HOSTS                 ADDRESS                     PORTS   AGE
ingress.extensions/jupyterhub   jupyterhub.lhws.com   10.20.80.204,10.20.80.205   80      88m
  • NodePort形式訪問


    image
  • ingress形式訪問


    image

    左側(cè)列表中出現(xiàn)的就是我自定義的部分,上面的配置文件中講到了,有定制需求的,可以參考上面的配置文件講解

FROM jupyter/base-notebook:a07573d685a4
ARG JUPYTERHUB_VERSION=1.1.*
USER root
RUN apt-get update && apt-get install --yes --no-install-recommends \
    git \
 && rm -rf /var/lib/apt/lists/*
#這里是我自定義的目錄
RUN mkdir -p /dataset/ /test/ /train/ /code/
USER $NB_USER

RUN python -m pip install nbgitpuller \
    $(bash -c 'if [[ $JUPYTERHUB_VERSION == "git"* ]]; then \
       echo ${JUPYTERHUB_VERSION}; \
     else \
       echo jupyterhub==${JUPYTERHUB_VERSION}; \
     fi') && \
    jupyter serverextension enable --py nbgitpuller --sys-prefix

默認(rèn)是/lab即jupyterlab 你也可以切換到/tree這樣就是jupyternotebooks了

結(jié)束語

現(xiàn)在,ldap和jupyterhub成功關(guān)聯(lián)上了,在ldap中創(chuàng)建的用戶,可以登陸到j(luò)upyterhub中,不建議在配置文件定制用戶管理開啟admin 不利于管理,只使用ldap創(chuàng)建管理用戶即可隅要!

參考文獻(xiàn):
https://zero-to-jupyterhub.readthedocs.io/en/latest/setup-jupyterhub/setup-jupyterhub.html
https://zero-to-jupyterhub.readthedocs.io/en/latest/customizing/user-environment.html
https://zero-to-jupyterhub.readthedocs.io/en/latest/administrator/advanced.html
https://jupyterhub.github.io/helm-chart/
https://github.com/jupyterhub/zero-to-jupyterhub-k8s/blob/master/images/singleuser-sample/Dockerfile
https://github.com/jupyterhub/zero-to-jupyterhub-k8s

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末昧识,一起剝皮案震驚了整個濱河市疮丛,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌驴党,老刑警劉巖瘪撇,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡倔既,警方通過查閱死者的電腦和手機恕曲,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來渤涌,“玉大人佩谣,你說我怎么就攤上這事∈蹬睿” “怎么了茸俭?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長瞳秽。 經(jīng)常有香客問我瓣履,道長率翅,這世上最難降的妖魔是什么练俐? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮冕臭,結(jié)果婚禮上腺晾,老公的妹妹穿的比我還像新娘。我一直安慰自己辜贵,他們只是感情好悯蝉,可當(dāng)我...
    茶點故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著托慨,像睡著了一般鼻由。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上厚棵,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天蕉世,我揣著相機與錄音,去河邊找鬼婆硬。 笑死狠轻,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的彬犯。 我是一名探鬼主播向楼,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼谐区!你這毒婦竟也來了湖蜕?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤宋列,失蹤者是張志新(化名)和其女友劉穎昭抒,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡戈鲁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年仇参,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片婆殿。...
    茶點故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡诈乒,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出婆芦,到底是詐尸還是另有隱情怕磨,我是刑警寧澤,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布消约,位于F島的核電站肠鲫,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏或粮。R本人自食惡果不足惜导饲,卻給世界環(huán)境...
    茶點故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望氯材。 院中可真熱鬧渣锦,春花似錦、人聲如沸氢哮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽冗尤。三九已至听盖,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間裂七,已是汗流浹背皆看。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留碍讯,地道東北人悬蔽。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像捉兴,于是被迫代替她去往敵國和親蝎困。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,614評論 2 353

推薦閱讀更多精彩內(nèi)容