
Docker密钥别直接塞进环境变量:Compose与Swarm做法并不相同

在 Docker Compose 中,可以用宿主机环境变量创建 secret,但容器内得到的是挂载文件,应用必须读取文件内容。Docker 的 Compose 密钥用法以 /run/secrets/ 下的文件交付凭据,并说明 _FILE 是部分镜像支持的读取约定;配置 DB_PASSWORD_FILE 时,变量保存的是文件路径,不是密码。
部署到 Swarm 时,要先创建集群 secret,再将它授权给服务;应用仍可读取同一容器内路径。Compose 的 secrets 语法允许 file 或宿主机 environment 作为来源,但明确指出 environment 来源不受 docker stack deploy 支持。迁移时可以保留应用的读文件方式,两种部署配置中的 secret 来源则要分别处理。
先确认应用如何读取密钥
先找出当前通过容器环境变量传入的数据库密码或 API 令牌,以及实际读取它们的应用或镜像。迁移的关键是把应用的读取入口改到文件:例如让支持 DB_PASSWORD_FILE 的应用读取 /run/secrets/db_password,再从服务配置中移除原先承载密码值的 DB_PASSWORD。只更改 secret 声明、保留应用原有的环境变量读取逻辑,应用就拿不到新凭据。
_FILE 不是 Docker 为所有应用自动实现的功能。第三方镜像需要核对其支持的变量名;自建应用则要自行打开文件,并处理空文件、读取失败及末尾换行等情况。下文的示意应用假定已经支持 DB_PASSWORD_FILE。如果它只认识 DB_PASSWORD,而启动脚本把文件内容重新写入 DB_PASSWORD,密码仍会进入容器进程环境,达不到这次迁移的目的。
以下示例使用 Linux 容器。服务以短格式声明 db_password 后,默认读取路径是 /run/secrets/db_password;顶层定义 secret 本身并不会让所有服务获得访问权。给 API_TOKEN 做同样的迁移时,可以换用 api_token 这样的 secret 名称,但应用也须支持对应的文件路径配置或直接读文件。
Compose 本地运行:从宿主机变量或文件提供凭据

如果密码目前由运行 Compose 命令的宿主机进程环境提供,下面是可保存为 compose.yaml 的示意配置,使用 YAML 单行写法:{services: {api: {image: your-app:latest, environment: {DB_PASSWORD_FILE: /run/secrets/db_password}, secrets: [db_password]}}, secrets: {db_password: {environment: DB_PASSWORD}}}。将 your-app:latest 换成支持 DB_PASSWORD_FILE 的实际镜像,并在执行 docker compose up -d 前,让该命令取得宿主机变量 DB_PASSWORD 的值。
这里的两个 environment 处在不同层级,作用也不同。顶层 secrets 中的 environment: DB_PASSWORD 指定凭据来源;服务中的 DB_PASSWORD_FILE 只把容器内文件路径交给应用。前者不会自动生成一个含密码值的容器环境变量,但密码仍存在于提供它的宿主机进程环境中,因此也要管理好原来的凭据供应方式。
如果凭据已保存在宿主机文件中,可把顶层声明改为 secrets: {db_password: {file: ./secrets/db_password.txt}},服务部分保持不变。该路径指向宿主机上的源文件,容器仍从 /run/secrets/db_password 读取。源文件应限制读取权限并排除在代码仓库之外;本地 Compose 以文件绑定挂载交付 secret,不能把这种交付方式等同于 Swarm 的集群加密存储。
Swarm 部署:先创建集群 secret,再授权服务

在已初始化的 Swarm 管理节点上,先准备不纳入版本控制的密码文件,再运行 docker secret create db_password ./db_password.txt。随后将以下示意配置保存为 stack.yaml:{services: {api: {image: your-app:latest, environment: {DB_PASSWORD_FILE: /run/secrets/db_password}, secrets: [db_password]}}, secrets: {db_password: {external: true}}}。运行 docker stack deploy -c stack.yaml demo 时,external: true 表示引用已创建的集群 secret,而不是从宿主机环境变量创建它。
Swarm 会把 secret 交给获授权的运行中服务任务。Docker 的 Swarm 密钥说明指出,secret 经双向 TLS 连接传给管理节点,保存在加密的 Raft 日志中,并在获授权的 Linux 容器内挂载到内存文件系统;其中的数据库密码轮换示例还说明,更换 secret 后仍须更新数据库内部保存的账户密码。这些传输、存储和运行时机制针对 Swarm,不能套用到普通的 docker compose up。
应用在这两份示意配置中都读取 /run/secrets/db_password,但 secret 的创建者不同。不要把 Compose 示例中的顶层 environment: DB_PASSWORD 原样复制到 stack.yaml;如果要让 stack 从部署端文件创建 secret,应改用 file 来源,若已通过 docker secret create 建立集群 secret,则使用 external 引用。
轮换时让新文件与实际凭据同步生效

Compose 使用宿主机环境变量作为来源时,先更新提供给 Compose 进程的值,再运行 docker compose up -d --force-recreate api,让相关容器重新创建。使用 file 来源时,先安全地更新源文件,再重新创建使用它的容器。若应用只在启动时读取一次文件,仅修改宿主机变量或文件,正在运行的应用进程不会因此自动采用新密码。
Swarm secret 创建后不能原地改写。先用新凭据创建 db_password_v2;接着在 stack.yaml 的顶层把它声明为 external,并将服务的 secrets 项改为 {source: db_password_v2, target: db_password}。重新运行 docker stack deploy -c stack.yaml demo 后,新任务仍从 /run/secrets/db_password 读取,但文件内容来自新版 secret。确认服务使用新凭据正常运行、旧 secret 已不再被服务引用后,才能删除旧 secret。
如果轮换的是数据库密码,还要协调数据库账户更新与应用任务切换:只换容器内文件,数据库仍持有旧密码时,新连接会失败。API 令牌也应按签发系统的实际规则安排新令牌启用与旧令牌撤销。完成迁移后,核对应用连接状态、目标文件的可读性,以及服务配置中是否已移除承载密码值的旧环境变量;无需在日志或命令输出中打印 secret 内容。
相关阅读:
相关文章
订阅我们的新闻通讯
将最新 Web3、AI 和加密货币新闻直接发送到您的邮箱。




