CLOVER🍀

That was when it all began.

Dockerコンテナ内で動作するNode.jsが認識するCPU数、メモリサイズ(ヒープサイズ除く)は、ホスト側のものになるという話

これは、なにをしたくて書いたもの?

Dockerコンテナ内で動作するNode.jsが認識するCPU数は、ホスト側のものになってしまうようなので、これを
メモしておこうかなと。

メモリについても同様のようですが、ヒープに限ってはコンテナ側の値を見てくれるようです。

情報元

Node.jsでは、Docker側の問題だとしているようです。

Not correct os.cpus().length inside the docker container with cpus limited. · Issue #28762 · nodejs/node · GitHub

Feature request: os: os.availableProcessors() · Issue #28855 · nodejs/node · GitHub

確認してみましょう。

環境

確認環境。

$ docker version
Client: Docker Engine - Community
 Version:           20.10.12
 API version:       1.41
 Go version:        go1.16.12
 Git commit:        e91ed57
 Built:             Mon Dec 13 11:45:33 2021
 OS/Arch:           linux/amd64
 Context:           default
 Experimental:      true

Server: Docker Engine - Community
 Engine:
  Version:          20.10.12
  API version:      1.41 (minimum version 1.12)
  Go version:       go1.16.12
  Git commit:       459d0df
  Built:            Mon Dec 13 11:43:42 2021
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.4.12
  GitCommit:        7b11cfaabd73bb80907dd23182b9347b4245eb5d
 runc:
  Version:          1.0.2
  GitCommit:        v1.0.2-0-g52b36a2
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

ホスト側のCPU数。

$ grep ^processor /proc/cpuinfo
processor       : 0
processor       : 1
processor       : 2
processor       : 3
processor       : 4
processor       : 5
processor       : 6
processor       : 7

ホスト側のメモリ。

$ head -n 1 /proc/meminfo
MemTotal:       16306452 kB

確認してみる

確認してみましょう。Node.js 16.13.1を使い、コンテナには2つのCPUと2Gのメモリを割り当てました。

$ docker container run -it --rm --name node --cpus 2 --memory 2g node:16.13.1-bullseye node
WARNING: Your kernel does not support swap limit capabilities or the cgroup is not mounted. Memory limited without swap.
Welcome to Node.js v16.13.1.
Type ".help" for more information.
> require('os').cpus().length
8
> require('os').totalmem()
16697806848
> 

確かに、ホスト側の情報が表示されます。

ヒープサイズについて

ヒープサイズ(--max-old-space-size)に関していうと、Node.js 12.7.0以降であればコンテナ側の値を
参照してくれるようです。

2019-07-23, Version 12.7.0 (Current), @targos

src: use cgroups to get memory limits by kjin · Pull Request #27508 · nodejs/node · GitHub

libuvの対応を、Node.jsに反映したもの(/proc/meminfo/sys/fs/cgroup/memory/memory.limit_in_bytesを比較して
小さい方を使う)みたいです。

Add uv_get_constrained_memory by kjin · Pull Request #2289 · libuv/libuv · GitHub

https://github.com/libuv/libuv/blob/v1.43.0/src/unix/linux-core.c#L819-L840

Javaだと

ちなみに、現在のJavaだとコンテナに割り当てたCPU数およびメモリサイズを認識します。

$ docker container run -it --rm --name openjdk --cpus 2 --memory 2g eclipse-temurin:17-jdk-focal jshell
WARNING: Your kernel does not support swap limit capabilities or the cgroup is not mounted. Memory limited without swap.
Jan 04, 2022 11:47:55 AM java.util.prefs.FileSystemPreferences$1 run
INFO: Created user preferences directory.
|  Welcome to JShell -- Version 17.0.1
|  For an introduction type: /help intro

jshell> Runtime.getRuntime().availableProcessors()
$1 ==> 2

jshell> ((com.sun.management.OperatingSystemMXBean)java.lang.management.ManagementFactory.getOperatingSystemMXBean()).getAvailableProcessors()
$2 ==> 2

jshell> ((com.sun.management.OperatingSystemMXBean)java.lang.management.ManagementFactory.getOperatingSystemMXBean()).getTotalMemorySize()
$3 ==> 2147483648

というか、コンテナ内の/proc/cpuinfo/proc/meminfoはホスト側のものが表示されるので、ランタイム側が
cgroupへの対応を入れないとJavaのようにはなりません、と…。