基于Podman的Python CI工作流自动化:exec_run调用失败求助
Python3 Podman CI 脚本实现(替代Perl Docker::CLI::Wrapper::Container)
问题核心
你遇到的exec_run报错,本质是只能在运行中的容器上创建执行会话——创建容器后必须先启动它,才能执行命令。下面提供两种实现思路,均无需Dockerfile,直接完成Fedora 42容器创建、代码复制、测试执行的CI流程。
方案1:使用PodmanClient API(官方SDK)
以下是完整可运行的脚本,包含错误处理和资源清理:
import os import tarfile from io import BytesIO from podman import PodmanClient def create_tar_archive(source_dir): """将本地目录打包为tar归档(用于传输到容器)""" tar_buffer = BytesIO() with tarfile.open(fileobj=tar_buffer, mode='w|gz') as tar: tar.add(source_dir, arcname=os.path.basename(source_dir)) tar_buffer.seek(0) return tar_buffer def main(): # 配置参数 IMAGE_NAME = "fedora:42" CONTAINER_NAME = "ci-fedora-test" LOCAL_GIT_REPO = "/path/to/your/local/git/repo" CONTAINER_REPO_PATH = "/opt/test-repo" TEST_COMMANDS = [ "dnf install -y git python3 python3-pip", f"cd {CONTAINER_REPO_PATH} && pip install -r requirements.txt", f"cd {CONTAINER_REPO_PATH} && python -m pytest tests/" ] # 替换为你的Podman套接字路径,普通用户一般是unix:///run/user/UID/podman/podman.sock client = PodmanClient(base_url="unix:///run/user/1000/podman/podman.sock") try: # 拉取镜像(如果本地没有) print(f"拉取镜像 {IMAGE_NAME}...") client.images.pull(IMAGE_NAME) # 创建并启动容器(用tail -f保持运行状态) print("创建并启动容器...") container = client.containers.create( IMAGE_NAME, name=CONTAINER_NAME, command=["tail", "-f", "/dev/null"], detach=True ) container.start() # 复制本地Git仓库到容器 print("复制本地代码到容器...") tar_archive = create_tar_archive(LOCAL_GIT_REPO) container.put_archive(CONTAINER_REPO_PATH, tar_archive) # 逐个执行测试命令,同步获取输出 for cmd in TEST_COMMANDS: print(f"执行命令: {cmd}") exit_code, output = container.exec_run( cmd, shell=True, detach=False, stdout=True, stderr=True ) # 输出命令结果 print(f"命令退出码: {exit_code}") print("标准输出:") print(output.decode('utf-8')) if exit_code != 0: print("错误:命令执行失败,终止流程") break except Exception as e: print(f"执行出错: {str(e)}") finally: # 清理容器:停止并删除 print("清理容器...") try: container = client.containers.get(CONTAINER_NAME) container.stop() container.remove() except: pass client.close() if __name__ == "__main__": main()
关键说明
- 容器启动方式:用
tail -f /dev/null让容器后台保持运行,这样exec_run才能正常工作 - 代码复制:通过
put_archive方法将本地目录打包后传输到容器,避免额外CLI调用 - 同步执行命令:
exec_run设置detach=False会阻塞直到命令完成,直接返回退出码和输出内容 - 资源清理:
finally块确保无论执行成功与否,都会停止并删除容器,避免残留
方案2:直接调用Podman CLI(贴近Perl Docker::CLI::Wrapper::Container思路)
如果更习惯封装CLI命令(和Perl实现逻辑一致),可以用subprocess直接调用podman命令:
import subprocess import os def run_podman_cmd(cmd_args): """执行podman命令,同步获取输出和退出码""" result = subprocess.run( ["podman"] + cmd_args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, check=False ) print(f"执行命令: podman {' '.join(cmd_args)}") print(f"输出:\n{result.stdout}") return result.returncode def main(): IMAGE_NAME = "fedora:42" CONTAINER_NAME = "ci-fedora-test" LOCAL_GIT_REPO = "/path/to/your/local/git/repo" CONTAINER_REPO_PATH = "/opt/test-repo" TEST_COMMANDS = [ "dnf install -y git python3 python3-pip", f"cd {CONTAINER_REPO_PATH} && pip install -r requirements.txt", f"cd {CONTAINER_REPO_PATH} && python -m pytest tests/" ] try: # 拉取镜像 run_podman_cmd(["pull", IMAGE_NAME]) # 创建并启动容器 run_podman_cmd(["run", "-d", "--name", CONTAINER_NAME, IMAGE_NAME, "tail", "-f", "/dev/null"]) # 复制本地代码 run_podman_cmd(["cp", LOCAL_GIT_REPO, f"{CONTAINER_NAME}:{CONTAINER_REPO_PATH}"]) # 执行测试命令 for cmd in TEST_COMMANDS: exit_code = run_podman_cmd(["exec", CONTAINER_NAME, "/bin/bash", "-c", cmd]) if exit_code != 0: print("命令执行失败,终止流程") break except Exception as e: print(f"出错: {str(e)}") finally: # 清理容器 run_podman_cmd(["stop", CONTAINER_NAME]) run_podman_cmd(["rm", CONTAINER_NAME]) if __name__ == "__main__": main()
优势
- 和Perl的
Docker::CLI::Wrapper::Container逻辑完全对齐,都是直接调用容器CLI命令 - 无需依赖Podman SDK,环境兼容性更好
报错原因说明
你之前遇到的can only create exec sessions on running containers: container state improper,是因为创建容器后没有调用container.start()(API方式)或podman start(CLI方式)——容器处于"created"而非"running"状态,无法执行exec命令。
内容的提问来源于stack exchange,提问作者Shlomi Fish
相关产品推荐
相关产品推荐

