Jenkins SSH远程EC2执行Docker部署脚本变量定义报错求助
问题描述
本地Jenkins Pipeline中无需SSH的Docker部署逻辑可正常运行,但尝试通过SSH在远程EC2实例上执行迁移到外部Groovy文件的相同逻辑时,出现语法错误:
bash: -c: line 1: syntax error near unexpected token `(' bash: -c: line 1: `def containerId = sh(script: "docker ps --quiet --filter name=$CONTAINER_NAME", returnStdout: true).trim()'
错误原因
你当前的写法是通过SSH将Jenkins Pipeline的Groovy代码直接传给远程EC2的bash执行,但远程EC2没有Jenkins的Groovy运行环境,bash无法识别Groovy的语法(比如def变量定义、sh(script:...)这种Jenkins Pipeline专属方法),因此触发语法错误。
解决方案
以下是三种可行的解决方式,按需选择:
方案1:将Groovy逻辑转为远程bash命令执行
把原Groovy中的条件判断逻辑转换成bash脚本,通过SSH直接执行bash命令,无需依赖远程Groovy环境。修改Jenkinsfile的SSH执行部分:
stage('Deploy Docker container on EC2') { steps { script { sshagent(['aws-ssh']) { sh ''' ssh -o StrictHostKeyChecking=no ec2-user@35.157.110.150 << 'EOF' CONTAINER_NAME="''' + CONTAINER_NAME + '''" DOCKERH_REPO="''' + DOCKERH_REPO + '''" IMAGE_TAG="''' + IMAGE_TAG + '''" BUILD_VERSION="''' + BUILD_VERSION + '''" DOCKER_RUN="''' + DOCKER_RUN + '''" containerId=$(docker ps --quiet --filter name=$CONTAINER_NAME) if [ -z "$containerId" ]; then echo "Container $CONTAINER_NAME not found. Skipping stop/remove steps." else echo "Stopping and removing existing container $CONTAINER_NAME ..." docker stop $CONTAINER_NAME docker rm $CONTAINER_NAME docker rmi $DOCKERH_REPO/$IMAGE_TAG:latest || true docker rmi $DOCKERH_REPO/$IMAGE_TAG:$BUILD_VERSION || true fi echo "Starting container $CONTAINER_NAME ..." $DOCKER_RUN docker image prune --force EOF ''' } } } }
- 使用
<< 'EOF'的Here Document传递bash脚本,单引号包裹EOF避免本地变量被提前解析 - 添加
|| true避免删除不存在的镜像时导致命令执行失败 - 将Jenkins Pipeline中的变量传递到远程bash脚本中
方案2:使用Jenkins SSH Steps插件执行远程命令
安装ssh-steps插件后,所有Groovy逻辑在Jenkins本地运行,仅将Docker命令通过SSH发送到远程执行:
stage('Deploy Docker container on EC2') { steps { script { def remote = [:] remote.name = 'EC2' remote.host = '35.157.110.150' remote.user = 'ec2-user' remote.identityFile = credentials('aws-ssh').path remote.allowAnyHosts = true // 远程执行docker ps获取容器ID def containerId = sshCommand remote: remote, command: "docker ps --quiet --filter name=$CONTAINER_NAME", returnStdout: true if (containerId.trim().isEmpty()) { echo "Container $CONTAINER_NAME not found. Skipping stop/remove steps." } else { echo "Stopping and removing existing container $CONTAINER_NAME ..." sshCommand remote: remote, command: "docker stop $CONTAINER_NAME" sshCommand remote: remote, command: "docker rm $CONTAINER_NAME" sshCommand remote: remote, command: "docker rmi $DOCKERH_REPO/$IMAGE_TAG:latest || true" sshCommand remote: remote, command: "docker rmi $DOCKERH_REPO/$IMAGE_TAG:$BUILD_VERSION || true" } echo "Starting container $CONTAINER_NAME ..." sshCommand remote: remote, command: "$DOCKER_RUN" sshCommand remote: remote, command: "docker image prune --force" } } }
- 所有Groovy逻辑在Jenkins本地处理,仅通过SSH发送单个Docker命令到远程
- 利用插件的
sshCommand方法获取远程命令输出,实现条件判断 - 无需在远程服务器上编写或执行Groovy代码
方案3:将部署逻辑写成独立bash脚本,上传到远程执行
- 在项目中创建
deploy.sh脚本:
#!/bin/bash set -e CONTAINER_NAME="$1" DOCKERH_REPO="$2" IMAGE_TAG="$3" BUILD_VERSION="$4" DOCKER_RUN="$5" containerId=$(docker ps --quiet --filter name=$CONTAINER_NAME) if [ -z "$containerId" ]; then echo "Container $CONTAINER_NAME not found. Skipping stop/remove steps." else echo "Stopping and removing existing container $CONTAINER_NAME ..." docker stop $CONTAINER_NAME docker rm $CONTAINER_NAME docker rmi $DOCKERH_REPO/$IMAGE_TAG:latest || true docker rmi $DOCKERH_REPO/$IMAGE_TAG:$BUILD_VERSION || true fi echo "Starting container $CONTAINER_NAME ..." eval "$DOCKER_RUN" docker image prune --force
- 修改Jenkinsfile,将脚本上传到远程并执行:
stage('Deploy Docker container on EC2') { steps { script { sshagent(['aws-ssh']) { // 上传脚本到远程 sh "scp -o StrictHostKeyChecking=no deploy.sh ec2-user@35.157.110.150:/tmp/" // 赋予执行权限并执行 sh ''' ssh -o StrictHostKeyChecking=no ec2-user@35.157.110.150 << 'EOF' chmod +x /tmp/deploy.sh /tmp/deploy.sh ''' + CONTAINER_NAME + ''' ''' + DOCKERH_REPO + ''' ''' + IMAGE_TAG + ''' ''' + BUILD_VERSION + ''' "''' + DOCKER_RUN + '''" EOF ''' } } } }
- 把部署逻辑封装成独立bash脚本,便于维护和调试
- 通过
scp上传脚本到远程,再执行脚本并传递参数
内容的提问来源于stack exchange,提问作者juronja
相关产品推荐
相关产品推荐

