目录html
2018年既是微服务架构火爆的一年,也是容器和Kubernetes收获赞誉盆满钵满的一年;在kubernetes的引领下,以容器为中心部署微服务已成为一种事实标准,并不断加速着微服务架构模式落地,持续地发挥着它的魔力。企业,特别是互联网公司,为了快速响应前端用户的需求,缩短产品从需求到交付的周期,经常须要快速地、细腻度地迭代产品,以抢占市场先机;在微服务模式下,能够很好地知足这个要求,只发布变化的服务,从而最小化单次迭代的风险,实现敏捷开发和部署。前端
当采用微服务模式后,整个业务流程将被垂直拆分红多个小单元;每一个小单元都是一个独立开发、独立部署和独立扩展的微处理服务,这样的灵活性很是适合敏捷开发模式,但也给开发和运维带来了固有的复杂性和难度。对于开发者而言,因为微服务应用总体做为一个分布式系统提供服务,须要选择合适服务通信协议,并处理潜在的网络分化瞬时故障等状况,除此以外,还须要建设服务发现、配置中心等基础设施;对于运维人员,须要利用容器的可移植性,持续地集成和部署微服务到不一样的集群环境,这些都要求运维人员具备很是全面的能力,好比:熟悉容器及k8s、能编写Linux Shell运维脚本、熟练一种持续集成部署工具(好比:gitlab、jenkins)等。linux
综上所述,如何搭建一条成熟稳定、且符合微服务特点的高度自动化DevOps管道又成为了另外一个难题。git
以最小的学习成本,搭建一条成熟稳定、且符合微服务特点的高度自动化DevOps管道,按需地持续集成/部署微服务到kubernetes。github
kubernetes + gitlab + shelldocker
在kubernetes的master节点部署gitlab-runner,充当gitlab服务器的客户端;当提交或合并代码到指定的分支时,gitlab-runner自动从gitlab拉取代码,利用master主机提供的边缘计算能力来执行已编排好的DevOps CI管道=》编译代码、运行单元和集成测试、容器化微服务成镜像,最后上传到企业镜像仓库,这就是持续集成流程,该阶段交付的产物为镜像。shell
在kubernetes的master节点部署gitlab-runner,充当gitlab服务器的客户端,当持续集成阶段交付了新版本的镜像后,从企业镜像仓库拉取最新版本的镜像,利用master主机提供的边缘计算能力执行已编排好的DevOps CD管道=》同步服务配置信息到配置中心(k8s的ConfigMap),并滚动更新kubernetes集群镜像版本。bash
安装目录:/root/gitrunner服务器
工做目录:/home/devops/gitrunnerrestful
> mkdir -p /root/gitrunner && mkdir -p /home/devops/gitrunner;
在kubernetes的master节点部署gitlab-runner,命令以下:
> wget -O /root/gitrunner/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-amd64; > cd /root/gitrunner; > chmod +x gitlab-runner; > # 注意:建议使用root用户进行安装,以免没必要要的权限问题。 > ./gitlab-runner install --user=root --working-directory=/home/devops/gitrunner; > ./gitlab-runner start;
gitlab支持注册两种类型的runner:
1. Specific Runners
这是隶属于特定项目的专有工人,不接受其余项目调遣。
2. Shared Runners
这是隶属于gitlab-server的工人,能够共享给全部的项目调遣。
这两种Runner各有千秋,若是为每个项目都注册专用Runner,会显得比较繁琐和多余,而使用共享Runner就很省事,可是一个工人一次只能作一件事情,当同时调遣一个工人时,那么就会出现竞争等待,故你们仍是实际状况来注册工人吧,只要不延误工期就行,嘿嘿。
在开发、预生产、生产环境注册Runner,并贴上标签:build、staging、prod。
备注:后面搭建DevOps管道时,将根据标签来调遣工人。
获取项目地址和注册token,依次查找路径:Settings => CI / CD => Runners settings,以下:
注册:
> cd /root/gitrunner; > ./gitlab-runner register > # 回车,根据提示填写项目地址、注册Token、标签、执行器 > # 假如,项目地址为:http://gitlab.justmine.cn:8002/, 项目注册token为:6iS4GBCh18NR4GPoMyef。 > ## 以开发环境为例(仅供参考) > Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com/): http://gitlab.justmine.cn:8002/ > Please enter the gitlab-ci token for this runner: 6iS4GBCh18NR4GPoMyef > Please enter the gitlab-ci description for this runner: [justmine.com]: development environment > Please enter the gitlab-ci tags for this runner (comma separated): build > Registering runner... succeeded runner=4iS4GwCh > Please enter the executor: ssh, docker+machine, kubernetes, virtualbox, docker-ssh+machine, docker, docker-ssh, parallels, shell: shell > Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded! > # 其余环境同理
上面的方案仅仅描述了愿景,也就是指望达成目标的最后结果,但对于如何落地一条真正的管道而言,仍是显得很是的空洞。其实这正是DevOps的难点,大致流程上都晓得有个持续集成、持续部署,讲起来如数家珍,落地时都之乎者也。
一样,秉承微服务的思想,分而治之,咱们将管道分为两个部分:建立、更新,即先建立一个主板次,而后再基于此主板次进行小版本迭代,不断地扩展新功能。经过这样有效的拆分,是否是就不那么的空洞了,就像领域驱动设计的CQRS模式同样,区别地对待读写,从而大大地减小了阻抗,也很是地切合产品的创新迭代,好比将需求拆分为3期,每一期都对应一个主版次,而后再小版本迭代每一期的需求,完成一期,封板一期,彼此隔离,互补影响,同时也方便追溯。
理清了整个管道的脉络,如今就须要思考一些实际问题了,好比:
1. 如何将持续集成/部署微服务流程脚本化,即如何实现基础设施代码化?
2. 如何动态解析git当前变化日志,实现准确地按需发布微服务?
3. 如何保留现场,并以最小的成本重试管道?
4. 在不修改管道脚本的状况下,如何手工控制按需发布、自动伸缩和回滚微服务?
5. 如何兼容新增的微服务?
6. 如何快速调试整个管道脚本?
只有把上面的问题都处理了,才算是一条成熟可用的、企业级别的CI/CD管道,才符合高度自动化、稳定、快速、容错等特色;在互联网公司,可能一天要提交好几个版本到不一样的环境,不能由于考虑不周而影响连续部署的进度,管道一旦投入使用就须要对修改闭合,只对扩展开放。
管道一览图:
示例项目(AspnetCore)已分享到github,请参考:MicroService.AutoDevOpsPipeLines,欢迎start,欢迎fork,欢迎issue。
为了验证管道的特性,我特地作了如下测试:
这是一个从0到一、从无到有的过程,这里一小步,倒是落地DevOps管道的一大步。
版本迭代的第一步就是建立微服务集群环境,那么如何快捷地建立这个环境呢?我将使用kubernetes的包管理器helm来完成这个任务,可能不少同窗都没用过这个工具,平时部署组件都是手工编写好yaml资源部署文件,虽然这种方式方便快捷,可是对于大量组件,以及须要实现基础设施代码化的场景,手工处理这种方式就不能知足需求了。
模板文件请参考示例项目,下面是建立环境的脚本化命令:
helm install /root/AutoDevOpsPipeLinesCharts \ --name=${releaseName} \ --set environment.upper=${Environment} \ --set environment.lower=${environment} \ --set namespace=${namespace} \ --set image.registryhost=${RegistryHost} \ --set image.username=${registryUserName} \ --set image.version=${version} \ --set replicas=${replicas}
从上面能够看出,实现部署服务脚本化的目的已经达到了。
下面咱们在来看看如何脚本化整个建立环境管道线:
# 001 Continuous integration image to registry. bash ./devops/PipeLines/Creation/001_CI.sh # 002 Create config information to k8s's configmap. bash ./devops/PipeLines/Creation/002_CreateConfig.sh # 003 Release major to k8s's cluster. bash ./devops/PipeLines/Creation/003_ReleaseMajor.sh # 004 Create gateway route. bash ./devops/PipeLines/Creation/Gateways/Kong/004_CreateGatewayRoute.sh
备注:管道线脚本请参考示例项目(AspnetCore):MicroService.AutoDevOpsPipeLines/devops/PipeLines/Creation。
将刚刚建立的helm模板文件上传到gitlab-runner所在服务器的/root目录下,并添加配置,以下:
<Project> <PropertyGroup> <Major>1</Major> <Minor>0</Minor> <Patch>0</Patch> </PropertyGroup> </Project>
而后合并代码到分支release/staging,以下:
从上面能够,第一个主板次(1.0.0)已经成功发布到预生产环境。
生产环境同理,在预生产环境跑完各类测试后,合并代码到分支release/production便可。
这个阶段将模拟在第一个主板次(1.0.0)上进行小版本迭代需求,距离上次发布已经一周了,开发部门也完成了第一个小版本的开发工做,如今须要发布版本1.0.1到预生产环境进行测试,首先修改文件version.props,以下:
<Project> <PropertyGroup> <Major>1</Major> <Minor>0</Minor> <Patch>1</Patch> </PropertyGroup> </Project>
除了发布本次需求修改的两个微服务:Identity.API、Marketing.API之外,还需强制发布微服务Basket.API,添加配置,在gitlab仓库依次查找 (Settings => CI/CD => Secret variables),以下:
最后合并代码到分支staging。
先来看看是否正确解析git变动日志和全局变量,准确地实现自动化和手工控制:
再来看看整个管道的执行状况:
最后看一下预生产环境的效果
从上面能够看出,第一个小版本(1.0.1)已经按需自动发布到预生产环境,一共滚动更新了三个微服务。若是当管道的某个阶段执行异常,只须要点击重试此阶段便可;若是须要从新手工干预,只须要添加配置信息,而后重试analysing-git-changes
阶段,再依次重试后面的Job便可,整个过程无需修改CI/CD管道脚本,真正实现高度自动化、快速等特色。
生产环境同理,在预生产环境跑完各类测试后,合并代码到分支master便可。
通过一段时间的观察发现预生产环境的购物车(Basket.API)微服务吞吐量颇高,故决定扩容它的实例数量到2个,首先修改项目属性文件deploy.props,以下:
<Replicas>2</Replicas>
而后添加配置,以下:
最后合并代码到分支scaling/staging,以下:
同理,首先修改项目属性文件deploy.props,以下:
<Replicas>2</Replicas>
而后添加配置,以下:
最后合并代码到分支scaling/staging,或者直接重试管道的auto-scaling阶段,以下:
从上面测试看到,只须要修改配置,就能够支持不一样粒度地伸缩微服务,也不用修改CI/CD管道脚本。
生产环境同理,只须要合并代码到分支scaling/production。
通过一段时间的观察,发现刚刚发布到预生产环境的版本1.0.1有问题,故决定回滚到上一个版次1.0.0,首先修改项目属性文件deploy.props,以下:
<!--回滚步长--> <RollBackStep>1</RollBackStep>
而后添加配置(只回滚购物车微服务),以下:
最后合并代码到分支rollback/staging,以下:
同理,首先修改项目属性文件deploy.props,以下:
<!--回滚步长--> <RollBackStep>1</RollBackStep>
而后添加配置回滚全部微服务,以下:
最后合并代码到分支rollback/staging,或者直接重试管道的roll-back阶段,以下:
生产环境同理,只须要合并代码到分支rollback/production。
通过一段时间的迭代,一期已经完工,二期新增了搜索微服务,这时修改helm模板文件支持部署搜索微服务,而后合并代码到release/staging,测试以下:
k8s
网关路由
从上面能够看到,新增的搜索微服务已经成功发布到第二个主版次了。除了修改helm模板文件之外,整个过程并无修改CI/CD管道脚本,圆满完成了兼容新增微服务的特性。
备注:咱们能够将helm模板当作服务编排文件。
build
构建和编译代码。
release/staging
建立预生产环境。
staging
滚动更新预生产环境。
release/production
建立生产环境。
master
滚动更新生产环境。
scaling/staging
伸缩预生产环境
scaling/production
伸缩生产环境
rollback/staging
回滚预生产环境
rollback/production
回滚生产环境
应用程序配置 - app.props
<!-- k8s命名空间前缀,好比:microservice-autodevopspipeline-v1 --> <NameSpace>microservice-autodevopspipeline</NameSpace> <!-- 应用程序名称,主要用于Tips --> <AppName>MicroService.AutoDevOpsPipeLine</AppName> <!-- 解决方案名称,用于生成项目 --> <SolutionName>MicroService.AutoDevOpsPipeLines.sln</SolutionName>
版本配置 - version.props
<!-- 主板次,不兼容升级 --> <Major>1</Major> <!-- 次板次,兼容升级 --> <Minor>0</Minor> <!-- 补丁版次,静默修复接口 --> <Patch>1</Patch>
部署配置 - deploy.props
<!-- Auto-scaling 实例数量 --> <Replicas>1</Replicas> <!-- Rollback 步长 --> <RollBackStep>1</RollBackStep> <!-- 镜像仓库用户名 --> <ImageUserName>devopspipelines</ImageUserName>
特定环境配置,如:deploy.staging.props
<!-- 镜像仓库主机域名 --> <RegistryHost>registry.staging.com:8100</RegistryHost> <!-- k8s restful地址 --> <K8sApiServer>https://192.168.2.110:6443</K8sApiServer> <!-- k8s 接口访问令牌 --> <AccessToken>utyeyerye.ytryeryeryyyyyyr.jhddghdhdhdhd</AccessToken> <!-- kong restful地址 --> <KongApiServer>http://192.168.2.110:81</KongApiServer> <!-- kong 域名绑定 --> <KongRouteDomain>staging.devops.com</KongRouteDomain>
分支环境配置 - branch.env.props
<!-- 解耦,目前用于滚动更新 --> <!-- key: branch name(last keyword), value: environment --> <staging>Staging</staging> <master>Production</master>
上面的测试几乎涵盖告终合k8s管理应用生命周期的全部流程(部署、伸缩、回滚、发布),你们能够放心地运用或者扩展这个管道到本身的微服务项目中,好比:目前仅支持自动建立路由到kong网关,建议你们fork项目后,自行扩展,测试完成后,也能够提取PR。若是你采用示例同样的项目结构,只须要修改配置信息,而后开箱即用。
若是你有什么需求没法实现,欢迎加入QQ群:564095699,一块儿探讨k8s实践微服务的方方面面。
若是有什么疑问和看法,欢迎评论区交流。
若是你以为本篇文章对您有帮助的话,感谢您的【推荐】。
若是你对微服务实践感兴趣的话能够关注我,我会按期的在博客分享个人学习心得。
欢迎转载,请在明显位置给出出处及连接。