云计算和容器技术开启全新的系统架构和思维方式演进,k8s(Kubernetes)已经是当前容器编排领域实质上的王者。K8s管理一个高度可用的计算机(Linux系统)集群,所有的应用以容器的方式运行在k8s内部并被管理。对于一些老旧的应用来说,不进行架构层次的重构与改变,很难直接被k8s管理。
随着微服务体系的发展,逐渐出现了服务网格(Service Mesh)的概念,它提供了一种透明的、与编程语言无关的方式,使网络配置、安全配置以及遥测等操作能够灵活而简便地实现自动化。Istio作为服务网格中的后起之秀,在市场和社区方面表现非常火爆。
Istio 1.7 版本其中一项备受期待的扩展便是专注于将服务网格扩展到基于虚拟机的云环境。Istio 1.8 版本新增了 ServiceEntry 及 智能 DNS 代理,使得如虚拟机这样的非 Kubernetes 工作负载可以在 Istio 中成为像 Pod 一样的资源进行管理。
下文所使用的的istio版本为v1.8,且以了解k8s与istio为基础进行阐述。
istio与虚拟机的安装教程参见官方的单个网络网格中的虚拟机与虚拟机安装。
Istio能够支持虚拟机上的服务,主要基于以下几点k8s和istio自身的特性:
正常情况下,一个微服务(Deployment)可以具备多个实例(Pod),每个实例里运行一个容器(Docker Container),该容器即为业务应用程序。
原生的k8s pod,支持运行多个容器。同一个pod内的所有容器,共享同一个网络,也可以通过配置使得多个容器共享同一块磁盘目录(可持久化也可临时内部目录)。
Istio支持自动与手动配置两种方式来为实例注入边车(Sidecar)容器,即在实例中再添加一个容器,这个名为istio-proxy
的容器是基于Envoy的代理容器,istio能够实现流量管理幕后主要运行的实体便是该代理容器。
实际上istio为每个实例又多注入了一个初始化容器(initContainer),初始化容器通过istio-iptables
命令组装了iptables规则,用于 pod 中设置 iptables 端口转发。
istio-iptables -p 15001 -z 15006 -u 1337 -m REDIRECT -i * -x * -b * -d 15090,15021,15020
istio-iptables [flags]
-p: 指定重定向所有 TCP 流量的 sidecar 端口(默认为 $ENVOY_PORT = 15001)
-m: 指定入站连接重定向到 sidecar 的模式,“REDIRECT” 或 “TPROXY”(默认为 $ISTIO_INBOUND_INTERCEPTION_MODE)
-b: 逗号分隔的入站端口列表,其流量将重定向到 Envoy(可选)。使用通配符 “*” 表示重定向所有端口。为空时表示禁用所有入站重定向(默认为 $ISTIO_INBOUND_PORTS)
-d: 指定要从重定向到 sidecar 中排除的入站端口列表(可选),以逗号格式分隔。使用通配符“*” 表示重定向所有入站流量(默认为 $ISTIO_LOCAL_EXCLUDE_PORTS)
-o:逗号分隔的出站端口列表,不包括重定向到 Envoy 的端口。
-i: 指定重定向到 sidecar 的 IP 地址范围(可选),以逗号分隔的 CIDR 格式列表。使用通配符 “*” 表示重定向所有出站流量。空列表将禁用所有出站重定向(默认为 $ISTIO_SERVICE_CIDR)
-x: 指定将从重定向中排除的 IP 地址范围,以逗号分隔的 CIDR 格式列表。使用通配符 “*” 表示重定向所有出站流量(默认为 $ISTIO_SERVICE_EXCLUDE_CIDR)。
-k:逗号分隔的虚拟接口列表,其入站流量(来自虚拟机的)将被视为出站流量。
-g:指定不应用重定向的用户的 GID。(默认值与 -u param 相同)
-u:指定不应用重定向的用户的 UID。通常情况下,这是代理容器的 UID(默认值是 1337,即 istio-proxy 的 UID)。
-z: 所有进入 pod/VM 的 TCP 流量应被重定向到的端口(默认 $INBOUND_CAPTURE_PORT = 15006)。
使得除了5090 端口(Mixer 使用)和 15092 端口(Ingress Gateway)除外的所有入站(Inbound)流量重定向到 15006 端口(Sidecar),再拦截应用容器的出站(Outbound)流量经过 sidecar 处理(通过 15001 端口监听)后再出站。关于 Istio 中端口用途可参考 Istio 官方文档。
通过sidecar模式,可以在边车容器中添加更多实用的功能,而无需额外第三方组件配置或修改应用程序代码,做到无侵入式配置。
当前k8s版本通过deployment与pod等资源类型用来跟踪与定义应用实例,那么istio想要管理虚拟机,也得需要一种资源类型来关联与配置虚拟机,而且这种资源类型还能兼容istio其他功能如流量管理。
因此在istio v1.8版本,新增了ServiceEntry
资源类型。
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: external-vm-app
spec:
hosts:
- foo.bar.com
location: MESH_EXTERNAL
ports:
- number: 80
name: http
protocol: HTTP
resolution: STATIC
endpoints:
- address: us.foo.bar.com
ports:
https: 8080
- address: 192.168.1.1
ports:
https: 9080
- address: 192.168.1.2
ports:
https: 7080
上述配置描述了当在网格内访问foo.bar.com
地址时,将被转发代理到endpoints中指定的多个虚机静态地址相应端口服务,此时的hosts配置可以认为是一种k8s内的服务(service)。
spec.hosts
中指定的主机名将会与网格内的虚拟服务
(VirtualService)和目标规则
(DestinationRule)中的配置进行匹配,以达到负载均衡等流量管理的功能;该字段也可以去匹配HTTP流量想系统中的Host\Authority
字段;详细配置含义可参考官网;
定义了ServiceEntry资源之后,仍然可以通过istio自带的虚拟服务
对多台虚机进行流量管理。至此,k8s集群内(被istio管理)的实例通过ServiceEntry就可以像访问本地其他service一样访问虚拟机了。
通过ServiceEntry,网格中的服务可以轻松的访问虚拟机上的服务,但是反过来却仍然遇到一些问题: 虚拟机上的服务如何做到k8s集群内pod访问其他服务的service一样访问网格内的服务呢?
众所周知,k8s内的实例ip会随着实例的消亡与重建而发生改变,因此k8s引入了service与kube-dns
\coredns
等来解决service解析的问题。
按照教程在配置虚拟机的过程中,我们会在虚拟机上也启动istio服务,这个服务不仅仅具备istiod的控制层功能,同时也是该虚机的sidecar,这个sidecar中内置了一个DNS代理。虚拟机上的istiod会缓存一份k8s集群内的所有主机到IP(hostname-to-IP-address)的映射表到DNS代理中。
同所有的DNS工作原理一样,当在虚机中访问一个域名时,会优先查找这个内置的DNS代理,若未找到,则会继续查询上游DNS服务器(在/etc/resolv.conf
定义)。
此时,在虚拟机上就可以轻松访问网格内部的服务或被网格管理的其他虚拟机。
K8s中通过Ingres控制器来处理外部进入集群的流量,但是由于其是一个面向HTTP工作负荷的简单规范,并且不能进行流量管理,因此Istio引入了网关(gateway)的资源对象。
Istio默认提供了入站网关(Ingressgateway)与出站网关(Egressgateway)用于管理入站和出站流量,实际上这两个网关所使用的镜像与注入服务的边车容器是同一个镜像,因此istio网关的实现其实也是一个基于Envoy的封装,是一个代理。
注意:
这里讲的IngressGateway与Gateway不是一个概念,IngressGateway以deployment的形式部署在集群内部,是一类实体;Gateway是istio用来配置IngressGateway的一种k8s资源,只是一种抽象配置。
网关资源本身只能配置L4-L6的负载均衡属性,例如暴露的端口,TLS设置等。但网关可以和绑定一个虚拟服务
,在虚拟服务
中可以配置七层路由规则,这些七层路由规则包括根据服务版本对请求进行导流,故障注入,HTTP重定向,HTTP重写等所有网格内部支持的路由规则。
以上适用于网格内部的服务通信流程。
根据官网安装虚拟机文档,在网格内部署东西网关
(EastwestGateway)并且通过示例配置暴露控制平面供外部访问。
$ samples/multicluster/gen-eastwest-gateway.sh --single-cluster | istioctl install -y -f -
$ kubectl apply -f samples/multicluster/expose-istiod.yaml
上述samples目录为istio源码中的目录。
上述操作之后,网格内部运行了一个名为istio-eastwestgateway
的deployment(也是与边车容器相同镜像),并且配置了相应的网关
、虚拟服务
、目标规则
。
# kubectl -nistio-system get deploy istio-eastwestgateway
NAME READY UP-TO-DATE AVAILABLE AGE
istio-eastwestgateway 1/1 1 1 3d5h
# kubectl -nistio-system get gateway istiod-gateway
NAME AGE
istiod-gateway 3d5h
# kubectl -nistio-system get vs istiod-vs
NAME GATEWAYS HOSTS AGE
istiod-vs [istiod-gateway] [istiod.istio-system.svc.cluster.local] 3d5h
# kubectl -nistio-system get dr istiod-dr
NAME HOST AGE
istiod-dr istiod.istio-system.svc.cluster.local 3d5h
以上配置使得流入15012\15017端口的TCP流量先经过东西网关
再进入网格,并且根据虚拟服务
的规则转发到istiod.istio-system.svc.cluster.local
主机。
近期针对istio对接虚拟机功能进行了调研 ,并且在环境中实际操作得到验证,再根据其中涉及到的相关技术和原理进行总结。