memo

2013-10-06

Systemd と ControlGroup

これ 読んだり色々試してみたりしての、自分なりのまとめ。

Systemd のバージョンは 208 。

リソース制御の単位

Systemd のリソース制御で登場する unit たち。

  • Service :: 設定を元に systemd により起動・停止されるプロセス群をまとめたもの

  • Scopes :: 任意のプロセスから fork() され、 PID 1 に登録されたプロセス群をまとめたもの

  • Slices :: 上記 services や scopes をまとめるためのもの

3つ共に cgroup の tree を割り当てられているようなので、 どれに対してもリソース制御を設定できる。

systemd-cgls で現在の tree の状態が表示されるので、とりあえず見てみるとよい。

Services, slices は設定ファイルからもしくは runtime に動的に生成でき、 scopes は runtime でのみ生成できる。 Runtime に生成された units を transient units と呼ぶ。

リソース制御の方法

設定ファイルから生成された units はそのファイル内でリソース制限を行える。 以下のコマンドにより、再設定することも出来る:

# systemctl set-property httpd.service CPUShares=500 MemoryLimit=500M

この場合、設定は恒久的に残り、リブート後も自動で設定が適用される。それが嫌なら --runtime つける。

何が設定できるかは man systemd.resource-control で。

Transient な unit を生成する

systemd-run コマンドがその辺やってくれる君のようなので、色々やってみる。

$ sudo systemd-run sh -c 'ls /; sleep 3'
Running as unit run-26504.service.
$ journalctl -u run-26504.service
-- Logs begin at Mon 2013-08-12 00:11:06 JST, end at Sun 2013-10-06 17:42:55 JST. --
Oct 06 17:42:55 localhost systemd[1]: Starting /bin/sh -c ls /; sleep 3...
Oct 06 17:42:55 localhost systemd[1]: Started /bin/sh -c ls /; sleep 3.
Oct 06 17:42:55 localhost sh[26515]: bin
Oct 06 17:42:55 localhost sh[26515]: boot
Oct 06 17:42:55 localhost sh[26515]: dev
Oct 06 17:42:55 localhost sh[26515]: etc
Oct 06 17:42:55 localhost sh[26515]: home
Oct 06 17:42:55 localhost sh[26515]: lib
Oct 06 17:42:55 localhost sh[26515]: lib32
Oct 06 17:42:55 localhost sh[26515]: lib64
Oct 06 17:42:55 localhost sh[26515]: lost+found
Oct 06 17:42:55 localhost sh[26515]: media
Oct 06 17:42:55 localhost sh[26515]: mnt
Oct 06 17:42:55 localhost sh[26515]: opt
Oct 06 17:42:55 localhost sh[26515]: proc
Oct 06 17:42:55 localhost sh[26515]: root
Oct 06 17:42:55 localhost sh[26515]: run
Oct 06 17:42:55 localhost sh[26515]: sbin
Oct 06 17:42:55 localhost sh[26515]: sys
Oct 06 17:42:55 localhost sh[26515]: tmp
Oct 06 17:42:55 localhost sh[26515]: usr
Oct 06 17:42:55 localhost sh[26515]: var
$ sudo systemd-run --scope bash
Running as unit run-25802.scope.
# cat /proc/$$/cgroup
10:hugetlb:/
9:net_prio:/
8:blkio:/
7:freezer:/
6:devices:/
5:memory:/
4:cpuacct,cpu:/
3:cpuset:/
2:name=systemd:/system.slice/run-25802.scope
# systemctl set-property --runtime run-25802.scope CPUShares=500 MemoryLimit=500M
# cat /proc/$$/cgroup
# cat /proc/$$/cgroup
10:hugetlb:/
9:net_prio:/
8:blkio:/
7:freezer:/
6:devices:/
5:memory:/system.slice/run-25802.scope
4:cpuacct,cpu:/system.slice/run-25802.scope
3:cpuset:/
2:name=systemd:/system.slice/run-25802.scope
# cat /sys/fs/cgroup/cpu/system.slice/run-25802.scope/cpu.shares
500
  • systemd-run で新しく transient な service が作られ、その下で指定されたコマンドが起動する。 コマンドは現在のプロセスとは独立して動作するので、出力を見たいなら journal で確認する。

  • sleep してるのは、そうしないと出力が journal に渡る前に service が終了 & 削除されちゃって、 journalctl -u で取れなくなるから。

  • systemd-run --scope で新しく scope unit が作られ、その下で指定されたコマンドが起動する。 コマンドは foreground で動く。

  • systemctl set-property には --runtime 付けないと効果がない模様

  • systemd-run には cgroup な property 設定する機能無い?

  • sudo なしで scope 作ろうと systemd-run --user を試してみたら、 Failed start transient unit: Process /bin/false exited with status 1 とかいって失敗する。 --user な systemd とうまく通信できてない?

おまけ

現在の scope/service を得るには sd_pid_get_unit() を使え、とのことらしいが、 C とかよく分からんし、 python で ctypes 使ってもうまくいかなかったので、 go でやってみたらなんか動いた。でも何やってるかよく分かってない。

sd_pid_get_unit.go:

package main

/*
#cgo LDFLAGS: -lsystemd-login
#include <systemd/sd-login.h>
*/
import "C"

import (
    "fmt"
    "os"
)

func main() {
    result := C.CString("")

    ret, err := C.sd_pid_get_unit(C.pid_t(os.Getpid()), &result)
    if err != nil {
        panic(err)
    }
    if ret != 0 {
        panic(ret)
    }

    fmt.Println(C.GoString(result))
}
$ go run sd_pid_get_unit.go
session-1.scope
$ sudo systemd-run --scope go run sd_pid_get_unit.go
Running as unit run-1539.scope.
run-1539.scope