如何无需手动SSH即可在多Docker容器中运行MPI程序
问题概述
我是Docker新手,正在用Docker模拟集群环境,已定义容器共享的自定义网络,为每个容器分配不同端口模拟网卡。现有Dockerfile可复制SSH密钥并自动启动SSH服务,ENTRYPOINT为service ssh start && bash。但当前存在以下问题:
容器启动后必须手动执行eval ssh-agent && ssh-add /root/.ssh/docker_id_rsa,再手动SSH到所有其他容器,才能运行MPI程序;不执行这些步骤则无法跨容器运行程序。
需求:
- 附加到某个容器后,无需手动执行上述步骤即可直接跨所有容器运行MPI程序
- 或至少能自动完成SSH到其他容器的操作后再运行程序
已尝试的方法:在ENTRYPOINT中添加eval和ssh-add命令,在docker run命令中加入这些命令,试过docker-compose但不太理解其功能,需要合适的实现建议。
现有配置
Dockerfile
FROM img_base AS img COPY /keys/ /root/.ssh COPY /keys/docker_id_rsa.pub /root/.ssh/authorized_keys RUN sed -i 's/#PermitRootLogin no/PermitRootLogin yes/g' /etc/ssh/sshd_config RUN sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/g' /etc/ssh/sshd_config RUN sed -i "s+StrictHostKeyChecking .*+StrictHostKeyChecking allow-new+" /etc/ssh/sshd_config RUN echo "localhost" >> hostfile RUN echo "root@container2" >> hostfile RUN echo "root@container3" >> hostfile RUN echo "root@container4" >> hostfile EXPOSE 22 ENTRYPOINT service ssh start && bash && eval `ssh-agent` && ssh-add /root/.ssh/docker_id_rsa
容器启动脚本
#!/bin/bash docker run --rm -dit --name container1 --network=my-net --ip=172.18.0.2 -p 4022:22 --add-host container2:172.18.0.3 --add-host container3:172.18.0.4 --add-host container4:172.18.0.5 img docker run --rm -dit --name container2 --network=my-net --ip=172.18.0.3 -p 3022:22 --add-host container1:172.18.0.2 --add-host container3:172.18.0.4 --add-host container4:172.18.0.5 img docker run --rm -dit --name container3 --network=my-net --ip=172.18.0.4 -p 5022:22 --add-host container2:172.18.0.3 --add-host container1:172.18.0.2 --add-host container4:172.18.0.5 img docker run --rm -dit --name container4 --network=my-net --ip=172.18.0.5 -p 6022:22 --add-host container2:172.18.0.3 --add-host container3:172.18.0.4 --add-host container1:172.18.0.2 img docker attach container1
解决方案建议
1. 修复ENTRYPOINT执行顺序问题
原ENTRYPOINT中bash命令在eval和ssh-add之前,一旦bash启动,后面的命令永远不会执行。需要调整顺序,或者将ssh-agent的配置放到bash初始化文件中。
2. 利用bash初始化文件自动加载SSH环境
将ssh-agent启动和密钥添加的命令写入/root/.bashrc,这样每次attach到容器、进入bash会话时都会自动执行:
RUN echo 'eval `ssh-agent` > /dev/null && ssh-add /root/.ssh/docker_id_rsa > /dev/null' >> /root/.bashrc
同时修改ENTRYPOINT为:
ENTRYPOINT service ssh start && bash
3. 预缓存主机密钥,避免首次SSH确认
在Dockerfile中添加命令,提前获取所有容器的主机密钥并写入known_hosts,这样首次SSH时不会弹出确认提示:
# 先创建known_hosts文件 RUN touch /root/.ssh/known_hosts # 预存所有容器的主机密钥(依赖自定义网络的DNS解析) RUN ssh-keyscan container1 container2 container3 container4 >> /root/.ssh/known_hosts
如果构建镜像时无法解析这些主机名,可以将StrictHostKeyChecking设置为no(仅测试环境使用,生产环境不建议):
RUN sed -i "s+StrictHostKeyChecking .*+StrictHostKeyChecking no+" /etc/ssh/sshd_config
4. 用docker-compose简化集群管理
docker-compose可以一次性定义所有容器、网络配置,不用手动编写多个docker run命令。示例docker-compose.yml:
version: '3' services: container1: image: img container_name: container1 networks: my-net: ipv4_address: 172.18.0.2 ports: - "4022:22" extra_hosts: - "container2:172.18.0.3" - "container3:172.18.0.4" - "container4:172.18.0.5" tty: true stdin_open: true container2: image: img container_name: container2 networks: my-net: ipv4_address: 172.18.0.3 ports: - "3022:22" extra_hosts: - "container1:172.18.0.2" - "container3:172.18.0.4" - "container4:172.18.0.5" tty: true stdin_open: true container3: image: img container_name: container3 networks: my-net: ipv4_address: 172.18.0.4 ports: - "5022:22" extra_hosts: - "container1:172.18.0.2" - "container2:172.18.0.3" - "container4:172.18.0.5" tty: true stdin_open: true container4: image: img container_name: container4 networks: my-net: ipv4_address: 172.18.0.5 ports: - "6022:22" extra_hosts: - "container1:172.18.0.2" - "container2:172.18.0.3" - "container3:172.18.0.4" tty: true stdin_open: true networks: my-net: ipam: config: - subnet: 172.18.0.0/16
启动集群只需执行docker-compose up -d,attach到container1执行docker attach container1。
5. 容器启动时自动测试SSH连通性
在Dockerfile中添加启动脚本,自动SSH到所有其他容器一次,完成密钥缓存:
# 创建启动脚本 RUN echo '#!/bin/bash' > /startup.sh && \ echo 'service ssh start' >> /startup.sh && \ echo 'for host in container2 container3 container4; do ssh root@$host "echo Connected to $host" > /dev/null 2>&1; done' >> /startup.sh && \ chmod +x /startup.sh ENTRYPOINT /startup.sh && bash
优化后的Dockerfile示例
FROM img_base AS img COPY /keys/ /root/.ssh COPY /keys/docker_id_rsa.pub /root/.ssh/authorized_keys RUN sed -i 's/#PermitRootLogin no/PermitRootLogin yes/g' /etc/ssh/sshd_config RUN sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/g' /etc/ssh/sshd_config # 测试环境关闭主机密钥检查 RUN sed -i "s+StrictHostKeyChecking .*+StrictHostKeyChecking no+" /etc/ssh/sshd_config RUN echo "localhost" >> hostfile RUN echo "root@container2" >> hostfile RUN echo "root@container3" >> hostfile RUN echo "root@container4" >> hostfile # 配置bash自动加载ssh-agent和密钥 RUN echo 'eval `ssh-agent` > /dev/null && ssh-add /root/.ssh/docker_id_rsa > /dev/null' >> /root/.bashrc # 创建启动脚本,启动SSH并预测试连通性 RUN echo '#!/bin/bash' > /startup.sh && \ echo 'service ssh start' >> /startup.sh && \ echo 'for host in container2 container3 container4; do ssh root@$host "echo Connected to $host" > /dev/null 2>&1; done' >> /startup.sh && \ chmod +x /startup.sh EXPOSE 22 ENTRYPOINT /startup.sh && bash
内容的提问来源于stack exchange,提问作者sigirius

