
Jaeger 以非 root 用户运行 Badger 存储目录权限问题分析与 Docker Compose 初始化修复方案【免费下载链接】jaegerCNCF Jaeger, a Distributed Tracing Platform项目地址: https://gitcode.com/GitHub_Trending/ja/jaeger自 Jaeger 1.50 起官方 Docker 镜像默认以非 root 用户UID 10001运行容器进程。当使用 Badger 作为持久化存储后端时如果数据目录由 Docker 命名卷named volume挂载容器内用户可能无法在该目录下创建文件进而触发 permission denied 写入错误。本文基于仓库中的 storage-file-non-root-permission.md 文档结合 Badger 存储的源码实现讲解问题成因、相关命令行参数并给出一个可复制的 docker-compose 初始化步骤init container修复方案帮助你在容器化环境中稳定运行 Badger 持久化存储。背景镜像非 root 化带来的存储目录写权限问题在 1.50 版本之前Jaeger 的 Docker 镜像以 root 身份运行主进程因此容器内的 Badger 进程可以自由地在任何挂载点上创建数据目录。而 1.50 之后镜像默认不再以 root 运行这在部分部署环境下会导致类似如下错误permission denied出现该错误时Badger 无法在数据目录中写入键keys与值values文件存储初始化失败。从本仓库的 cmd/jaeger/Dockerfile 中可以找到这一改动的直接证据第 11 行定义了构建参数ARG USER_UID10001第 50 行通过USER ${USER_UID}将生产镜像的启动用户固定为 UID 10001调试镜像在第 56、99 行采用同样配置。也就是说容器内主进程以UID 10001非 root运行。当数据目录是宿主机目录或 Docker 命名卷的挂载点时其属主往往不是 10001目录权限不足以让 Badger 进程写入文件于是出现 permission denied。为什么 Badger 存储需要可写目录源码视角要理解修复方案需要先了解 Badger 存储初始化时对目录的处理逻辑。本仓库中 Badger 存储工厂位于 internal/storage/v1/badger/factory.go其Initialize方法第 81-138 行区分两种模式临时存储Ephemeral默认模式。工厂通过os.MkdirTemp(, badger)在系统临时目录创建数据区进程退出后数据即丢失持久化存储当--badger.ephemeralfalse时工厂调用initializeDir分别创建键目录与值目录再调用 Badger 打开数据库。关键函数initializeDirfactory.go的实现在此值得关注// initializeDir makes the directory and parent directories if the path doesnt exists yet. func initializeDir(path string) { if _, err : os.Stat(path); err ! nil os.IsNotExist(err) { os.MkdirAll(path, 0o700) } }它仅在目录不存在时以0o700权限创建目录。这里有两个隐含约束目录的创建动作发生在容器启动之后如果父目录挂载点对当前用户UID 10001不可写os.MkdirAll会失败即便目录已存在进程仍然需要对该目录拥有读写权限才能创建 Badger 的键日志、值日志value log等数据文件。此外badger.Openfactory.go会同时打开键目录与值目录的文件句柄任何一个目录无权限都会导致初始化失败。因此问题的本质是非 root 的 Jaeger 进程必须拥有对数据目录的属主或写权限。Badger 存储相关命令行参数全解析修复方案中出现的参数均来自 Badger 存储的 flag 定义完整清单位于 internal/storage/v1/badger/options.go第 26-67 行。下表整理自该文件供配置时参考命令行参数类型默认值说明--badger.ephemeralbooltrue是否使用临时存储数据存放在 tmpfs。设为false才能启用下面两个目录参数--badger.directory-keystring{可执行文件目录}/data/keys键索引存储目录官方建议放在 SSD 磁盘需ephemeralfalse才生效--badger.directory-valuestring{可执行文件目录}/data/values值span 数据存储目录需ephemeralfalse才生效--badger.span-store-ttlduration72h0m0sspan 数据的保留时长格式为 Gotime.Duration--badger.consistencyboolfalse是否立即将每次写入同步刷盘开启会显著影响写入性能--badger.maintenance-intervalduration5m0s值日志维护任务ValueLogGC的运行周期--badger.metrics-update-intervalduration10sJaeger 采集 Badger 指标expvar的周期主要用于测试--badger.read-onlyboolfalse以只读模式打开数据库允许多实例同时打开同一数据目录相关默认值定义在 internal/storage/v1/badger/config.go第 14-24 行与第 64-79 行默认 TTL 为72h与本文文档示例中的72h0m0s一致默认目录为可执行文件目录/data/keys与可执行文件目录/data/values默认Ephemeraltrue即默认数据不持久化重启即丢失——生产环境必须显式指定--badger.ephemeralfalse并给出持久化目录。参数解析由initFromViperoptions.go完成它会将 viper 读取到的值写入Config结构体而Config.Validate则通过govalidator.ValidateStruct对配置做合法性校验见 config.go。另外值得了解的是 TTL 的落地方式span 写入时会为索引项设置 Badger 的过期时间TTL 由 writer.go 的NewSpanWriter传入并写入索引服务名、操作名索引的过期状态则由 cache.go 维护。这意味着--badger.span-store-ttl不只是清理任务参数而是直接控制 Badger 内部过期键的生命周期。修复方案以 root 运行一次初始化步骤预创建数据目录官方文档给出的解决思路是增加一个以 root 运行的初始化容器预先创建 Badger 数据目录并把目录属主改为真正运行 Jaeger 主进程的用户UID 10001。之后主容器以非 root 身份启动时数据目录已经具备可写权限。文档中的完整 docker-compose 配置如下原样继承并补充了注释version: 3.9 services: [...] jaeger: image: jaegertracing/all-in-one:latest command: - --badger.ephemeralfalse - --badger.directory-key/badger/data/keys - --badger.directory-value/badger/data/values - --badger.span-store-ttl72h0m0s # limit storage to 72hrs environment: - SPAN_STORAGE_TYPEbadger # Mount host directory jaeger_badger_data as /badger inside the container. # The actual data directory will be /badger/data, # since we cannot change permissions on the mount. volumes: - jaeger_badger_data:/badger ports: - 16686:16686 - 14250 - 4317 depends_on: prepare-data-dir: condition: service_completed_successfully prepare-data-dir: # Run this step as root so that we can change the directory owner. user: root image: jaegertracing/all-in-one:latest command: /bin/sh -c mkdir -p /badger/data touch /badger/data/.initialized chown -R 10001:10001 /badger/data volumes: - jaeger_badger_data:/badger volumes: jaeger_badger_data:配置要点逐项拆解命名卷挂载到/badger而不是直接挂到数据目录Docker 命名卷首次创建时的属主通常是 root无法通过命令直接修改挂载点本身的属主因此文档将卷挂载在/badger真正的数据目录/badger/data是初始化容器在卷内创建的子目录可以自由chown。初始化容器的命令做了三件事mkdir -p /badger/data预创建数据目录键与值子目录会由 Badger 工厂自行创建见上文initializeDirtouch /badger/data/.initialized留一个标记文件便于人工确认初始化已完成若后续希望保留标记请留意 Badger 只读模式下该文件与数据库文件的共存关系chown -R 10001:10001 /badger/data将目录属主递归改为 UID:GID 10001与 cmd/jaeger/Dockerfile 中USER_UID10001保持一致。user: root覆盖镜像默认用户初始化容器显式指定以 root 运行这是它能够执行chown的前提。depends_on配合condition: service_completed_successfully确保主容器只在初始化容器成功退出退出码为 0后才启动形成严格的先初始化、后启动顺序。若初始化失败主容器不会启动避免了竞态导致的随机性 permission denied。主容器通过命令行参数指定持久化存储路径--badger.ephemeralfalse关闭临时存储--badger.directory-key与--badger.directory-value分别指向初始化容器创建好的目录--badger.span-store-ttl72h0m0s将数据保留期限限制为 72 小时防止磁盘被无限增长的 Badger 数据占满。环境变量SPAN_STORAGE_TYPEbadger告知 Jaeger 使用 Badger 作为 span 存储后端。方案原理与适用边界这一方案的原理可以概括为用一次 root 权限完成目录属主移交之后所有数据写入都在非 root 权限下完成与以 root 运行整个 Jaeger 进程相比攻击面更小、更符合最小权限原则。它在以下场景下特别适用使用 Docker Compose 或单机容器部署 all-in-one且希望 Badger 数据持久化挂载点是 Docker 命名卷或宿主机目录无法预先把属主改为 10001希望保持镜像默认的非 root 安全基线。从源码角度该方案与 Badger 工厂的初始化逻辑完全契合主容器启动时initializeDir发现/badger/data/keys与/badger/data/values的父目录已存在且属主正确badger.Open即可顺利打开数据库见 factory.go。需要注意的边界与限制卷一旦初始化属主固定为 10001如果后续改用其他 UID 运行例如修改 Dockerfile 中的USER_UID需要重新执行一次初始化可先清空卷再重跑prepare-data-dirWindows/macOS Docker Desktop 场景文件权限语义与 Linux 不同通常不会触发该问题但本方案依然无害多副本/多实例场景Badger 仅允许单实例读写同一数据目录除非启用--badger.read-only只读模式本方案面向单实例 all-in-one 部署如需横向扩展应优先考虑 Cassandra、Elasticsearch/OpenSearch、ClickHouse 等支持多实例的存储后端。延伸不使用 Compose 时的等价做法如果未使用 docker-compose也可以手动执行两个步骤达到同样效果# 1. 先以 root 运行一次容器预创建目录并修正属主 docker run --rm -u root \ -v jaeger_badger_data:/badger \ jaegertracing/all-in-one:latest \ /bin/sh -c mkdir -p /badger/data chown -R 10001:10001 /badger/data # 2. 再以默认非 root 用户启动主容器 docker run -d \ -e SPAN_STORAGE_TYPEbadger \ --badger.ephemeralfalse ...此处省略端口与卷参数 \ jaegertracing/all-in-one:latest另外如果你只是临时试用、不需要持久化保持默认的--badger.ephemeraltrue即可此时数据写入系统临时目录不会遇到目录权限问题只有切换到持久化目录时才需要上述初始化步骤。总结Jaeger 1.50 之后的镜像默认以 UID 10001 的非 root 用户运行这是容器安全的改进但也让 Badger 持久化目录的属主问题浮出水面。理解这一问题的关键证据都在仓库源码中镜像的USER ${USER_UID}cmd/jaeger/Dockerfile、Badger 工厂的initializeDir与badger.Openinternal/storage/v1/badger/factory.go以及完整的 flag 定义与默认值internal/storage/v1/badger/options.go、internal/storage/v1/badger/config.go。本文提供的 docker-compose 方案通过一个 root 初始化容器预创建/badger/data并chown 10001:10001再配合depends_on的完成条件约束启动顺序即可在不牺牲镜像非 root 安全基线的前提下让 Badger 持久化存储稳定运行。原文档 storage-file-non-root-permission.md 保留在仓库中可作为排查该问题时的第一手参考。【免费下载链接】jaegerCNCF Jaeger, a Distributed Tracing Platform项目地址: https://gitcode.com/GitHub_Trending/ja/jaeger创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考