今天使用1panel部署了一下jenkins,对项目进行自动构建和部署,不想每次修改都手动打成jar包部署。

配置认证

github上的项目,需要生成token,在settings中找到Developer Settings,配只好权限,然后生成token。

之后在jenkins的凭据管理中进行配置

配置pipeline

首先先写好dockerfile和jenkinsfile

# 运行阶段(JAR 由 Jenkins 构建好后复制进来)
FROM eclipse-temurin:21-jre-alpine

WORKDIR /app

# 设置时区,安装 curl 用于健康检查
RUN apk add --no-cache tzdata curl \
    && cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
    && echo "Asia/Shanghai" > /etc/timezone

# 复制 JAR 文件
COPY target/tb-survey-*.jar app.jar

# 创建上传目录
RUN mkdir -p /app/uploads

# 暴露端口
EXPOSE 8081

# 启动命令 - 使用 prod 配置
ENTRYPOINT ["java", "-jar", "app.jar", "--spring.profiles.active=prod"]

pipeline {
    agent any

    environment {
        IMAGE_NAME = 'tb-survey'
        CONTAINER_NAME = 'tb-survey'
        APP_PORT = '8081'
    }

    stages {
        stage('检出代码') {
            steps {
                checkout scm
                echo "代码检出完成,构建版本: ${BUILD_NUMBER}"
            }
        }

        stage('Maven 构建') {
            steps {
                sh 'chmod +x mvnw'
                sh './mvnw clean package -DskipTests'
                echo "Maven 构建完成"
            }
        }

        stage('构建 Docker 镜像') {
            steps {
                sh "docker build -t ${IMAGE_NAME}:${BUILD_NUMBER} ."
                sh "docker tag ${IMAGE_NAME}:${BUILD_NUMBER} ${IMAGE_NAME}:latest"
                echo "Docker 镜像构建完成: ${IMAGE_NAME}:${BUILD_NUMBER}"
            }
        }

        stage('部署容器') {
            steps {
                script {
                    // 停止并删除旧容器
                    sh """
                        docker stop ${CONTAINER_NAME} 2>/dev/null || true
                        docker rm ${CONTAINER_NAME} 2>/dev/null || true
                    """

                    // 启动新容器
                    sh """
                        docker run -d \
                            --name ${CONTAINER_NAME} \
                            --restart=always \
                            --network host \
                            -v /opt/tb-survey/uploads:/app/uploads \
                            ${IMAGE_NAME}:${BUILD_NUMBER}
                    """

                    echo "容器已启动"
                }
            }
        }

        stage('健康检查') {
            steps {
                script {
                    echo "等待应用启动..."
                    sleep(time: 30, unit: 'SECONDS')

                    // 先检查容器是否在运行
                    def containerRunning = sh(
                        script: "docker ps --filter name=${CONTAINER_NAME} --format '{{.Status}}'",
                        returnStdout: true
                    ).trim()

                    if (!containerRunning) {
                        echo "容器未在运行,查看日志..."
                        sh "docker logs ${CONTAINER_NAME} --tail 100"
                        error "容器启动失败!"
                    }

                    echo "容器状态: ${containerRunning}"

                    // 使用 docker exec 在容器内部检查,避免 Jenkins 容器网络问题
                    def healthy = false
                    for (int i = 1; i <= 5; i++) {
                        def result = sh(
                            script: "docker exec ${CONTAINER_NAME} curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:${APP_PORT}/api/doc.html 2>/dev/null || echo '000'",
                            returnStdout: true
                        ).trim()

                        if (result == '200') {
                            echo "应用启动成功!"
                            healthy = true
                            break
                        }
                        echo "第 ${i} 次检查,状态码: ${result},等待重试..."
                        sleep(time: 10, unit: 'SECONDS')
                    }

                    if (!healthy) {
                        echo "健康检查失败,查看容器日志..."
                        sh "docker logs ${CONTAINER_NAME} --tail 100"
                        error "健康检查失败!"
                    }
                }
            }
        }
    }

    post {
        always {
            // 清理旧镜像(保留最近3个版本)
            sh """
                docker images ${IMAGE_NAME} --format '{{.ID}} {{.Tag}}' | \
                    grep -v latest | \
                    sort -k2 -rn | \
                    tail -n +4 | \
                    awk '{print \$1}' | \
                    xargs -r docker rmi 2>/dev/null || true
            """
        }
        success {
            echo "部署成功!访问地址: http://服务器IP:${APP_PORT}/api/doc.html"
        }
        failure {
            echo '部署失败,请检查日志!'
        }
    }
}

配置好github repository的地址,和github凭证和jenkinsfile的位置,就可以进行保存构建了。

webhook

可在对应的github仓库中配置webhook,填好jenkins的 url+/github-webhook/,之后推送代码时就能自动构建部署了。