参照前面内容中介绍的步骤,在Kubernetes集群中安装好Istio,并确保安装配置kubectl能够连接上Kubernetes集群。
首先通过命令行或者控制台创建命名空间bookinfo,并部署我们修改之后的官方bookinfo应用。在这个修改后的版本中去掉了details组件,并定义了ingressgateway。本示例中涉及到的文件可以从目录istio-meshexpansion中获取到。执行以下命令创建示例:
kubectl create ns bookinfo kubectl label namespace bookinfo istio-injection=enabled kubectl apply -n bookinfo -f ./bookinfo/bookinfo-without-details.yaml kubectl apply -n bookinfo -f ./bookinfo/destination-rule-all.yaml kubectl apply -n bookinfo -f ./bookinfo/bookinfo-gateway.yaml
我们的网格扩展示例是基于官方示例而修改的部署,其中details组件和数据库运行在Kubernetes之外的ECS(Linux虚拟机)上,如图10-4所示。

图10-4 网格扩展示例
正常运行之后,通过ingressgateway暴露的地址访问/productpage页面,效果如图10-5所示,details部分应该不能正常显示。
由前面示例部署图知道,有两个服务需要运行在ECS上,一个是details服务,另一个是数据库服务。
通过以下命令模拟(仅仅是用Docker模拟而已,如果没有在虚拟机上安装过Docker的话,可以参考Docker官方文档了解如何安装Docker)一个details服务,运行在ECS上并暴露端口9080,命令如下:
docker pull istio/examples-bookinfo-details-v1:1.8.0 docker run -d -p 9080:9080 --name details-on-vm istio/examples-bookinfo-details-v1:1.8.0

图10-5 示例运行效果
等待Docker容器启动之后,通过curl命令可以访问details服务,返回如下类似结果:
$ curl localhost:9080/details/1
{"id":1,"author":"William Shakespeare","year":1595,"type":"paperback","pages":200,
"publisher":"PublisherA","language":"English","ISBN-10":"1234567890","ISBN-13":
"123-1234567890"}
用于配置Sidecar来拦截端口的配置文件存在于/var/lib/istio/envoy/sidecar.env,其中使用环境变量ISTIO_INBOUND_PORTS可以设置响应的端口。具体配置参数包括如下内容:
# 用于配置Istio启动的环境变量 # 以逗号分隔的用于服务的CIDR列表。 如果设置,将运行iptables以允许Istio sidecar拦截对已配置地址的出站调用。 如果没有设置,将不会通过iptables使用Istio sidecar进行拦截。 # ISTIO_SERVICE_CIDR= # 暴露的服务名称。 # ISTIO_SERVICE=myservice # 用于将入站连接重定向到Envoy的模式。 # 此设置对出站流量没有影响,因为iptables REDIRECT模式始终用于出站连接。 # 如果设置为“REDIRECT”模式,则会使用iptables REDIRECT到NAT并重定向到Envoy。 # 需要注意的是,“REDIRECT”模式在重定向期间丢失源地址。 # 如果设置为“TPROXY”模式,则会使用iptables TPROXY重定向到Envoy。 # 与“REDIRECT”模式不同,“TPROXY”模式会保留源和目标IP地址和端口,以便它们可用于高级过滤和操作。 # 此外,“TPROXY”模式还将sidecar配置为使用CAP_NET_ADMIN功能运行,这是使用TPROXY所必需的。 # 如果未设置,则默认为“REDIRECT”。 # ISTIO_INBOUND_INTERCEPTION_MODE=REDIRECT # 当拦截模式为"TPROXY"时,在每个入站数据包上设置iptables skb(socketbuffer)标记, # 以便重定向到Envoy。 # 如果未设置,则默认为"1337". # ISTIO_INBOUND_TPROXY_MARK=1337 # 当拦截模式为"TPROXY"时,配置并用于将入站连接路由到回路接口的路由表的编号,以便重定向到Envoy。 # 如果未设置,则默认为"133"。 # ISTIO_INBOUND_TPROXY_ROUTE_TABLE=133 # 为入站服务设置的逗号分隔的本地端口列表,将使用Istio sidecar。 # 如果设置,将配置iptables规则以拦截入站流量并重定向到sidecar。 # 如果未设置,则不会启用任何规则 # ISTIO_INBOUND_PORTS= # 如果ISTIO_INBOUND_PORTS设置为*,该变量表示从入站拦截中排除的端口列表; # 端口22自动排除 # ISTIO_INBOUND_EXCLUDE_PORTS= # 集群命名空间 # ISTIO_NAMESPACE=default # 指定端点使用的IP地址。如果未设置,将使用'hostname --ip-address'获取地址. # 如果主机有多个IP,则需要设置 # ISTIO_SVC_IP= # 如果istio-pilot配置了mTLS身份验证,即--controlPlaneAuthPolicy MUTUAL_TLS,那么必须配置此项 # ISTIO_PILOT_PORT=15005 # ISTIO_CP_AUTH=MUTUAL_TLS # Envoy使用的端口,默认为15001 # ENVOY_PORT=15001 # 运行Envoy的用户 # ENVOY_USER=istio-proxy # 取消注释以启用调试 # ISTIO_AGENT_FLAGS="--proxyLogLevel debug" # stdout重定向的目录,将用于访问日志 # ISTIO_LOG_DIR=/var/log/istio # Istio二进制文件的安装目录 # ISTIO_BIN_BASE=/usr/local/bin # Istio配置的位置 # ISTIO_CFG=/var/lib/istio
在运行details服务的虚拟机上执行如下命令:
echo "ISTIO_INBOUND_PORTS=9080" > /var/lib/istio/envoy/sidecar.env systemctl restart istio
接下来注册details服务到Istio控制面板中,首先查找虚拟机的IP地址,用来加入服务网格,如下所示:
$ hostname -I 192.168.0.155
手工配置一个没有选择器的服务和端点,用来承载没有对应Kubernetes Pod的服务。例如在一个有权限且能够使用istioctl命令访问Kubernetes集群的环境中,注册details服务,命令如下所示:
$ istioctl -n bookinfo register details 192.168.0.155 http:9080
2019-02-20T06:47:41.790625Z info Registering for service 'details' ip '192.168.
0.155', ports list [{9080 http}]
2019-02-20T06:47:41.790678Z info 0 labels ([]) and 1 annotations ([alpha.istio.io/
kubernetes-serviceaccounts=default])
2019-02-20T06:47:41.833461Z warn Got 'services "details" not found' looking up svc 'details' in namespace 'bookinfo', attempting to create it
2019-02-20T06:47:41.862598Z warn Got 'endpoints "details" not found' looking up endpoints for 'details' in namespace 'bookinfo', attempting to create them
2019-02-20T06:47:41.877239Z info Before: found endpoints &Endpoints{ObjectMeta:
k8s_io_apimachinery_pkg_apis_meta_v1.ObjectMeta{Name:details,GenerateName:,Namespace:
bookinfo,SelfLink:/api/v1/namespaces/bookinfo/endpoints/details,UID:6b913b76-34db-
11e9-9f8f-00163e0c902f,ResourceVersion:460929,Generation:0,CreationTimestamp:2019-
02-20 14:47:41 +0800 CST,DeletionTimestamp:<nil>,DeletionGracePeriodSeconds:nil,Labels:
map[string]string{},Annotations:map[string]string{alpha.istio.io/kubernetes-servicea-
ccounts: default,},OwnerReferences:[],Finalizers:[],ClusterName:,Initializers:nil,},
Subsets:[],}
2019-02-20T06:47:41.877538Z info No pre existing exact matching ports list found,
created new subset {[{192.168.0.155 <nil> nil}] [] [{http 9080 }]}
2019-02-20T06:47:41.887099Z info Successfully updated details, now with 1 endpoints
2019-02-20T06:47:41.887416Z info Details: &Endpoints{ObjectMeta:k8s_io_apimachinery_pkg_apis_meta_v1.ObjectMeta{Name:details,GenerateName:,Namespace:bookinfo,SelfLink:/api/v1/namespaces/bookinfo/endpoints/details,UID:6b913b76-34db-11e9-9f8f-00163e0c902f,ResourceVersion:460930,Generation:0,CreationTimestamp:2019-02-20 14:47:41 +0800 CST,DeletionTimestamp:<nil>,DeletionGracePeriodSeconds:nil,Labels:map[string]string{},Annotations:map[string]string{alpha.istio.io/kubernetes-serviceaccounts: default,},OwnerReferences:[],Finalizers:[],ClusterName:,Initializers:nil,},Subsets:[{[{192.168.0.155 <nil> nil}] [] [{http 9080 TCP}]}],}
这个时候再次访问/productpage页面,效果如图10-6所示,details部分应该可以正常显示。

图10-6 示例的details部分
默认ratings服务不访问数据库,通过如下命令更新版本,使得ratings服务切换到访问数据库的版本:
kubectl apply -n bookinfo -f ./bookinfo/bookinfo-ratings-v2-mysql-vm.yaml kubectl apply -n bookinfo -f ./bookinfo/virtual-service-ratings-mysql-vm.yaml
此时访问/productpage页面,效果如图10-7所示,ratings部分不能正常显示,下一步就是在ECS上搭建数据库服务,并将之加入到Istio中。

图10-7 ratings服务还来加入到Istio
在虚拟机上安装数据库MariaDB,将其作为ratings服务的后端数据库服务;并配置MariaDB可以支持远程访问。在虚拟机上的终端窗口中执行如下命令:
apt-get update && apt-get install -y mariadb-server sed -i 's/127\.0\.0\.1/0\.0\.0\.0/g' /etc/mysql/mariadb.conf.d/50-server.cnf sudo mysql # 授予 root 权限 GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'password' WITH GRANT OPTION; FLUSH PRIVILEGES; quit; sudo systemctl restart mysql
在虚拟机上通过执行如下脚本内容进行ratings数据库的初始化:
CREATE DATABASE test;
USE test;
CREATE TABLE 'ratings' (
'ReviewID' INT NOT NULL,
'Rating' INT,
PRIMARY KEY ('ReviewID')
);
INSERT INTO ratings (ReviewID, Rating) VALUES (1, 2);
INSERT INTO ratings (ReviewID, Rating) VALUES (2, 2);
把上述脚本内容存为文件mysqldb-init.sql,并运行如下命令:
mysql -h 127.0.0.1 -ppassword < mysqldb-init.sql
与前面注册details服务类似,我们需要注册数据库服务到Istio控制面板中。用于配置Sidecar来拦截端口的配置文件存在于/var/lib/istio/envoy/sidecar.env,其中使用环境变量ISTIO_INBOUND_PORTS可以设置响应的端口。在运行数据库服务的虚拟机上执行如下命令:
echo "ISTIO_INBOUND_PORTS=9080,3306" > /var/lib/istio/envoy/sidecar.env systemctl restart istio
接下来注册数据库服务到Istio控制面板中,首先查找虚拟机的IP地址,用来加入服务网格如下所示:
$ hostname -I 192.168.0.155
手工配置一个没有选择器的服务和端点,用来承载没有对应Kubernetes pod的服务。例如在一个有权限且能够使用istioctl命令访问Kubernetes集群的环境中,注册details服务,如下所示:
$ istioctl -n bookinfo register mysqldb 192.168.0.155 3306
2019-02-20T07:08:20.762760Z info Registering for service 'mysqldb' ip '192.168.0.155', ports list [{3306 mysql}]
2019-02-20T07:08:20.762800Z info 0 labels ([]) and 1 annotations ([alpha.istio.io/kubernetes-serviceaccounts=default])
2019-02-20T07:08:20.800300Z warn Got 'services "mysqldb" not found' looking up svc 'mysqldb' in namespace 'bookinfo', attempting to create it
2019-02-20T07:08:20.824352Z warn Got 'endpoints "mysqldb" not found' looking up endpoints for 'mysqldb' in namespace 'bookinfo', attempting to create them
2019-02-20T07:08:20.835265Z info Before: found endpoints &Endpoints{ObjectMeta:
k8s_io_apimachinery_pkg_apis_meta_v1.ObjectMeta{Name:mysqldb,GenerateName:,Namespace:
bookinfo,SelfLink:/api/v1/namespaces/bookinfo/endpoints/mysqldb,UID:4e0bbad5-34de-
11e9-9f8f-00163e0c902f,ResourceVersion:464404,Generation:0,CreationTimestamp:2019-
02-20 15:08:20 +0800 CST,DeletionTimestamp:<nil>,DeletionGracePeriodSeconds:nil,Labels:
map[string]string{},Annotations:map[string]string{alpha.istio.io/kubernetes-serviceaccounts:
default,},OwnerReferences:[],Finalizers:[],ClusterName:,Initializers:nil,},Subsets:[],}
2019-02-20T07:08:20.835303Z info No pre existing exact matching ports list found, created new subset {[{192.168.0.155 <nil> nil}] [] [{mysql 3306 }]}
2019-02-20T07:08:20.848586Z info Successfully updated mysqldb, now with 1 endpoints
2019-02-20T07:08:20.848648Z info Details: &Endpoints{ObjectMeta:k8s_io_apimachinery_pkg_apis_meta_v1.ObjectMeta{Name:mysqldb,GenerateName:,Namespace:bookinfo,SelfLink:/api/v1/namespaces/bookinfo/endpoints/mysqldb,UID:4e0bbad5-34de-11e9-9f8f-00163e0c902f,ResourceVersion:464405,Generation:0,CreationTimestamp:2019-02-20 15:08:20 +0800 CST,DeletionTimestamp:<nil>,DeletionGracePeriodSeconds:nil,Labels:map[string]string{},Annotations:map[string]string{alpha.istio.io/kubernetes-serviceaccounts: default,},OwnerReferences:[],Finalizers:[],ClusterName:,Initializers:nil,},Subsets:[{[{192.168.0.155 <nil> nil}] [] [{mysql 3306 TCP}]}],}
经过这个步骤,Kubernetes pod和其他网格扩展包含的服务器就可以访问运行于这一服务器上的数据库服务了。
此时访问/productpage页面,效果如图10-8所示,details与ratings部分均能正常显示,而且这两个服务都是来自于ECS。从中可以更清晰地观察Bookinfo应用在输出方面的差异,评级记录值来自于数据库,所生成的评级显示与前面没有使用数据库的情况下生成的评级显示不同。

图10-8 ECS上的rating服务加入Istion之后的页面效果