一、问题背景:好好的服务突然打不开

1.1 发现异常的过程

上周三下午,团队测试人员突然在群里反馈,之前一直正常的商品查询服务突然打不开,访问时一直报503错误。我当时在处理其他bug,顺手查了下监控,发现服务的Pod(Kubernetes里最小的部署单元,可理解为装服务代码的小盒子)数量是正常的,但进来的请求大多被拒绝,返回“service unavailable”提示。

1.2 初步判断不是服务代码问题

我先看了Pod状态,全是Running,没有崩溃或OOM报错,也没收到服务内部逻辑的异常日志,排除代码本身bug。之前服务一直正常,上周仅调整过配置,大概率是配置改坏了。

二、先搞懂Knative Serving的流量限流到底是啥

2.1 为什么开发中需要给服务限流

只要是对外服务,都要考虑限流。比如公共API怕被恶意刷请求,突发流量会打满服务资源,连累依赖它的其他服务。限流就像装在服务前的“安全阀门”,超过承载能力的流量会被拦住,避免服务过载崩溃。

2.2 Knative里限流的实现逻辑

Knative Serving基于Kubernetes,给每个Pod加了Queue Proxy代理处理请求,限流规则靠配置里的核心参数控制:containerConcurrency,代表每个Pod能同时处理的最多请求数,默认是0(不限制),要控制流量就得手动设这个值。

三、问题复现:配置错了导致服务拒绝访问

3.1 错误的配置示例

我翻上周改的配置,发现手滑把containerConcurrency设成了1(本该是10),整理后的错误配置(技术栈:Kubernetes + Knative Serving)如下:

apiVersion: serving.knative.dev/v1
kind: Service
metadata:
  name: product-query
  namespace: test
spec:
  template:
    spec:
      containers:
        - image: registry.cn-hangzhou.aliyuncs.com/demo/product-query:v1
          resources:
            requests:
              cpu: "250m"
              memory: "256Mi"
      # 错误:把单Pod最大并发请求数设为1,导致流量稍大就拒绝
      containerConcurrency: 1

3.2 复现问题的步骤

验证这个配置的问题,用curl发起两个并发请求:

# 并发发起2个请求,间隔0.1秒,模拟短时间内的流量冲击
for i in {1..2}; do curl -w "HTTP %{http_code}\n" http://product-query.test.example.com & sleep 0.1; done

结果显示第一个请求返回200,第二个返回503,确认是限流设得太小导致请求被拒绝。

四、排查问题的过程

4.1 先看Pod的基础状态

用kubectl命令确认Pod本身没问题:

# 查看指定namespace下的商品查询服务的Pod
kubectl -n test get pod -l serving.knative.dev/service=product-query

输出显示Pod全是Running,Ready状态为True,说明Pod本身正常,问题出在流量转发环节。

4.2 查配置文件的错误参数

查看服务的完整配置,找到出错的参数:

# 查看ksvc的详细配置,确认限流参数
kubectl -n test get ksvc product-query -o yaml

果然containerConcurrency的值是1,不是需要的10,这就是根因。

4.3 验证限流拒绝指标

Knative的Queue Proxy会暴露请求处理的监控指标,查看是否有请求被拒绝:

# 查看Queue Proxy的拒绝请求指标,替换成Pod实际名称
kubectl -n test exec <product-query-pod-name> -c queue-proxy -- curl -s http://localhost:9090/metrics | grep rejected_requests

输出里有queue_proxy_rejected_requests_total{...} 1,说明确实有请求被拒绝,和复现结果一致。

五、正确配置的方法

5.1 确定合理的限流参数值

参数不能随便写,要根据服务压测结果和Pod资源情况定。这个服务每个Pod有250m CPU、256M内存,压测后单Pod稳定处理10个并发请求没问题,所以containerConcurrency设为10合适。

5.2 完整的正确配置示例

修正后的配置,还配合了自动扩缩容,进一步保障服务稳定性:

apiVersion: serving.knative.dev/v1
kind: Service
metadata:
  name: product-query
  namespace: test
spec:
  template:
    spec:
      containers:
        - image: registry.cn-hangzhou.aliyuncs.com/demo/product-query:v1
          resources:
            requests:
              cpu: "250m"
              memory: "256Mi"
      # 正确:按压测结果设单Pod最大并发请求数为10
      containerConcurrency: 10
      # 配合自动扩缩容,流量上来时自动增加Pod
      scale:
        min: 1
        max: 5

5.3 验证修复结果

应用配置后,再次发起并发请求:

# 再次发起2个并发请求
for i in {1..2}; do curl -w "HTTP %{http_code}\n" http://product-query.test.example.com & sleep 0.1; done

这次两个请求都返回200,没有503,服务恢复正常。

六、技术优缺点、注意事项、应用场景

6.1 应用场景

需要设置限流的情况:

  1. 服务是公开API,怕被恶意刷流量耗尽资源;
  2. 服务依赖第三方服务,第三方的并发请求有上限,需要限制本服务的并发;
  3. 预计有突发流量,需要防止Pod被打满保持响应速度。

6.2 技术优缺点

优点:快速限制单Pod流量,防止单个Pod过载;配合Knative自动扩缩容,流量上来时自动加Pod,避免请求堆积。缺点:设太小会误杀正常流量,设太大起不到限流作用,需要结合压测调参。

6.3 注意事项

要避开这些坑:

  1. 先压测再设参数:每个服务性能不同,不能随便写数字,要压测出单Pod的最大处理能力;
  2. 配合自动扩缩容:只设限流不扩缩容,流量上来时Pod不够还是会崩,要同时设置scale参数;
  3. 避免和Ingress限流冲突:如果Knative前有K8s Ingress,两者的限流参数要协调,不然要么太严要么太松。

七、文章总结

这次问题的核心是配置Knative服务时,把流量限流的containerConcurrency参数设得太小,导致并发请求稍多就被拒绝。排查过程很清晰:先确认Pod状态排除服务本身,再查配置找到错误参数,最后调整到合理值并配合自动扩缩容,就解决了问题。对基础开发者来说,只要记住“限流参数要压测后设,配合扩缩容使用”,就能避免类似的配置错误,保障服务稳定。