当前位置: 首页 > 科技观察

微服务持续部署的实践和指南

时间:2023-03-17 11:30:05 科技观察

我们在讨论微服务架构时,通常会把它与Monolithicarchitecture(单一架构)进行比较。在Monolithic架构中,随着功能的添加,一个简单的应用程序会随着时间的推移变得越来越大。当一个单一的应用程序变成一个整体时,没有人能完全理解它的作用。这时候无论是增加新功能还是修复bug,都是一个非常痛苦和极其耗时的过程。微服务架构逐渐被许多公司(Amazon、eBay、Netflix)采用,以解决单体架构带来的问题。这个想法是将应用程序分解成可以相互组合的小微服务。这些微服务通过轻量级机制进行交互,通常使用基于HTTP协议的服务。每个微服务完成一个独立的业务逻辑,可以是提供给其他服务或客户端的HTTPAPI服务。也可以是数据迁移的ETL服务。每个微服务除了业务上独立,还有自己独立的运行环境,独立的开发部署流程。这种独立性给服务的部署和运行带来了巨大的挑战。因此,持续部署(ContinuousDeployment)是微服务场景下的重要技术实践。本文将介绍持续部署微服务的实践和指南。实践:使用Docker容器化服务使用DockerCompose跑测试指南:构建适合团队的持续部署流水线VersionizeeverythingContainerizeeverything1.使用Docker容器化服务需要为其配置服务器环境。使用Docker容器化的微服务,让我们发布的不仅仅是服务,还有它需要的运行环境。容器化之后,我们就可以构建我们基于Docker的持续部署流水线了:上图描述了一个基于RubyonRails(简称:Rails)服务的持续部署流水线。我们使用Dockerfile来配置Rails项目运行所需的环境,并将Dockerfile和项目放在Git代码仓库中进行版本管理。下面的Dockerfile可以描述一个Rails项目的基本环境:FROMruby:2.3.3RUNapt-getupdate-y&&\apt-getinstall-ylibpq-devnodejsgitWORKDIR/appADDGemfile/app/GemfileADDGemfile.lock/app/Gemfile.lockRUNbundleinstallADD./appEXPOSE80CMD["bin/run"]在持续集成服务器上,会同时下载(gitclone)项目代码和Dockerfile,进行构建(BuildImage)、单元测试(Testing)、最终发布(Publish)。至此,整个构建过程都基于Docker,构建结果是一个DockerImage,最终会发布到DockerRegistry。在部署阶段,部署机只需要配置Docker环境,从DockerRegistry上的PullImage进行部署即可。服务容器化后,我们可以让整个持续部署流水线只依赖Docker,而不需要为不同环境的服务单独配置。2、使用DockerCompose跑测试在整个持续部署流水线中,我们需要在持续集成服务器上部署服务,跑单元测试和集成测试。DockerCompose为我们提供了一个很好的解决方案。DockerCompose可以组合多个Docker镜像。当服务需要访问数据库时,我们可以通过DockerCompose将服务的镜像和数据库的镜像组合起来,然后使用DockerCompose在持续集成服务器上进行部署和运行测试。上图描述了Rails服务与Postgres数据库的组装过程。我们只需要在项目中额外添加一个docker-compose.yml来描述组装过程:db:image:postgres:9.4ports:-"5432"service:build:.command:./bin/runvolumes:-.:/appports:-"3000:3000"dev:extends:file:docker-compose.ymlservice:servicelinks:-dbenvironment:-RAILS_ENV=developmentci:extends:file:docker-compose.ymlservice:servicelinks:-dbenvironment:-RAILS_ENV=测试使用DockerCompose运行单元测试和集成测试:docker-composerun-rmcibundleexecrake3.构建适合团队的持续部署流水线当我们的代码提交到代码仓库后,持续部署流水线应该能够构建,测试,和最后将服务部署到生产环境。为了让持续部署流水线更好地为团队服务,我们通常会对持续部署流水线进行一些调整,以更好地为团队的工作流服务。例如,如下图所示,一个敏捷团队的工作流程:通常团队会有业务分析师(BA)做需求分析,业务分析师将需求转化为用户故事卡(StoryCard)适合工作,开发人员(Dev)会先分析新的用户故事卡,然后与业务分析师和技术主管(TechLead)讨论需求和技术实施计划(Kickoff)。开发者在开发阶段会在分支(Branch)上进行开发,以PullRequest的形式提交代码,并邀请他人进行代码审查(Review)。PullRequest审核通过后,该分支会合并到Master分支,代码会自动部署到测试环境(Test)。在微服务场景下,很难在本地搭建完整的集成环境。通常,测试环境有一个完整的集成环境。部署到测试环境后,测试人员(QA)将在测试环境上进行测试。测试完成后,测试人员会与业务分析师和技术主管进行验收测试(UserAcceptanceTest),确认需求的实现和验收的技术实现方案。接受的用户故事卡将被部署到生产环境(Production)。在上述团队工作流程下,如果持续部署流水线只是对Master分支进行打包、测试和发布,那么在开发阶段(即代码还在分支时)无法得到持续集成的反馈,直到代码合并到主分支。并运行构建以获得反馈,这通常会导致“本地测试成功,但持续集成失败”的场景。因此,团队对仅基于Master分支的持续部署流水线做了一些改进。可以支持构建PullRequest代码:如上图所示:持续部署流水线区分PullRequest和Master。PullRequest只运行单元测试,Master运行完成所有构建并自动将代码部署到测试环境。生产环境部署引入手动操作,验收完成后手动触发生产环境部署。调整后的持续部署流水线可以让团队在开发阶段快速得到持续集成的反馈,更好地掌控生产环境的部署。4.版本化一切都是版本化,即所有与服务开发和部署相关的系统都是版本化的。我们不仅将项目代码纳入版本管理,还对项目相关的服务和基础设施进行版本管理。对于一个服务,我们一般会为其单独配置持续部署流水线,为其配置独立运行的基础设施。此时涉及两个非常重要的技术实践:BuildpipelineascodeInfrastructureascodeBuildpipelineascode。通常我们使用Jenkins或Bamboo来构建和配置持续部署管道。每次创建管道时,我们都需要手动配置它。这些手工操作不易复用,可读性差。管道配置的每次更改都不会保存在历史记录中。这意味着我们无法跟踪配置更改。今年上半年,团队将所有持续部署流水线从Bamboo迁移到BuildKite,后者对构建流水线即代码有很好的支持。下图描述了BuildKite的工作原理:在BuildKite场景中,我们将在每个服务代码库中添加一个pipeline.yml来描述构建步骤。构建服务器(CI服务)将从项目的pipeline.yml中读取配置并生成构建步骤。例如,我们可以使用如下代码来描述流水线:/docker-tag"branches:"master"agents:queue:test-wait-name:"DeployToTest"command:"shared_ci_script/bin/deploy"branches:"master"env:DEPLOYMENT_ENV:testagents:queue:test-block-name:"DeploytoProduction"command:"shared_ci_script/bin/deploy"branches:"master"env:DEPLOYMENT_ENV:productionagents:queue:production在上面的配置中,command中的步骤(即:test,docker-tag,deploy)是具体构建scripts,这些脚本放在一个公共的sharedciscript代码库中,sharedciscript会作为一个git子模块引入到各个服务代码库中。构建流水线即代码改造后,持续部署流水线的任何改动都会在Git中进行跟踪,具有良好的可读性。基础设施即代码。对于一个基于HTTP协议的API服务基础设施,可以是:用于部署和网络配置的机器IP设备硬件监控服务(CPU、Memory等)负载均衡器(LoadBalancer)DNS服务AutoScalingService(自动缩放service)SplunkLogcollectionNewRelicperformancemonitoringPagerDutyalarm这些基础设施可以用代码来描述,AWSCloudformation在这方面提供了很好的支持。我们可以使用AWSCloudformation设计器或遵循AWSCloudformation的语法来配置基础设施。下图是服务基础设施组件图,构建了上述大部分基础设施:在AWSCloudformation中,基础设施描述代码可以是JSON文件,也可以是YAML文件。我们还将这些文件放入项目的代码库中进行版本管理。对基础设施的所有操作都是通过修改AWSCloudformation配置来修改的,所有的修改都应该在Git版本控制中。由于我们使用代码来描述基础设施,而大多数服务都遵循相同的部署流程和基础设施,因此基础设施代码的相似度非常高。DevOps团队为团队创建自己的部署工具,以简化基础架构配置和部署过程。5.容器化一切通常在部署服务时,我们还需要一些辅助服务,我们也将这些服务容器化并使用Docker运行。下图描述了一个服务在AWSEC2Instance上的运行环境:当服务部署到AWSEC2Instance上时,我们需要为该服务配置日志收集服务,为该服务配置Nginx反向代理。根据12因素原则,我们在fluentd的基础上,以日志流的形式处理日志。其中logs-router用于分发日志,splunk-forwarder负责将日志转发到Splunk。一切容器化之后,我们的服务启动只需要依赖Docker环境,相关服务的依赖也可以通过Docker机制来运行。总结微服务给业务和技术的可扩展性带来了极大的便利,同时也带来了组织层面和技术层面的巨大挑战。由于在架构演进过程中会产生很多新的服务,持续部署是技术层面的挑战之一。良好的持续部署实践和指导方针可以让团队从基础设施中分离出来,专注于产生业务价值的功能的实现。