如何使用Paramiko访问EC2实例上运行的Docker容器并执行命令
核心错误点
你的代码无法正常执行docker exec是以下几个写法问题直接导致的:
docker exec命令外层多包了一层单引号,Shell会把整段引号内的内容识别为单个可执行文件名,系统中不存在这个名称的程序,自然不会有任何输出- 命令里的
&& pwd是宿主机Shell层面的拼接逻辑,只有前面的命令完全退出后才会在宿主机执行,根本不会进入容器内部运行 - 非交互SSH场景下不需要给
docker exec加-it参数,其中t是分配交互终端的参数,无实际终端的会话下加这个参数会导致输出异常,你注释里写的get_pty=True也不需要随意开启 - 逻辑bug:你循环连接多台EC2实例时,每次循环都会覆盖SSH连接对象,循环外的容器ID查询、命令执行逻辑只会作用于最后一台连接的实例,前面的实例连接全部被丢弃,也没有做连接关闭处理,会出现资源泄漏
- 用固定
time.sleep()等待命令执行不可靠,网络波动、命令耗时长的时候会出现没等命令跑完就读输出的情况,拿不到完整结果
修正后代码
import paramiko ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) private_key = paramiko.RSAKey.from_private_key_file("<你的本地密钥文件路径>") result_list = [] for ec2_ip in ec2_Ip: # 逐台实例建立连接,处理完成后主动关闭 ssh.connect( hostname=ec2_ip, username='ec2-user', pkey=private_key, look_for_keys=False, allow_agent=False, port=22, timeout=10 ) # 查询实例上第一个运行中的容器ID stdin, stdout, stderr = ssh.exec_command("docker ps --format '{{ .ID }}' | sed -n '1p'") container_id = stdout.read().decode().strip() err_msg = stderr.read().decode() if err_msg: print(f"实例{ec2_ip}查询容器ID失败: {err_msg}") ssh.close() continue if not container_id: print(f"实例{ec2_ip}无运行中的Docker容器") ssh.close() continue # 执行容器内命令:去掉-t参数,不要多余单引号,所有容器内执行的命令放到bash -c后统一传入 # 以下命令替换成你实际需要执行的jps、jstat即可 exec_cmd = f"docker exec -i {container_id} bash -c 'pwd && jps && jstat -gc <对应Java进程ID>'" stdin, stdout, stderr = ssh.exec_command(exec_cmd) # 阻塞等待命令执行完成,替代固定sleep exit_code = stdout.channel.recv_exit_status() exec_output = stdout.read().decode() exec_err = stderr.read().decode() result_list.append({ "ec2_ip": ec2_ip, "container_id": container_id, "output": exec_output, "error": exec_err, "exit_code": exit_code }) print(f"=== 实例{ec2_ip} 容器{container_id} 执行结果 ===") print(exec_output) if exec_err: print(f"执行报错: {exec_err}") ssh.close()
更优实现方案
- 非交互场景执行容器内命令,不需要模拟
docker exec -it 容器 bash的交互进终端流程,直接把要执行的全量命令通过bash -c '命令序列'传给容器即可,稳定性远高于模拟交互输入 - 你的场景是AWS ECS运行容器,完全可以不用走SSH连宿主机再执行docker命令的绕路方案,直接用AWS官方boto3 SDK调用ECS的execute_command接口即可直连容器执行命令,不需要开放22端口、不需要维护SSH密钥,权限管控也更规范
- 所有SSH命令执行后必须同时读取stdout和stderr两个流,很多时候看不到报错不是命令没执行,是你没有读取标准错误流,拿不到错误提示
内容的提问来源于stack exchange,提问作者yaniv
相关产品推荐
相关产品推荐

