跳到主要内容

基础镜像

ssh 和 jupyter 启动模式需要一个自带 SSH 服务、JupyterLab 和启动契约的镜像。像 pytorch/pytorch 这样的普通上游镜像一样都没有,这就是这两种模式被限制为使用派生自 Superheat 基础镜像的镜像的原因。

superheat-cn-registry.cn-shanghai.cr.aliyuncs.com/superheat/base

这是基础镜像在 Superheat CN 上的名称,托管在阿里云 ACR 上。在模板上请写这个完整引用:不带 ACR 主机的 superheat/base 在 ssh 和 jupyter 模式下会部署失败。

仓库是公开的,允许匿名拉取,所以主机无需凭据即可拉取,使用基础镜像的模板也不需要配置镜像仓库凭据。ACR 上的其他 Superheat 镜像和第三方镜像见Superheat CN 上的镜像。

已发布的标签​

标签是什么
cuda-12.1CUDA 12.1
cuda-12.4CUDA 12.4
cuda-12.6CUDA 12.6
cuda-12.8CUDA 12.8
latest最新的 CUDA 次版本

ACR 上只有这些标签。

每个 cuda-<minor> 标签都基于 Ubuntu 22.04 上对应的 NVIDIA CUDA cuDNN runtime 镜像构建——cuda-12.4 来自 nvidia/cuda:12.4.1-cudnn-runtime-ubuntu22.04。镜像只有 amd64 版本,这与机群一致:Superheat 主机都是 x86_64。

cuda-<minor> 这套命名也是 [Automatic] 模板标签解析时对照的对象。把模板的标签保留为 [Automatic],会选中所选机器的 CUDA 能运行的最新标签,于是一个模板可以适配驱动版本参差不齐的机群。当你需要一次构建可复现而不是保持最新时,请固定一个具体标签。

它增加了什么​

在 CUDA runtime 之上:openssh-server、带 pip 和 jupyterlab 的 python3、tini、gosu、openssl、ca-certificates、curl、git 和 rsync。工作目录是 /workspace,入口点是 tini -- /opt/superheat/entrypoint.sh。

镜像带有标签 com.superheat.base="true",任何 FROM 基础镜像构建的镜像都会继承它。Superheat 接管入口点时,主机会在拉取镜像之后、创建容器之前检查这个标签。但决定 Superheat 是否在 ssh 和 jupyter 模式下接管入口点的是镜像引用,而不是标签:在 Superheat CN 上,只有 ACR 上 superheat/ 命名空间里的镜像会被接管。参见Superheat CN 上的镜像。

入口点做了什么​

每一步都是幂等的,所以停止再启动实例时,在同一个容器上重新运行是安全的。

步骤详情
1. 导出环境容器环境会被写入 /etc/environment 和 /etc/profile.d/superheat-env.sh,因为 sshd 和 Jupyter 启动的登录 shell 不会继承它。预置阶段的机密被排除在外。
2. 安装密钥PUBLIC_KEY 和 SUPERHEAT_SSH_PUBLIC_KEYS 会写入 /root/.ssh/authorized_keys。
3. 启动 sshd主机密钥缺失时会先生成,然后 sshd 监听容器 22 端口。仅支持密钥;不接受密码。
4. 启动 Jupyter仅当设置了 JUPYTER_TOKEN 时。自签名证书只生成一次,JupyterLab 通过 HTTPS 绑定到 0.0.0.0:${JUPYTER_PORT},Jupyter 自带的 Origin 检查保持开启。
5. 启动访问代理仅当设置了 SUPERHEAT_ACCESS_PORTS 时。代理在容器自己的地址上占住这些端口,并要求出示访问令牌;入口点把 SUPERHEAT_APP_HOST 设为 127.0.0.1,让应用在它后面监听。参见要求访问令牌。
6. 运行启动脚本SUPERHEAT_ONSTART 运行一次,由哨兵文件把关。失败会被记录,容器保持运行。
7. 移交如果镜像有 CMD 就执行它;否则容器进入空闲状态,以便为端口映射保持运行。

环境契约​

这些变量名就是 Superheat 与镜像之间的接口。基于基础镜像构建,入口点会替你处理它们;自己写入口点,这些就是你必须实现的东西。

变量用途
PUBLIC_KEY单个 OpenSSH 公钥,追加到 authorized_keys
SUPERHEAT_SSH_PUBLIC_KEYS以换行分隔的 OpenSSH 公钥,追加到 authorized_keys
SUPERHEAT_ONSTART首次启动时运行一次的 Bash 脚本。属敏感内容;不会写入环境文件。裸别名 ONSTART 同样被接受
JUPYTER_TOKEN设置后,JupyterLab 会以它作为访问令牌启动
JUPYTER_PORTJupyter 绑定端口,默认 8080
JUPYTER_DIRJupyter 根目录,默认 /workspace
JUPYTER_LABtrue 表示 JupyterLab(默认),false 表示经典 notebook
SUPERHEAT_ACCESS_PORTS访问代理守护的容器端口,以逗号分隔。平台为声明了 auth: proxy 的端口设置
SUPERHEAT_ACCESS_TOKEN这些端口要求的实例访问令牌。属敏感内容;不会写入环境文件
SUPERHEAT_APP_HOST由入口点设置,而不是读取:只要设置了 SUPERHEAT_ACCESS_PORTS 就是 127.0.0.1——即使代理启动不了也一样,受保护的端口因此保持不可达,而不是敞开——否则不设。让你的应用在 "${SUPERHEAT_APP_HOST:-0.0.0.0}" 上启动
OPEN_BUTTON_PORT仅供参考——控制台打开按钮的目标端口
CONTAINER_ID仅供参考——工作负载标识符
GPU_COUNT仅供参考——分配了多少块 GPU
SUPERHEAT_TCP_PORT_<n>仅供参考——容器端口 <n> 对应发布的主机端口

Superheat 在你的模板环境变量和租用者的任何覆盖之后,最后才设置这些变量,因此它们都无法被压过。参见环境变量。

构建你自己的镜像​

从 CUDA 版本符合你代码需要的那个标签继承,安装你的依赖,别动入口点:

FROM superheat-cn-registry.cn-shanghai.cr.aliyuncs.com/superheat/base:cuda-12.4

RUN pip install --no-cache-dir torch torchvision transformers

COPY train.py /workspace/train.py
WORKDIR /workspace

有三条规则能让结果保留基础镜像的入口点契约:

  1. 继承,不要重建。 从 nvidia/cuda 构建并塞进你自己的 sshd,会丢掉 com.superheat.base 标签,连带丢掉入口点契约。
  2. 不要覆盖 ENTRYPOINT。 正是基础镜像的入口点在启动 sshd、Jupyter 和你的启动脚本。覆盖它,这些都不会再启动。
  3. 把你的进程放进 CMD。 入口点会在预置完成后执行 CMD,所以这样启动的长期运行的服务会与 sshd 和 Jupyter 并存。

构建好的镜像要推送到你自己的中国大陆镜像仓库,例如你自己的阿里云 ACR 实例。它不在 ACR 的 superheat/ 命名空间里,所以 Superheat 不会在 ssh 和 jupyter 模式下接管它的入口点,也不会自动开放 SSH 端口:请以 args 模式运行它,并在模板上声明它提供服务的端口。参见使用你自己的镜像。

你无法控制的上游镜像,或者带有自己入口点的镜像,同样以 args 模式运行。args 模式运行镜像原生的入口点,不需要基础镜像提供任何东西。参见启动模式。

该从哪个标签继承​

场景标签
你的框架锁定了某个 CUDA 次版本对应的 cuda-<minor>
你想要可用的最新 CUDA,并且可以重新构建latest
你需要几个月后仍能得到完全相同的构建构建一次,推送到你自己的镜像仓库,之后固定使用你自己的标签

固定一个 cuda-<minor> 标签通常是正确答案。latest 会在新的 CUDA 次版本发布时移动,这对开发镜像没问题,但作为可复现训练的基础就很差。

文件系统布局​

路径内容
/workspace工作目录,默认也是 Jupyter 的根目录
/opt/superheat/entrypoint.sh预置入口点
/var/lib/superheat/onstart.sh实际被执行的启动脚本内容
/var/lib/superheat/.onstart-done阻止启动脚本重复运行的哨兵文件
/var/log/superheat/onstart.log启动脚本输出
/var/log/superheat/jupyter.logJupyter 输出
/etc/profile.d/superheat-env.sh登录 shell 看到的环境

sshd 和 Jupyter 也会输出到容器日志,也就是控制台显示的内容。参见日志。