• Docker部署jupyterhub及自定制(亲测)


    Docker 安装自定制 jupyterhub

    • 官方最新 jupyterhub 镜像存在问题,这里使用1.0.0版本
    • 默认使用 linux 用户体系进行用户认证,需要在 jupyterhub 的 Docker 容器中,/home 路径需要加创建文件夹的权限

    部署流程

    1. 拉取镜像 
      docker pull jupyterhub/jupyterhub:1.0.0 
      docker pull jupyterhub/singleuser:1.0.0
    2. 创建 jupyterhub_network 网络 
      docker network create --driver bridge jupyterhub_network
    3. 创建 jupyterhub 的 volume  
      mkdir -pv /data/jupyterhub/jupyterhub-custom  # 用于创建自定制的文件
      mkdir -pv /data/jupyterhub/jupyterhub-docker-con # 用于映射docker容器内部的路径,如/home
      chmod -R 777 /data/jupyterhub
    4. 在 /data/jupyterhub/jupyterhub-custom 下创建 jupyterhub_config.py 文件
      # coding:utf-8
      
      from tornado import gen
      from jupyterhub.auth import Authenticator
      import os
      import pwd
      import requests
      
      class MyAuthenticator(Authenticator):
      
          def system_user_exists(self, username):
              """Check if the user exists on the system"""
              try:
                  self.log.info('create user: %s' % username)
                  pwd.getpwnam(username)
              except Exception as e:
                  self.log.error('create password for user error: %s' % e)
                  return False
              else:
                  return True
      
          def add_system_user(self, username, password):
              """Create a new local UNIX user on the system.
              Tested to work on FreeBSD and Linux, at least.
              """
              res = os.system('useradd  %(name1)s ' % {'name1': username})
              if res:
                  self.log.warn('user %s create failure: %s' % (username, res))
                  return False
      
              # res = os.system('echo %(pass)s |passwd --stdin %(name1)s' % {'name1': username, 'pass': password})
              res = os.system('echo %(name1)s:%(pass)s | chpasswd' % {'name1': username, 'pass': password})
      
              if res:
                  self.log.warn('user %s password create failure: %s' % (username, res))
                  return False
              return True
      
          def check_token_local(self, token):
              sec = 'l55cj)hh95jorr6!vmhleo0tuyors)xy@@+jaj-^l6wp)))=d$'
              algorithm = 'HS256'
              try:
                  d = jwt.decode(token, sec, algorithm)
                  return d.get('user_id')
              except:
                  return None
      
          @gen.coroutine
          def authenticate(self, handler, data):
              '''
      
              :param handler:
              :param data:
              :return: 成功:username,失败:None
              '''
              self.log.warn(data)
              token = data.get('token')
              self.log.warn('request token is: %s' % token)
              if not token:
                  return None
      
              # 验证token
              user_id, username = self.check_token_local(token)
              self.log.warn('--- current user id: %s' % user_id)
      
              if not user_id or not username:
                  return None
      
              user = 'user_%s' %user_id
              password = 'deault_jupyter_pwd_random_string_for_user'
      
              if not self.system_user_exists(user):
                  if self.add_system_user(user, password):
                      return user
                  else:
                      return None
      
              return user
      
      
              #user = handler.request.headers.get("User_info")
              #if user is not None:
              #    user = json.loads(user)
              #    username = user.get("username")
              #    return username
      
      c.JupyterHub.authenticator_class = MyAuthenticator
      
      c.PAMAuthenticator.encoding = 'utf8'
      
      # 指定cookie secret的文件,内容必须是64位哈希字符串,如6dd65ff19de7b8cb6d53031b0ad940e7379e15cf7ab612094d19e8b5141cc52c
      # c.JupyterHub.cookie_secret_file = '/srv/jupyterhub/jupyterhub_cookie_secret'
      
      #创建用户时已经开指定的目录,这里就不需要在指定工作目了
      #c.Spawner.notebook_dir = '/data/file'
      
      #开启管理员用户
      c.JupyterHub.admin_access = True
      c.JupyterHub.admin_users = {"jupyterhub", "root"}
      
      # 白名单
      # c.Authenticator.whitelist = {}
      
      # Jupyterhub service setting
      # c.JupyterHub.spawner_class = 'sudospawner.SudoSpawner'
      c.JupyterHub.base_url = '/jupyter/'
      c.JupyterHub.cookie_max_age_days = 1  # cookie有效期为1天,默认值14为2周
      
      # customer templstes path, default is []
      c.JupyterHub.template_paths = ["templates"]
      使用jwt对进行自定义token认证
    5. 在 /data/jupyterhub/jupyterhub-custom 下创建userlist文件,写入admin用户,该用户是容器的管理员用户
      jupyterhub admin
      root admin
    6. 在 /data/jupyterhub/jupyterhub-custom 下创建 Dockerfile
      ARG BASE_IMAGE=jupyterhub/jupyterhub:1.0.0
      FROM ${BASE_IMAGE}
      
      ADD templates /srv/jupyterhub/templates
      ADD jupyterhub_config.py /srv/jupyterhub
      ADD userlist /srv/jupyterhub
      
      RUN echo "[global]
      index-url = https://mirrors.aliyun.com/pypi/simple/" > /etc/pip.conf &&
          pip install --no-cache --upgrade jupyter &&
          pip install --no-cache dockerspawner &&
          pip install --no-cache oauthenticator  &&
          chmod -R 777 /home
      EXPOSE 8000
      
      USER root
    7. 执行 build 命令构建镜像 
      docker build -t custom/jupyterhub .
    8. 在 /data/jupyterhub/jupyterhub-custom 下创建 singleuser 文件夹,在该文件夹下创建 Dockerfile
      ARG BASE_IMAGE=jupyterhub/singleuser:1.0.0
      FROM ${BASE_IMAGE}
      
      RUN pip install jupyterlab &&
          jupyter serverextension enable --py jupyterlab --sys-prefix
      
      USER root
    9. 在 /data/jupyterhub/jupyterhub-custom/singleuser 下执行build命令构建镜像
       docker build -t custom/jupyter_lab_singleuser .
    10. 创建/data/jupyterhub/jupyterhub-docker-con/docker-home用于映射容器内部的/home路径
    11. 开启容器
      docker run -d --name jupyterhub -p18000:8000  
      --network jupyterhub_network 
      -v /var/run/docker.sock:/var/run/docker.sock  
      -v /data/jupyterhub/jupyterhub-custom:/srv/jupyterhub 
      -v /data/jupyterhub/jupyterhub-docker-con/docker-home:/home  
      jupyterhub/jupyterhub:latest 
    12. 进入容器,修改 /home路径在的权限
      docker exec -it jupyterhub bash
      chmod -R 777 /home

    前端自定制

      jupyterhub 内核使用 Tornado 框架开发,前后端不分离,使用的是后端 render 或者 redirect 配合前端 jinja2 模板引擎渲染的方式实现,类似于 Django。

      默认支持自定制几个基本的前端页面,自定制的 HTML 文件需要放在上述 jupyterhub-custom 路径的 template 文件夹下(template 文件夹需自行创建),然后在 jupyterhub_config.py 中加入一行 c.JupyterHub.template_paths = ["templates"]

      jupyterhub_config.py 为 jupyterhub 的配置文件,在服务中有一份默认的配置,用户自己创建的 jupyterhub_config.py 中的配置优先级会大于默认配置,如:

    # 默认jinja模板路径配置
    c.JupyterHub.template_paths = []  
    # 自定义配置
    c.JupyterHub.template_paths = ["templates"]  
    # 如果不进行自定义配置,即使有HTML文件,服务也找不到
    • 支持自定义的 HTML 文件如下:
      • login.html:登录页面
      • home.html:个人主页
      • token.html:token页面
      • 404.html
      • admin.html
      • error.html
      • logout.html
      • page.html:其他 html 的基类模板
      • not_running.html
      • oauth.html
      • spawm.html
      • spawn_pending.html
      • stop_pending.html
    • 其他的深度自定制则需要进入容器中修改源码,如
      • 自定制后端登录功能:需要修改 /opt/conda/lib/python3.6/site-packages/jupyterhub/login.py 
      • 自定制 notebook 页面的导航条:需要修改 /opt/conda/lib/python3.6/site-packages/jupyterhub/singleuser.py 
      • 深度自定制 notebook 页面:需要修改 /opt/conda/lib/python3.6/site-packages/notebook/templates/tree.html

    HTML 代码来源

      jupyterhub 的 jinja 模板文件(即那些HTML文件)用了大量的模板继承(extend语法),修改这些文件前需要先明白模板的继承顺序。

    1.  /opt/conda/sharejupyterhub/templates:支持被自定义的HTML文件,也就是说想修改这些文件不需要修改源码,只需要在 jupyterhub-custom 路径的 template 文件夹下有同名文件就可覆盖
    2.  /opt/conda/lib/python3.6/site-packages/notebook/templates:不支持被自定义,也就是说想修改这些文件需要直接修改源码
    3. 后端代码:其他

    模板继承、通过后端自定制前端

    • “1”中有一个 page.html 是 “1” 中其他 HTML 文件的基板,也就是说 “1” 中其他 HTML 文件都继承了 page.html。
    • “2”中有一个 page.html 是 “2” 中其他 HTML 文件的基板,也就是说 “2” 中其他 HTML 文件都继承了 page.html。
    • “2”中有的 page.html 也是 “1”中 page.html 的基板,也就是说 “1”中的 page.html 继承了 “2” 中的 page.html。
    • 后端代码中有很多 HTML 格式的字符串直接 render 到前端,需要自行研究。如:
      • “2”中的 page.html 页面的导航条(也是所有其他页面的导航条),自定制该导航条需要修改 /opt/conda/lib/python3.6/site-packages/jupyterhub/singleuser.py 的 page_template 变量
      • .......

    其他自定制

       主要是修改源码,未完待续。。

                             

  • 相关阅读:
    JavaScript学习总结(三)——闭包、IIFE、原型、函数与对象
    JavaScript学习总结(二)——延迟对象、跨域、模板引擎、弹出层、AJAX示例
    CSS3与页面布局学习总结(八)——浏览器兼容与前端性能优化
    CSS3与页面布局学习总结(七)——前端预处理技术(Less、Sass、CoffeeScript、TypeScript)
    CSS3与页面布局学习总结(六)——CSS3新特性(阴影、动画、渐变、变形、伪元素等)
    CSS3与页面布局学习总结(四)——页面布局大全
    CSS3与页面布局学习总结(五)——Web Font与Sprite
    CSS3与页面布局学习总结(三)——BFC、定位、浮动、7种垂直居中方法
    CSS3与页面布局学习总结(二)——Box Model、边距折叠、内联与块标签、CSSReset
    HTML5 学习总结(四)——canvas绘图、WebGL、SVG
  • 原文地址:https://www.cnblogs.com/zhuminghui/p/13542094.html
Copyright © 2020-2023  润新知