【话在前头】

用 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. 交换一个数字的任意两个位置,指定K次的最值

    Anton has a positive integer n, however, it quite looks like a mess, so he wants to make it beautifu ...

  2. 每天玩转3分钟 MyBatis-Plus - 1. 配置环境

    每天玩转3分钟 MyBatis-Plus - 1. 配置环境 每天玩转3分钟 MyBatis-Plus - 2. 普通查询 MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 ...

  3. Redis系列(二):Redis的5种数据结构及其常用命令

    上一篇博客,我们讲解了什么是Redis以及在Windows和Linux环境下安装Redis的方法, 没看过的同学可以点击以下链接查看: Redis系列(一):Redis简介及环境安装. 本篇博客我们来 ...

  4. Scrapy信号量

    1.类 from scrapy import signals class MySingle(object): def __init__(self): pass @classmethod def fro ...

  5. [洛谷P3254] [网络流24题] 圆桌游戏

    Description 假设有来自m 个不同单位的代表参加一次国际会议.每个单位的代表数分别为ri (i =1,2,--,m). 会议餐厅共有n 张餐桌,每张餐桌可容纳ci (i =1,2,--,n) ...

  6. 【WPF学习】第十三章 理解路由事件

    每个.NET开发人员都熟悉“事件”的思想——当有意义的事情发生时,由对象(如WPF元素)发送的用于通知代码的消息.WPF通过事件路由(event routing)的概念增强了.NET事件模型.事件路由 ...

  7. 频繁插入(insert)的业务,用什么存储引擎更合适? | 数据库系列(转)

    本文来自微信公众号 继续回答星球水友提问: 沈老师,MyISAM只支持表锁,但网上文章却说,在并发插入量比较大的时候,比较适合使用MyISAM,这矛盾吗? 这个问题,涉及MySQL表锁的一些细节,借着 ...

  8. GP工作室-团队项目Beta冲刺

    GP工作室-团队项目Beta冲刺 这个作业属于哪个课程 https://edu.cnblogs.com/campus/xnsy/GeographicInformationScience/ 这个作业要求 ...

  9. [HNOI2008]Cards(dp,Burnside引理)

    Burnside引理: 参考自 某大佬对Burnside引理和Polya定理的讲解 相关概念 群:在数学中,群表示一个拥有满足封闭性.满足结合律.有单位元.有逆元的二元运算的代数结构. 置换群:由有限 ...

  10. qt creator源码全方面分析(0)

    本人主攻C++和Qt. 上两天刚研究完Qt install framework(IFW)应用程序安装框架. google没发现有正儿八经的官方文档的翻译,我就进行了翻译哈!! 系列文章具体见:http ...