Jenkins流水线Deploy阶段异常:容器运行于Jenkins容器而非目标EC2实例
问题描述
我使用如下Jenkins流水线:
pipeline { agent any environment { UPLOADED_FILES_DIR='/tmp/spring_fileupload_dir' DOCKER_USR=credentials('docker_usr') DOCKER_PASSWD=credentials('docker_passwd') } stages { stage('Test') { steps { sh 'mvn test' } } stage('Package') { steps { sh 'mvn clean package -Dmaven.test.skip=true' } } stage('Build') { steps { sh """ docker build -t azold6/fileupload-pv-pvc:$BUILD_NUMBER . """ } } stage('Push') { steps { sh """ docker login -u="$DOCKER_USR" -p="$DOCKER_PASSWD" docker push azold6/fileupload-pv-pvc:$BUILD_NUMBER """ } } stage('Deploy') { steps { sshagent(credentials : ['key-to-ec2']) { sh "docker run -d --name fileupload --rm azold6/fileupload-pv-pvc:$BUILD_NUMBER" } } } } }
执行到Deploy阶段时,抛出错误:
docker: Error response from daemon: Conflict. The container name "/fileupload" is already in use by container "d7c3ee093de7a06c90e71db1bfc85df849ee218da3ae0cf59aa0bd733d9501e2". You have to remove (or rename) that container to be able to reuse that name.
排查后发现,容器实际运行在Jenkins容器中,而非目标EC2实例,尽管用了SSH Agent插件,Jenkins还是指向了错误的机器。
解决方案
核心原因
sshagent插件仅负责加载SSH密钥,但当前的sh命令仍是在Jenkins代理节点(即Jenkins容器)本地执行,并未通过SSH连接到目标EC2实例。
具体修复方式
1. 修改Deploy阶段,通过SSH远程执行Docker命令
将本地docker run命令改为SSH连接EC2实例后执行的形式,示例代码:
stage('Deploy') { steps { sshagent(credentials : ['key-to-ec2']) { sh """ ssh -o StrictHostKeyChecking=no ec2-user@<你的EC2实例IP或域名> ' # 容错处理:停止并移除已存在的同名容器 docker stop fileupload || true docker rm fileupload || true # 拉取最新镜像并启动容器 docker pull azold6/fileupload-pv-pvc:$BUILD_NUMBER docker run -d --name fileupload --rm azold6/fileupload-pv-pvc:$BUILD_NUMBER ' """ } } }
- 替换
<你的EC2实例IP或域名>为实际的EC2地址 -o StrictHostKeyChecking=no用于避免首次连接时的主机密钥确认提示(生产环境可根据安全需求调整)- 增加
docker stop和docker rm的容错命令,避免容器已存在导致的冲突
2. 确保EC2实例满足前置条件
- EC2实例已安装Docker并启动服务
- Jenkins代理节点(Jenkins容器)能通过指定的SSH密钥连接到EC2实例
- EC2实例的操作用户(示例中为
ec2-user)拥有Docker命令的执行权限(可通过将用户加入docker组实现)
3. 可选:使用SSH Pipeline Steps插件简化操作
若觉得手动编写SSH命令繁琐,可安装SSH Pipeline Steps插件,使用更简洁的语法:
stage('Deploy') { steps { sshagent(credentials : ['key-to-ec2']) { script { def remote = [:] remote.name = 'EC2' remote.host = '<你的EC2实例IP或域名>' remote.user = 'ec2-user' remote.allowAnyHosts = true sshCommand remote: remote, command: ''' docker stop fileupload || true docker rm fileupload || true docker pull azold6/fileupload-pv-pvc:$BUILD_NUMBER docker run -d --name fileupload --rm azold6/fileupload-pv-pvc:$BUILD_NUMBER ''' } } } }
该方式语法更清晰,也更便于维护。
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

