基于自动化发布流程多个可实现高效运维工具的实战应用分享.docx
前言曾几何时,生产部若是一件令运维头痛的事,充满着大量沟通和手动操作,可以说几乎占据着运维人员一半以上的工作时间.在部署前期,架构师要把部署的架构和运维人员交代清楚,然后写成部署文档,运维人员要把这个文档中需要开墙的部分写成开墙需求提交网络管理人员,同时和系统管理人员,交代云上部罟的虚拟机或云下部署的实体机,还需要申请上线的时间窗口。在部若期间,运维人员需要参照部署手册上的内容,一条条的实施部署,一个个的去启动应用,费时耗力,还很容易出错.一旦上线失败,就是责任事故,风睑颇大。由于测试环境和生产环境的相对独立,有时一个微小的环境上的差异,会导致整个发布卡死甚至失败.而现在,自动化发布完全取代了原先的发布,在发布前期,运维人员通过Terraform构建的完全一致的基础架构,用GitOps的流水线使得发布的代码库的唯一性,使用Jenkins使得持续集成持续发布CICD自动化,使用kubenetes使得测试环境和测试环境完全一致,而在CICD中还集成的单元测试、代码质量检测和代码安全检测等多种功能.可以说,现在的生产发布,几乎是一种水到渠成的工程.完全解决的过去发布的痛点.而在经济大环境的影响下,很多企业都需要降本增效,Serverless正在被越来越多的引入到生产环境上来,很多其他平时的任务和作业适合于这种应用,甚至AWS在Database和Redis上相继推出的Serverless版,打破了业界的认知.实际上已经是业界的一个标准,适用于各大公有云,适用性较广,可迁移性较强.在做适当修改后,在aws、tecent云也能部署.1.2.2 Jenkins不用我说几乎每个开发都知道这个工具,我用过其他的CICD工具比如ansible、tekton.argocd等,虽然其他工具也很灵活,但这同样也是缺点所在,太灵活导致配苣太多了,如果要增加功能很多都需要客户化开发.而jenkins有大H的插件可供我们选择,可以实现低代码化。1.2.3 1.ambda每一个公有云目前都有自己的功能函数,AWS中的1.ambda,aliyun中的FC1这些可以实现无服务化.下面,我将详述介绍这三个工具如何实现以及案例.2基础设施即代码2.1 什么是基础设施即代码基础设施即代码是脚本自动执行基础结构和配簿管理的一种方法。通过相应的工具集,您可以对物理环境的详细信息进行抽象,从而使您能够专注于重要的代码.基础设施即代码可以解决许多问题,例如,简化配置管理,确保您的基础设施的预配方式与预配方式相同.它不仅仅是文档,也是任何现代软件开发的版本控制的重要组成部分。2.2 解决的问题图二其次就是官方文档虽然比较完整,但是要花一些时间才能找到如何在代码中设置更好的设置.由于GUi设置的名称和代码中的设者并不总是匹配的,因此可能有必要在某种程度上猜测内容,比如上面GUI图对应的部分代码如图三,从中可以看到,对于图中S3对象存储GUl,并没有办法匹配代码中所有的配冒信息.也许是出于对以上缺点的考虑,出现了terraform这个工具,这个工具有如下特点:A.声明式配置:Terraform使用一种声明式的编程语言来描述和定义所需的基础设施资源,这种声明式配置方式使得用户可以清晰地定义所需的资源和其属性,而不耗要关心底层的具体实现细节.B.版本控制Jerraform支持将基拙设施配置代码存房在版本控制系统中,如Git,这使得用户可以轻松地跟踪和管理基础设施的配覆历史,以及回滚到之前的版本.C.多云和混合云支持Jerraform可以与多个云服务提供商集成,如AWS,Azure.GoogleCloud等,用户可以使用Terraform来管理踣多个云平台的基础设施,实现资源的跨云迁移和复制.D.自动化部署和更新:Terraform通过执行计划来自动化基础设施的部署和更新过程,用户只需要编写一次基础设施配三S代码,然后通过运行Terraform命令来创建、修改或删除资源.E高可用性和可伸缩性:Terraform提供了一些高级功能,如故獐恢复、负载均衡和自动扩展等,以确保基础设施的高可用性和可伸缩性.F.安全性和审计:Terraform支持对基础设施资源的访问控制和权限管理,以及对操作的审计日志记录,这有助于保护基础设施的安全性和合规性.2.4实际案例该案例要构建一个项目,在阿里云上构建三个VPC,分别为computerVPC1storageVPCJaCadeVPC,其中computerVPC中放2S一个ACK的容器集群,storageVPC中放者Rds、Redis、Kafka,faadeVPC中设置对外的A1.B、MSE.SNAT等.三个VPC间用企联网CEN打通.为了高可用,设占了三个可用区,三个可用区结合三个VPC共设置九个VSWitCh。总体架构,如图四图四Terraform要对这个较为复杂的项目进行编码时,我们要设计一个总的项目目录tf-project,其中包含environmenst和modules两个文件夹,在environment中包含dev、prod两个目录。这体现了我们希望设计的目标,那就是modules共用,但环境的terrafOrm代码和各自的目录下,如图五.图五在dev和prod的main.tf中,写出总体的设计代码,如下所示,其中locals并没有给出.module"vpc-computer"source-"./.modulesvpc"avaiIability-zone=local.availability-zonevpc_name=local.vpc_computer_namevpc_cidr_block=local.vpc_computer_cidr_blockvsw_computer_node_cidrs"local.vsw_computer_node_cidrsvsw_computer_terway_cidrs"local.vsw_computer_terway_cidrsmodule"vpc_storage"(source="././modules/vpc"availability-zone=local.availability-zonevpc-ame三local.vpc_storage_namevpc_cidr_block-local.vpc_storage_cidr_blockvsw-cidrs"local.vsw_storage_cidrs)i»odule"vpc_facade"(source=* *./.modulesvc-availability-zone»local.availability-zonevpc-ame-local.vpcfacade-namevpc_cidr_block-local.vpc_facade_cidr_blockvsw_cidrs=local.vsw-facade-cidrsmodule"secgroup*source=ZmodulesZsecgroup-vc-,id-module.vpc_facade.vpc-idmodule"secdbgroup'*(source-modulessecgroup,'vpc-id-module.vpc-storage.vpc-idJmodule-ecs*source=* ././modules/ecs"region=local.regioninstance-name=',bastion-l,vsw-id-module.vpc-facade.vsws-idsecgroup-idmodule.secgroup.secgroup-idJmodule"ack”source-modulesack,k8s-name-prefix=local.ack-nameavailability-zone=local.availability-zoneack_vswitch_id=module.vpc-comuter,vsws-coputer-idack-terway-vswitch-id-module.vpc-co<nputer.vsws-computer-terway-idack-service-cidr-local.service-cidrmodule"rds"source=m././modules/rdsHistace-name=local.rds-instance-nameinstance-type* local.rds-istace-tyeinstance-storage«local.rds-istance-storagesecurity-ipslocal.rds-security-ipsVPJid=module.vpc-storageVPjidvswitch-id=module.vpc-storage,vsws-idsecurity-group-ids-module.secgroup.secdbgroup-idavailability-zone-local.availabilityzonedb-name=local.rds-db-namedb-accout=local.rds-db-accountdb-passwd«local.rds-db-passwtd)module*'redis-source-"./.modulesredis*db-instance-name三local.redis-instance-nameinstance-class=local.redis_instance_classsecurity-ips=local.redis-security-isvswitch-id-module.vpc-storage.vsws-idavailability-zonelocal.availability_zoneaccount-name=local.redis_account_nameaccount-passwd=local.redis-accout-passtd)module,'kafka"(source./.moduleskafka,*name=local.kafkapartition-num=local.kafka-namepartition11umdisk-tye=local.kafka-disk-typedisk-sizelocal.kafkaiomax«local.kafkadisksizeiomaxSPeJtyPelocal.kafka-spec-typevswitch-id=module.vpc-storage.vsws-id1slb-speclocal.mse-slb-specgateway-name«local.mse-specVSWJdvpc-idsource=module"mse”source="./.modulesmse0local.mse_gateway_namemse-specmodule.vpc-computer.vsws_computer_terway_idmodule.vpc_co<nuter.vpc-idmodule"certs-"./.modulescertification-module"alb"source三',././modules/alb-alb-name-local.alb_gateway_nameload_balancer_edition=local.alb-editionaddress-type=local.alb_address_typevsw_id=module.vpc_facade.vsws-idvpc_id三module.vpc_facade.vpc_idacl-entries-local.alb_whitelistacl_name=local.alb_acl_nameserveridip=module.pamse.mse_gateway_slb_ip$0cert-id=module.pacerts.cert_douyin_id,module.patents.cert_apollo_id,module.pacerts.cert_vue_idJiwxiule-cen-source"modulescen"vpc_computer_id=module.vpc-computer.vpc-idvpc_storage_id=module.vpc-storage.vpc_idvpc_facade_id=module.vpc_facade.vpc_idvsws_computer_id«module.vpc-computer.vsws_computer_idvsws_computer_terway_idmodule.vpc_computer.vsws_co«nputer_terway_idvsws_storage_id=module.vpc_storage.vsws_idvsws_facade_id=module.vpc_facade.vsws-id)在modules文件夹中,是具体的每个组件实现的代码,如图六所示.图六可以看到,其中redis模块的main.tf中,包含了redis的配百、备份、账户信息.而在prod中的main.tf就可以对其调用.如图七所示.图七2.5部署风除和应对TerrafOrin的部署支持完整的回退销毁,使用命令teaformdestroy可以锢毁所以使用IerrafOnnaPPIy部署的茶础纲件,所以在生产上线前部署是非常安全的,一旦发现有部署不当,可以销毁、调整代码.重建.但是唯一的缺点就是不支持代码上的忽略.就是说,代码上有的组件必须部署不能忽略.这时,我们可以使用terraformrastate,把不帮要制署的组件从代码清单中去除。以后福要再用terrafoi-mimport导入配跣,3.应用部署即代码基础架构部署完后,接下来就要部署应用了.这也就是大家熟知的DevOps中的持续集成持续部署(CICD).持续集成持续部署可以用很多工具来实现.3.1 什么是持续集成持续部署3.1.1 持续集成:A.集成:就是在一起:代码commit是集成(代码在一起),B.编译是集成(逻辑在一起);C.部署是集成(部署包跟环境在一起),D.测试是集成(功能在一起),E.灰度是集成(系统在一起)不断的做集成和集成结果的修正,就是持续集成;1.1.2 持续交付A,交付:就是将最终的产品发布到线上环境,给用户使用.B.持续交付描述的软件开发,是从原始需求识别到最终产品部署到生产环境这个过程中,需求以小批果形式在团队的各个角色间顺畅流动,能够以较短地周期完成需求的小粒度频繁交付.频繁的交付周期带来了更迅速的对软件的反馈,并且在这个过程中,各个角色密切协作,相比于传统的瀑布式软件团队,更少浪费.1.1.3 持续部署A.就是持续的将需求部署到目标环境上.B持续交付的延伸就是持续部署3.2 持续集成持续部署的工具工具上大致分为2大类,服务器CICD工具和容器CICD工具.3.2.1 服务器ClCD工具就是以服务器上的应用部署为主的部署,主要工具是ansible+git,slatstack+git等.此类工具在很长一段时间内盛极一时。成为很多大公司的不二之选,Ansible还得到很大的发展,出现了AnSibIeT。Wer等图形化应用自动化部署工具,如图.图八3.2.2 容器ClCD工具就是以kubenetes容器应用为主的部署,主要工具就是Tiktok+ArgoCD.Jenkins等工具。此类工具是目前的主流,其中Tiktok+ArgoCD工具涉及到大f的配百和定制化的开发,使用较为不变,而Jenkins可以和groovy语句结合,并使用大量的plugins扩大起功能,所以在国内外使用非常广泛。下面对Jenkins在生产使用中的种种配置,一一介绍.Jenkins可以实现参数化部署(BuildwithParameter),这是最为常见的部罟方式.在参数中,你可以定义环境、是否需要代码质量检测、版本号等诸多设笆,如图九所示.图九Jenkins可以和gitlabxgithub、bitbucket等多种git工具结合,实现真正的GitOps,如图十.图十在生产上Jenkins所有部若都不在Jenkisn上设首,都依赖从git中传来的的Jenkinsfile文件r如图出一.图十一3.3 实际案例使用Jenkins+groovf来实现CICD部署,在工作中,一般我会遇到2种CICD的方案,一是Q和CD融合,完全流程化.这是在标准化非后成熟的团队使用的方案,大致的意思就是,在这个团队中,开发、测试、运堆都实现了标准化,一个工程从研发分支、单元测试、性能测试,不同环境的部署都有详细的定义,流程被不择不扣的执行。二是CI和CD分离,部分流程化,其中加入了人为决定的环节,比如要上线时,什么功能能上线,什么功胡要缓一缓要开发主管来决定,而不是由质员测试部门来决定。三是CI和CD部分融合,不完全流程化,比如在测试环境实现Q和CD融合而在生产环境实现分离.本案例所示是第三种方案.下面是Q的JenkinSfiIe的代码使用UbUntU的agent以下代码包括了CheCkoUt,updatetheimage两个stage91.ibrary('x××Jenkins1.ibrary¢2',"XxxxJenkins1.ibrary')_importjava.text.SimpleDateFor,n>atimportgroovy.json.JsonOutputnode(,ubuntu-,java-ll-docker-2")env.USEReEMAI1.«"xxxx"env.USER"*'××xx"env.TOKEN*,sonar-xx-××x-token,node(,ubuntu-java-ll-docker-2,)env.USER-EMAI1.="xxx)CgXXXX.com”env.USER=MxxxxMev.TOKEN=,sonar-x×-××x-tokeenv.SONAR-HOST-UR1.«,https:/xxxx.tools×××net/-env.PROJECT_NAME»',×xxx.xxxx.java"stage('checkout,)(checkoutSCmcommitld-tools.git.getCommitId()version三BUI1.D-NUMBERdefSdf=newSimpleDateFormat1.yyyyMWddHHMss")env.COMPONENT=env.APP-NAMEenv.CWPONENT_COmiT»co«witIdenv.COMPONENTeVERSION=Versionenv.DateTime«sdf.format(newDate()/setdockerimagenameif(*,develop,*.anybranch->BRANCH_NAME.startsWith(branch)env.SimplifIedBranchName='develop,elseif("release/*,',release",*feature","bugfix/","hotfi×n,function/',.anybranch->BRANCH_NAT4E.startsWith(branch)env.SimplifiedBranchName=BRANCH-NAME.to1.owerCase().split(,/,)elseif("PR-".any(branch->BRANCHeNAME.startsWith(branch)env.SimplifIedBranchName='pull-request,elseif('master,.anybranch->BRANH_NAME.StartsWith(branch)env.SimplifiedBranchName=*master,elseenv.SimplifiedBranchName=''if(env.SimplifiedBranchName!«")env.IMAGE_NAME-env.APP-NAT4E*'÷env.SimplifiedBranchName*'1:*,÷'-,÷env.DateTimeif(*,develop,*.anybranch->BRANCH-NAME.StartsWith(branch)env.DEP1.OYeENV=,development,elseenv.DEP1.OYeENV=''if(env.BRANH_NAME三三,develop,env.BRANCH-NAME.startsWith(function/,)env.BRANCHNAME.startsWith(bugfix/*)env.BRANCH-NAME.startsWith('feature/,)env.BRANCH_NAME.startsWith('release/,)stage(,Uploadtheimage*)printin-OMNjCOMPoNENT_C0MMIT”0MNl/OMPONENTqMMIT“timeout(time:3,unit:,MINUTES,)WithCredentials(string(credentialsld:ARTIFACTORY_CREDENTIA1.,variable:,JFR(X5-KEY,),usernamePass¼*ord(credentialsId:ACReDOCKEReCREDENTIA1.,UsernameVariable:'ACR-DOCKER-USERNAME,passwordvariable:,ACR-DOCKER-PASSWORD*),UsernamePassword(credentialsld:ADI-DOCKER,CREDENTIA1.jusernamevariable:,ADI_DOCKER_USERNAME,passwordvariable:,ADI-DOCKER-PASSWORD,)sh""#!/bin/bashset-xenvsubst$JFROGJ<EY'<.mvn/settings.xml.template>.mvsettings.xmldockerloginSADI->HARBOR,ADDR-u$ADI_DOCKER_USERNAME-pJADI-DOCKER_PASSWORD).mvnwcleanpackage-Dmaven.test.skip=true-s.mvsettings.×ml-Udockerlogin$ACR_HARBOR_ADOR-U$ACR_DOCKER_USERNAME-p$ACR_DOCKER_PASSWORD)dockerbuild-t$ACR_HARBOR_ADDR)/$ACR_NAMESPACE/$IMAGE_NAME-build-argOMNI1.CoMPoMENT=$0WN1.COMPONENT-build-argOMNjeOMPoNEN1.VERSION=$0MNI_COMPoNENT_VERSION-build-arg0mi-C0f<P0NENT-C0miT三J0MNI-C0MPONENT-C0>WIT.dockerpush$ACR-HARBOR_ADDR)/$ACR_NAMESPACE/$IMAGE_NAMEdockerrmi$ACR_HAR80R_AD0R/$ACR_NAMESPACE/$IMAGEeNAME)上传镜像后如果是测试环境,需要去调动CD流水线,执行发布,代码如下:if(env.IMAGE-NAME!='*&&env.DEP1.OY-ENV!=,)Stage(TriggerDeployment,)buildjob:env.DEPlOYMENTePIPe1.INE,parameters:string(name:,IMAGE_NAME,value:env.IMAGE_NAME),string(name:'environment*,value:env.DEP1.OY-ENV),string(name:*APPNAME',value:env.APP-NAME)最后一个stage是sendnotification,对相关人员的通知.if(env.BRANCH_NAME三三'develop'11env.BRANH_NAME.StartsWith('release/')stage('SendNotification*)defbody"BuildCompleted:$env.30BNAMEwithbuildnumber$env.BUI1.D_NUMBER(<$(env.BUI1.D_UR1.)|1.ink)>itsresultwasunclear"if(CurrentBuild.CurrentResult三三"SUCCESS")body三"Job:$env.JOB_NAME)nStatus:SUCCESSnBuildReport:$env.BUI1.D_UR1."elseif(currentBuild.CurrentResult="FAI1.URE")body="Job:$env.JOB-NAMEnStatus:*FAl1.URE*nErrordescription:*Failedwhilebuildingapplication*WWnBuildReport:$env.BUI1.D_UR1.)"elseif(CurrentBuild.CurrentResult="UNSTAB1.E")body="Job:$env.3O6_NAME)nStatus:*UNSTAB1.E*nErrordescription:*Ustablewhilebuildingapplication*WWnBuildReport:$env.BUI1.D_UR1.)"5,unit:,MINUTES,)subject:'Deploymentresult',"$USER_EMAI1.)",replyTo:"$USER_EMAI1.",truetimeout(time:emaile×tbody:body,to:mimeType:'te×thtml',Compress1.og:在CD代码中,该项目使用(customize实现对不同的环境,使用不同的配置进行部雪,同样是如同terraform一样的目录结构,base目录是基本的配音,通常我们这里放的是开发环境的死置而在。Verlay中的四个目录对应用于devstaging、perprod,production四个环境,可以把对不同环境所对应的配置写在里面.如图十二所示.图十二比如,如果在测试环境中,我要加入pod反亲和力,来实现离散的pod部署.如图十三所示.图十三在CD流水线的Jenkinsfile中,主要是对ack(k8s)cluster的部署,以下2个stage就是对k8s的部署和检直部署是否成功stage(',deploydeploymenttok8s")(/Deployink8sif("$params.environment)"三三'production')/RequestuserinputinordertodeploytoProductionUserProviderInput(message:"DeployinProduction",time。UtjneSsage:"Thereisapending$APP_NAMEdeploymentof$params.environment)InWWnPlease,confirmthedeployment*<$env.RUN_DISP1.AY_UR1.)|here>»M,)withCredentials(file(crede11tialsld:'svc-aliyun-ack-kubeconfig',variable:tKUBECONFIGt)sh"chmod+wJKUBECONFIG)"Sh-kubectlConfiguse-contextSconte×tsenvironment)"sh,'kustomizebuild.overlayJe11viro11me11t)kubectlapply-f-n$(namespace5environment"elsewithCredentials(file(credentialsId:'svc-aliyun-ack-kubeconfig',variable:,KUBECONFIG')sh-ChmOd+w$KUBECONFIG!"Sh"kubectlconfiguse-context$contextsenvironment"sh"kustomizebuild./oVerIay”environmentkubectlapply-f-nSnamespacesenvironment"stage("Checkdeploymentink8s")withCredentials(file(credentialsld:,svc-aliyun-ack-kubeconfig',variable:,KUBECONFIG')sh-Chmod*w$KUBECONFIG"Sh"kubectlconfiguse-contextSfcontexts(environment)"for(Stringitem:deploymentsenvironment)sh"""ffl/bin/bashkubectlrolloutstatusdeploy-nSnamespacesenvironment)&waitprintf"TheDeploymenthasbeensuccessful!,'3.4部署风除和应对在编写JenkinSpipelineHf,一股由于疏忽和环境因素会遇到各种错误,但是一旦顺利胞通,再制定相应的悌;作SOP,那失败的风险就主要在于应用开发的错误,所以,JenkinsPiPeIine一般可以交付给开发使用,运维人员几乎不用操心。4 .无服务部署4.1 什么是无服务部署Serverless架构是采用FaaS(函数即服务)和BaaS(后端服务)服务来解决问题的一种设计.FaaS就是Functionasaservice(函数即服务),每一个函数都是一个服务,函数可以由任何语言编写,直接托管在云平台,以服务形式运行,通过事件触发。BaaS则是Backendasaservice(后端即服务)。云平台提供的后端组件整合,开发者无需开发和维护后端服务,通过AP1/SDK的调用,便可获得例如数据存储、消息推送、账号管理等能力.4.2 无服务部署的实质SerVerIeSS的背后,依然是虚拟机和容器.只不过,服务器部runtime安装、编浮等工作,都由Serverless计算平台负责完成了.对开发人员来说,只需要维护源代码和SerVeHeSS执行环境的相关配舌即可.这就叫"无服务器计凭"。4.3 无服务部署的优势Serverless架构的最大优势,显然就是帮助用户彻底摆脱了基础设施管理这样的"杂事",吏加专注于业务开发,从而提升了效率,降低了开发和运营成本.根据业界的统计,在商业和企业数据中心里的典型服务器,日常仅仅只提供了5%15%的平均最大处理能力的输出.这是一种算力资源的巨大浪斐.Serverless的出现,可以让用户按照实际算力使用量进行付费,属于真正的"精确计费”.4.4 实际例子在AWS中,运维需要把测试环境中的资源在休息日自动关闭,或在晚上关闭,以实现降本增效.这里用1.ambda来取代pod的部若,可以实现用时收赛,不同免费,这是Serverless的精神所在.以下是在aws的1.ambda中,用python写下非常短小的代码:/关闭服芬涔importboto3region=*cn-north-l,itEnteryourinstanceshere:ex.t×-×XXX×XXXt,'X-XXXXXXXX'instances=,i-×××××e××××××x×x×x,deflambda-handler(event,context):ec2«boto3.client('ec2',regio-name-region)ec2.StopeInstances(InstanceIds-Instances)print(,stoppedyourinstances:,+str(instances)关闭DOCUmentDBmPorboto3deflambda-handler(event,context):docdb-boto3.client(,docdb,)response三docdb.describe-db-clusters(DBClusterIdentifier='uat-pms-documentdb,)status«resonsefDBClusters,'Status,ifstatus-«'available,:responsedocdb.stop_db_cluster(DBClusterIdentifier='uat-pms-documentdb,)print(tStoppedthecluster')关闭RDS数据原importboto3deflambda-handIer(event,context):rds=boto3.client(,rds,)response=rds.describe-db-clusters(DeClusterIdentifier=uat-pms-aurora)status«resonsetDBClusters*'Status'ifstatus-三,available,:response-rds.stopdb-cluster(DBCIusterIdentifier=,uat-pms-aurora*)print('Stoppedtheclu