Skip to content

14Itio入门:基于最新1.7版本的环境搭建和介绍

今天我要和你分享的内容是 Istio 入门,主要包括 Istio 基础知识和基于最新版本的环境搭建。

谈到 Service Mesh,不得不提及 Istio,作为 Google、IBM、lynx 联合推出的 Service Mesh 解决方案,Istio 几乎成了 Service Mesh 的代名词,但实际上 Istio 仅仅是 Service Mesh 中的控制面,它的主要功能是为数据面下发服务治理策略

早期 Istio 也尝试过接管一些流量治理的功能,比如"为人诟病"的 Mixer 模块,就是用来做限流和遥测信息收集的,后来因为转发性能的问题,在后续版本还是交由数据面负责了。

Istio 最近在 1.5 版本进行了一次大的变更,由早期的微服务架构变成了单体架构。之前版本的Istio 由 Pilot、Citadel、Galley、Sidecar 注入器等多个微服务组成,但现在只需要一个 Istiod 的单体服务

这样的单体架构,使 Istio 作为一个开源软件,更容易部署、配置和维护。另外得益于部署便利性的提升,Istio 也支持在虚拟机环境部署了。

最新版本的 Istio 控制面由以下几个组件组成。

  • Pilot:Istio 控制面中最核心的模块,负责运行时配置下发,具体来说,就是和 Envoy 之间基于 xDS 协议进行的各种 Envoy 配置信息的推送,包括服务发现、路由发现、集群发现、监听器发现等。

  • Citadel:负责证书的分发和轮换,使 Sidecar 代理两端实现双向 TLS 认证、访问授权等。

  • Galley:配置信息的格式和正确性校验,将配置信息提供给 Pilot 使用。

现在你已经对 Istio 的基础知识有了一个简单的了解,接下来我将带你动手搭建一个 Istio 的环境,并通过环境搭建带你进一步认识 Istio ,你也可以在搭建的过程中进一步感受这几个组件的功能。

Istio 环境搭建

因为 Istio 强依赖于 Kubernetes 环境,所以我们需要先进行 Kubernetes 环境的搭建,为了更加方便,我们使用 Minikube 在本地搭建。

在 macOS 环境下,只要简单运行如下命令即可:

java
$ brew install minikube

安装成功后,使用如下命令启动:

java
$ minikube start --kubernetes-version=v1.19.2 --driver=docker

这里需要注意的是,因为 Istio 的最新版本要求是 1.16 以上的 Kubernetes 环境,所以要指定高于此版本的版本号,这里我直接使用了最新的稳定版本。

另外启动过程中,如果遇到问题,可以加上 --alsologtostderr 输出详细的错误信息,建议将 Docker 版本也升级至最新。

下载最新版本的 Istio:

java
$ curl -L https://istio.io/downloadIstio | ISTIO_VERSION=1.7.3 sh -

为了保证后续命令的准确性,这里指定了版本号。

进入下载目录:

java
$ cd istio-1.7.3

将 Istioctl 客户端添加到可执行路径:

java
$ export PATH=$PWD/bin:$PATH

使用 Istioctl 客户端安装 Istio:

java
$ istioctl install --set profile=demo

在默认命名空间开启自动注入 Envoy Sidecar:

java
$ kubectl label namespace default istio-injection=enabled

通过上述操作,Istio 的环境就安装好了,你可以通过下面的命令查看 Istio 组件:

java
$ kubectl get svc -n istio-system
NAME                   TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)                                                                      AGE
istio-egressgateway    ClusterIP      10.104.140.107   <none>        80/TCP,443/TCP,15443/TCP                                                     2d5h
istio-ingressgateway   LoadBalancer   10.103.123.96    <pending>     15021:32148/TCP,80:32124/TCP,443:31370/TCP,31400:31690/TCP,15443:32721/TCP   2d5h
istiod                 ClusterIP      10.106.54.115    <none>        15010/TCP,15012/TCP,443/TCP,15014/TCP,853/TCP                                2d5h

Istio 以一个服务的形式部署在 Kubernetes 集群中。我们可以看到,部署好的 Pods 中,除了有 Istiod 这个核心组件外,还有 Istio Egressgate Way 和 Istio Ingressgate Way 两个组件,分别是出口网关和入口网关。

至此,我们已经成功完成平台搭建。接下来,我们将部署示例程序 Bookinfo,通过这个官方 Demo 你可以直观感受 Istio 的功能。

部署 Bookinfo 示例程序

部署示例程序:

java
$ kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml

通过下面的命令可以查看 Pod 的运行状态:

java
$ kubectl get pods

当 Pod ready 后,我们可以通过内部服务访问的方式,查看服务的运行情况:

java
$ kubectl exec "$(kubectl get pod -l app=ratings -o jsonpath='{.items[0].metadata.name}')" -c ratings -- curl -s productpage:9080/productpage | grep -o "<title>.*</title>"
<title>Simple Bookstore App</title>

现在,服务已经正常启动了。接下来,我们通过 Ingress 的方式,让浏览器可以打开这个页面。

通过 Ingress 打通内外网服务:

java
$ kubectl apply -f samples/bookinfo/networking/bookinfo-gateway.yaml
gateway.networking.istio.io/bookinfo-gateway created
virtualservice.networking.istio.io/bookinfo created

查看 Minikube 的 IP:

java
$ minikube ip
127.0.0.1

然后,打开新的终端窗口,为 LoadBalancer 类型的服务打通外网:

java
$ minikube tunnel
The service Istio -ingressgateway requires privileged ports to be exposed: [80 443]
sudo permission will be asked for it.
Starting tunnel for service Istio -ingressgateway.
Password:

打开浏览器访问http://127.0.0.1/productpage(IP 替换为上面运行的 Minikube IP), 就可以看到如下页面了。

现在,我们已经可以访问 Bookinfo 的这个程序了。接下来,看一下如何在 Istio 中部署基于 Kiali 的可视化界面。

可观测性部署

Kiali 是一个基于服务网格的 Istio 管理控制台,它提供了一些数据的仪表盘和可观测能力,同时可以让你去操作网格的配置。

使用如下方式快速部署一个用于演示的 Kiali:

java
$ kubectl apply -f samples/addons

因为安装的组件比较多,也比较消耗资源,如果机器资源并非特别充足,可能会出现启动比较慢的情况,需要多等待一段时间。查看 Pod 的状态可以通过以下命令进行:

java
$ kubectl get pod -n istio-system -o wide
NAME                                    READY   STATUS    RESTARTS   AGE   IP            NODE       NOMINATED NODE   READINESS GATES
grafana-57bb676c4c-4nfcd                1/1     Running   3          13d   172.18.0.11   minikube   <none>           <none>
istio-egressgateway-8556f8c8dc-pnlbl    1/1     Running   4          16d   172.18.0.4    minikube   <none>           <none>
istio-ingressgateway-589d868684-xcx42   1/1     Running   4          16d   172.18.0.6    minikube   <none>           <none>
istiod-86d65b6959-jl8fs                 1/1     Running   4          16d   172.18.0.3    minikube   <none>           <none>
jaeger-75948789b4-9bc4f                 1/1     Running   15         13d   172.18.0.13   minikube   <none>           <none>
kiali-7d5cb68b45-cz75l                  1/1     Running   13         13d   172.18.0.5    minikube   <none>           <none>
prometheus-7c8bf6df84-8jp99             2/2     Running   12         13d   172.18.0.9    minikube   <none>           <none>

如果有启动比较慢的 Pod,我们也可以通过下面的命令查看一下 Pod 的事件,了解一下具体原因。

java
$ kubectl describe pod kiali-7d5cb68b45-cz75l -n istio-system

Pod 全部启动成功后,通过下面的命令查看整个调用关系情况:

java
$ istioctl dashboard kiali

运行之后就可以看到下面的页面了。

调用关系图

现在,我们已经可以通过页面看到完整的调用关系图,这可以让我们更直观地了解程序的运行状态。

实例服务原理解析

我们可以分析一下整个 Bookinfo 示例程序的搭建过程,就拿我们访问的 productpage 页面来说。

java
apiVersion: v1
kind: Service
metadata:
  name: productpage
  labels:
    app: productpage
    service: productpage
spec:
  ports:
  - port: 9080
    name: http
  selector:
    app: productpage

我们首先定义了一个 Service 类型的资源,而 Service 是 Kubernetes 抽象出来的服务概念,一组 Pod 的集合便是 Kubernetes 的 Service。Pod 是有生命周期的,Pod 的 IP 在不同的生命周期内发生变化,但抽象出来的 Service 名称是不会变化的。这样我们就可以通过服务名访问这些后端 IP,而不用感知后端 Pod IP 的变化。

然后我们在 Metadata 中定义了这一组 Service 的 label ,通过 selector 指定响应的标签,就可以访问带有这组标签定义的 Pod 集合。另外这组配置还指定了服务端口为 9080,服务的协议为 HTTP ,这些信息将用于 Sidecar 的代理转发。

接下来,我们通过 Deployment 控制 Pod 的生命周期,定义了 Pod 的副本数量。下面的示例中,定义一个 replicas 为 1,也就是 Pod 的副本数量为 1,因为是测试环境,所以没有设置更多的副本数量保障服务的 SLA 。

当然一些常规的镜像拉取地址和拉取方式也做了定义:

java
apiVersion: apps/v1
kind: Deployment
metadata:
  name: productpage-v1
  labels:
    app: productpage
    version: v1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: productpage
      version: v1
  template:
    metadata:
      labels:
        app: productpage
        version: v1
    spec:
      serviceAccountName: bookinfo-productpage
      containers:
      - name: productpage
        image: docker.io/istio/examples-bookinfo-productpage-v1:1.16.2
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 9080
        volumeMounts:
        - name: tmp
          mountPath: /tmp
      volumes:
      - name: tmp
        emptyDir: {}

另外在 Kubernetes 的 Service 里面并没有版本的概念,如果我们想进行染色或者金丝雀发布,就需要借助 Istio 的能力。通过上述命令可以看到,在 Deployment 类型中定义了 Version V1 的 lable,这个 lable 最终会转化成不同的服务名,比如 productpage-v1 的方式,下发到 Sidecar 中, Sidecar 根据这个服务名称进行服务发现,以实现不同版本号访问的方法。

下一步,我们用到了 Ingress 模式。我们定义一组网关类型的资源, Istio 通过 Gateway 将服务发布成外部可访问的服务,通过 80 端口将服务通过 Ingress 网关转发到特定的服务上。

java
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: bookinfo-gateway
spec:
  selector:
    istio: ingressgateway # use istio default controller
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "*"

在这里,Gateway 资源类型,需要配合 VirtualService 类型的资源一起使用。那么,定义匹配到的 URI 具体路由到哪个服务呢?

在这个程序中你可以看到匹配到 /productpage 路径的服务,而在 route 中定义了 destination 的 host 将会是路由的服务名

java
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: bookinfo
spec:
  hosts:
  - "*"
  gateways:
  - bookinfo-gateway
  http:
  - match:
    - uri:
        exact: /productpage
    - uri:
        prefix: /static
    - uri:
        exact: /login
    - uri:
        exact: /logout
    - uri:
        prefix: /api/v1/products
    route:
    - destination:
        host: productpage
        port:
          number: 9080

最后我们通过一张图,整体回顾一下整个 Istio 的架构:

Istio 架构示意图

Istio 通过 Galley 模块管理配置,Pilot 模块解析配置为 xDS 协议格式,通过 gRPC 和 Envoy 进行通信,以便完成配置和节点信息更新, pilot-agent 作为 Envoy 守护模块,保证 Envoy 的正常运行和平滑重启。

本地业务服务 productpage 通过 iptables 劫持的方式和本地 Envoy 进行通信,Envoy 完成服务发现后将请求转发到 ProductDetail 服务的所在 Pod,同样通过 iptables 劫持的方式将请求转发到本地的业务服务 ProductDetail。

总结

这一讲我主要讲解了 Istio 的基础知识,并通过一个简单的示例让你了解了整个 Istio 的运作模式。

本讲内容总结如下:

参照上面的示例,通过之前的 Kiali 的调用关系图,你可以发现在示例中,productpage 页面中会随机展示 reviews 服务的不同版本。那如何变更默认配置,可以让访问 reviews 服务显示特定版本呢?如果按照不同比例访问 review 特定版本,又如何配置呢?欢迎在留言区和我分享你的观点。

下一讲我会与你分享 xDS:控制面和数据面的通信桥梁。利用 xDS 协议,Envoy 可以实现配置的完全动态化,配置实时更新而无须重启 Envoy 或者影响业务。通过这部分内容的学习,希望你可以掌握数据面 Envoy 如何动态地更新服务的各种配置。我们下一讲再见!