服务网格
什么是服务网格:
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 有:Gateway
、VirtualService
、Destination
。
Gateway
Gateway 是用来为集群提供流量进出配置负载均衡,其由 istio 的 ingress-gateway 组件实现,通过 selector 绑定 istio-ingress-gateway 的 pod。
字段 | 类型 | 说明 |
---|---|---|
selector | map<string, string> | 主要用于绑定 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 配置
通过在容器中用 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
查看istio proxyv2支持哪些监控命令
打开 15000 端口:$ kubectl port-forward -nrcmd test-tool-5f9f6dfccb-hzxwt 15000:15000
Forwarding from 127.0.0.1:15000 -> 15000
Forwarding from [::1]:15000 -> 15000
用浏览器打开 127.0.0.1:15000
查看:
命令:/: Admin home page
/certs: 打印机器证书
/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
/drain_listeners: drain listeners
/healthcheck/fail: cause the server to fail health checks
/healthcheck/ok: cause the server to pass health checks
/heapprofiler: enable/disable the heap profiler
/help: print out list of admin commands
/hot_restart_version: print the hot restart compatibility version
/init_dump: dump current Envoy init manager information (experimental)
/listeners: 打印监听器信息
/logging: 查询或改变日志级别
/memory: print current allocation/heap usage
/quitquitquit: exit the server
/ready: print server state, return 200 if LIVE, otherwise return 503
/reopen_logs: reopen access logs
/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
/stats/recentlookups: Show recent stat-name lookups
/stats/recentlookups/clear: clear list of stat-name lookups and counter
/stats/recentlookups/disable: disable recording of reset stat-name lookup names
/stats/recentlookups/enable: enable recording of reset stat-name lookup names
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 故障问题汇总
- 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.com
和mysql–instance2.us-east-1.rds.amazonaws.com
,由于其采用的是公共的DNS解析得到IP地址,sidecar 在监听 0.0.0.0:3306
流量的时候,并不知道他是去 db1
还是 db2
的,这时候所有流量都会被转发到其中一个目标。如果要解决这个问题,只能把resolution
设置为None,将流量转发到应用程序请求的原始 IP。
- 如何自定义label控制sidecar注入
istiod 主要是通过MutatingWebhookConfiguration
来控制,我们看1.8.3版本的istio默认的配置:- admissionReviewVersions:
- v1beta1
- v1
clientConfig:
service:
name: istiod
namespace: istio-system
path: /inject
port: 443
failurePolicy: Fail
matchPolicy: Exact
name: sidecar-injector.istio.io
namespaceSelector:
matchLabels:
istio-injection: enabled
reinvocationPolicy: Never
rules:
- apiGroups:
- ""
apiVersions:
- v1
operations:
- CREATE
resources:
- pods
scope: '*'
sideEffects: None
timeoutSeconds: 30
可以看到,默认只有 namespaceSelector,也就是只针对命名空间的label为istio-injection: enabled
的会生效,如果我们希望不仅针对命名空间生效,同时可以针对pod生效,而且pod label的优先级大于命名空间,怎么设置呢?
我们可以通过组合两个 webhook 来实现:- admissionReviewVersions:
- v1beta1
- v1
clientConfig:
service:
name: istiod
namespace: istio-system
path: /inject
port: 443
failurePolicy: Fail
matchPolicy: Exact
name: sidecar-injector.istio.io
namespaceSelector:
matchLabels:
istio-injection: enabled
objectSelector:
matchExpressions:
- key: sidecar.istio.io/inject
operator: NotIn
values:
- "disabled"
reinvocationPolicy: Never
rules:
- apiGroups:
- ""
apiVersions:
- v1
operations:
- CREATE
resources:
- pods
scope: '*'
sideEffects: None
timeoutSeconds: 30
- admissionReviewVersions:
- v1beta1
- v1
clientConfig:
service:
name: istiod
namespace: istio-system
path: /inject
port: 443
failurePolicy: Fail
matchPolicy: Exact
name: object.sidecar-injector.istio.io
objectSelector:
matchLabels:
sidecar.istio.io/inject: "enabled"
reinvocationPolicy: Never
rules:
- apiGroups:
- ""
apiVersions:
- v1
operations:
- CREATE
resources:
- pods
scope: '*'
sideEffects: None
timeoutSeconds: 30
这里有两个 admissionReviewVersions,一个是包涵了 namespaceSelector 和 objectSelector 的,表示命名空间有 istio-injection: enabled
同时,pod上面没有sidecar.istio.io/inject: disabled
的pod 才会自动注入。另一个是pod 的label有sidecar.istio.io/inject: enabled
的 pod 会自动注入。