我们知道在服务网格集群中的每个工作负载实例上都会透明地注入一个 Istio sidecar 代理,这个代理拦截负载的出入流量,并根据配置完成相应的流量管理,包括流量、安全、可观测性等等。为了更加细粒度的控制代理的行为,从 1.1 版本开始 Istio 便引入了和服务网格数据面 Sidecar 同名的 Sidecar CRD 资源对象,控制负载上的出入流量以及课访问的目标服务等。
Sidecar 对象描述了 sidecar 代理的配置,sidecar 代理管理与其连接的工作负载的 inbound 和 outbound 流量。默认情况下,Istio 将为网格中的所有 sidecar 代理服务,使其具有到达网格中每个工作负载所需的必要配置,并在与工作负载关联的所有端口上接收流量。Sidecar 资源提供了一种的方法,在向工作负载转发流量或从工作负载转发流量时,微调端口集合和代理将接收的协议,此外,可以限制代理在从工作负载转发 outbound 流量时可以达到的服务集合。
比如我们可以创建一个如下所示的 Sidecar 对象:
apiVersion:.NETworking.istio.io/v1beta1
kind: Sidecar
metadata:
name: test-sc
spec:
egress:
- hosts:
- "istio-system/*"
- "default/*"
在上面的 Sidecar 对象中我们指定了 egress 字段,这个字段用于指定 sidecar 代理的出口流量,其中 hosts 字段用于指定 sidecar 代理可以访问的目标服务,这里我们指定了 istio-system/* 和 default/*,意思是我们可以控制 default 命名空间下的 sidecar 代理只可以访问 istio-system 和 default 命名空间下的服务,其他命名空间下的服务则无法访问。
整体上 Sidecar 对象的核心包括四个字段:workloadSelector、ingress 与 egress、outboundTrafficPolicy。
port:这是一个必选的字段,表示监听器对应的端口。
bind:监听器绑定的地址。
captureMode:配置流量捕获模式,与 egress 中的 captureMode 字段一样。
defaultEndpoint:也是必选字段,表示流量的转发目标地址,比如 127.0.0.1:port 或者 0.0.0.0:port。
outboundTrafficPolicy:这个字段用来配置 sidecar 代理对应工作负载的出流量控制,该字段有两种访问配置:
ALLOW_ANY:表示允许访问任意服务,sidecar 代理在拦截到这个出流量后,会直接透传。
REGISTRY_ONLY:sidecar 代理会拦截所有的出口流量,只允许服务网格内部服务可以被访问,对于外部服务需要使用 ServiceEntry 注册才可以被访问。
Sidecar 对象可以定义在根命名空间 istio-system 下,这样就会应用到所有命名空间下的工作负载上,比如我们可以创建一个如下所示的 Sidecar 对象:
# global-sidecar.yaml
apiVersion: networking.istio.io/v1beta1
kind: Sidecar
metadata:
name: default
namespace: istio-system
spec:
egress:
- hosts:
- "./*"
上面的这个 Sidecar 对象定义在 istio-system 命名空间下,这样就会应用到所有命名空间下的工作负载上,其中 egress 字段中的 hosts 字段指定了可以访问的服务,这里我们指定了 "./*",表示限制整个服务网格中的服务只能访问本命名空间的服务。在实践中我们推荐使用这种方式在全局范围定义一个统一的 Sidecar 规则,然后在特定的命名空间下再定义一个 Sidecar 对象来覆盖全局的 Sidecar 规则。
比如我们可以在 default 命名空间下创建一个如下所示的 Sidecar 对象来覆盖上面全局的这个对象:
# default-sidecar.yaml
apiVersion: networking.istio.io/v1beta1
kind: Sidecar
metadata:
name: default
namespace: default
spec:
egress:
- hosts:
- "foo/*"
这个对象就允许 default 命名空间的服务可以访问 foo 命名空间的服务。
同样我们还可以使用 workloadSelector 字段来指定 sidecar 代理所属的工作负载,比如我们可以创建一个如下所示的 Sidecar 对象:
# default-sidecar.yaml
apiVersion: networking.istio.io/v1beta1
kind: Sidecar
metadata:
name: default
namespace: default
spec:
workloadSelector:
labels:
App: bar
egress:
- hosts:
- "bar/foo-api"
上面的这个对象只会应用到 app: bar 标签的工作负载上,并覆盖以上命名空间级别的规则,使得 default 命名空间下面的 app: bar 标签的工作负载只能访问 bar 命名空间下面的 foo-api 服务。
接下来我们使用 sleep 和 httpbin 应用来进行测试说明,将这两个应用部署到 default 和 other 两个命名空间下面:
kubectl create ns other
kubectl label ns other istio-injectinotallow=enabled
kubectl apply -f samples/sleep/sleep.yaml -n default
kubectl apply -f samples/sleep/sleep.yaml -n other
kubectl apply -f samples/httpbin/httpbin.yaml -n default
kubectl apply -f samples/httpbin/httpbin.yaml -n other
默认情况下,注入了 Istio 的工作负载会进行全网格的传播,假设 default 和 other 两个不相干的命名空间,other 中有大量的服务,而 default 中只有几个,因为路由传播的关系,default 命名空间中的工作负载,其 sidecar 代理中也会带上 other 命名空间中的路由信息。例如:
$ istioctl proxy-config clusters sleep-9454cc476-jfw97 |grep other
httpbin.other.svc.cluster.local 8000 - outbound EDS
sleep.other.svc.cluster.local 80 - outbound EDS
可以看到,在 default 命名空间中的 Pod,保存了其它命名空间中的路由信息。这不管是对内存消耗还是路由控制来说,都会造成一定浪费,这个时候我们就可以定义一个 Sidecar 资源,限制 sleep 服务只访问同一命名空间的其他服务,如下所示:
# sleep-sidecar.yaml
apiVersion: networking.istio.io/v1alpha3
kind: Sidecar
metadata:
name: sleep
spec:
workloadSelector:
labels:
app: sleep
egress:
- hosts:
- "default/*"
直接应用上面的资源对象即可:
$ kubectl apply -f sleep-sidecar.yaml
$ kubectl get sidecar
NAME AGE
sleep 16s
这个时候可以看到在 sleep 应用中只剩下了本命名空间之内的服务了:
$ istioctl proxy-config clusters sleep-9454cc476-jfw97
SERVICE FQDN PORT SUBSET DIRECTION TYPE DESTINATION RULE
80 - inbound ORIGINAL_DST
BlackHoleCluster - - - STATIC
InboundPassthroughClusterIpv4 - - - ORIGINAL_DST
PassthroughCluster - - - ORIGINAL_DST
agent - - - STATIC
httpbin.default.svc.cluster.local 8000 - outbound EDS
kubernetes.default.svc.cluster.local 443 - outbound EDS
prometheus_stats - - - STATIC
sds-grpc - - - STATIC
sleep.default.svc.cluster.local 80 - outbound EDS
xds-grpc - - - STATIC
zipkin - - - STRICT_DNS
现在我们可以在 sleep 应用中去访问下 httpbin 的应用:
$ kubectl exec -it sleep-9454cc476-jfw97 -- curl http://httpbin.default:8000/ip
{
"origin": "127.0.0.6"
}
可以看到 default 命名空间下面的应用可以正常访问,那么对于 other 命名空间下面的服务正常就不能访问了。
$ kubectl exec -it sleep-9454cc476-jfw97 -- curl http://httpbin.other.svc.cluster.local:8000/ip
可以看到 default 命名空间下面的应用无法访问 other 命名空间下面的服务了。
Istio 默认情况下,服务网格内部的所有数据面代理都通过 xDS 从控制面获取全量的配置,这种方式在数据面代理数量较少的情况下是没有问题的,但是当数据面代理数量较多的大规模服务网格的场景下,这种方式显然会造成性能问题,全量的配置会引起数据面代理的内存暴涨,所以 Sidecar 对象是非常有必要的,通过 Sidecar 对象只维护少量依赖服务的配置,可以大大减少无用的内存消耗,所以在生产环境中我们推荐大家使用 Sidecar 对象来控制数据面代理的配置。