【话在前头】

用 Docker 部署 JupyterLab 感觉是部署 JupyterLab 最方便的方式了,官方提供了很多可选的镜像,也可以自己从 jupyter/base-notebook 中继续打包,镜像启动命令加上“--NotebookApp.password”就可以直接用密码登录用了。虽然只是自己一个人用,但是如果放在互联网上访问的话,总感觉不是那么安全,还是希望能像其他服务一样,能独立管理用户信息,能设置二次验证(2FA)。不过搜了下网上关于 JupyterHub 的资料比较少,甚至于官方的说明文档写的也不是很详细,有些配置和参数只能去源码里扒。

【文章索引】

  1. 打包 JupyterHub 镜像
  2. 配置 JupyterHub 启动参数
  3. 启动 JupyterHub
  4. 隔离 JupyterHub/JupyterLab 网络

【一、打包 JupyterHub 镜像】

JupyterHub 架构的介绍和原理官方文档中描述的非常清楚了,这里不再赘述了,简单说就是 JupyterHub 把 认证 和 单用户 JupyterLab 的管理 分别拆成了 Authenticator 和 Spawner 模块,可以根据不同的需要配置不同的认证方式或管理方式。不过官方的 JupyterHub 镜像只包含了 JupyterHub 项目 本身,只有最基本的认证和管理(如通过 Linux 下 PAM 进行认证、通过本地进程运行 JupyterLab 等)。如果想通过自定义账号密码、并且开启 2FA 的话,JupyterHub 其实也已经实现了一个官方的 NativeAuthenticator 模块,官方文档还是比较详细的,默认用户信息存储在 JupyterHub 的 sqlite 数据库中,可以通过数据源配置改成 Mysql,如果需要连接 Mysql 的话,官方的镜像也不包含相关模块,也需要自行安装。

除此之外,如果 JupyterHub 管理的 JupyterLab 也想在 docker 中运行的话,还需要使用官方提供的 DockerSpawner 进行管理,不过官方文档不是特别详细,好在代码不多,扒扒代码也能看明白具体应该怎么配置。

所以,如果我们需要实现能独立管理的用户信息、支持2FA、使用Mysql数据库存储用户数据,用户的 JuyterLab 也通过 docker 镜像进行运行和管理的话,我们可以通过如下的 Dockerfile 在官方镜像之上打一个更完整的镜像。

 ARG BASE_IMAGE=jupyterhub/jupyterhub:1.2
FROM $BASE_IMAGE LABEL maintainer="MaysWind <i@mayswind.net>" # Install Dependencies
RUN apt-get update \
&& apt-get install -y --no-install-recommends unzip \
&& rm -rf /var/lib/apt/lists/* \
&& rm -rf /tmp/* # Install Mysql
RUN pip3 --no-cache-dir install mysql-connector \
&& rm -rf /tmp/* # Install NativeAuthenticator
RUN curl "https://github.com/jupyterhub/nativeauthenticator/archive/master.zip" -L -o /tmp/nativeauthenticator.zip \
&& unzip /tmp/nativeauthenticator.zip -d /tmp \
&& mv /tmp/nativeauthenticator-master /usr/local/bin/nativeauthenticator \
&& pip --no-cache-dir install -e /usr/local/bin/nativeauthenticator \
&& rm -rf /tmp/* # Install DockerSpawner
RUN pip --no-cache-dir install dockerspawner \
&& rm -rf /tmp/*

注:写这篇博客的时候,JupyterHub 的最新 Release 版本是 1.1.0,但是 1.1.0 的 docker 镜像存在问题(静态资源没有编译等),所以这里使用的是还在开发中的镜像(1.2 tag 目前与 1.2.0dev tag 一致)。

【二、配置 JupyterHub 启动参数】

打完镜像后后其实就可以启动了,不过通常还有些配置需要调整下。我通过 docker-compose 启动 JupyterHub 容器,所有配置参数都通过参数或环境变量进行配置,同时由于 JupyterHub 在 docker 容器中,还需要把宿主机的 docker.sock 挂载到容器内,以便 JupyterHub 能够管理 JupyterLab 容器。并且为 JupyterHub 和之后的 JupyterLab 建了一个单独的网络,方便之后对 JupyterLab 的请求进行隔离,如果没有需求的话实际上按默认的网络配置也是可以的,相关的 yml 示例配置如下

 version: "2"
networks:
jupyter-network:
driver: bridge
ipam:
config:
- subnet: 192.168.254.0/24
gateway: 192.168.254.1
services:
jupyterhub:
image: 你的 JupyterHub 镜像名称
container_name: jupyterhub
hostname: "jupyterhub"
networks:
- "jupyter-network"
command:
- "jupyterhub"
- "--JupyterHub.hub_bind_url='http://:8081'" # JupyterHub 默认绑定 127.0.0.1,需要改成绑定所有 IP 使 JupyterLab 能跨容器访问
- "--JupyterHub.db_url='mysql+mysqlconnector://Mysql用户名:Mysql密码@数据库地址/数据库名称'" # 设置 Mysql 数据库,如果使用默认 Sqlite,可以挂载目录到 /srv/jupyterhub 实现数据库持久化
- "--JupyterHub.authenticator_class='nativeauthenticator.NativeAuthenticator'" # 使用 NativeAuthenticator
- "--JupyterHub.spawner_class='dockerspawner.DockerSpawner'" # 使用 DockerSpawner
- "--JupyterHub.admin_access=True" # 启用管理员功能
- "--Authenticator.admin_users={'管理员账户名称'}" # 管理员名称
- "--Authenticator.allow_2fa=True" # 开启 2FA 功能
- "--DockerSpawner.remove_containers=True" # 每次启动 JuypyterLab 容器时都删除之前的容器,如果通过 docker-compose 设置的网络,docker-compose 重新配置网络后一定要重新创建容器才能启动
- "--DockerSpawner.notebook_dir='/home/jovyan/work'" # 设置笔记本默认目录(默认是 ~)
- "--DockerSpawner.image='你的 JupyterLab 镜像名称'"
- "--DockerSpawner.network_name='JupyterLab 网络名称'" # 如果是通过 docker-compose 设置的网络,与第3行可能不一致,需要通过 docker network ls 查看
- "--DockerSpawner.args=['--Application.log_level=WARN']" # 设置日志默认输出级别
- "--DockerSpawner.environment={\
'JUPYTER_ENABLE_LAB': 'yes'\ # 开启 JupyterLab
}"
- "--DockerSpawner.volumes={\
'/etc/localtime': {'bind': '/etc/localtime', 'mode': 'ro'},\
'本机 Jupyter 笔记存储路径': '/home/jovyan/work'\ # 可以使用 “{username}” 占位,表示用户名,如 '/mnt/data1/jupyter/{username}/work': '/home/jovyan/work'
}"
volumes:
- "/etc/localtime:/etc/localtime:ro"
- "/var/run/docker.sock:/var/run/docker.sock"
restart: on-failure

其中,JupyterHub 配置文件中的配置都可以通过启动参数的方式进行配置,如上述配置中 command 中的配置项,所有 JupyterHub 配置可以参考官方文档。对于 NativeAuthenticator,也额外提供了一些其他参数,如自己注册完账号,可以设置“Authenticator.open_signup”参数为 False,关闭开放注册功能,“Authenticator.ask_email_on_signup” 注册时需要提供邮箱账号等,这些参数可以如上附到启动参数中,或者也可写入到配置文件中,更多参数和用法可以参考官方文档。对于 DockerSpawner,有些参数是实现了基础类 Spawner 中定义的,可以查阅 Spawner 的定义文档 进行配置,也有部分是其本身单独实现的,可以查阅其源代码,例如其支持限制内存 “DockerSpawner.mem_limit”、限制CPU “DockerSpawner.cpu_limit”等参数,都是实现基础类 Spawner 中定义的,Docker 网络名称 “DockerSpawner.network_name ”、启动容器前删除已有容器的参数 “DockerSpawner.remove_containers” 等都是其本身自己实现的。

如果之前也是通过 docker 部署的 JupyterLab,可能下述几个参数能迁移大部分之前的个性化配置,

  • DockerSpawner.args 可以追加 JupyterLab 容器的启动参数,默认启动命令是“start-notebook.sh --ip=0.0.0.0 --port=8888”,可以追加多个参数(如上述设置了配置了日志输出级别为WARN,JupyterLab 配置文件中的配置都可以使用此方式进行配置,相关配置可以参考官方文档),参数格式是 python 的 dict。
  • DockerSpawner.environment 可以设置 JupyterLab 容器的环境变量,如上述设置了开启 JupyterLab 功能,容器所有环境变量可以参考官方文档,参数格式是 python 的 dict。
  • DockerSpawner.volumes 可以设置 JupyterLab 容器的挂载配置,提供了两种配置方式(读写模式:'source_path': 'target_path',或自定义读写模式(如只读):'source_path': {'bind': 'target_path', 'mode': 'ro'}),格式是 python 的 dict。

【三、启动 JupyterHub】

根据第二步的配置,就可以通过 docker-compose 或者其他方式启动 JupyterHub 的 docker 镜像了,只不过很有可能会失败,主要是由于 NativeAuthenticator 对 Mysql 的兼容性问题,用于管理注册用户信息的那张表没有自动创建成功,不过我们可以帮他完成这个任务,即编写类似如下的SQL(具体存储引擎、编码可以根据自己实际情况调整)。

CREATE TABLE `users_info` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(255) NOT NULL,
`password` blob NOT NULL,
`is_authorized` bit(1) DEFAULT NULL,
`email` varchar(255) DEFAULT NULL,
`has_2fa` bit(1) DEFAULT NULL,
`otp_secret` varchar(16) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;

创建完 users_info 表后 JupyterHub 应该就能正常启动了,接下来就可以自己进行注册账号了,如果没有关闭开放注册功能或者注册的账号名在配置中的管理员用户名中的话,账号直接就可以登录,否则需要自行去数据库中找到自己注册的记录,并将 “is_authorized” 字段设置为1。

登录后应该会默认启动 JupyterLab,或者也可以自行选择启动,启动成功后会自动跳转到 JupyterLab,下次访问时直接就会访问 JupyterLab,而不会再显示 JupyterHub 的界面了。如果启动失败,也可以通过 docker 查看 JupyterLab 的容器情况。

【四、隔离 JupyterHub/JupyterLab 网络】

JupyterLab 里什么都能干,能执行代码,能运行脚本,总觉得部署了 JupyterLab 后,直接把内网环境对外打开了,所以还想再对 JupyterHub/JupyterLab 的网络进行隔离,不允许其访问内网。这块通过 iptables 就可以实现,比如上述我定义了 jupyter-network 网络,IP 是 192.168.254.0/24,我内网 IP 是 192.168.1.0/24,路由(网关)是 192.168.1.1,所以我在宿主机上定义如下 iptables,禁止来自 jupyter-network 的 IP 请求内网 IP(但允许通过路由访问互联网)。当然,如果 Mysql 服务器不与 JupyterHub/JupyterLab 在一台宿主机上的话,别忘了允许 JupyterHub 的 IP 地址访问 Mysql 端口。

iptables -I DOCKER-USER -s 192.168.254.0/ -d 192.168.1.0/ -j DROP
iptables -I DOCKER-USER -s 192.168.254.0/ -d 192.168.1.1 -j ACCEPT

此外,如果宿主机上还有其他服务或 docker 实例,如果需要禁止 JupyterHub/JupyterLab 访问他们,还需要再定义一条

iptables -I INPUT -s 192.168.254.0/ -p tcp -j DROP

这样,应该就相对安全了一些吧。

用Docker部署自己的JupyterHub的更多相关文章

  1. ASP.NET Core开发-Docker部署运行

    ASP.NET Core开发Docker部署,.NET Core支持Docker 部署运行.我们将ASP.NET Core 部署在Docker 上运行. 大家可能都见识过Docker ,今天我们就详细 ...

  2. Docker部署Hadoop集群

    Docker部署Hadoop集群 2016-09-27 杜亦舒 前几天写了文章"Hadoop 集群搭建"之后,一个朋友留言说希望介绍下如何使用Docker部署,这个建议很好,Doc ...

  3. 程序开发使用docker部署

    我们公司自己研发了一套 grand-line 系统,使用 docker 来部署项目. 我是第一批小白鼠,一开始网络差,build 一次要半个小时,连接进入 web shell 也很慢,部署一个微信项目 ...

  4. 我使用celery以及docker部署遇到的问题

    首先我本机测试时没有问题的,但是在线上docker中,任务一直显示 "Sending due task".超时的任务是 django orm update 操作,本地不会出现这样的 ...

  5. Docker部署SDN环境

    2014-12-03 by muzi Docker image = Java class Docker container = Java object 前言 5月份的时候,当我还是一个大学生的时候,有 ...

  6. 在生产环境使用Docker部署应用

    导读 Docker现在越来越流行,但是真正在生产环境部署Docker还是个比较新的概念,还没有一个标准的流程.作者是ROR的程序员,作者结合平时的部署经验,联系Docker的特点,向大家分享了其在生产 ...

  7. Docker 使用指南 (六)—— 使用 Docker 部署 Django 容器栈

    版权声明:本文由田飞雨原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/98 来源:腾云阁 https://www.qclou ...

  8. NET Core Docker部署

    NET Core Docker部署 前言 在前面文章中,介绍了 ASP.NET Core在 macOS,Linux 上基于Nginx和Jexus的发布和部署,本篇文章主要是如何在Docker容器中运行 ...

  9. 我使用 Docker 部署 Celery 遇到的问题

    问题1 - Sending due task 本机测试时没有问题的,但是在线上 docker 中,任务一直显示 "Sending due task".超时的任务是 Django O ...

随机推荐

  1. spark storm 反压

    因特殊业务场景,如大促.秒杀活动与突发热点事情等业务流量在短时间内剧增,形成巨大的流量毛刺,数据流入的速度远高于数据处理的速度,对流处理系统构成巨大的负载压力,如果不能正确处理,可能导致集群资源耗尽最 ...

  2. Governing sand 贪心

    题目链接:https://ac.nowcoder.com/acm/contest/887/C 题目描述 The Wow village is often hit by wind and sand,th ...

  3. vue引用外部JS的两种种方案

    前言 肯定会遇到没有npm化的库 自己写的js 方法 在Vue中该怎么引用呢 第一种 如果库是es6写的 就可以用import 引入 比如我自己写的http 封装接口的方法 就可以这样子导入哦 第二种 ...

  4. numpy 其它常用方法

    一.创建特殊的数组 1.ones() 语法 np.ones(shape, dtype=None) # shape 创建数组的shape # dtype 指定数组的数据类型 例子 import nump ...

  5. Java入门 - 面向对象 - 02.重写与重载

    原文地址:http://www.work100.net/training/java-override-overload.html 更多教程:光束云 - 免费课程 重写与重载 序号 文内章节 视频 1 ...

  6. 国产CPU 申威1621 异数OS基础组件理论性能测试报告

    国产CPU 申威1621 异数OS基础组件理论性能测试报告 文章目录 国产CPU 申威1621 异数OS基础组件理论性能测试报告 前言 测试平台 测试项目 SW1621 异数OS 容器虚拟交换机模拟性 ...

  7. springboot 报错nested exception is java.lang.IllegalStateException: Failed to check the status of the service xxxService No provider available for the service

    spring: dubbo:#关闭所有服务的启动时检查:(没有提供者时报错) consumer: check: false timeout: 3000

  8. 使用eNSP配置QinQ

    参考链接:https://blog.csdn.net/alone_map/article/details/52217094 本文主要记录使用华为eNSP模拟器来实现配置QinQ,并对QinQ的报文进行 ...

  9. 数据结构——栈的应用 NOI2.2 括号匹配问题

    栈是一种数据结构,相当于一个容器,将一个又一个变量从顶端压进去,需要使用时,又从顶端拿出来,其具体使用方法,下面是详细讲解: #include<stack>必须使用此头文件 stack&l ...

  10. laravel脚手架搭建项目问题之生产环境element-ui组件字体图标显示错误问题

    问题描述: 1.element-ui组件使用的是npm安装 2.npm scripe模式开发 3.使用git命令行工具开发 3.开发环境下图标正常显示 4.生产环境下图标显示不正常 分析原因: 图标文 ...