浏览 4.2k
原文作者:Amir Rawdat
原文链接:配置流量控制 | 《使用 NGINX 部署和保护 Kubernetes Ingress Controller》章节精选
转载来源:NGINX 开源社区
NGINX 唯一中文官方社区 ,尽在 nginx.org.cn
编者按——企业级 Kubernetes 应用和 API 互联指南《使用 NGINX 部署和保护 Kubernetes Ingress Controller》中文版现已正式上线,现可点击文末“阅读原文”免费下载电子版全本。
这本超过 180 页的实践指南适用于平台工程、应用开发以及基础设施和运营团队。它通过提供有关安装和配置、四层和七层负载均衡、流量精分技术、分布式跟踪和监控、单点登录等方面信息,展示了如何将 NGINX Ingress Controller、NGINX Service Mesh 和 NGINX App Protect 等工具集成到您的环境中,以简化 Kubernetes 应用互联。
本文节选了本书的第 2 章《流量管理用例》中的精彩段落:配置流量控制。
对于大多数在线提供服务的公司来说,客户满意度和资源的始终可访问性至关重要。客户流失、收入损失(每停机一小时损失高达 55 万美元)及员工工作效率下降不仅会直接减损公司收益,而且还将损害公司声誉。如果一家公司未能成功使用现代应用交付技术和方法来管理其在线流量,客户就会迅速在社交媒体上做出评价——您肯定不想成为大家口中的“那家公司”。
因此,流量管理策略是现代应用架构规划和交付中的关键一环。这需要改变流量处理方式,比如控制服务而非数据包,或通过 Kubernetes API 动态调整流量管理规则。无论您是要限制应用请求数以避免级联故障,还是要测试新应用服务在实际流量负载下的稳定性——在不停机的情况下管理流量既是一门科学,也是一门艺术,当然更是一种兼修并蓄的方法。
流量控制和流量精分对于最大限度地提高应用性能至关重要,但选择何种方法取决于您的目标:
您可以利用 NGINX Ingress Controller 和 NGINX Service Mesh 实施上述所有方法,在几秒钟内轻松配置稳健的流量路由和精分策略。
NGINX Ingress Controller 和 NGINX Service Mesh 可帮助您在几秒钟内实现稳健的流量控制和流量精分。但是,它们并非同样适用于所有用例和应用架构。一般情况下:
在本节中,您将利用 Traffic-Management/bookinfo-vs.yaml 中定义的 VirtualServer 资源,使用 NGINX Ingress Controller 来暴露 bookinfo 示例应用。
VirtualServer 和 VirtualServerRoute 是定制的 NGINX 资源,在 NGINX Ingress Controller 1.5 中引入。它们能实现的用例,包括流量精分和基于内容的高级路由(标准 Kubernetes Ingress 资源中则有限的注解,这是一种有限的使用)。
bookinfo-vs.yaml 的第 8 行引用了在 Traffic-Management/bookinfo-secret.yaml 中定义的面向 bookinfo 应用的 Kubernetes Secret。在示例应用中,Secret 是自签名;在生产环境中,我们强烈建议您使用由证书颁发机构生成的真实密钥和证书。
bookinfo-vs.yaml 的第 9-16 行定义了将 bookinfo.example.com 请求定向到 productpage service 的路由规则。
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
metadata:
name: bookinfo-vs
spec:
host: bookinfo.example.com
tls:
secret: bookinfo-secret
upstreams:
- name: backend
service: productpage
port: 9080
routes:
- path: /
action:
pass: backend
以下是 bookinfo-secret.yaml,在本示例中使用自签名密钥和证书。
apiVersion: v1
kind: Secret
metadata:
name: bookinfo-secret
type: kubernetes.io/tls
data:
tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0...
tls.key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVk...
部署示例应用:
1.加载密钥和证书,并激活用于 bookinfo 的 VirtualServer 资源:
$ kubectl apply -f ./Traffic-Management/bookinfo-secret.yaml secret/bookinfo-secret created
$ kubectl apply -f ./Traffic-Management/bookinfo-vs.yaml virtualserver.k8s.nginx.org/bookinfo-vs created
2.若要支持外部客户端通过 NGINX Ingress Controller 访问集群中的资源,您需要公布一个公共 IP 地址作为集群的入口点。
获取 LoadBalancer service 的公共 IP 地址(为方便阅读,输出结果分成了两行):
$ kubectl get svc nginx-ingress -n nginx-ingress
NAME TYPE CLUSTER-IP ...
nginx-ingress LoadBalancer 203.0.x.5 ...
... EXTERNAL-IP PORT(S) AGE
... a309c13t-2.elb.amazonaws.com 80:32148/TCP,443:32001/TCP 1h
对于 Azure 和 Google Cloud Platform,该 IP 地址为 EXTERNAL-IP 字段中报告的公共 IP 地址。但是,对于 AWS,网络负载均衡器 (NLB) 的公共 IP 地址并非一成不变,而是 EXTERNAL-IP 字段报告其 DNS 名称,如以上示例输出所示。若要查找公共 IP 地址,请运行 nslookup 命令(在本例中公共 IP 地址为 203.0.x.66):
$ nslookup a309c13t-2.elb.amazonaws.com
Server: 198.51.100.1
Address: 198.51 100.1#53
Non-authoritative answer:
Name: a309c13t-2.elb.amazonaws.com
Address: 203.0.x.66
3.编辑本地 /etc/hosts 文件,为 bookinfo.example.com 添加公共 IP 地址条目。例如:203.0.x.66 bookinfo.example.com
4.部署 Traffic-Management/bookinfo.yaml 中定义的 bookinfo 示例应用:
$ kubectl apply -f ./Traffic-Management/bookinfo.yaml
service/details created
serviceaccount/bookinfo-details created
deployment.apps/details-v1 created
service/ratings created
serviceaccount/bookinfo-ratings created
deployment.apps/ratings-v1 created
service/reviews created
serviceaccount/bookinfo-reviews created
deployment.apps/reviews-v1 created
service/productpage created
serviceaccount/bookinfo-productpage created
deployment.apps/productpage-v1 created
5.确认所有 pod 都注入了 sidecar,如 CONTAINERS 字段中的 nginx-mesh-sidecar 所示:
$ kubectl get pods -o custom-columns=NAME:.metadata.
name,CONTAINERS:.spec.containers[*].name
NAME CONTAINERS
details-v1-847c7999fb-9vvbv details,nginx-mesh-sidecar
productpage-v1-764fd8c446-kxskn productpage,nginx-mesh-sidecar
ratings-v1-7c46bc6f4d-vpf74 ratings,nginx-mesh-sidecar
reviews-v1-988d86446-zvwvc reviews-v1,nginx-mesh-sidecar
6.在集群中部署一个 bash 容器(在 Traffic-Management/bash.yaml 中进行定义):
$ kubectl apply -f ./Traffic-Management/bash.yaml deployment.apps/bash created
7.若要验证 bookinfo 应用是否正在运行,请从 bash 容器中提取其主页面:
$ kubectl exec deploy/bash -it -c bash -- bash
$ curl -I -k http://productpage:9080/productpage\?u=normal
HTTP/1.1 200 OK
Server: nginx/1.21.5
Date: Day, DD Mon HH:MM:SS YYYY TZ
Content-Type: text/html; charset=utf-8
Content-Length: 4183
Connection: keep-alive
X-Mesh-Request-ID: d8d3418d7b701363e6830155b520bf3e
8.若要验证外部客户端能否通过连接 NGINX Ingress Controller 访问应用,请在浏览器中导航至 https://bookinfo.example.com/。
在本指南中,我们将在 mTLS strict 模式下部署 NGINX Service Mesh。如果使用 off 或 permissive 模式,验证客户端能否访问应用的另一种方法是运行以下命令,从而将产品页面端口转发到您的本地环境,然后在浏览器中打开http://localhost:9080/。
$ kubectl port-forward svc/productpage 9080
Forwarding from 127.0.0.1:9080 -> 9080
Forwarding from [::1]:9080 -> 9080
流量控制是指从来源、流量和目的地等方面对应用流量进行控制的行为。当在生产环境中运行 Kubernetes 时必须执行此操作,以保护您的基础设施和应用免遭攻击和流量激增。简单地说,对流入应用或服务的流量施加控制总是有益的。
流量控制包括两种技术:
如果应用收到的 HTTP 请求数量超过其处理能力,则很可能会崩溃。无论这些请求是显而易见的有害请求(比如暴力破解密码)还是看上去是好消息但实际会造成麻烦的情况(比如大批顾客积极参与您的“双十一”促销活动),这都完全不重要——因为无论哪种情况,您的服务都面临危险。速率限制通过限制应用在规定时间内从每个客户端接受的请求数来防止过载。
在非 Kubernetes 环境和许多 Kubernetes 环境中,来自外部客户端的过多请求是最严峻的威胁。在这种情况下,使用 NGINX Ingress Controller 实施速率限制很有必要,因为它可以根据许多标准对客户端进行分类,并根据这些标准施加速率限制。(如果需要限制来自 Kubernetes 集群中其他 service 的请求,请使用 NGINX Service Mesh 应用速率限制,如“使用 NGINX Service Mesh 启用服务间速率限制”一节中所述)。
对所有客户端进行速率限制的一种简单方法是创建 NGINX Ingress Controller Policy 资源,并将其应用于 VirtualServer 和 VirtualServerRoute 资源。
VirtualServer 或 VirtualServerRoute 可引用多个速率限制策略。当有多个策略时,NGINX Ingress Controller 会对 NGINX 进行配置以使用全部策略,其中最严格的策略设置实际执行速率。第一个引用策略中 dryRun、logLevel 和 rejectCode 参数的值会被其他策略继承。
1.创建速率限制策略。此示例策略 (Traffic-Management/nic-rate-limit- policy.yaml) 每秒只从每个 IP 地址接受 1 个请求;该秒内到达的其他请求都会被拒绝,并显示 503(服务不可用)响应代码:
apiVersion: k8s.nginx.org/v1
kind: Policy
metadata:
name: nic-rate-limit-policy
spec:
rateLimit:
rate: 1r/s
zoneSize: 10M
key: ${binary_remote_addr}
logLevel: warn
rejectCode: 503
dryRun: false
2.若要使 NGINX Ingress Controller 应用策略,请在 Traffic-Management/bookinfo-vs-rate-limit.yaml 中添加对该策略的引用:
17 policies:
18 - name: nic-rate-limit-policy
3.将变更应用到您在部署示例应用中使用的 bookinfo 应用:
$ kubectl apply -f ./Traffic-Management/nic-rate-limit-policy.yaml policy.k8s.nginx.org/nic-rate-limit-policy created
$ kubectl apply -f ./Traffic-Management/bookinfo-vs-rate-limit.yaml virtualserver.k8s.nginx.org/bookinfo-vs configured
4.若要验证策略是否生效,请使用 Traffic-Management/generate-traffic.sh 脚本,该脚本会生成流量并将其定向到 Ingress controller:
#!/bin/bash
# 获取 NGINX Ingress Controller 的 IP 地址
IC_IP=$(kubectl get svc -n nginx-ingress -o jsonpath="{.items[0]. status.loadBalancer.ingress[0].ip}")
[ -z "$IC_IP" ] && IC_IP=$(kubectl get svc -n nginx-ingress -o jsonpath="{.items[0].status.loadBalancer.ingress[0].hostname}")
# 向 bookinfo 发送 300 个请求
for i in $(seq 1 300);
do curl -I -k https://$IC_IP:443/productpage\?u=normal -H "host: bookinfo.example.com";
done
5.在本地计算机上运行脚本。超过速率限制的请求将被拒绝,返回错误代码 503(服务不可用),如本例中的第二个请求所示:
$ bash generate-traffic.sh
HTTP/1.1 200 OK
Server: nginx/1.21.5
Date: Day, DD Mon HH:MM:SS YYYY TZ
Content-Type: text/html; charset=utf-8
Content-Length: 4183
Connection: keep-alive
X-Mesh-Request-ID: c9df5e030d3c6871745527ea93e403b8
HTTP/1.1 503 Service Unavailable
Server: nginx/1.21.5
Date: Day, DD Mon HH:MM:SS YYYY TZ
Content-Type: text/html
Content-Length: 197
Connection: keep-alive
为了确保令人满意的用户体验,您通常需要更灵活地实施速率限制策略,以适应会有突发状况的应用。此类应用往往会快速连续地发送多个请求,然后闲置一段时间。如果将速率限制策略设置为始终拒绝突发流量,那么许多合法的客户端请求都会被拒绝。
为了避免这种情况,您可以将超过限制的请求缓存到队列中,并及时进行处理,而不是立即拒绝。rateLimit 策略中的 burst 字段定义了客户端在速率上限之外可发出的请求数量,超过 burst 的请求会被立即拒绝。我们还可以控制队列请求的发送速度。noDelay 字段设置了 NGINX Ingress Controller 无延迟代理到应用的队列请求数(必须小于 burst);其余队列请求将被延迟处理,从而达到已定义的速率限制标准。
1.创建允许突发的策略。在上一节介绍的策略更新 (Traffic-Management/nic-rate-limit- policy-burst.yaml) 中,每秒 10 个请求的速率(第 7 行)、noDelay 参数(第 13 行)和突发值 10(第 14 行)的组合意味着对于每个客户端,NGINX Ingress Controller 每秒即时代理多达 20 个请求。
apiVersion: k8s.nginx.org/v1
kind: Policy
metadata:
name: nic-rate-limit-policy
spec:
rateLimit:
rate: 10r/s
zoneSize: 10M
key: ${binary_remote_addr}
logLevel: warn
rejectCode: 503
dryRun: false
noDelay: true
burst: 10
2.运行 Traffic-Management/generate- traffic.sh 脚本,从而验证新速率限制是否生效,如上一节中的第 4 步所示。
借助 NGINX Limit Requests 模块,使用 NGINX Ingress Controller 实施速率限制。有关速率限制工作原理的详细解释,请参阅 NGINX 博文。
Kubernetes 集群中 service 之间的流量并不一定在 NGINX Ingress Controller 的范围之内,因此 NGINX Service Mesh 是更合适的流量速率限制方式。

与 NGINX Ingress Controller 一样,您可以创建速率限制策略,让 NGINX Service Mesh 限制应用在规定时间内从每个 service 接受的请求数。
NGINX Service Mesh RateLimit 对象采用与 NGINX Ingress Controller rateLimit 策略不同的参数:
Traffic-Management/nsm-rate-limit.yaml 中的这一策略定义设置了每分钟 10 个请求或每 6 秒 1 个请求的限制(第 16 行)。
apiVersion: specs.smi.nginx.com/v1alpha2
kind: RateLimit
metadata:
name: nsm-rate-limit
namespace: default
spec:
destination:
kind: Service
name: productpage
namespace: default
sources:
- kind: Deployment
name: bash
namespace: default
name: 10rm
rate: 10r/m
burst: 0
delay: nodelay
1.应用策略:
$ kubectl apply -f ./Traffic-Management/nsm-rate-limit.yaml ratelimit.specs.smi.nginx.com/nsm-rate-limit created
2.若要测试策略,请初始化 bash 容器:
$ kubectl exec deploy/bash -it -c bash -- bas
3.在 bash 容器中快速连续运行以下 curl 命令几次,以验证是否施加了速率限制。如第二个请求所示,错误代码 503(服务不可用)表示请求被拒绝,因为它超出了限制。
$ curl -I -k http://productpage:9080/productpage\?u=normal
HTTP/1.1 200 OK
Server: nginx/1.21.5
Date: Day, DD Mon HH:MM:SS YYYY TZ
Content-Type: text/html; charset=utf-8
Content-Length: 5183
Connection: keep-alive
X-Mesh-Request-ID: 27c4030698264b7136f2218002d9933f
$ curl -I -k http://productpage:9080/productpage\?u=normal
HTTP/1.1 503 Service Unavailable
Server: nginx/1.21.5
Date: Day, DD Mon HH:MM:SS YYYY TZ
Content-Type: text/html
Content-Length: 198
Connection: keep-alive
当服务不可用或出现高延迟时,传入请求的超时时间以及客户端收到错误响应的时间可能很长。这种长超时可能会造成级联故障,即一个服务中断导致其他服务超时,最终引发整个应用故障。
断路器模式可通过以下方式防止级联故障:
如需使用 NGINX Service Mesh 启用断路器,您需要设置规定时间内发生的错误次数限制。当失败次数超过限制时,断路器会在请求到达时立即向客户端返回错误响应。您还可以定义自定义信息页面,以便在服务无法正常运行或正在维护时返回该页面,详情请参阅“返回自定义页面”一节。
断路器将持续拦截和拒绝请求,等过了指定的时长后再放行有限数量的请求以作测试。如果这些请求成功,断路器将停止限流。如果不成功,则开始重新计时,期间断路器会继续拒绝请求。
使用断路器不仅可以消除对故障组件的调用,避免造成超时或延迟,从而提高应用性能,而且通常还能够减轻非必要组件故障的影响。
Traffic-Management/broken-deployment.yaml 中的以下几行模拟了 service 故障,其中 reviews 服务的版本 2 的发布后面紧跟一个命令(第 44-45 行),该命令导致相关 pod 崩溃并开始返回错误代码 502(Bad Gateway)。
18 apps/v1
19 kind: Deployment
25 spec:
31 template:
32 metadata:
33 labels:
34 app: reviews-v2
34 version: v2
36 spec:
37 serviceAccountName: bookinfo-reviews
38 containers:
39 - name: reviews-v2
40 image: docker.io/istio/examples-bookinfo-reviews-v2:1.15.0
41 imagePullPolicy: IfNotPresent
42 ports:
43 - containerPort: 9080
44 command: ["/bin/sh","-c"]
45 args: ["timeout --signal=SIGINT 5 /opt/ibm/wlp/bin/server run defaultServer"]
1.应用故障模拟。在 kubectl get pods 输出的 STATUS 列中,reviews-v2 pod 的 CrashLoopBackOff 值表明它在反复启动和崩溃。当向 reviews 服务发送 curl 请求时,您会收到 502(Bad Gateway)错误响应:
$ kubectl apply -f ./Traffic-Management/broken-deployment.yaml
service/reviews configured
deployment.apps/reviews-v2 created
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
bash-5bbdcb458d-tbzrb 2/2 Running 0 42h
details-v1-847c7999fb-47fsw 2/2 Running 0 9d
maintenance-v1-588566b84f-57wrr 2/2 Running 0 3h53m
productpage-v1-764fd8c446-px5p9 2/2 Running 0 4m47s
ratings-v1-7c46bc6f4d-qjqff 2/2 Running 0 9d
reviews-v1-76ddd45467-vvw56 2/2 Running 0 9d
reviews-v2-7fb86bc686-5jkhq 1/2 CrashLoopBackOff 9 2m
$ kubectl exec deploy/bash -it -c bash -- bash
$ curl -I -k http://reviews:9080/health HTTP/1.1 502 Bad Gateway
Server: nginx/1.21.5
Date: Day, DD Mon HH:MM:SS YYYY TZ
Content-Type: text/html Content-Length: 158 Connection:
keep-alive
2.配置 NGINX Service Mesh CircuitBreaker 对象,以将流量从 reviews-v2 pod 路由到 reviews-v1,从而防止客户端收到 502 错误响应。当 30 秒内出现 3 次以上错误时,该配置(在 Traffic-Management/nsm-circuit-breaker.yaml 中进行定义)便会触发熔断。
apiVersion: specs.smi.nginx.com/v1alpha1
kind: CircuitBreaker
metadata:
name: nsm-circuit-breaker
namespace: default
spec:
destination:
kind: Service
name: reviews
namespace: default
errors: 3
timeoutSeconds: 30
fallback:
service: default/reviews-v1
port: 9080
3.应用断路器。尽管 kubectl get pods 的输出显示 reviews-v2 pod 仍不可用,但向 reviews 服务发出的 curl 请求却成功了,状态代码为 200 (OK):
$ kubectl apply -f ./Traffic-Management/nsm-circuit-breaker.yaml
service/reviews-backup created
circuitbreaker.specs.smi.nginx.com/nsm-circuit-breaker created
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
bash-5bbdcb458d-tbzrb details- 2/2 Running 0 42h 9d
v1-847c7999fb-47fsw 2/2 Running 0 3h53m
maintenance-v1-588566b84f-57wrr 2/2 Running 0 4m47s
productpage-v1-764fd8c446-px5p9 2/2 Running 0 9d
ratings-v1-7c46bc6f4d-qjqff 2/2 Running 0 9d
reviews-v1-76ddd45467-vvw56 2/2 Running 0 26m
reviews-v2-7fb86bc686-5jkhq 1/2 CrashLoopBackOff 9
$ kubectl exec deploy/bash -it -c bash -- bash
$ curl -I -k http://reviews:9080/health
HTTP/1.1 200 OK
Server: nginx/1.21.5
Date: Day, DD Mon HH:MM:SS YYYY TZ
Content-Type: text/html; charset=utf-8
Content-Length: 4063
Connection: keep-alive
X-Mesh-Request-ID: 574e93b8b1736f7dcfd866bca547d370
注:NGINX Service Mesh 断路器依靠被动健康检查来监控服务端点的状态。在上面所示配置下,如果从 bash 容器发出的超过 3 个请求在 30 秒内触发一个错误响应,它就会将故障部署标记为状态异常。
在使用基于 NGINX Plus 的 NGINX Ingress Controller 实施断路器模式时,可采用主动健康检查。(更多信息,请参阅 NGINX 博文)。
带有备份 service 的断路器可减少客户端收到的错误消息数量,从而改善用户体验,但它并不能完全消除此类消息。当出现故障时,我们可以通过返回比错误代码更有用的响应,进一步提升用户体验。
举例来说,一个具有 Web 或移动界面的应用,除了提供客户端请求的具体信息以外,还提供辅助项目列表,比如文章评论、推荐、广告等。如果生成该列表的 Kubernetes service 出现故障,默认情况下会返回错误代码 502(Bad Gateway)。
您可为断路器创建一个更合适的响应,比如重定向到解释故障的 URL。
Traffic-Management/bookinfovs-circuit-breaker.yaml 中的以下几行修改了面向 bookinfo 应用的 VirtualServer 配置,不再返回错误代码 502,而是重定向到“抱歉,正在维护中”页面:
17 errorPages:
18 - codes: [502]
19 redirect:
20 code: 301
21 url: https://cdn.f5.com/maintenance/f5.com/SorryPage.html
1.应用执行重定向的新配置:
$ kubectl apply -f ./Traffic-Management/bookinfo-vs-circuit- breaker.yaml virtualserver.k8s.nginx.org/bookinfo-vs configured
2.关闭 productpage-v1 service 会导致故障。现在,向 bookinfo 应用发送的 curl 请求将导致重定向(代码 301 Moved Permanently)到维护页面,而非返回 502 错误:
$ kubectl scale --replicas=0 deployment/productpage-v1
deployment.apps/productpage-v1 scaled
$ curl -k -I https://bookinfo.example.com
HTTP/1.1 301 Moved Permanently
Server: nginx/1.21.5
Date: Day, DD Mon HH:MM:SS YYYY TZ
Content-Type: text/html
Content-Length: 169
Connection: keep-alive
Location: https://cdn.f5.com/maintenance/f5.com/SorryPage.html
本指南对任何设置、运行和使用 Kubernetes 环境的团队来说都大有裨益,无论您是 Kubernetes 资深用户还是新手,希望这本电子书能助您顺利开启 Kubernetes 之旅。
扫描下方二维码,前往 NGINX 中文官网免费下载电子版全本。

NGINX 唯一中文官方社区 ,尽在 nginx.org.cn

按点赞数排序
按时间排序
微信公众号
加入微信群