pipeline { agent any tools { maven 'maven 3.9.12' // 修正Maven工具名称 jdk 'openjdk21' // 修正JDK工具名称,使用Jenkins中实际配置的名称 } environment { REGISTRY = 'timeline-registry:5000' PROJECT_NAME = 'timeline-server' DOCKER_REGISTRY = 'timeline-registry:5000' } parameters { choice( name: 'DEPLOY_ENV', choices: ['dev', 'staging', 'prod'], description: '选择部署环境' ) string( name: 'BRANCH_NAME', defaultValue: 'main', description: '构建分支' ) } stages { stage('Checkout') { steps { checkout scm script { echo "当前分支: ${params.BRANCH_NAME}" } } } stage('Build') { steps { script { echo '开始构建项目' sh 'mvn clean compile -DskipTests' } } } stage('Test') { steps { script { echo '运行单元测试' sh 'mvn test' } } post { always { publishTestResults testResultsPattern: 'target/surefire-reports/*.xml' } } } stage('Package') { steps { script { echo '打包项目' sh 'mvn package -DskipTests' // 保存构建产物 archiveArtifacts artifacts: 'timeline-gateway-service/target/*.jar, timeline-user-service/target/*.jar, timeline-story-service/target/*.jar, timeline-file-service/target/*.jar', fingerprint: true } } } stage('Build Docker Images') { steps { script { def services = ['gateway', 'user', 'story', 'file'] def imageTags = [:] for (service in services) { def serviceDir = "timeline-${service}-service" def imageName = "${REGISTRY}/timeline-${service}-service:${BUILD_NUMBER}" def latestImageName = "${REGISTRY}/timeline-${service}-service:latest" // 检查Dockerfile是否存在,如果不存在则创建 if (!fileExists("${serviceDir}/Dockerfile")) { writeFile file: "${serviceDir}/Dockerfile", text: getDockerfileContent(serviceDir) } // 构建镜像 sh "docker build -t ${imageName} -t ${latestImageName} ${serviceDir}/." imageTags[service] = imageName } env.IMAGE_TAGS = writeJSON returnText: imageTags } } } stage('Push Images') { steps { script { def imageTags = readJSON text: env.IMAGE_TAGS def services = ['gateway', 'user', 'story', 'file'] for (service in services) { def imageName = imageTags[service] sh "docker push ${imageName}" // 也推送latest标签 def latestImageName = imageName.replace(BUILD_NUMBER, "latest") sh "docker push ${latestImageName}" } } } } stage('Deploy') { steps { script { // 创建或更新docker-compose文件 def composeContent = getComposeFileContent(BUILD_NUMBER) writeFile file: 'docker-compose.yml', text: composeContent // 拉取最新镜像 sh 'docker-compose pull' // 停止旧容器 sh 'docker-compose down || true' // 启动新容器 sh 'docker-compose up -d' echo "所有服务已部署完成" } } } } post { success { script { sh 'echo "构建和部署成功完成"' // 发送成功通知 emailext ( subject: "构建成功: ${env.JOB_NAME} - ${env.BUILD_NUMBER}", body: "构建成功: ${env.BUILD_URL}", to: "dev-team@example.com" ) } } failure { script { sh 'echo "构建或部署失败"' // 发送失败通知 emailext ( subject: "构建失败: ${env.JOB_NAME} - ${env.BUILD_NUMBER}", body: "构建失败: ${env.BUILD_URL}", to: "dev-team@example.com" ) } } always { cleanWs() } } } // 生成Dockerfile内容的函数 def getDockerfileContent(serviceDir) { return """FROM openjdk:21-jdk-slim VOLUME /tmp COPY target/*.jar app.jar EXPOSE 8080 ENTRYPOINT ["java","-jar","/app.jar"] """ } // 生成docker-compose文件内容的函数 def getComposeFileContent(buildNumber) { return """version: '3.8' services: mysql: image: mysql:8.0 container_name: timeline-mysql ports: - "33306:33306" environment: MYSQL_ROOT_PASSWORD: WoCloud@9ol7uj MYSQL_DATABASE: timeline volumes: - mysql_data:/var/lib/mysql redis: image: redis:7-alpine container_name: timeline-redis ports: - "36379:6379" command: redis-server --requirepass 123456 minio: image: minio/minio:latest container_name: timeline-minio ports: - "9000:9000" - "9001:9001" environment: MINIO_ROOT_USER: 9ttSGjvQxek2uKKlhpqI MINIO_ROOT_PASSWORD: 12CaKew53tu94tgyDLoqAwAq32iDuz3SWW0O1hex command: server /data --console-address ":9001" volumes: - minio_data:/data timeline-story-service: image: timeline-registry:5000/timeline-story-service:${buildNumber} container_name: timeline-story-service ports: - "30001:30001" environment: - server.port=30001 - spring.datasource.url=jdbc:mysql://mysql:3306/timeline?serverTimezone=UTC&allowPublicKeyRetrieval=true - spring.datasource.username=root - spring.datasource.password=WoCloud@9ol7uj - spring.data.redis.host=redis - spring.data.redis.port=6379 - spring.data.redis.password=123456 - file.service.url=http://timeline-file-service:30002/file/ - user.service.url=http://timeline-user-service:30003/user/ depends_on: - mysql - redis timeline-file-service: image: timeline-registry:5000/timeline-file-service:${buildNumber} container_name: timeline-file-service ports: - "30002:30002" environment: - server.port=30002 - spring.datasource.url=jdbc:mysql://mysql:3306/timeline?serverTimezone=UTC&allowPublicKeyRetrieval=true - spring.datasource.username=root - spring.datasource.password=WoCloud@9ol7uj - minio.endpoint=http://minio:9000 - minio.accessKey=9ttSGjvQxek2uKKlhpqI - minio.secretKey=12CaKew53tu94tgyDLoqAwAq32iDuz3SWW0O1hex - minio.bucketName=timeline-test depends_on: - mysql - minio timeline-user-service: image: timeline-registry:5000/timeline-user-service:${buildNumber} container_name: timeline-user-service ports: - "30003:30003" environment: - server.port=30003 - spring.datasource.url=jdbc:mysql://mysql:3306/timeline?serverTimezone=UTC&allowPublicKeyRetrieval=true - spring.datasource.username=root - spring.datasource.password=WoCloud@9ol7uj - spring.data.redis.host=redis - spring.data.redis.port=6379 - spring.data.redis.password=123456 depends_on: - mysql - redis timeline-gateway-service: image: timeline-registry:5000/timeline-gateway-service:${buildNumber} container_name: timeline-gateway-service ports: - "30000:30000" environment: - server.port=30000 - spring.cloud.gateway.routes[0].id=story-service - spring.cloud.gateway.routes[0].uri=http://timeline-story-service:30001 - spring.cloud.gateway.routes[0].predicates[0]=Path=/story/** - spring.cloud.gateway.routes[0].filters[0]=StripPrefix=0 - spring.cloud.gateway.routes[1].id=file-service - spring.cloud.gateway.routes[1].uri=http://timeline-file-service:30002 - spring.cloud.gateway.routes[1].predicates[0]=Path=/file/** - spring.cloud.gateway.routes[1].filters[0]=StripPrefix=0 - spring.cloud.gateway.routes[2].id=user-service - spring.cloud.gateway.routes[2].uri=http://timeline-user-service:30003 - spring.cloud.gateway.routes[2].predicates[0]=Path=/user/** - spring.cloud.gateway.routes[2].filters[0]=StripPrefix=0 - spring.cloud.gateway.routes[3].id=user-service-ws - spring.cloud.gateway.routes[3].uri=http://timeline-user-service:30003 - spring.cloud.gateway.routes[3].predicates[0]=Path=/user/ws/** - spring.cloud.gateway.routes[3].filters[0]=StripPrefix=0 - spring.datasource.url=jdbc:mysql://mysql:3306/timeline?serverTimezone=UTC&allowPublicKeyRetrieval=true - spring.datasource.username=root - spring.datasource.password=WoCloud@9ol7uj depends_on: - timeline-story-service - timeline-file-service - timeline-user-service volumes: mysql_data: minio_data: """ }