Lework Study hard, improve every day.

学习周报「2023」8月

2023-09-01
lework
本文 3366 字,阅读全文约需 10 分钟

以每周一个节点,记录知识点。

2023-09-18~22

apisix 代理 gin 应用时,经常出现个别的请求502

现象:

apisix error 日志

message: upstream prematurely closed connection while reading response header from upstream,

上游服务
netstat -s |grep reset
    293168 connection resets received
    8770166 resets sent
    7158420 resets received for embryonic SYN_RECV sockets
    3489523 connections reset due to unexpected data
    27249 connections reset due to early user close

原因:

apisix 的 keepalive 配置

  "keepalive_pool": {
    "idle_timeout": 60,
    "requests": 1000,
    "size": 320
  }

上游服务 gin 的配置

const ReadTimeout = 1   // 读取超时配置. 单位: 秒
const WriteTimeout = 20 // 写入超时配置. 单位: 秒

s := &http.Server{
	Addr:           ":8080",
	Handler:        myHandler,
	ReadTimeout:    ReadTimeout,
	WriteTimeout:   WriteTimeout,
}

因未配置 IdleConnTimeout , 其值取的是 ReadTimeout, IdleTimeout 是启用keep-alive时,等待下一个请求的最大时间。超过最大时间是,关闭这个链接。这时nginx还在保持这个TCP链接,因此出现了 502,并抛出 upstream prematurely closed connection while reading response header from upstream, 错误。

解决方案:

将上游服务 gin 的 IdleConnTimeout 时间设置大于 apisix 的 idle_timeout

go http 常用值说明

https://pkg.go.dev/net/http#Transport

ReadTimeout:
ReadTimeout is the maximum duration for reading the entire request, including the body.
Because ReadTimeout does not let Handlers make per-request decisions on each request body’s acceptable deadline or upload rate, most users will prefer to use ReadHeaderTimeout. It is valid to use them both.

ReadTimeout翻译:
ReadTimeout是读取整个请求(包括请求体)的最大持续时间。
因为ReadTimeout不允许处理程序对每个请求主体的可接受的截止日期或上传速率做出每个请求的决定,大多数用户更喜欢使用ReadHeaderTimeout。两者都可以使用。
 

ReadHeaderTimeout:
ReadHeaderTimeout is the amount of time allowed to read request headers. The connection’s read deadline is reset after reading the headers and the Handler can decide what is considered too slow for the body. If ReadHeaderTimeout is zero, the value of ReadTimeout is used. If both are zero, there is no timeout.

ReadHeaderTimeout翻译:
ReadHeaderTimeout是允许读取请求头的时间。连接的读取截止日期在读取请求头后被重置,处理程序可以决定什么对主体来说太慢。
如果ReadHeaderTimeout为0,则使用ReadTimeout的值。如果两者都为零,则不存在超时。
这里就很精髓了,连接的读取截止日期再读取请求头后被重置。意思就是每次请求都重置截止时间。那反过来看就是 ReadTimeout就可能是导致keep alive 断开的罪魁祸首。

WriteTimeout:
WriteTimeout is the maximum duration before timing out writes of the response. It is reset whenever a new request’s header is read. Like ReadTimeout, it does not let Handlers make decisions on a per-request basis.

WriteTimeout翻译:
WriteTimeout是响应写入超时之前的最大持续时间。每当读取一个新的请求头时,它都会被重置。与ReadTimeout一样,它不允许处理程序根据每个请求做出决定。
每次读取一个新的请求头都会被重置,那么连接就不会因此断开。假设设置为3秒钟,那么意思是读取完请求头之后有3秒的时间可以处理,3秒内必须返回。

IdleTimeout:
IdleTimeout is the maximum amount of time to wait for the next request when keep-alives are enabled. If IdleTimeout is zero, the value of ReadTimeout is used. If both are zero, there is no timeout.

IdleTimeout翻译:
IdleTimeout是启用keep-alive时等待下一个请求的最大时间。
如果IdleTimeout为0,则使用ReadTimeout的值。如果两者都为零,则不存在超时。
很明确了,就是因为我们设置了ReadTimeout的值,导致IdleTimeout为0时使用了ReadTimeout的值,3秒…

MaxHeaderBytes:
MaxHeaderBytes controls the maximum number of bytes the server will read parsing the request header’s keys and values, including the request line. It does not limit the size of the request body. If zero, DefaultMaxHeaderBytes is used.

MaxHeaderBytes翻译:
MaxHeaderBytes控制服务器解析请求头的键和值(包括请求行)时将读取的最大字节数。它不限制请求体的大小。
如果为0,则使用DefaultMaxHeaderBytes。

2023-09-11~15

使用 Docker 和 Kubernetes 以非 root 用户身份绑定到低端口

现象:

​ 启动程序时出现 listen tcp :80: bind: permission denied

解决方案:

  1. 使用更高的端口,比如 8000
  2. 使用 sysctl 设置 ` net.ipv4.ip_unprivileged_port_start = 0`
  3. 增加特权 CAP_NET_BIND_SERVICE

常见应用:

  1. 在docker-compose 中使用 sysctls
services:
  web:
    build: "."
    sysctls:
    - "net.ipv4.ip_unprivileged_port_start=0"
  1. 在 Kubernetes 中使用
apiVersion: "apps/v1"
kind: "Deployment"
spec:
  template:
    spec:
      containers:
      - name: "demo-app"
        securityContext:
          capabilities:
            drop:
            - "ALL"
            add:
            - "NET_BIND_SERVICE"
原文地址 https://lework.github.io/2023/09/01/journal/

Similar Posts

Comments