本教程已加入 Istio 系列:https://istio.whuanle.cn
可观测性
Istio 集成了 Jaeger、Zipkin 和 Skywalking 等链路追踪应用,能够有效地捕获服务网格的结构,展示网络拓扑结构,并分析网格的健康状况。
这一切都得益于 Envoy 代理的实现。由于所有进出流量都需要经过 Envoy 代理,Envoy 可以捕获这些流量记录,并将其推送到相应的链路追踪系统中。这样一来,可以链路追踪系统轻松地监控和分析服务网格内的流量情况。
另外 Istio 还支持 Prometheus、 Grafana 收集指标数据。
下面我们将使用官方的模板部署 Kiali 、 还有 Jaeger,然后通过 Kiali 统一查看集群的指标信息。
Kiali 界面示例:
拉取 Istio 官方的仓库:
git clone https://github.com/istio/istio.git
在 samples/addons/
目录中有以下目录或文件:
samples/addons/
├── extras
│ ├── prometheus-operator.yaml
│ ├── prometheus_vm_tls.yaml
│ ├── prometheus_vm.yaml
│ ├── skywalking.yaml
│ └── zipkin.yaml
├── grafana.yaml
├── jaeger.yaml
├── kiali.yaml
├── prometheus.yaml
└── README.md
我们启用 grafana.yaml
、jaeger.yaml
、kiali.yaml
、prometheus.yaml
四个文件。
kubectl apply -f samples/addons
这些服务默认安装在 istio-system 命名空间下,因此不需要自行设置。
Istio 默认使用 Jaeger 做链路追踪,我们也可以使用 Skywalking 来做追踪。extras 目录中的配置我们可以自行部署。
执行命令查看其 Service 对应的 IP 和端口:
kubectl get svc -n istio-system
现在,我们有两种方式让 kiali 在外部访问,一种是修改 Service 配置,将其访问类型修改为 NodePort,另一种是使用 istio-ingressgateway 配置流量入口。
第二种方式比较麻烦,但是为了验证我们的学习成果,我们不妨使用 Gateway 的方式暴露服务。
通过 Gateway 访问 Kiali
首先,创建一个 Gateway 。
kiali_gateway.yaml
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: kiali-gateway
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 15029
name: http-kiali
protocol: HTTP
hosts:
- "*"
kubectl -n istio-system apply -f kiali_gateway.yaml
接下来,创建一个 VirtualService 资源,将 Gateway 路由到 Kiali 服务.
kiali_vs.yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: kiali
spec:
hosts:
- "*"
gateways:
- kiali-gateway
http:
- match:
- uri:
prefix: /kiali
route:
- destination:
host: kiali.istio-system.svc.cluster.local
port:
number: 20001
kubectl -n istio-system apply -f kiali_vs.yaml
然后修改 istio-ingressgateway,新增加一个配置为 kiali 暴露服务。
kubectl edit svc istio-ingressgateway -n istio-system
- name: kiali
nodePort: 32667
port: 15029
protocol: TCP
targetPort: 15029
然后访问:http://192.168.3.150:32667/kiali
查看链路追踪数据
现在我们在 Shell 执行命令轮询一段时间前面部署的微服务,以便给集群创造访问流量。
for i in <code>seq 1 1000</code>; do curl -s -o /dev/null http://192.168.3.150:30666/productpage; done
因为默认链路追踪采样率是 1%,所以可以将请求次数设置大一些。
最终会得到一张类似的图片。
Kiali 的 Graph 数据主要来自两个来源:Prometheus 和 Istio 本身的遥测数据。
Prometheus:Prometheus 是一个开源监控和警报工具,它用于收集和存储 Istio 服务网格中的指标数据。Istio 使用 Envoy 代理收集遥测数据,这些数据随后被 Prometheus 抓取和存储。Kiali 使用这些 Prometheus 数据来生成服务之间的流量、错误率、延迟等指标。
Istio 遥测数据:Istio 服务网格生成的遥测数据包括请求、响应、延迟以及 Envoy 代理的其他性能指标。这些数据由 Istio 组件(例如 Mixer 和 Pilot)以及 Envoy 代理本身生成。Kiali 从这些遥测数据中获取服务拓扑信息,以创建服务之间的依赖关系图。
Kiali 将这两个数据源的信息整合在一起,生成 Graph,它展示了服务网格的拓扑结构、服务之间的流量以及其他性能指标。这有助于用户更好地理解服务之间的依赖关系,发现潜在的性能问题,并优化服务网格配置。
可能失败的原因
如果你的 Kiali 一直显示 Empty Graph。请关注以下几种可能的情况:
- 集群版本低于 1.23 ,需要升级 Kubernetes 集群。
- 安装了 Kubesphere,说多了都是泪,Kubesphere 太重了,笔者花了一晚上时间重新安装集群。
- 访问的地址不正确,没有配置对
/productpage
的访问地址,请求流量没有打入集群。 - Pod 没有被注入 istio-proxy。
你可以在 Kiali 的 Workloads
查看每个负载的 Pod 信息,正常情况应当如下所示:
修复 Kiali Grafana 问题
点击右上角的消息,可能会提示配置不正确,因为 kiali 需要从 Grafana 拉取数据。
编辑 configmap 。
kubectl edit configmap kiali -n istio-system
在里面添加如下两行内容。
grafana: \n enabled: true \n url: \"http://grafana.istio-system.svc.cluster.local:3000\"
\ \n in_cluster_url: \"http://grafana.istio-system.svc.cluster.local:3000\"\n
如果使用的是可视化工具,添加就简单了。
grafana:
enabled: true
url: "http://grafana.istio-system.svc.cluster.local:3000"
in_cluster_url: "http://grafana.istio-system>.svc.cluster.local:3000"
然后使用 kubectl describe configmap kiali -n istio-system
查看配置是否正确。
5,出入口网关
Istio 可以管理集群的出入口流量,当客户端访问集群内的应用时, Istio 可以将经过 istio-ingressgateway 的流量实现负载均衡和熔断等一系列功能。
可是,如果集群内的一个应用要访问 google.com ,那么我们可以给内部所有请求了 google.com 的流量设置负载均衡吗?答案是可以,Istio 提供了 istio-egressgateway 实现这种功能。因为 Pod 中的容器要访问网络时,会被 Envoy 拦截,Envoy 可以很容易地分析这些请求,然后通过一系列手段影响着请求的行为。
在本章中,将会简单说一下 istio-ingressgateway 和 istio-egressgateway。
istio-ingressgateway
入口网关指的是从外部经过 istio-ingressgateway 流入集群的流量,需要创建 Gateway 绑定流量。
关于 istio-ingressgateway 经过前面几章的学习,大家应该不陌生了。
istio-ingressgateway 由 Pod 和 Service 组成。 istio-ingressgateway 本身就是一个网关应用,你可以把它当作 Nginx、Apisix、Kong ,你可以从各种各种网关应用中找到与 istio-ingressgateway 类似的概念。
作为一个应用,它需要对外开放一些端口,只有当流量经过这些端口时, istio-ingressgateway 才会起作用。为了在 Kubernetes 中暴露端口, istio-ingressgateway 还有一个 Service 对象。
有了 istio-ingressgateway 之后,我们可以通过 Istio Gateway 监控一些域名或IP,然后暴露集群内部的服务 。
Gateway 的概念跟 Nginx 有很多相似之处。
比如从配置上看, Gateway 跟 Nginx 如果要监控某个入口流量,它们的配置如下:
Nginx:
server {
listen 80;
server_name example.org www.example.org;
#...
}
Gateway:
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- example.org
- www.example.org
这些配置指定了 Gateway 和 Nginx 只监控哪些流量。
紧接着,监控到指定入口的流量之后,需要将流量转发到集群内的应用中。
Nginx 可以直接在同一个配置文件里面设置:
server {
listen 80;
server_name example.org www.example.org;
#...
}
location /some/path/ {
proxy_pass http:/bookinfo:9080/;
}
而 Gateway 需要使用 VirtualService 指定流量转发到哪里,并且 VirtualService 还可以进一步筛选入口地址。
spec:
hosts:
- "www.example.org"
gateways:
# 绑定 Gateway
- mygateway
http:
route:
- destination:
host: bookinfo
port:
number: 9080
所以总结起来,Istio 的做法是 Gateway 监控入口流量,通过 VirtualService 设置流量进入的策略,并指向 Service。而 DestinationRule 则定义了流量流向 Pod 的策略。
部署服务
下面我们将使用 httpbin 服务作为示例,如何一步步配置在外部访问 httpbin 服务。
首先部署一个 httpbin 服务,这个 httpbin 服务很简单,包含了 Service 和 Deployment 。
httpbin.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: httpbin
---
apiVersion: v1
kind: Service
metadata:
name: httpbin
labels:
app: httpbin
service: httpbin
spec:
ports:
- name: http
port: 8000
targetPort: 80
selector:
app: httpbin
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: httpbin
spec:
replicas: 1
selector:
matchLabels:
app: httpbin
version: v1
template:
metadata:
labels:
app: httpbin
version: v1
spec:
serviceAccountName: httpbin
containers:
- image: docker.io/kennethreitz/httpbin
imagePullPolicy: IfNotPresent
name: httpbin
ports:
- containerPort: 80
kubectl -n bookinfo apply -f httpbin.yaml
配置 Gateway
然后创建一个 Gateway ,指定监听哪些入口流量。
httpbin_gw.yaml
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: httpbin-gateway
spec:
selector:
istio: ingressgateway # use Istio default gateway implementation
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "httpbin.s1.whuanle.cn"
- "*"
这一步为了大家能够通过域名更加直观地了解 Gateway,大家可以修改
httpbin.s1.whuanle.cn
替换为自己的域名。然后在自己的电脑中打开
C:\Windows\System32\drivers\etc\hosts
增加一条记录 ,将 IP 指向自己的服务器。
kubectl -n bookinfo apply -f httpbin_gw.yaml
现在,我们已经让 istio-ingressgateway 帮我们关注 httpbin.s1.whuanle.cn 这个地址,如果有人访问了 httpbin.s1.whuanle.cn,那么这个流量将会流入到 httpbin-gateway。
接下来我们将要为 Gateway 配置服务地址,并配置外部允许访问的地址后缀。
配置 VistualService:
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: httpbin
spec:
hosts:
- "*"
gateways:
- httpbin-gateway
http:
- match:
- uri:
prefix: /status
- uri:
prefix: /delay
route:
- destination:
port:
number: 8000
host: httpbin
当 Gateway 和 VirtualService 端口只有一个时,不需要配置端口绑定。
kubectl -n bookinfo apply -f httpbin_vs.yaml
找到 istio-ingressgateway 对外暴露的端口。
kubectl get svc istio-ingressgateway -n istio-system
httpbin 是一个 http 测试程序,我们可以通过使用 /status/{状态码}
获取对应的 http 请求状态。
例如:
如果我们不希望这个服务被外界访问到,我们可以先把 /status
删除。
kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: httpbin
spec:
hosts:
- "*"
gateways:
- httpbin-gateway
http:
- match:
- uri:
prefix: /delay
route:
- destination:
port:
number: 8000
host: httpbin
EOF
此时你将无法访问 status
路径。但是我们还可以访问 /delay
路径。
httpbin 的 /delay
路径用于测试延迟 http 请求响应使用,/delay/{秒数}
可以指定服务器在多久之后才会返回响应。
例如 http://192.168.3.150:32309/delay/5 将在 5 秒后响应。
httpbin 还有很多路由接口,我们可以通过 VirtualService 配置放通哪些路径。
如果需要全部放通,可以使用:
kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: httpbin
spec:
hosts:
- "*"
gateways:
- httpbin-gateway
http:
- match:
- uri:
prefix: /
route:
- destination:
port:
number: 8000
host: httpbin
subset: v1
EOF
子版本
第四章中进行版本路由实验时使用到,可以将流量导入到不同的版本之中。
kubectl -n bookinfo apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: httpbin
spec:
host: httpbin
subsets:
- name: v1
labels:
version: v1
EOF
首先是使用 DestinationRule 指向一个 Service:
host: httpbin
当然,我们也可以写成
host: httpbin.bookinfo.svc.cluster.local
通过 host 可以识别到对应的 Kubernetes Service,然后从 Service 对应的 Endpoints 中获得所有 Pod 列表。
通过 Endpoints 获得所有 Pod 之后,查看每个 Pod 的描述信息。当有一个请求到达时,根据 DestinationRule 中的标签选择器,选择合适的 Pod 进行访问。
- name: v1
labels:
version: v1
istio-egressgateway
istio-egressgateway 也是 Istio 中的一种组件,需要自行安装。安装 istio-egressgateway 命令:
helm install istio-egressgateway istio/gateway -n istio-system
在集群中,如果 A 应用访问的地址属于集群中的应用,那么 Istio 可以给这些请求注入各种行为,实现负载均衡和熔断等。
可是,如果集群内部要访问外部的一个服务时,需要配置访问地址,如 aaa.com,我们应该如何实现负载均衡和熔断这些功能呢?
Istio ServiceEntry 是一种资源,允许将外部服务(即不在 Istio 服务网格中的服务)纳入Istio服务网格。通过将外部服务添加到网格,可以使用 Istio 的流量管理和策略功能来控制与这些外部服务的交互。
以下是一个ServiceEntry示例,将外部HTTP服务 www.google.com
添加到Istio服务网格:
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: google
spec:
hosts:
- www.google.com
addresses:
- 192.168.1.1
ports:
- number: 80
name: http
protocol: HTTP
location: MESH_EXTERNAL
resolution: DNS
endpoints:
- address: "www.google.com"
ports:
http: 80
locality: "us-west1/zone1"
exportTo:
- "*"
在此示例中,我们创建了一个名为httpbin-ext
的ServiceEntry资源。指定的主机为httpbin.org
,端口号为80,协议为HTTP。此外,我们将resolution
设置为DNS
,将location
设置为MESH_EXTERNAL
,表示该服务位于网格之外。
要将此ServiceEntry应用到集群,请将其保存到一个YAML文件(例如:httpbin-ext.yaml
),然后运行以下命令:
kubectl apply -f httpbin-ext.yaml
现在,Istio 服务网格中的服务访问 www.google.com
时仍受Istio策略的控制。例如,可以为此 ServiceEntry 创建 VirtualService 以应用流量管理规则,或者为其创建 DestinationRule 以配置负载均衡和连接池设置。
spec
: 包含ServiceEntry的具体配置的对象。
hosts
: 一个包含要导入的外部服务的主机名(FQDN)的列表。例如:["httpbin.org"]
。addresses
: (可选)与外部服务关联的虚拟IP地址的列表。例如:["192.168.1.1"]
。- ports: 一个描述外部服务使用的端口的列表。每个端口都有以下属性:
number
: 端口号,例如:80。name
: 端口的名称,例如:http
。protocol
: 使用的协议,例如:HTTP
、TCP
、HTTPS
等。
location
: 服务的位置。可以是MESH_EXTERNAL
(表示服务在网格外部)或MESH_INTERNAL
(表示服务在网格内部,但不属于任何已知服务)。resolution
: 用于确定服务实例地址的解析方法。可以是NONE
(默认值,表示不解析地址),STATIC
(表示使用addresses
字段中的IP地址),DNS
(表示使用DNS解析主机名)或MESH_EXTERNAL
。- endpoints: (可选)外部服务的端点列表。每个端点都有以下属性:
address
: 端点的IP地址或主机名。ports
: 一个包含端口名称和端口号的映射,例如:"http": 8080
。labels
: (可选)应用于端点的标签。locality
: (可选)端点的地理位置,例如:us-west1/zone1
。
exportTo
: (可选)一个包含命名空间名称的列表,指定可以访问此ServiceEntry的命名空间。可以使用星号(*
)表示所有命名空间。默认值为*
。subjectAltNames
: (可选)用于验证服务器证书主题替代名(SANs)的列表。
读者可以从官方文档中了解更多:
https://istio.io/latest/zh/docs/tasks/traffic-management/egress/egress-control/
6,金丝雀发布
项目总是处于不断变化之中,每次发布新的版本时,都考验了团队的运维能力。
新版本上线之前,经历过开发和测试人员的验证,也经过产品经理的验收。可是当要上线到生产环境时,谁也保证不了上线一定就能跑起来。所以往往需要在上线时保持新版本和旧版本同时在用,测试人员或内测用户可以访问新版本,其他人继续使用旧版本。再有就是上线时新旧系统能够丝滑切换,用户完全感知不到这种变化。
并且新版本上线时,不应该影响旧版本的运行,要求实现不停止更新。但是除了应用本身,还涉及到新旧版本共有同一个数据库,新旧版本应用对应的数据库表结构不一样。
新旧版本切换带来的问题很多,但是,在本系列教程中我们只考虑外部访问效果即可。
很多项目的版本更新做得并不好。很多初级阶段的 Web 服务团队,每次更新都需要中断当前的应用,后端服务有一段时间内不可用。如果新版本有问题,那么还需要重新发布旧版本。如果只是内部服务,问题不大,可是如果系统是给客户使用的,那么很容易让客户给出一个低分的评价。
Kubernetes 中虽然有滚动升级,能够逐渐使用新版本的 Pod 替换旧版本的 Pod,两个版本共存,但是并不能做到流量自由切分,一个流量进入时,依然会在新旧版本的 Pod 中轮询选择。但是我们还是可以调整 v1 和 v2 的 Pod 数量,实现流量按照比例划分,比如 v1 的 Pod 数量有 40 个,v2 的 Pod 的数量有 60 个,那么按照比例,会有 60% 的流量进入到 v2 中。不过这样并没有什么鸟用。
Kubernetes 滚动升级、伸缩参考资料:https://k8s.whuanle.cn/3.pod/6.scale.html
Istio 中虽然没有名为 金丝雀发布的功能,但是按照之前我们所学到的 Istio 的 VirtualService 、DestinationRule 就可以实现根据请求的 header 等,将流量转发到不同的子版本中,我们使用这些操作方法即可实现金丝雀发布。
金丝雀发布
尴尬,在学习 Istio 之前,我对蓝绿发布、灰度发布、金丝雀发布的概念并不清晰,很多文章都是将它们分成三类发布方式。实际上蓝绿发布和金丝雀发布都是灰度发布的一种。
灰度发布是两个版本共存,将一小部分流量切到新版本中,当新版本经过一段时间验证后,如果没有问题,再将流量逐步切换到新版本中。
所以,可以把蓝绿发布、A/B 测试、金丝雀发布都划分为灰度发布。
当然,它们有一些差别,每个人的划分方法也有差别,有的说法是金丝雀发布才是灰度发布。
蓝绿发布
蓝绿发布的方式是使用另一套资源部署新版本,然后将旧版本的流量都切换到新版本中去,缺点是需要消耗两套资源,而且蓝绿发布是全量切换,如果新版本出现问题则会影响到所有用户。
A/B 测试
A/B 测试是同时部署两个版本对等的版本来接收流量,但它不是指新旧版本。比如,产品经理有一个好主意,做新时代的农场区块链,想知道大家喜欢养猪还是养鸡。于是发布了养猪农场和养鸡农场两个对等的应用,然后邀请不同的用户进行内测,喜欢养猪的用户会被导向养猪农场应用。然后收集用户的意见和各种指标数据,分析用户喜欢养什么动物,最后决定上线什么版本。
金丝雀发布
先上线一个新版本,然后根据规则将一小部分用户导向到新应用,观察新版本在生产环境的表现,如果达到预期,则逐步将流量切换到新版本中。
按照流量比例划分
在第四章中,我们使用 DestinationRule 为 reviews 定义了三个版本,但是每个版本只有一个 Pod。
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: reviews
spec:
host: reviews
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
- name: v3
labels:
version: v3
现在使用命令将 v1 版本增加到 10 个 pod。
kubectl scale deployment reviews-v1 -n bookinfo --replicas=10
在原配置不变的情况下,我们来部署 VirtualService,将流量的 90% 打入到包含 10个 Pod 的 v1 版本。因为 v2 版本只有10% 的流量,所以只需要 1 个 Pod 也可以支撑了叭。
kubectl -n bookinfo apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- route:
- destination:
host: reviews
subset: v1
weight: 90
- destination:
host: reviews
subset: v2
weight: 10
EOF
命令之前完毕之后,不断刷新 productpage页面,会发现大部分情况下不会显示评星,少部分情况会出现黑色的星星。
按照 Header 划分
当然,也可以使用 header 的方式,将流量导入到不同的版本中。比如只有测试人员的流量才会打入到新版本中。
http:
- match:
- headers:
end-user:
exact: jason
route:
- destination:
host: reviews
subset: v2
- route:
- destination:
host: reviews
subset: v1
当我们检查 v2 版本没有问题之后,我们就将 v2 版本扩容。
kubectl scale deployment reviews-v2 -n bookinfo --replicas=10
与此同时,修改 VirtualService,将全部流量路由到 v2 中。
kubectl -n bookinfo apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- route:
- destination:
host: reviews
subset: v1
weight: 0
- destination:
host: reviews
subset: v2
weight: 100
EOF
当一段时间后,如果一切正常,将 v1 版本的 Pod 数量降低为 0。
kubectl scale deployment reviews-v1 -n bookinfo --replicas=0
但是不要删除 v1 版本,如果新版本出现问题,我们只需要调整 v1版本的 Pod 数量,然后修改 VirtualService ,将理论导入到 v1 即可。
金丝雀发布其实很简单,除了 Istio 之外,我们通过 Nginx、Apisix、Kong 等网关都可以轻松实现。
文章评论