Docker Spaces
Spaces 支持使用自定义 Docker 容器 来运行超出 Streamlit 和 Gradio 范围的应用。Docker Spaces 让用户可以突破标准 SDK 之前的限制:从 FastAPI、Go 端点,到 Phoenix 应用和各类 ML Ops 工具,Docker Spaces 都可以很好地承载。
设置 Docker Spaces
在创建新 Space 时选择 Docker 作为 SDK,会在 README.md 的 YAML 块中将 sdk 属性设置为 docker 来初始化 Space。或者,对于已有的 Space 仓库,你也可以直接在 README.md 顶部的 YAML 区块中写上 sdk: docker。你还可以通过设置 app_port: 7860 来修改默认对外暴露的端口 7860。之后,就可以像平时那样编写常规的 Dockerfile。
---
title: Basic Docker SDK Space
emoji: 🐳
colorFrom: purple
colorTo: gray
sdk: docker
app_port: 7860
---
在容器内部,你可以按需开放任意数量的端口。例如,你可以在 Space 中安装 Elasticsearch,并在内部通过其默认端口 9200 调用。
如果你希望对外暴露运行在多个端口上的应用,一个常见做法是在容器中使用 Nginx 等反向代理:对外只打开一个端口,由反向代理将互联网请求分发到不同的内部端口。
密钥和变量管理
你可以在 Space 设置中管理 Space 的环境变量。了解更多信息请参阅此处。
变量
构建时
在构建 Docker Space 时,变量会作为 build-arg 传入。如何在 Dockerfile 中使用这些参数,可以参考 Docker 官方文档。
# 使用 ARG 声明环境变量
ARG MODEL_REPO_NAME
FROM python:latest
# [...]
# 像环境变量一样使用它们
RUN predict.py $MODEL_REPO_NAME
运行时
在运行时,这些变量会注入到容器的环境变量中。
密钥
构建时
出于安全考虑,Docker Spaces 中 secrets 的管理方式与普通变量不同。你可以在 Settings 标签页 中创建 secret,然后在 Dockerfile 中通过挂载的方式在构建阶段使用它。
例如,如果 SECRET_EXAMPLE 是你在 Settings 中创建的 secret 名称,就可以在构建时将其挂载为文件,然后通过 $(cat /run/secrets/SECRET_EXAMPLE) 读取其值。
示例如下:
# 在构建时暴露 secret SECRET_EXAMPLE 并使用其值作为 git remote URL
RUN --mount=type=secret,id=SECRET_EXAMPLE,mode=0444,required=true \
git init && \
git remote add origin $(cat /run/secrets/SECRET_EXAMPLE)
# 在构建时暴露 secret SECRET_EXAMPLE 并使用其值作为 Bearer token 用于 curl 请求
RUN --mount=type=secret,id=SECRET_EXAMPLE,mode=0444,required=true \
curl test -H 'Authorization: Bearer $(cat /run/secrets/SECRET_EXAMPLE)'
运行时
和公共 Variables 一样,在运行时你可以以环境变量的方式访问 secrets。例如,在 Python 中可以使用 os.environ.get("SECRET_EXAMPLE")。可以参考这个使用 secrets 的 Docker Space 示例:secret-example。
权限
容器以用户 ID 1000 运行。为避免权限问题,你应在进行任何 COPY 或下载操作之前创建一个用户并设置其 WORKDIR。
# 创建一个名为 "user" 的新用户,并设置用户 ID 为 1000
RUN useradd -m -u 1000 user
# 切换到 "user" 用户
USER user
# 设置 home 为用户的 home 目录
ENV HOME=/home/user \
PATH=/home/user/.local/bin:$PATH
# 设置工作目录为用户的 home 目录
WORKDIR $HOME/app
# 尝试运行 pip 命令,设置用户后避免 Python 的权限问题
RUN pip install --no-cache-dir --upgrade pip
# 将当前目录内容复制到容器中的 $HOME/app 目录,并设置所有者为 user
COPY --chown=user . $HOME/app
# 下载一个 checkpoint
RUN mkdir content
ADD --chown=user https://<SOME_ASSET_URL> content/<SOME_ASSET_NAME>
始终在 ADD 和 COPY 中指定 --chown=user,以确保新文件归你的用户所有。
如果你仍然遇到权限问题,可能需要在 Dockerfile 中使用 chmod 或 chown 来授予正确的权限。例如,如果你想使用目录 /data,可以这样做:
RUN mkdir -p /data
RUN chmod 777 /data
你应该始终避免多余的 chown 操作。
更新文 件的元数据会在新层中创建新副本。因此,递归 chown 可能会由于所有受影响文件的重复而导致镜像非常大。
与其通过运行 chown 来修复权限:
COPY checkpoint .
RUN chown -R user checkpoint
你应该始终这样做:
COPY --chown=user checkpoint .
(ADD 命令也是如此)
数据持久化
每当你的 Docker Space 重启时,写入磁盘的数据都会丢失,除非你选择持久化存储升级。
如果你选择持久化存储升级,可以使用 /data 目录来存储数据。该目录挂载在持久化卷上,这意味着写入此目录的数据将在重启后保留。
目前,/data 卷仅在运行时可用,即你不能在 Dockerfile 的构建步骤中使用 /data。
你也可以在特定情况下使用我们的 Datasets Hub,你可以在 git LFS 仓库中存储状态和数据。你可以在此处找到一个持久化示例:space_to_dataset_saver,它使用 huggingface_hub 库以编程方式将文件上传到数据集仓库。这个 Space 示例以及本指南将帮助你确定最适合你数据类型的解决方案。
最后,在某些情况下,你可能希望从 Space 的代码中使用外部存储解决方案,如外部托管数据库、S3 等。
带 GPU 的 Docker 容器
你可以通过使用我们的 GPU 类型 Spaces 硬件之一来运行支持 GPU 的 Docker 容器。
我们建议使用 Docker Hub 上的 nvidia/cuda 作为基础镜像,它预装了 CUDA 和 cuDNN。
在 Docker 构建期间,你无法访问 GPU 硬件。因此,你不应该在 Dockerfile 的构建步骤中尝试运行任何与 GPU 相关的命令。例如,在构建镜像时不能运行 nvidia-smi 或 torch.cuda.is_available()。了解更多信息请参阅此处。