markdown 构建多用户隔离的Jupyter Lab单服务器原生环境

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了markdown 构建多用户隔离的Jupyter Lab单服务器原生环境相关的知识,希望对你有一定的参考价值。

# 构建多用户隔离的 Jupyter Lab 单服务器原生环境

## 概述

我们需要:

- 在我们的 `GPU` 服务器上,建立一个用户隔离的 [Jupyter][] 远程 Web 环境
- 每个用户在一个独立的 [Python][] 环境中使用 [Jupyterlab][]
- 每个用户都可以自由的使用 [Conda][] 或者 [Pip][] 安装软件

## 操作系统与GPU驱动加速程序

我们目前拥有 NVIDIA 1080 TI GPU 的图形卡,为了方便的使用该 GPU 硬件,和相关驱动、加速程序,以及依赖这些程序的库、框架、应用,我们做出了如下选择:

- [Ubuntu][] 是一个成熟且流行的选择
- 截至成稿时,[PyTorch][] 的预编译包所支持的 [CUDA][] 版本是 `9.0`;[TensorFlow][] 的预编译包同样也只支持 [CUDA][] `9.0`。考虑到`9.0`版本的 [CUDA][] 的预编译包仅支持 [Ubuntu][] `1604` 与 `1704`,最终选择的是支持时间更长的 **[Ubuntu][] `1604` `x86_64bit`** , 且安装其默认图形界面(桌面版)。

主要硬件/软件环境:

- Hardwares:
  - CPU: Intel Xeon E5 2670
  - GPU: NVIDIA GeForce GTX 1080 Ti
- Softwares:
  - OS: Ubuntu 1604 x86_64
    - GPU Support:
      - NVDIA Dirver 410
      - CUDA 9.0
      - cuDNN 7.3 for CUDA 9.0
      - NCCL 2.3.7 for CUDA 9.0

装配好硬件和操作系统之后,按照以下的记录安装/配置步骤,进行操作。期间须保证该计算机可以畅通地连接到互联网:

### 更新操作系统

1. 在终端执行:

   ```sh
   sudo apt update
   sudo apt upgrade --auto-remove
   ```

2. 重启

### 安装 NVIDIA 驱动

NVidia 官方驱动的安装比较繁琐,涉及到 `Linux` 的 `modprobe` 与 `initramfs` 的修改,本文不予记载。

建议采用 `Graphics Drivers` 软件仓库提供的驱动:

1. 在终端执行:

   ```bash
   sudo add-apt-repository ppa:graphics-drivers/ppa
   sudo apt-get update
   ```

1. 通过图形界面的"系统设置"指定系统要使用的驱动:

   1. 打开“系统设置” -> “软件和更新” -> “附加驱动”
   1. 待“搜索可用驱动”完毕后,选择 `NVIDIA Corporation` 下最新的长期支持版驱动版本(访问 <https://www.nvidia.com/object/unix.html> 查看 `Unix` 驱动的版本信息)
   1. 等待驱动下载和安装,完成后重启计算机。

### 安装 CUDA `9.0`

访问 <https://developer.nvidia.com/cuda-90-download-archive> ,根据具体环境选择安装方式,并按照说明进行操作。

建议的选择:

| Operating System | Architecture | Distribution | Version | Installer Type |
| ---------------- | ------------ | ------------ | ------- | -------------- |
| Linux            | x86_64       | Ubuntu       | 16.04   | deb (network)  |

按照建议选择下载安装文件后的安装步骤:

```bash
sudo dpkg -i cuda-repo-ubuntu1704_9.0.176-1_amd64.deb
sudo apt-key adv --fetch-keys https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1704/x86_64/7fa2af80.pub
sudo apt update
sudo apt install -y cuda-9-0
```

**⚠ 警告**:

> - **一定** 要安装 **`cuda-9-0`** 而不是 [CUDA][] `9.0` 官方下载页面所言,否则被安装的可能是不兼容的高版本的[CUDA][]。
> - 安装完毕后,**不要** 升级软件包`cuda-repo-ubuntu1604`,否则 [CUDA][] 可能被升级到不兼容的高版本。

要固定 [CUDA][] 版本,避免意料之外的升级,我们可以这样操作:

```bash
sudo apt-mark hold cuda-repo-ubuntu1604
```

### 安装 cuDNN

访问 [cuDNN][] 下载页面 ,根据具体环境选择安装方式,并按照说明进行安装。

我们的环境需要的版本是:

- [cuDNN v7.4.2 (Dec 14, 2018), for CUDA 9.0](https://developer.nvidia.com/rdp/cudnn-download#a-collapse742-90)

要下载和安装的软件包有:

- [cuDNN Runtime Library for Ubuntu16.04  (Deb)](https://developer.nvidia.com/compute/machine-learning/cudnn/secure/v7.4.2/prod/9.0_20181213/Ubuntu16_04-x64/libcudnn7_7.4.2.24-1%2Bcuda9.0_amd64.deb)

- [cuDNN Developer Library for Ubuntu16.04  (Deb)](https://developer.nvidia.com/compute/machine-learning/cudnn/secure/v7.4.2/prod/9.0_20181213/Ubuntu16_04-x64/libcudnn7-dev_7.4.2.24-1%2Bcuda9.0_amd64.deb)

### 安装 NCCL

访问 [NCCL][] 下载页面 ,根据具体环境选择安装方式,并按照说明进行安装。

我们的环境需要的版本是:

- [NCCL v2.3.7, for CUDA 9.0, Nov 8 & Dec 14, 2018](https://developer.nvidia.com/nccl/nccl-download#a-collapse237-90)

建议下载和安装该软件包的离线版:

- [Local installer for Ubuntu 16.04](https://developer.nvidia.com/compute/machine-learning/nccl/secure/v2.3/prod3/nccl-repo-ubuntu1604-2.3.7-ga-cuda9.0_1-1_amd64.deb)

## 安装和配置 Jupyterhub

### 安装 Python

[Jupyter][] 是 [Python][] 程序,需要的版本是 `3.4` 及以上。

我们有三种选择可用于安装 [Python][] `3`:

1. 通过 [apt][] 安装 使用 [Ubuntu][] `1604` 默认软件仓库提供的 [Python][] `3.5`。
1. 从源码安装 [Python][] `3.6` 。目前不推荐使用 `3.7`,因为 [TensorFlow][] 目前没有此版本的官方支持。
1. 使用 [Conda][]。

其中,1, 2 两种方式是将 [Python][] 安装到系统全局,而 3 是用户级的安装。

具体的安装方法是:

- [apt][] 安装:

  安装操作系统默认软件仓库提供的 [Python][] `3`:

  ```bash
  sudo apt install -y python3 python3-setuptools python3-pip python3-dev build-essential
  ```

  [Ubuntu][] `1604` 默认仓库提供的 [Python][] `3` 版本是 `3.5`

- 源码安装:

  以 [Python][] `3.6` 为例:

  1. 在操作系统上安装编译构建工具和相关依赖软件的开发包:

     ```bash
     sudo apt install -y build-essential libssl-dev zlib1g-dev libbz2-dev liblzma-dev libsqlite3-dev libdb-dev libgdbm-dev libncurses5-dev libreadline-dev libexpat1-dev tk-dev
     ```

  2. 从 <https://python.org/> 下载最新的 [Python][]`3.6`  源代码压缩包(此处以`3.6.6`为例),解压、构建、安装:

     ```bash
     wget https://www.python.org/ftp/python/3.6.6/Python-3.6.6.tgz
     tar -xf Python-3.6.6
     cd Python-3.6.6
     ./configure --enable-optimizations
     make
     sudo make altinstall
     ```

     **⚠ 警告:**

     > **一定** 要使用 `make altinstall` 命令安装,而 **不是** `make install` ,否则可能破环操作系统。

  3. 检查 [Python][] `3.6` 是否安装成功:

     ```bash
     $ which python3.6
     /usr/local/bin/python3.6

     $ python3.6 --version
     Python 3.6.6
     ```

  4. 确保安装了 [pip][] 模块:

     ```bash
     sudo -H python3.6 -m ensurepip
     ```

- [Conda][]:

  这是**推荐的方式**。与以上两种方式不同的是,[Conda][] 将 [Python][] 安装到用户环境中,而不是整个操作系统的全局环境。

  所以,应选定一个已有的操作系统账户、或者新建一个专门的账户,专门用于安装这些软件包。假定我们使用名为 `jupyterhub` 的账户,如果它还不存在,可以这样新建:

  ```bash
  sudo adduser jupyterhub
  ```

  设置好这个用户的密码后,切换到该账户或者以该账户登录:

  ```bash
  $ su jupyterhub
  Password:
  ```

  安装 [Conda][] (以 `Anaconda3 5.2.0` 为例,这是目前最新的 [Python][] `3.6` 版本):

  ```bash
  $ wget https://repo.anaconda.com/archive/Anaconda3-5.2.0-Linux-x86_64.sh
  ./Anaconda3-5.2.0-Linux-x86_64.sh
  ```

  按照提示,使用默认值进行安装即可。

  当安装程序询问

  ```python
  >>> Do you wish the intaller to pretend the Anaconda3 install location to PATH in your /home/jupyterhub/.bashrc ? [yes|no]
  ```

  的时候,**输入 `yes`**

  安装完毕后,执行:

  ```bash
  source ~/.bashrc
  ```

  此时,该用户(`jupyterhub`)的 [Python][] 执行文件路径应是:

  ```bash
  $ which python
  /home/jupyterhub/anaconda3/bin/python
  ```

**ℹ 说明**:

> 下文中,我们将用 `$PYTHON` 代表以上步骤中安装的 [Python][] 的可执行文件。小心多个 `python` 并存的情况。具体以实际情况为准。
>
> 就上面几种不同的安装方式而言:
>
> - [apt][] 安装: `$PYTHON` 绝对路径是: `/usr/bin/python3`
> - 源码安装: `$PYTHON` 绝对路径是: `/usr/lobal/bin/python3.6`
> - [Conda][]: `$PYTHON` 绝对路径是: `/home/jupyter/anaconda3/bin/python`
>
> **[Conda][] 是推荐的方式**

### 安装 Jupyter 系列程序

- 如果 [Python][] 是全局安装的:

  使用 [Pip][] 安装这些软件到**系统全局**环境:

  ```bash
  sudo -H $PYTHON -m pip install jupyter jupyterlab jupyterhub
  ```

  **⚠ 警告**:

  > 强烈建议使用`sudo`以超级用户执行安装,不要 `pip install --user`,否则以超级用户的身份运行 [Jupyterhub][] 的时候,会找不到包 (当然可以修改 `PYTHONPATH` ,以及各种 Hack 方法解决这个问题,但不推荐)。

- 如果 [Python][] 是通过 [Conda][] 安装的:

  [Conda][] 已经带有 [Jupyter][] 和 [Jupyterlab][],故直接安装 [Jupyterhub][] 到该**用户的 `Base` 环境**:

  ```bash
  conda install -c conda-forge jupyterhub
  ```

安装之后,我们还需要安装程序 `configurable-http-proxy` 到**操作系统全局环境**。之所以需要系统级全局安装,是因为我们将要以 `root` 身份,以后台服务的形式运行 [Jupyterhub][] ,且 [Jupyterhub][] 在自动启动 `configurable-http-proxy` 时不具备指定路径的能力。

`configurable-http-proxy` 是一个 [Node.js][] 程序,它需要的 [Node.js][] 版本大于 [Ubuntu][] `1604` 默认仓库提供的。所以我们首先需要这样安装 [Node.js][] 的较高版本。

- 如果采用全局 [Python][]:

  应使用 [apt][] 安装 [Node.js][] 最新稳定版(目前是 `v10.x`):

  ```bash
  curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash -
  sudo apt update
  sudo apt install -y nodejs
  ```

  验证 [Node.js][] 的路径和版本:

  ```bash
  $which node
  /usr/bin/node

  $node --version
  v10.15.0
  ```

  然后通过 [npm][] 全局安装 `configurable-http-proxy`:

  ```bash
  sudo npm install -g configurable-http-proxy
  ```

- 如果使用 [Conda][]:

  上个步骤中,[Conda][] 在安装 [Jupyterhub][] 的时候,已经自动安装了它所需要的 [Node.js][] 以及 `configurable-http-proxy` 到 `Base` 环境。
  这与我们以 `root` 在后台运行的目标相悖;且 [Jupyterhub][] 以 `root` 执行时,无法找到 `jupyterhub` 用户目录中的 `configurable-http-proxy` 执行文件,根本无法启动。

  要解决这个问题,我们可以为 `jupyterhub` 用户目录中的 `configurable-http-proxy` 在操作系统的可执行文件目录建立一个软链接:

  ```bash
  sudo ln -s /home/jupyterhub/anaconda3/bin/configurable-http-proxy /usr/local/bin/configurable-http-proxy
  ```

### jupyterlab-hub 插件

这个插件提供了额外的 [Jupyterlab][] 和 [Jupyterhub][] 集成功能。这是一个可选功能。

- 如果使用的是全局安装的 [Python][],执行安装命令:

  ```bash
  sudo -H $PYTHON -m jupyter labextension install @jupyterlab/hub-extension
  ```

- 如果使用的是 [Conda][] ,注意首先切换到安装 [Conda][] 的用户(之前步骤中新建的用户`jupyterhub`):

  ```bash
  $ su jupyterhub
  Password:
  ```

  执行安装命令:

  ```bash
  $PYTHON -m jupyter labextension install @jupyterlab/hub-extension
  ```

### 配置 Jupyterhub

1. 生成默认配置文件

   将配置文件放到系统目录 `/etc` 中,之后我们将在启动命令行中指定这个路径。

   ```bash
   $ jupyterhub --generate-config
   Writing default config to: jupyterhub_config.py
   $ sudo mkdir -p /etc/jupyterhub
   $ sudo mv -a jupyterhub_config.py /etc/jupyterhub
   ```

2. 修改配置文件

   1. 修改 `c.LocalProcessSpawner.shell_cmd` ,默认以 `Bash` “登录”方式启动 [`sing-user`](https://jupyterhub.readthedocs.io/en/stable/getting-started/spawners-basics.html) 子进程:

      ```python
      c.LocalProcessSpawner.shell_cmd = ['bash', '-l', '-c']
      ```

   2. 修改 `c.Spawner.cmd` ,以支持 `jupyterlab-hub` 插件。

      ```python
      c.Spawner.cmd = ['jupyter-labhub']
      ```

      **ℹ 说明:**

      > 该步骤是可选的。如果不使用 `jupyterlab-hub` 插件,就不要修改这个配置。

### 启动命令行

现在,我们尝试运行 [Jupyterhub][] 。我们将以超级用户的身份执行,按照上个步骤的配置文件,在全部IP地址监听:

```bash
sudo $PYTHON -m jupyterhub -f /etc/jupyterhub/jupyterhub_config.py --ip "0.0.0.0"
```

其中:

- `-f /etc/jupyterhub/jupyterhub_config.py`: 指定配置文件
- `--ip "0.0.0.0"`: 表示在所有的 IPv4 地址上监听
- 默认端口是 `8000`

**ℹ 说明:**

> 如果 [Python][] 执行文件来自 [Conda][] 的非系统全局安装,例子中的 `$PYTHON` 应提供完整的绝对路径,如 `/home/jupyterhub/anaconda3/bin/python`

### 设置后台服务

可以用 Systemd, Initd 等将这个程序设置成后台服务。此处,我们选用 [Supervisor][]。

首先安装 [Supervisor][]:

```bash
sudo apt install -y supervisor
```

然后为 [Jupyterhub][] 新建服务配置文件 `/etc/supervisor/conf.d/jupyterhub.conf`:

```bash
echo_supervisord_conf | sudo tee /etc/supervisor/conf.d/jupyterhub.conf
```

修改配置文件 —— 针对上文提到的不同的 [Python][] 安装方式,配置文件的 `program` 段和`command` 属性值分别是:

- 通过 [apt][] 安装的 [Python][]:

  ```ini
  [program:jupyterhub]
  command=python3 -m jupyterhub -f /etc/jupyterhub/jupyterhub_config.py --ip 0.0.0.0
  ```

- 从源代码安装的 [Python][] `3.6`:

  ```ini
  [program:jupyterhub]
  command=python3.6 -m jupyterhub -f /etc/jupyterhub/jupyterhub_config.py --ip 0.0.0.0
  ```

- [Conda][]:

  ```ini
  [program:jupyterhub]
  command=/home/jupyterhub/anaconda3/bin/python -m jupyterhub -f /etc/jupyterhub/jupyterhub_config.py --ip 0.0.0.0
  ```

**ℹ 说明:**

> - 可以直接使用 `jupyterhub` 命令,不一定要通过 `python -m jupyter` 启动。注意执行文件的路径和所属用户。
> - 各参数可以按照实际需求进行修改

最后重加载 [supervisor][] 配置,并使之生效:

```bash
sudo supervisorctl reread
sudo supervisorctl update jupyterhub
```

要查看服务的运行情况,可以执行:

```bash
sudo supervisorctl status
```

## 用户管理

现在,[Jupyterhub][] 已经作为服务程序,以超级用户的身份在运行了。

但我们还是无法正常访问——因为我们还没有给 [Jupyterhub][] 添加用户。

我们采用 [Jupyterhub][] 默认的 [`single-user`](https://jupyterhub.readthedocs.io/en/stable/getting-started/spawners-basics.html) 模式,其登录用户就是操作系统账户。

### 新建用户

#### 新建操作系统账户

要新建 [Jupyterhub][] 用户,我们首先需要新建一个操作系统账户,假定其用户名为 `newuser`:

```bash
sudo adduser newuser
```

设置好用户密码等信息后,切换到这个用户:

```bash
$ su newuser
Password:
```

#### 安装 Conda

我们的目标是为每个用户提供独立的机器学习环境,故不宜使用操作系统全局 [Python][] 。

为了避免互相干扰,我们的选择有:

1. 仍使用全局 [Python][]:[Jupyterhub][] 服务通过系统级的 `jupyter-lab` 或者 `jupyter-labhub` 命令启动用户环境。启动后,让每个用户使用 [pip][] 以用户模式(`pip install --user <pacakge>`)安装软件包。
1. 为每个用户安装独立的 [Python][]:[Jupyterhub][] 服务通过用户级的 [Conda][] `Base` 环境下的 `jupyter-lab` 或者 `jupyter-labhub` 命令启动用户环境。启动后,使用 [Conda][] 直接在 `Base` 环境下安装软件包,或者使用 [Conda][] `Base` 环境下的 [pip][] 安装软件包。

考虑到 [Conda][] 可以支持 [pip][],而反之不可,我们选用第二种方法。

以 `Anaconda3 5.2.0` 为例:

```bash
wget https://repo.anaconda.com/archive/Anaconda3-5.2.0-Linux-x86_64.sh
./Anaconda3-5.2.0-Linux-x86_64.sh
```

按照提示,使用默认值进行安装。

当安装程序询问

```python
>>> Do you wish the intaller to pretend the Anaconda3 install location to PATH in your /home/newuser/.bashrc ? [yes|no]
```

的时候,**输入 `yes`**

安装完毕后,执行:

```bash
source ~/.bashrc
```

此时,该用户(`newuser`)的 [Python][] 执行文件路径应是:

```bash
$ which python
/home/newuser/anaconda3/bin/python
```

下面,**十分重要**的一步:我们还需要修改这个用户 `~/.profile`,将 `Conda`的`Base`环境可执行目录附加到`PATH`环境变量,以便 [Jupyterhub][] 以该账户启动子进程时可以找到 `jupyter-lab` 或 `jupyter-labhub` 可执行文件:

```bash
echo "PATH=\"/home/newuser/anaconda3/bin:$PATH\"" >> ~/.profile
```

#### 安装 Jupyterhub

[Conda][] 已经带有 [Jupyter][] 和 [Jupyterlab][],直接在这个用户的[Conda][] `Base` 环境安装 [Jupyterhub][] 即可:

```bash
conda install -c conda-forge jupyterhub
```

#### 安装 jupyterlab-hub

```bash
jupyter labextension install @jupyterlab/hub-extension
```

#### Jupyterhub 账户配置

如果要使用登录账户白名单,在 `jupyterhub_config.py` 配置文件中,将用户名加入`c.Authenticator.whitelist`集合:

```python
c.Authenticator.whitelist = {'newuser'}
```

如果要将该用户设置为管理员,在 `jupyterhub_config.py` 配置文件中,将用户名加入`c.Authenticator.admin_users`集合:

```python
c.Authenticator.admin_users = {'newuser'}
```

**ℹ 说明:**

> - 注意将`newuser`换成实际的值。
> - 如果安装了 `jupyterlab-hub` 插件,登录后在图形界面的 `hub` 菜单中有额外选项。

现在,通过浏览器访问 [Jupyterhub][] 的 Web 界面,如 <http://localhost:8000/>,使用操作系统账户 `newuser` 的账户密码登录,就可以直接使用 [Jupyterlab][] 的全部功能了

### 删除用户

直接删除操作系统账户即可

## 多内核环境管理

此处的“内核”指的是[Jupyter][]的`kernel`。

到目前为止,我们已经让一个新用户在全局 [Python][] 以及 [Conda][] 的 `Base` 环境中运行了 [Jupyterlab][]。此时,如果登录系统,所有的笔记将默认以这个[Python][]环境的内核运行。如果需要多个不同[Python][]版本/环境,或者其它语言的运行时,我们就得为这个用户安装多个[Jupyter][]内核。

### 新建 Jupyter Kernel

要让该用户的 [Jupyterlab][] 使用另一个新的 [Conda][] 环境,我们需要:

1. 新建一个 [Conda][] 环境。例如,新建一个名为 `mypy36env` , [Python][] 版本为 `3.5`的环境:

   ```bash
   conda create --name mypy35env python=3.5
   ```

2. 在新的环境中安装 [`ipykernel`](https://ipython.readthedocs.io/en/stable/install/kernel_install.html)

   ```bash
   conda activate mypy35env
   conda install pip ipykernel
   python -m ipykernel install --user --name mypy35env --display-name "Python 3.5(conda:mypy35env)"
   ```

现在,刷新 [Jupyterlab][] 页面,可以看到新的 `kernel` 。

### 删除 Jupyter Kernel

要删除这个 [Jupyter][] Kernel,执行:

```bash
jupyter kernelspec uninstall mypy35env
```

如果要连同 [Conda][] 运行环境一同删除,执行:

```bash
conda deactivate
conda env remove -n mypy35env
```

## 安装 Python 软件包

用户通过 [Jupyterlab][] 的笔记或者终端,使用 [Conda][] 或 [pip][] 安装。注意不要搞错 [Conda][]、 [venv][] 环境。

## 安装 Jupyterlab 插件

由于各个用户的 [Jupyterlab][] 环境是隔离的,所以可以自行安装,不会相互干扰。

## 升级 Jupyter

用户和系统的 [Jupyter][] 可以分别升级, [Conda][] 和 [pip][] 都可以。当然,前提是系统的 [Jupyterhub][] 与用户的 [jupyterlab][] 能够兼容。

## 安装其它软件

理论上,用户可以通过 [Jupyterlab][] 终端,做权限允许的任何事情,安装任何软件,包括 [Jupyter][] Kernel。

但是考虑到我们一般不为 [Jupyterhub][] 用户配置系统级的安装权限,用户应优先考虑使用 [Conda][] ,在本用户范围之内安装所需软件。

如果通过其它途径的安装软件,也应尽量考虑让该方案在用户权限范围之内可行。

----

[APT]: https://wiki.debian.org/Apt
[CUDA]: https://developer.nvidia.com/cuda-downloads
[cuDNN]: https://developer.nvidia.com/cudnn
[NCCL]: https://developer.nvidia.com/nccl
[pypi]: https://pypi.python.org/
[PyTorch]: https://pytorch.org
[Ubuntu]: https://www.ubuntu.com/index_kylin
[venv]: https://docs.python.org/3/library/venv.html
[TensorFlow]: https://www.tensorflow.org/
[wheel]: https://pypi.org/project/wheel/
[Python]: https://python.org/
[Conda]: https://conda.io/
[Pip]: https://packaging.python.org/tutorials/installing-packages/
[Jupyter]: https://jupyter.org/
[Jupyterhub]: https://jupyter.org/hub
[Jupyterlab]: https://jupyterlab.readthedocs.io/
[Supervisor]: http://supervisord.org/
[Node.js]: https://nodejs.org/
[npm]: https://www.npmjs.com/

以上是关于markdown 构建多用户隔离的Jupyter Lab单服务器原生环境的主要内容,如果未能解决你的问题,请参考以下文章

在Linux环境安装pyenvVtualenv虚拟环境和jupyter

markdown Jupyter有用的东西#jupyter

jupyter通过notedown使用markdown

Markdown 中的表格(在 Jupyter 中)

jupyter 中markdown使用

Jupyter notebook中markdown书写格式