Podman 与 docker-compose 协同实战:env_and_volume 环境变量与共享卷测试全解析

Podman 与 docker-compose 协同实战:env_and_volume 环境变量与共享卷测试全解析 容器运行时云原生CLI【免费下载链接】podmanPodman: A tool for managing OCI containers and pods.项目地址https://gitcode.com/gh_mirrors/po/podman点击查看免费下载导读本文深入剖析 Podman 仓库中test/compose/env_and_volume这一 docker-compose v2 集成测试它用两个运行 Flask 的容器完整验证了 Compose 的环境变量注入与跨容器共享卷两大核心能力一个容器把环境变量PODMAN_MSG的值写入共享卷中的文件另一个容器读取该文件并通过 HTTP 对外返回。读完本文你将掌握该测试的编排文件结构、双容器数据流设计、源码实现细节以及如何借助test-compose测试框架在本地复现并验证这一场景。一、测试背景Podman 对 docker-compose v2 的兼容性验证在 Podman 仓库中test/compose 目录专门用于验证docker-compose v2在 Podman 下的行为。该目录说明明确写道Tests for docker-compose v2 under podman. docker-compose v1 is no longer supported upstream so we no longer test with it.这意味着每个子目录都代表一个独立的 Compose 场景测试env_and_volume正是其中专门覆盖**环境变量environment与共享卷volume**交互的用例。从仓库结构看test/compose 下并列存在simple_port_map、mount_and_label、ipam_set_ip、two_networks等多个场景而env_and_volume侧重的是容器间通过共享卷传递数据的典型模式——这一模式在真实微服务架构如生产者写入、消费者读取的中间件场景中非常常见。二、测试设计总览两个 Flask 容器如何协作env_and_volume/README.md 用一段话清晰描述了测试的设计意图This test creates two containers both of which are running flask. The first container has an environment variable called PODMAN_MSG. That container pipes the contents of PODMAN_MSG to a file on a shared volume between the containers. The second container then reads the file and returns the PODMAN_MSG value via flask (http).整个测试的数据流可以概括为三条链路环境变量注入writer容器启动时通过 Compose 的environment获得PODMAN_MSGpodman_rulez共享卷写入writer把该变量的值写入挂载在/data的共享卷中的message文件跨容器读取与 HTTP 暴露reader容器挂载同一卷读取message文件内容并通过 Flask 在 HTTP 端口对外返回。两个容器各司其职writer负责生产reader负责消费共享卷则是二者之间的数据管道。这验证了 Compose 两层关键能力——environment配置项能否正确把变量传入容器进程以及同名命名卷named volume能否被多个服务同时挂载并实现数据互通。三、docker-compose.yml 逐项拆解测试的核心编排文件是 docker-compose.yml完整内容如下version: 3 services: writer: environment: - PODMAN_MSGpodman_rulez build: write ports: - 5000:5000 volumes: - data:/data reader: build: read ports: - 5001:5000 volumes: - data:/data volumes: data:各配置项的作用与设计意图配置项值说明version: 3Compose 文件版本声明使用 Compose v3 规范这也是 docker-compose v2 默认支持的格式services.writer.environmentPODMAN_MSGpodman_rulez定义注入到writer容器的环境变量即测试要验证的核心数据services.writer.buildwrite指定从write/子目录构建镜像services.writer.ports5000:5000宿主机端口 5000 映射到容器内 Flask 默认端口 5000services.writer.volumesdata:/data将命名卷data挂载到容器内/data目录services.reader.buildread指定从read/子目录构建镜像services.reader.ports5001:5000宿主机端口 5001 映射到容器内 5000避免与writer冲突services.reader.volumesdata:/data与writer共享同一个命名卷datavolumes.data空定义声明名为data的命名卷供两个服务共同引用值得注意的关键点在于两个服务挂载的是同一个命名卷data而不是各自独立的匿名卷。这正是共享卷生效的前提——Compose 会在创建服务时将同名卷解析为同一存储位置使/data/message文件对两个容器都可见。四、容器镜像与 Flask 应用源码剖析4.1 写入端 writer写入端的镜像构建文件 write/Dockerfile 如下FROM quay.io/libpod/podman_python WORKDIR /app COPY . /app ENTRYPOINT [python3] CMD [app.py]它基于 Podman 项目维护的quay.io/libpod/podman_python镜像内含 Python 与 Flask 环境把write/目录内容复制到/app以python3 app.py作为入口。应用代码 write/app.pyfrom flask import Flask import os app Flask(__name__) app.route(/) def hello(): f open(/data/message, w) f.write(os.getenv(PODMAN_MSG)) f.close() return done if __name__ __main__: app.run(host0.0.0.0)writer的关键行为集中在根路由处理器中通过os.getenv(PODMAN_MSG)读取容器环境变量——这正是 Composeenvironment注入结果的直接验证点以写模式打开共享卷上的/data/message把变量值写入文件向访问者返回字符串done作为写入成功的信号。这里app.run(host0.0.0.0)绑定所有网卡配合 Compose 的端口映射使宿主机上的curl http://localhost:5000可以直接访问。4.2 读取端 reader读取端镜像构建文件 read/Dockerfile 与写入端完全一致同样基于podman_python镜像。应用代码 read/app.pyfrom flask import Flask app Flask(__name__) app.route(/) def hello(): f open(/data/message, r) return f.read() if __name__ __main__: app.run(host0.0.0.0)reader的逻辑更加简洁直接以读模式打开同一路径/data/message把文件内容作为 HTTP 响应体返回。整个过程不依赖任何网络通信或外部服务完全依靠共享卷完成数据传输从而把环境变量 共享卷这条链路从writer到reader完整闭环。五、验证方法tests.sh 与 HTTP 断言env_and_volume/tests.sh 定义了该测试的最终断言# -*- bash -*- test_port 5000 done test_port 5001 podman_rulez这与 README 中列出的两条验证命令一一对应* curl http://localhost:5000 and verify message * curl http://localhost:5001 and verify messagetest_port 5000 done请求writer暴露的 5000 端口期望返回done确认写入端已成功把PODMAN_MSG写入共享卷文件test_port 5001 podman_rulez请求reader暴露的 5001 端口期望返回podman_rulez即环境变量注入的原始值确认读取端从共享卷读到了writer写入的内容且内容与环境变量值完全一致。5.1 test_port 的实现原理test_port函数定义在测试框架脚本 test-compose 中其核心实现为function test_port() { local port$1 # e.g. 5000 local op$2 # or ~ local expect$3 # what to expect from curl output local actual actual$(curl --retry 3 --retry-all-errors -s -S http://127.0.0.1:$port/) ... case $op in ) is $actual $expect $testname : port $port ;; ~) like $actual $expect $testname : port $port ;; *) die Invalid operator $op ;; esac }test_port使用curl --retry 3 --retry-all-errors对127.0.0.1:port发起请求内置 3 次重试以容忍容器启动的延迟。操作符方面表示完全相等~则借助expr做子串匹配支持通配。从 test/compose/README.md 的约定可以知道tests.sh will probably contain commands of the formtest_port 12345 hello thereWhere 12345 is the port to curl to; checks equality, ~ usesexprto check substrings.这一设计让每个测试子目录的tests.sh都能用极简的一行命令完成端口级行为断言。六、测试框架test-compose 如何驱动整个场景6.1 整体执行流程env_and_volume并不是一个孤立脚本它由仓库根目录级别的test/compose/test-compose统一驱动。根据 test/compose/README.md每个测试子目录的执行流程如下在空的工作目录下建立全新的 Podman 根root与运行根runroot保证测试环境隔离启动一个以该根为后端的 Podman 服务podman system service并通过 Unix socket 暴露 Docker 兼容 API切换到测试子目录执行docker-compose up -dsource该目录下的tests.sh执行断言执行docker-compose down清理资源。此外测试目录中还可以放置setup.sh和teardown.sh分别在up之前和down之后被执行用于准备或清理额外资源。6.2 服务端启动与隔离细节从 test-compose 的start_service函数可以看到 Podman 服务是如何以隔离方式启动的$PODMAN_BIN \ --log-level debug \ --storage-drivervfs \ --root $WORKDIR/root \ --runroot $WORKDIR/runroot \ --cgroup-managersystemd \ --network-config-dir $WORKDIR/networks \ --cdi-spec-dir $WORKDIR/cdi \ system service \ --time 0 unix://$DOCKER_SOCK \ $WORKDIR/server.log 要点每个测试都使用mktemp生成的独立WORKDIR其下的root、runroot、networks、cdi目录全部指向临时路径实现存储与网络配置的完全隔离存储驱动固定为vfs避免对 overlayfs 等宿主机内核特性的依赖服务通过unix://$DOCKER_SOCK暴露 Docker 风格 socket供 docker-compose 客户端连接。6.3 root 与 rootless 双模式支持框架对 root 和 rootless 两种运行方式做了差异化处理root 模式导出DOCKER_HOSTunix://$DOCKER_SOCKdocker-compose 直接使用该环境变量连接 Podman 服务rootless 模式socket 路径放在用户可访问的$WORKDIR/docker.sock并通过podman system connection add compose-sock unix://$DOCKER_SOCK注册连接同时设置PODMAN_CONNECTIONS_CONF指向临时配置文件避免污染用户的真实连接配置。此时podman_compose函数会以$PODMAN_BIN --connection compose-sock compose $的方式调用。6.4 运行与调试命令按 test/compose/README.md 的用法说明可以在仓库根目录下运行全部 Compose 测试或仅运行指定子目录# 运行全部 compose 测试需要 root sudo test/compose/test-compose # 只运行与 env_and_volume 匹配的子目录 sudo test/compose/test-compose env_and_volume脚本通过参数通配匹配子目录${TEST_ROOTDIR}/*${i}*/docker-compose.yml因此传env_and_volume即可只跑本用例。若希望容器在docker-compose down之前暂停以便排查可以设置COMPOSE_WAIT环境变量env COMPOSE_WAIT1 sudo --preserve-envCOMPOSE_WAIT test/compose/test-compose暂停期间可以在另一个终端借助测试输出中的临时目录X/var/tmp/test-compose.tmp.XXXXXX直接对测试容器执行诊断podman --root $X/root --runroot $X/runroot ps -a podman --root $X/root --runroot $X/runroot logs -l6.5 测试结果输出框架以近似 TAP 13 的格式输出测试结果便于 CI 的 logformatter 识别每个子目录会依次报告up、tests、down三个阶段的通过情况。若test_port的 curl 请求失败还会自动输出server.log与测试日志帮助定位问题。七、场景解读该测试验证了哪些 Podman Compose 能力结合源码与编排文件可以归纳出env_and_volume实际覆盖的验证维度环境变量注入environmentCompose 中的environment列表项被正确转换为容器进程可见的环境变量writer通过os.getenv(PODMAN_MSG)能读到podman_rulez命名卷共享named volume两个服务声明挂载同一个data卷后writer在/data/message的写入能被reader在同一路径读取证明卷在不同容器间共享且文件系统可见性一致端口映射与多服务共存ports两个容器都在容器内监听 5000 端口通过宿主机 5000/5001 的不同映射实现并行可访问验证了 Compose 端口映射的隔离性端到端 HTTP 行为通过curl断言确认两条 HTTP 链路返回内容符合预期证明 Podman 兼容层能够完整支撑 docker-compose 的工作流。从工程实践角度看这个测试也演示了一种可复用的设计模式用写入方 共享卷 读取方三个要素构造最小可验证的数据管道。writer暴露生产接口返回done表示落盘成功reader暴露消费接口返回真实数据测试断言同时覆盖两端任何一端的异常都会导致对应端口校验失败从而精确定位问题出在写入链路还是读取链路。八、总结test/compose/env_and_volume虽然是一个体量很小的测试目录却以极低的复杂度完整覆盖了 docker-compose 生态中最常用的两项能力——环境变量注入与命名卷共享并且给出了清晰的验证路径。通过阅读 docker-compose.yml、write/app.py、read/app.py 与 tests.sh可以把它当作一份Compose 双容器共享数据的参考实现而 test-compose 脚本则示范了如何在隔离的 Podman 环境里驱动 docker-compose 并做端口级断言。对于希望在自己的 Podman Compose 环境中验证环境变量与卷共享行为的开发者这套目录本身就是一份可复制、可运行的模板。赞分享容器运行时云原生CLI【免费下载链接】podmanPodman: A tool for managing OCI containers and pods.项目地址https://gitcode.com/gh_mirrors/po/podman点击查看免费下载相关推荐Zonos-v0.1 Docker Compose配置多服务协同与环境变量管理Zonos v0.1 Docker Compose配置多服务协同与环境变量管理 你是否在部署Zonos v0.1时遇到过服务启动失败、GPU资源无法调用或环境语音音频基础模型AI 应用Path of Building PoE2流放之路2角色构建的终极解决方案Path of Building PoE2流放之路2角色构建的终极解决方案 你是否曾经花费数小时研究《流放之路2》的天赋树却在游戏中发现角色表现远不如预期桌面应用游戏开发Docker Compose与Spring Boot环境变量解析问题深度解析Docker Compose与Spring Boot环境变量解析问题深度解析 问题背景 在使用Docker Compose部署Spring Boot应用时开发云原生容器编排DevOpsCLI上一篇Qdrant终极性能优化指南如何通过SIMD加速实现CPU硬件性能极致释放下一篇如何用700行代码打造专业音频分离工具Ultimate Vocal Remover GUI完整指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考