You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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()

关键说明

  1. 容器启动方式:用tail -f /dev/null让容器后台保持运行,这样exec_run才能正常工作
  2. 代码复制:通过put_archive方法将本地目录打包后传输到容器,避免额外CLI调用
  3. 同步执行命令:exec_run设置detach=False会阻塞直到命令完成,直接返回退出码和输出内容
  4. 资源清理: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 05:25:18