主要配置
需要创建凭证连接ssh时和仓库时

git
ssh

serverName等配置

groovy
pipeline {
agent any
agent
environment {
SERVICE_NAME = "${serverName}"
BUILD_DIR = "dist"
CODE_DIR = "${frontPath}"
REMOTE_HOST = "172.18.0.11"
REMOTE_USER = "root"
HOST_JENKINS_WS = "/opt/jenkins/data"
JENKINS_CONTAINER_ID = "${sh(script: 'hostname', returnStdout: true).trim()}"
NODE_IMAGE = ""
SSH_CREDENTIALS_ID = "${REMOTE_HOST}"
GITTOKEN = credentials('4ae8e248-66b2-4270-8d0f-3a7649c01e41')
}
stages {
stage('检查环境') {
steps {
script {
def NODEVER = "${params.requiredNodeVersion}"
echo "=== 环境信息 ==="
echo "需要的Node版本: ${NODEVER}"
echo "Jenkins容器ID: ${env.JENKINS_CONTAINER_ID}"
echo "宿主机JENKINS_HOME路径: ${env.HOST_JENKINS_WS}"
}
}
}
stage('检查 Docker 权限') {
steps {
script {
sh '''
set +e
echo "=== Docker 环境检查 ==="
echo "当前用户: $(whoami)"
echo "用户信息: $(id)"
echo "docker.sock 信息:"
ls -l /var/run/docker.sock || echo "未找到 /var/run/docker.sock"
echo "docker version 检查:"
docker version >/tmp/docker_check.log 2>&1
STATUS=$?
cat /tmp/docker_check.log
if [ $STATUS -ne 0 ]; then
echo ""
echo "❌ Docker 不可用。"
echo "最可能原因:"
echo "1. Jenkins 容器未挂载 /var/run/docker.sock"
echo "2. 当前用户无权访问 /var/run/docker.sock"
echo "3. 容器内 docker CLI 存在,但无权限连接宿主机 Docker Daemon"
exit 1
fi
echo "✅ Docker 可用"
'''
}
}
}
stage('拉取代码') {
steps {
cleanWs()
withCredentials([string(credentialsId: '4ae8e248-66b2-4270-8d0f-3a7649c01e41', variable: 'GIT_TOKEN')]) {
sh '''
set -e
auth_url=$(echo "${gitlab}" | sed "s#http://#http://oauth2:${GIT_TOKEN}@#")
git clone -b ${branch} --depth=1 "${auth_url}" .
echo "=== 检查克隆后的文件列表 ==="
ls -lah
echo "=== 确认 package.json 是否存在 ==="
if [ -f "package.json" ]; then
echo "✅ package.json 存在"
echo "Jenkins 当前工作目录: $(pwd)"
else
echo "❌ ERROR: 当前工作区缺少 package.json"
exit 1
fi
'''
}
}
}
stage('拉取镜像') {
steps {
script {
def nodeImage = "node:${params.requiredNodeVersion}"
def imageExists = sh(
script: """
set +e
docker image inspect ${nodeImage} >/dev/null 2>&1
if [ \$? -eq 0 ]; then
echo "exists"
else
echo "missing"
fi
""",
returnStdout: true
).trim()
if (imageExists == 'missing') {
sh """
set -e
echo "=== 拉取 Node 镜像: ${nodeImage} ==="
docker pull ${nodeImage}
"""
} else {
echo "✅ 镜像已存在: ${nodeImage}"
}
env.NODE_IMAGE = nodeImage
}
}
}
stage('验证文件位置') {
steps {
script {
sh '''
set -e
echo "=== 文件位置验证 ==="
WORKSPACE=$(pwd)
echo "当前工作目录: $WORKSPACE"
if [ -f "package.json" ]; then
echo "✅ 当前目录 package.json 存在"
ls -la package.json
echo "文件 inode 信息:"
stat package.json
else
echo "❌ 当前目录没有 package.json"
exit 1
fi
'''
}
}
}
stage('构建项目') {
steps {
script {
def containerWs = pwd()
def hostWs = "${env.HOST_JENKINS_WS}${containerWs.replace('/var/jenkins_home', '')}"
def nodeImage = "node:${params.requiredNodeVersion}"
def BUILD_ENV = params.buildEnv ?: ""
echo "=== 构建路径确认 ==="
echo "Jenkins 工作区路径: ${containerWs}"
echo "宿主机实际路径: ${hostWs}"
echo "Node镜像: ${nodeImage}"
sh """
set -e
docker exec ${env.JENKINS_CONTAINER_ID} test -f "${containerWs}/package.json" || (echo "文件丢失!" && exit 1)
CURRENT_UID=\$(id -u)
CURRENT_GID=\$(id -g)
docker run --rm \\
--user root:root \\
-v "${hostWs}":/app \\
-w /app \\
${nodeImage} \\
bash -lc '
set -e
ls -lah /app
test -f /app/package.json
node --version
npm --version
npm install -g npm@11.13.0
pnpm --version || npm install -g pnpm
echo "=== 初始化 Git 子模块 ${GITTOKEN} ==="
if [ -f ".gitmodules" ]; then
git config --global url."http://oauth2:${GITTOKEN}@".insteadOf "http://"
git config --global --add safe.directory /app
main_branch="${branch}"
if [[ "\$main_branch" = "dev" || "\$main_branch" = "master" ]]; then
submodule_branch="master"
else
submodule_branch="\$main_branch"
fi
echo "主分支: \$main_branch, 子模块分支: \$submodule_branch"
git submodule init
git submodule update --recursive
git submodule foreach "git checkout \$submodule_branch || git checkout -b \$submodule_branch origin/\$submodule_branch"
git submodule foreach "git pull origin \$submodule_branch || true"
echo "✅ Git 子模块初始化完成"
else
echo "⚠️ 未检测到 .gitmodules 文件"
fi
pnpm install --registry https://registry.npmmirror.com
pnpm update:tsconfig
pnpm build
test -d dist && echo "✅ 构建成功" || (echo "❌ 构建失败" && exit 1)
ls -lah dist/
'
"""
}
}
}
stage('上传到远程服务器') {
when {
expression { return env.REMOTE_HOST?.trim() }
}
steps {
script {
sshagent([env.SSH_CREDENTIALS_ID]) {
sh '''
set -e
echo "=== 创建远程目录 ==="
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${REMOTE_USER}@${REMOTE_HOST} "mkdir -p ${CODE_DIR}"
echo "=== 上传文件 ==="
scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -r ${BUILD_DIR}/* ${REMOTE_USER}@${REMOTE_HOST}:${CODE_DIR}/
echo "=== 设置权限 ==="
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${REMOTE_USER}@${REMOTE_HOST} "chmod -R 755 ${CODE_DIR}"
echo "✅ 上传完成"
'''
}
}
}
}
stage('重启服务') {
steps {
sshagent([env.SSH_CREDENTIALS_ID]) {
sh '''
set -e
echo "=== 重启nginx ==="
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${REMOTE_USER}@${REMOTE_HOST} "${nginxPath} reload"
echo "✅ 重启完成"
'''
}
}
}
stage('健康检查') {
when {
expression { return env.REMOTE_HOST?.trim() }
}
steps {
script {
timeout(time: 3, unit: 'MINUTES') {
waitUntil(initialRecurrencePeriod: 5000) {
def status = sh(
script: "curl -s -o /dev/null -w '%{http_code}' --connect-timeout 5 http://${REMOTE_HOST}/${SERVICE_NAME}/",
returnStdout: true
).trim()
echo "Health check status code: ${status}"
return status in ["200", "301", "302"]
}
echo "✅ Health check passed"
}
}
}
}
}
post {
always {
echo "=== Clean workspace ==="
cleanWs()
}
success {
echo "✅ Pipeline executed successfully!"
}
failure {
echo "❌ Pipeline execution failed, please check logs!"
}
}
}
groovy
pipeline {
agent { label "172.18.0.11" }
environment {
SERVICE_NAME = "${serverName}"
BUILD_DIR = "./"
CODE_DIR = "${frontPath}"
REMOTE_HOST = "172.18.0.11"
REMOTE_USER = "root"
HOST_JENKINS_WS = "/opt/jenkins/data"
JENKINS_CONTAINER_ID = "${sh(script: 'hostname', returnStdout: true).trim()}"
NODE_IMAGE = ""
SSH_CREDENTIALS_ID = "${REMOTE_HOST}"
}
stages {
stage('检查环境') {
steps {
script {
def NODEVER = "${params.requiredNodeVersion}"
echo "=== 环境信息 ==="
echo "需要的Node版本: ${NODEVER}"
echo "Jenkins容器ID: ${env.JENKINS_CONTAINER_ID}"
echo "宿主机JENKINS_HOME路径: ${env.HOST_JENKINS_WS}"
}
}
}
stage('检查 Docker 权限') {
steps {
script {
sh '''
set +e
echo "=== Docker 环境检查 ==="
echo "当前用户: $(whoami)"
echo "用户信息: $(id)"
echo "docker.sock 信息:"
ls -l /var/run/docker.sock || echo "未找到 /var/run/docker.sock"
echo "docker version 检查:"
docker version >/tmp/docker_check.log 2>&1
STATUS=$?
cat /tmp/docker_check.log
if [ $STATUS -ne 0 ]; then
echo ""
echo "❌ Docker 不可用。"
echo "最可能原因:"
echo "1. Jenkins 容器未挂载 /var/run/docker.sock"
echo "2. 当前用户无权访问 /var/run/docker.sock"
echo "3. 容器内 docker CLI 存在,但无权限连接宿主机 Docker Daemon"
exit 1
fi
echo "✅ Docker 可用"
'''
}
}
}
stage('拉取代码') {
steps {
cleanWs()
withCredentials([string(credentialsId: '4ae8e248-66b2-4270-8d0f-3a7649c01e41', variable: 'GIT_TOKEN')]) {
sh '''
set -e
auth_url=$(echo "${gitlab}" | sed "s#http://#http://oauth2:${GIT_TOKEN}@#")
git clone -b ${branch} --depth=1 "${auth_url}" .
echo "=== 检查克隆后的文件列表 ==="
ls -lah
'''
}
}
}
stage('拉取镜像') {
steps {
script {
def nodeImage = "node:${params.requiredNodeVersion}"
def imageExists = sh(
script: """
set +e
docker image inspect ${nodeImage} >/dev/null 2>&1
if [ \$? -eq 0 ]; then
echo "exists"
else
echo "missing"
fi
""",
returnStdout: true
).trim()
if (imageExists == 'missing') {
sh """
set -e
echo "=== 拉取 Node 镜像: ${nodeImage} ==="
docker pull ${nodeImage}
"""
} else {
echo "✅ 镜像已存在: ${nodeImage}"
}
env.NODE_IMAGE = nodeImage
}
}
}
stage('验证文件位置') {
steps {
script {
sh '''
set -e
echo "=== 文件位置验证 ==="
WORKSPACE=$(pwd)
echo "当前工作目录: $WORKSPACE"
'''
}
}
}
stage('构建项目') {
steps {
script {
def containerWs = pwd()
def hostWs = containerWs
def nodeImage = "node:${params.requiredNodeVersion}"
def BUILD_ENV = params.buildEnv ?: ""
echo "=== 构建路径确认 ==="
echo "Jenkins 工作区路径: ${containerWs}"
echo "宿主机实际路径: ${hostWs}"
echo "Node镜像: ${nodeImage}"
sh """
set -e
echo "=== 构建前最后验证 ==="
ls -lah "${containerWs}"
if ! docker version >/dev/null 2>&1; then
echo "❌ Docker 不可用"
exit 1
fi
echo "✅ Docker 可用"
if [ ! -d "${hostWs}" ]; then
echo "❌ 工作区目录不存在: ${hostWs}"
exit 1
fi
docker run --rm \\
--user root:root \\
-v "${hostWs}":/app \\
-w /app \\
${nodeImage} \\
bash -lc '
set -e
ls -lah /app
test -d ./ && echo "✅ 构建成功" || (echo "❌ 构建失败" && exit 1)
ls -lah ./
'
"""
}
}
}
stage('上传到远程服务器') {
when {
expression { return env.REMOTE_HOST?.trim() }
}
steps {
script {
sshagent([env.SSH_CREDENTIALS_ID]) {
sh '''
set -e
echo "=== 创建远程目录 ==="
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${REMOTE_USER}@${REMOTE_HOST} "mkdir -p ${CODE_DIR}"
echo "=== 上传文件 ==="
scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -r ${BUILD_DIR}/* ${REMOTE_USER}@${REMOTE_HOST}:${CODE_DIR}/
echo "=== 设置权限 ==="
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${REMOTE_USER}@${REMOTE_HOST} "chmod -R 755 ${CODE_DIR}"
echo "✅ 上传完成"
'''
}
}
}
}
stage('重启服务') {
steps {
sshagent([env.SSH_CREDENTIALS_ID]) {
sh '''
set -e
echo "=== 重启nginx ==="
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${REMOTE_USER}@${REMOTE_HOST} "${nginxPath} reload"
echo "✅ 重启完成"
'''
}
}
}
stage('健康检查') {
when {
expression { return env.REMOTE_HOST?.trim() }
}
steps {
script {
timeout(time: 3, unit: 'MINUTES') {
waitUntil(initialRecurrencePeriod: 5000) {
def status = sh(
script: "curl -s -o /dev/null -w '%{http_code}' --connect-timeout 5 http://${REMOTE_HOST}/${SERVICE_NAME}/",
returnStdout: true
).trim()
echo "Health check status code: ${status}"
return status in ["200", "301", "302"]
}
echo "✅ Health check passed"
}
}
}
}
}
post {
always {
echo "=== Clean workspace ==="
cleanWs()
}
success {
echo "✅ Pipeline executed successfully!"
}
failure {
echo "❌ Pipeline execution failed, please check logs!"
}
}
}