ASP.NET Core微服务之基于Jenkins Pipeline的持续集成实践

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介: 最近在公司实践持续集成,使用到了Jenkins的Pipeline来提高团队基于ASP.NET Core API服务的集成与部署,因此这里总结一下。一、关于持续集成与Jenkins Pipeline1.1 持续集成相关概念  互联网软件的开发和发布,已经形成了一套标准流程,最重要的组成部分就是持续集成(Continuous integration,简称 CI) 。

最近在公司实践持续集成,使用到了Jenkins的Pipeline来提高团队基于ASP.NET Core API服务的集成与部署,因此这里总结一下。

一、关于持续集成与Jenkins Pipeline

1.1 持续集成相关概念

  互联网软件的开发和发布,已经形成了一套标准流程,最重要的组成部分就是持续集成(Continuous integration,简称 CI) 。 

  持续集成指的是,频繁地 (一天多次) 将代码集成到主干

  它的好处主要有两个:

(1)快速发现错误。每完成一点更新,就集成到主干,可以快速发现错误,定位错误也比较容易。
(2)防止分支大幅偏离主干。如果不是经常集成,主干又在不断更新,会导致以后集成的难度变大,甚至难以集成。

  持续集成的目的,就是让产品可以快速迭代,同时还能保持高质量

Martin Fowler 说:“ 持续集成并不能消除 Bug,而是让它们非常容易发现和改正。”  

  与持续集成相关的,还有持续交付和持续部署。

  持续交付指的是:频繁地将软件的新版本,交付给质量团队或者用户,以供评审。如果评审通过,代码就进入生产阶段。它强调的是,不管怎么更新,软件是随时随地可以交付的

  持续部署是持续交付的下一步,指的是代码通过评审以后,自动部署到生产环境。它强调的是代码在任何时刻都是可部署的,可以进入生产阶段

1.2 Jenkins Pipeline

  Jenkins 是一款流行的开源持续集成(CI)与持续部署(CD)工具,广泛用于项目开发,具有自动化构建、测试和部署等功能。有关Jenkins的安装,可以参考我的这一篇文章进行安装。

  相信很多童鞋都已经在使用Jenkins或者计划使用Jenkins来代替传统的人工发布流程了,因此我们创建了很多自由风格(Free Style)的构建任务用于多个Job,而我们经常会听到说流水线任务,那么流水线是什么呢?

  流水线Pipeline是一套运行于Jenkins上的工作流框架,将原本独立运行于单个或者多个节点的任务连接起来,实现单个任务难以完成的复杂流程编排与可视化。下图是一个Jenkins Pipeline的实例效果:

Pipeline :Build => Test => Deploy

  这里涉及到Pipeline中的几个重要概念,需要了解一下:

  • _Stage_: 阶段,一个Pipeline可以划分为若干个Stage,每个Stage代表一组操作。注意,Stage是一个逻辑分组的概念,可以跨多个Node。如上图所示,Build,Test和Deploy就是Stage,代表了三个不同的阶段:编译、测试和部署。
  • _Node_: 节点,一个Node就是一个Jenkins节点,或者是Master,或者是Slave,是执行Step的具体运行期环境。
  • _Step_: 步骤,Step是最基本的操作单元,小到创建一个目录,大到构建一个Docker镜像,由各类Jenkins Plugin提供。

二、准备ASP.NET Core Docker环境

2.1 安装Docker环境

  可以参考我的这一篇《.NET Core微服务之ASP.NET Core on Docker》来安装和配置Docker环境,建议在Linux环境下配置。

2.2 安装SFTP服务

  在Linux下,SSH服务默认会安装,而在Windows Server下,需要单独安装,可以借助FreeSSHD这个免费工具来实现。由于我的物理机都是Windows Server,物理机上的VM是Linux(Docker运行环境),所以需要给物理机配置FreeSSHD,用来实现从CI服务器发布Release到物理服务器中的VM。

  至于如何安装配置FreeSSHD,可以参考这一篇《freeSSHD在windows环境下搭建SFTP服务器》。

三、配置Jenkins Pipeline流水线任务

3.1 总体目标

  (1)持续集成:实现编译+单元测试的自动运行

  这里我要实现的目标是:当有人push代码到git server中(这里我使用的git server是Gogs,需要给Gogs设置一个Webhook,如下图所示,需要注意的是设置的密钥文本要和在Pipeline中填写的一致,否则Jenkins无法正确接收Web钩子),git server会触发一个webhook发送一个post的请求给CI server,CI server会触发Pipeline任务的构建,一路pull代码+编译+单元测试。

  (2)持续发布:实现编译+发布到具体的测试环境

  由于在开发阶段,我不需要每次Push都进行发布,因此我这里设置的是手动在Jenkins中触发发布任务来实现自动化发布。

3.2 全局设置

  首先,肯定是Jenkins的插件安装了。

  (1)Generic WebHook Trigger => 触发WebHook必备

  (2)Gogs Plugin => 因为我使用的Git Server是Gogs搭建的

  (3)MSBuild Plugin => 进行sln、csproj项目文件的编译

  (4)MSTest & xUnit => 进行基于MSTest或基于xUnit的单元测试

  (5)Nuget Plugin => 拉取Nuget包必备

  (6)Pipeline => 实现Pipeline任务必备,建议将Pipeline相关插件都安装上

  (7)Powershell Plugin => 如果你的CI服务器是基于Windows的,那么安装一下Powershell插件来执行命令吧

  (8)Publish Over SSH => 远程发布Release必备

  (9)WallDisplay => 电视投屏构建任务列表必备

  其次,为了提示邮件,也要Email插件(Email Extension)的支持,并进行以下配置:

  (1)第一处:Jenkins Location

  (2)第二处:Email扩展插件全局变量设置

  这里主要是需要设置Subject和Content,就可以在各个Pipeline中使用了。因此,这里贴出我的Default Content内容:

<!DOCTYPE html>  
<html>  
<head>  
<meta charset="UTF-8">  
<title>${ENV, var="JOB_NAME"}-第${BUILD_NUMBER}次构建日志</title>  
</head>  
  
<body leftmargin="8" marginwidth="0" topmargin="8" marginheight="4"  
    offset="0">  
    <table width="95%" cellpadding="0" cellspacing="0"  style="font-size: 11pt; font-family: Microsoft YaHei, Tahoma, Arial, Helvetica">  
        <tr>  
            <td>各位同事,大家好,以下为 ${PROJECT_NAME } 构建任务信息</td>  
        </tr>  
        <tr>  
            <td><br />  
            <b style="font-weight:bold; color:#66cc00">构建信息</b>  
            <hr size="2" width="100%" align="center" /></td>  
        </tr>  
        <tr>  
            <td>  
                <ul>  
                    <li>任务名称 : ${PROJECT_NAME}</li>  
                    <li>构建编号 : 第${BUILD_NUMBER}次构建</li>  
                    <li>触发原因: ${CAUSE}</li>  
                    <li>构建状态: <span style="font-weight:bold; color:#FF0000">${BUILD_STATUS}</span></li>  
                    <li>构建日志: <a href="${BUILD_URL}console">${BUILD_URL}console</a></li>  
                    <li>构建  Url : <a href="${BUILD_URL}">${BUILD_URL}</a></li>  
                    <li>工作目录 : <a href="${PROJECT_URL}ws">${PROJECT_URL}ws</a></li>  
                    <li>项目  Url : <a href="${PROJECT_URL}">${PROJECT_URL}</a></li>  
                </ul>  
            </td>  
        </tr>  
    </table>  
</body>  
</html>

  为了能够发给更多的人,建议勾选以上两个选项。

  这里是Email通知必填的SMTP服务器配置。

  最后,是SSH服务器的声明,指定可以进行SSH发布的服务器有哪些,IP又是多少:

3.3 新增Pipeline脚本

  (1)持续集成Pipeline

  首先,填写Webhook的密钥文本:

  其次,Build Triggers的时机选择“Build when a change is pushed to Gogs”,即有人push代码到仓库就触发。当然,这里需要提前在Gogs设置Webhook。

  其次,编写Pipeline脚本,各个Stage写清楚职责:

  具体的Pipeline脚本在下边:

pipeline{
    agent any
    stages {
        stage('XDP Core Services Checkout') {
            steps{
             checkout([$class: 'GitSCM', branches: [[name: '*/dev-xds']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: '35b9890b-2338-45e2-8a1a-78e9bbe1d3e2', url: 'http://192.168.18.150:3000/EDC.ITC.XDP.Core/EDC.XDP.Core.git']]])
             echo 'Core Services Checkout Done' 
            }
        }
        stage('XDP Core Services Build') {
            steps{
              bat  '''cd "D:\\Jenkins\\workspace\\XDS.Dev.CI.Pipeline\\src\\services\\EDC.XDP.Core\\"
              dotnet build EDC.XDP.Core-All.sln'''
              echo 'Core Services Build Done' 
            }
        }
        stage('Core Delivery Service Unit Test') {
            steps{
                bat  '''cd "D:\\Jenkins\\workspace\\XDS.Dev.CI.Pipeline\\src\\services\\EDC.XDP.Core\\Services\\EDC.XDP.Core.Delivery.UnitTest"
                dotnet test -v n --no-build EDC.XDP.Core.Delivery.UnitTest.csproj'''
                echo 'Core Delivery Service Unit Test Done'  
            }
        }
        stage('XDS Delivery Service Checkout') {
            steps{
             checkout([$class: 'GitSCM', branches: [[name: '*/dev-service']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: '35b9890b-2338-45e2-8a1a-78e9bbe1d3e2', url: 'http://192.168.18.150:3000/EDC.ITC.XDP.XDS/EDC.XDP.XDS.git']]])
             echo 'Core Delivery Service Checkout Done' 
            }
        }
        stage('XDS Delivery Service Build') {
            steps{
               bat  '''cd "D:\\Jenkins\\workspace\\XDS.Dev.CI.Pipeline\\src\\services\\EDC.XDP.XDS"
               dotnet build EDC.XDP.XDS.sln'''
               echo 'XDS Service Build Done' 
            }
        }
        stage('XDS Delivery Service Unit Test') {
            steps{
                bat  '''cd "D:\\Jenkins\\workspace\\XDS.Dev.CI.Pipeline\\src\\services\\EDC.XDP.XDS\\EDC.XDP.XDS.Delivery.UnitTest"
                dotnet test -v n --no-build EDC.XDP.XDS.Delivery.UnitTest.csproj'''
                echo 'XDS Service Unit Test Done'  
            }
        } 
    }
    post{
        failure {
            emailext (
                subject: '${DEFAULT_SUBJECT}',
                body: '${DEFAULT_CONTENT}',
                to: "edisonchou@qq.com,xxxxx@qq.com")
        }
    }
}

  (2)持续发布Pipeline

  持续发布Pipeline与持续集成Pipeline类似,只是在脚本处有所不同:

pipeline{
    agent any
    stages {
        stage('Core Delivery Service Checkout') {
            steps{
             checkout([$class: 'GitSCM', branches: [[name: '*/dev-xds']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: '35b9890b-2338-45e2-8a1a-78e9bbe1d3e2', url: 'http://192.168.18.150:3000/EDC.ITC.XDP.Core/EDC.XDP.Core.git']]])
             echo 'Core Delivery Service Dev Branch Checkout Done' 
            }
        }
        stage('Core Delivery Service Build & Publish') {
            steps{
              bat  '''cd "D:\\Jenkins\\workspace\\XDS.API.Dev.CD.Pipeline\\src\\services\\EDC.XDP.Core"
               dotnet build EDC.XDP.Core-DataServices.sln
               dotnet publish "%WORKSPACE%\\src\\services\\EDC.XDP.Core\\Services\\EDC.XDP.Core.Delivery.API\\EDC.XDP.Core.Delivery.API.csproj" -o "%WORKSPACE%\\EDC.XDP.Core.Delivery.API/publish" --framework netcoreapp2.1
               '''
               echo 'Core Delivery Service Build & Publish Done'
            }
        }
        stage('Core Delivery Service Deploy To 190 Server') {
            steps{
            sshPublisher(publishers: [sshPublisherDesc(configName: 'XDP-DEV-Server', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: '''docker stop xdp_core_deliveryservice; docker rm xdp_core_deliveryservice; docker run --ulimit core=0 --restart=always -v /etc/localtime:/etc/localtime -d -e ASPNETCORE_ENVIRONMENT=dev --privileged=true --name=xdp_core_deliveryservice -p 8010:80 -v /XiLife/publish/EDC.XDP.Core.Delivery.API/:/app -w /app xdp_service_runtime:latest  dotnet EDC.XDP.Core.Delivery.API.dll''', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: 'EDC.XDP.Core.Delivery.API/', remoteDirectorySDF: false, removePrefix: 'EDC.XDP.Core.Delivery.API/publish/', sourceFiles: 'EDC.XDP.Core.Delivery.API/publish/**')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
            echo 'Delivery Service Deploy To 190 Done'    
            }
        }
        stage('Core Delivery Service Deploy To 175 Server') {
            steps{
            sshPublisher(publishers: [sshPublisherDesc(configName: 'XDP-DEV-MT-Server', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: '''docker stop xdp_core_deliveryservice; docker rm xdp_core_deliveryservice; docker run --ulimit core=0 --restart=always -v /etc/localtime:/etc/localtime -d -e ASPNETCORE_ENVIRONMENT=devmt --privileged=true --name=xdp_core_deliveryservice -p 8010:80 -v /XiLife/publish/EDC.XDP.Core.Delivery.API/:/app -w /app xdp_service_runtime:latest  dotnet EDC.XDP.Core.Delivery.API.dll''', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: 'EDC.XDP.Core.Delivery.API/', remoteDirectorySDF: false, removePrefix: 'EDC.XDP.Core.Delivery.API/publish/', sourceFiles: 'EDC.XDP.Core.Delivery.API/publish/**')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
            echo 'Delivery Service Deploy To 175 Done'    
            }
        }
        stage('XDS Delivery Service Checkout') {
            steps{
             checkout([$class: 'GitSCM', branches: [[name: '*/dev-service']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: '35b9890b-2338-45e2-8a1a-78e9bbe1d3e2', url: 'http://192.168.18.150:3000/EDC.ITC.XDP.XDS/EDC.XDP.XDS.git']]])
             echo 'XDS Delivery Service Checkout Done' 
            }
        }
        stage('XDS Delivery Service Build & Publish') {
            steps{
              bat  '''cd "D:\\Jenkins\\workspace\\XDS.API.Dev.CD.Pipeline\\src\\services\\EDC.XDP.XDS"
               dotnet build EDC.XDP.XDS.sln
               dotnet publish "%WORKSPACE%\\src\\services\\EDC.XDP.XDS\\EDC.XDP.XDS.Delivery.API\\EDC.XDP.XDS.Delivery.API.csproj" -o "%WORKSPACE%\\EDC.XDP.XDS.Delivery.API/publish" --framework netcoreapp2.1
               '''
               echo 'XDS Delivery Service Build & Publish Done' 
            }
        }
        stage('XDS Delivery Service Deploy To 190 Server') {
            steps{
            sshPublisher(publishers: [sshPublisherDesc(configName: 'XDP-DEV-Server', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: '''docker stop xdp_xds_delivery_service;docker rm xdp_xds_delivery_service; docker run --ulimit core=0 --restart=always -v /etc/localtime:/etc/localtime -d -e ASPNETCORE_ENVIRONMENT=dev --privileged=true --name=xdp_xds_delivery_service -p 9020:80 -v /XiLife/publish/EDC.XDP.XDS.Delivery.API/:/app -w /app xdp_service_runtime:latest  dotnet EDC.XDP.XDS.Delivery.API.dll''', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: 'EDC.XDP.XDS.Delivery.API/', remoteDirectorySDF: false, removePrefix: 'EDC.XDP.XDS.Delivery.API/publish/', sourceFiles: 'EDC.XDP.XDS.Delivery.API/publish/**')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
            echo 'XDS Delivery Service Deploy to 190 Done'    
            }
        }
        stage('XDS Delivery Service Deploy To 175 Server') {
            steps{
            sshPublisher(publishers: [sshPublisherDesc(configName: 'XDP-DEV-MT-Server', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: '''docker stop xdp_xds_delivery_service;docker rm xdp_xds_delivery_service; docker run --ulimit core=0 --restart=always -v /etc/localtime:/etc/localtime -d -e ASPNETCORE_ENVIRONMENT=devmt --privileged=true --name=xdp_xds_delivery_service -p 9020:80 -v /XiLife/publish/EDC.XDP.XDS.Delivery.API/:/app -w /app xdp_service_runtime:latest  dotnet EDC.XDP.XDS.Delivery.API.dll''', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: 'EDC.XDP.XDS.Delivery.API/', remoteDirectorySDF: false, removePrefix: 'EDC.XDP.XDS.Delivery.API/publish/', sourceFiles: 'EDC.XDP.XDS.Delivery.API/publish/**')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
            echo 'XDS Delivery Service Deploy to 175 Done'    
            }
        }
    }
}

  这里由于我的测试环境分为两个,一个是开发人员联调环境190,另一个是集成测试环境175,统一在一个Pipeline任务中进行发布。

  对于Master分支,我们还可以将Web系统的发布也集成到同一个Pipeline任务中,实现一个一条龙的发布流水线任务,由于各个Web系统的实现技术不一样,这里就不再贴脚本了。

四、效果演示

  (1)持续集成示例

  (2)持续发布示例

  (3)构建失败告警

  (4)构建大屏显示

  再来一张投屏到工作区域电视屏幕中的效果,大家抬头就可以看到构建结果,是绿了还是红了?当然,我们都喜欢“绿”的,呼呼。

五、小结

  借助持续集成和持续发布,我们开发人员可以节省很多质量保证和发布部署的时间,从而减少很多因为人为QA和Deploy造成的失误影响,从另一个层面上,它也可以使我们避免996(好吧,虽然关联有点牵强)。后续,我还会探索K8S,到时候希望能够分享一个ASP.NET Core on K8S的系列文章,敬请期待。

参考资料

大宝鱼,《玩转Jenkins Pipeline

李志强,《Jenkins高级用法 - Pipeline 安装

李志强,《Jenkins高级用法 - Jenkinsfile 介绍及实战经验

三只松鼠,《jenkins + pipeline构建自动化部署

ofnhkb1,《.NET项目从CI到CD-Jenkins_Pipeline的应用

目录
相关文章
|
14天前
|
数据可视化 网络协议 C#
C#/.NET/.NET Core优秀项目和框架2024年3月简报
公众号每月定期推广和分享的C#/.NET/.NET Core优秀项目和框架(每周至少会推荐两个优秀的项目和框架当然节假日除外),公众号推文中有项目和框架的介绍、功能特点、使用方式以及部分功能截图等(打不开或者打开GitHub很慢的同学可以优先查看公众号推文,文末一定会附带项目和框架源码地址)。注意:排名不分先后,都是十分优秀的开源项目和框架,每周定期更新分享(欢迎关注公众号:追逐时光者,第一时间获取每周精选分享资讯🔔)。
|
29天前
|
负载均衡 测试技术 持续交付
高效后端开发实践:构建可扩展的微服务架构
在当今快速发展的互联网时代,后端开发扮演着至关重要的角色。本文将重点探讨如何构建可扩展的微服务架构,以及在后端开发中提高效率的一些实践方法。通过合理的架构设计和技术选型,我们可以更好地应对日益复杂的业务需求,实现高效可靠的后端系统。
|
1月前
|
项目管理 微服务
云效常见问题之将多个微服务应用集成到一次研发流程中发布上线如何解决
云效(CloudEfficiency)是阿里云提供的一套软件研发效能平台,旨在通过工程效能、项目管理、质量保障等工具与服务,帮助企业提高软件研发的效率和质量。本合集是云效使用中可能遇到的一些常见问题及其答案的汇总。
26 0
|
1月前
|
供应链 安全 Linux
简单、透明、安全、高度集成!龙蜥可信 SBOM 能力探索与实践
从攻击面管理的角度解决软件供应链SBOM复杂体系的安全可信问题。
|
13天前
|
Kubernetes 安全 Java
构建高效微服务架构:从理论到实践
【4月更文挑战第9天】 在当今快速迭代与竞争激烈的软件市场中,微服务架构以其灵活性、可扩展性及容错性,成为众多企业转型的首选。本文将深入探讨如何从零开始构建一个高效的微服务系统,覆盖从概念理解、设计原则、技术选型到部署维护的各个阶段。通过实际案例分析与最佳实践分享,旨在为后端工程师提供一套全面的微服务构建指南,帮助读者在面对复杂系统设计时能够做出明智的决策,并提升系统的可靠性与维护效率。
|
29天前
|
消息中间件 敏捷开发 运维
构建高效可靠的微服务架构:策略与实践
随着现代软件开发的复杂性增加,微服务架构逐渐成为企业解决大型应用系统分解、敏捷开发和持续部署问题的有效手段。本文深入探讨了构建一个高效且可靠的微服务架构的关键策略,包括服务的合理划分、通信机制的选择、数据一致性保障以及容错处理。通过分析这些策略在具体案例中的应用,我们旨在为开发者提供一套可行的微服务设计及实施指南。
130 6
|
6天前
|
消息中间件 运维 监控
现代化软件开发中的微服务架构设计与实践
本文将深入探讨现代化软件开发中微服务架构的设计原则和实践经验。通过分析微服务架构的优势、挑战以及常见的设计模式,结合实际案例,帮助开发者更好地理解如何构建可靠、可扩展、高效的微服务系统。
|
6天前
|
负载均衡 Java 开发者
细解微服务架构实践:如何使用Spring Cloud进行Java微服务治理
【4月更文挑战第17天】Spring Cloud是Java微服务治理的首选框架,整合了Eureka(服务发现)、Ribbon(客户端负载均衡)、Hystrix(熔断器)、Zuul(API网关)和Config Server(配置中心)。通过Eureka实现服务注册与发现,Ribbon提供负载均衡,Hystrix实现熔断保护,Zuul作为API网关,Config Server集中管理配置。理解并运用Spring Cloud进行微服务治理是现代Java开发者的关键技能。
|
7天前
|
测试技术 持续交付 Docker
Django中的自动化部署与持续集成实践
【4月更文挑战第15天】本文介绍了Django项目中自动化部署与持续集成的实践方法。自动化部署通过选择Ansible、Fabric或Docker等工具,编写部署脚本,配置持续集成工具(如Jenkins、GitLab CI),确保服务器环境一致,实现快速应用上线。持续集成则涉及配置版本控制系统,设置自动化构建和测试,编写全面的测试用例,集成代码质量检查工具,并配置通知机制,以提升代码质量和开发效率。这两者结合能有效提升项目的迭代速度和可靠性。
|
9天前
|
Kubernetes 监控 Cloud Native
构建高效云原生应用:基于Kubernetes的微服务治理实践
【4月更文挑战第13天】 在当今数字化转型的浪潮中,企业纷纷将目光投向了云原生技术以支持其业务敏捷性和可扩展性。本文深入探讨了利用Kubernetes作为容器编排平台,实现微服务架构的有效治理,旨在为开发者和运维团队提供一套优化策略,以确保云原生应用的高性能和稳定性。通过分析微服务设计原则、Kubernetes的核心组件以及实际案例,本文揭示了在多变的业务需求下,如何确保系统的高可用性、弹性和安全性。
13 4