28

使用Docker Compose搭建Service Mesh

 3 years ago
source link: http://dockone.io/article/10440
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

本文将介绍如何使用Docker Compose搭建Istio。Istio号称支持多种平台(不仅仅Kubernetes)。然而,官网上非基于Kubernetes的教程仿佛不是亲儿子,写得非常随便,不仅缺了一些内容,而且还有坑。本文希望能补实这些内容。我认为在学习Istio的过程中,相比于Kubernetes,使用Docker Compose部署更能深刻地理解Istio各个组件的用处以及他们的交互关系。在理解了这些后,可以在其他环境,甚至直接在虚拟机上部署Istio。当然,生产环境建议使用Kubernetes等成熟的容器框架。

本文使用官方的 Bookinfo示例 。通过搭建Istio控制平面,部署Bookinfo应用,最后配置路由规则,展示Istio基本的功能和架构原理。

本文涉及的名词、用到的端口比较多。Don't panic。

准备工作

  • 安装Docker和Docker Compose。
  • 安装kubectl(Kubernetes的客户端)。
  • 下载Istio release 1.1.0并解压。注意,这里下载的是Linux的版本。即使你用的Windows或者OSX操作系统,也应该下载Linux版本的Istio,因为我们要放到Docker容器里去运行的。

Service Mesh架构

在微服务架构中,通常除了实现业务功能的微服务外,我们还会部署一系列的基础组件。这些基础组件有些会入侵微服务的代码。比如服务发现需要微服务启动时注册自己,链路跟踪需要在HTTP请求的headers中插入数据,流量控制需要一整套控制流量的逻辑等。这些入侵的代码需要在所有的微服务中保持一致。这导致了开发和管理上的一些难题。

为了解决这个问题,我们再次应用抽象和服务化的思想,将这些需要入侵的功能抽象出来,作为一个独立的服务。这个独立的服务被称为sidecar,这种模式叫sidecar模式。对每个微服务节点,都需要额外部署一个sidecar来负责业务逻辑外的公共功能。所有的出站入站的网络流量都会先经过sidecar进行各种处理或者转发。这样微服务的开发就不需要考虑业务逻辑外的问题。另外所有的sidecar都是一样的,只需要部署的时候使用合适的编排工具即可方便地为所有节点注入sidecar。

Sidecar不会产生额外网络成本。Sidecar会和微服务节点部署在同一台主机上并且共用相同的虚拟网卡。所以sidecar和微服务节点的通信实际上都只是通过内存拷贝实现的。

M32e6re.png!web

图片来自: Pattern: Service Mesh

Sidecar只负责网络通信。还需要有个组件来统一管理所有sidecar的配置。在Service Mesh中,负责网络通信的部分叫数据平面(data plane),负责配置管理的部分叫控制平面(control plane)。数据平面和控制平面构成了Service Mesh的基本架构。

Avi6ZvQ.png!web

图片来自: Pattern: Service Mesh

Istio的数据平面主要由Envoy实现,控制平面则主要由Istio的Pilot组件实现。

部署控制平面

如果你使用Linux操作系统,需要先配置DOCKER_GATEWAY环境变量。非Linux系统不要配。

$ export DOCKER_GATEWAY=172.28.0.1:

到install/consul目录下,使用istio.yaml文件启动控制平面:

根据自己的网络情况(你懂得),可以把istio.yaml中的镜像gcr.io/google_containers/kube-apiserver-amd64:v1.7.3换成mirrorgooglecontainers/kube-apiserver-amd64:v1.7.3。

$ docker-compose -f istio.yaml up -d

用命令docker-compose -f istio.yaml ps看一下是不是所有组件正常运行。你可能(大概率)会看到pilot的状态是Exit 255。使用命令docker-compose -f istio.yaml logs | grep pilot查看日志发现,pilot启动时访问istio-apiserver失败。这是因为Docker Compose是同时启动所有容器的,在pilot启动时,istio-apiserver也是处于启动状态,所以访问istio-apiserver就失败了。

等istio-apiserver启动完成后,重新运行启动命令就能成功启动pilot了。你也可以写一个脚本来自动跑两次命令:

docker-compose -f istio.yaml up -d

# 有些依赖别人的第一次启动会挂

sec=10  # 根据你的机器性能这个时间可以修改

echo "Wait $sec seconds..."

sleep $sec

docker-compose -f istio.yaml up -d

docker-compose -f istio.yaml ps

配置kubectl,让kubectl使用我们刚刚部署的istio-apiserver作为服务端。我们后面会使用kubectl来执行配置管理的操作。

$ kubectl config set-context istio --cluster=istio

$ kubectl config set-cluster istio --server=http://localhost:8080

$ kubectl config use-context istio

部署完成后,使用地址localhost:8500可以访问consul,使用地址localhost:9411可以访问zipkin。

控制平面架构

在下一步之前,我们先来看一下控制平面都由哪些组件组成。下面是istio.yaml文件的内容:

# GENERATED FILE. Use with Docker-Compose and consul

# TO UPDATE, modify files in install/consul/templates and run install/updateVersion.sh

version: '2'

services:

etcd:

image: quay.io/coreos/etcd:latest

networks:

  istiomesh:

    aliases:

      - etcd

ports:

  - "4001:4001"

  - "2380:2380"

  - "2379:2379"

environment:

  - SERVICE_IGNORE=1

command: ["/usr/local/bin/etcd", "-advertise-client-urls=http://0.0.0.0:2379", "-listen-client-urls=http://0.0.0.0:2379"]



istio-apiserver:

# 如果这个镜像下载不了的话,可以换成下面的地址:

# image: mirrorgooglecontainers/kube-apiserver-amd64:v1.7.3

image: gcr.io/google_containers/kube-apiserver-amd64:v1.7.3

networks:

  istiomesh:

    ipv4_address: 172.28.0.13

    aliases:

      - apiserver

ports:

  - "8080:8080"

privileged: true

environment:

  - SERVICE_IGNORE=1

command: ["kube-apiserver", "--etcd-servers", "http://etcd:2379", "--service-cluster-ip-range", "10.99.0.0/16", "--insecure-port", "8080", "-v", "2", "--insecure-bind-address", "0.0.0.0"]



consul:

image: consul:1.3.0

networks:

  istiomesh:

    aliases:

      - consul

ports:

  - "8500:8500"

  - "${DOCKER_GATEWAY}53:8600/udp"

  - "8400:8400"

  - "8502:8502"

environment:

  - SERVICE_IGNORE=1

  - DNS_RESOLVES=consul

  - DNS_PORT=8600

  - CONSUL_DATA_DIR=/consul/data

  - CONSUL_CONFIG_DIR=/consul/config

entrypoint:

  - "docker-entrypoint.sh"

command: ["agent", "-bootstrap", "-server", "-ui",

          "-grpc-port", "8502"

          ]

volumes:

  - ./consul_config:/consul/config



registrator:

image: gliderlabs/registrator:latest

networks:

  istiomesh:

volumes:

  - /var/run/docker.sock:/tmp/docker.sock

command: ["-internal", "-retry-attempts=-1", "consul://consul:8500"]



pilot:

image: docker.io/istio/pilot:1.1.0

networks:

  istiomesh:

    aliases:

      - istio-pilot

expose:

  - "15003"

  - "15005"

  - "15007"

ports:

  - "8081:15007"

command: ["discovery",

          "--httpAddr", ":15007",

          "--registries", "Consul",

          "--consulserverURL", "http://consul:8500",

          "--kubeconfig", "/etc/istio/config/kubeconfig",

          "--secureGrpcAddr", "",

          ]

volumes:

  - ./kubeconfig:/etc/istio/config/kubeconfig



zipkin:

image: docker.io/openzipkin/zipkin:2.7

networks:

  istiomesh:

    aliases:

      - zipkin

ports:

  - "9411:9411"



networks:

istiomesh:

ipam:

  driver: default

  config:

    - subnet: 172.28.0.0/16

      gateway: 172.28.0.1

控制平面部署了这几个组件(使用istio.yaml里写的名称):

  • etcd:分布式key-value存储。Istio的配置信息存在这里。
  • istio-apiserver:实际上是一个kube-apiserver,提供了Kubernetes格式数据的读写接口。
  • Consul:服务发现。
  • Registrator:监听Docker服务进程,自动将容器注册到Consul。
  • Pilot:从consul和istio-apiserver收集主机信息与配置数据,并下发到所有的sidecar。
  • Zipkin:链路跟踪组件。与其他组件的关系相对独立。

这些组件间的关系如下图:

vUNFjqi.png!web

控制平面主要实现了以下两个功能:

  • 借用Kubernetes API管理配置数据。etcd和kube-apiserver的组合可以看作是一个对象存储系统,它提供了读写接口和变更事件,并且可以直接使用kubectl作为客户端方便地进行操作。Istio直接使用这个组合作为控制平面的持久化层,节省了重复开发的麻烦,另外也兼容了Kubernetes容器框架。
  • 使用Pilot-discovery将主机信息与配置数据同步到Envoy。pilot容器中实际执行的是pilot-discovery(发现服务)。它从consul收集各个主机的域名和IP的对应关系,从istio-apiserver获取流量控制配置,然后按照Envoy的xDS API规范生成Envoy配置,下发到所有sidecar。

部署微服务和sidecar

接下来我们开始部署微服务。这里我们使用Istio提供的例子,一个Bookinfo应用。

Bookinfo 应用分为四个单独的微服务:

  • productpage:productpage微服务会调用details和reviews两个微服务,用来生成页面。
  • details:这个微服务包含了书籍的信息。
  • reviews:这个微服务包含了书籍相关的评论。它还会调用ratings微服务。
  • ratings:ratings微服务中包含了由书籍评价组成的评级信息。

reviews微服务有3个版本:

  • v1版本不会调用ratings服务。
  • v2版本会调用ratings服务,并使用1到5个黑色星形图标来显示评分信息。
  • v3版本会调用ratings服务,并使用1到5个红色星形图标来显示评分信息。

Bookinfo应用的架构如下图所示:

NbumUv6.png!web

图片来自: Bookinfo应用

首先,我们切换到这个示例的目录samples/bookinfo/platform/consul下。

使用bookinfo.yaml文件启动所有微服务:

$ docker-compose -f bookinfo.yaml up -d

这里只启动了微服务,还需使用bookinfo.sidecar.yaml文件启动所有sidecar:

$ docker-compose -f bookinfo.sidecars.yaml up -d

部署完毕。但是当我们访问时……

Bookinfo暴露到外面的端口是9081,使用地址localhost:9081/productpage访问productpage页面。

Emmm……出错了:

2UBbEjm.png!web

本来应该显示reviews的部分报错了,而details还是正常的。经过一番排查,我们发现,在所有微服务的容器上,不管你访问的是productpage、details、reviews还是ratings,网络请求都会跑到details。

你的情况不一定是details,也有可能所有流量都跑到另外的某个服务。这是随机的。

# 到reviews查reviews,返回404

$ docker exec -it consul_ratings-v1_1 curl reviews.service.consul:9080/reviews/0

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">

<HTML>

<HEAD><TITLE>Not Found</TITLE></HEAD>

<BODY>

<H1>Not Found</H1>

`/reviews/0' not found.

<HR>

<ADDRESS>

 WEBrick/1.3.1 (Ruby/2.3.8/2018-10-18) at

 reviews.service.consul:9080

</ADDRESS>

</BODY>

</HTML>



# 到reviews查details,反倒能查出数据,诡异的路由……

$ docker exec -it consul_ratings-v1_1 curl reviews.service.consul:9080/details/0

{"id":0,"author":"William Shakespeare","year":1595,"type":"paperback","pages":200,"publisher":"PublisherA","language":"English","ISBN-10":"1234567890","ISBN-13":"123-1234567890"} 

不用怀疑部署的时候哪里操作失误了,就是官方的部署文件有坑……

要解决这个问题,我们来看看sidecar的原理。

Istio Sidecar模式的原理

首先看看两个部署用的yaml文件都做了什么。由于每个微服务的部署都大同小异,这里只贴出productpage相关的内容。

bookinfo.yaml:

version: '2'

services:

……

productpage-v1:

image: istio/examples-bookinfo-productpage-v1:1.10.1

networks:

  istiomesh:

    ipv4_address: 172.28.0.14

dns:

  - 172.28.0.1

  - 8.8.8.8

dns_search:

    - service.consul

environment:

  - SERVICE_NAME=productpage

  - SERVICE_TAGS=version|v1

  - SERVICE_PROTOCOL=http

ports:

  - "9081:9080"

expose:

  - "9080"

……
  • dns_search: - search.consul。Docker Compose部署的这套样例对短服务主机名的解析可能会有问题,所以这里需要加个后缀。
  • environment环境变量的几个设置。registrator会以这些环境变量为配置将服务注册到consul。SERVICE_NAME是注册的服务名,SERVICE_TAGS是注册服务的ServiceTags,而SERVICE_PROTOCOL=http则会将protocol: http加入到ServiceMeta。

bookinfo.sidecar.yaml:

version: '2'

services:

……

productpage-v1-init:

image: docker.io/istio/proxy_init:0.7.1

cap_add:

  - NET_ADMIN

network_mode: "container:consul_productpage-v1_1"

command:

  - -p

  - "15001"

  - -u

  - "1337"

productpage-v1-sidecar:

image: docker.io/istio/proxy_debug:1.1.0

network_mode: "container:consul_productpage-v1_1"

entrypoint:

  - su

  - istio-proxy

  - -c

  - "/usr/local/bin/pilot-agent proxy --serviceregistry Consul --serviceCluster productpage-v1 --zipkinAddress zipkin:9411 --configPath /var/lib/istio >/tmp/envoy.log"

……
  • sidecar由两部分组成,一个是负责初始化的proxy_init,这个容器执行完就退出了;另一个是实际的sidecar程序proxy_debug。
  • 注意这两个容器的network_mode,值为container:consul_productpage-v1_1。这是Docker的容器网络模式,意思是这两个容器和productpage-v1共用同一个虚拟网卡,即它们在相同网络栈上。

proxy_init

sidecar的网络代理一般是将一个端口转发到另一个端口。所以微服务使用的端口就必须和对外暴露的端口不一样,这样一来sidecar就不够透明。

为了使sidecar变得透明,以Istio使用proxy_init设置了iptables的转发规则(proxy_init、proxy_debug和productpage-v1在相同的网络栈上,所以这个配置对这三个容器都生效)。添加的规则为:

  1. 回环网络的流量不处理。
  2. 用户ID为1337的流量不处理。1337是Envoy进程的用户ID,这条规则是为了防止流量转发死循环。
  3. 所有出站入站的流量除了规则1和规则2外,都转发到15001端口——这是Envoy监听的端口。

比如productpage服务使用的9080端口,当其他服务通过9080端口访问productpage是,请求会先被iptables转发到15001端口,Envoy再根据路由规则转发到9080端口。这样访问9080的流量实际上都在15001绕了一圈,但是对外部来说,这个过程是透明的。

RRnM3e7.png!web

proxy_debug

proxy_debug有两个进程:pilot-agent和envoy。proxy_debug启动时,会先启动pilot-agent。pilot-agent做的事很简单,它生成了envoy的初始配置文件/var/lib/istio/envoy-rev0.json,然后启动envoy。后面的事就都交给envoy了。

使用下面命令导出初始配置文件:

$ docker exec -it consul_productpage-v1-sidecar_1 cat /var/lib/istio/envoy-rev0.json > envoy-rev0.json

使用你心爱的编辑器打开初始配置文件,可以看到有这么一段:

……

    "name": "xds-grpc",

    "type": "STRICT_DNS",

    "connect_timeout": "10s",

    "lb_policy": "ROUND_ROBIN",



    "hosts": [

      {

        "socket_address": {"address": "istio-pilot", "port_value": 15010}

      }

    ],

……

这一段的意思是envoy会连接到pilot(控制平面的组件,忘记了请往上翻翻)的15010端口。这俩将按照xDS的API规范,使用GRPC协议实时同步配置数据。

xDS是Envoy约定的一系列发现服务(Discovery Service)的统称。如CDS(Cluster Discovery Service),EDS(Endpoint Discovery Service),RDS(Route Discovery Service)等。Envoy动态配置需要从实现了xDS规范的接口(比如这里的pilot-discovery)获取配置数据。

总结一下,Envoy配置初始化流程为:

i2aMN3I.png!web

图片来自: Istio流量管理实现机制深度解析

那么说Envoy实际使用的路由配置并不在初始配置文件中,而是pilot生成并推送过来的。如何查看Envoy的当前配置呢?还好Envoy暴露了一个管理端口15000:

$ docker exec -it consul_productpage-v1-sidecar_1 curl localhost:15000/help

admin commands are:

/: Admin home page

/certs: print certs on machine

/clusters: upstream cluster status

/config_dump: dump current Envoy configs (experimental)

/contention: dump current Envoy mutex contention stats (if enabled)

/cpuprofiler: enable/disable the CPU profiler

/healthcheck/fail: cause the server to fail health checks

/healthcheck/ok: cause the server to pass health checks

/help: print out list of admin commands

/hot_restart_version: print the hot restart compatibility version

/listeners: print listener addresses

/logging: query/change logging levels

/memory: print current allocation/heap usage

/quitquitquit: exit the server

/reset_counters: reset all counters to zero

/runtime: print runtime values

/runtime_modify: modify runtime values

/server_info: print server version/status information

/stats: print server stats

/stats/prometheus: print server stats in prometheus format

我们可以通过/config_dump接口导出Envoy的当前配置:

$ docker exec -it consul_productpage-v1-sidecar_1 curl localhost:15000/config_dump > envoy.json

打开这个配置,看到这么一段:

……

 "listener": {

  "name": "0.0.0.0_9080",

  "address": {

   "socket_address": {

    "address": "0.0.0.0",

    "port_value": 9080

   }

  },

  "filter_chains": [

   {

    "filters": [

     {

      "name": "envoy.tcp_proxy",

      "typed_config": {

       "@type": "type.googleapis.com/envoy.config.filter.network.tcp_proxy.v2.TcpProxy",

       "stat_prefix": "outbound|9080||details.service.consul",

       "cluster": "outbound|9080||details.service.consul",

……

猜一下也能知道,这一段的意思是,访问目标地址9080端口的出站流量,都会被路由到details。太坑了!

解决问题

从上面原理分析可知,这个问题的根源应该在于pilot给Envoy生成的配置不正确。

查看pilot源码得知,pilot在生成配置时,用一个map保存Listener信息。这个map的key为<ip>:<port>。如果服务注册的时候,没有指明端口<port>上的协议的话,默认认为TCP协议。pilot会将这个Listener和路由写入到这个map,并拒绝其他相同地址端口再来监听。于是只有第一个注册的服务的路由会生效,所有流量都会走到那个服务。如果这个端口有指定使用HTTP协议的话,Pilot-discovery这里生成的是一个RDS的监听,这个RDS则根据域名路由到正确的地址。

简单说就是所有微服务在注册到consul时应该在ServiceMeta中说明自己9080端口的协议是http。

等等,前面的bookinfo.yaml配置里,有指定9080端口的协议是了呀。我们访问一下consul的接口看下ServiceMeta是写入了没有:

7N3UB3r.png!web

果然没有……看来Registrator注册的时候出了岔子。网上搜了下,确实有Issue提到了这个问题:gliderlabs/registrator#633。istio.yaml中使用的latest版本的Registrator不支持写入Consul的ServiceMeta。应该改为master版本。

修改一下istio.yaml配置。按照部署倒叙关闭sidecar、微服务,重新启动控制平面,等registrator启动完毕后,重新部署微服务和sidecar。

# /samples/bookinfo/platform/consul

$ docker-compose -f bookinfo.sidecars.yaml down

$ docker-compose -f bookinfo.yaml down

# /install/consul

$ docker-compose -f istio.yaml up -d

# /samples/bookinfo/platform/consul

$ docker-compose -f bookinfo.yaml up -d

$ docker-compose -f bookinfo.sidecars.yaml up -d

再访问Consul的接口试试,有了(没有的话可能是Registrator没启动好导致没注册到Consul,再新部署下微服务和sidecar):

FvQnQbB.png!web

再访问页面,OK了。目前没有配置路由规则,reviews的版本是随机的。多刷新几次页面,可以看到打星在“没有星星”、“黑色星星”和“红色星星”三种效果间随机切换。

使用地址 http://localhost:9411 能访问Zipkin链路跟踪系统,查看微服务请求链路调用情况。

我们来看看正确的配置是什么内容。再取出Envoy的配置,0.0.0.0_9080的Listener内容变为:

……

 "listener": {

  "name": "0.0.0.0_9080",

  "address": {

   "socket_address": {

    "address": "0.0.0.0",

    "port_value": 9080

   }

  },

  "filter_chains": [

   {

    "filters": [

     {

      "name": "envoy.http_connection_manager",

      "typed_config": {

       "@type": "type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager",

       "stat_prefix": "0.0.0.0_9080",

       "rds": {

        "config_source": {

         "ads": {}

        },

        "route_config_name": "9080"

       },

……

9080端口的出站路由规则由一个名称为"9080"的route_config定义。找一下这个route_config:

……

 "route_config": {

  "name": "9080",

  "virtual_hosts": [

   {

    "name": "details.service.consul:9080",

    "domains": [

     "details.service.consul",

     "details.service.consul:9080",

     "details",

     "details:9080",

     "details.service",

     "details.service:9080"

    ],

    "routes": [

     {

      "match": {

       "prefix": "/"

      },

      "route": {

       "cluster": "outbound|9080|v1|details.service.consul",

……

      },

……

     }

    ]

   },

   {

    "name": "productpage.service.consul:9080",

    "domains": [

     "productpage.service.consul",

     "productpage.service.consul:9080",

     "productpage",

     "productpage:9080",

     "productpage.service",

     "productpage.service:9080"

    ],

    "routes": [

     {

      "match": {

       "prefix": "/"

      },

      "route": {

       "cluster": "outbound|9080|v1|productpage.service.consul",

……

      },

……

     }

    ]

   },

……

由于内容太长,这里只贴details和productpage的关键内容。可以看到,9080端口的出站流量会根据目标地址的域名正确地转发到对应的微服务。

Istio路由控制

注意:本节工作目录为/samples/bookinfo/platform/consul。

最后我们尝试一下Istio的路由控制能力。在配置路由规则之前,我们要先使用DestinationRule定义各个微服务的版本:

$ kubectl apply -f destination-rule-all.yaml

DestinationRule:DestinationRule定义了每个服务下按照某种策略分割的子集。在本例子中按照版本来分子集,reviews分为v1、v2、v3三个版本的子集,其他微服务都只有v1一个子集。

使用命令kubectl get destinationrules -o yaml可以查看已配置的DestinationRule。

接下来我们使用VirtualService来配置路由规则。virtual-service-all-v1.yaml配置会让所有微服务的流量都路由到v1版本。

$ kubectl apply -f virtual-service-all-v1.yaml

VirtualService:定义路由规则,按照这个规则决定每次请求服务应该将流量转发到哪个子集。

使用命令kubectl get virtualservices -o yaml可以查看已配置的VirtualService。

再刷新页面,现在不管刷新多少次,reviews都会使用v1版本,也就是页面不会显示星星。

下面我们试一下基于用户身份的路由规则。配置文件virtual-service-reviews-test-v2.yaml配置了reviews的路由,让用户jason的流量路由到v2版本,其他情况路由到v1版本。

$ kubectl apply -f virtual-service-reviews-test-v2.yaml

执行命令后刷新页面,可以看到reviews都使用的v1版本,页面不会显示星星。点击右上角的Sign in按钮,以jason的身份登录(密码随便),可以看到reviews切换到v2版本了,页面显示了黑色星星。

查看virtual-service-reviews-test-v2.yaml文件内容可以看到,基于身份的路由是按照匹配HTTP的headers实现的。当HTTP的headers有end-user: jason的内容时路由到v2版本,否则路由到v1版本。

apiVersion: networking.istio.io/v1alpha3

kind: VirtualService

metadata:

name: reviews

spec:

hosts:

- reviews.service.consul

http:

- match:

- headers:

    end-user:

      exact: jason

route:

- destination:

    host: reviews.service.consul

    subset: v2

- route:

- destination:

    host: reviews.service.consul

    subset: v1

几点注意事项的总结

  1. istio.yaml引用的Registrator的latest版本不支持consul的ServiceMeta。要改为master版本。
  2. 第一次启动istio.yaml后,因为启动时pilot连不上istio-apiserver,pilot会失败退出。等待istio-apiserver启动完毕后再跑一次istio.yaml。
  3. 配置kubectl的context,让kubectl使用istio-apiserver提供的Kubernetes API接口。
  4. 使用bookinfo.yaml启动各个微服务后,还要运行bookinfo.sidecar.yaml以初始化和启动sidecar。

整体架构图

YjMbYfM.png!web

原文链接: https://www.cnblogs.com/skabyy/p/10668079.html


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK