内容简介
近两年作者在海外交付中参与 microservices 下的团队,为客户提升 Finance 系统的扩展性。作者所在团队,3 对开发(pair programming, 2 个 dev 为 pair)为客户支撑着 11 个 services,持续部署流水线(CD pipeline)是其中必不可少的一个技术实践。本次分享作者将从实践的角度分享 microservices 架构下的持续部署(CD)。内容概述-
1. microservice 概述:简要介绍 microservice 架构下的挑战
-
2. 持续部署实践:这里会提到『Build Pipeline as Code』,『Infrastructure as Code』等概念。
-
3. 持续部署了之后呢? 这里会介绍 CD 结合团队的敏捷开发流程的实践经验。
microservices 概述
当提到 microservice 的时候,我们通常会从下图开始:
为了从业务和技术方面得到更好的扩展能力,我们将单一架构的系统(Monolithic architecture),拆分成若干的微服务(Microservices architecture),这种拆分是架构演进的一个过程。在整个拆分过程中,对团队的组织架构,数据的管理方式,部署监控技术方面都带来极大的挑战。持续部署(简称 CD)是 microservices 架构中一个必备的实践之一。本文将介绍基于Docker 的 CD 方式。
部署方面带来的挑战
- - 10 mins Unit Test
- - 2 hours Acceptance Test
- - 15 mins package
- - 20 mins deployment
- - 1 mins Unit Test
- - 1 mins Integration test
- - 5 mins package
- - 5 mins deployment
持续部署方面的实践
当我们谈持续部署的时候,此时也会包括持续集成。持续部署的整个过程会从代码 push 到 master 开始:我们采用 Docker 来解决技术栈差异的问题,DevOps 创建部署工具将部署,监控,报警等配置模板化。实践:
- - 使用 Docker 构建和发布 service
- - 采用 Docker Compose 运行测试
- - 使用 Docker 进行部署
- - Build pipe line as Code
- - Infrastructure as Code(base on AWS)
- - 共享构建脚本
使用 Docker 构建和发布 service
- - 使用 Docker 构建 service,service 已 docker image 的方式发布
- - 将 docker 发布到 docker registry
- - 从 docker registry 上 pull docker image 进行部署
使用 Docker Compose 运行测试
Build pipeline as Code
Infrastructure as Code
- - 可部署的机器
- - 机器的 IP 和网络配置
- - 设备硬件监控服务(GPU,Memory 等)
- - 负载均衡(Load Balancer)
- - DNS
- - AutoScaling (services 自动伸缩服务)
- - Splunk 日志收集
- - NewRelic 性能监控
- - Sentry.io 和 PagerDuty 报警
- - 采用 AWS 云服务进行部署
- - 采用 AWS CloudFormation 描述和创建资源
- - 将对资源操作的脚本进行 source control
- - 对资源的描述和操作应该在 git 中
- - 在所有环境中采用相同的部署流程
- - 使用 ssh 等手动操作资源的方式,只能用于测试环境和做一些 debug。
共享构建脚本
- 1. 运行测试
- 2. 构建发布 docker image
- 3. 部署
- 1. test.sh
- 2. docker-tag.sh
- 3. deploy <test|prod>
之后为上述脚本创建 git repository,并且将其以 git submodule 的方式引入各个项目。持续部署了之后呢?
让 CD pipeline 服务团队的工作流程
我们搭建好 CD pipeline,需要让它在团队的敏捷开发流程中发挥为威力:团队职责:
- - 团队主要分为 BA,Developer(简称 Dev),Tech Lead(简称TL)
- - BA 负责分析业务,并在故事墙上创建 Story
- - Dev 负责开发,QA,运维(跨能型团队)
- - Tech Lead 负责技术
- 1. Dev 从 Backlog 中拿卡进行分析,分析完成后跟 BA,TL一起 kickoff 确定需求、技术实现。
- 2. kickoff 之后, Dev 在 repository 上创建 Pull Request(简称 PR) 开始工作。此时在 PR 上的每一次 git push 会触发 PR 的 pipeline,此时在 CI 机器上只会运行单元测试和集成测试。
- 3. Dev 开发完成后,其他 Dev 对 PR 进行 Review,Review 通过之后将 PR merge 到 master 分支,此时会 trigger master 分支上的 pipeline,将最新代码自动部署到 test 环境。
- 4. 部署 test 环境成功后,Dev 基于 test 环境进行 QA。
- 5. QA 完成后向 BA, TL 做 showcase 进行 user acceptance test。
- 6. 通过 user acceptance test 之后,在 BuildKite 上点击部署到 production 按钮完成发布。
按照以上流程,团队可以快速从 CI/CD pipeline 上得到反馈,高度自动化的 CD pipeline 可以让团队做到按照 Story 进行 service 发布。Summary
Q&AQ1:你们的docker是用什么来管理的?k8s,swarm 还是其他什么?
A1: K8S, Swarm 都没使用,对这两个服务我也只是听说过。 每个独立的 service Docker image 会单独运行在 AWS EC 2 Instance。管理一般也都是围绕 EC 2 Instance 来做。关于 Docker Images 仓库,采用自建的 Docker Registry 服务来 push 和 pull docker image。================Q2:如何做跨语言的服务集成A2: 我们做过 Ruby 和 Node.js 的集成。服务器间通信采用 HTTP 协议,JSON 作为传输格式, JSON 基于 hyper media link 的实现之一 HAL 。 如何解决 service 之间的约定,我们采用 Consumer Driven Contract 的方式,使用单元测试代替集成测试,这部分实现采用 Pact 。================Q3:service的粒度如何确定A3: 这是一个非常好的问题,也是很难回答的问题。 我个人觉得 microservices 中最难回答的两个问题是:1. 什么拆分出一个 service?2. 怎么拆分?(也就是拆分多细,系统边界如何确定)service 粒度如何确定,一句话来回答(TL;DR):『采用领域驱动设计(DDD)的方式拆分』。更长版本,我们项目是为客户解决已运行10年以上财务生态系统的拆分问题。也就是老系统改造,也是我觉得最适合实施 micro services 的场景。老系统存在很多痛点,并且需求较为稳定。那么如何拆:1. 需要引入业务专家,技术专家,相关利益人一起分析业务场景,系统构成确定领域语言对业务进行建模。2. 将以前以业务切分系统的思路转为按照数据模型切分的思路。3. 每个类数据模型可以划分多种子模型来辅助主数据模型。4. service 的粒度一般以数据模型的粒度来确认。还有一些按照非功能性需求来切分的原则,比如实施 CQRS 的时候,将读写拆成两部分,采用 Even Sourcing 解决分布式系统数据状态同步的问题。================Q4:为什么没有考虑gocd,gocd可是first class的CI/CD工具A4: 好问题,这个我需要找 ThoughtWorks 负责 GoCD 的同时聊聊销售问题。BTW,我个人还没机会使用过 GoCD================Q5:目前发现从esb soa转microservice,原本进程内的调用关系变成了网络调用,一次rpc变成了几次或者几十次rpc,同等条件下性能损失严重。这个问题如何得到解决?A5: 好问题,性能问题也是被同事问得最多的问题。关于性能问题我没遇见过高并发的场景,当时对于降低 services 依赖,控制网络请求我们是有一些解决方案。1. 所有 services 基本都部署在同一个网络,比如 AWS 的网络上,可以讲起想想成内部局网,HTTP call 带来的性能损耗目前为止还算可以接受。2. 从系统设计的角度讲,与其降低HTTP请求的消耗时间,不如减少HTTP 请求的发送次数。从系统设计上考虑,大部数据模型都是 immutable (不可变得),在系统系统间我们将数据缓存,减少 HTTP 请求的次数。