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

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脚本,上传到远程执行

  1. 在项目中创建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
  1. 修改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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 18:24:52