学习目标

通过本篇教程,你将学会:

  • 掌握 Maven 多模块项目的打包配置
  • 学会发布到 Maven 中央仓库和私有仓库
  • 理解 Docker 容器化部署流程
  • 掌握生产环境部署的最佳实践

概念讲解:发布部署架构

发布流程概览

graph TB
    subgraph "开发阶段"
        A1[源码开发] --> A2[单元测试]
        A2 --> A3[集成测试]
        A3 --> A4[代码质量检查]
    end
    
    subgraph "构建阶段"
        B1[Maven 编译] --> B2[注解处理]
        B2 --> B3[资源打包]
        B3 --> B4[JAR/WAR 生成]
    end
    
    subgraph "发布阶段"
        C1[版本标签] --> C2[签名验证]
        C2 --> C3[仓库上传]
        C3 --> C4[文档发布]
    end
    
    subgraph "部署阶段"
        D1[环境准备] --> D2[应用部署]
        D2 --> D3[配置管理]
        D3 --> D4[健康检查]
    end
    
    A4 --> B1
    B4 --> C1
    C4 --> D1
    
    style A1 fill:#e8f5e8
    style B1 fill:#e3f2fd
    style C1 fill:#fff3e0
    style D1 fill:#fce4ec

部署架构模式

flowchart LR
    subgraph "开发环境"
        DEV1[本地开发]
        DEV2[单元测试]
        DEV3[集成测试]
    end
    
    subgraph "测试环境"
        TEST1[功能测试]
        TEST2[性能测试]
        TEST3[兼容性测试]
    end
    
    subgraph "预生产环境"
        STAGE1[预发布验证]
        STAGE2[压力测试]
        STAGE3[回归测试]
    end
    
    subgraph "生产环境"
        PROD1[蓝绿部署]
        PROD2[滚动更新]
        PROD3[监控告警]
    end
    
    DEV1 --> TEST1
    TEST3 --> STAGE1
    STAGE3 --> PROD1
    
    style DEV1 fill:#e8f5e8
    style TEST1 fill:#e3f2fd
    style STAGE1 fill:#fff3e0
    style PROD1 fill:#ffebee

实现步骤:完整的发布部署流程

步骤 1:Maven 打包配置优化

父 POM 发布配置

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>io.github.nemoob</groupId>
    <artifactId>atlas-mapper-parent</artifactId>
    <version>1.0.0</version>
    <packaging>pom</packaging>

    <name>Atlas Mapper Parent</name>
    <description>高性能 Java Bean 映射框架 - 基于编译时代码生成</description>
    <url>https://github.com/nemoob/atlas-mapper</url>

    <!--  项目信息 -->
    <licenses>
        <license>
            <name>MIT License</name>
            <url>https://opensource.org/licenses/MIT</url>
            <distribution>repo</distribution>
        </license>
    </licenses>

    <developers>
        <developer>
            <id>nemoob</id>
            <name>杨杨杨大侠</name>
            <email>[email protected]</email>
            <organization>Atlas Mapper</organization>
            <organizationUrl>https://github.com/nemoob</organizationUrl>
            <roles>
                <role>architect</role>
                <role>developer</role>
            </roles>
            <timezone>+8</timezone>
        </developer>
    </developers>

    <scm>
        <connection>scm:git:git://github.com/nemoob/atlas-mapper.git</connection>
        <developerConnection>scm:git:ssh://github.com:nemoob/atlas-mapper.git</developerConnection>
        <url>https://github.com/nemoob/atlas-mapper/tree/main</url>
    </scm>

    <issueManagement>
        <system>GitHub</system>
        <url>https://github.com/nemoob/atlas-mapper/issues</url>
    </issueManagement>

    <ciManagement>
        <system>GitHub Actions</system>
        <url>https://github.com/nemoob/atlas-mapper/actions</url>
    </ciManagement>

    <!--  属性配置 -->
    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.build.sourceEncoding>
        
        <!-- 依赖版本 -->
        <spring.version>5.2.25.RELEASE</spring.version>
        <spring-boot.version>2.2.13.RELEASE</spring-boot.version>
        <freemarker.version>2.3.32</freemarker.version>
        <junit.version>5.8.2</junit.version>
        
        <!-- 插件版本 -->
        <maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
        <maven-source-plugin.version>3.2.1</maven-source-plugin.version>
        <maven-javadoc-plugin.version>3.4.1</maven-javadoc-plugin.version>
        <maven-gpg-plugin.version>3.0.1</maven-gpg-plugin.version>
        <nexus-staging-maven-plugin.version>1.6.13</nexus-staging-maven-plugin.version>
        <jacoco-maven-plugin.version>0.8.8</jacoco-maven-plugin.version>
        <maven-surefire-plugin.version>3.0.0-M7</maven-surefire-plugin.version>
        <maven-failsafe-plugin.version>3.0.0-M7</maven-failsafe-plugin.version>
        
        <!--  发布配置 -->
        <maven.deploy.skip>false</maven.deploy.skip>
        <gpg.skip>false</gpg.skip>
        <skipTests>false</skipTests>
    </properties>

    <!--  子模块 -->
    <modules>
        <module>atlas-mapper-core</module>
        <module>atlas-mapper-processor</module>
        <module>atlas-mapper-spring-boot-starter</module>
        <module>atlas-mapper-example</module>
    </modules>

    <!--  依赖管理 -->
    <dependencyManagement>
        <dependencies>
            <!-- Spring Framework BOM -->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-framework-bom</artifactId>
                <version>${spring.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            
            <!-- Spring Boot BOM -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            
            <!-- 内部模块 -->
            <dependency>
                <groupId>io.github.nemoob</groupId>
                <artifactId>atlas-mapper-core</artifactId>
                <version>${project.version}</version>
            </dependency>
            <dependency>
                <groupId>io.github.nemoob</groupId>
                <artifactId>atlas-mapper-processor</artifactId>
                <version>${project.version}</version>
            </dependency>
            <dependency>
                <groupId>io.github.nemoob</groupId>
                <artifactId>atlas-mapper-spring-boot-starter</artifactId>
                <version>${project.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <!--  构建配置 -->
    <build>
        <pluginManagement>
            <plugins>
                <!-- 编译插件 -->
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>${maven-compiler-plugin.version}</version>
                    <configuration>
                        <source>${maven.compiler.source}</source>
                        <target>${maven.compiler.target}</target>
                        <encoding>${project.build.sourceEncoding}</encoding>
                        <showWarnings>true</showWarnings>
                        <showDeprecation>true</showDeprecation>
                    </configuration>
                </plugin>

                <!-- 源码插件 -->
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-source-plugin</artifactId>
                    <version>${maven-source-plugin.version}</version>
                    <executions>
                        <execution>
                            <id>attach-sources</id>
                            <goals>
                                <goal>jar-no-fork</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>

                <!-- 文档插件 -->
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-javadoc-plugin</artifactId>
                    <version>${maven-javadoc-plugin.version}</version>
                    <configuration>
                        <encoding>${project.build.sourceEncoding}</encoding>
                        <charset>${project.build.sourceEncoding}</charset>
                        <docencoding>${project.build.sourceEncoding}</docencoding>
                        <doclint>none</doclint>
                        <quiet>true</quiet>
                    </configuration>
                    <executions>
                        <execution>
                            <id>attach-javadocs</id>
                            <goals>
                                <goal>jar</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>

                <!-- 测试插件 -->
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-surefire-plugin</artifactId>
                    <version>${maven-surefire-plugin.version}</version>
                    <configuration>
                        <skipTests>${skipTests}</skipTests>
                        <includes>
                            <include>**/*Test.java</include>
                            <include>**/*Tests.java</include>
                        </includes>
                    </configuration>
                </plugin>

                <!-- 集成测试插件 -->
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-failsafe-plugin</artifactId>
                    <version>${maven-failsafe-plugin.version}</version>
                    <configuration>
                        <skipTests>${skipTests}</skipTests>
                        <includes>
                            <include>**/*IT.java</include>
                            <include>**/*IntegrationTest.java</include>
                        </includes>
                    </configuration>
                    <executions>
                        <execution>
                            <goals>
                                <goal>integration-test</goal>
                                <goal>verify</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>

                <!-- 代码覆盖率插件 -->
                <plugin>
                    <groupId>org.jacoco</groupId>
                    <artifactId>jacoco-maven-plugin</artifactId>
                    <version>${jacoco-maven-plugin.version}</version>
                    <executions>
                        <execution>
                            <goals>
                                <goal>prepare-agent</goal>
                            </goals>
                        </execution>
                        <execution>
                            <id>report</id>
                            <phase>test</phase>
                            <goals>
                                <goal>report</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </pluginManagement>

        <plugins>
            <!-- 默认启用的插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-source-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-javadoc-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.jacoco</groupId>
                <artifactId>jacoco-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

    <!--  发布配置 -->
    <profiles>
        <!-- 发布到中央仓库 -->
        <profile>
            <id>release</id>
            <build>
                <plugins>
                    <!-- GPG 签名插件 -->
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-gpg-plugin</artifactId>
                        <version>${maven-gpg-plugin.version}</version>
                        <executions>
                            <execution>
                                <id>sign-artifacts</id>
                                <phase>verify</phase>
                                <goals>
                                    <goal>sign</goal>
                                </goals>
                                <configuration>
                                    <skip>${gpg.skip}</skip>
                                </configuration>
                            </execution>
                        </executions>
                    </plugin>

                    <!-- Nexus 发布插件 -->
                    <plugin>
                        <groupId>org.sonatype.plugins</groupId>
                        <artifactId>nexus-staging-maven-plugin</artifactId>
                        <version>${nexus-staging-maven-plugin.version}</version>
                        <extensions>true</extensions>
                        <configuration>
                            <serverId>ossrh</serverId>
                            <nexusUrl>https://s01.oss.sonatype.org/</nexusUrl>
                            <autoReleaseAfterClose>true</autoReleaseAfterClose>
                        </configuration>
                    </plugin>
                </plugins>
            </build>
        </profile>

        <!-- 快照版本发布 -->
        <profile>
            <id>snapshot</id>
            <properties>
                <gpg.skip>true</gpg.skip>
            </properties>
        </profile>

        <!-- 跳过测试 -->
        <profile>
            <id>skip-tests</id>
            <properties>
                <skipTests>true</skipTests>
            </properties>
        </profile>
    </profiles>

    <!--  分发管理 -->
    <distributionManagement>
        <snapshotRepository>
            <id>ossrh</id>
            <url>https://s01.oss.sonatype.org/content/repositories/snapshots</url>
        </snapshotRepository>
        <repository>
            <id>ossrh</id>
            <url>https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/</url>
        </repository>
    </distributionManagement>
</project>

发布脚本

#!/bin/bash

#  Atlas Mapper 发布脚本
# 作者: 杨杨杨大侠 (MasterY)
# 版本: 1.0.0

set -e

# 颜色定义
RED='33[0;31m'
GREEN='33[0;32m'
YELLOW='33[1;33m'
BLUE='33[0;34m'
NC='33[0m' # No Color

# 日志函数
log_info() {
    echo -e "${BLUE}[INFO]${NC} $1"
}

log_success() {
    echo -e "${GREEN}[SUCCESS]${NC} $1"
}

log_warning() {
    echo -e "${YELLOW}[WARNING]${NC} $1"
}

log_error() {
    echo -e "${RED}[ERROR]${NC} $1"
}

# 检查必要的工具
check_prerequisites() {
    log_info "检查发布前置条件..."
    
    # 检查 Maven
    if ! command -v mvn &> /dev/null; then
        log_error "Maven 未安装或不在 PATH 中"
        exit 1
    fi
    
    # 检查 Git
    if ! command -v git &> /dev/null; then
        log_error "Git 未安装或不在 PATH 中"
        exit 1
    fi
    
    # 检查 GPG
    if ! command -v gpg &> /dev/null; then
        log_warning "GPG 未安装,将跳过签名"
        export GPG_SKIP=true
    fi
    
    # 检查工作目录是否干净
    if [[ -n $(git status --porcelain) ]]; then
        log_error "工作目录不干净,请先提交或暂存更改"
        git status --short
        exit 1
    fi
    
    log_success "前置条件检查通过"
}

# 获取版本信息
get_version_info() {
    CURRENT_VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)
    log_info "当前版本: $CURRENT_VERSION"
    
    if [[ $CURRENT_VERSION == *"-SNAPSHOT" ]]; then
        RELEASE_VERSION=${CURRENT_VERSION%-SNAPSHOT}
        NEXT_VERSION=$(echo $RELEASE_VERSION | awk -F. '{$NF = $NF + 1;} 1' | sed 's/ /./g')-SNAPSHOT
    else
        RELEASE_VERSION=$CURRENT_VERSION
        NEXT_VERSION=$(echo $RELEASE_VERSION | awk -F. '{$NF = $NF + 1;} 1' | sed 's/ /./g')-SNAPSHOT
    fi
    
    log_info "发布版本: $RELEASE_VERSION"
    log_info "下一个开发版本: $NEXT_VERSION"
}

# 运行测试
run_tests() {
    log_info "运行测试套件..."
    
    # 单元测试
    log_info "执行单元测试..."
    mvn clean test -q
    
    # 集成测试
    log_info "执行集成测试..."
    mvn verify -q
    
    # 代码覆盖率检查
    log_info "生成代码覆盖率报告..."
    mvn jacoco:report -q
    
    # 检查覆盖率阈值
    COVERAGE=$(grep -o 'Total.*[0-9]+%' target/site/jacoco/index.html | grep -o '[0-9]+%' | head -1 | sed 's/%//')
    if [[ $COVERAGE -lt 80 ]]; then
        log_warning "代码覆盖率 ($COVERAGE%) 低于推荐阈值 (80%)"
    else
        log_success "代码覆盖率: $COVERAGE%"
    fi
}

# 构建项目
build_project() {
    log_info "构建项目..."
    
    # 清理并编译
    mvn clean compile -q
    
    # 生成源码和文档
    mvn source:jar javadoc:jar -q
    
    # 打包
    mvn package -DskipTests -q
    
    log_success "项目构建完成"
}

# 更新版本号
update_version() {
    local new_version=$1
    log_info "更新版本号到: $new_version"
    
    mvn versions:set -DnewVersion=$new_version -q
    mvn versions:commit -q
    
    # 更新 README 中的版本号
    if [[ -f README.md ]]; then
        sed -i.bak "s/<version>.*</version>/<version>$new_version</version>/g" README.md
        rm -f README.md.bak
    fi
}

# 创建 Git 标签
create_git_tag() {
    local version=$1
    local tag_name="v$version"
    
    log_info "创建 Git 标签: $tag_name"
    
    git add .
    git commit -m "Release version $version"
    git tag -a $tag_name -m "Release version $version"
    
    log_success "Git 标签创建完成"
}

# 发布到仓库
deploy_to_repository() {
    local profile=$1
    log_info "发布到仓库 (profile: $profile)..."
    
    if [[ $profile == "release" ]]; then
        # 发布正式版本
        mvn clean deploy -P release -DskipTests -q
    else
        # 发布快照版本
        mvn clean deploy -P snapshot -DskipTests -q
    fi
    
    log_success "发布完成"
}

# 推送到远程仓库
push_to_remote() {
    log_info "推送到远程仓库..."
    
    git push origin main
    git push origin --tags
    
    log_success "推送完成"
}

# 生成发布说明
generate_release_notes() {
    local version=$1
    local notes_file="RELEASE_NOTES_$version.md"
    
    log_info "生成发布说明: $notes_file"
    
    cat > $notes_file << EOF
# Atlas Mapper $version 发布说明

##  发布日期
$(date '+%Y-%m-%d')

##  版本亮点
- 高性能编译时代码生成
- 完整的 Spring Boot 集成
- 丰富的映射配置选项
- 详细的调试和监控功能

##  Maven 依赖
```xml
<dependency>
    <groupId>io.github.nemoob</groupId>
    <artifactId>atlas-mapper-spring-boot-starter</artifactId>
    <version>$version</version>
</dependency>
```

##  升级指南
1. 更新 Maven 依赖版本
2. 检查配置文件兼容性
3. 运行测试确保功能正常

##  已知问题
- 无

##  文档链接
- [用户指南](https://github.com/nemoob/atlas-mapper/blob/main/README.md)
- [API 文档](https://nemoob.github.io/atlas-mapper/)
- [示例项目](https://github.com/nemoob/atlas-mapper/tree/main/atlas-mapper-example)

##  致谢
感谢所有贡献者和用户的支持!

---
**完整更新日志**: https://github.com/nemoob/atlas-mapper/compare/v$(git describe --tags --abbrev=0 HEAD^)...v$version
EOF

    log_success "发布说明生成完成: $notes_file"
}

# 清理临时文件
cleanup() {
    log_info "清理临时文件..."
    
    # 清理 Maven 临时文件
    find . -name "*.versionsBackup" -delete
    find . -name "pom.xml.bak" -delete
    
    log_success "清理完成"
}

# 主函数
main() {
    log_info "开始 Atlas Mapper 发布流程..."
    
    # 解析命令行参数
    RELEASE_TYPE=${1:-"snapshot"}
    
    if [[ $RELEASE_TYPE != "release" && $RELEASE_TYPE != "snapshot" ]]; then
        log_error "无效的发布类型: $RELEASE_TYPE (支持: release, snapshot)"
        exit 1
    fi
    
    # 执行发布流程
    check_prerequisites
    get_version_info
    
    if [[ $RELEASE_TYPE == "release" ]]; then
        # 正式版本发布流程
        run_tests
        build_project
        
        # 更新到发布版本
        update_version $RELEASE_VERSION
        create_git_tag $RELEASE_VERSION
        deploy_to_repository "release"
        
        # 更新到下一个开发版本
        update_version $NEXT_VERSION
        git add .
        git commit -m "Prepare for next development iteration: $NEXT_VERSION"
        
        push_to_remote
        generate_release_notes $RELEASE_VERSION
        
        log_success "正式版本 $RELEASE_VERSION 发布完成!"
        log_info "下一个开发版本: $NEXT_VERSION"
        
    else
        # 快照版本发布流程
        run_tests
        build_project
        deploy_to_repository "snapshot"
        
        log_success "快照版本 $CURRENT_VERSION 发布完成!"
    fi
    
    cleanup
    log_success "Atlas Mapper 发布流程完成!"
}

# 错误处理
trap 'log_error "发布过程中发生错误,请检查日志"; cleanup; exit 1' ERR

# 执行主函数
main "$@"

步骤 2:Docker 容器化

Dockerfile

#  Atlas Mapper 示例应用 Dockerfile
# 多阶段构建,优化镜像大小

# 构建阶段
FROM maven:3.8.6-openjdk-8-slim AS builder

# 设置工作目录
WORKDIR /app

# 复制 POM 文件(利用 Docker 缓存)
COPY pom.xml .
COPY atlas-mapper-core/pom.xml atlas-mapper-core/
COPY atlas-mapper-processor/pom.xml atlas-mapper-processor/
COPY atlas-mapper-spring-boot-starter/pom.xml atlas-mapper-spring-boot-starter/
COPY atlas-mapper-example/pom.xml atlas-mapper-example/

# 下载依赖(利用 Docker 缓存)
RUN mvn dependency:go-offline -B

# 复制源码
COPY . .

# 构建应用
RUN mvn clean package -DskipTests -B

# 运行阶段
FROM openjdk:8-jre-alpine

# 设置时区
RUN apk add --no-cache tzdata && 
    cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && 
    echo "Asia/Shanghai" > /etc/timezone && 
    apk del tzdata

# 创建应用用户
RUN addgroup -g 1000 atlas && 
    adduser -D -s /bin/sh -u 1000 -G atlas atlas

# 设置工作目录
WORKDIR /app

# 复制应用 JAR
COPY --from=builder /app/atlas-mapper-example/target/atlas-mapper-example-*.jar app.jar

# 创建日志目录
RUN mkdir -p /app/logs && 
    chown -R atlas:atlas /app

# 切换到应用用户
USER atlas

# 健康检查
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 
    CMD wget --no-verbose --tries=1 --spider http://localhost:8080/actuator/health || exit 1

# 暴露端口
EXPOSE 8080

# JVM 参数优化
ENV JAVA_OPTS="-Xms512m -Xmx1024m -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap"

# 启动应用
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar app.jar"]

Docker Compose 配置

# docker-compose.yml
version: '3.8'

services:
  # Atlas Mapper 示例应用
  atlas-mapper-app:
    build:
      context: .
      dockerfile: Dockerfile
    image: atlas-mapper-example:latest
    container_name: atlas-mapper-app
    ports:
      - "8080:8080"
    environment:
      - SPRING_PROFILES_ACTIVE=docker
      - JAVA_OPTS=-Xms512m -Xmx1024m -XX:+UseG1GC
    volumes:
      - ./logs:/app/logs
      - ./config:/app/config
    networks:
      - atlas-network
    depends_on:
      - redis
      - mysql
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/actuator/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 60s

  # Redis 缓存
  redis:
    image: redis:6.2-alpine
    container_name: atlas-redis
    ports:
      - "6379:6379"
    volumes:
      - redis-data:/data
      - ./redis/redis.conf:/usr/local/etc/redis/redis.conf
    command: redis-server /usr/local/etc/redis/redis.conf
    networks:
      - atlas-network
    restart: unless-stopped

  # MySQL 数据库
  mysql:
    image: mysql:8.0
    container_name: atlas-mysql
    ports:
      - "3306:3306"
    environment:
      - MYSQL_ROOT_PASSWORD=atlas123
      - MYSQL_DATABASE=atlas_mapper
      - MYSQL_USER=atlas
      - MYSQL_PASSWORD=atlas123
    volumes:
      - mysql-data:/var/lib/mysql
      - ./mysql/init.sql:/docker-entrypoint-initdb.d/init.sql
    networks:
      - atlas-network
    restart: unless-stopped

  # Nginx 反向代理
  nginx:
    image: nginx:1.21-alpine
    container_name: atlas-nginx
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./nginx/ssl:/etc/nginx/ssl
      - ./nginx/logs:/var/log/nginx
    networks:
      - atlas-network
    depends_on:
      - atlas-mapper-app
    restart: unless-stopped

  # Prometheus 监控
  prometheus:
    image: prom/prometheus:latest
    container_name: atlas-prometheus
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
      - prometheus-data:/prometheus
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'
      - '--web.console.libraries=/etc/prometheus/console_libraries'
      - '--web.console.templates=/etc/prometheus/consoles'
    networks:
      - atlas-network
    restart: unless-stopped

  # Grafana 可视化
  grafana:
    image: grafana/grafana:latest
    container_name: atlas-grafana
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=atlas123
    volumes:
      - grafana-data:/var/lib/grafana
      - ./grafana/dashboards:/etc/grafana/provisioning/dashboards
      - ./grafana/datasources:/etc/grafana/provisioning/datasources
    networks:
      - atlas-network
    depends_on:
      - prometheus
    restart: unless-stopped

networks:
  atlas-network:
    driver: bridge

volumes:
  redis-data:
  mysql-data:
  prometheus-data:
  grafana-data:

构建和部署脚本

#!/bin/bash

#  Docker 构建和部署脚本

set -e

# 颜色定义
RED='33[0;31m'
GREEN='33[0;32m'
YELLOW='33[1;33m'
BLUE='33[0;34m'
NC='33[0m'

log_info() {
    echo -e "${BLUE}[INFO]${NC} $1"
}

log_success() {
    echo -e "${GREEN}[SUCCESS]${NC} $1"
}

log_warning() {
    echo -e "${YELLOW}[WARNING]${NC} $1"
}

log_error() {
    echo -e "${RED}[ERROR]${NC} $1"
}

# 构建 Docker 镜像
build_image() {
    local version=${1:-"latest"}
    
    log_info "构建 Docker 镜像 (版本: $version)..."
    
    # 构建应用镜像
    docker build -t atlas-mapper-example:$version .
    
    # 标记为 latest
    if [[ $version != "latest" ]]; then
        docker tag atlas-mapper-example:$version atlas-mapper-example:latest
    fi
    
    log_success "Docker 镜像构建完成"
}

# 推送到镜像仓库
push_image() {
    local registry=${1:-"docker.io"}
    local version=${2:-"latest"}
    
    log_info "推送镜像到仓库 ($registry)..."
    
    # 标记镜像
    docker tag atlas-mapper-example:$version $registry/nemoob/atlas-mapper-example:$version
    
    # 推送镜像
    docker push $registry/nemoob/atlas-mapper-example:$version
    
    if [[ $version != "latest" ]]; then
        docker tag atlas-mapper-example:$version $registry/nemoob/atlas-mapper-example:latest
        docker push $registry/nemoob/atlas-mapper-example:latest
    fi
    
    log_success "镜像推送完成"
}

# 启动服务
start_services() {
    log_info "启动 Docker Compose 服务..."
    
    # 创建必要的目录
    mkdir -p logs config mysql redis nginx/ssl nginx/logs prometheus grafana/dashboards grafana/datasources
    
    # 启动服务
    docker-compose up -d
    
    # 等待服务启动
    log_info "等待服务启动..."
    sleep 30
    
    # 检查服务状态
    check_services_health
    
    log_success "服务启动完成"
}

# 检查服务健康状态
check_services_health() {
    log_info "检查服务健康状态..."
    
    local services=("atlas-mapper-app" "redis" "mysql" "nginx")
    
    for service in "${services[@]}"; do
        if docker-compose ps $service | grep -q "Up"; then
            log_success "$service: 运行正常"
        else
            log_error "$service: 运行异常"
            docker-compose logs $service
        fi
    done
    
    # 检查应用健康检查
    log_info "检查应用健康状态..."
    for i in {1..10}; do
        if curl -f http://localhost:8080/actuator/health > /dev/null 2>&1; then
            log_success "应用健康检查通过"
            break
        else
            log_warning "等待应用启动... ($i/10)"
            sleep 10
        fi
    done
}

# 停止服务
stop_services() {
    log_info "停止 Docker Compose 服务..."
    
    docker-compose down
    
    log_success "服务停止完成"
}

# 清理资源
cleanup() {
    log_info "清理 Docker 资源..."
    
    # 停止并删除容器
    docker-compose down -v
    
    # 删除镜像(可选)
    if [[ $1 == "--remove-images" ]]; then
        docker rmi atlas-mapper-example:latest || true
    fi
    
    # 清理未使用的资源
    docker system prune -f
    
    log_success "资源清理完成"
}

# 查看日志
view_logs() {
    local service=${1:-"atlas-mapper-app"}
    
    log_info "查看 $service 服务日志..."
    
    docker-compose logs -f $service
}

# 执行数据库迁移
migrate_database() {
    log_info "执行数据库迁移..."
    
    # 等待 MySQL 启动
    until docker-compose exec mysql mysqladmin ping -h"localhost" --silent; do
        log_info "等待 MySQL 启动..."
        sleep 2
    done
    
    # 执行迁移脚本
    docker-compose exec mysql mysql -u atlas -patlas123 atlas_mapper < ./mysql/migration.sql
    
    log_success "数据库迁移完成"
}

# 备份数据
backup_data() {
    local backup_dir="./backups/$(date +%Y%m%d_%H%M%S)"
    
    log_info "备份数据到: $backup_dir"
    
    mkdir -p $backup_dir
    
    # 备份 MySQL 数据
    docker-compose exec mysql mysqldump -u atlas -patlas123 atlas_mapper > $backup_dir/mysql_backup.sql
    
    # 备份 Redis 数据
    docker-compose exec redis redis-cli BGSAVE
    docker cp atlas-redis:/data/dump.rdb $backup_dir/redis_backup.rdb
    
    # 备份配置文件
    cp -r config $backup_dir/
    
    log_success "数据备份完成: $backup_dir"
}

# 恢复数据
restore_data() {
    local backup_dir=$1
    
    if [[ -z $backup_dir || ! -d $backup_dir ]]; then
        log_error "请指定有效的备份目录"
        exit 1
    fi
    
    log_info "从备份恢复数据: $backup_dir"
    
    # 恢复 MySQL 数据
    if [[ -f $backup_dir/mysql_backup.sql ]]; then
        docker-compose exec -T mysql mysql -u atlas -patlas123 atlas_mapper < $backup_dir/mysql_backup.sql
        log_success "MySQL 数据恢复完成"
    fi
    
    # 恢复 Redis 数据
    if [[ -f $backup_dir/redis_backup.rdb ]]; then
        docker-compose stop redis
        docker cp $backup_dir/redis_backup.rdb atlas-redis:/data/dump.rdb
        docker-compose start redis
        log_success "Redis 数据恢复完成"
    fi
    
    # 恢复配置文件
    if [[ -d $backup_dir/config ]]; then
        cp -r $backup_dir/config/* config/
        log_success "配置文件恢复完成"
    fi
    
    log_success "数据恢复完成"
}

# 性能测试
performance_test() {
    log_info "执行性能测试..."
    
    # 检查应用是否运行
    if ! curl -f http://localhost:8080/actuator/health > /dev/null 2>&1; then
        log_error "应用未运行,请先启动服务"
        exit 1
    fi
    
    # 使用 Apache Bench 进行压力测试
    if command -v ab &> /dev/null; then
        log_info "使用 Apache Bench 进行压力测试..."
        ab -n 1000 -c 10 http://localhost:8080/api/users/1/dto
    else
        log_warning "Apache Bench 未安装,跳过性能测试"
    fi
    
    # 使用 curl 进行简单测试
    log_info "执行功能测试..."
    
    # 测试健康检查
    curl -f http://localhost:8080/actuator/health
    
    # 测试映射接口
    curl -f http://localhost:8080/api/users/1/dto
    
    log_success "性能测试完成"
}

# 显示帮助信息
show_help() {
    cat << EOF
Atlas Mapper Docker 部署脚本

用法: $0 [命令] [参数]

命令:
  build [version]           构建 Docker 镜像
  push [registry] [version] 推送镜像到仓库
  start                     启动所有服务
  stop                      停止所有服务
  restart                   重启所有服务
  status                    查看服务状态
  logs [service]            查看服务日志
  cleanup [--remove-images] 清理资源
  migrate                   执行数据库迁移
  backup                    备份数据
  restore [backup_dir]      恢复数据
  test                      执行性能测试
  help                      显示帮助信息

示例:
  $0 build 1.0.0           构建版本 1.0.0 的镜像
  $0 push docker.io 1.0.0  推送镜像到 Docker Hub
  $0 start                  启动所有服务
  $0 logs atlas-mapper-app  查看应用日志
  $0 backup                 备份数据
  $0 test                   执行性能测试

EOF
}

# 主函数
main() {
    local command=${1:-"help"}
    
    case $command in
        build)
            build_image $2
            ;;
        push)
            push_image $2 $3
            ;;
        start)
            start_services
            ;;
        stop)
            stop_services
            ;;
        restart)
            stop_services
            start_services
            ;;
        status)
            docker-compose ps
            ;;
        logs)
            view_logs $2
            ;;
        cleanup)
            cleanup $2
            ;;
        migrate)
            migrate_database
            ;;
        backup)
            backup_data
            ;;
        restore)
            restore_data $2
            ;;
        test)
            performance_test
            ;;
        help|*)
            show_help
            ;;
    esac
}

# 执行主函数
main "$@"

步骤 3:Kubernetes 部署

Kubernetes 部署配置

# k8s/namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: atlas-mapper
  labels:
    name: atlas-mapper

---
# k8s/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: atlas-mapper-config
  namespace: atlas-mapper
data:
  application.yml: |
    server:
      port: 8080
    spring:
      profiles:
        active: k8s
      datasource:
        url: jdbc:mysql://mysql-service:3306/atlas_mapper
        username: atlas
        password: atlas123
      redis:
        host: redis-service
        port: 6379
    management:
      endpoints:
        web:
          exposure:
            include: health,info,metrics,prometheus
      endpoint:
        health:
          show-details: always
    atlas:
      mapper:
        debug:
          enabled: false
        optimization:
          cache-max-size: 10000
          enable-performance-monitoring: true

---
# k8s/secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: atlas-mapper-secret
  namespace: atlas-mapper
type: Opaque
data:
  mysql-password: YXRsYXMxMjM=  # atlas123 base64 encoded
  redis-password: ""

---
# k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: atlas-mapper-app
  namespace: atlas-mapper
  labels:
    app: atlas-mapper-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: atlas-mapper-app
  template:
    metadata:
      labels:
        app: atlas-mapper-app
    spec:
      containers:
      - name: atlas-mapper-app
        image: nemoob/atlas-mapper-example:latest
        ports:
        - containerPort: 8080
        env:
        - name: SPRING_PROFILES_ACTIVE
          value: "k8s"
        - name: JAVA_OPTS
          value: "-Xms512m -Xmx1024m -XX:+UseG1GC"
        volumeMounts:
        - name: config-volume
          mountPath: /app/config
        - name: logs-volume
          mountPath: /app/logs
        livenessProbe:
          httpGet:
            path: /actuator/health
            port: 8080
          initialDelaySeconds: 60
          periodSeconds: 30
          timeoutSeconds: 10
          failureThreshold: 3
        readinessProbe:
          httpGet:
            path: /actuator/health
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10
          timeoutSeconds: 5
          failureThreshold: 3
        resources:
          requests:
            memory: "512Mi"
            cpu: "250m"
          limits:
            memory: "1Gi"
            cpu: "500m"
      volumes:
      - name: config-volume
        configMap:
          name: atlas-mapper-config
      - name: logs-volume
        emptyDir: {}

---
# k8s/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: atlas-mapper-service
  namespace: atlas-mapper
  labels:
    app: atlas-mapper-app
spec:
  selector:
    app: atlas-mapper-app
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080
  type: ClusterIP

---
# k8s/ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: atlas-mapper-ingress
  namespace: atlas-mapper
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/rewrite-target: /
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  tls:
  - hosts:
    - atlas-mapper.example.com
    secretName: atlas-mapper-tls
  rules:
  - host: atlas-mapper.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: atlas-mapper-service
            port:
              number: 80

---
# k8s/hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: atlas-mapper-hpa
  namespace: atlas-mapper
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: atlas-mapper-app
  minReplicas: 3
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80

---
# k8s/mysql-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql
  namespace: atlas-mapper
spec:
  replicas: 1
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - name: mysql
        image: mysql:8.0
        env:
        - name: MYSQL_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: atlas-mapper-secret
              key: mysql-password
        - name: MYSQL_DATABASE
          value: atlas_mapper
        - name: MYSQL_USER
          value: atlas
        - name: MYSQL_PASSWORD
          valueFrom:
            secretKeyRef:
              name: atlas-mapper-secret
              key: mysql-password
        ports:
        - containerPort: 3306
        volumeMounts:
        - name: mysql-storage
          mountPath: /var/lib/mysql
      volumes:
      - name: mysql-storage
        persistentVolumeClaim:
          claimName: mysql-pvc

---
# k8s/mysql-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: mysql-service
  namespace: atlas-mapper
spec:
  selector:
    app: mysql
  ports:
  - protocol: TCP
    port: 3306
    targetPort: 3306

---
# k8s/mysql-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-pvc
  namespace: atlas-mapper
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi

---
# k8s/redis-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis
  namespace: atlas-mapper
spec:
  replicas: 1
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
    spec:
      containers:
      - name: redis
        image: redis:6.2-alpine
        ports:
        - containerPort: 6379
        volumeMounts:
        - name: redis-storage
          mountPath: /data
      volumes:
      - name: redis-storage
        persistentVolumeClaim:
          claimName: redis-pvc

---
# k8s/redis-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: redis-service
  namespace: atlas-mapper
spec:
  selector:
    app: redis
  ports:
  - protocol: TCP
    port: 6379
    targetPort: 6379

---
# k8s/redis-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: redis-pvc
  namespace: atlas-mapper
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi

Kubernetes 部署脚本

#!/bin/bash

#  Kubernetes 部署脚本

set -e

# 颜色定义
RED='33[0;31m'
GREEN='33[0;32m'
YELLOW='33[1;33m'
BLUE='33[0;34m'
NC='33[0m'

log_info() {
    echo -e "${BLUE}[INFO]${NC} $1"
}

log_success() {
    echo -e "${GREEN}[SUCCESS]${NC} $1"
}

log_warning() {
    echo -e "${YELLOW}[WARNING]${NC} $1"
}

log_error() {
    echo -e "${RED}[ERROR]${NC} $1"
}

# 检查 kubectl 连接
check_kubectl() {
    log_info "检查 kubectl 连接..."
    
    if ! command -v kubectl &> /dev/null; then
        log_error "kubectl 未安装"
        exit 1
    fi
    
    if ! kubectl cluster-info &> /dev/null; then
        log_error "无法连接到 Kubernetes 集群"
        exit 1
    fi
    
    log_success "kubectl 连接正常"
}

# 部署应用
deploy() {
    log_info "部署 Atlas Mapper 到 Kubernetes..."
    
    # 应用所有配置
    kubectl apply -f k8s/
    
    # 等待部署完成
    log_info "等待部署完成..."
    kubectl wait --for=condition=available --timeout=300s deployment/atlas-mapper-app -n atlas-mapper
    kubectl wait --for=condition=available --timeout=300s deployment/mysql -n atlas-mapper
    kubectl wait --for=condition=available --timeout=300s deployment/redis -n atlas-mapper
    
    log_success "部署完成"
}

# 检查部署状态
check_status() {
    log_info "检查部署状态..."
    
    echo "=== Pods ==="
    kubectl get pods -n atlas-mapper
    
    echo -e "n=== Services ==="
    kubectl get services -n atlas-mapper
    
    echo -e "n=== Ingress ==="
    kubectl get ingress -n atlas-mapper
    
    echo -e "n=== HPA ==="
    kubectl get hpa -n atlas-mapper
}

# 查看日志
view_logs() {
    local pod_name=$1
    
    if [[ -z $pod_name ]]; then
        # 获取第一个应用 Pod
        pod_name=$(kubectl get pods -n atlas-mapper -l app=atlas-mapper-app -o jsonpath='{.items[0].metadata.name}')
    fi
    
    log_info "查看 Pod 日志: $pod_name"
    kubectl logs -f $pod_name -n atlas-mapper
}

# 扩缩容
scale() {
    local replicas=${1:-3}
    
    log_info "扩缩容到 $replicas 个副本..."
    
    kubectl scale deployment atlas-mapper-app --replicas=$replicas -n atlas-mapper
    
    # 等待扩缩容完成
    kubectl wait --for=condition=available --timeout=300s deployment/atlas-mapper-app -n atlas-mapper
    
    log_success "扩缩容完成"
}

# 滚动更新
rolling_update() {
    local image=${1:-"nemoob/atlas-mapper-example:latest"}
    
    log_info "执行滚动更新,镜像: $image"
    
    kubectl set image deployment/atlas-mapper-app atlas-mapper-app=$image -n atlas-mapper
    
    # 等待滚动更新完成
    kubectl rollout status deployment/atlas-mapper-app -n atlas-mapper
    
    log_success "滚动更新完成"
}

# 回滚部署
rollback() {
    local revision=$1
    
    log_info "回滚部署..."
    
    if [[ -n $revision ]]; then
        kubectl rollout undo deployment/atlas-mapper-app --to-revision=$revision -n atlas-mapper
    else
        kubectl rollout undo deployment/atlas-mapper-app -n atlas-mapper
    fi
    
    # 等待回滚完成
    kubectl rollout status deployment/atlas-mapper-app -n atlas-mapper
    
    log_success "回滚完成"
}

# 删除部署
delete() {
    log_warning "删除 Atlas Mapper 部署..."
    
    read -p "确认删除?(y/N): " -n 1 -r
    echo
    
    if [[ $REPLY =~ ^[Yy]$ ]]; then
        kubectl delete -f k8s/
        log_success "删除完成"
    else
        log_info "取消删除"
    fi
}

# 端口转发
port_forward() {
    local local_port=${1:-8080}
    local remote_port=${2:-80}
    
    # 获取服务名
    local service_name="atlas-mapper-service"
    
    log_info "端口转发: localhost:$local_port -> $service_name:$remote_port"
    
    kubectl port-forward service/$service_name $local_port:$remote_port -n atlas-mapper
}

# 执行命令
exec_command() {
    local pod_name=$1
    shift
    local command="$@"
    
    if [[ -z $pod_name ]]; then
        # 获取第一个应用 Pod
        pod_name=$(kubectl get pods -n atlas-mapper -l app=atlas-mapper-app -o jsonpath='{.items[0].metadata.name}')
    fi
    
    log_info "在 Pod $pod_name 中执行命令: $command"
    
    kubectl exec -it $pod_name -n atlas-mapper -- $command
}

# 获取监控指标
get_metrics() {
    log_info "获取监控指标..."
    
    # CPU 和内存使用情况
    kubectl top pods -n atlas-mapper
    
    # HPA 状态
    kubectl get hpa -n atlas-mapper
    
    # 事件
    kubectl get events -n atlas-mapper --sort-by='.lastTimestamp'
}

# 健康检查
health_check() {
    log_info "执行健康检查..."
    
    # 检查 Pod 状态
    local unhealthy_pods=$(kubectl get pods -n atlas-mapper --field-selector=status.phase!=Running -o name)
    
    if [[ -n $unhealthy_pods ]]; then
        log_warning "发现不健康的 Pod:"
        echo $unhealthy_pods
    else
        log_success "所有 Pod 运行正常"
    fi
    
    # 检查服务端点
    kubectl get endpoints -n atlas-mapper
    
    # 检查应用健康状态(通过端口转发)
    log_info "检查应用健康状态..."
    
    # 临时端口转发
    kubectl port-forward service/atlas-mapper-service 18080:80 -n atlas-mapper &
    local pf_pid=$!
    
    sleep 5
    
    if curl -f http://localhost:18080/actuator/health > /dev/null 2>&1; then
        log_success "应用健康检查通过"
    else
        log_error "应用健康检查失败"
    fi
    
    # 停止端口转发
    kill $pf_pid 2>/dev/null || true
}

# 备份配置
backup_config() {
    local backup_dir="./k8s-backups/$(date +%Y%m%d_%H%M%S)"
    
    log_info "备份 Kubernetes 配置到: $backup_dir"
    
    mkdir -p $backup_dir
    
    # 备份所有资源
    kubectl get all -n atlas-mapper -o yaml > $backup_dir/all-resources.yaml
    kubectl get configmap -n atlas-mapper -o yaml > $backup_dir/configmaps.yaml
    kubectl get secret -n atlas-mapper -o yaml > $backup_dir/secrets.yaml
    kubectl get pvc -n atlas-mapper -o yaml > $backup_dir/pvcs.yaml
    
    log_success "配置备份完成: $backup_dir"
}

# 显示帮助信息
show_help() {
    cat << EOF
Atlas Mapper Kubernetes 部署脚本

用法: $0 [命令] [参数]

命令:
  deploy                    部署应用到 Kubernetes
  status                    查看部署状态
  logs [pod_name]           查看 Pod 日志
  scale [replicas]          扩缩容应用
  update [image]            滚动更新应用
  rollback [revision]       回滚部署
  delete                    删除部署
  port-forward [local] [remote] 端口转发
  exec [pod] [command]      在 Pod 中执行命令
  metrics                   获取监控指标
  health                    健康检查
  backup                    备份配置
  help                      显示帮助信息

示例:
  $0 deploy                 部署应用
  $0 scale 5                扩容到 5 个副本
  $0 update nemoob/atlas-mapper-example:v1.1.0  更新镜像
  $0 logs                   查看应用日志
  $0 port-forward 8080 80   端口转发
  $0 health                 健康检查

EOF
}

# 主函数
main() {
    local command=${1:-"help"}
    
    # 检查 kubectl(除了 help 命令)
    if [[ $command != "help" ]]; then
        check_kubectl
    fi
    
    case $command in
        deploy)
            deploy
            ;;
        status)
            check_status
            ;;
        logs)
            view_logs $2
            ;;
        scale)
            scale $2
            ;;
        update)
            rolling_update $2
            ;;
        rollback)
            rollback $2
            ;;
        delete)
            delete
            ;;
        port-forward)
            port_forward $2 $3
            ;;
        exec)
            shift
            exec_command "$@"
            ;;
        metrics)
            get_metrics
            ;;
        health)
            health_check
            ;;
        backup)
            backup_config
            ;;
        help|*)
            show_help
            ;;
    esac
}

# 执行主函数
main "$@"

示例代码:CI/CD 流水线

GitHub Actions 工作流

# .github/workflows/ci-cd.yml
name: Atlas Mapper CI/CD

on:
  push:
    branches: [ main, develop ]
    tags: [ 'v*' ]
  pull_request:
    branches: [ main ]

env:
  REGISTRY: docker.io
  IMAGE_NAME: nemoob/atlas-mapper-example

jobs:
  #  代码质量检查
  code-quality:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout code
      uses: actions/checkout@v3

    - name: Set up JDK 8
      uses: actions/setup-java@v3
      with:
        java-version: '8'
        distribution: 'temurin'

    - name: Cache Maven dependencies
      uses: actions/cache@v3
      with:
        path: ~/.m2
        key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
        restore-keys: ${{ runner.os }}-m2

    - name: Run code quality checks
      run: |
        mvn clean compile
        mvn checkstyle:check
        mvn spotbugs:check

  #  单元测试
  unit-tests:
    runs-on: ubuntu-latest
    needs: code-quality
    steps:
    - name: Checkout code
      uses: actions/checkout@v3

    - name: Set up JDK 8
      uses: actions/setup-java@v3
      with:
        java-version: '8'
        distribution: 'temurin'

    - name: Cache Maven dependencies
      uses: actions/cache@v3
      with:
        path: ~/.m2
        key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
        restore-keys: ${{ runner.os }}-m2

    - name: Run unit tests
      run: mvn clean test

    - name: Generate test report
      uses: dorny/test-reporter@v1
      if: success() || failure()
      with:
        name: Maven Tests
        path: '**/target/surefire-reports/*.xml'
        reporter: java-junit

    - name: Upload coverage to Codecov
      uses: codecov/codecov-action@v3
      with:
        file: ./target/site/jacoco/jacoco.xml

  #  集成测试
  integration-tests:
    runs-on: ubuntu-latest
    needs: unit-tests
    services:
      mysql:
        image: mysql:8.0
        env:
          MYSQL_ROOT_PASSWORD: atlas123
          MYSQL_DATABASE: atlas_mapper_test
          MYSQL_USER: atlas
          MYSQL_PASSWORD: atlas123
        ports:
          - 3306:3306
        options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3

      redis:
        image: redis:6.2-alpine
        ports:
          - 6379:6379
        options: --health-cmd="redis-cli ping" --health-interval=10s --health-timeout=5s --health-retries=3

    steps:
    - name: Checkout code
      uses: actions/checkout@v3

    - name: Set up JDK 8
      uses: actions/setup-java@v3
      with:
        java-version: '8'
        distribution: 'temurin'

    - name: Cache Maven dependencies
      uses: actions/cache@v3
      with:
        path: ~/.m2
        key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
        restore-keys: ${{ runner.os }}-m2

    - name: Run integration tests
      run: mvn clean verify -Dspring.profiles.active=test
      env:
        MYSQL_URL: jdbc:mysql://localhost:3306/atlas_mapper_test
        REDIS_HOST: localhost

  #  构建和发布
  build-and-publish:
    runs-on: ubuntu-latest
    needs: [unit-tests, integration-tests]
    if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/'))
    steps:
    - name: Checkout code
      uses: actions/checkout@v3

    - name: Set up JDK 8
      uses: actions/setup-java@v3
      with:
        java-version: '8'
        distribution: 'temurin'

    - name: Cache Maven dependencies
      uses: actions/cache@v3
      with:
        path: ~/.m2
        key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
        restore-keys: ${{ runner.os }}-m2

    - name: Configure Maven settings
      uses: s4u/[email protected]
      with:
        servers: |
          [{
            "id": "ossrh",
            "username": "${{ secrets.OSSRH_USERNAME }}",
            "password": "${{ secrets.OSSRH_PASSWORD }}"
          }]

    - name: Import GPG key
      if: startsWith(github.ref, 'refs/tags/')
      uses: crazy-max/ghaction-import-gpg@v5
      with:
        gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
        passphrase: ${{ secrets.GPG_PASSPHRASE }}

    - name: Build project
      run: mvn clean package -DskipTests

    - name: Publish snapshot to OSSRH
      if: github.ref == 'refs/heads/main'
      run: mvn deploy -P snapshot -DskipTests

    - name: Publish release to OSSRH
      if: startsWith(github.ref, 'refs/tags/')
      run: mvn deploy -P release -DskipTests

    - name: Set up Docker Buildx
      uses: docker/setup-buildx-action@v2

    - name: Log in to Docker Hub
      uses: docker/login-action@v2
      with:
        registry: ${{ env.REGISTRY }}
        username: ${{ secrets.DOCKER_USERNAME }}
        password: ${{ secrets.DOCKER_PASSWORD }}

    - name: Extract metadata
      id: meta
      uses: docker/metadata-action@v4
      with:
        images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
        tags: |
          type=ref,event=branch
          type=ref,event=pr
          type=semver,pattern={{version}}
          type=semver,pattern={{major}}.{{minor}}

    - name: Build and push Docker image
      uses: docker/build-push-action@v4
      with:
        context: .
        push: true
        tags: ${{ steps.meta.outputs.tags }}
        labels: ${{ steps.meta.outputs.labels }}
        cache-from: type=gha
        cache-to: type=gha,mode=max

  #  部署到测试环境
  deploy-staging:
    runs-on: ubuntu-latest
    needs: build-and-publish
    if: github.ref == 'refs/heads/main'
    environment: staging
    steps:
    - name: Checkout code
      uses: actions/checkout@v3

    - name: Configure kubectl
      uses: azure/k8s-set-context@v3
      with:
        method: kubeconfig
        kubeconfig: ${{ secrets.KUBE_CONFIG_STAGING }}

    - name: Deploy to staging
      run: |
        sed -i 's|nemoob/atlas-mapper-example:latest|nemoob/atlas-mapper-example:main|g' k8s/deployment.yaml
        kubectl apply -f k8s/ -n atlas-mapper-staging
        kubectl rollout status deployment/atlas-mapper-app -n atlas-mapper-staging

    - name: Run smoke tests
      run: |
        kubectl port-forward service/atlas-mapper-service 18080:80 -n atlas-mapper-staging &
        sleep 10
        curl -f http://localhost:18080/actuator/health
        curl -f http://localhost:18080/api/users/1/dto

  #  部署到生产环境
  deploy-production:
    runs-on: ubuntu-latest
    needs: build-and-publish
    if: startsWith(github.ref, 'refs/tags/')
    environment: production
    steps:
    - name: Checkout code
      uses: actions/checkout@v3

    - name: Configure kubectl
      uses: azure/k8s-set-context@v3
      with:
        method: kubeconfig
        kubeconfig: ${{ secrets.KUBE_CONFIG_PRODUCTION }}

    - name: Extract version
      id: version
      run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT

    - name: Deploy to production
      run: |
        sed -i 's|nemoob/atlas-mapper-example:latest|nemoob/atlas-mapper-example:${{ steps.version.outputs.VERSION }}|g' k8s/deployment.yaml
        kubectl apply -f k8s/ -n atlas-mapper-production
        kubectl rollout status deployment/atlas-mapper-app -n atlas-mapper-production

    - name: Run production tests
      run: |
        kubectl port-forward service/atlas-mapper-service 18080:80 -n atlas-mapper-production &
        sleep 10
        curl -f http://localhost:18080/actuator/health

  #  安全扫描
  security-scan:
    runs-on: ubuntu-latest
    needs: build-and-publish
    steps:
    - name: Run Trivy vulnerability scanner
      uses: aquasecurity/trivy-action@master
      with:
        image-ref: '${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:main'
        format: 'sarif'
        output: 'trivy-results.sarif'

    - name: Upload Trivy scan results to GitHub Security tab
      uses: github/codeql-action/upload-sarif@v2
      with:
        sarif_file: 'trivy-results.sarif'

  #  性能测试
  performance-test:
    runs-on: ubuntu-latest
    needs: deploy-staging
    if: github.ref == 'refs/heads/main'
    steps:
    - name: Checkout code
      uses: actions/checkout@v3

    - name: Run performance tests
      run: |
        # 这里可以集成 JMeter、K6 或其他性能测试工具
        echo "Running performance tests..."
        # 示例:使用 curl 进行简单的性能测试
        for i in {1..100}; do
          curl -s http://staging.atlas-mapper.example.com/api/users/1/dto > /dev/null
        done

  #  通知
  notify:
    runs-on: ubuntu-latest
    needs: [deploy-staging, deploy-production]
    if: always()
    steps:
    - name: Notify Slack
      uses: 8398a7/action-slack@v3
      with:
        status: ${{ job.status }}
        channel: '#atlas-mapper'
        webhook_url: ${{ secrets.SLACK_WEBHOOK }}
      env:
        SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}

效果演示:完整的发布部署流程

本地开发到生产部署

# 1. 本地开发和测试
git clone https://github.com/nemoob/atlas-mapper.git
cd atlas-mapper
mvn clean test

# 2. 构建和打包
./scripts/release.sh snapshot

# 3. Docker 构建和测试
./scripts/docker-deploy.sh build 1.0.0
./scripts/docker-deploy.sh start
./scripts/docker-deploy.sh test

# 4. 推送到镜像仓库
./scripts/docker-deploy.sh push docker.io 1.0.0

# 5. 部署到 Kubernetes
./scripts/k8s-deploy.sh deploy
./scripts/k8s-deploy.sh health

# 6. 监控和维护
./scripts/k8s-deploy.sh metrics
./scripts/k8s-deploy.sh logs

发布版本流程

# 1. 准备发布
git checkout main
git pull origin main

# 2. 执行发布
./scripts/release.sh release

# 3. 验证发布
curl -s https://repo1.maven.org/maven2/io/github/nemoob/atlas-mapper-core/1.0.0/

# 4. 部署到生产环境
git tag v1.0.0
git push origin v1.0.0

# GitHub Actions 会自动触发生产部署

监控和维护

# 查看应用状态
kubectl get pods -n atlas-mapper-production

# 查看监控指标
curl http://grafana.atlas-mapper.example.com/d/atlas-mapper

# 查看日志
kubectl logs -f deployment/atlas-mapper-app -n atlas-mapper-production

# 扩容应用
./scripts/k8s-deploy.sh scale 5

# 滚动更新
./scripts/k8s-deploy.sh update nemoob/atlas-mapper-example:1.0.1

常见问题

Q1: Maven 发布到中央仓库失败怎么办?

A: 检查以下配置:

# 1. 检查 GPG 签名
gpg --list-secret-keys --keyid-format LONG

# 2. 检查 OSSRH 账号
curl -u username:password https://s01.oss.sonatype.org/service/local/staging/profiles

# 3. 检查 POM 配置
mvn help:effective-pom | grep -A 10 distributionManagement

# 4. 手动发布测试
mvn clean deploy -P release -DskipTests

Q2: Docker 镜像构建失败怎么办?

A: 常见问题和解决方案:

# 1. 检查 Dockerfile 语法
docker build --no-cache -t test .

# 2. 检查基础镜像
docker pull openjdk:8-jre-alpine

# 3. 检查构建上下文
echo "target/" >> .dockerignore
echo ".git/" >> .dockerignore

# 4. 多阶段构建优化
# 确保构建阶段和运行阶段分离

Q3: Kubernetes 部署失败怎么办?

A: 故障排查步骤:

# 1. 检查 Pod 状态
kubectl describe pod <pod-name> -n atlas-mapper

# 2. 查看事件
kubectl get events -n atlas-mapper --sort-by='.lastTimestamp'

# 3. 检查资源限制
kubectl top pods -n atlas-mapper

# 4. 检查配置
kubectl get configmap atlas-mapper-config -n atlas-mapper -o yaml

# 5. 检查网络
kubectl exec -it <pod-name> -n atlas-mapper -- nslookup mysql-service

Q4: 性能问题如何优化?

A: 性能优化策略:

# 1. JVM 参数调优
JAVA_OPTS="-Xms1g -Xmx2g -XX:+UseG1GC -XX:MaxGCPauseMillis=200"

# 2. 应用层优化
# - 启用缓存
# - 使用连接池
# - 优化数据库查询

# 3. Kubernetes 资源优化
# - 调整 CPU/内存限制
# - 配置 HPA
# - 使用节点亲和性

# 4. 监控和分析
# - 使用 APM 工具
# - 分析 GC 日志
# - 监控业务指标

本章小结

通过本章学习,你应该掌握了:

  1. Maven 发布:多模块项目打包和发布到中央仓库
  2. Docker 容器化:完整的容器化部署方案
  3. Kubernetes 部署:生产级的 K8s 部署配置
  4. CI/CD 流水线:自动化的持续集成和部署

教程系列完成

恭喜你完成了 Atlas Mapper 的全部 10 篇教程!你现在已经掌握了:

核心技能

  • 框架设计思路和架构原理
  • 环境搭建和项目初始化
  • 核心注解和基础映射
  • 高级映射技巧和类型转换
  • 集合映射和嵌套对象处理
  • Spring Boot 集成和自动配置
  • 单元测试和集成测试
  • 性能优化和最佳实践
  • 故障排查和调试技巧
  • 项目打包发布和部署

下一步建议

  1. 实践项目:在实际项目中应用 Atlas Mapper
  2. 社区贡献:参与开源项目,提交 PR 和 Issue
  3. 技术分享:写博客或做技术分享
  4. 持续学习:关注新技术和最佳实践

参考资源

  • Atlas Mapper GitHub
  • Spring Boot 官方文档
  • Maven 官方文档
  • Docker 官方文档
  • Kubernetes 官方文档
本站提供的所有下载资源均来自互联网,仅提供学习交流使用,版权归原作者所有。如需商业使用,请联系原作者获得授权。 如您发现有涉嫌侵权的内容,请联系我们 邮箱:[email protected]