Service Mesh 现在如此火热,你了解多少?

作者:杨章显,思科高级系统工程师,多年企业级在线会议系统的运维以及软件发布、变更管理经验,熟悉容器技术、容器编排、自动化运维、部署、监控等,目前为思科内部容器 PaaS 主要负责人,负责技术选型、实现、落地等工作。

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 可以提供简单、统一、平台无关的多种服务发现机制,如基于 DNS,K/V 键值对存储的服务发现机制。

  • 熔断:动态的环境中服务实例中断或者不健康导致服务中断可能会经常发生,这就要求应用或者其他工具具有快速监测并从负载均衡池中移除不提供服务实例的能力,这种能力也称熔断,以此使得应用无需消耗更多不必要的资源不断地尝试,而是快速失败或者降级,甚至这样可避免一些潜在的关联性错误。而 Service Mesh 可以很容易实现基于请求和连接级别的熔断机制。

  • 动态路由:随着服务提供商以提供高稳定性、高可用性以及高 SLA 服务为主要目标,为了实现所述目标,出现各种应用部署策略尽可能从技术手段达到无服务中断部署,以此避免变更导致服务的中断和稳定性降低,例如:Blue/Green 部署、Canary 部署,但是实现这些高级部署策略通常非常困难。关于应用部署策略,可参考 Etienne Tremel
    的文章,他对各种部署策略做了详细的比较。而如果运维人员可以轻松的将应用流量从 staging 环境到产线环境,一个版本到另外一个版本,更或者从一个数据中心到另外一个数据中心进行动态切换,甚至可以通过一个中心控制层控制多少比例的流量被切换。那么 Service Mesh 提供的动态路由机制和特定的部署策略如 Blue/Green 部署结合起来,实现上述目标更加容易。

  • 安全通讯:无论何时,安全在整个公司、业务系统中都有着举足轻重的位置,也是非常难以实现和控制的部分。而微服务环境中,不同的服务实例间通讯变得更加复杂,那么如何保证这些通讯是在安全、授权情况下进行非常重要。通过将安全机制如 TLS 加解密和授权实现在 Service Mesh 上,不仅可以避免在不同应用的重复实现,而且很容易在整个基础设施层更新安全机制,甚至无需对应用做任何操作。

  • 多语言支持:由于 Service Mesh 作为独立运行的透明代理,很容易支持多语言。

  • 多协议支持:同多语言支持一样,实现多协议支持也非常容易。

  • 指标和分布式追踪:Service Mesh 对整个基础设施层的可见性使得它不仅可以暴露单个服务的运行指标,而且可以暴露整个集群的运行指标。

  • 重试和最后期限:Service Mesh 的重试功能避免将其嵌入到业务代码,同时最后期限使得应用允许一个请求的最长生命周期,而不是无休止的重试。

业界 Service Mesh 产品

当前,业界主要有以下相关产品:

  • Buoyant 的 linkerd,基于 Twitter 的 Fingle,长期的实际产线运行经验及验证,支持 Kubernetes,DC/OS 容器管理平台,CNCF 官方支持的项目之一。

  • Lyft 的 Envoy,7层代理及通信总线,支持7层 HTTP 路由、TLS、gRPC、服务发现以及健康监测等,也是 CNCF 官方支持项目之一。

  • IBM、Google、Lyft 支持的 Istio,一个开源的微服务连接、管理平台以及给微服务提供安全管理,支持 Kubernetes、Mesos 等容器管理工具,其底层依赖于 Envoy。

我们需要 Service Mesh 吗?

前面我们已经讲述了 Service Mesh 带来的各种好处,解决各种问题,作为下一代微服务的风口,Service Mesh 可以使得快速转向微服务或者云原生应用,以一种自然的机制扩展应用负载,解决分布式系统不可避免的部分失败,捕捉分布式系统动态变化, 完全解耦于应用等等。

我相信 Service Mesh 在微服务或者云原生应用领域一定别有一番天地,下面我们开始讲述 Service Mesh 的一个实现:linkerd。

什么是 linkerd?

linkerd 是 Buoyant 开发的快速、轻量级、高性能的,每秒以最小的时延及负载处理万级请求,易于水平扩展,经过产线测试及验证的 Service Mesh 工具,其官方定义为:

linker∙d is a transparent proxy that adds service discovery, routing, failure handling, and visibility to modern software applications.

我们可总结为:

  • 为云原生应用提供弹性的 Service Mesh。

  • 透明高性能网络代理。

  • 提供服务发现机制、动态路由、错误处理机制及应用运行时可视化。

linkerd 主要功能

Service Mesh 现在如此火热,你了解多少?_第1张图片

如图所示,linkerd 提供;

1、基于感知时延的负载均衡

  • 由于 linkerd 工作于 RPC 层,它能实时观测到所有 RPC 请求延时、队列里即将要处理请求的数量,因此可基于实时性能数据分发请求,相对于传统启发式负载均衡算法如 LRU、TCP 活动情况等,这种分发机制性能更优,可尽可能降低延时,提高稳定性。
  • 提供多种负载均衡算法如: Power of Two Choices (P2C): Least LoadedPower of Two Choices: Peak EWMAAperture: Least LoadedHeap: Least Loaded以及Round-Robin

2、运行时动态路由

  • 支持基于请求级别路由,可在 HTTP 请求里嵌入特定包头,linkerd 将匹配该包头信息的请求路由到特定的应用实例。
  • 动态修改路由规则实现流量迁移、Blue/Green 部署、Canary 部署、跨数据中心 failover 等。

3、熔断机制

  • 快速失败(Fail Fast):基于连接的熔断器,如果 linkerd 观测到访问某个应用实例时有连接错误,linkerd 将该实例从维护的连接池移除,与此同时,linkerd 在后台不断尝试与移除的实例建立连接,一旦确认已经恢复,linkerd 重新将其加入连接池。
  • 失败计提(Failure Accrual):基于请求的熔断器,如果 linkerd 观测到访问某个应用实例时,指定数目请求连续失败,linkerd 将该实例标注为死亡状态,不再接受新的请求尝试。而在后台,linkerd 基于设置的退避间隔发送请求以验证实例是否恢复,一旦恢复,便可接受新的请求。

4、插入式服务发现

支持各种服务发现机制如:基于文件(File-based)、DNS 以及基于 KV 键值对的 Zookeeper、Consul 及 Kubernetes,可以很方便的接入现有或者新的服务发现工具。

5、指标及分布式追踪

  • 作为透明代理使 linkerd 第一时间知道从单个到整个集群应用实例的运行时指标。
  • 除此之外,也可以跟各种分布式追踪系统集成,如 Zipkin,无需对应用做任何调整。

除此之外,linkerd 支持任意开发语言和多种协议如 HTTP/1.1、HTTP/2、gRPC、Thrift、Mux。

安装 linkerd

关于如何安装 linkerd,我们将介绍以两种不同的方法安装,一种是传统安装方法,即下载 linkerd 的安装包直接安装,另一种是通过 Docker 的方式安装。为了统一实验环境,在安装之前需要准备相关环境依赖:

  • Virtualbox 5.2.2
  • CentOS 7.4
  • Vagrant 2.0.1
  • OpenJDK 8
  • Docker Engine 1.12.6
  • 工具集:wget,telnet、tree 和 net-tools

准备实验环境

  1. 安装 Virtualbox

    本文 Virualbox 的安装是在 Mac 系统上进行的,如果你的工作系统是 Windows 或者
    Linux,请到 Virtualbox 官方下载对应的安装包,并根据安装指南进行安装,在这里就不详细介绍了。

    在 Mac 上安装 Virtualbox 相对简单,打开终端,然后执行下面的命令即可。

    $ brew cask install virtualbox

    如果没有任何错误,你会看到virtualbox was successfully installed!,表示安装成功。

  2. 安装 Vagrant

    同 VirtualBox 一样,如果你的工作系统是 Windows 或者 Linux,请到 Vagrant 官方下载对应的安装包进行安装。同样在 Mac 终端中执行下面的命令安装 Vagrant:

    $ brew cask install vagrant

    安装完成后,执行如下命令验证 Vagrant 的版本信息。

    $ vagrant --version
    Vagrant 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 文件我们做以下调整:

    • 设置 box 为 centos/7。
    • 配置 linkerd 转发端口,使得可以从本机访问运行在 VirtualBox 虚机里 linkerd 的管理页面。

    调整后我们移除 Vagrantfile 的其他默认配置,只保留我们需要的,内容如:

    
    # -*- mode: ruby -*-
    
    
    # vi: set ft=ruby :
    
    
    Vagrant.configure("2") do |config|
      config.vm.box = "centos/7"
    
      # linkerd router on port 4140
      config.vm.network "forwarded_port", guest: 4140, host: 4140
      # linkerd admin on port 9990
      config.vm.network "forwarded_port", guest: 9990, host: 9990
    end

    执行以下命令启动虚拟机,初次启动可能时间较长,因为需要下载 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
  3. 安装 OpenJDK

    进入已准备好的虚拟机,通过 yum 安装 OpenJDK,不需要额外配置 yum 源,默认的即可安装。

    
    # yum install -y java-1.8.0-openjdk
    

    完成后确认 OpenJDK 版本信息。

    
    # java -version
    
    openjdk 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)
  4. 安装 Docker Engine

    当前 CentOS 7.4 官方支持的 Docker 版本是1.12.6,如果你需要安装更新版本的 Docker Engine,请参考 Docker 官方安装指南。

    
    # yum install -y docker
    

    通过docker version可查看所安装Docker的详细信息:

    
    # docker version
    
    Client:
    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/amd64
    Cannot 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
    

传统安装 linkerd

现在所有环境依赖已经准备好,我们可以在 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.3
linkerd-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 配置文件存放于该目录
  • disco 目录:如果采用基于文件方式进行服务发现,相关配置信息存于该目录
  • logs 目录:默认 linkerd 将所产生的日志写于该目录
  • linkerd 可执行文件
  • docs 文档目录

首先预览 config 目录下的默认配置 linkerd.yaml,然后将标签为/host/thrift-framed/host/thrift-buffered部分配置删除,新的配置为:

# cd linkerd-1.3.3 # 进入linkerd-1.3.3目录
# more config/linkerd.yaml # 预览linkerd.yml
admin:
  port: 9990
# 默认admin使用loopback地址,这里需手动修改成0.0.0.0,为了能从宿主机访问linkerd管理页面
  ip: 0.0.0.0

namers:
- kind: io.l5d.fs
  rootDir: disco

routers:
- 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-150042
I 1204 01:19:24.453 UTC THREAD1: Finagle version 7.1.0 (rev=37212517b530319f4ba08cc7473c8cd8c4b83479) built at 20170906-132024
I 1204 01:19:27.031 UTC THREAD1: Tracer: com.twitter.finagle.zipkin.thrift.ScribeZipkinTracer
I 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:9990
I 1204 01:19:27.439 UTC THREAD1: serving int on /0.0.0.0:4140
I 1204 01:19:27.530 UTC THREAD1: serving /host/thrift-framed on /0.0.0.0:4141
I 1204 01:19:27.561 UTC THREAD1: serving /host/thrift-buffered on /0.0.0.0:4142
I 1204 01:19:27.570 UTC THREAD1: initialized

从输出信息我们知道:

  • linkerd 的版本是1.3.3
  • Finagle 的版本是7.1.0(linkerd 底层依赖Finagle)
  • linkerd admin 运行于9990端口,可通过0.0.0.0:9990进行访问
  • linkerd 的 http router 运行于4140口,通过0.0.0.0:4140访问

在宿主机的浏览器访问 http://127.0.0.1:9990 可进入 linkerd 的管理界面,从上面你可以看到一些信息如请求数量、成功率、失败率等,除此之外,还可以从页面进行 dtab(Delegation Table)调测以及调整日志打印级别。

Service Mesh 现在如此火热,你了解多少?_第2张图片

基于 Docker 的安装

相对传统安装,使用 Docker 安装 linkerd 更加方便,首先我们创建另一个目录/root/linkerd/docker存放相关配置文件,

mkdir /root/linkerd/docker
cd /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 程序在后面的文章也将被使用到。

  1. 部署应用 helloworld

    helloworld 是一个参考 linkerd 官方helloworld
    例子改写的简单程序,可以返回服务名字和 IP 地址以及服务监听端口。你可以通过运行下面命令查看帮助:

    
    # docker run --rm docker.io/zhanyang/helloworld:1.0 --help
    
    Usage 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端口启动
    
  2. 配置 linkerd 使其代理应用请求

    在 dico 目录下新建一文件helloworld,无论是 /root/linkerd/docker 还是 /root/linkerd/local 目录的 dico 都可以,然后在helloworld文件添加两条记录,即正在运行的2个 helloworld 实例 IP 地址和端口:

    0.0.0.0 7777
    0.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 将应用请求转发给后端运行实例。

来自 思科高级系统研发工程师 · 杨章显 的达人课:《Service Mesh 入门实战》

Service Mesh 现在如此火热,你了解多少?_第3张图片

在 Service Mesh 入门实战 中,我将以实例为基础详细介绍 Service Mesh 的主要实现 linkerd。

其中包括:

  • 深入了解 linkerd 的主要功能;
  • 如何安装部署 linkerd;
  • 深入理解 linkerd 配置,dtab 路由规则;
  • linkerd 工作机制、数据访问流、部署模型等。

认真学习完这个课程之后,你将获得或者具备以下能力:从0到1理解 linkerd 的工作机制;具备通过 linkerd 构建弹性的、高可见性的、可追踪的云原生应用。

你可能感兴趣的:(运维)