安装
下载
1
2
|
[root@kube-mas ~]# curl -L https://istio.io/downloadIstio | ISTIO_VERSION=1.6.8 TARGET_ARCH=x86_64 sh
指定版本为1.6.8 架构为x86_64,如果下载速度慢,建议使用本地迅雷下载之后再传到服务器上
|
解压并设置环境变量
1
2
3
4
5
|
[root@kube-mas ~]# tar -xf istio-1.6.8-linux-amd64.tar.gz
[root@kube-mas ~]# vim /etc/profile
...
export PATH=/root/istio-1.6.8/bin:$PATH
[root@kube-mas ~]# source /etc/profile
|
安装部署
1
2
3
4
5
6
7
8
9
10
11
|
需要注意1.6之前的版本和1.6之后的版本安装会有细微区别,具体看帮助
[root@kube-mas ~]# istioctl install --set profile=demo -y
[root@kube-mas ~]# kubectl get po -n istio-system
NAME READY STATUS RESTARTS AGE
grafana-b54bb57b9-jvfsf 1/1 Running 0 7m20s
istio-egressgateway-7447bd847b-8qz29 1/1 Running 0 7m21s
istio-ingressgateway-59c788fd4f-ktgpf 1/1 Running 0 7m21s
istio-tracing-9dd6c4f7c-jp89b 1/1 Running 0 7m20s
istiod-54d84dc79c-hppjw 1/1 Running 0 8m34s
kiali-d45468dc4-d4v9l 1/1 Running 0 7m20s
prometheus-79fb649b4d-68x98 2/2 Running 0 7m20s
|
常用命令
查看所有profile类型
1
2
3
4
5
6
7
8
|
[root@kube-mas ~]# istioctl profile list
Istio configuration profiles:
preview
remote
default
demo
empty
minimal
|
下图为对应类型会安装的组件,但是到1.7版本之后,有很多组件都不会自动安装了
导出某个profile的yaml模板
1
|
[root@kube-mas ~]# istioctl profile dump demo > demo.yaml //导出demo profile的yaml模板
|
安装
1
2
3
4
5
6
7
8
|
1.7 版本之前
istioctl manifest apply --set profile=demo
例如,当前我不想安装kiali、prometheus、jaeger,可以用如下方法安装
[root@kube-mas ~]# istioctl manifest apply --set profile=demo -y --set values.kiali.enabled=false --set values.prometheus.enabled=false --set values.tracing.enabled=false
1.7 版本之后
istioctl manifest install --set profile=demo
|
卸载
1
2
|
卸载程序将删除 RBAC 权限、istio-system 命名空间和所有相关资源。可以忽略那些不存在的资源的报错,因为它们可能已经被删除掉了
[root@kube-mas ~]# istioctl manifest generate --set profile=demo | kubectl delete -f -
|
安装组件
注意到1.7版本后有很多组件不会自动安装了,比如demo 只会安装egressgateway、ingressgateway、istiod
安装kiali、prometheus、jaeger
1
2
3
4
5
6
7
8
9
10
|
1.7版本之前安装
[root@kube-mas ~]# istioctl manifest apply --set profile=demo -y --set values.kiali.enabled=true --set values.prometheus.enabled=true --set values.tracing.enabled=true
1.7版本之后安装
[root@kube-mas ~]# istioctl manifest install --set profile=demo --set values.kiali.enabled=true --set values.prometheus.enabled=true --set values.tracing.enabled=true
部署jaeger
[root@kube-mas ~]# kubectl apply -f istio-1.6.8/samples/addons/jaeger.yaml
[root@kube-mas ~]# kubectl patch svc -n istio-system jaeger-query -p '{"spec":{"type":"NodePort"}}'
|
将ingressgateway网关类型改为nodeport
1
2
3
4
|
[root@kube-mas ~]# kubectl patch svc istio-ingressgateway -n istio-system -p '{"spec":{"type":"NodePort"}}'
service/istio-ingressgateway patched
[root@kube-mas ~]# kubectl get svc -n istio-system | grep ingress
istio-ingressgateway NodePort 10.96.61.36 <none> 15021:30167/TCP,80:32099/TCP,443:32266/TCP,31400:30597/TCP,15443:31792/TCP 13m
|
istio的注入方式
手动注入方式
示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
创建一个测试的名称空间
[root@kube-mas ~]# kubectl create ns test
创建一个简单的nginx deploy资源
[root@kube-mas ~]# mkdir test
[root@kube-mas ~]# cd test/
[root@kube-mas test]# cat nginx-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-dep
labels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.19.1
imagePullPolicy: IfNotPresent
[root@kube-mas test]# kubectl apply -f nginx-deploy.yaml -n test
istio注入之前pod的资源状态
[root@kube-mas test]# kubectl get po -n test
NAME READY STATUS RESTARTS AGE
nginx-dep-7c68497bd9-ktrqt 1/1 Running 0 15s
使用kube-inject手动注入,先导出配置文件
导出方法1:
[root@kube-mas test]# istioctl kube-inject -f nginx-deploy.yaml -o nginx-deploy-inject.yaml
导出方法2:
[root@kube-mas test]# istioctl kube-inject -f nginx-deploy.yaml > nginx-deploy-inject.yaml
直接注入
[root@kube-mas test]# istioctl kube-inject -f nginx-deploy.yaml | kubectl apply -n test -f -
istio注入后的pod的资源状态
[root@kube-mas test]# kubectl get po -n test
NAME READY STATUS RESTARTS AGE
nginx-dep-6966cf86fd-7mqbb 2/2 Running 0 46s
可以看到pod被注入了一个新的容器,这个再导出来的yaml文件中也可以看到相关配置
|
自动注入方式
示例
1
2
3
4
5
6
7
8
9
10
11
12
|
自动注入是很简单的,只需要在名称空间上加上label标签 istio-injection=enabled即可
[root@kube-mas test]# kubectl label ns test istio-injection=enabled
验证,把之前手动注入的deploy资源删除,然后直接应用没有被注入过的deploy的yaml文件
[root@kube-mas test]# kubectl delete -f . -n test
deployment.apps "nginx-dep" deleted
[root@kube-mas test]# kubectl apply -f nginx-deploy.yaml -n test
[root@kube-mas test]# kubectl get po -n test
NAME READY STATUS RESTARTS AGE
nginx-dep-7c68497bd9-vbfl6 2/2 Running 0 42s
可以看到这里自动注入了istio的边车pod
|
注入原理以及细节
注意,应用注入,实际上并不是从原来的pod插入,而是重新启动了一个新的pod,然后再杀掉老的pod
在手动注入的时候导出来的nginx-deploy-inject.yaml这个配置文件中,我们可以看到,注入之后,多出来了2个容器,一个初始化容器,一个主容器
初始化容器
name: istio-init image: docker.io/istio/proxyv2:1.6.8
可以通过查看日志来看initcontainer容器究竟做了什么操作?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
|
[root@kube-mas test]# kubectl get po -n test
NAME READY STATUS RESTARTS AGE
nginx-dep-7c68497bd9-vbfl6 2/2 Running 0 16h
[root@kube-mas test]# kubectl logs nginx-dep-7c68497bd9-vbfl6 -n test -c istio-init
Environment:
------------
ENVOY_PORT=
INBOUND_CAPTURE_PORT=
ISTIO_INBOUND_INTERCEPTION_MODE=
ISTIO_INBOUND_TPROXY_MARK=
ISTIO_INBOUND_TPROXY_ROUTE_TABLE=
ISTIO_INBOUND_PORTS=
ISTIO_LOCAL_EXCLUDE_PORTS=
ISTIO_SERVICE_CIDR=
ISTIO_SERVICE_EXCLUDE_CIDR=
Variables:
----------
PROXY_PORT=15001
PROXY_INBOUND_CAPTURE_PORT=15006
PROXY_UID=1337
PROXY_GID=1337
INBOUND_INTERCEPTION_MODE=REDIRECT
INBOUND_TPROXY_MARK=1337
INBOUND_TPROXY_ROUTE_TABLE=133
INBOUND_PORTS_INCLUDE=*
INBOUND_PORTS_EXCLUDE=15090,15021,15020
OUTBOUND_IP_RANGES_INCLUDE=*
OUTBOUND_IP_RANGES_EXCLUDE=
OUTBOUND_PORTS_EXCLUDE=
KUBEVIRT_INTERFACES=
ENABLE_INBOUND_IPV6=false
Writing following contents to rules file: /tmp/iptables-rules-1622107917045002406.txt217260741
* nat
-N ISTIO_REDIRECT
-N ISTIO_IN_REDIRECT
-N ISTIO_INBOUND
-N ISTIO_OUTPUT
-A ISTIO_REDIRECT -p tcp -j REDIRECT --to-ports 15001
-A ISTIO_IN_REDIRECT -p tcp -j REDIRECT --to-ports 15006
-A PREROUTING -p tcp -j ISTIO_INBOUND
-A ISTIO_INBOUND -p tcp --dport 22 -j RETURN
-A ISTIO_INBOUND -p tcp --dport 15090 -j RETURN
-A ISTIO_INBOUND -p tcp --dport 15021 -j RETURN
-A ISTIO_INBOUND -p tcp --dport 15020 -j RETURN
-A ISTIO_INBOUND -p tcp -j ISTIO_IN_REDIRECT
-A OUTPUT -p tcp -j ISTIO_OUTPUT
-A ISTIO_OUTPUT -o lo -s 127.0.0.6/32 -j RETURN
-A ISTIO_OUTPUT -o lo ! -d 127.0.0.1/32 -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 -o lo ! -d 127.0.0.1/32 -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
COMMIT
iptables-restore --noflush /tmp/iptables-rules-1622107917045002406.txt217260741
Writing following contents to rules file: /tmp/ip6tables-rules-1622107917154183931.txt536994656
ip6tables-restore --noflush /tmp/ip6tables-rules-1622107917154183931.txt536994656
iptables-save
# Generated by iptables-save v1.6.1 on Thu May 27 09:31:57 2021
*mangle
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
COMMIT
# Completed on Thu May 27 09:31:57 2021
# Generated by iptables-save v1.6.1 on Thu May 27 09:31:57 2021
*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
:ISTIO_INBOUND - [0:0]
:ISTIO_IN_REDIRECT - [0:0]
:ISTIO_OUTPUT - [0:0]
:ISTIO_REDIRECT - [0:0]
-A PREROUTING -p tcp -j ISTIO_INBOUND
-A OUTPUT -p tcp -j ISTIO_OUTPUT
-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
COMMIT
# Completed on Thu May 27 09:31:57 2021
# Generated by iptables-save v1.6.1 on Thu May 27 09:31:57 2021
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
COMMIT
# Completed on Thu May 27 09:31:57 2021
|
从上面输出的日志,我们可以看到初始化容器实际上是做了一系列的iptables转发规则以及环境变量的初始化
主容器
name: istio-proxy image: docker.io/istio/proxyv2:1.6.8
这个主容器,主要启动了什么进程?使用了什么端口?可以通过如下命令查看
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
[root@kube-mas test]# kubectl exec nginx-dep-7c68497bd9-vbfl6 -it -n test -c istio-proxy -- ps -ef
UID PID PPID C STIME TTY TIME CMD
istio-p+ 1 0 0 01:33 ? 00:00:03 /usr/local/bin/pilot-agent proxy sidecar --domain test.svc.cluster.local --serviceCluster nginx.test --proxyLogLevel=warning --proxyComponentLogLevel=misc:error --trust-domain=cluster.local --concurrency 2
istio-p+ 13 1 0 01:33 ? 00:00:06 /usr/local/bin/envoy -c etc/istio/proxy/envoy-rev0.json --restart-epoch 0 --drain-time-s 45 --parent-shutdown-time-s 60 --service-cluster nginx.test --service-node sidecar~10.244.1.69~nginx-dep-7c68497bd9-vbfl6.test~test
istio-p+ 25 0 0 01:55 pts/0 00:00:00 ps -ef
[root@kube-mas test]# kubectl exec nginx-dep-7c68497bd9-vbfl6 -it -n test -c istio-proxy -- ss -nltp
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 128 0.0.0.0:15021 0.0.0.0:* users:(("envoy",pid=13,fd=25))
LISTEN 0 128 0.0.0.0:80 0.0.0.0:*
LISTEN 0 128 0.0.0.0:15090 0.0.0.0:* users:(("envoy",pid=13,fd=24))
LISTEN 0 128 127.0.0.1:15000 0.0.0.0:* users:(("envoy",pid=13,fd=14))
LISTEN 0 128 0.0.0.0:15001 0.0.0.0:* users:(("envoy",pid=13,fd=57))
LISTEN 0 128 0.0.0.0:15006 0.0.0.0:* users:(("envoy",pid=13,fd=58))
LISTEN 0 128 *:15020 *:* users:(("pilot-agent",pid=1,fd=7))
LISTEN 0 128 [::]:80 [::]:*
|
-
通过上面我们可以看出,istio-proxy容器,主要启动了2个进程,一个是pilot-agent,另一个是envoy,而且从进程的ppid可以得出envoy进程是由pilot-agent启动的
-
Pilot-agent进程会随时watch envoy进程,envoy进程死掉了,pilot-agent会重新启动envoy
主要负责工作:
参考文章
思考
为什么istio-init和istio-proxy都是使用的docker.io/istio/proxyv2:1.6.8 这个镜像,为什么istio-init 状态是Terminated而istio-proxy却是Running呢?
1
2
3
4
5
6
7
8
9
10
11
12
|
[root@kube-node ~]# docker inspect docker.io/istio/proxyv2:1.6.8
[
...
"Cmd": [
"/bin/sh",
"-c",
"#(nop) ",
"ENTRYPOINT [\"/usr/local/bin/pilot-agent\"]"
],
...
]
|
可以看到istio-proxy执行的是/usr/local/bin/pilot-agent,但是istio-init却没有执行这个,这是为什么呢?
这里会涉及到一个作用于的问题,会有以下几种情况
- 如果k8s配置文件中的command和args没有配置,那么用docker的默认配置
- 如果command写了,args没有写,那么docker默认配置会被忽略而仅仅执行.yaml文件中的command(不带任何参数)
- 如果command没有写,但是args写了,那么docker默认配置的entrypoint的命令会被执行,但是调用的参数是.yaml文件中配置的args
- 如果command和args都写了,那么默认配置会被忽略,使用.yaml中的配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
[root@kube-mas test]# kubectl exec nginx-dep-7c68497bd9-vbfl6 -it -n test -c istio-proxy -- bash
istio-proxy@nginx-dep-7c68497bd9-vbfl6:/$ pilot-agent --help
Istio Pilot agent runs in the sidecar or gateway container and bootstraps Envoy.
Usage:
pilot-agent [command]
Available Commands:
help Help about any command
istio-clean-iptables Clean up iptables rules for Istio Sidecar
istio-iptables Set up iptables rules for Istio Sidecar
proxy Envoy proxy agent
request Makes an HTTP request to the Envoy admin API
version Prints out build version information
Flags:
-h, --help help for pilot-agent
--log_as_json Whether to format output as JSON or in plain console-friendly format
--log_caller string Comma-separated list of scopes for which to include caller information, scopes can be any of [all, authorization, cache, citadelclient, configmapcontroller, default, googleca, model, sds, secretfetcher, stsclient, stsserver, token, validation, vault]
--log_output_level string Comma-separated minimum per-scope logging level of messages to output, in the form of <scope>:<level>,<scope>:<level>,... where scope can be one of [all, authorization, cache, citadelclient, configmapcontroller, default, googleca, model, sds, secretfetcher, stsclient, stsserver, token, validation, vault] and level can be one of [debug, info, warn, error, fatal, none] (default "default:info")
--log_rotate string The path for the optional rotating log file
--log_rotate_max_age int The maximum age in days of a log file beyond which the file is rotated (0 indicates no limit) (default 30)
--log_rotate_max_backups int The maximum number of log file backups to keep before older files are deleted (0 indicates no limit) (default 1000)
--log_rotate_max_size int The maximum size in megabytes of a log file beyond which the file is rotated (default 104857600)
--log_stacktrace_level string Comma-separated minimum per-scope logging level at which stack traces are captured, in the form of <scope>:<level>,<scope:level>,... where scope can be one of [all, authorization, cache, citadelclient, configmapcontroller, default, googleca, model, sds, secretfetcher, stsclient, stsserver, token, validation, vault] and level can be one of [debug, info, warn, error, fatal, none] (default "default:none")
--log_target stringArray The set of paths where to output the log. This can be any path as well as the special values stdout and stderr (default [stdout])
Use "pilot-agent [command] --help" for more information about a command.
|
从上面可以发现pilot-agent 不同args会有不同的作用,所以这里istio-init、istio-proxy对应的作用域中的第三种情况,通过args来使得他们的行为不一样。(在1.5版本以及之前,istio-init 对应的是第二种情况,通过command来改变docker运行的指令)
相关概念
环境准备
以官方示例bookinfo为例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
[root@kube-mas test]# cd /root/istio-1.6.8/samples/bookinfo/platform/kube/
[root@kube-mas kube]# kubectl apply -f . -n test
[root@kube-mas kube]# kubectl get po -n test
NAME READY STATUS RESTARTS AGE
details-v1-558b8b4b76-chjvb 2/2 Running 0 8m28s
details-v2-85d44f95c9-5bdqh 2/2 Running 0 8m30s
mongodb-v1-78d87ffc48-7pgd9 2/2 Running 0 8m30s
mysqldb-v1-6898548c56-rlq5x 2/2 Running 0 8m29s
productpage-v1-6987489c74-lfrw2 2/2 Running 0 8m28s
ratings-v1-7dc98c7588-5dfkw 2/2 Running 0 8m28s
ratings-v2-86f6d54875-gl9zf 2/2 Running 0 8m28s
ratings-v2-mysql-6b684bf9cd-phth6 2/2 Running 0 8m29s
ratings-v2-mysql-vm-7748d7687d-xhg2r 2/2 Running 0 8m29s
reviews-v1-7f99cc4496-9dggd 2/2 Running 0 8m28s
reviews-v2-7d79d5bd5d-qk87n 2/2 Running 0 8m28s
reviews-v3-7dbcdcbc56-kltv4 2/2 Running 0 8m28s
|
Gateway网关
Gateway用于管理进出网格的流量,指定可以进入或离开网格的流量。Gateway配置应用于网格边缘的独立的Envoy代理上,而不是服务负载的Envoy代理上。
Istio的Gateway资源仅允许配置4-6层的负载属性,如暴露的端口,TLS配置等,但是结合Istio的VirtualService,就可以像管理Istio网格中的其他数据面流量一样管理Gateway的流量。
主要用于管理ingress流量,但是也可以配置egress gateway。通过egress gateway可以配置流量离开网格的特定节点,限制哪些服务可以访问外部网络,或者通过egress安全控制来提高网格的安全性。Gateway可以用于配置一个纯粹的内部代理。
总而言之,就是服务的进出口,要将服务暴露到外部,就得定义ingress-gateway
详细参数信息
1
2
3
4
|
通过查看pod可以看到ingress和egress都是一个普通的pod,该pod仅包含一个istio-proxy容器
[root@kube-mas kube]# kubectl get po -n istio-system | grep gateway
istio-egressgateway-7447bd847b-5gmsh 1/1 Running 0 21m
istio-ingressgateway-59c788fd4f-7qd2n 1/1 Running 0 21m
|
ingressGateway示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
[root@kube-mas ~]# mkdir test/gw
[root@kube-mas ~]# cd test/gw
[root@kube-mas gw]# cat ingress-gw.yaml
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: bookinfo-gw
namespace: test
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "bookinfo.xieys.club"
|
配置解释
第一部分
1
2
|
selector:
istio: ingressgateway
|
将此配置下发到哪个pod,我们可以在默认的istio-ingressgateway中看到有istio=ingressgateway
这个标签。所以此配置会配置到默认的ingress-gateway这个pod中
第二部分
1
2
3
4
5
6
7
|
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "bookinfo.xieys.club"
|
port部分配置表示在网关上开启了80端口对应的是HTTP协议,注意这里是列表,可以开多个端口,根据自己要求而定
hosts部分,是指定对应的fqdn地址(网关域名),这里也是列表,可以绑定多个地址。如果没有具体的域名,这里也可以直接使用IP代替。
不同服务可以单独配置自己的网关,然后跟virtualService进行绑定即可
VirtualService 虚拟服务
VirtualService虚拟服务,可以将流量路由到istio服务网格中的服务,每个虚拟服务由一组路由规则组成,这些路由规则按顺序进行评估。
相比kubernetes service来说,使用virtualservice可以实现类似按百分比来分配流量等更加复杂、丰富、细粒度的流量控制。
详细参数信息
注意: 虚拟服务相当于 k8s service的sidecar,在原本k8s service的功能之上,提供了更加丰富的路由控制
VirtualService示例1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
[root@kube-mas gw]# mkdir /root/test/vs
[root@kube-mas gw]# cd !$
[root@kube-mas vs]# cat productpage-vs.yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: productpage-vs
namespace: test
spec:
gateways:
- bookinfo-gw
- mesh
hosts:
- "productpage.test.svc.cluster.local"
http:
- match:
- uri:
exact: /productpage
- uri:
prefix: /static
- uri:
exact: /login
- uri:
prefix: /api/v1/products
route:
- destination:
host: productpage.test.svc.cluster.local
subset: v1
port:
number: 9080
|
VirtualService示例2 (更有逻辑)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
[root@kube-mas vs]# cat productpage-vs.yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: productpage-vs
namespace: test
spec:
gateways:
- bookinfo-gw # 外部使用
- mesh # 内部使用
hosts:
- "productpage.test.svc.cluster.local" # 内部的k8s fqdn地址
- "bookinfo.xieys.club" #外网域名
http:
- match:
- uri:
exact: /productpage
- uri:
prefix: /static
- uri:
exact: /login
- uri:
prefix: /api/v1/products
- gateways:
- bookinfo-gw
route:
- destination:
host: productpage.test.svc.cluster.local
subset: v1
port:
number: 9080
- match:
- gateways:
- mesh #应用到网格中的所有服务
route:
- destination:
host: productpage.test.svc.cluster.local
subset: v1
port:
number: 9080
- route:
- destination:
host: productpage.test.svc.cluster.local
subset: v1
port:
number: 9080
|
这个示例区分了应用到内部的virtualservice以及应用到网关的virtualservice
参数解释
第一部分
1
2
3
|
gateways:
- bookinfo-gw # 外部使用
- mesh # 内部使用
|
当前Virtualservice绑定的ingressgateway网关,可向网格外暴露这些Host,网格内部通信存在一个默认的mesh保留字,mesh用来代指网格中的所有sidecar。
- 当没有定义gateway的时候,就会只是用缺省值(mesh),也就是针对网格中所有的sidecar生效
- 如果提供了gateways字段,这一规则就只会应用到声明的gateway之中。要让规则同时对gateway和网格内服务生效,需要显示的将mesh加入gateways列表中
第二部分
1
2
3
|
hosts:
- "productpage.test.svc.cluster.local" # 内部的k8s fqdn地址
- "bookinfo.xieys.club" #外网域名
|
一般为fqdn地址,通常是k8s的某个service地址,也可以是ip地址,dns域名或者依赖于平台的一个简称(例如k8s的服务短名称),隐式或显示的指向一个完全限定域名(FQDN),也可以使用通配符(*)前缀,让你创建一组所有服务的路由规则
第三部分
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
http:
- match:
- uri:
exact: /productpage
- uri:
prefix: /static
- uri:
exact: /login
- uri:
prefix: /api/v1/products
- gateways:
- bookinfo-gw
route:
- destination:
host: productpage.test.svc.cluster.local
subset: v1
port:
number: 9080
- match:
- gateways:
- mesh #应用到网格中的所有服务
route:
- destination:
host: productpage.test.svc.cluster.local
subset: v1
port:
number: 9080
|
http字段包含了Virtualservice的路由规则,描述匹配条件和根据所指定的host字段通过HTTP/1.1、HTTP2以及GRPC路由目标地址(也可以使用tcp和tls字段为TCP和TLS流量设置路由规则)
- 匹配条件,在同一个match块某个规则,添加多个匹配条件,关系是AND的关系,例如
1
2
3
4
5
|
- match:
- uri:
exact: /productpage
uri:
prefix: /produ
|
- 在match下添加的多个规则,关系是OR的关系,例如
1
2
3
4
5
|
- match:
- uri:
exact: /productpage
- uri:
prefix: /static
|
对于给定的virtualService,可以配置多条路由规则,比如基于负责的条件判断,或者想基于权重百分比分发流量(A/B测试或者金丝雀部署非常有用)
具体条件判断可以使用的字段和值可以参考:官方文档
在match关键字下的route,如果条件匹配的话,定义匹配的路由规则。
一条路由规则包含零个或者多个匹配条件的目标地址,路由规则按照从上到下的顺序选择,第一条规则具有最高优先级,如果不满足第一条路由规则均流向下一条路由规则,如果都不满足,那么就走默认的路由规则(也就是第四部分定义在http关键字下跟match同级的route)。所以通常建议定义一个默认的路由规则来确保virtualservice至少有一条匹配的路由。
主要通过此定义去查找定义的DestinationRule(目标规则)资源。
第四部分
1
2
3
4
5
6
|
- route:
- destination:
host: productpage.test.svc.cluster.local
subset: v1
port:
number: 9080
|
为默认路由规则
总结
如果把virtualservice和nginx配置文件类比的话,Virtualservice就相当于nginx的location块
DestinationRule 目标规则
一个Virtualservice可以看作是如何将流量分发到给定的目标地址(相当于路由规则),然后调用DestinationRule来配置分发到该目标地址的流量(相当于后端真实的pod)。
需要注意的是,DestinationRule是在VirtualService的路由规则之后才起作用
由 VirtualService的match -> route -> destination里的host以及subset(对应到DestinationRule的host以及匹配到定义好的subsets列表中的一条配置)之后才起作用。通过它进行把流量分发到真实的service上。
可以使用DestinationRule来指定subsets服务子集,例如,根据版本对服务的实例进行分组,然后通过virtualservice的路由规则中的subset将控制流量分发到不同的服务实例中。
示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
前面以及定义好了Gateway以及VirtualService,但是现在还无法访问,因为还没有定义相应的目标规则
[root@kube-mas vs]# mkdir /root/test/dr
[root@kube-mas vs]# cd !$
[root@kube-mas dr]# cat productpage-dr.yaml
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: productpage-dr
namespace: test
spec:
host: productpage.test.svc.cluster.local
subsets:
- name: v1
labels:
version: v1
|
参数解释
1
|
host: productpage.test.svc.cluster.local
|
对应k8s service的fqdn地址
1
2
3
4
|
subsets:
- name: v1
labels:
version: v1
|
定义相应子集,这里相当于通过subset的名字为v1,可以匹配到productpage.test.svc.cluster.local这个服务打了标签为version: v1的pod
DestinationRule允许在调用完整的目标服务或特定的服务子集(如倾向使用的负载均衡模型,TLS安全模型或断路器)时自定义Envoy流量策略。
iatio默认会使用轮训策略,此外istio也支持如下负载均衡模型,可以在DestinationRule中使用这些模型,将请求分发到特定的服务或服务子集。
- Random: 将请求转发到一个随机的实例上
- Weighted: 按照指定的百分比将请求转发到实例上
- Least requests: 将请求转发到具有最少请求数目的实例上。
例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
[root@kube-mas dr]# cat my-test.yaml
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: my-test-dr
namespace: test
spec:
host: my-test
trafficPolicy: # 默认的负载均衡策略模型为随机
loadBalancer:
simple: RANDOM
subsets:
- name: v1 # subset1,将流量转发到具有标签version: v1 的ood上
labels:
version: v1
- name: v2 # subset2,将流量转发到具有标签version: v2的pod上,同时指定负载均衡为轮询
labels:
version: v2
trafficPolicy:
loadBalancer:
simple: ROUND_ROBIN
- name: v3 #subset3,将流量转发到具有标签version: v3的pod上
labels:
version: v3
|
每一个子集由一个或多个labels定义,对应kubernetes中的对象(如pod)的labels(key/value对)。
除了定义子集外,DestinationRule还定义了该目的地中所有子集的默认流量策略,以及仅覆盖该子集的特定策略。默认策略定义在subset字段的统计字段trafficPolicy内,为v1和v3子集设置了随机负载均衡的默认策略,在v2策略中使用了特定策略轮询负载均衡
详细参数信息
总结
跟nginx配置文件比较,destinationRule则相当于nginx proxy_pass 对应的后端节点(upstream块的后端节点)
资源应用
1
2
3
4
5
6
7
8
9
|
应用前面所定义的所有资源
[root@kube-mas dr]# kubectl apply -f /root/test/gw/ingress-gw.yaml
[root@kube-mas dr]# kubectl apply -f /root/test/vs/productpage-vs.yaml
[root@kube-mas dr]# kubectl apply -f productpage-dr.yaml
查看ingressgateway对应80端口对应的nodeport端口
[root@kube-mas dr]# kubectl get svc -n istio-system | grep gateway
istio-egressgateway ClusterIP 10.96.138.204 <none> 80/TCP,443/TCP,15443/TCP 19h
istio-ingressgateway NodePort 10.96.61.36 <none> 15021:30167/TCP,80:32099/TCP,443:32266/TCP,31400:30597/TCP,15443:31792/TCP 19h
|
通过域名访问
可以到外网已经可以正常访问到了
VirtualService和DestinationRule的配置选项
ServiceEntry服务条目
istio提供了三种访问外部服务的方法
- 允许sidecar将请求传递到未在网格内配置过的任何外部服务。使用这种方法时,无法监控到外部服务的访问,也不能利用istio的流量控制功能
- 配置ServiceEntry以提供对外部服务的受控访问。这是istio官方推荐使用的方法
- 对特定范围的IP,完全绕过sidecar。仅当处于性能或其他原因无法使用sidecar配置外部访问时,才建议使用该配置方法
使用ServiceEntry完成对网格外部服务的受控访问。使用服务条目资源可以将条目添加到istio内部维护的服务注册表中。添加服务条目后,Envoy代理可以将流量发送到该服务,就好像该服务条目是网格中的服务一样。通过配置服务条目,可以管理在网格外部运行的服务的流量。
同时,还可以配置VirtualService和DestinationRule,以更精细的方式来控制服务条目的流量,就像为网格中的其他任务服务配置流量一样。
对于sidecar对外部服务的处理方式,istio提供了两种选项:
- ALLOW_ANY:默认值,表示istio代理允许调用未知的外部服务。上面的第一种方式就是使用了该配置项。(默认)
- REGISTRY_ONLY:istio代理会阻止任何没有在网格中定义的HTTP服务或ServiceEntry的主机
详细参数
示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
部署官方sleep这个示例应用,用作发送请求的测试源
[root@kube-mas dr]# kubectl apply -f /root/istio-1.6.8/samples/sleep/sleep.yaml -n test
使用默认策略的时候,是允许Envoy代理将请求传递到未在网格内配置过的服务
[root@kube-mas dr]# export SOURCE_POD=$(kubectl get po -n test -l app=sleep -o jsonpath={.items..metadata.name})
[root@kube-mas dr]# kubectl exec -it $SOURCE_POD -n test -c sleep -- curl -I https://www.baidu.com | grep "HTTP/"
HTTP/1.1 200 OK
修改默认策略为REGISTRY_ONLY
[root@kube-mas dr]# kubectl edit cm istio -n istio-system -o yaml
...
data:
mesh: |-
outboundTrafficPolicy:
mode: REGISTRY_ONLY
...
在mesh 添加 字段outboundTrafficPolicy并设置mode为REGISTRY_ONLY
再次进行验证,访问https返回
[root@kube-mas dr]# kubectl exec -it $SOURCE_POD -n test -c sleep -- curl -I https://www.baidu.com | grep "HTTP/"
command terminated with exit code 35
访问http返回
[root@kube-mas dr]# kubectl exec -it $SOURCE_POD -n test -c sleep -- curl -I http://www.baidu.com | grep "HTTP/"
HTTP/1.1 502 Bad Gateway
可以发现修改默认策略后,没有在网格内配置过的服务已经被阻止了
配置一个ServiceEntry服务条目
[root@kube-mas dr]# mkdir /root/test/se
[root@kube-mas dr]# cd !$
[root@kube-mas se]# cat baidu-se.yaml
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: baidu-se
namespace: test
spec:
hosts:
- www.baidu.com
ports:
- number: 80
name: http
protocol: HTTP
- number: 443
name: https
protocol: HTTPS
resolution: DNS
location: MESH_EXTERNAL
[root@kube-mas se]# kubectl apply -f baidu-se.yaml
再次验证,访问https
[root@kube-mas se]# kubectl exec -it $SOURCE_POD -n test -c sleep -- curl -I https://www.baidu.com | grep "HTTP/"
HTTP/1.1 200 OK
访问http
[root@kube-mas se]# kubectl exec -it $SOURCE_POD -n test -c sleep -- curl -I http://www.baidu.com | grep "HTTP/"
HTTP/1.1 200 OK
已经都可以正常进行访问了
|
参数解析
第一部分
1
2
|
hosts:
- www.baidu.com
|
将外部的那个fqdn或者ip,配置成网格内的serviceEntry资源
第二部分
1
2
3
4
5
6
7
|
ports:
- number: 80
name: http
protocol: HTTP
- number: 443
name: https
protocol: HTTPS
|
外部服务fqdn或者ip,服务所对应的端口
第三部分
设置对应主机的服务发现模式为DNS
第四部分
1
|
location: MESH_EXTERNAL
|