跳到主要内容

8 篇博文 含有标签「Istio」

查看所有标签

Istio 安全管理

· 阅读需 15 分钟

主要涉及

  • 配置 TLS 网关
  • mTLS
  • 设置访问策略
  • 认证

制作环境

![chap5初始化环境](/img/Istio 安全管理/chap5初始化环境.png)

# 初始化环境
kubectl get svc
#svc1
#vm2-svc
kubectl delete svc svc1
kubectl expose --name=svc1 pod pod1 --port=80
kubectl expose --name=svc2 pod pod2 --port=80

mkdir chap5 && cd chap5
cp ../chap4/mygw1.yaml ../chap4/vs.yaml ../chap4/vs2.yaml ./

cat mygw1.yaml
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: mygw
spec:
selector:
istio: ingressgateway # use istio default controller
servers:
- port:
number: 80
name: http-1
protocol: HTTP
hosts:
- "*.yuan.cc"

cat vs.yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: myvs
spec:
hosts:
- "aa.yuan.cc"
gateways:
- mygw
http:
- route:
- destination:
host: svc1
# 创建 vs
kubectl apply -f vs.yaml

mv vs2.yaml vs3.yaml
cat vs3.yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: myvs3
spec:
hosts:
- "vm.yuan.cc"
gateways:
- mygw
http:
- route:
- destination:
host: vm2-svc
# 创建 vs3
kubectl apply -f vs3.yaml

cp vs.yaml vs2.yaml
cat vs2.yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: myvs2
spec:
hosts:
- "bb.yuan.cc"
gateways:
- mygw
http:
- route:
- destination:
host: svc2
# 创建 vs2
kubectl apply -f vs2.yaml

客户端服务器修改hosts

vim /etc/hosts
192.168.26.230 aa.yuan.cc aa
192.168.26.230 bb.yuan.cc bb
192.168.26.230 vm.yuan.cc vm

# 测试连通
curl aa.yuan.cc #111
curl bb.yuan.cc #222
curl vm.yuan.cc #hello vm vm

为了安全性,我们更建议使用 https 访问

https ---- http + TLS (传输层加密) 在 SSL 被放弃使用后转向 TLS

  • 有效的提升安全性
  • 有效提升网站权重(百度/谷歌)
  • 有效解决流量劫持

启用 TLS 网关

对于istio 来说,所有需要的密钥都需要放入特定的目录中

# 生成密钥对(自签发)
mkdir -p /etc/istio/ingressgateway-certs/
# 有效期为 365天,rsa算法,私钥为.key ,公钥为.crt(公钥就是证书)
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/istio/ingressgateway-certs/mykey.key -out /etc/istio/ingressgateway-certs/mycrt.crt -subj "/CN=mytest/O=my-test"
# 注意:一定要放在/etc/istio/ingressgateway-certs/里

# 创建tls类型的secret
kubectl create secret tls istio-ingressgateway-certs --key /etc/istio/ingressgateway-certs/mykey.key --cert /etc/istio/ingressgateway- certs/mycrt.crt -n istio-system
# 注意:密钥名必须是 istio-ingressgateway-certs

# 创建 secret 的目录,是让isgressgateway知道有这个证书和私钥

cp mygw1.yaml mygw1-tls.yaml
vim mygw1-tls.yaml

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: mygw
spec:
selector:
istio: ingressgateway # use istio default controller
servers:
#- port:
# number: 80
# name: http-1
# protocol: HTTP
# hosts:
# - "*.yuan.cc"
- port:
number: 443
name: https
protocol: HTTPS
hosts:
- "aa.yuan.cc"
tls:
mode: SIMPLE #简单的单项认证(客户端单项的认证服务器端)
serverCertificate: /etc/istio/ingressgateway-certs/mycrt.crt
privateKey: /etc/istio/ingressgateway-certs/mykey.key

# 客户端访问测试
curl -kv https://aa.yuan.cc
#curl: (35) OpenSSL SSL_connect: SSL_ERROR_SYSCALL in connection to aa.yuan.cc:443

#查看istio-ingressgateway日志
kubectl get pods -n istio-system
kubectl logs istio-ingressgateway-8f747d485-g3h35 -n istio-system
#/etc/istio/ingressgateway-certs/mycrt.crt: no such file or directory

kubectl get depoly istio-ingressgateway -o yaml -n istio-system

kubectl get secrets -n istio-system

#进入容器查看
kubectl exec -it istio-ingressgateway-8f747d485-g3h35 -n istio-system -- bash
ls /etc/istio/ingressgateway-certs
# tls.crt tls.key

# 修改 mygw1-tls.yaml
vim mygw1-tls.yaml
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: mygw
spec:
selector:
istio: ingressgateway # use istio default controller
servers:
#- port:
# number: 80
# name: http-1
# protocol: HTTP
# hosts:
# - "*.yuan.cc"
- port:
number: 443
name: https
protocol: HTTPS
hosts:
- "aa.yuan.cc"
tls:
mode: SIMPLE
#serverCertificate: /etc/istio/ingressgateway-certs/mycrt.crt
#privateKey: /etc/istio/ingressgateway-certs/mykey.key
serverCertificate: /etc/istio/ingressgateway-certs/tls.crt
privateKey: /etc/istio/ingressgateway-certs/tls.key
# 重载 mygw1-tls.yaml
kubectl apply -f mygw1-tls.yaml
#客户端测试
curl -kv https://aa.yuan.cc # 访问通过,但是访问http是访问不通的,如果需要访问需要放行80端口,如果需要跳转需要加上 tls:httpsRedirect:
--------------------------------------------
# http 跳转 https
# 修改 mygw1-tls.yaml
vim mygw1-tls.yaml
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: mygw
spec:
selector:
istio: ingressgateway # use istio default controller
servers:
- port:
number: 80 #放行 80 端口
name: http-1
protocol: HTTP
hosts:
- "aa.yuan.cc" #当然此处的hosts和下面的hosts也可以使用通配符,但是一般不同的三级域名会有自己的证书,不太建议这么做,比如写成 "*.yuan.cc"
tls:
httpsRedirect: true #跳转 https
- port:
number: 443
name: https
protocol: HTTPS
hosts:
- "aa.yuan.cc"
tls:
mode: SIMPLE
#serverCertificate: /etc/istio/ingressgateway-certs/mycrt.crt
#privateKey: /etc/istio/ingressgateway-certs/mykey.key
serverCertificate: /etc/istio/ingressgateway-certs/tls.crt
privateKey: /etc/istio/ingressgateway-certs/tls.key

基于虚拟主机做 TLS

kubectl get secrets -n istio-system
kubectl delete secrts istio-ingressgateway-certs -n istio-system

# 生成证书
ls /etc/istio/ingressgateway-certs
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/istio/ingressgateway- certs/mykey22.key -out /etc/istio/ingressgateway-certs/mycrt22.crt -subj "/CN=mytest22/O=my-test22"

# 生成是generic类型的证书,不是tls证书
kubectl create secret generic istio-ingressgateway-certs \
--from-file=/etc/istio/ingressgateway-certs/mycrt.crt \
--from-file=/etc/istio/ingressgateway-certs/mykey.key \
--from-file /etc/istio/ingressgateway-certs/mycrt22.crt \
--from-file /etc/istio/ingressgateway-certs/mykey22.key -n istio-system

# 做 TLS 类型的证书的话,他只支持给一个站点提供服务
#编辑 mygw1-tls.yaml
vim mygw1-tls.yaml
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: mygw
spec:
selector:
istio: ingressgateway # use istio default controller
servers:
- port:
number: 80
name: http-1
protocol: HTTP
hosts:
- "aa.yuan.cc"
tls:
httpsRedirect: true #跳转 https
- port:
number: 443
name: https-2
protocol: HTTPS
hosts:
- "aa.yuan.cc"
tls:
mode: SIMPLE
serverCertificate: /etc/istio/ingressgateway-certs/mycrt.crt
privateKey: /etc/istio/ingressgateway-certs/mykey.key
- port:
number: 443
name: https
protocol: HTTPS
hosts:
- "bb.yuan.cc"
tls:
mode: SIMPLE
serverCertificate: /etc/istio/ingressgateway-certs/mycrt22.crt
privateKey: /etc/istio/ingressgateway-certs/mykey22.key

# 重载 mygw-tls
kubectl apply -f mygw-tls.yaml

# 客户端测试
curl -kv https://aa.yuan.cc #可以访问
curl -kv https://bb.yuan.cc #可以访问

# 查看 gateway 证书验证
kubectl get pods -n istio-system
kubectl exec -it istio-ingressgateway-8f568d595-8cvl8 -n istio-system -- bash
ls /etc/istio/ingressgateway-certs/
# mycrt.crt mycrt22.crt mykey.key mykey22.key

cert-manager 概念及使用

使用 cert-manager 来管理证书

通过 let encrypt 去帮助我们申请证书 (免费,但只有90天有效期,需要续期)

帮助文档:

https://letsencrypt.org/zh-cn/docs/

https://letsencrypt.org/zh-cn/how-it-works/

mkdir cert-m && cd cert-m
#创建 cert-manager
wget https://github.com/cert-manager/cert-manager/releases/download/v0.16.0/cert-manager.yaml
kubectl apply -f cert-manager.yaml #如果拉不到镜像,可以对对应的镜像 save 再 load 回来
kubectl get crd | grep cert

# 查看官网手册
cert-manager.io/docs/configuration/acme/dns01

# 修改DNS
# --- 登入阿里云,对域名的 DNS 服务器改为 dara.ns.cloudflare.com 和 rob.ns.cloudflare.com

# 登入 cloudflare,使用 cloudflare DNS
https://dash.cloudflare.com/
1、添加站点
2、选择 0 美元免费版
# 生成token , 添加API令牌
dash.cloudflare.com/profile/api-tokens
#创建自定义令牌 参考 cert-manager.io/docs/configuration/acme/dns01/cloudflare
创建完令牌后会给出一段令牌,需要及时保存

#创建 Secret, 编辑 mydnssecret
vim mydnssecret.yaml
apiVersion: v1
kind: Secret
metadata:
name: cloudflare-api-token-secret
type: Opaque
stringData:
api-token: <API Token> #将令牌复制到此处

#创建 secret
kubectl get ns
kubectl apply -f mydnssecret.yaml -n cert-manager

#创建 clusterissuer
kubectl get clusterissuer
# No resources found
vim ciss.yaml
apiVersion: cert-manager.io/v1alpha2
kind: ClusterIssuer
metadata:
name: letsencrypt-dns01 #用dns来审核,会比http审核的方式会方便一下
spec:
acme:
privateKeySecretRef:
name: letsencrypt-dns01
server: https://acme-v02.api.letsencrypt.org/directory #通过此站点访问
solvers:
- dns01:
cloudflare:
email: my-cloudflare-acc@example.com
apiTokenSecretRef:
key: api-token #此处value即为secret的stringData所对应的api-token,名字保持一致
name: cloudflare-api-token-secret #和secret的name保持一致

#启动
kubectl apply -f ciss.yaml
kubectl get clusterissuer
#letsencrypt-dns01 True

# 开始申请证书
kubectl get certificate
# www-ck8s-com True www-ck8s-com-tls
kubectl get secrets www-ck8s-com-tls -o yaml #可以看到证书的相关信息
kubectl get secrets www-ck8s-com-tls -o jsonpath='{.data.tls\.crt}' | base64 -d > mycertx.crt
kubectl get secrets www-ck8s-com-tls -o jsonpath='{.data.tls\.key}' | base64 -d > mycertx.key
cp mycertx.crt mycertx.key /etc/istio/ingressgateway-certs/

# 删除原来的 tls 证书
kubectl delete secrets istio-ingressgateway-certs -n istio-system

# 生成证书
kubectl create secret generic istio-ingressgateway-certs --from-file=/etc/istio/ingressgateway-certs/mycertx.crt --from-file=/etc/istio/ingressgateway-certs/mykeyx.key -n istio-system

vim vs.yaml
spec:
hosts:
- "www.ck8s.com"

vim mygw1-tls.yaml
servers:
- port:
number: 80
name: http-1
protocol: HTTP
hosts:
- "www.ck8s.com"
tls:
httpsRedirect: true #跳转 https
- port:
number: 443
name: https-2
protocol: HTTPS
hosts:
- "www.ck8s.com"
tls:
mode: SIMPLE
serverCertificate: /etc/istio/ingressgateway-certs/mycertx.crt
privateKey: /etc/istio/ingressgateway-certs/mykeyx.key

kubectl apply -f vs.yaml
kubectl apply -f mygw1-tls.yaml

# 内网测试机 本机修改 hosts 文件 192.168.26.230 www.ck8s.com www
浏览器访问 www.ck8s.com ,显示https 并且没有显示警告不安全

# 将 vs hosts 恢复为之前的

![创建自定义令牌](/img/Istio 安全管理/创建自定义令牌.png)

![令牌创建成功](/img/Istio 安全管理/令牌创建成功.png)

mTLS认证 PeerAuthentication

mTLS (mutual TLS,双向TLS): 让客户端和服务器端通信的时候都必须进行TLS认证

默认情况下,在网格内部 默认启用了mTLS 了。

PERMISSIVE:工作负载接受双向 TLS 和纯文本流量。

当没有 Sidecar 的工作负载无法使用双向 TLS 时,此模式适合用在迁移过程。

通过使用 sidecar 注入迁移工作负载后,应该将模式切换为 STRICT。

STRICT:工作负载仅接受双向 TLS 通信。

### 测试没有添加 mTLS 
# 编辑 vs yaml
vim vs.yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: myvs
spec:
hosts:
- "aa.yuan.cc"
- "svc1" #添加
gateways:
- mygw
http:
- route:
- destination:
host: svc1

# 重载 vs yaml
kubectl apply -f vs.yaml

kubectl get pods -n default # 没有被注入的pod
#podx 1/1 Running
kubectl exec -it podx -n default -- bash
curl svc1.ns1 #返回111,代表网格之外现在也可以被访问
111
--------------------------------------------------------
### 测试添加 mTLS,如果没有在网格里的 pod 是不能访问的
vim mtls1.yaml
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: default
spec:
mtls:
mode: STRICT #仅接受双向 TLS 通信

# 创建 mtls
kubectl get PeerAuthentication
# No resources found in ns1 namespace
kubectl apply -f mtls1.yaml

#网格外 pod 测试
kubectl exec -it podx -n default -- bash
curl svc1.ns1 #访问不通的
# curl: (56) Recv failure: COnnection reset by peer

# istio 对 podx 进行注入,加入网格内在进行测试
kubectl delete pod podx -n default
istioctl kube-inject -f podx.yaml | kubectl apply -f - -n default
kubectl get pods -n default
#podx 2/2 Running
kubectl exec -it podx -n default -- bash
curl svc1.ns1 #访问的通了,返回111
# 111

单pod设置mTLS

#把网格内的 podx 去除
kubectl delete pod podx -n default
kubectl apply -f podx.yaml -n default
kubectl exec -it podx -n default -- bash
curl svc1.ns1
#curl: (56) Recv failure: Connection reset by peer

#创建namespace ns2,给 ns2 添加 istio 标签
kubectl create ns ns2
kubectl get ns --show-labels
#ns1 istio-injection=enabled.kubernetes.io/metadata.name=ns1
#ns2 kubernetes.io/metadata.name=ns2
kubectl label ns ns2 istio-injection=enabled

# 创建 ns2 内的 podx,svc2 ,让 default 中的 podx 访问测试
kubectl apply -f podx.yaml -n ns2
kubectl get pods -n ns2
#podx 2/2 Running
kubectl expose --name=svc2 pod podx --port=80 -n ns2

#default podx进行测试
kubectl exec -it podx -n default -- bash
curl svc2.ns2 #返回 nginx 页面,访问连通

#清理下环境
kubectl delete pod podx -n ns2
kubectl delete svc svc2 -n ns2

------------------------------------------------------------
# 修改 mtls,针对单 pod 设置 mTLS
vim mtls1.yaml
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: default
spec:
selector:
matchLabels:
run: pod1 #只应用与 pod1 上,与 pod1 通信时必须通过 mTLS
mtls:
mode: STRICT #仅接受双向 TLS 通信

# 修改 mtls,针对单 pod 设置 mTLS,放行某端口,对某端口可以不建立 mTLS
vim mtls1.yaml
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: default
spec:
selector:
matchLabels:
run: pod1 #只应用与 pod1 上,与 pod1 通信时必须建立 mTLS
mtls:
mode: STRICT #或者此处可以改为PERMISSIVE
portLevelMtls:
80:
mode: DISABLE # 和端口80通信可以不需要 mTLS

授权管理AuthorizationPolicy

设置的是允许哪些客户端允许过来访问

拒绝优先级 > 允许优先级

基于命名空间做授权

#清理环境,先把相关的 tls 先删掉
kubectl delete -f mtls1.yaml
#---有点卡,把vm2相关的先关掉
kubectl delete svc vm2-svc
kubectl delete -f ../chap4/mygw.yaml
kubectl delete -f ../chap4/xx/

#客户端测试, default 中的 podx
kubectl exec -it podx -n default -- bash
curl aa.yuan.cc
111
curl bb.yuan.cc
222

kubectl get AuthorizationPolicy
#No resources found in ns1 namespace
mkdir chap5/ap && cd chap5/ap

#编辑全局拒绝策略
vim rule-default-deny-all.yaml
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: ns1-deny-all
spec:
{}
#这种在spec下直接写一个{} 默认是拒绝的意思,且优先级最低

# 启用AuthorizationPolic,podx测试
kubectl apply -f rule-default-deny-all.yaml
curl svc1.ns1
#RBAC: access denied

#设置某个客户端可以访问
vim rule-allow-ns.yaml
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: allow-ns
namespace: ns1
spec:
selector:
matchLabels:
run: pod1
action: ALLOW #允许规则,如果没有匹配下面的规则,是不允许其他客户端来访问的
rules:
- from:
- source:
namespaces:
- "ns2" #只允许ns2命名空间的可以来访问
#- "default"

#启动允许策略,再创建个 ns2 的 podx 进行测试
kubectl apply -f rule-allow-ns.yaml
kubectl apply -f podx.yaml -n ns2
kubectl exec -it podx -n ns2 -- bash
curl svc1 #可以访问
#111

## 注意:
#命名空间测试的话,客户端pod要注入 istio 才能生效,from和to 可以一起用。当前命名 要通过 svc1.ns1 这种访问才能生效。

基于 IP 的限制

# 编辑放行规则
vim rule-allow-ns.yaml

apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: allow-ns
namespace: ns1
spec:
selector:
matchLabels:
run: pod1
action: ALLOW #允许规则,如果没有匹配下面的规则,是不允许其他客户端来访问的
rules:
- from:
#- source:
#notnamespace #除了此命名空间,都可以访问
#namespaces:
#- "ns2" #只允许ns2命名空间的可以来访问
- source:
ipBlocks:
#- "0.0.0.0/0" #允许所有客户端访问
#- "10.244.0.0/16"
- "192.168.26.9/24" #允许此网段的服务器访问
#- source:
#remoteIpBlocks: #这里remoteIpBlocks设置的是拒绝的意思,凡是匹配了这个地址,都是拒绝的
#- "192.168.26.23"
#- source:
#principals: ["cluster.local/ns/ns1/sa/default"] #指定ns2里,以default跑的pod,且同样的sa才可以访问

# 重载 放行规则
kubectl apply -f rule-allow-ns.yaml

when 的使用

- to:
- operation:
methods: ["GET"]
hosts:
- svc1.ns1
paths:
- /demo1/*
- when:
- key: request.headers[test]
values:
- "test"
# when和from这些是或的关系
# pod 测试
curl svc1.ns1
#RBAC: access denied
curl svc1.ns1 --header "test:test"
#111

JWT 认证

JWT全称: Json Web Token

本质就是类似用户名和密码,客户端访问的时候,必须要提供这些token信息,不然访问不了。

这个token有固定的格式,采用JWK/JWKs格式的。

JWK: Json Web Key

JWKs :JWK set

生成TOKEN

https://jwt.io/

生成 token

yum install boost-python36.x86_64 python3-pip.noarch -y
pip3 install ipython pip

#国内源
mkdir -p .pip
vim .pip/pip.conf
[global]
index-url = http://pypi.douban.com/simple
[install]
trusted-host=pypi.douban.com

pip3 install --upgrade pip
pip3 install jwt
pip3 install ipython jwcrypto

mkdir token && cd token
openssl genrsa -out key.pem 2048
openssl rsa -in key.pem -pubout -out pub.pem

写一个脚本-- 用于实现生成 jwt token

vim aa.py

#!/usr/bin/python3
#-*- coding: UTF-8 -*-
from jwcrypto.jwk import JWK
from pathlib import Path

private_key = Path("key.pem").read_bytes()
jwk = JWK.from_pem(private_key)

# 导出公钥 RSA Public Key
public_key = jwk.public().export_to_pem()
print(public_key)

print("="*30)

# 导出 JWK
jwk_bytes = jwk.public().export()
print(jwk_bytes)

python3 aa.py

显示生成kid

登入 https://jwt.io/,加密类型选择 RS256,添加自己的信息,得到 Encode

# 创建 RequestAuthentication
vim ra.yaml

apiVersion: "security.istio.io/v1beta1"
kind: "RequestAuthentication"
metadata:
name: "myra"
spec:
selector:
matchLabels:
run: pod1 #应用于pod1
jwtRules:
- issuer: "testing@secure.istio.io" #与jwt生成信息对应
jwks: |
{ "keys":
[
{
"e":"AQAB",
"kid":"DHFbpoIUqrY8t2zpA2qXfCmr5VO5ZEr4RzHU_-envvQ",
"kty":"RSA",
"n":"xAE7eB6qugXyCAG3yhh7pkDkT65pHymX- P7KfIupjf59vsdo91bSP9C8H07pSAGQO1MV_xFj9VswgsCg4R6otmg5PV2He95lZdHtOcU5DXIg_pbhLdKXbi66GlVeK6ABZOUW3 WYtnNHD-91gVuoeJT_DwtGGcp4ignkgXfkiEm4sw-4sfb4qdt5oLbyVpmW6x9cfa7vs2WTfURiCrBoUqgBo_- 4WTiULmmHSGZHOjzwa8WtrtOQGsAFjIbno85jp6MnGGGZPYZbDAa_b3y5u- YpW7ypZrvD8BgtKVjgtQgZhLAGezMt0ua3DRrWnKqTZ0BJ_EyxOGuHJrLsn00fnMQ"
}
]
}

# 启用 ra
kubectl apply -f ra.yaml

# 参数分析
{
"alg": "RS256", # 算法「可选参数」
"kty": "RSA", # 密钥类型
"use": "sig", # 被用于签名「可选参数」
"kid": "xxx", # key 的唯一 id
"n": "xxx", #n 是公钥的指数
"e": "AQAB" #是公钥的模数(modulus)
}

# 测试
kubectl apply -f default-deny-all.yaml #默认拒绝所有
vim allow-only2.yaml #只允许特定的可以访问
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: require-jwt
spec:
selector:
matchLabels:
run: pod1
action: ALLOW
rules:
- from:
- source:
requestPrincipals: ["testing@secure.istio.io/testing@secure.istio.io"] #含有被此签名的才可以被访问

kubectl apply -f allow-only2.yaml
#进入pod2 进行测试
curl -I -H "Authorization: Bearer $TOKEN" svc1.ns1 #使用正确的token才可以访问
#111
curl -I -H "Authorization: Bearer invalidToken" svc1.ns1 #随便写了个假的,返回401
# Jwt is not in the form of Header.Payload.Signature with two dots and 3 sectionsroot@pod2

Istio ServiceEntry 流量管理

· 阅读需 9 分钟

ServiceEntry 是通常用于在 Istio 网格之外启用对服务的请求

# 现有环境
kubectl get gw
#mygw
kubectl get vs
#myvs ["mygw"] ["aa.yuan.cc"]
kubectl get dr
#mydr svc1
kubectl get pods
#pod1
#pod2

kubectl exec -it pod1 -- bash
curl https://www.baidu.com #可以访问到外网,代表所有的数据都是可以出去的
# serviceEntry 就可以解决这个问题

创建 ServiceEntry

mkdir chap4 && cd chap4
cp ../chap3/vs.yaml ../chap3/mygw1.yaml ./

# 编辑 serviceEntry yaml
vim se1.yaml

apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: myse
spec:
hosts:
- www.baidu.com #对此网址进行限制
ports:
- number: 443 #通过443访问
name: https
protocol: HTTPS #通过HTTPS的方式访问
resolution: DNS #解析通过DNS解析
location: MESH_EXTERNAL # MESH_EXTERNAL -- 网格外部;MESH_INTERNAL -- 网格内部

# 创建 serviceEntry
kubectl apply -f se1.yaml
# 进入pod测试
kubectl exec -it pod1 -- bash
curl https://www.baidu.com #并没有被拦截,代表通过DNS并不可行

# 相当于黑名单
# 修改 serviceEntry ,通过自定义指定不可访问
vim se1.yaml

apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: myse
spec:
hosts:
- www.baidu.com #对此网址进行限制
ports:
- number: 443 #通过443访问
name: https
protocol: HTTPS #通过HTTPS的方式访问
#resolution: DNS #解析通过DNS解析
resolution: STATIC
location: MESH_EXTERNAL
endpoints:
- address: 192.168.26.3 #指定访问的 www.baidu.com 就是 192.168.26.3

# 重启 serviceEntry
kubectl apply -f se1.yaml
# 进入pod测试
kubectl exec -it pod1 -- bash
curl https://www.baidu.com #访问被拦击

默认策略

# 修改策略
kubectl edit configmap istio -n istio-system -o yaml

data:
mesh: |-
accessLogFile: /dev/stdout
defaultConfig:
discoveryAddress: istiod.istio-system.svc:15012
proxyMetadata: {}
tracing:
zipkin:
address: zipkin.istio-system:9411
enablePrometheusMerge: true
outboundTrafficPolicy: # 如果不加则默认是 ALLOW_ANY
mode: REGISTRY_ONLY # 只有注册的,才可以访问
rootNamespace: istio-system
#此策略也可以在安装istio时进行设置:
istioctl install --set profile=demo -y --set meshConfig.outboundTrafficPolicy.mode=REGISTRY_ONLY

# 测试
kubectl exec -it pod1 -- bash
curl https://www.jd.com #访问不通
curl svc1 #可以访问通,返回111

# 模拟环境 创建 podx
vim /root/podx.yaml

apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: podx
name: podx
spec:
terminationGracePeriodSeconds: 0
containers:
- image: nginx
imagePullPolicy: IfNotPresent
name: podx
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}

#启动 podx
kubectl apply -f /root/podx.yaml

#启动 svcx
kubectl expose --name=svcx pod podx --port=80

# 使用 pod1 测试
kubectl exec -it pod1 -- bash
curl svcx #可以访问到

# 更改 podx 的 namespace 再次测试 (default namespace 没有自动被 istio 注入)
kubectl apply -f podx.yaml -n default
kubectl expose --name=svcx pod podx --port=80 -n default

------------
#pod1 还是可以访问通的,可能是集群内是可以访问的通的,只是限制出口的流量
kubectl get pods -n default
kubectl delete pod podx -n default
kubectl get svc svcx -n default
kubectl delete svc svcx -n default

# 相当于设置 se 白名单
# 修改 serviceEntry yaml
vim se1.yaml

apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: myse
spec:
hosts:
- www.baidu.com #此处就相当于了放行
- www.jd.com
ports:
- number: 443 #通过443访问
name: https
protocol: HTTPS #通过HTTPS的方式访问
resolution: DNS #解析通过DNS解析
#resolution: STATIC
location: MESH_EXTERNAL
# 启动 se ,进行测试
kubectl apply -f se1.yaml
# 使用 pod1 测试
kubectl exec -it pod1 -- bash
curl https://www.jd.com #可以访问通
curl https://www.baidu.com #可以访问通
curl https://www.qq.com #访问不通

#如果想修改的所有放行,则修改回之前的策略
kubectl edit configmap istio -n istio-system -o yaml

outboundTrafficPolicy: # 如果不加则默认是 ALLOW_ANY
mode: ALLOW_ANY # 改为所有
#删除 se
kubectl delete -f se1.yaml
# pod 则默认都可以访问外网地址

workEntry

![workEntry 管理流程图](/img/Istio ServiceEntry 出口流量/workEntry管理流程图.png)

**目的是把 其他主机/虚拟机/服务器 纳入到service mesh里 **

此处现将我们现有的测试环境做一套快照,不然环境弄乱了不好恢复.....

  • 启用主动注册的性能 (先不做)
istioctl install --set profile=demo --set 

values.pilot.env.PILOT_ENABLE_WORKLOAD_ENTRY_AUTOREGISTRATION=true
  • 启动 自动注册 和 智能DNS
istioctl install --set profile=demo --set values.pilot.env.PILOT_ENABLE_WORKLOAD_ENTRY_AUTOREGISTRATION=true --set meshConfig.defaultConfig.proxyMetadata.ISTIO_META_DNS_CAPTURE='\"true\"'

#此处本地的测试环境启动智能DNS会失败,就启动自动注册就好
istioctl install --set profile=demo --set values.pilot.env.PILOT_ENABLE_WORKLOAD_ENTRY_AUTOREGISTRATION=true

kubectl get pods -n istio-system -o wide
#istiod 10.244.186.142

kubeadm config view
# podSubnet: 10.244.0.0/16

新创建虚拟机进行测试

#创建虚拟机 IP:192.168.26.23, ping k8s pod 的 ip 是 ping 不通的
ping 10.244.186.142 #不通

#添加网关
route add -net 10.244.0.0 gw 192.168.26.82 netmask 255.255.0.0 #gw的ip是gw所在节点的IP,可以写进配置文件中永久生效

ping 10.244.186.142 #可以访问了

## 下载 sidecar 安装包,根据系统来选择
https://storage.googleapis.com/istio-release/releases/1.10.3/deb/istio-sidecar.deb
dpkg -i istio-sidecar.deb

https://storage.googleapis.com/istio-release/releases/1.10.3/rpm/istio-sidecar.rpm
rpm -ivh istio-sidecar.rpm

创建 WorkloadGroup

cd chap4 && vim mywg.yaml

apiVersion: networking.istio.io/v1alpha3
kind: WorkloadGroup
metadata:
name: mywg
namespace: ns1
spec:
metadata:
annotations: {}
labels:
app: test #指定的标签
template:
ports: {}
serviceAccount: sa1 #使用sa1来管理

# 创建 wg
kubectl get wg
kubectl apply -f mygw.yaml && kubectl get wg

# 创建 sa
kubectl get sa
# default 1 ##是在 default 的 namespace
kubens
# ns1 所在 ns1 的 namespace 上

#创建sa1
kubectl create sa sa1

# 生成虚拟机安装所需要的文件
mkdir 11
istioctl x workload entry configure -f mywg.yaml -o 11
ls 11
# cluster.env hosts istio-token mesh.yaml root-cert.pem
# 把11目录及里面的东西拷贝到虚拟机
scp -r 11/ 192.168.26.23:~

虚拟机的操作

# centos 7.6 
# 在虚拟机上安装根证书
mkdir -p /etc/certs
cp 11/root-cert.pem /etc/certs/root-cert.pem

# 安装令牌
mkdir -p /var/run/secrets/tokens
cp 11/istio-token /var/run/secrets/tokens/istio-token
cp 11/cluster.env /var/lib/istio/envoy/cluster.env
cat /var/lib/istio/envoy/cluster.env #可以看到集群生成的一些配置信息

# 将网格配置安装到/etc/istio/config/mesh
cp 11/mesh.yaml /etc/istio/config/mesh

mkdir -p /etc/istio/proxy
chown -R istio-proxy /var/lib/istio /etc/certs /etc/istio/proxy /etc/istio/config /var/run/secrets /etc/certs/root-cert.pem #安装完 sidecar 自动生成 istio-proxy 的用户

# 修改/etc/hosts
#kubectl get pods -o wide -n istio-system 拿到 istiod 的 IP 地址
10.244.186.142 istiod.istio-system.svc

# 查看并启动 istio 的 sidecar
systemctl list-unit-files | grep istio
#disabled
systemctl start istio.service
systemctl enable istio.service #设置上开机自启动
systemctl is-active istio.service
#activating

# 查看日志
tail -f /var/log/istio/istio.log
#报有 eroor 。。。
#尝试换一个系统,用 ubuntu 试一下
# 使用 ubuntu 18.04 试一下,ip 改为 192.168.26.23 ,再做一遍上面的操作
https://storage.googleapis.com/istio-release/releases/1.10.3/deb/istio-sidecar.deb
dpkg -i istio-sidecar.deb

# 后把11目录及里面的东西拷贝到虚拟机
scp -r 11/ 192.168.26.23:~ #冲突那就是ssh的hosts有问题,删掉就可以了
vim ~/.ssh/known_hosts #删除192.168.26.23信息
scp -r 11/ 192.168.26.23:~

# 在虚拟机上安装根证书
mkdir -p /etc/certs
cp 11/root-cert.pem /etc/certs/root-cert.pem

# 安装令牌
mkdir -p /var/run/secrets/tokens
cp 11/istio-token /var/run/secrets/tokens/istio-token
cp 11/cluster.env /var/lib/istio/envoy/cluster.env
cat /var/lib/istio/envoy/cluster.env #可以看到集群生成的一些配置信息

# 将网格配置安装到/etc/istio/config/mesh
cp 11/mesh.yaml /etc/istio/config/mesh

mkdir -p /etc/istio/proxy
chown -R istio-proxy /var/lib/istio /etc/certs /etc/istio/proxy /etc/istio/config /var/run/secrets /etc/certs/root-cert.pem #安装完 sidecar 自动生成 istio-proxy 的用户

# 修改/etc/hosts
#kubectl get pods -o wide -n istio-system 拿到 istiod 的 IP 地址
10.244.186.142 istiod.istio-system.svc

#添加路由
route add -net 10.244.0.0 gw 192.168.26.82 netmask 255.255.0.0
# 删除路由
route delete 10.244.0.0 dev ens32 #SIOCDELRT: No such process
route -n #查看路由
route delete gw 10.244.0.0 dev ens32 #gw: Host name lookup filure
route -n #查看路由
ping 10.244.186.142 #测试能否 ping 通,此时是 ping 通的

# 查看并启动istio
systemctl is-active istio #inactive 还没有启动
systemctl start istio && systemctl enable istio
systemctl is-active istio #active 已经启动了起来

# 查看日志
tail -f /var/log/istio/istio.log #已经启动了,日哦 可以了,看来centos没有ubuntu支持的好
kubectl get we #No resources found in ns1 namespace

# 为了防止目录太乱,在 chap 创建 xx 存放 we yaml
mkdir xx && cd xx
vim we.yaml

apiVersion: networking.istio.io/v1beta1
kind: WorkloadEntry
metadata:
name: mywe
namespace: ns1
spec:
serviceAccount: sa1 # 对应 WorkloadGroup 的 sa
address: 192.168.26.23 # 相当于把此虚拟机当成 pod 来用
labels:
app: test
#instance-id: vm2
---
apiVersion: v1
kind: Service
metadata:
name: vm2-svc
namespace: ns1
labels:
app: test
spec:
ports:
- port: 80
name: http-vm
targetPort: 80
selector:
app: test

# 创建 we
kubectl apply -f we.yaml
kubectl get svc
#svc1
#svcx #用不到了,删掉了
#vm2-svc #创建了此 svc
kubectl delete svc svcx

# 测试连通
#vm23 启动80端口
python3 -m http.server 80

kubectl exec -it pod1 -- bash
curl vm2-svc #可以看到返回内容

#vm23
apt-get install nginx -y
echo "hello vm vm"
systemctl start nginx

kubectl exec -it pod1 -- bash
curl vm2-svc #可以看到 nginx 页面内容
--------------------------------------------------------------
#此时代表 istio 网格内 pod 和 虚拟机vm23已经建立了通信
#清理一下环境
cd xx
kubectl delete -f we.yaml
cd ../ && kubectl delete -f mywg.yaml

外部服务器与网格内服务通信

cd chap4
kubectl apply -f mywg.yaml
cd xx && kubectl apply -f we.yaml
kubectl get gw
kubectl get vs
cp vs.yaml vs2.yaml

#编辑 vs2 yaml
vim vs2.yaml

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: myvs2
spec:
hosts:
- "bb.yuan.cc"
gateways:
- mygw
http:
- route:
- destination:
host: vm2-svc

# 启动 vs2
kubectl apply -f vs2.yaml

#创建一个虚拟机,在此虚拟机操作
vim /etc/hosts
192.168.26.230 bb.yuan.cc bb
curl bb.yuan.cc
# 返回 vm23 nginx的信息:hello vm vm

Istio DestinationRule 流量管控

· 阅读需 8 分钟

DestinationRule 是 VirtualService 路由生效后,配置应用于请求的策略集

dr 的作用:

  • 定义子集
  • 流量控制

![DestinationRule流量管控.png](/img/Istio DestinationRule 流量管控/DestinationRule流量管控.png)

DR的基本用法-定义子集

mkdir chap3 && cd chap3
cp chap2/vs.yaml chap2/mygw1.yaml chap3/

#清除之前环境的svc,再新建一个 svc 来负载 pod1 pod2
kubectl delete svc svc2
kubectl delete svc svc1

#给 pod1 pod2 设置同样的标签
kubectl label pod pod1 name=pod
kubectl label pod pod2 name=pod
kubectl get pods -l name=pod

#查看标签
kubectl get pods --show-label
#具有共同的标签 name=pod,不同的标签:run=pod1 ; run=pod2

# 虽然写的是pod1,但是还是根据selecter的标签
kubectl expose --name=svc1 pod pod1 --port=80 --selector=name=pod

#查看 svc1 关联的pod
kubectl describe svc svc1 #在Endpoints上可以看到已经关联了 pod1 pod2

# 编辑 vs yaml
vim vs.yaml

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: myvs
spec:
hosts:
- "aa.yuan.cc"
gateways:
- mygw
http:
- route:
- destination:
host: svc1
subset: xx #subset是自定的标识
weight: 55
- destination:
host: svc1
subset: yy
weight: 45

# 启动 vs
kubectl apply -f vs.yaml

# 定义dr,编辑 dr yaml
vim dr.yaml

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: mydr
spec:
host: svc1 #管理的是 svc1 的流量
subsets:
- name: xx #自定义,跟 vs 的 subset 做匹配
labels: #根据标签做匹配
#version: v1
run: pod1
- name: yy #自定义
labels:
#version: v2
run: pod2

# 启动 dr
kubectl apply -f dr.yaml
kubectl get dr

# 客户端测试
while true ; do curl aa.yuan.cc ; sleep 1 ; done

查看 kiali 权重图

![istio测试权重50](/img/Istio DestinationRule 流量管控/istio测试权重50.png)

蓝绿部署

# 定义dr,编辑 dr yaml
vim dr.yaml

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: mydr
spec:
host: svc1 #管理的是 svc1 的流量
subsets:
- name: v1 #自定义,跟 vs 的 subset 做匹配
labels: #根据标签做匹配
#version: v1
run: pod1
- name: v2 #自定义
labels:
#version: v2
run: pod2

# 编辑 vs yaml
vim vs.yaml

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: myvs
spec:
hosts:
- "aa.yuan.cc"
gateways:
- mygw
http:
- route:
- destination:
host: svc1
subset: v1 #subset是自定的标识
weight: 100
- destination:
host: svc1
subset: v2
weight: 0 # 在权重上做蓝绿部署,100 : 0

# 启动 vs,dr
kubectl apply -f dr.yaml
kubectl apply -f vs.yaml

# 查看 kiali 流量分布

金丝雀发布(灰度发布)

# 编辑 vs yaml
vim vs.yaml

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: myvs
spec:
hosts:
- "aa.yuan.cc"
gateways:
- mygw
http:
- route:
- destination:
host: svc1
subset: v1
weight: 100
- destination:
host: svc1
subset: v2
weight: 0

# 流量的变化
1、v2 并没有对任何人开放
2、v2 对极少一部分人开放 #将权重从 100:0 切为 90:10
3、v2 给更多的人开放 #权重继续更改...

会话保持

会话保持的目的是,让同一个客户端访问的时候,访问到同一个pod上,istio用一致性哈希算法实现

了会话保持。

在trafficPolicy.loadBalancer.consistentHash里的字段包括:

  • httpHeaderName: 根据HTTP Header获取哈希值

  • httpCookie: 根据HTTP Cookie获取哈希值 (根据 Cookie 来做会话保持)

  • userSourceIp: 根据源IP获取哈希值 (根据 IP 地址来做的会话保持)

  • minimumRingSize: 哈希环所需的最小虚拟节点数量,默认值为1024

  • 使用httpCookie时的字段:

  • name: cookie的名称

  • path: 设置cookie的路径

  • ttl:cookie的生命期

while true ; do curl aa.rhce.cc --cookie "user=tester" --silent -w "Status: %{http_code}\n"; sleep 1 ; done
# 备份一下 vs yaml,恢复最初的 vs
mv vs.yaml vs-subset.yaml
cp ../chap2/vs.yaml .

# 编辑 vs yaml
vim vs.yaml

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: myvs
spec:
hosts:
- "aa.yuan.cc"
gateways:
- mygw
http:
- route:
- destination:
host: svc1

# 启动 vs
kubectl apply -f vs.yaml

# 修改 dr yaml,添加 trafficPolicy: httpCookie:
vim dr.yaml

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: mydr
spec:
host: svc1
trafficPolicy:
loadBalancer:
consistentHash:
httpCookie:
name: user
ttl: 60s
subsets:
- name: v1
labels:
run: pod1
- name: v2
labels:
run: pod2

# 启动 dr
kubectl apply -f dr.yaml

# 客户端测试,需要带着 cookie 做测试
while true ; do curl --cookie "user=tester" aa.rhce.cc ; sleep 1 ; done
# 返回结果一直是 pod1的111 或者是 pod2的222,不会切换,如果 cookie 的 value 不是 user,则会话不保持:
while true ; do curl --cookie "user11=tester" aa.rhce.cc ; sleep 1 ; done #这是不保持的

基于 ip 地址的会话保持

# 修改 dr yaml,添加 trafficPolicy:
vim dr.yaml

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: mydr
spec:
host: svc1
trafficPolicy:
loadBalancer:
consistentHash:
useSourceIp: true #根据 IP 地址来做的会话保持
#httpCookie:
# name: user
# ttl: 60s
subsets:
- name: v1
labels:
run: pod1
- name: v2
labels:
run: pod2

# 启动 dr
kubectl apply -f dr.yaml

# 客户端测试
while true ; do curl aa.rhce.cc ; sleep 1 ; done # IP不变,返回结果是固定的

# 清理环境
cp dr.yaml dr-session.yaml
kubectl delete -f dr.yaml

DR调度算法-RANDOM-ROUND_ROBIN

# 编写 dr-arithmetic yaml
cp dr.yaml dr-arithmetic.yaml
vim dr-arithmetic.yaml

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: mydr
spec:
host: svc1
trafficPolicy:
loadBalancer:
simple: RANDOM #随机策略
#simple: ROUND_ROBIN #轮询策略

-------------------------------------------------
# 针对于多个 svc ,但只需对某个 svc 做策略
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: mydr
spec:
host: svc1
subsets:
- name: v1
labels:
run: pod1
- name: v2
labels:
run: pod2
trafficPolicy:
loadBalancer:
simple: RANDOM #随机策略
#simple: ROUND_ROBIN #轮询策略

DR调度算法-LEAST_CONN

kubectl get pod -o wide # 查看pod在哪个节点

# 编写 dr-arithmetic yaml
vim dr-arithmetic.yaml

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: mydr
spec:
host: svc1
trafficPolicy:
loadBalancer:
simple: LEAST_CONN #当进行轮训的时候,先检测哪个pod负载较轻, 就把流量转发到哪个pod上去。

# 重启 dr arithmetic
kubectl apply -f dr-arithmetic.yaml

#在对应节点安装 webbench 压测工具
yum install ctags* gcc -y
mkdir -p -m 644 /usr/local/man/man1

wget wget http://www.ha97.com/code/webbench-1.5.tar.gz
tar zxvf webbench-1.5.tar.gz ; cd webbench-1.5

make && make install

# 查看 pod ip
kubectl get pods -owide

# 压测 pod2
webbench -c 500 -t 300 http://10.244.223.203/

# 查看 kiali
# 清理环境
kubectl delete -f dr-arithmetic.yaml

熔断

熔断的定义是在DR里定义的,主要有2部分

第一部分:

trafficPolicy:

用于定义连接池

连接池的定义分成两类:

  • TCP 连接
    • maxConncections: 到目标主机的最大连接数
    • connectTimeOut: TCP连接超时,最小值必须要大于1ms
  • Http 连接
    • http1MaxPendingReguests: 针对一个目标的HTTP请求最大排队数量,默认是1024
    • http2MaxRequests: 对一个后端的最大请求数
    • maxRequestsPerConnection:
    • maxReties: 在给定的时间,集群所有主机最大重试数,默认值为3

第二部分:

outlierDetection:

​ 用于定义熔断的条件,达到什么条件就开始熔断

  • consecutiveErrors: 超过这错误数量之后,主机会被移除连接池。默认是5,当上游服务是

    http服务时,5xx返回代码会记录为错误。当上游主机提供的是TCP服务时,TCP连接超时和连接错

    误被标记为错误。

  • interval: 在移除检测之间的时间间隔,默认是10s,必须要>=1ms

  • baseEjectionTime: 最小的移除时间长度。主机每次被移除后的间隔时间等于被移除的次数和

    最小移除时间的乘积。这样的实现,让系统能够自动增加不健康上游服务实例的间隔时间,默认时

    间为30s。

  • maxEjectionPercent: 上游服务的负载均衡池中允许被移除的主机的最大百分比,默认是10%。

# 编辑 dr rongduan 
vim dr-rongduan.yaml

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: mydr
spec:
host: svc1
trafficPolicy:
connectionPool:
http:
http1MaxPendingRequests: 1 #针对一个目标的HTTP请求最大排队数量,最大为1(根据业务而设置)
maxRequestsPerConnection: 1
outlierDetection:
consecutiveGatewayErrors: 1
interval: 10s
baseEjectionTime: 3m
maxEjectionPercent: 100
#subsets:
#- name: v2
# labels:
# run: pod2

# 启动 dr-rongduan
kubectl apply -f dr-rongduan.yaml

# 下载 fortio 测试
rpm -i https://github.com/fortio/fortio/releases/download/v1.30.0/fortio-1.30.0-1.x86_64.rpm

# fortio测试
fortio load -c 5 -n 20 -qps 0 http://aa.yuan.cc
# 命令解释如下: -c 表示并发数 -n 一共多少请求 -qps 每秒查询数,0 表示不限制
# 可以看到 Code 200:(25.0 %) ; Code 503:(75.0 %)

# 如果对 dr-rongduan.yaml 进行修改 http1MaxPendingRequests: 5,那么再次执行 fortio 并发为 5,则 Code 状态码都为 200