【话在前头】

用 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. Java架构师中的内存溢出和内存泄露是什么?实际操作案例!

    JAVA中的内存溢出和内存泄露分别是什么,有什么联系和区别,让我们来看一看. 01 内存泄漏 & 内存溢出 1.内存泄漏(memory leak ) 申请了内存用完了不释放,比如一共有 102 ...

  2. 1z0-062 题库解析6

    You want execution of large database operations to suspend, and then resume, in the event of space a ...

  3. mysql复习1

    SQL语句分为以下三种类型: DML: Data Manipulation Language 数据操纵语言,用于查询与修改数据记录,包括如下SQL语句:INSERT:添加数据到数据库中UPDATE:修 ...

  4. LeetCode 第98题--验证二叉搜索树

    1. 题目 2.题目分析与思路 3.代码 1. 题目 给定一个二叉树,判断其是否是一个有效的二叉搜索树. 假设一个二叉搜索树具有如下特征: 节点的左子树只包含小于当前节点的数.节点的右子树只包含大于当 ...

  5. 【5min+】帮我排个队,谢谢。await Task.Yield()

    系列介绍 [五分钟的dotnet]是一个利用您的碎片化时间来学习和丰富.net知识的博文系列.它所包含了.net体系中可能会涉及到的方方面面,比如C#的小细节,AspnetCore,微服务中的.net ...

  6. 用Kolla在阿里云部署10节点高可用OpenStack

    为展现 Kolla 的真正实力,我在阿里云使用 Ansible 自动创建 10 台虚机,部署一套多节点高可用 OpenStack 集群! 前言 上次 Kolla 已经表示了要打 10 个的愿望,这次我 ...

  7. 史上最简单的的HashTable源码分析

    HashTable源码分析 1.前言 Hashtable 一个元老级的集合类,早在 JDK 1.0 就诞生了 1.1.摘要 在集合系列的第一章,咱们了解到,Map 的实现类有 HashMap.Link ...

  8. WinSCP整合SecureCRT打开终端

    使用WinSCP直接操作文件非常方便,但是如果用它来连入SSH进行指令交互就不方便了,使用XShell.SecureCRT.Putty等SSH终端软件敲指令却很不错,于是想能不能将两者结合起来使用.我 ...

  9. 04--Java--使用eclipse创建开发java项目步骤

    eclipse创建开发java步骤 1.三种创建java项目 1)方式一:在包资源管理器(package explorer)窗口中鼠标右击任意位置选择New --> Java Project,如 ...

  10. STVP编译时遇到no default placement for segment .FLASH_CODE

    最近编译STM8S003时需要使用flash库函数,看起来简单,实则折腾了超过1天.今天总结方法如下: 1.修改stm8s.h 156行  #define RAM_EXECUTION  注释去掉  如 ...