Linux systemd-run

systemd-run を使うと一時的な cgroup を作成してその中でコマンドを実行できる。

systemd-run はデフォルトでは service ユニットとしてコマンドを実行し、--scope オプションを指定すると scope ユニットとして実行する。

次の二つを確認する。

  • systemd-run で開始する service ユニットと scope ユニットの違い
  • systemd-run を使ってリソース制限をかけてコマンドを実行する方法

service ユニットは非同期に実行される

man systemd-run によると、

If a command is run as transient service unit, it will be started and managed by the service manager like any other service, and thus shows up in the output of systemctl list-units like any other unit. It will run in a clean and detached execution environment, with the service manager as its parent process. In this mode, systemd-run will start the service asynchronously in the background and return after the command has begun execution (unless –no-block or –wait are specified, see below).

systemd-run で開始した service ユニットは、 サービスマネージャーが親プロセスになる。 service ユニットは、実行元とは別の クリーンな環境で 、非同期に実行される。

scope ユニットは同期的に実行される

man systemd-run によると、

If a command is run as transient scope unit, it will be executed by systemd-run itself as parent process and will thus inherit the execution environment of the caller. However, the processes of the command are managed by the service manager similar to normal services, and will show up in the output of systemctl list-units. Execution in this case is synchronous, and will return only when the command finishes. This mode is enabled via the –scope switch (see below).

scope ユニットは systemd-run 自身が親プロセスとなってコマンドを実行し、 呼び出し元の環境を引き継いで 同期的に実行される。 --scope オプションで scope ユニットになる。

検証環境

Ubuntu 18.04 で cgroup v2 を使用。

1
2
$ uname -a
Linux ip-172-31-45-104 5.3.0-1028-aws #30~18.04.1-Ubuntu SMP Mon Jun 22 15:48:21 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
1
2
$ mount | grep cgroup
cgroup on /sys/fs/cgroup type cgroup2 (rw,nosuid,nodev,noexec,relatime)

service ユニットでコマンドを実行

1
2
$ sudo systemd-run echo 'hello'
Running as unit: run-r21673aa7f9594763bfbae30b99576796.service

service ユニットではコマンドはバックグラウンドで非同期に実行され、出力は journald に送られる。 journald のログを確認する。

1
2
3
4
$ journalctl -u run-r21673aa7f9594763bfbae30b99576796.service
-- Logs begin at Sat 2020-06-27 02:42:21 UTC, end at Sat 2020-06-27 06:26:50 UTC. --
Jun 27 06:26:50 ip-172-31-45-104 systemd[1]: Started /bin/echo hello.
Jun 27 06:26:50 ip-172-31-45-104 echo[2154]: hello

ユニット名を指定する

先ほどの例ではユニットに対して自動的に生成された名前 (run-r21673aa7f9594763bfbae30b99576796.service) が付けられた。

オプション --unit で名前を指定できる。

1
2
$ sudo systemd-run --unit test echo 'hello'
Running as unit: test.service
1
2
3
4
$ journalctl -u test
-- Logs begin at Sat 2020-06-27 02:42:21 UTC, end at Sat 2020-06-27 06:32:21 UTC. --
Jun 27 06:32:21 ip-172-31-45-104 systemd[1]: Started /bin/echo hello.
Jun 27 06:32:21 ip-172-31-45-104 echo[2201]: hello

scope ユニットで実行する

--scope オプションを指定すると scope ユニットで実行される。

1
2
3
$ sudo systemd-run --scope echo 'hello'
Running scope as unit: run-rad1d45d3d34f4ad195da49ce1c116b12.scope
hello

scope ユニットでは同期的に実行され、出力が標準出力に出力された。

service ユニットが非同期 scope ユニットの環境の違いを確認

比較用に実行元の環境を確認

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ env
LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.zst=01;31:*.tzst=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.wim=01;31:*.swm=01;31:*.dwm=01;31:*.esd=01;31:*.jpg=01;35:*.jpeg=01;35:*.mjpg=01;35:*.mjpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.m4a=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.oga=00;36:*.opus=00;36:*.spx=00;36:*.xspf=00;36:
LESSCLOSE=/usr/bin/lesspipe %s %s
LANG=C.UTF-8
SUDO_GID=1001
USERNAME=ubuntu
SUDO_COMMAND=/bin/bash
USER=ubuntu
PWD=/home/ubuntu
HOME=/home/ubuntu
SUDO_USER=ssm-user
TMUX=/tmp/tmux-1000/default,1294,0
XDG_DATA_DIRS=/usr/local/share:/usr/share:/var/lib/snapd/desktop
SUDO_UID=1001
MAIL=/var/mail/ubuntu
TERM=screen
SHELL=/bin/bash
TMUX_PANE=%0
SHLVL=2
LOGNAME=ubuntu
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin
LESSOPEN=| /usr/bin/lesspipe %s
_=/usr/bin/env

service ユニットで env を実行して確認

1
2
$ sudo systemd-run env
Running as unit: run-r84a4491781fb4b0b988e00e6d1079c15.service

journalctl でログを確認すると、実行元とは異なるクリーンな環境で実行されているのが確認できる。

1
2
3
4
5
6
7
$ journalctl -u run-r84a4491781fb4b0b988e00e6d1079c15.service
-- Logs begin at Sat 2020-06-27 02:42:21 UTC, end at Sat 2020-06-27 06:37:15 UTC. --
Jun 27 06:37:15 ip-172-31-45-104 systemd[1]: Started /usr/bin/env.
Jun 27 06:37:15 ip-172-31-45-104 env[2243]: LANG=C.UTF-8
Jun 27 06:37:15 ip-172-31-45-104 env[2243]: PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin
Jun 27 06:37:15 ip-172-31-45-104 env[2243]: INVOCATION_ID=a948d55608804c40944184d48d045783
Jun 27 06:37:15 ip-172-31-45-104 env[2243]: JOURNAL_STREAM=9:28836

scope ユニットで env を実行して確認

scope ユニットで実行すると、実行元の環境が引き継がれているのが確認できる。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
$ sudo systemd-run --scope env
Running scope as unit: run-r18ae6e2cda9646be84f546cfed86d256.scope
LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.zst=01;31:*.tzst=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.wim=01;31:*.swm=01;31:*.dwm=01;31:*.esd=01;31:*.jpg=01;35:*.jpeg=01;35:*.mjpg=01;35:*.mjpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.m4a=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.oga=00;36:*.opus=00;36:*.spx=00;36:*.xspf=00;36:
LANG=C.UTF-8
HOME=/home/ubuntu
TERM=screen
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin
MAIL=/var/mail/root
LOGNAME=root
USER=root
USERNAME=root
SHELL=/bin/bash
SUDO_COMMAND=/usr/bin/systemd-run --scope env
SUDO_USER=ubuntu
SUDO_UID=1000
SUDO_GID=1000

実行中の service ユニットのリソースを制限

検証用に service ユニットを開始

検証用に service ユニットで無限ループを実行。 これは制限なし。

1
2
$ sudo systemd-run --unit test sh -c 'while true; do : ; done'
Running as unit: test.service

test.service の PID を確認。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
$ sudo systemctl status test.service
● test.service - /bin/sh -c while true; do : ; done
   Loaded: loaded (/run/systemd/transient/test.service; transient)
Transient: yes
   Active: active (running) since Sat 2020-06-27 09:07:27 UTC; 19s ago
 Main PID: 2664 (sh)
    Tasks: 1 (limit: 2294)
   CGroup: /system.slice/test.service
           └─2664 /bin/sh -c while true; do : ; done

Jun 27 09:07:27 ip-172-31-45-104 systemd[1]: Started /bin/sh -c while true; do : ; done.

確認した PID 2664 の CPU 使用率を確認するとほぼ 100% になっている。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
$ top -b -p 2664
top - 09:08:23 up  5:38,  1 user,  load average: 0.72, 0.51, 0.27
Tasks:   1 total,   1 running,   0 sleeping,   0 stopped,   0 zombie
%Cpu(s):  1.2 us,  0.1 sy,  0.0 ni, 98.7 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  2003024 total,  1213880 free,   149156 used,   639988 buff/cache
KiB Swap:        0 total,        0 free,        0 used.  1694772 avail Mem

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
 2664 root      20   0    4624    872    808 R 100.0  0.0   0:56.37 sh

top - 09:08:26 up  5:38,  1 user,  load average: 0.75, 0.52, 0.27
Tasks:   1 total,   1 running,   0 sleeping,   0 stopped,   0 zombie
%Cpu(s): 50.1 us,  0.2 sy,  0.0 ni, 49.8 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  2003024 total,  1213872 free,   149160 used,   639992 buff/cache
KiB Swap:        0 total,        0 free,        0 used.  1694764 avail Mem

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
 2664 root      20   0    4624    872    808 R  99.7  0.0   0:59.37 sh

CPU リソースを 50% までに制限する

リソースを制限するには systemctl set-property を利用する。 指定できる制限は man systemd.resource-control で確認できる。

CPUQuota=50% を指定すると CPU 使用率を最大 50% に制限できる。

1
$ sudo systemctl set-property test.service CPUQuota=50%

CPU 使用率を確認すると 50% になっている。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
$ top -b -p 2664
top - 09:10:23 up  5:40,  1 user,  load average: 0.96, 0.68, 0.36
Tasks:   1 total,   1 running,   0 sleeping,   0 stopped,   0 zombie
%Cpu(s):  1.5 us,  0.1 sy,  0.0 ni, 98.4 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  2003024 total,  1213872 free,   149128 used,   640024 buff/cache
KiB Swap:        0 total,        0 free,        0 used.  1694792 avail Mem

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
 2664 root      20   0    4624    872    808 R  53.3  0.0   2:52.53 sh

top - 09:10:26 up  5:40,  1 user,  load average: 0.89, 0.67, 0.36
Tasks:   1 total,   1 running,   0 sleeping,   0 stopped,   0 zombie
%Cpu(s): 24.9 us,  0.2 sy,  0.0 ni, 75.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  2003024 total,  1213872 free,   149128 used,   640024 buff/cache
KiB Swap:        0 total,        0 free,        0 used.  1694792 avail Mem

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
 2664 root      20   0    4624    872    808 R  50.3  0.0   2:54.04 sh

service ユニットを開始時にリソースを制限する

systemd-run にオプション -p でリソース制限を指定できる。 指定形式は先ほどの systemctl set-property と同じ。

service ユニット開始時に CPU リソースを 50% に制限する。

1
$ sudo systemd-run --unit test -p CPUQuota=50% sh -c 'while true; do : ; done'

PID を確認。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
$ sudo systemctl status test.service
● test.service - /bin/sh -c while true; do : ; done
   Loaded: loaded (/run/systemd/transient/test.service; transient)
Transient: yes
   Active: active (running) since Sat 2020-06-27 09:11:29 UTC; 4s ago
 Main PID: 2741 (sh)
    Tasks: 1 (limit: 2294)
   CGroup: /system.slice/test.service
           └─2741 /bin/sh -c while true; do : ; done

Jun 27 09:11:29 ip-172-31-45-104 systemd[1]: Started /bin/sh -c while true; do : ; done.

確認した PID 2741 の CPU 使用率を確認するとほぼ 50% になっている。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
$ top -b -p 2741
top - 09:11:51 up  5:41,  1 user,  load average: 0.64, 0.61, 0.37
Tasks:   1 total,   1 running,   0 sleeping,   0 stopped,   0 zombie
%Cpu(s):  1.6 us,  0.1 sy,  0.0 ni, 98.3 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  2003024 total,  1213024 free,   149936 used,   640064 buff/cache
KiB Swap:        0 total,        0 free,        0 used.  1693980 avail Mem

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
 2741 root      20   0    4624    784    716 R  60.0  0.0   0:10.99 sh

top - 09:11:54 up  5:41,  1 user,  load average: 0.64, 0.61, 0.37
Tasks:   1 total,   1 running,   0 sleeping,   0 stopped,   0 zombie
%Cpu(s): 25.5 us,  0.2 sy,  0.0 ni, 74.4 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  2003024 total,  1213100 free,   149856 used,   640068 buff/cache
KiB Swap:        0 total,        0 free,        0 used.  1694056 avail Mem

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
 2741 root      20   0    4624    784    716 R  50.0  0.0   0:12.49 sh

参考