项目一:

项目要求README:

# Docker 训练营项目指南

## 项目概述

本结业项目旨在通过构建一系列渐进式的 Jupyter 环境,帮助你掌握 Docker 容器化技术在数据科学平台搭建中的应用。项目分为四个阶段,每个阶段都会增加新的功能和复杂度,让你逐步应用所学的 Docker 知识。

- **阶段 1-3**: 基本要求,完成这些阶段即可达到及格标准
- **阶段 4**: 进阶要求,完成此阶段可获得优秀评价

## 项目结构

.
├── 01-single-container/ # 阶段1: 单用户 Jupyter 环境
├── 02-jupyterhub/ # 阶段2: 基础多用户环境
├── 03-jupyterhub-github-auth/ # 阶段3: GitHub 认证集成
└── 04-jupyterhub-ai-enhanced/ # 阶段4: AI 增强版本(优秀要求)

## 阶段 1: 单容器 Jupyter 环境

### 目标

构建一个基于 Docker 的单用户 Jupyter Notebook 环境,适合个人数据分析和学习使用。

### 需要完成的任务

1. 创建一个基于 Python 数据科学镜像的 Dockerfile
2. 配置 docker-compose.yml 文件实现容器编排
3. 实现数据持久化存储
4. 添加示例数据和欢迎笔记本

### 技术要点

- Docker 基础命令
- Dockerfile 编写
- Docker Compose 基础配置
- 数据卷(Volumes)挂载
- 端口映射

### 参考资源

- [Jupyter Docker Stacks 文档](https://jupyter-docker-stacks.readthedocs.io/)
- [Docker Compose 文档](https://docs.docker.com/compose/)
- [Dockerfile 最佳实践](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/)

### 提示

- 使用官方的 `jupyter/scipy-notebook``jupyter/datascience-notebook` 作为基础镜像
- 注意配置正确的工作目录和权限
- 使用 Docker Compose 的 volumes 配置实现数据持久化
- 确保 Jupyter 服务可以通过浏览器访问

## 阶段 2: JupyterHub 多用户环境

### 目标

构建一个支持多用户的 JupyterHub 环境,每个用户拥有独立的工作空间。

### 需要完成的任务

1. 创建 JupyterHub 的 Dockerfile
2. 配置 docker-compose.yml 实现多容器编排
3. 编写 jupyterhub_config.py 配置文件
4. 实现用户数据的持久化
5. 添加基本的系统测试

### 技术要点

- Docker 网络配置
- Docker Compose 多服务编排
- JupyterHub 配置
- DockerSpawner 使用
- 容器间通信

### 参考资源

- [JupyterHub 文档](https://jupyterhub.readthedocs.io/)
- [DockerSpawner 文档](https://jupyterhub-dockerspawner.readthedocs.io/)
- [Docker 网络配置](https://docs.docker.com/network/)

### 提示

- 使用 DockerSpawner 为每个用户创建独立容器
- 配置 Docker 网络使 JupyterHub 能与用户容器通信
- 使用命名卷为每个用户提供持久化存储
- 注意容器的资源限制配置

## 阶段 3: GitHub 认证集成

### 目标

为 JupyterHub 环境添加 GitHub OAuth 认证,提高系统安全性和用户管理能力。

### 需要完成的任务

1. 配置 GitHub OAuth 应用
2. 更新 jupyterhub_config.py 添加认证配置
3. 使用环境变量管理敏感信息
4. 实现用户权限管理

### 技术要点

- OAuth 认证流程
- Docker 环境变量配置
- Docker Secrets 管理
- JupyterHub 认证插件配置

### 参考资源

- [OAuthenticator 文档](https://oauthenticator.readthedocs.io/)
- [GitHub OAuth 应用创建指南](https://docs.github.com/en/developers/apps/building-oauth-apps)
- [Docker 环境变量](https://docs.docker.com/compose/environment-variables/)
- [Docker Secrets](https://docs.docker.com/engine/swarm/secrets/)

### 提示

- 创建 .env.example 文件作为环境变量模板
- 使用 GitHub 的 OAuth 应用设置正确的回调 URL
- 配置管理员用户和访问控制
- 注意保护 OAuth 密钥和令牌

## 阶段 4: AI 增强版本(优秀要求)

### 目标

构建一个集成多种 AI 工具和模型的高级 JupyterHub 环境,支持 AI 辅助编程和数据分析。

### 需要完成的任务

1. 创建支持 AI 工具的自定义 Notebook 镜像
2. 配置多种 AI 模型(国际和国内)的接入
3. 添加 AI 示例笔记本
4. 实现资源限制和管理
5. 编写详细的使用文档

### 技术要点

- 高级 Dockerfile 多阶段构建
- Docker Compose profiles 配置
- 容器资源限制
- API 密钥管理
- 多容器协作
- 高级网络配置

### 参考资源

- [Jupyter AI 文档](https://jupyter-ai.readthedocs.io/)
- [Docker 多阶段构建](https://docs.docker.com/build/building/multi-stage/)
- [Docker Compose Profiles](https://docs.docker.com/compose/profiles/)
- [容器资源限制](https://docs.docker.com/config/containers/resource_constraints/)
- [各 AI 服务提供商 API 文档]
- [OpenAI API](https://platform.openai.com/docs/api-reference)
- [Anthropic API](https://docs.anthropic.com/claude/reference/getting-started-with-the-api)
- [阿里云百炼 API](https://help.aliyun.com/document_detail/2400152.html)
- [DeepSeek API](https://platform.deepseek.com/docs)

### 提示

- 使用 Docker Compose profiles 分离构建和运行环境
- 创建启动和停止脚本简化操作
- 为不同的 AI 模型提供配置选项
- 添加详细的示例和教程
- 考虑国内用户的网络环境,提供替代方案

## 提交要求

1. 完整的源代码,包含所有 Dockerfile、docker-compose.yml 和配置文件
2. README.md 文件,包含项目说明和使用指南

## 实施建议

1. **循序渐进**: 按照阶段顺序完成项目,每个阶段都建立在前一个阶段的基础上
2. **参考官方文档**: 优先参考官方文档解决问题
3. **版本控制**: 使用 Git 管理代码,每个阶段完成后提交一次
4. **测试验证**: 每完成一个功能就进行测试,确保其正常工作
5. **记录问题**: 记录遇到的问题和解决方案,作为报告的一部分

祝你在项目中取得好成绩!如有任何问题,请随时向导师咨询。

阶段一: 单容器 Jupyter 环境

写完compose和dockerfile运行之后发现容器直接退出了,想到查看容器日志:

docker logs 01-single-container-jupyterhub-1

发现

[W 2025-07-18 06:08:00.476 ServerApp] notebook_shim | error linking extension: [Errno 13] Permission denied: '/home/jovyan/.local/share'
...
PermissionError: [Errno 13] Permission denied: '/home/jovyan/.local/share'

这里猜测可能是权限报错,因此回去检查dockerfile文件的权限问题,并把dockerfile文件的内容丢给了AI
发现可能有一个有问题的点:

# ...
USER root
# ... 安装系统包 ...
USER $NB_UID # <--- 问题点 1
# ...
WORKDIR /home/jovyan/work
# ...
COPY requirements.txt /tmp/requirements.txt
RUN pip install ... # <--- 问题点 2
RUN mkdir -p ... # <--- 问题点 3
RUN jupyter lab --generate-config ... # <--- 问题点 4

AI回答说:

$NB_UID 是 Jupyter 基础镜像在容器运行时(通过 start-notebook.sh 脚本)才会设置的环境变量。在构建镜像时(即 docker build 或 docker compose build 期间),这个变量是不可用的。因此,USER $NB_UID 这条指令很可能会失效,导致后续的所有 RUN, COPY 等指令仍然是以 root 用户的身份执行的。

因此修改dockerfile文件,通过添加 chown 命令:在所有需要 root 权限的操作完成后,通过 chown 命令将 /home/jovyan 目录的所有权交还给 jovyan 用户。
RUN mkdir -p /home/jovyan/work && \ chown -R ${NB_UID}:${NB_GID} /home/jovyan/

但是,修改完成后重新docker compose up -d --build后发现容器还是直接退出了,再次回去logs查看日志,发现这次还是文件权限的问题,不满足于之前的测试,AI向我提供了一个新的测试方案:

步骤二:进入容器进行交互式调试
如果日志信息不够明确,或者你想亲自动手检查,可以按以下步骤操作。这个过程不需要 docker compose down
启动一个临时的、可交互的容器
在你的 01-single-container 目录下,运行以下命令:

docker compose run --rm jupyter /bin/bash

* docker compose run: 这个命令会为 compose.yml 文件中定义的服务(这里是 jupyter)创建一个新的临时容器,并运行你指定的命令。
* --rm: 表示当容器退出时,自动删除它。非常适合一次性的调试任务。
* jupyter: 这是你在 compose.yml 文件里定义的服务名称。
* /bin/bash: 这是我们要运行的命令。它会覆盖 Dockerfile 中默认的 CMD ["start-notebook.sh", ...],转而启动一个 Bash 交互式命令行。
在容器内部进行检查
运行上述命令后,你的终端提示符会变成类似 jovyan@<container_id>:/home/jovyan/work$ 的样子,这表示你已经成功进入了容器内部。现在,你可以像在普通的 Linux 系统里一样进行检查:
* 检查当前用户:运行 whoami。输出应该是 jovyan,这表示用户切换是成功的。
* 检查文件权限:运行 ls -la /home/jovyan。检查该目录下的文件和文件夹(特别是 .local, .jupyter)的所有者是不是 jovyan users。这是为了验证我们之前在 Dockerfile 里加的 chown 命令是否生效。
* 手动执行启动命令:这是最关键的一步。直接在命令行里运行 Dockerfile 里的启动脚本,看看会输出什么:
/usr/local/bin/start-notebook.sh --ServerApp.token='' --ServerApp.password=''

因为你是在交互式终端里直接运行它,任何错误都会立刻打印在你的屏幕上,这样就能非常直观地定位问题。

之后我通过docker run -rm jupyter bash进入容器内部,检查当前用户:运行 whoami。输出确定是 jovyan,这表示用户切换是成功的。之后我检查文件权限:运行 ls -la /home/jovyan。检查该目录下的文件和文件夹(特别是 .local, .jupyter)的所有者是不是 jovyan users。发现.jupyter的所有者是jovyan user但是.local的所有者还是root,之后我手动执行启动命令:直接在命令行里运行 Dockerfile 里的启动脚本/usr/local/bin/start-notebook.sh --ServerApp.token='' --ServerApp.password=''。得到输出跟我前面运行容器是一样的,得到权限的报错:PermissionError: [Errno 13] Permission denied: '/home/jovyan/.local/share'
这导致我考虑到是不是另一个yml文件有问题,我找到docker compose文件查看有关权限设置的地方,发现有一段挂载卷的内容涉及到了.local文件,同时AI也回答说当你在 docker-compose.yml 中使用命名卷(如 jupyter_packages)并将其挂载到容器的某个目录(如 /home/jovyan/.local)时,Docker 会在宿主机上创建一个由 root 用户拥有的目录来管理这个卷。这个 root 权限会覆盖掉你在 Dockerfile 中为 jovyan 用户设置的权限,导致容器内的 jovyan 用户无法写入这些目录。

  volumes:
# ...
# 持久化Python包安装
- jupyter_packages:/home/jovyan/.local
# 持久化Jupyter配置
- jupyter_config:/home/jovyan/.jupyter
# ...

正确的做法是只挂载你需要持久化的工作数据(比如你的 notebook 文件和数据集),而不要挂载用户的配置文件或软件包安装目录。软件包应该构建在镜像里,配置文件让容器自己管理。
修改为:

volumes:
# 只挂载工作目录。这会将你本地的 notebooks 和 data 文件夹
# 映射到容器的 /home/jovyan/work 目录中。
# 这样你的代码和数据就可以持久化,并且可以在容器内外同步。
- ./notebooks:/home/jovyan/work/notebooks
- ./data:/home/jovyan/work/data

之后我们再运行docker compose up -d --build,容器终于成功运行了!
到此为止我们该项目的阶段一:构建一个单容器的Jupyter环境就完成了!
image

阶段二:JupyterHub 多用户环境:

阶段任务:


### 目标

构建一个支持多用户的 JupyterHub 环境,每个用户拥有独立的工作空间。

### 需要完成的任务

1. 创建 JupyterHub 的 Dockerfile
2. 配置 docker-compose.yml 实现多容器编排
3. 编写 jupyterhub_config.py 配置文件
4. 实现用户数据的持久化
5. 添加基本的系统测试

### 技术要点

- Docker 网络配置
- Docker Compose 多服务编排
- JupyterHub 配置
- DockerSpawner 使用
- 容器间通信

### 参考资源

- [JupyterHub 文档](https://jupyterhub.readthedocs.io/)
- [DockerSpawner 文档](https://jupyterhub-dockerspawner.readthedocs.io/)
- [Docker 网络配置](https://docs.docker.com/network/)

### 提示

- 使用 DockerSpawner 为每个用户创建独立容器
- 配置 Docker 网络使 JupyterHub 能与用户容器通信
- 使用命名卷为每个用户提供持久化存储
- 注意容器的资源限制配置

JupyterHub是什么?

JupyterHub 是一个轻量级的工具,用于部署多个 Jupyter Notebook 服务器,每个用户都可以有自己的独立工作空间。这使得团队成员能够在共享的基础设施上进行协作,同时也为教育机构提供了强大的教学平台。
JupyterHub 的特点:
1.多用户支持:允许多个用户同时使用 Jupyter Notebook。
2.资源管理:根据用户需求动态分配资源。
3.安全性:提供多种认证机制。
4.可扩展性:可以根据需要增加或减少服务器资源。

很符合该阶段的一些要求,同时提示中还提到了DockerSpawn

1.项目基础介绍和主要编程语言
DockerSpawner 是一个开源项目,旨在帮助 JupyterHub 在 Docker 容器中启动单用户笔记本服务器。该项目的主要编程语言是 Python,它充分利用了 Python 的灵活性和强大的生态系统来实现其功能。
2.项目的核心功能
DockerSpawner 的核心功能包括:
DockerSpawner: 为经过身份验证的用户在 Docker 容器中启动单用户笔记本服务器。
SwarmSpawner: 在 Docker Swarm 模式下将单用户笔记本服务器作为服务启动。
SystemUserSpawner: 启动与系统用户对应的单用户笔记本服务器。
这些功能使得 JupyterHub 能够更高效地管理和扩展其用户环境,特别是在需要隔离和资源管理的情况下。

之后我把任务要求发给AI让AI先帮我初步实现一个代码

以下是AI实现这个目标生成的三个文件。

1. 02-jupyterhub/Dockerfile - JupyterHub 服务的构建蓝图

首先,我们需要一个 Dockerfile 来构建 JupyterHub 服务本身。这个镜像将包含 JupyterHub、DockerSpawner 以及运行它们所需的环境。

02-jupyterhub/ 目录下创建 Dockerfile 文件,内容如下:

# 使用一个精简的 Python 3.10 镜像作为基础
FROM python:3.10-slim

# 安装 JupyterHub 和 DockerSpawner
# --no-cache-dir 参数可以减少镜像的体积
RUN pip install --no-cache-dir jupyterhub dockerspawner

# 复制 JupyterHub 的配置文件到容器中
# 我们将本地的 conf/jupyterhub_config.py 文件复制到容器的 /srv/jupyterhub/ 目录下
COPY conf/jupyterhub_config.py /srv/jupyterhub/jupyterhub_config.py

# 暴露 JupyterHub 的默认端口 8000
EXPOSE 8000

# 设置容器启动时执行的命令
# 使用 -f 参数指定配置文件的路径来启动 jupyterhub
CMD ["jupyterhub", "-f", "/srv/jupyterhub/jupyterhub_config.py"]

文件解析:

  • FROM python:3.10-slim: 我们选择了一个轻量级的 Python 官方镜像作为基础。
  • RUN pip install ...: 安装了两个核心的 Python 包:jupyterhub(Hub 服务本身)和 dockerspawner(让 Hub 能够与 Docker 交互的插件)。
  • COPY conf/...: 将我们本地的配置文件 jupyterhub_config.py 复制到容器内的指定位置,以便启动时使用。
  • CMD ["jupyterhub", ...]:定义了容器启动时要执行的命令,即用我们提供的配置文件来启动 JupyterHub 服务。

2. 02-jupyterhub/compose.yml - 多容器编排文件

这个文件是整个多用户系统的“总指挥”,负责定义和连接所有服务。

文件内容 (02-jupyterhub/compose.yml):

services:
jupyterhub:
build:
context: .
dockerfile: Dockerfile
container_name: jupyterhub
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
ports:
- "8000:8000"
networks:
- jupyterhub_network
environment:
HUB_NETWORKS_NAME: jupyterhub_network
DOCKER_JUPYTER_IMAGE: quay.io/jupyter/scipy-notebook:latest
USER_MEM_LIMIT: 4G
USER_CPU_LIMIT: 4
HUB_ACTIVE_SERVER_MAX: 4
HUB_ADMIN_USER: yiqiu

networks:
jupyterhub_network:
name: jupyterhub_network
driver: bridge

文件解析:

  • build:: 指示 Docker Compose 使用我们刚刚创建的 Dockerfile 来构建 jupyterhub 服务的镜像。
  • volumes: - "/var/run/docker.sock:/var/run/docker.sock": 这是实现 DockerSpawner 功能的核心。它将你本地主机的 Docker 套接字文件 (docker.sock) 挂载到容器内部。这使得 JupyterHub 容器可以“命令”主机上的 Docker 来创建、管理其他容器(即每个用户的 Jupyter 容器)。
  • ports: - "8000:8000": 将 JupyterHub 的登录页面端口映射到本地,这样你就可以通过 http://localhost:8000 访问。
  • networks:: 创建了一个名为 jupyterhub_network 的自定义桥接网络。所有由 Hub 生成的用户容器都会被连接到这个网络,确保它们之间以及它们与 Hub 之间可以顺畅通信。
  • environment:: 通过环境变量传入配置,增加了灵活性。
    • DOCKER_JUPYTER_IMAGE: 指定了为每个用户创建容器时所使用的单用户 Jupyter 镜像。这里我们直接使用了官方的 scipy-notebook 镜像。
    • HUB_ADMIN_USER: 定义了管理员用户的名称,这个值会被 jupyterhub_config.py 读取。

3. 02-jupyterhub/conf/jupyterhub_config.py - JupyterHub 核心配置

这个 Python 脚本定义了 JupyterHub 的所有行为。

文件内容 (02-jupyterhub/conf/jupyterhub_config.py):

import os
c = get_config()

# 启用 DockerSpawner,让 Hub 可以创建 Docker 容器
c.JupyterHub.spawner_class = 'dockerspawner.DockerSpawner'

# 从环境变量中读取为用户生成的 Notebook 镜像名称
c.DockerSpawner.image = os.environ['DOCKER_JUPYTER_IMAGE']

# 配置网络,确保 Hub 和用户容器在同一个网络中
c.DockerSpawner.network_name = os.environ['HUB_NETWORKS_NAME']
c.JupyterHub.hub_ip = '0.0.0.0'
c.JupyterHub.hub_connect_ip = 'jupyterhub' # 用户容器通过这个主机名找到 Hub

# 【实现用户数据持久化】
# 为每个用户创建一个独立的命名卷,并挂载到其工作目录
data_dir = os.environ.get('DATA_VOLUME_CONTAINER', '/home/jovyan/work')
c.DockerSpawner.volumes = { 'jupyterhub-user-{username}': data_dir }

# 用户容器退出时自动删除
c.DockerSpawner.remove_containers = True

# 使用 DummyAuthenticator,允许任何用户登录,方便测试
c.JupyterHub.authenticator_class = 'jupyterhub.auth.DummyAuthenticator'

# 从环境变量设置管理员用户
admin_name = os.environ['HUB_ADMIN_USER']
c.Authenticator.admin_users = [admin_name]
c.DummyAuthenticator.admin_users = [admin_name]

# 配置空闲服务器自动关闭的服务,以节省资源
c.JupyterHub.services = [
{
'name': 'idle-culler',
'command': ['python3', '-m', 'jupyterhub_idle_culler', '--timeout=360'],
'admin': True,
}
]

docker compose up -d --build后容器服务运行成功了。
但是当我测试页面的时候发现jupyterhub输入账号密码后页面没有跳转,还是停留在需要输入账号密码的页面,因此我们需要再次查看日志寻找出错原因。
之前的日志是我们解决“容器无法启动”问题的,现在容器已经启动了,我们需要在尝试登录的那一刻看看后台发生了什么,因此应该使用docker logs -f jupyterhub命令来查看日志。
-f: (follow) 参数,它会让命令持续运行,并实时打印出所有新的日志,就像看直播一样。
之后我们再次尝试输入账号密码查看日志输出的结果:

项目二:

任务要求:

官方要求

  • 您需要按照原始项目说明在 CNB 云原生开发平台中将项目运行起来
  • 您需要确保启动后的服务功能正常可用,可以正常进行 RAG 问答。
  • 确保整个 docker compose 启动服务时间尽可能短。
  • 系统提供默认账号 (admin / Admin123),登录后可以体验问答功能。
  • 如果您对 RAG 领域有兴趣,也可以对项目进项二次开发,完成你想做的功能【可选】。