nginx worker process Nginx Worker Processss_配置文件

Docker 技术鼻祖系列


1.  问题描述

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

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

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

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

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

第一个方法可行,但是需要修改配置文件,nginx 需要 reload。实际部署时,必须将 nginx.conf 作为配置文件挂载,对一些不太熟悉 nginx 的用来说,心智负担会比较重。

第二个方法,在 Kubernetes 上会遇到一些问题。通过在容器中观察可以发现,nginx 启动的 worker process,并没有遵循我们给 Pod 设置的 limit,而是与 Pod 所在 node 的 cpu 核数保持一致。

这在宿主机的 cpu 核数比较多,而 Pod 的 cpu 配置较小时,会因为每个 worker 分配的时间片比较少,带来明显的响应慢的问题。

2. 问题原因

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

resources:
  limits:
    cpu: 500m
    memory: 256Mi
  requests:
    cpu: 500m
    memory: 256Mi

我们到这个 Pod 所在宿主机上去查看相关信息。

$ docker inspect 17f5f35c3500|grep -i cgroup
            "Cgroup": "",
            "CgroupParent": "/kubepods/burstable/podb008ccda-9396-11ea-bc20-ecf4bbd63ee8",
            "DeviceCgroupRules": null,
$ cd /sys/fs/cgroup/cpu/kubepods/burstable/podb008ccda-9396-11ea-bc20-ecf4bbd63ee8
$ cat cpu.cfs_quota_us
$ cat cpu.cfs_period_us

可以看到,实际是通过 cpu.cfs_quota_us/cpu.cfs_period_us 来限制该 Pod 能使用的 cpu 核数的。

但是 nginx 的 worker_processes,是通过 sysconf(_SC_NPROCESSORS_ONLN) 来查询宿主机上的 cpu 个数的(getconf _NPROCESSORS_ONLN),我们通过 strace 来观察下这个过程。

$ 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 个数的。

默认 Kubernetes 上,/sys/devices/system/cpu/online文件实际就是宿主机的,因此,nginx 启动的 worker process 个数,与宿主机 cpu 个数一致,也就不奇怪了。

3. 解决方案

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

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

lxcfs

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

/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就可以了。

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 实际处理的函数如下。

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。

4. 结论

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

nginx worker process Nginx Worker Processss_nginx_02

nginx worker process Nginx Worker Processss_nginx_03

你可能还喜欢

点击下方图片即可阅读

nginx worker process Nginx Worker Processss_Pod_04


最华丽的 Kubernetes 桌面客户端:Lens