过去一年时间,Service Mesh 悄然兴起,成为云堆栈的一个重要组件。如 Google,IBM、Lyft 这些公司都在各自的产品应用中添加了Service Mesh。但是究竟为什么 Service Mesh 会受到如此大的关注呢?
本系列课程将带领读者学习什么是 Service Mesh, Service Mesh 能做什么,为什么 Service Mesh 是构建云原生应用的主要构件,并以实例为基础详细介绍 Service Mesh 的主要实现 linkerd。
其中包括:
认真学习完这个课程之后,你将获得或者具备以下能力:从0到1理解 linkerd 的工作机制;具备通过 linkerd 构建弹性的、高可见性的、可追踪的云原生应用。
杨章显,多年企业级在线会议系统的运维以及软件发布、变更管理经验,熟悉容器技术、容器编排、自动化运维、部署、监控等,目前为思科内部容器 PaaS 主要负责人,负责技术选型、实现、落地等工作。
随着近年来云计算技术的快速发展,软件开发也从传统的单体应用到 SOA 以及时下流行的微服务,均随着技术的演变发生巨大的变化,无论是对开发人员还是运维人员的技术理念和思维都要求极大的转变。尤其是在云原生时代,微服务已经成为业界开发应用的主要方式,而一些云计算技术的出现如 Docker ,使得开发和发布微服务更加容易。但是微服务架构并不是万能银弹,虽然一方面可收之桑榆,但另外也可能失之东隅。其中最具挑战性的是如何确保分布在复杂网络环境中微服务处理网络弹性逻辑及可靠地交付应用请求,正如 L. Peter Deutsch 在分布式系统的谬误中论述,我们不能一厢情愿地认为:
因此应用应当具有规避网络不可靠、丢包、延时等的能力。那么从开发人员和运维人员来说,怎么才能确保分布在复杂网络环境中的微服务具有处理网络弹性逻辑能力及可靠地交付请求呢? 一些常用技术手段如:
当然,在微服务架构中,多语言、多协议支持以及透明监控等问题也需要得到足够的重视。
下面我们看处理这些挑战和问题时技术方案是如何演进的。
在这种方案中,通常我们把上述的一些技术手段如负载均衡、服务发现或分布式追踪等跟业务逻辑代码一起封装起来,使得应用具有处理网络弹性逻辑的能力。该模式如下:
这种模式非常简单,但是从软件设计的角度,大家能很快发现它有很多缺点:
关于第一种方案,虽然应用具有处理网络弹性逻辑能力,增强动态运行环境中如何处理服务发现、负载均衡等,向提供高可用、高稳定性、高 SLA 应用更进一步,与此同时,你也看到这种模式具有很多缺点。为此,我们是否可以考虑将由应用处理服务发现、负载均衡、分布式追踪、安全通讯等设计为一个公用库呢?这样使得应用与这些功能具有更低的耦合性,而且更加灵活、提高利用率及运维性,更主要的是开发人员只需要关注公用库有,而不是自己实现,从而降低开发人员的负担。这方面很多公司如 Twitter、Facebook 等走在业界前列,像 Twitter 提供给 JVM 的可扩展 RPC 库 Finagle 和 Facebook 的 C++ HTTP 框架 Proxygen,Netflix 的各种开发套件,还有如分布式追踪系统 Zipkin,这些库和开发套件的出现大量减少重复实现的工作。对于这种模式:
虽然相对前一种解决方案,新的方案在耦合性、灵活性、利用率等方面有很大的提升,但是仍然有些不足之处:
显然,没有任何方案能一劳永逸的解决我们所有的问题,而各种问题驱使我们不断地向前演进、发展,探索新的解决方法。那么,针对我们现在所面临的问题,我们有更好的解决方案吗?答案当然有。
相信大家对 OSI 七层模型应该不陌生,OSI 定义了开放系统的层次结构、层次之间的相互关系以及各层所包括的可能的任务,上层并不需要对底层具体功能有详细的了解,只需按照定义的准则协调工作即可,因此,我们也可参照 OSI 七层模型将公用库设计为位于网络栈和应用业务逻辑之间的独立层,即:透明网络代理,新的独立层完全从业务逻辑中抽离,作为独立的运行单元,与业务不再直接紧密关联。通过在独立层的透明网络代理上实现负载均衡、服务发现、熔断、运行时动态路由等功能,这样透明代理现在在业界有一个更加新颖时髦的名字:Service Mesh。率先使用这个 Buzzword 的产品恐怕非 Buoyant 的 linkerd 莫属了,随后 Lyft 也发布了他们的 Service Mesh 实现 Envoy,再有 Istio 也迎面赶上。新的模式如:
在这种方案中,由于 Service Mesh 作为独立运行层,它很好的解决了上述所面临的挑战,使应用具备处理网络弹性逻辑和提供可靠交互请求的能力。它使得耦合性更低、灵活性更强,与现有环境的集成时间和人力代价更小,也提供多语言支持、多协议支持,运维和管理成本更低。最主要的是开发人员只需关注业务代码逻辑,而不是业务代码以外的其他功能,即 Service Mesh 对开发人员是透明的。下面我们将讲述什么是 Service Mesh。
关于 Service Mesh,Buoyant 创始人 William Morgan 如是说:
A service mesh is a dedicated infrastructure layer for handling service-to-service communication. It’s responsible for the reliable delivery of requests through the complex topology of services that comprise a modern, cloud native application. In practice, the service mesh is typically implemented as an array of lightweight network proxies that are deployed alongside application code, without the application needing to be aware.
对此,可总结 Service Mesh 为:
Service Mesh 作为透明代理,它可以运行在任何基础设施环境,而且跟应用非常靠近,那么,Service Mesh 能做什么呢?
当前,业界主要有以下相关产品:
前面我们已经讲述了 Service Mesh 带来的各种好处,解决各种问题,作为下一代微服务的风口,Service Mesh 可以使得快速转向微服务或者云原生应用,以一种自然的机制扩展应用负载,解决分布式系统不可避免的部分失败,捕捉分布式系统动态变化, 完全解耦于应用等等。我相信 Service Mesh 在微服务或者云原生应用领域一定别有一番天地,下一节我们开始讲述 Service Mesh 的一个实现:linkerd。
- Fallacies of distributed computing
- Pattern: Service Mesh
- Fingle
- Proxygen
- Zipkin
- OSI七层模型
- linkerd
- Envoy
- Istio
- What’s a service mesh? And why do I need one?
- Six Strategies for Application Deployment
上一节我们讲述了为什么有 Service Mesh 的出现,什么是 Service Mesh,Service Mesh 解决了什么问题等。从这一节开始我们开始详细介绍 Service Mesh 的一种实现:linkerd。
linkerd 是 Buoyant 开发的快速、轻量级、高性能的,每秒以最小的时延及负载处理万级请求,易于水平扩展,经过产线测试及验证的 Service Mesh 工具,其官方定义为:
linker∙d is a transparent proxy that adds service discovery, routing, failure handling, and visibility to modern software applications.
我们可总结为:
如图所示,linkerd 提供:
1、基于感知时延的负载均衡
Power of Two Choices (P2C)
: Least Loaded
、Power of Two Choices
: Peak EWMA
、Aperture
: Least Loaded
、Heap
: Least Loaded
以及Round-Robin
。2、运行时动态路由
3、熔断机制
4、插入式服务发现
支持各种服务发现机制如:基于文件(File-based)、DNS 以及基于 KV 键值对的 Zookeeper、Consul 及 Kubernetes,可以很方便的接入现有或者新的服务发现工具。
5、指标及分布式追踪
除此之外,linkerd 支持任意开发语言和多种协议如 HTTP/1.1、HTTP/2、gRPC、Thrift、Mux。
关于如何安装 linkerd,我们将介绍以两种不同的方法安装,一种是传统安装方法,即下载 linkerd 的安装包直接安装,另一种是通过 Docker 的方式安装。为了统一实验环境,在安装之前需要准备相关环境依赖:
安装 Virtualbox
本文 Virualbox 的安装是在 Mac 系统上进行的,如果你的工作系统是 Windows 或者Linux,请到 Virtualbox 官方下载对应的安装包,并根据安装指南进行安装,在这里就不详细介绍了。
在 Mac 上安装 Virtualbox 相对简单,打开终端,然后执行下面的命令即可。
$ brew cask install virtualbox
如果没有任何错误,你会看到virtualbox was successfully installed!
,表示安装成功。
安装 Vagrant
同 VirtualBox 一样,如果你的工作系统是 Windows 或者 Linux,请到 Vagrant 官方下载对应的安装包进行安装。同样在 Mac 终端中执行下面的命令安装 Vagrant:
$ brew cask install vagrant
安装完成后,执行如下命令验证 Vagrant 的版本信息。
$ vagrant --versionVagrant 2.0.1
接下来我们开始配置 Vagrantfile 文件,然后从 Vagrant Boxes 下载 CentOS 7.4 镜像,最后根据配置的 Vagrantfile 启动虚机。
切换你的目录下初始化 Vagrantfile,比如在我的环境是/Users/zhanyang/vagrant/linkerd/chapter2
。
$ cd /Users/zhanyang/vagrant/linkerd/chapter2$ vagrant init # 执行完该命令后在/Users/zhanyang/vagrant/linkerd/chapter2目录会产生默认Vagrantfile文件
对于这个默认 Vagrantfile 文件我们做以下调整:
调整后我们移除 Vagrantfile 的其他默认配置,只保留我们需要的,内容如:
# -*- mode: ruby -*-# vi: set ft=ruby :Vagrant.configure("2") do |config| config.vm.box = "centos/7"
# linkerd router on port 4140config.vm.network "forwarded_port", guest: 4140, host: 4140
# linkerd admin on port 9990 config.vm.network "forwarded_port", guest: 9990, host: 9990end
执行以下命令启动虚拟机,初次启动可能时间较长,因为需要下载 CentOS 的镜像文件。
$ vagrant up
然后登录虚拟机:
$ vagrant ssh # 进入CentOS虚拟机$ sudo su - # 切换到root用户# cat /etc/redhat-release # 验证CentOS版本信息CentOS Linux release 7.4.1708 (Core)
安装工具集:
yum install -y wget telnet tree net-tools
安装 OpenJDK
进入已准备好的虚拟机,通过 yum 安装 OpenJDK,不需要额外配置 yum 源,默认的即可安装。
# yum install -y java-1.8.0-openjdk
完成后确认 OpenJDK 版本信息。
# java -versionopenjdk version "1.8.0_151"OpenJDK Runtime Environment (build 1.8.0_151-b12)OpenJDK 64-Bit Server VM (build 25.151-b12, mixed mode)
安装 Docker Engine
当前 CentOS 7.4 官方支持的 Docker 版本是1.12.6,如果你需要安装更新版本的 Docker Engine,请参考 Docker 官方安装指南。
# yum install -y docker
通过docker version
可查看所安装Docker的详细信息:
# docker versionClient: Version: 1.12.6 API version: 1.24 Package version: docker-1.12.6-61.git85d7426.el7.centos.x86_64 Go version: go1.8.3 Git commit: 85d7426/1.12.6 Built: Tue Oct 24 15:40:21 2017 OS/Arch: linux/amd64Cannot connect to the Docker daemon. Is the docker daemon running on this host?
你可发现安装的 Docker Engine 的确是1.12.6,而且 Docker daemon 还没有启动,下面启动 Docker daemon:
# systemctl enable docker # 确保系统重启时Docker自动启动Created symlink from /etc/systemd/system/multi-user.target.wants/docker.service to /usr/lib/systemd/system/docker.service.# systemctl start docker
现在所有环境依赖已经准备好,我们可以在 CentOS 虚机里安装 linkerd,首先创建一个安装目录,比如/root/linkerd/local
,然后下载 linkerd 安装包。
# mkdir /root/linkerd/local# cd /root/linkerd/local# wget https://github.com/linkerd/linkerd/releases/download/1.3.3/linkerd-1.3.3.tgz
然后解压 linkerd 安装包并启动 linkerd。
# tar xzf linkerd-1.3.3.tgz# tree linkerd-1.3.3linkerd-1.3.3├── config│ └── linkerd.yaml├── disco│ ├── thrift-buffered│ ├── thrift-framed│ └── web├── docs│ ├── CHANGES.md│ └── README.md├── linkerd-1.3.3-exec└── logs
linkerd 安装包包括:
首先预览 config 目录下的默认配置 linkerd.yaml,然后将标签为/host/thrift-framed
和/host/thrift-buffered
部分配置删除,新的配置为:
# cd linkerd-1.3.3 # 进入linkerd-1.3.3目录# more config/linkerd.yaml # 预览linkerd.ymladmin: port: 9990# 默认admin使用loopback地址,这里需手动修改成0.0.0.0,为了能从宿主机访问linkerd管理页面 ip: 0.0.0.0namers:- kind: io.l5d.fs rootDir: discorouters:- protocol: http dtab: | /svc => /#/io.l5d.fs; httpAccessLog: logs/access.log label: int servers: - port: 4140 ip: 0.0.0.0
通过配置我们可知:
admin
模块:设置 linkerd 管理接口,默认监听 loopback 地址的9990端口namers
模块:设置如何通过服务发现工具进行服务发现,默认配置为基于文件的服务发现,服务相关信息存储在 disco 目录的文件中,每个文件以服务名字命名,包括一系列主机名字/端口对。routers
模块: 设置服务间通讯协议,默认为http;dtab 路由规则,其根据访问信息如请求主机头从namers
指定的服务发现工具中找到需要访问的服务,默认配置指定从namers
模块配置的文件目录读取信息;日志写入路径,默认 logs 目录下的access.log 文件;配置 router 服务器的监听地址和端口,默认为0.0.0.0的4140端口;其他配置如 router的标签为 int。然后通过 linkerd.yaml 启动 linkerd 进程:
# ./linkerd-1.3.3-exec config/linkerd.yaml &...I 1204 01:19:23.776 UTC THREAD1: linkerd 1.3.3 (rev=9c47a744e433ae35d3a413e3f4448814a744f1a6) built at 20171201-150042I 1204 01:19:24.453 UTC THREAD1: Finagle version 7.1.0 (rev=37212517b530319f4ba08cc7473c8cd8c4b83479) built at 20170906-132024I 1204 01:19:27.031 UTC THREAD1: Tracer: com.twitter.finagle.zipkin.thrift.ScribeZipkinTracerI 1204 01:19:27.067 UTC THREAD1: connecting to usageData proxy at Set(Inet(stats.buoyant.io/104.28.22.233:443,Map()))I 1204 01:19:27.404 UTC THREAD1: serving http admin on /0.0.0.0:9990I 1204 01:19:27.439 UTC THREAD1: serving int on /0.0.0.0:4140I 1204 01:19:27.530 UTC THREAD1: serving /host/thrift-framed on /0.0.0.0:4141I 1204 01:19:27.561 UTC THREAD1: serving /host/thrift-buffered on /0.0.0.0:4142I 1204 01:19:27.570 UTC THREAD1: initialized
从输出信息我们知道:
在宿主机的浏览器访问 http://127.0.0.1:9990 可进入 linkerd 的管理界面,从上面你可以看到一些信息如请求数量、成功率、失败率等,除此之外,还可以从页面进行 dtab(Delegation Table)调测以及调整日志打印级别。
相对传统安装,使用 Docker 安装 linkerd 更加方便,首先我们创建另一个目录/root/linkerd/docker
存放相关配置文件,
mkdir /root/linkerd/dockercd /root/linkerd/docker # 进入Docker安装目录cp -R /root/linkerd/local/linkerd-1.3.3/{config,disco} . 复制传统安装的配置信息以便使用
在启动之前需要对 config 目录下 linkerd.yaml 的namers
模块做调整使 Docker 从/disco
读取服务信息,调整后如:
namers:- kind: io.l5d.fs rootDir: /disco
然后我们启动 linkerd 的容器,使用 host 网络模式是为了方便从宿主机浏览器打开管理页面或者访问通过 linkerd 代理的服务。
# docker run -d --name linkerd --network host -v `pwd`/disco:/disco -v `pwd`/config/linkerd.yaml:/linkerd.yaml buoyantio/linkerd:1.3.3 /linkerd.yaml
为了避免端口冲突,在启动 linkerd 容器之前须停止已启动的 linkerd 进程。
跟传统安装一样,你可以在宿主机的浏览器通过 http://127.0.0.1:9990 打开 linkerd 管理页面。
在完成前面的安装后(无论是传统安装还是基于 Docker 的安装),我们使用一个简单的应用程序 helloworld 来验证是否 linkerd 将应用请求转发给后端实例,helloworld 程序在后面的文章也将被使用到。
部署应用 helloworld
helloworld 是一个参考 linkerd 官方helloworld例子改写的简单程序,可以返回服务名字和 IP 地址以及服务监听端口。你可以通过运行下面命令查看帮助:
# docker run --rm docker.io/zhanyang/helloworld:1.0 --helpUsage of /usr/local/bin/helloworld:-addr string address to serve on (default ":7777")-failure-rate float rate of error responses to return-json return JSON instead of plaintext responses (HTTP only)-latency duration time to sleep before processing request-protocol string API protocol: http or grpc (default "http")-target string target service to call before returning-text string service to serve (default "HelloWorld")
通过addr
选项或者环境变量 PORT 可以覆盖默认运行端口7777,而环境变量 IP 指定容器运行的宿主机 IP 地址,text
选项指定服务名称,默认 HelloWorld。如果target
选项指定之后,在服务返回之前先调用target
指定的服务,将target
服务返回的信息和当前服务返回的信息连接后输出。
下面我们启动2个 helloworld 实例:
# docker run -d --network host --env IP=0.0.0.0 docker.io/zhanyang/helloworld:1.0 # 第一个实例,以默认7777端口启动# curl localhost:7777 # 在虚机上执行,返回helloworld运行的IP地址和端口信息HelloWorld (0.0.0.0:7777)!# docker run -d --network host --env IP=0.0.0.0 --env PORT=8888 docker.io/zhanyang/helloworld:1.0 # 第二个实例,以8888端口启动
配置 linkerd 使其代理应用请求
在 dico 目录下新建一文件helloworld
,无论是 /root/linkerd/docker 还是 /root/linkerd/local 目录的 dico 都可以,然后在helloworld
文件添加两条记录,即正在运行的2个 helloworld 实例 IP 地址和端口:
0.0.0.0 77770.0.0.0 8888
由于 linkerd 在发现 disco 目录更新后自动加载新的配置,因此无须重启 linkerd 服务便生效,如果你使用 Docker 的方式安装 linkerd,最好重启下 linkerd 服务,如 issue 所描述一样,可能有些时候新的记录不能被加载,因此建议不要在产线使用基于文件的服务发现方式。然后通过 curl 命令验证 linkerd 是否可以成功地将应用求转发到实际后端实例。
# curl -H "Host: helloworld" localhost:4140 # 跟直接访问helloworld返回一样的信息,该命令可以在宿主机和虚机执行都可以,因为我们在Vagrantfile已配置转发规则
基于文件的服务发现还可能导致消耗大量的CPU,不建议产线使用这种方式。
执行该命令后可能得到输出HelloWorld (0.0.0.0:8888)!
或者HelloWorld (0.0.0.0:7777)!
,不同的结果是linkerd基于它的负载均衡算法导致的。
由运行结果可知 linkerd 很好的将应用请求代理到后端实例,并且其基于文件的服务发现能将请求代理到不同的运行实例。
这一节我们主要介绍了 linkerd 的基本功能、如何以不同的方式安装 linkerd,最后通过一个简单的应用程序验证 linkerd 将应用请求转发给后端运行实例。
阅读全文: http://gitbook.cn/gitchat/column/5a3740d7d7fd1364997391bf