k8s中nginx worker process自动设置

问题描述

nginx容器化之后,有一个普遍遇到的问题: 如何设置nginx worker process的数量?

nginx官方容器镜像的nginx.conf配置文件中,会有一条worker process配置:

nginx 复制代码
# /etc/nginx/nginx.conf
worker_processes 1;

它会配置nginx仅启动一个woker。这在nginx容器为1核时,可以良好的工作。

当我们希望nginx给更高的配置,例如4c或者16c,我们需要确保nginx也能启动响应个数的work process。有两个办法:

  1. 修改 nginx.conf,将worker_processes 的个数调整为cpu核数
  2. 修改nginx.conf,将woeker_processes的个数修改为auto

第一个方法,在k8s上需要对nginx进行reload。实际部署的时候,必须将nginx.conf作为配置文件挂载,对一些不熟悉Nginx的用户来说,心智负担比较重。

第二个方法,在k8s也会遇到一些问题,通过容器观察可以发现,nginx启动的worker process并没有遵循我们给Pod设置的Limit,而是与Pod所在node的cpu核数保持一致,这在宿主机cpu核数比较多,但是Pod的cpu配置较小的情况下,会 因为每个worker分配的时间片比较小,对性能带来明显的影响

nginx 复制代码
/ # ps -aux
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root           1  0.0  0.0   9528  5504 ?        Ss   09:11   0:00 nginx: master process nginx -g daemon off;
nginx         30  0.0  0.0   9992  2556 ?        S    09:11   0:00 nginx: worker process
nginx         31  0.0  0.0   9992  2556 ?        S    09:11   0:00 nginx: worker process
nginx         32  0.0  0.0   9992  2684 ?        S    09:11   0:00 nginx: worker process
nginx         33  0.0  0.0   9992  2556 ?        S    09:11   0:00 nginx: worker process
nginx         34  0.0  0.0   9992  2556 ?        S    09:11   0:00 nginx: worker process
nginx         35  0.0  0.0   9992  2684 ?        S    09:11   0:00 nginx: worker process
nginx         36  0.0  0.0   9992  2556 ?        S    09:11   0:00 nginx: worker process
nginx         37  0.0  0.0   9992  2556 ?        S    09:11   0:00 nginx: worker process
root          38  0.0  0.0   1708  1024 pts/0    Ss   09:40   0:00 sh
root          45  0.0  0.0   2524  1664 pts/0    R+   09:40   0:00 ps -aux
/ # cd  /etc/nginx/
/etc/nginx # ls
conf.d          fastcgi.conf    fastcgi_params  mime.types      modules         nginx.conf      scgi_params     uwsgi_params
/etc/nginx # cat nginx.conf 
user  nginx;
worker_processes  auto;

error_log  /var/log/nginx/error.log notice;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    keepalive_timeout  65;

    server {
        listen       80;
        server_name  _;

        location / {
            root   /usr/share/nginx/html;
            index  index.html index.htm;
        }

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   /usr/share/nginx/html;
        }
    }
}
/etc/nginx # 

问题原因

我们知道,在k8s为容器配置cpu的limits为2时,容器其实并不是真正的分配了两个cpu, 而是通过cgroup进行了限制。

yaml 复制代码
resources:
  limits:
	cpu: 200m
	memory: 256Mi
  requests:
	cpu: 100m
	memory: 128Mi

查看cgroup资源限制

  1. 获取pod名字
bash 复制代码
[root@k8smaster-ims ~]# kubectl  get pod 
NAME                    READY   STATUS    RESTARTS   AGE
nginx-c98766567-26mjm   1/1     Running   0          3h21m
  1. 根据pod名字获取容器ID
bash 复制代码
[root@k8smaster-ims ~]# crictl ps |grep nginx-c98766567-26mjm
18962c2ba08b4       bc1521b43e9bc       3 hours ago         Running             nginx                       0                   ee2f2d827d4b7       nginx-c98766567-26mjm
[root@k8smaster-ims ~]# 
  1. 根据容器ID获取cgroupsPath
bash 复制代码
crictl inspect 18962c2ba08b4     |  grep -A5 cgroupsPath
        "cgroupsPath": "kubepods-burstable-podee600507_4f0f_4016_b522_ae24df7be346.slice:cri-containerd:18962c2ba08b4f42701896e30a8d03d19e9e27d23adb9e49cb4a294937e36d82",
        "namespaces": [
          {
            "type": "pid"
          },
          {
  1. 进入到公共目录 /sys/fs/cgroup/kubepods.slice/kubepods-burstable.slice/,然后按照cgroupsPath进入到具体容器的目录
bash 复制代码
cd /sys/fs/cgroup/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-podee600507_4f0f_4016_b522_ae24df7be346.slice/cri-containerd-18962c2ba08b4f42701896e30a8d03d19e9e27d23adb9e49cb4a294937e36d82.scope
  1. 查看资源显示
bash 复制代码
# CPU 限制
cat cpu.max
# 输出格式:max_usec period_usec
# 例:20000 100000 → 20000us = 0.02s = 0.2 CPU = 200m ✅

# CPU 权重(requests)
cat cpu.weight
# 范围 1-10000,默认 100
# k8s 映射:requests.cpu=100m → weight ≈ 2

# 内存限制
cat memory.max
# 例:268435456 = 256Mi ✅

# 内存当前使用
cat memory.current

# 内存 OOM 次数
cat memory.events
# 看 oom 和 oom_kill 计数

# CPU throttle 统计(看有没有被限制)
cat cpu.stat
# nr_throttled / throttled_usec

问题分析

通过cgroup信息可以看到,cgroup实际允许nginx服务只能有 0.2各个核,和 256M的内存使用量。

但是nginx的worker_processes,是通过sysconf(_SC_NPROCESSORS_ONLN) 来查询宿主机上的cpu个数的(getconf _NPROCESSORS_ONLN)

bash 复制代码
# strace getconf _NPROCESSORS_ONLN
execve("/bin/getconf", ["getconf", "_NPROCESSORS_ONLN"], [/* 23 vars */]) = 0
brk(0)                                  = 0x606000
...
open("/sys/devices/system/cpu/online", O_RDONLY|O_CLOEXEC) = 3
read(3, "0-31\n", 8192)                 = 5
close(3)                                = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 5), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f6a922a0000
write(1, "32\n", 332

可见,getconf _NPROCESSORS_ONLN 实际上是通过读取文件 /sys/devices/system/cpu/online 来获取cpu的个数的。

默认kuberneters上的/sys/devices/system/cpu/online就是宿主机的,因此,nginx启动的 worker processes个数与宿主机cpu个数一致,也就呵护情理了。

解决方案

解决方案实际也不难想到,修改容器中的/sys/devices/system/cpu/online就行了。

社区的lxcfs已经解决了这个问题。

lxcfs

LXCFS是一个小型的FUSE文件系统,其目的是让Linux容器感觉更像一个虚拟机。LXCFS会关注的procfs中的关键文件:

bash 复制代码
/proc/cpuinfo
/proc/diskstats
/proc/meminfo
/proc/stat
/proc/swaps
/proc/uptime
/sys/devices/system/cpu/online

可以看到,我们需要的/sys/devices/system/cpu/online文件,也在lxcfs关注列表中。

lxcfs的使用方法也比较简单,只要将宿主机的/var/lib/lxc/lxcfs/proc/online挂载到容器的/sys/devices/system/cpu/online就可以了。

bash 复制代码
  containers:
  - args:
	- infinity
	command:
	- sleep
	volumeMounts:
	- mountPath: /sys/devices/system/cpu/online
	  name: lxcfs-2
	  readOnly: true
  volumes:
  - hostPath:
	  path: /var/lib/lxc/lxcfs/proc/online
	  type: ""
	name: lxcfs-2

当我们在容器中读取/sys/devices/system/cpu/online文件时,由于kubelet将该文件绑定了/var/lib/lxc/lxcfs/proc/online,该read请求会交给lxcfs daemon来处理。

lxcfs实际处理的函数如下。

c 复制代码
int max_cpu_count(const char *cg)
{
	__do_free char *cpuset = NULL;
	int rv, nprocs;
	int64_t cfs_quota, cfs_period;
	int nr_cpus_in_cpuset = 0;

	read_cpu_cfs_param(cg, "quota", &cfs_quota);
	read_cpu_cfs_param(cg, "period", &cfs_period);

	cpuset = get_cpuset(cg);
	if (cpuset)
		nr_cpus_in_cpuset = cpu_number_in_cpuset(cpuset);

	if (cfs_quota <= 0 || cfs_period <= 0){
		if (nr_cpus_in_cpuset > 0)
			return nr_cpus_in_cpuset;

		return 0;
	}

	rv = cfs_quota / cfs_period;

	/* In case quota/period does not yield a whole number, add one CPU for
	 * the remainder.
	 */
	if ((cfs_quota % cfs_period) > 0)
		rv += 1;

	nprocs = get_nprocs();
	if (rv > nprocs)
		rv = nprocs;

	/* use min value in cpu quota and cpuset */
	if (nr_cpus_in_cpuset > 0 && nr_cpus_in_cpuset < rv)
		rv = nr_cpus_in_cpuset;

	return rv;
}

根据前面的信息,可以看到最终返回的值为 200000/100000 = 2。

结论

因此,当有lxcfs的加持时,nginx可以放心的将worker_processes配置为auto,不需要担心启动了过多的worker processes。

相关推荐
沙蒿同学1 小时前
我用 Go 搭了一条 AI Agent 流水线:从 1 张商品图到一整套淘宝详情页
前端·javascript·后端
Terra.K1 小时前
后端+AIAGENT项目开发指南
后端·agent·个人开发
拖孩2 小时前
代码我能全交给 AI,流量主这 500 个访客它一个都替不了我
前端·后端·微信小程序
烈风逍遥2 小时前
第七篇:提示词模板管理与 Agent 提示词编排
前端·人工智能·后端
站大爷IP2 小时前
Python的Django ORM把我坑惨了,原来select_related和prefetch_related的区别这么大
后端
用户EasyAdminBlazor3 小时前
AdminTable 源码解析:EasyAdminBlazor 如何实现通用 CRUD?
后端
知守观3 小时前
Spring AOP + 自定义注解实现三角色权限控制:从设计到落地的完整方案
后端
Mikko73 小时前
jackson-databind 升到 2.21.6 就安全了吗?jackson-core 是另一个坐标,它那条 high 全局库至今没收
java·后端·安全·json
Ticnix3 小时前
我调了三个月 overlap=50,它其实一次都没生效
后端·python·agent