Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

容器资源可见性问题与 GOMAXPROCS 配置 #216

Open
islishude opened this issue Dec 10, 2019 · 3 comments
Open

容器资源可见性问题与 GOMAXPROCS 配置 #216

islishude opened this issue Dec 10, 2019 · 3 comments

Comments

@islishude
Copy link
Owner

islishude commented Dec 10, 2019

Go 程序启动时候会根据 CPU 数量设置 GOMAXPROCS,比如下面的程序中在 8 核处理下输出为 8。

package main

import (
	"runtime"
)

func main() {
	println(runtime.NumCPU())
}

当我们使用 Docker 的时候也是如此,不过当我们使用 docker run 的时候同时限制了 CPU 数量,那么程序中会正常显示吗?

不会,比如 docker run --cpus=2 IMAGE,输出还是宿主机的数量。

这样就带来一个问题,如果宿主机是一个配置特别大的机器,同时限制了运行容器的 CPU ,那么就会造成 GOMAXPROCS 过大,程序运行中也会过多生成线程。

线程越多,但是 CPU 很少,这就造成线程调度负担,从而导致程序运行变慢。

所以需要去提前设置 GOMAXPROCS 为准确的值,uber 开源了 uber-go/automaxprocs 库,就很好的解决了这个问题,使用很简单。

package main

import (
       "runtime"

	_ "go.uber.org/automaxprocs"
)

func main() {
}

重新构造 Docker 镜像,再运行,可以看到这里适配了准确的 CPU 数量后自动打印了数据。

$ docker build -t test .
$ docker run --cpus=2 --rm test
2019/12/10 09:46:31 maxprocs: Updating GOMAXPROCS=2: determined from CPU quota

这样就很OK了,另外还有一个可选项 LXCFS。什么是 LXCFS ?docker 资源控制的原理的是使用了 cgroup,可以参考耗子叔的文章具体了解。

程序不能获取 cpu 限制,问题出在 docker 直接挂载 cgroup 信息到容器的 /sys/fs/cgroup 目录下,而 lxcfs 所做的就是让这些信息重新挂载到 /proc 目录下。

接下来介绍如何使用 lxcfs,首先在机器上安装 lxcfs

yum install -y fuse fuse-lib fuse-devel libtool
git clone --branch=lxcfs-4.0.6 --depth=1 https://github.com/lxc/lxcfs
cd lxcfs
./bootstrap.sh
./configure
make
make install

然后可以根据 which lxcfs 获取到已经安转到 /usr/local/bin/lxcfs,接着配置 systemd 守护进程

第一步,创建lxcfs mount 目录 mkdir -p /var/lib/lxcfs,接着配置 systemd

cat > /usr/lib/systemd/system/lxcfs.service <<EOF
[Unit]
Description=FUSE filesystem for LXC
ConditionVirtualization=!container
Before=lxc.service
Documentation=man:lxcfs(1)

[Service]
ExecStart=/usr/local/bin/lxcfs --enable-cfs /var/lib/lxcfs
KillMode=process
Restart=on-failure
ExecStopPost=-/usr/bin/fusermount -u /var/lib/lxcfs
Delegate=yes
ExecReload=/bin/kill -USR1 $MAINPID

[Install]
WantedBy=multi-user.target
EOF

然后就可以启动lxcfs进程,并设置开机启动

$ systemctl daemon-reload
$ systemctl start lxcfs
$ system enable lxcfs

最后验证查看启动状态

$ systemctl status lxcfs
● lxcfs.service - FUSE filesystem for LXC
   Loaded: loaded (/usr/lib/systemd/system/lxcfs.service; disabled; vendor preset: disabled)
   Active: active (running) since Sun 2020-11-15 11:02:58 CST; 7s ago
     Docs: man:lxcfs(1)
 Main PID: 25418 (lxcfs)
    Tasks: 3
   Memory: 336.0K
   CGroup: /system.slice/lxcfs.service
           └─25418 /usr/local/bin/lxcfs /var/lib/lxcfs

Nov 15 11:02:58 vm82100 lxcfs[25418]: - proc_diskstats
Nov 15 11:02:58 vm82100 lxcfs[25418]: - proc_loadavg
Nov 15 11:02:58 vm82100 lxcfs[25418]: - proc_meminfo
Nov 15 11:02:58 vm82100 lxcfs[25418]: - proc_stat
Nov 15 11:02:58 vm82100 lxcfs[25418]: - proc_swaps
Nov 15 11:02:58 vm82100 lxcfs[25418]: - proc_uptime
Nov 15 11:02:58 vm82100 lxcfs[25418]: - shared_pidns
Nov 15 11:02:58 vm82100 lxcfs[25418]: - cpuview_daemon
Nov 15 11:02:58 vm82100 lxcfs[25418]: - loadavg_daemon
Nov 15 11:02:58 vm82100 lxcfs[25418]: - pidfds

如果你遇到 Process: 25502 ExecStopPost=/usr/bin/fusermount -u /var/lib/lxcfs (code=exited, status=1/FAILURE) 的错误,目前看这是正常的

这样我们就可以验证结果了,输出的 cpu 数量就是 2。

$ docker run -it --rm -m 256m --cpus=2 \
      -v /var/lib/lxcfs/proc/cpuinfo:/proc/cpuinfo:rw \
      -v /var/lib/lxcfs/proc/diskstats:/proc/diskstats:rw \
      -v /var/lib/lxcfs/proc/meminfo:/proc/meminfo:rw \
      -v /var/lib/lxcfs/proc/stat:/proc/stat:rw \
      -v /var/lib/lxcfs/proc/swaps:/proc/swaps:rw \
      -v /var/lib/lxcfs/proc/uptime:/proc/uptime:rw \
      ubuntu:18.04 /bin/bash
$ cat /proc/cpuinfo | grep processor | wc -l
2

用上面的 go 程序不能验证结果,这是因为 docker 具体而言使用了 CFS公平调度算法 限制容器的 CPU 使用,虽然 lxcfs 成功挂载了必要信息到 /proc/cpuinfo 里,但是 golang 运行时通过 sched_getaffinity系统调用来获取 cpu 数量,所以 lxcfs 还是不能彻底解决问题。

这个问题目前没有解决,可以订阅这个 golang: runtime: make GOMAXPROCS cfs-aware on GOOS=linux 问题进行跟踪。上面介绍的 uber 开源的 automaxprocs 库解决这个问题的思路是读取 cgroup 信息,然后在启动的时候就设置 GOMAXPROCS。

另外你用 lscpu不能验证结果,也是同样的道理,都是系统调用,lxcfs 并不能拦截系统调用。

@islishude islishude added the Go label Dec 10, 2019
@islishude islishude changed the title Go: 在容器中自动设置GOMAXPROCS GO 容器资源可见性问题与 GOMAXPROCS 设置 Nov 15, 2020
@islishude islishude changed the title GO 容器资源可见性问题与 GOMAXPROCS 设置 容器资源可见性问题与 GOMAXPROCS 配置 Nov 17, 2020
@0xleizhang
Copy link

请问博主 Containerd container runtime也会有这个问题吗/

@islishude
Copy link
Owner Author

请问博主 Containerd container runtime也会有这个问题吗/

这个肯定是一样的,容器的资源限制都是使用 cgroup,不论是什么 runtime

@0xleizhang
Copy link

0xleizhang commented May 5, 2023

go方面是希望k8s来解决这个issue吗?没看到有k8s方面的讨论?

看样子是k8s也在等go team ,目前一直丢在backlog 没有处理

Right now we're watching the Go issue to fix this for all go applications in the standard library / runtime. There's recent indication that the Go team plan to make Go aware of this.

— Benjamin Elder (@BenTheElder) May 5, 2023
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants