概念
- 服务网格
服务网格是描述微服务网络以及它们之间的交互,一个Pod是否处于服务网格内,要看这个Pod是否被注入
- Istio
随着服务网格规模的和复杂度不断增长,服务网格变得越难越理解和管理,Istio就是来管理服务网格的,它的功能包含:解决服务发现,负载均衡,故障恢复,度量和监控,以及更为复杂的运维需求,比如A/B测试,金丝雀发布,速率限制、访问控制和端对端认证
- Istio优势
Istio 可以轻松为一个已经部署的服务创建网格,而服务的代码只需很少甚至不用更改,其使用sidecar代理为服务添加Istio的支持,而代理会拦截微服务之间的所有网络通信
- 为 HTTP、gRPC、WebSocket 和 TCP 流量自动负载均衡。
- 通过丰富的路由规则、重试、故障转移和故障注入对流量行为进行细粒度控制。
- 可插拔的策略层和配置 API,支持访问控制、速率限制和配额。
- 集群内(包括集群的入口和出口)所有流量的自动化度量、日志记录和追踪。
- 在具有强大的基于身份验证和授权的集群中实现安全的服务间通信。
快速开始
平台兼容
最新版的Istio(1.7),已经在Kubernetes1.16,1.17,1.18中测试通过,也就是说如果你想安装Istio1.7那么你的Kubernetes必须>=1.16 =<1.18
下载安装Istio
1 | curl -L https://istio.io/downloadIstio | ISTIO_VERSION=1.7.0 sh - |
目录结构
1 | $ tree -L 1 |
配置istioctl环境变量和自动补全
1 | IstioPath="<IstioPath>" |
安装control plane && data plane
istioctl install 可以重新安装istio全部组件,但是重建会删除之前创建的所有istio crd 资源
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 $ istioctl install --set profile=default \
--set values.gateways.istio-egressgateway.enabled=false \
--set values.gateways.istio-ingressgateway.sds.enabled=true \
--set values.gateways.istio-ingressgateway.type=NodePort \
--set values.prometheus.enabled=true \
--set values.grafana.enabled=true \
--set values.tracing.enabled=true \
--set values.tracing.provider=jaeger \
--set values.kiali.enabled=true \
--set values.tracing.jaeger.storageClassName="" \
$ kubectl patch svc -n istio-system istio-ingressgateway -p '{"spec":{"ports":[{"name":"http2","nodePort":80,"port":80}]}}'
$ kubectl patch svc -n istio-system istio-ingressgateway -p '{"spec":{"ports":[{"name":"https","nodePort":443,"port":443}]}}'
$ istioctl version
client version: 1.7.0
control plane version: 1.7.0
data plane version: 1.7.0 (3 proxies)
安装问题
- 如果安装过之前的版本,没有删除感觉会出现
error installer failed to create "EnvoyFilter/istio-system/metadata-exchange-1.6": Internal error occurred: failed calling webhook "pilot.validation.istio.io": Postlink 1
2
3### 卸载istio
```bash
$ istioctl x uninstall --purge
手动注入一个和创建deployment
1 | $ kubectl create ns istiol-test |
手动解除注册
1 | kubectl get deployment nginx --image nginx:latest --port 80 -n istio-test --dry-run=client -oyaml | istioctl experimental kube-uninject -f - |kubectl apply -f - |
查看注入后的deployment中的container
可以看到注入后的deployment比之前多个一个istio-init的initContainer和一个istio-proxy的Container
1 | $ kubectl get deployments.apps -n istio-test webserver1 -o jsonpath='{.spec.template.spec.containers[*].name}' |
自动注入一个deployment
istio 是根据istiod watch 带有istio-injection=enabled标签的namespace来实现资源的自动注入的,所以要先给namespace打label
1 | $ kubectl label ns istio-test istio-injection=enabled |
关于手动注入和自动注入的区别,可以通过kubectl get deployment
-oyaml 可以看到webserver1和webserver2 的编排文件是不一样的,webserver1的编排文件被自己patch,而webserver2却是正常的,但是webserver1和webserver2 创建的Pod都被注入,所以,可以断定,istio的自动注入是对Pod的自动注入
访问被注入的资源
service暴露deployment
1
2$ kubectl expose deployment -n istio-test webserver1
$ kubectl expose deployment -n istio-test webserver2测试访问
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$ kubectl run centos --image centos:latest -it -n istio-test
root@centos /]# curl webserver1
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
[root@centos /]# curl webserver2
<html><body><h1>It works!</h1></body></html>
流量管理
Istio 流量管理Crd 介绍
VirtualService
virtualservice 定义了一组寻址主机要应用的路由规则,每个路由规则为特定协议的流量定义了匹配条件。
DestinationRule
DestinationRule 定义了发生路由后的流量策略,也可以设置流量流向指定版本的策略。
一. 路由转发-地址重写
需求:创建webserver1、2的共同service,要求访问url webserver/nginx 流量转发到webserver1,访问url webserver/httpd 流量转发到webserver2
创建webserver1、webserver2 deployment 共同service
1 | $ kubectl expose deployment -n istio-test webserver1 --dry-run=client -oyaml | kubectl patch -f - -p '{"metadata":{"name":"webserver"}}' --dry-run=client -oyaml -n istio-test|kubectl patch -f - -p '{"metadata":{"labels":{"app":"webserver"}}}' --dry-run=client -oyaml -n istio-test |kubectl patch -f - -p '{"spec":{"selector":{"istio":"virtualservice"}}}' --dry-run=client -oyaml -n istio-test |kubectl create -f - |
[root@centos /]# curl webserver
It works!
[root@centos /]# curl webserver
<!DOCTYPE html>
Welcome to nginx!
If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.
For online documentation and support please refer to
nginx.org.
Commercial support is available at
nginx.com.
Thank you for using nginx.
1 |
|
访问测试virtualservice
1 | [root@centos /]# curl webserver -I |
二. 路由转发-配置权重
需求: 访问webserver,80%的流量转发到webserver2 20%的流量转发到webserver1
修改webserver1 nginx的index.html
1 | kubectl exec -it -n istio-test webserver1-7d78c5dfc7-6fnsg -c nginx -- bash -c "echo '<h1>Im nginx</h1>' >/usr/share/nginx/html/index.html" |
创建权重virtualservice
1 | apiVersion: networking.istio.io/v1alpha3 |
访问测试
分别在default和istio-test命名空间创建一个centos的Pod,用于测试访问,可以看到istio-test命名空间下的centos被自动注入,处于服务网格内,default命名空间下的没有被注入,处于服务网格外,访问测试,服务网格内的按照virtualservice 配置的权重访问,服务网格外的安装service的轮询策略访问
1 | $ kubectl get pod |
三、基于hearder、cookeis 的灰度发布
1). hearder中携带用户名,请求hearder中带有user:zlx的流量转发给webserver2,其他的转发给webserver1
创建新的service
1
2$ kubectl expose deployment -n istio-test webserver1 --dry-run=client -oyaml | kubectl patch -f - -p '{"metadata":{"name":"webserver-hearder"}}' --dry-run=client -oyaml -n istio-test|kubectl patch -f - -p '{"metadata":{"labels":{"app":"webserver"}}}' --dry-run=client -oyaml -n istio-test |kubectl patch -f - -p '{"spec":{"selector":{"istio":"virtualservice"}}}' --dry-run=client -oyaml -n istio-test |kubectl create -f -
$ kubectl patch service -n istio-test webserver-hearder --type='json' -p='[{"op": "remove", "path": "/spec/selector/app", "value":"webserver1"}]'创建virtualservice
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: webserver-hearder
namespace: istio-test
spec:
hosts:
- webserver-hearder
http:
- match:
- headers:
user:
exact: "zlx"
route:
- destination:
host: webserver2
port:
number: 80
- route:
- destination:
host: webserver1
port:
number: 80访问测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19$ kubectl exec -it -n istio-test centos -c centos -- bash -c "curl -H "user:zlx" webserver-hearder -s|jq"
{
"msg": "hello word v2"
}
$ kubectl exec -it -n istio-test centos -c centos -- bash -c "curl -H "user:zlx" webserver-hearder -s|jq"
{
"msg": "hello word v2"
}
$ kubectl exec -it -n istio-test centos -c centos -- bash -c "curl webserver-hearder -s|jq"
{
"msg": "hello word v1"
}
$ kubectl exec -it -n istio-test centos -c centos -- bash -c "curl webserver-hearder -s|jq"
{
"msg": "hello word v1"
}
2). cookie中携带用户名,请求cookie中带有user:zlx的流量转发给webserver1,其他的转发给webserver2
创建service
1
2$ kubectl expose deployment -n istio-test webserver1 --dry-run=client -oyaml | kubectl patch -f - -p '{"metadata":{"name":"webserver-cookie"}}' --dry-run=client -oyaml -n istio-test|kubectl patch -f - -p '{"metadata":{"labels":{"app":"webserver"}}}' --dry-run=client -oyaml -n istio-test |kubectl patch -f - -p '{"spec":{"selector":{"istio":"virtualservice"}}}' --dry-run=client -oyaml -n istio-test |kubectl create -f -
$ kubectl patch service -n istio-test webserver-cookie --type='json' -p='[{"op": "remove", "path": "/spec/selector/app", "value":"webserver1"}]'创建virtualservice
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: webserver-cookie
namespace: istio-test
spec:
hosts:
- webserver-cookie
http:
- match:
- headers:
cookie:
exact: "user=zlx"
route:
- destination:
host: webserver1
port:
number: 80
- route:
- destination:
host: webserver2
port:
number: 80访问测试
1
2
3
4
5
6
7
8
9
10$ kubectl exec -it -n istio-test centos -c centos -- bash -c "curl --cookie "user=zlx" webserver-cookie -s|jq"
{
"msg": "hello word v1"
}
# lisong @ zhangxiangdeMacBook-Pro in ~ [17:06:10]
$ kubectl exec -it -n istio-test centos -c centos -- bash -c "curl webserver-cookie -s|jq"
{
"msg": "hello word v2"
}
四. Tcp流量代理
部署Tcp echo服务
1 | kubectl create -f https://raw.githubusercontent.com/istio/istio/release-1.7/samples/tcp-echo/tcp-echo-services.yaml -n istio-test |
创建virtualservice/destinationRule
1 | apiVersion: networking.istio.io/v1alpha3 |
访问测试
1 | $ for i in `seq 10`;do kubectl exec -it -n istio-test centos -c centos -- sh -c "(date; sleep 1) | nc tcp-echo 9000";done |
五. 服务熔断
线上服务因服务访问量巨大而采取的牺牲局部,保全整体的方式叫熔断。限流就是限制系统的输入输出流量来达到保护系统的目的,一般来说系统吞吐量是可以测算的,为了保证系统的稳定运行,一旦达到的需要限制的阈值,就需要限制流量并采取一些措施以完成限制流量的目的,这些都是熔断。
创建destinationrule,配置熔断规则
1 | apiVersion: networking.istio.io/v1alpha3 |
部署client
1 | kubectl create -f https://raw.githubusercontent.com/istio/istio/release-1.7/samples/httpbin/sample-client/fortio-deploy.yaml |
熔断测试
熔断规则配置了,最大请求数和并发数都为1,所以只要请求数超过100和并发数超过10,即会触发熔断
1
2
3
4
5
6
7 # 并发10,请求100 全部通过,没有触发熔断
$ kubectl exec -it -n istio-test fortio-deploy-6dc9b4d7d9-xrz2t -c fortio -- fortio load -c 10 -qps 0 -n 100 -loglevel Warning http://webserver1|grep Code
Code 200 : 100 (100.0 %)
# 将并发调大至30,触发熔断
$ kubectl exec -it -n istio-test fortio-deploy-6dc9b4d7d9-xrz2t -c fortio -- fortio load -c 30 -qps 0 -n 100 -loglevel Warning http://webserver1|grep Code
Code 200 : 72 (72.0 %)
Code 503 : 28 (28.0 %)
六. 流量镜像
软件开发在功能迭代的时候,有时候因为测试环境难以模拟线上实际的访问流量,又不能直接上到线上直接测试,所以这个时候我们就需要流量镜像,将线上的请求实时发送到待测版本,istio mirror会先将流量路由到主服务,然后执行mirror规则发送全部或者部分流量到镜像服务。
创建webserver1、webserver2的共同service
1 | $ kubectl expose deployment -n istio-test webserver1 --dry-run=client -oyaml | kubectl patch -f - -p '{"metadata":{"name":"webserver-mirror"}}' --dry-run=client -oyaml -n istio-test|kubectl patch -f - -p '{"metadata":{"labels":{"app":"webserver"}}}' --dry-run=client -oyaml -n istio-test |kubectl patch -f - -p '{"spec":{"selector":{"istio":"virtualservice"}}}' --dry-run=client -oyaml -n istio-test |kubectl create -f - |
创建virtualservice
1 | apiVersion: networking.istio.io/v1alpha3 |
可以观察到流量都被路由到webserver1
1
2
3
4
5 $ kubectl exec -it -n istio-test centos -c centos -- bash -c "echo -n \"`date +%Y%m%d\" \"%H:%M:%S` \"; curl webserver-mirror"
20200908 14:14:11 {"msg":"hello word v1"
$ kubetail webserver -n istio-test
[webserver1-7d78c5dfc7-6fnsg nginx] 127.0.0.1 - - [08/Sep/2020:06:07:47 +0000] "GET / HTTP/1.1" 200 24 "-" "curl/7.61.1" "-"
[webserver1-7d78c5dfc7-6fnsg nginx] 127.0.0.1 - - [08/Sep/2020:06:08:07 +0000] "GET / HTTP/1.1" 200 24 "-" "curl/7.61.1" "-"
配置virtualservice mirror
1 | apiVersion: networking.istio.io/v1alpha3 |
访问测试
1 | $ kubectl exec -it -n istio-test centos -c centos -- bash -c "echo -n \"`date +%Y%m%d\" \"%H:%M:%S` \"; curl webserver-mirror" |
网格服务的对外访问Istio Gateway
前面讲过,Istio网格服务,只有在服务网格中的服务才能互相访问调用,如果一个服务想要对外访问,我们需要Istio Gateway
一. 最基础的Gateway
修改istio-ingressgateway service的Type为NodePort
istio-ingressgateway service 类型为Loadbalancer,如果你有Loadbalancer ip,可以配置Loadbalancer类型,如果没有,那可以采用NodePort类型
1
2
3 $ kubectl patch svc -n istio-system istio-ingressgateway -p '{"spec":{"type":"NodePort"}}'
$ kubectl patch svc -n istio-system istio-ingressgateway -p '{"spec":{"ports":[{"name":"http2","nodePort":80,"port":80}]}}'
$ kubectl patch svc -n istio-system istio-ingressgateway -p '{"spec":{"ports":[{"name":"https","nodePort":443,"port":443}]}}'
创建Gateway/virtualservice
1 | echo ' |
访问测试
1 | $ sudo echo "172.30.7.96 webserver1.chulinx.com" >> /etc/hosts |
二. 基于文件挂载的TSL ingress网关
生成证书
生成证书一步有个需要注意的地方,和ingress-nginx不同,ingress-nginx生成的secret默认调用是在和ingress资源所在的命名空间下去找,而istio的gateway生成的secret一定要在istio-system命名空间下
1
2
3
4
5
6
7 # 一直输y即可
$ git clone https://github.com/nicholasjackson/mtls-go-example && cd mtls-go-example && ./generate.sh webserver1.chulinx.com 123456
$ mkdir webserver1.chulinx.com && mv 1_root 2_intermediate 3_application 4_client webserver1.chulinx.com
# secret 名字必须是istio-ingressgateway-certs
$ kubectl create secret tls istio-ingressgateway-certs --key=./webserver1.chulinx.com/3_application/private/webserver1.chulinx.com.key.pem \
--cert=./webserver1.chulinx.com/3_application/certs/webserver1.chulinx.com.cert.pem -n istio-system
secret/istio-ingressgateway-certs created
查看证书挂载
istio-ingressgateway会自动挂载tls证书和key到Pod内
1
2
3
4
5
6
7
8 kubectl exec -it -n istio-system $(kubectl -n istio-system get pods -l istio=ingressgateway -o jsonpath='{.items[0].metadata.name}') -- ls -al /etc/istio/ingressgateway-certs
total 4
drwxrwsrwt 3 root istio-proxy 120 Sep 9 02:22 .
drwxr-xr-x 1 root root 4096 Sep 8 10:39 ..
drwxr-sr-x 2 root istio-proxy 80 Sep 9 02:22 ..2020_09_09_02_22_40.578875089
lrwxrwxrwx 1 root istio-proxy 31 Sep 9 02:22 ..data -> ..2020_09_09_02_22_40.578875089
lrwxrwxrwx 1 root istio-proxy 14 Sep 9 02:22 tls.crt -> ..data/tls.crt
lrwxrwxrwx 1 root istio-proxy 14 Sep 9 02:22 tls.key -> ..data/tls.key
创建Gateway、virtualservice
1 | apiVersion: networking.istio.io/v1alpha3 |
访问测试
1 | $ curl -s -HHost:webserver1.chulinx.com \ |
多个域名配置TSL ingress Gateway
生成证书
1
2
3
4
5$ ./generate.sh webserverfile.chulinx.com 123456
$ mkdir webserverfile.chulinx.com && mv 1_root 2_intermediate 3_application 4_client webserverfile.chulinx.com/
$ kubectl create secret tls istio-ingressgateway-webserverfile-certs --key=./webserverfile.chulinx.com/3_application/private/webserverfile.chulinx.com.key.pem \
--cert=./webserverfile.chulinx.com/3_application/certs/webserverfile.chulinx.com.cert.pem -n istio-system
secret/istio-ingressgateway-webserverfile-certs createdpatch istio-ingress
1 | $ cat > gateway-patch.json <<EOF |
查看挂载
1
kubectl exec -it -n istio-system $(kubectl -n istio-system get pods -l istio=ingressgateway -o jsonpath='{.items[0].metadata.name}') -- ls -al /etc/istio/ingressgateway-webserverfile-certs
配置Gateway和virtualservice
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
34apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: webserverfile-mount-gateway
spec:
selector:
istio: ingressgateway # use istio default ingress gateway
servers:
- port:
number: 443
name: https
protocol: HTTPS
tls:
mode: SIMPLE
serverCertificate: /etc/istio/ingressgateway-webserverfile-certs/tls.crt
privateKey: /etc/istio/ingressgateway-webserverfile-certs/tls.key
hosts:
- "webserverfile.chulinx.com"
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: webserverfile-mount
spec:
hosts:
- "webserverfile.chulinx.com"
gateways:
- webserverfile-mount-gateway
http:
- route:
- destination:
port:
number: 80
host: webserver1访问测试
1
2
3
4
5
6$ curl -s -HHost:webserverfile.chulinx.com \
--cacert webserverfile.chulinx.com/2_intermediate/certs/ca-chain.cert.pem \
https://webserverfile.chulinx.com |jq
{
"msg": "hello word v1"
}
三. 更优雅的配置TLS,使用secrt配置Gateway Https
生成secret
1 | $ kubectl create secret generic webserver --from-file=key=./webserver1.chulinx.com/3_application/private/webserver1.chulinx.com.key.pem \ |
创建gateway
注意gateway的端口号(number)和协议(protocol)
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 apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: webserver1-gateway-https
spec:
selector:
istio: ingressgateway # use Istio default gateway implementation
servers:
- port:
number: 443
name: https
protocol: HTTPS
tls:
mode: SIMPLE
credentialName: webserver
hosts:
- "webserver1.chulinx.com"
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: webserver1-http
spec:
hosts:
- "webserver1.chulinx.com"
gateways:
- webserver1-gateway-https
http:
- route:
- destination:
port:
number: 80
host: webserver1
curl访问测试
1 | $ curl -s -HHost:webserver1.chulinx.com \ |
Istio 监控集成Prometheus
安装Prometheus
1 | $ kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.7/samples/addons/prometheus.yaml |
其他
服务网格service Port Name 命名问题
Istio 默认支持代理所有的TCP流量,但为了提供附加能力,比如路由和丰富的指标,使用什么协议必须被确定。协议可以被自动检测或者明确声明
- 协议自动检测
目前Istio 可以仅支持自动检测HTTP、HTTP2流量。如果无法确定协议,流量会被当做普通TCP流量对待- 协议手动指定
手动指定协议是根据k8s 资源service中Port name去指定的,例如如下service指定了一个mysql的协议和http的协议:
1 | 注意: 因为这个规则所以必须注意service的Port Name如果要以 |
1 | kind: Service |
ISTIO 注入Nginx问题
- Nginx转发426 Upgrade Required
Nginx的默认转发协议是Http/1.0 而ISTIO的HTTP转发协议默认是Http/1.1,所以426是要求升级协议,配置转发协议
proxy_http_version 1.1;
即可
- ISTIO注入Nginx,Nginx转发404
如果规则没有问题的话,那大概是配置了
proxy_set_header Host $host;
,看istio-proxy的日志显示在转发的时候找不到后端host的地址,所以这个地方要明确后端的service地址proxy_set_header Host backend;
,而不是用变量去代替。
ISTIO-Proxy 流量转发问题
- Istio流量协议匹配
istio-proxy 配到service中port的name为http-xxx 就会认为这个端口为http协议,如果是grpc-xxx,就会认为这个端口协议是grpc,目前支持通过端口名区分的协议包含为grpc grpc-web http http2 https mongo mysql redis tcp tls udp
Istio 可以自动检测出 HTTP 和 HTTP/2 流量。如果协议无法自动确定,流量将会被当作普通 TCP 流量对待
ISTIO 对于ExternalName类型的service存在的问题
ExternalName类型的service将服务映射到 DNS 名称,而不是典型的选择器,当主机查找serviceName.namespace.svc.cluster.local时,集群 DNS 服务返回 CNAME 记录
此问题是ExternalName类型的service配置了port,istio会记住port,将服务网格内所有访问这个端口的流量全部转发到这个service
处理方法: ExternalName类型的service不要配置port,配置了也不会进行转发
- 本文作者: ChuLinx
- 本文链接: http://yoursite.com/2020/09/04/Istio步步来/
- 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!