服务网格:Istio架构

服务网格

什么是服务网格:

Linkerd给出的 Service Mesh 的定义:

服务网格是用于处理服务间通信的专用基础设施层。它负责通过包含现代云原生应用程序的复杂服务拓扑来可靠地传递请求。实际上,服务网格通常通过一组轻量级网络代理来实现,这些代理与应用程序代码一起部署,而不需要感知应用程序本身。

istio给出的 Service Mesh 的定义:

服务网格用来描述这样一组组成微服务的网络以及它们之间的交互,包括服务发现、负载均衡、故障恢复、度量和监控等。服务网格通常还有更复杂的运维需求,比如 A/B 测试、金丝雀发布、速率限制、访问控制和端到端认证。

总结下,从linkerd和istio对 Service Mesh 的定义中我们可以看到服务网格的特征:

  • 主要是用于处理服务间通信的中间层(处理东西流量)
  • 使用轻量级网络代理为应用程序解决各种问题,如超时/重试、日志、监控等等
  • 采用 sidercar 模式,应用无感知
  • 原本在代码中实现的功能,通过交给服务网格实现解耦

服务网格演变史

从上面定义可以知道服务网格本质是服务间的网络控制

  • 从最原始的主机之间直接使用网线相连
  • 网络层的出现
  • 集成到应用程序内部的控制流
  • 分解到应用程序外部的控制流
  • 应用程序的中集成服务发现和断路器
  • 出现了专门用于服务发现和断路器的软件包/库,如 Twitter 的 Finagle 和 Facebook 的 Proxygen,这时候还是集成在应用程序内部
  • 出现了专门用于服务发现和断路器的开源软件,如 Netflix OSS、Airbnb 的 synapse 和 nerve
    最后作为微服务的中间层服务网格出现

Istio 安装

安装前检查

(1) 端口检查

端口 协议 使用者 描述
8060 HTTP Citadel GRPC 服务器
8080 HTTP Citadel agent SDS service 监控
9090 HTTP Prometheus Prometheus
9091 HTTP Mixer 策略/遥测
9876 HTTP Citadel, Citadel agent ControlZ 用户界面
9901 GRPC Galley 网格配置协议
15000 TCP Envoy Envoy 管理端口 (commands/diagnostics)
15001 TCP Envoy Envoy 传出
15006 TCP Envoy Envoy 传入
15004 HTTP Mixer, Pilot 策略/遥测 - mTLS
15010 HTTP Pilot Pilot service - XDS pilot - 发现
15011 TCP Pilot Pilot service - mTLS - Proxy - 发现
15014 HTTP Citadel, Citadel agent, Galley, Mixer, Pilot, Sidecar Injector 控制平面监控
15020 HTTP Ingress Gateway Pilot 健康检查
15029 HTTP Kiali Kiali 用户界面
15030 HTTP Prometheus Prometheus 用户界面
15031 HTTP Grafana Grafana 用户界面
15032 HTTP Tracing Tracing 用户界面
15443 TLS Ingress and Egress Gateways SNI
15090 HTTP Mixer Proxy
42422 TCP Mixer 遥测 - Prometheus

(2) UID检查

确保用户ID(UID)没有使用1337

(3)Pod 安全策略检查

istioctl

istio 提供了非常便利的安装工具 istioctl, istioctl 安装模式有六种:default、demo、minimal、remote、empty、preview。
default: 是istio的默认安装,可以用于生产环境。
demo: 主要用于展示istio各种功能,安装组件较多,istio教程示例就是用这种模式
minial: 最小组件安装,只提供了istio流量管理的最少组件
remote: 用于配置通过共享控制面板安装多集群istio的远程集群
empty: 这种模式更多只是作为自定义安装的基础模板使用,deploys nothing
preview: 预览版会提供一些未来,实验中的新功能,更多类似新版本体验。

istioctl install  --set profile=XXX

Istio CRD

常用资源

istio 的CRD资源列表:

kubectl get crd | grep istio | awk '{print $1}'
adapters.config.istio.io
attributemanifests.config.istio.io
authorizationpolicies.security.istio.io
clusterrbacconfigs.rbac.istio.io
destinationrules.networking.istio.io
envoyfilters.networking.istio.io
gateways.networking.istio.io
handlers.config.istio.io
httpapispecbindings.config.istio.io
httpapispecs.config.istio.io
instances.config.istio.io
istiooperators.install.istio.io
peerauthentications.security.istio.io
quotaspecbindings.config.istio.io
quotaspecs.config.istio.io
rbacconfigs.rbac.istio.io
requestauthentications.security.istio.io
rules.config.istio.io
serviceentries.networking.istio.io
servicerolebindings.rbac.istio.io
serviceroles.rbac.istio.io
sidecars.networking.istio.io
templates.config.istio.io
virtualservices.networking.istio.io
workloadentries.networking.istio.io
workloadgroups.networking.istio.io

但常用的 CRD 有:GatewayVirtualServiceDestination

Gateway

Gateway 是用来为集群提供流量进出配置负载均衡,其由 istio 的 ingress-gateway 组件实现,通过 selector 绑定 istio-ingress-gateway 的 pod。

字段 类型 说明
selector map 主要用于绑定 ingress-gateway 的 label ,默认的 workloads 会搜索说有命名空间的 lable,比如将 命名空间”A”下的 gateway 绑定到命名空间”B”的 pod,这些都是由 istiod 控制,可以通过设置istiod 的环境变量 PILOT_SCOPE_GATEWAY_TO_NAMESPACE=false 实现严格匹配。 如果 selector 为 nil,gateway 会应用于所有的workloads
servers Server[] 主要用于描述负载均衡的端口,主要包括 name、hosts、port、tls,hosts 主要用于表示对外暴露的hosts值,可以多个,采用数组形式,可以是自定义域名,也可以是IP地址, port 表示对外暴露端口

Servers 字段说明

字段 类型 说明
port Port 主要由 number 端口号, protocel 类型组成,targetPort 目标接收端口
host string[] VirtualService 绑定到 gateway,通过匹配hosts生效,host可以是HTTP服务地址,也可以是TLS的SNI, host还可以接 namespace 做前缀,比如 namespace/*.example.com
tls ServerTLSSettings 主要用来做tls配置的,比如密钥公钥,tls 模式等

Istio 运维

通过curl 设置isito sidecar 的 debug 日志

对特定 sidecar 设置日志模式:

kubectl exec <pod名字> -c istio-proxy -- curl -XPOST -s -o /dev/null http://localhost:15000/logging?level=debug

Envoy 配置dump出来

通过在容器中用 curl 访问

kubectl exec <pod名字> -c istio-proxy -- curl http://127.0.0.1:15000/config_dump

在本地节点可以直接通过nsenter访问:

nsenter -t <容器Pid> -n curl http://127.0.0.1:15000/config_dump -o envoy.config

Envoy 配置文件主要看几个部分:

Listener(监听器)

istioctl pc listener <pod名字>.rcmd-tt

Route(路由器)

istioctl pc route <pod名字>.rcmd-tt

Cluster(集群,类似nginx的upstream)

istioctl pc cluster <pod名字>.rcmd-tt

Endpoint(端点,cluster对应的具体ip地址)

istioctl pc endpoint <pod名字>.rcmd-tt

Sidecar 运维

查看sidecar 容器的转发规则:
查看容器Pid:

docker inspect <容器名> | grep Pid

通过 nsenter 执行 iptables 命令查看:

$ nsenter -t 7499 -n iptables -t nat  -S
-P PREROUTING ACCEPT
-P INPUT ACCEPT
-P OUTPUT ACCEPT
-P POSTROUTING ACCEPT
-N ISTIO_INBOUND
-N ISTIO_IN_REDIRECT
-N ISTIO_OUTPUT
-N ISTIO_REDIRECT
-A PREROUTING -s 169.254.1.1/32 -i veth1 -m comment --comment "yangtse PREROUTING rules" -j RETURN
-A PREROUTING -p tcp -j ISTIO_INBOUND
-A OUTPUT -p tcp -j ISTIO_OUTPUT
-A POSTROUTING -o eth0 -m comment --comment "yangtse SNAT rules" -j MASQUERADE --random
-A ISTIO_INBOUND -p tcp -m tcp --dport 15008 -j RETURN
-A ISTIO_INBOUND -p tcp -m tcp --dport 22 -j RETURN
-A ISTIO_INBOUND -p tcp -m tcp --dport 15090 -j RETURN
-A ISTIO_INBOUND -p tcp -m tcp --dport 15021 -j RETURN
-A ISTIO_INBOUND -p tcp -m tcp --dport 15020 -j RETURN
-A ISTIO_INBOUND -p tcp -j ISTIO_IN_REDIRECT
-A ISTIO_IN_REDIRECT -p tcp -j REDIRECT --to-ports 15006
-A ISTIO_OUTPUT -s 127.0.0.6/32 -o lo -j RETURN
-A ISTIO_OUTPUT ! -d 127.0.0.1/32 -o lo -m owner --uid-owner 1337 -j ISTIO_IN_REDIRECT
-A ISTIO_OUTPUT -o lo -m owner ! --uid-owner 1337 -j RETURN
-A ISTIO_OUTPUT -m owner --uid-owner 1337 -j RETURN
-A ISTIO_OUTPUT ! -d 127.0.0.1/32 -o lo -m owner --gid-owner 1337 -j ISTIO_IN_REDIRECT
-A ISTIO_OUTPUT -o lo -m owner ! --gid-owner 1337 -j RETURN
-A ISTIO_OUTPUT -m owner --gid-owner 1337 -j RETURN
-A ISTIO_OUTPUT -d 127.0.0.1/32 -j RETURN
-A ISTIO_OUTPUT -j ISTIO_REDIRECT
-A ISTIO_REDIRECT -p tcp -j REDIRECT --to-ports 15001

这个可以看出, istio envoy 劫持了所有入口流量(ISTIO_IN_REDIRECT)到 15006 端口,出口流量(ISTIO_REDIRECT)到 15001

Istio 故障问题汇总

  1. ExternalName导致 istio 监听异常

Istio 针对 externalName 会有异常,主要是针对带 port 设置的 ExternalName,istio 会有一定概率(针对下游域名没有name类型的)生成一个监听 0.0.0.0:port 的 listener,这个监听器会将所有访问 port 的流量转发到 ExternalName。详细见:https://github.com/istio/istio/issues/20703

所以在安装 istio 的环境中使用ExternalName 不要添加 port 及可解决。

这个问题其实和ServiceEntry中使用 DNS 解析,两个端口重复导致流量被转发是一样的。(https://cloudnative.to/blog/istio-dns-proxy/)

在 istio 中,如果设置了两个相同 port 端口的域名解析,而且端口协议是TCP没有 VIP,则会出现流量交叉。
如下面两个 ServiceEntry:

apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: db1
namespace: ns1
spec:
hosts:
- mysql–instance1.us-east-1.rds.amazonaws.com
ports:
- name: mysql
number: 3306
protocol: TCP
resolution: DNS
---
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: db2
namespace: ns1
spec:
hosts:
- mysql–instance2.us-east-1.rds.amazonaws.com
ports:
- name: mysql
number: 3306
protocol: TCP
resolution: DNS

分别是mysql–instance1.us-east-1.rds.amazonaws.commysql–instance2.us-east-1.rds.amazonaws.com,由于其采用的是公共的DNS解析得到IP地址,sidecar 在监听 0.0.0.0:3306 流量的时候,并不知道他是去 db1 还是 db2 的,这时候所有流量都会被转发到其中一个目标。如果要解决这个问题,只能把resolution设置为None,将流量转发到应用程序请求的原始 IP。

文献

shikanon wechat
欢迎您扫一扫,订阅我滴↑↑↑的微信公众号!