抖音技术能力大揭密!钜惠大礼、深度体验,尽在火山引擎增长沙龙,就等你来! 立即报名>> 了解详情
写点什么

案例剖析:从 0 开始搭建一个微服务的持续交付系统

2016 年 9 月 01 日

本文介绍了如何利用开源软件快速搭建一套微服务的持续交付系统。本文假设的环境是 Linux 操作系统,用到的软件包括 Git、Jenkins、Salt、ZooKeeper、Apache 等。开始之前,我先简单介绍下持续交付和微服务的概念,以便大家更好的理解本文的精华。

什么是持续交付?我们先举个物流的例子,现在各大电商都非常重视物流的自动化建设,在实现包括运输、装卸、包装、分拣、识别等作业过程的设备和设施自动化的同时,更在研究无人机和自动驾驶汽车送货,达到物流的全自动。

那么软件开发呢,从开发人员 check in 代码到代码仓库,到代码的构建、部署、测试、发布,我们可以形象地把这个过程称为“软件物流”,现实世界的物流实现了相当的自动化,“软件物流”也应如是,实现从开发人员 check in 代码(客户下单)到生产系统上线(送货上门)的自动化。

说到这里,我们可以给持续交付下一个“非专业”的定义,持续交付就是实现“软件物流”的自动化。

图 1. 持续交付流水线

图 1 摘自《持续交付:发布可靠软件的系统方法》,展示了持续交付具体包括的内容。本文重点讨论如何实现微服务的持续交付流程,所以会忽略掉整个流程的一些细节(如代码分析、单元测试等等)。

那什么是微服务呢?微服务的概念最初由 Martin Fowler 与 James Lewis 于 2014 年共同提出,微服务架构风格是一种使用一套小服务来开发单个应用的方式途径,每个服务运行在自己的进程中,并使用轻量级机制通信,通常是 HTTP API,这些服务基于业务能力构建,并能够通过自动化部署机制来独立部署,这些服务使用不同的编程语言书写,以及不同数据存储技术,并保持最低限度的集中式管理。目前微服务的主流实现方式有两种:RESTful API 和消息队列。

图 2 RESTful 微服务

图 3 message queue 微服务

图 2、图 3 是两种典型微服务架构的简略图。当然现实中的系统会复杂的多,比如会有微服务聚合,多级缓存,注册中心等。

微服务相对单体式应用来说有明显的好处:

  1. 解决了单体式应用的复杂性问题,单个微服务很容易开发、理解和维护。
  2. 每个微服务都可以由独立的团队来开发,可以自由选择开发语言。
  3. 每个微服务可以独立部署,系统可以快速演进。
  4. 可以对每个微服务进行独立扩展,极大的提高系统伸缩性及资源利用率。

但在一个单体式应用拆分成数十个乃至上百个微服务,由于服务数量的增加,以及微服务支持多种编程语言的特性,对软件的构建,部署,测试,监控都带来了全新的挑战。本文将讨论如何通过持续交付来降低微服务构建,部署的复杂度。

微服务的持续交付:统一方法

由于微服务的特性,微服务的持续交付会比单体式应用的持续交付复杂的多。本节列出了为了降低微服务持续交付的复杂度,我们遵循的一些原则:

  1. 统一方法。这里有两个层面的含义,第一是流程的统一,有很多公司对运维自动化非常重视,但在开发、测试阶段没有采用自动化的方法。随着 DevOPS 运动的兴起,大家逐渐意识到需要在开发,测试阶段采用与生产环境相同的交付方法,这样在系统部署到生产环境的时候,这一交付流程已经经过多次的检验,出错的概率大大降低了。第二层含义与微服务相关,各个微服务可能用不同的语言实现,如 Java、Python、C++、Golang、纯前端(JavaScript),我们要对采用不同语言实现的微服务使用统一的交付方法。
  2. 在版本控制系统中,每个微服务应该对应一个独立的仓库。以 Git 为例,一个 Project 下面,每个微服务对应一个独立 Repository。这样各个微服务可以独立 check in 代码,而不会在持续构建的时候互相影响。
  3. 设计持续交付系统时要考虑实现软件交付的全自动化,虽然在现实中,会存在提交测试,生产变更审核等人工环节。但在理想情况下,开发人员 check in 代码之后,能够自动触发构建多套环境的部署及测试。
  4. 支持单个微服务升降级,这要求持续交付系统,对每个可部署的单元(微服务)要有独立的版本号。
  5. 程序与配置分离。要支持一套程序(可执行包 + 配置文件包)多处部署,这里强调了一套程序,是指在开发人员 check in 代码后,构建系统只生成一份程序(可执行包 + 配置文件包)。不管是部署到开发环境、测试环境,还是生产环境我们要用同一套程序,而不是对每个环境单独打包。我们知道 Java war 包会要求把配置文件包含在里面,这会造成不同的环境要求提供不同的 war 包,这就违反了我们说的这个原则,后面我们会讨论如何处理这个问题。
  6. 在应用程序部署时,不得依赖外网资源。我们把部署过程独立为两个阶段:环境准备阶段和应用程序部署阶段。环境准备包括操作系统,JDK 或其他语言运行时系统级依赖库的安装,得益于 IaaS 的相对成熟,我们把这一阶段独立出来。而应用的部署需要定制化,也是本文讨论的部分。在部署应用时,要求所有的资源从内网获得,这样可以保证应用部署过程的快速、稳定、可重复。

快速搭建微服务的持续交付:持续构建

下面我们结合一个虚构的项目来介绍持续交付的实现细节,假设我们有一个项目 BetaCat,由 ms1、ms2…msN,n 个微服务构成。下面我们重点介绍 ms1 微服务如何实现持续交付,其它微服务可以类推。

本节讨论下如何实现持续构建,下一节会探讨持续部署。

图 4 Jenkins 处理仓库代码流程

如图 4 所示,开发人员 check in 代码到 Git 仓库后,Jenkins 会自动地进行构建工作,并把打好的包上传到 Repo server 上。

图 5 配置文件示例

作为统一方法的一部分,我们在每个微服务仓库上创建了 CI 目录,用于配置文件的打包,在 CI 目录里,只放入需要参数化的配置文件,执行脚本等,并会严格遵循原有系统的目录结构,如图 5 所示,我们要求有 start.sh、stop.sh 及 service(用于 Linux 的 init 启停该微服务)。

图 5 中配置文件参数化内容,参数部分用“{{”与“}}”包围起来,在持续部署的时候会根据传入的参数替换为特定的值。

我们还定义了持续构建的统一输出,对每个微服务采用 tgz 的打包格式,微服务 ms1 持续构建的输出文件示例如下:

  • ms1-1.0.7.tgz (可执行包)
  • ms1_config-1.0.7.tgz(配置文件包)

在可执行包里面要求把所有的依赖库(除了系统 lib 库)都包含在里面,对不同编程语言的微服务的构建工具没有强制要求,统一由 Jenkins 调用。C/C++ 我们推荐使用 CMake,Java 一般用 Maven,Python 直接打包。

配置文件包就是前面 GIT 仓库的 CI 目录直接打包而成。

图 6 Bundle 示例

同时为了在部署时不用具体指定每个微服务的版本号,我们引入了 bundle 的概念,如图 6。在任何一个微服务构建之后,会触发 bundle,sha512 校验文件生成,并上传到 Repo Server。

最后让我们看下持续交付上传到 Repo Server 的目录结构:

图 7 目录结构

这样持续构建的工作就完成了,接下来就需要进行持续部署了。

快速搭建微服务的持续交付:持续部署

在开始持续部署的讨论之前,我们先描述一下软件运行注入配置的三个时点:

图 8 配置注入的三个时间点

打包时点,典型的是 Java 的 war 包,会把配置文件打包在一起。部署时点,在部署的时候利用专门的部署工具更新配置文件,这也是我们采用的方法;运行时点,程序运行时通过环境变量或注册中心 / 配置中心获得配置信息,如用 Docker 部署微服务时就要考虑通过这种方法来获得所需要的配置信息。

图 9 采用 salt 进行部署

图 9 显示了我们对不同的环境统一采用 salt 进行部署。由于我们支持用户只输入 bundle 的版本信息来实现部署,这就要求在持续部署的时候,部署系统能自动获取每个微服务的版本号,为此我们对 salt/foreman 做了一点小改动,修改后返回的 pillar 格式包含各个微服务的版本,同时下载并解压对应的配置文件包到 salt master 的相应目录,以及关闭 salt master file_list 缓存:fileserver_list_cache_time: 0。

图 10 foreman web 界面以及 Salt 格式

图 10 左边表示我们在 foreman web 界面上设置的参数,右边表示通过 salt pillar.items 取得的格式,可以看到多了每个微服务的版本号信息。

下面我们按照部署三部曲(安装、配置注入、服务运行)来介绍部署规则文件(saltstate、sls 文件)的编写:

1、betacat_ms1.sls 第一部分:安装

在这一部分,检查并创建安装目录,下载需要的可执行包,并解压到正确的位置,可执行包直接从 Repo Server 获取,并通过 sha512 验证文件的完整性。

2、betacat_ms1.sls 第二部分:配置注入

配置注入部分,读取配置文件包,通过 salt master 转换后下发给目标机。这里用红框标出了设计的核心。通过 salt 的 file.recurse 和之前持续部署中打好的配置程序包,并把所有的配置项传入。可以做到不用对多个配置文件单独编写部署逻辑,完全参数化。

3、betacat_ms1.sls 第三部分:服务运行

在这一部分,确保微服务在运行状态,并在必要的时候重启。这里需要特别指出的一点,在整个 sls 文件中,对不同的微服务来说,只有 3 个元参数:项目名称(BeatCat)、微服务名称(ms1)以及 sig(ms1, 微服务进程的唯一识别字符串)。那么我们可以通过简单的脚本来自动生成 sls 文件,而不需要手工编写。大大降低持续部署的开发维护成本。

快速搭建微服务的持续交付:全自动化

为了支持持续交付流程的全自动化,我们引入了 ZooKeeper,如图 14。

图 14 引入 ZooKeeper 后的流程

  1. 代码 check in 到 Git 后,触发构建,Jenkins 会把打好的包上传到 Repository Server,并更新 ZooKeeper 的本次及 latest 包版本信息。
  2. 侦听到 ZooKeeper 的 latest 包版本信息变动后,会触发 saltstack 的部署命令向各个环境部署最新的程序。
  3. 部署完毕,会更新 ZooKeeper 上的目标机部署版本信息。
  4. 侦听到 ZooKeeper 上的目标机部署版本信息变动后,会触发一套或多套自动化测试脚本的运行。
  5. 自动化测试通过后,会更新 ZooKeeper 上的包版本的测试信息。
  6. 通过测试的包,可以自动上传到生产环境的 repo server,并更新生产环境 ZooKeeper 的包版本信息。
  7. 生产环境,侦听到 ZooKeeper 的包版本信息变动后,会触发生产环境的部署。
  8. 生产环境部署完毕,会更新 ZooKeeper 上的目标机部署版本信息。

总结

在前面几节我们结合一个虚构的项目 betacat 介绍了如何实现持续交付。下面让我们来做个简单的回顾:

  1. 我们提到了统一方法,我们通过在持续构建阶段对不同语言的微服务代码输出统一的 tgz 包格式,并引入了 bundle 文件,简化了后续的持续部署的实现;我们在开发环境、测试环境、生产环境都 salt/foreman 来进行统一部署,实现了部署方式的统一。
  2. 我们提到了每个微服务应该对应一个独立的仓库,在实践中,也会碰到一份代码需要生成多份微服务的例子(工具类的微服务),这个需要在持续构建的时候多做点工作。也会碰到一个仓库中包含了多个需要独立部署的微服务,一般建议把它们独立出来,当然也可以在构建这些微服务时共有一个仓库,但每次代码 check in 都会触发多个微服务的构建。
  3. 实现软件交付的全自动化,为此我们引入了 ZooKeeper,并通过各个脚本作为简单的粘合剂来实现各个环节的自动触发,配合完成整个交付的全自动化。
  4. 支持单个微服务的升降级,我们对每个微服务都有版本号,并为了简化部署,我们还引入了 bundle,用户在一般情况下只要指定一个 bundle 的版本,就可以自动把需要的多个微服务部署到目标机上,特殊情况下,也可以具体指定每个微服务的版本号。
  5. 程序与配置分离,在开发环境与测试环境,我们用的同一个 Repo server,实现多套环境的部署,生产环境的 Repo 也只是原始的 Repo 的一个简单子集。Java war 包要求把配置文件打包在里面,其实在原有 war 包带上版本号的基础上,再配合一个配置文件包,应用统一方法更新即可。
  6. 我们提到在应用程序部署时,不得依赖外网资源。把所有的外部依赖要么在环境准备阶段准备好,要么把依赖打包在可执行包内。在持续构建时候,可以把 C/C++、Java lib 库包括在可执行包里面;前端比较流行的 bower_components 等相关文件也可以在持续构建阶段打包进可执行包。

优点回顾

我们提出了一套利用开源软件快速搭建微服务的持续交付的方法,下面分析一下它的优点。

  1. 低侵入性

在改造已有微服务项目时,我们通过在每个微服务代码仓库添加 CI 目录来实现配置项的参数化,最大程度上降低了对既有项目的侵入性。而在项目切换到持续交付流程时,只要保持 CI 目录下的配置为最新。
2. 低维护成本

用户在微服务添加配置项或配置文件时,只要往 CI 目录 check in 文件(如果是全新的配置项需要在 foreman 里设置),就可以实现配置文件的自动更新,不需要修改持续构建,持续部署系统本身。
3. 低耦合

得益于系统的低耦合,我们可以自由地替换各个部分,Git 也可以用 SVN,现有流行的持续构建工具对主流的版本控制系统都有很好的支持;Jenkins 也可以替换为 Teamcity 或其他持续构建工具,只要保持输出到 Repo 的包保持一致;Salt 也可以用其他持续部署工具来替换(puppet 的 file 部署目录时 source 与 recurse 有冲突,实现 salt “file.recurse”的类似效果可能会有障碍);ZooKeeper 也可以用 etcd 或消息队列来轻松替换。
4. 可以快速切换到 Docker 部署。

局限性反思

1、基于部署目标机来填写配置信息,配置过程为:

选择机器 -> 选择要部署的微服务集合 -> 设置配置信息。

这需要对每台机器都要配置一遍配置信息,虽然我们之前实现了配置信息的去重。更合理的方式应该是:

选择要部署的微服务集合 -> 选择配置信息–> 选择要部署的机器

微服务集合类似于 k8s 的 pod 的概念,配置信息可以多个版本并存,这样在实现配置的集中管理的同时,可以方便地实现蓝绿发布或金丝雀发布。更进一步,目标机简单可抛弃,实现不可变基础设施(Immutable Infrastructure)。

2、暂未实现数据库 schema 的自动变更。

作者介绍

祝小华,森浦资讯 DevOPS 技术负责人,十多年软件开发工作经验,先后就职于 Alcatel-Lucent wireless,IBM DS8000 部门从事软件开发工作。关注存储,微服务架构,容器技术及数据分析。


感谢郭蕾对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ @丁晓昀),微信(微信号: InfoQChina )关注我们。

2016 年 9 月 01 日 17:218410

评论

发布
暂无评论
发现更多内容

负载均衡算法之一 - 以 Golang 方式

hedzr

load-balancing weighted round-robin consistent-hashing

发布60分钟!霸榜Github的阿里面试指导小册,啃透涨薪10k

Java架构师迁哥

基于WebRTC的1对1通话实战(一)环境搭建

IT酷盖

音视频 WebRTC

前端 JS 之 AJAX 简介及使用

编程三昧

JavaScript ajax 前端 异步请求

云上 ARM 实例应用优化?现在带你读懂它!

亚马逊云科技 (Amazon Web Services)

如何针对美工与设计师的Maya工具进行版本控制

龙智—DevOps解决方案

百度的云图丹青

脑极体

DSS如何实现RTSP协议支持与分发处理

Changing Lin

音视频 6 月日更

优质高效!基于Spring-boot-admin的微服务监控系统实现

程序员小毕

Java spring 程序员 面试 springboot

CSS技巧 | 优雅的处理文本溢出截断

devpoint

CSS 6 月日更

Python分类预测模型

Qien Z.

Python 预测模型 6月日更

区块链技术让传统旅游业焕发新机

CECBC区块链专委会

Kubernetes手记(12)- StatefulSet 控制器

雪雷

k8s 六月日更

智慧新发展:打造更富动感的智慧园区3D可视化决方案!

一只数据鲸鱼

数据可视化 智慧城市 智慧园区 三维可视化

从金融街往事到全场景智慧金融未来

白洞计划

数字经济下的数据库发展以及应用

容光

Flask-Limiter详细使用说明

行者AI

flask

小伙伴们在催更Spring系列,于是我写下了这篇注解汇总!!

冰河

spring 程序员 架构师 aop ioc

尽情阅读,技术进阶,详解mmap的原理

奔着腾讯去

c++ 内存管理 Mmap 虚拟内存 共享内存

阿里P8工作10年,离职时发现只剩这份《Java架构速成》笔记了

Java架构师迁哥

详解 SQL 的集合运算

悟空聊架构

数据库 sql 6 月日更 集合运算

从货币流动性角度看区块链流动性发展

CECBC区块链专委会

话题讨论|从2021苹果全球开发者大会中,你得到了什么启发?

石云升

wwdc 话题讨论 6月日更

微服务沉思录-可观测性

余朋飞聊IT

微服务 监控 日志 可观测性 链路追踪

夯实区块链产业发展根基是当务之急 标准体系、知识产权是国际竞争角力之焦点

CECBC区块链专委会

企业如何成功应用机器学习?看这四点就够了!

亚马逊云科技 (Amazon Web Services)

基于开源引擎打造自主可控服务体系

张亮

大数据 开源 To B业务

网易云信大规模聊天室系统架构解析

网易云信

聊天室 IM

👑【Hystrix技术专题】原理和特性介绍

李浩宇/Alex

Hystrix spring-cloud 熔断器 6月日更 6 月日更

别闹,那个在加密世界拿着长枪的库币

猫Buboo

区块链 区块链+ 加密资产

5分钟速读之Rust权威指南(二十二)迭代器

码生笔谈

rust

Study Go: From Zero to Hero

Study Go: From Zero to Hero

案例剖析:从0开始搭建一个微服务的持续交付系统-InfoQ