上次说到,Istio 1.6新版本引入了Workload Entry来增强对于非K8S(虚拟机和裸机)工作负载的支持。在理论上,网格内的服务彼此之间可以通过服务名互相调用,但是对于网格外的服务,有哪些访问方式呢?本宝宝专门花了很大的功夫做了实验,今天就一起来看看。
先来看一些理论
目前,Istio对于外部服务的支持有三种:
- 通过
istioctl register
注册一个Service和一个Endpoint; - 通过ServiceEntry定义;
- 通过WorkloadEntry + ServiceEntry 组合定义。
第一种方式 istioctl register
在这种方式下,你需要将每个网格外服务都通过istioctl register
进行注册,这个命令将自动创建并部署对应的service
和endpoint
。注册进来的服务将被分配网格内部的域名,使用mTLS,并且直接受Istio管理。VM需要安装Istio各个组件(Envoy proxy、node-agent、istio-agent),并且保证可被istio控制平面访问。
第二种方式 ServiceEntry
什么是ServiceEntry?
先来看官网对ServiceEntry的定义:
ServiceEntry enables adding additional entries into Istio’s internal service registry, so that auto-discovered services in the mesh can access/route to these manually specified services.
也就是说,他是将其他非网格内服务加入Istio的服务发现,像网格内的服务一样进行管理。这里没有直接使用“外部服务”,是因为ServiceEntry适用的主体不仅仅包括网格外的服务(比如Web API),还包括一些处于网格内部但却不存在于平台的服务注册表中的服务(例如需要和 Kubernetes 服务沟通的一组虚拟机服务)。在这种方式下,该服务所处VM不需要安装Istio组件,只需要保证Istio能访问该服务即可。
ServiceEntry怎么定义外部服务?
对于外部服务,你需要按照下面的规则来配置:
-
hosts
:指定外部服务对应的主机名或DNS域名。对于HTTP流量,就是HTTP Header的Host;对于HTTPS/TLS的流量,就是SNI。 -
location
:指定为MESH_EXTERNAL,表示它是在网格外部,需要通过API来访问的接口。 -
ports
:外部服务的端口。 -
resolution
:表示服务发现的模式- 如果是一个明确IP,配置为
NONE
。 - 如果使用了endpoints,配置为
STATIC
。 - 如果使用了DNS域名,配置为
DNS
。
- 如果是一个明确IP,配置为
比如,如果想将外部网页服务注册到istio中,可以这样配置:
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: external-svc-https
spec:
hosts:
- api.dropboxapi.com
- www.googleapis.com
- api.facebook.com
location: MESH_EXTERNAL
ports:
- number: 443
name: https
protocol: TLS
resolution: DNS
---
ServiceEntry怎么定义内部服务?
在定义内部服务时,ServiceEntry需要和DestinationRule配合使用,也就是说,通过ServiceEntry定义服务,通过DestionationRule初始化到istio到服务的mTLS连接。
在这种方式下,你需要将每个网格外服务的地址都显式定义在ServiceEntry的endpoints字段中,但你不需要在VM或裸机上安装Istio。
第三种方式 WorkloadEntry
什么是WorkloadEntry?
WorkloadEntry的出现就是为了将ServiceEntry中的endpoints定义与ServiceEntry本身进行分离,使ServiceEntry专注定义非网格内部服务,而WorkloadEntry定义该服务来源(VM或裸机)的信息。引用官网的介绍,
WorkloadEntry enables operators to describe the properties of a single non-Kubernetes workload such as a VM or a bare metal server as it is are onboarded into the mesh. A WorkloadEntry must be accompanied by an Istio ServiceEntry that selects the workload through the appropriate labels and provides the service definition for a MESH_INTERNAL service (hostnames, port properties, etc.).
也就是说,WorkloadEntry也是与ServiceEntry配套使用的,那么由于ServiceEntry是和DestinationRule配套使用的,因此他们三个要一起使用。
在这种方式下,你可以用WorkloadEntry定义VM,再用ServiceEntry定义非网格内服务,并且可以选择一个或多个运行在K8S和非K8S上的服务,实现了最大程度的灵活性。
实验做什么
下面,我们就分别用这三种方式尝试将虚拟机上的服务注册到istio中进行管理。实验基于Istio提供的示例应用Bookinfo,增加Ratings服务的新版本,使其调用运行在VM上的mysql作为后端。应用架构图如下:
环境说明
为了做实验,专门用了一台阿里云和一台华为云,配置了安全组,使其相互能访问。其中,阿里云上跑Istio,华为云作为VM跑mysql服务:
阿里云ECS:4核8G、CentOS 8.0、Docker(v18.06.1)、kubectl(v1.15.0)、minikube(v1.2.0阿里社区版)、istio(1.6.2)
华为云云耀云:2核4G、Ubuntu 18.04、Docker(v19.03.11)、kubectl(v1.15.0)、minikube(v1.1.0阿里社区版)、mysql(v10.1.44-MariaDB)
(由于目前sidecar安装只提供了deb版本,对于CentOS需要手动make编译安装,因此VM直接选用Ubuntu。)
实验怎么做
在本宝宝看了大量文档连蒙带猜后,终于理出了头绪,因此专门分开介绍在VM和Istio两端分别需要做怎样的配置。
首先说VM端
VM起一个Mysql服务,里面放评分数据,并作为ratings服务的后端。
- 安装Envoy sidecar
curl -L https://storage.googleapis.com/istio-release/releases/1.6.3/deb/istio-sidecar.deb
sudo dpkg -i istio-sidecar.deb
配置Mysql
Mysql默认只允许本地访问,于是先要配置使其允许远程访问。详细步骤请参考《填坑指南 1.如何开启Mysql远程访问》。将评分数据加入Mysql
curl -q https://raw.githubusercontent.com/istio/istio/release-1.6/samples/bookinfo/src/mysql/mysqldb-init.sql | mysql -u root -ppassword
示例使用名为test
的数据库,可以查询并自定义评分数据。
MariaDB [(none)]> use test
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
MariaDB [test]> select * from ratings;
+----------+--------+
| ReviewID | Rating |
+----------+--------+
| 1 | 3 |
| 2 | 2 |
+----------+--------+
2 rows in set (0.01 sec)
至此,VM需要配置的内容就结束了。下面来看Istio端。
再说istio端
Istio端需要将VM上的Mysql加入网格中,并配置服务调用策略,使Ratings服务调用VM上的Mysql服务查询评分数据。
- 启用Istio meshExpansion。
在安装Istio的时需要开启meshExpansion功能。
istioctl install --set profile=demo --set values.global.meshExpansion.enabled=true
2(*). 开启Kiali
Kiali是为Istio提供图形化界面Dashboard的开源项目,可以通过它监控网格内服务的实时状态,以及网格配置。为了能够监测流量流向,我们开启Kiali,详细步骤请参考《填坑指南->2. 如何开启Kaili进行流量监控?》。
- 部署并应用v2-mysql版本的ratings服务。
查看了应用源码,发现在ratings服务的源码中,是从环境变量中取值初始化DB连接。
因此,在ratings服务的Deployment中,也需要将后端DB连接信息传入环境变量。
部署并应用v2-mysql版本ratings,MYSQL_DB_HOST
使用服务名mysqldb
,istio会自动寻找注册过的名为mysqldb
的Service或ServiceEntry。
部署并应用应用新版本VirtualService。
使用virtual-service-ratings-mysql.yaml,使流量应用v3版本的reviews服务和v2-mysql版本的ratings服务。在Istio中注册”mysqldb“。
上文介绍到有三种方式配置非K8S服务,下面就分别试一下。
方式一:使用istioctl register
- 注册
mysqldb
服务。
它会默认创建出来同名的Service和endpoints。
[root@node2 kube]# istioctl register mysqldb 124.**.65.221 3306
2020-06-23T06:58:15.842443Z warn Got 'services "mysqldb" not found' looking up svc 'mysqldb' in namespace 'default', attempting to create it
2020-06-23T06:58:15.852592Z warn Got 'endpoints "mysqldb" not found' looking up endpoints for 'mysqldb' in namespace 'default', attempting to create them
- 确认service和endpoint状态。
[root@node2 kube]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
details ClusterIP 10.101.253.166 9080/TCP 50m
frontend NodePort 10.102.61.100 80:30004/TCP 16d
kubernetes ClusterIP 10.96.0.1 443/TCP 17d
mongodb ClusterIP 10.100.95.126 27017/TCP 14d
mysqldb ClusterIP 10.97.255.81 3306/TCP 114s
productpage ClusterIP 10.109.248.58 9080/TCP 50m
ratings ClusterIP 10.99.19.120 9080/TCP 50m
reviews ClusterIP 10.100.95.201 9080/TCP 50m
[root@node2 kube]# kubectl get endpoints
NAME ENDPOINTS AGE
details 172.17.0.21:9080,172.17.0.7:9080 48m
kubernetes 172.26.205.16:8443 17d
mongodb 172.17.0.24:27017 14d
mysqldb 124.**.65.221:3306 35s
productpage 172.17.0.9:9080 48m
ratings 172.17.0.14:9080,172.17.0.26:9080 48m
reviews 172.17.0.10:9080,172.17.0.11:9080,172.17.0.25:9080 48m
-
访问bookinfo应用。
先在华为云上将评分数据更新为五星,五星,之后调用productpage。可以看到评分已经发生变化。
使用Kiali查看调用路径,可以看到v2-mysql版本的ratings调用了mysqldb服务。
方式二:使用ServiceEntry
- 定义并部署ServiceEntry
-
hosts
:对于非HTTP的流量,该字段不生效,它将后面通过address或endpoint定义的服务映射为网格内部的一个虚拟服务,以hosts标识,并遵循istio内部的服务名定义格式 。 -
endpoints
:表示与网格服务相关的网络地址,可以是IP或主机名,也可以用多个address组成一个vip组。
按照上述规则,在hosts里填mysqldb
,在endpoints
里填VM的IP。
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: mysqldb-se
spec:
hosts:
- mysqldb
location: MESH_INTERNAL
ports:
- number: 3306
name: mysql
protocol: mysql
resolution: STATIC
endpoints:
- address: 124.**.65.221
---
- 定义并部署DestinationRule
-
hosts
:表示规则适用的对象,这里指定以ServiceEntry方式注册的网格外服务名称。 -
trafficPolicy
:规则内容的定义,这里只规定了tls的模式,在ISTIO_MUTUAL
模式下,Istio 会依据内部实现机制自动设置密钥和证书的路径,不需要手动指定clientCertificate、 privateKey和 caCertificates的位置。
按照上述规则,在host里填mysqldb
。
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: mtls-mysqldb
spec:
host: mysqldb
trafficPolicy:
tls:
mode: ISTIO_MUTUAL
---
-
访问bookinfo应用。
先在华为云上将评分数据更新为三星和五星,之后调用productpage。可以看到评分已经发生变化。
使用Kiali查看调用路径,可以看到v2-mysql版本的ratings调用了ServiceEntry类型的mysqldb-se
,同时出现了PassthroughtCluster
。想了解PassthroughCluster,请参考《填坑指南->3. PassthroughCluster是什么鬼?》
方式三:使用WorkloadEntry
- 定义并部署WorkloadEntry
-
address
: 表示与网格服务相关的网络地址,可以是IP或主机名。
按照上述规则,在adddress中配置VM的IP,并添加标签”mysqlvm“。
apiVersion: networking.istio.io/v1alpha3
kind: WorkloadEntry
metadata:
name: mysqldb-we
spec:
address: 124.**.65.221
labels:
app: mysqlvm
instance-id: ubuntu-vm-mariadb
class: vm
---
- 定义并部署ServiceEntry
-
workloadSelector
:通过label来选择K8S上有这个标签的Pod,或通过WorkloadEntry定义的VM上的工作负载。也就是说,通过这种方式,就不需要关心工作负载是跑在哪里的了,只需要指定应用的标签,istio会自动选择有相应标签的工作负载。
按照上述规则,在hosts里填mysqldb
,在workloadSelector
通过标签”mysqlvm“定义相应的WorkloadEntry。
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: mysqldb-vm-se
spec:
hosts:
- mysqldb
location: MESH_INTERNAL
ports:
- number: 3306
name: mysql
protocol: mysql
resolution: STATIC
workloadSelector:
labels:
app: mysqlvm
---
- 定义并部署DestinationRule
在DestinationRule中配置使用ISTIO_MUTUAL连接mysqldb
。
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: mtls-mysqldb-vm
spec:
host: mysqldb
trafficPolicy:
tls:
mode: ISTIO_MUTUAL
---
-
访问bookinfo应用。
先在华为云上将评分数据更新为一星和一星,之后调用productpage。可以看到评分已经发生变化。
使用Kiali查看调用路径,可以看到v2-mysql版本的ratings调用了ServiceEntry类型的mysqldb-vm-se
。
吐槽加总结一下
这次做实验本来是为了尝试了新版本的WorkloadEntry,但是由于Istio的文档实在是太乱,网上的经验帖又太少,看了不下几十个链接,最终只能连蒙带猜,根据属性说明尝试配置,算是成功了,但总觉得Istio乱花渐欲的配置实在是太迷,还有很多不是理解不够深刻的地方。
走了很多弯路,特意总结出这三种访问外部服务的方式,附带填坑指南供大家参考。
喜欢本宝宝就请多多关注哦。
填坑指南
1. 如何开启Mysql远程访问?
1)注释掉bing-address。
Mariadb的配置文件位置和普通Mysql有所不同,bind-address在/etc/mysql/mariadb.conf.d/50-server.cnf
这里。
vi /etc/mysql/mariadb.conf.d/50-server.cnf
# Instead of skip-networking the default is now to listen only on
# localhost which is more compatible and is not less secure.
#bind-address = 127.0.0.1
2) 使用GRANT
命令增加允许远程访问的host。
GRANT ALL ON *.* to root@'39.**.145.34' IDENTIFIED BY 'password';
FLUSH PRIVILEGES;
MariaDB [mysql]> select host, user from user;
+---------------+------+
| host | user |
+---------------+------+
| % | root |
| 39.**.145.34 | root |
| localhost | root |
+---------------+------+
3 rows in set (0.00 sec)
2. 如何开启Kiali进行流量监控?
1)查看Kiali服务状态。
[root@node2 ~]# kubectl get svc -n istio-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
...
kiali ClusterIP 10.103.7.219 20001/TCP 3d5h
...
2)启用port-forward,将20001端口直接暴露出来。
kubectl -n istio-system port-forward --address 0.0.0.0 $(kubectl -n istio-system get pod -l app=kiali -o jsonpath='{.items[0].metadata.name}') 20001:20001 &
3)打开浏览器,使用
3. PassthroughCluster是什么鬼?
参考官网说明,监控被阻止的和透传的外部服务流量。
为了更好的监控外部流量,Istio 控制平面使用了预定义集群BlackHoleCluster
和Passthrough
来配置 sidecar 代理,它们的作用分别是阻止和通过所有流量。当将global.outboundTrafficPolicy.mode
设置为 ALLOW_ANY
时, PassthroughCluster 是在 Envoy 配置中创建的虚拟集群。在此模式下,允许流向外部服务的所有流量。
为了实现此目的,将使用 SO_ORIGINAL_DST 且监听 0.0.0.0:15001 的默认虚拟出站监听器设置为 TCP 代理,并将 PassthroughCluster 作为静态集群。 对于每个基于端口/协议的监听器,虚拟路由配置都会添加 PassthroughCluster 以作为默认路由,发向PassthroughCluster的请求会被直接发送到其请求中要求的原始目地的,Envoy不会对请求进行重新路由。