Rabbit Slide Show

Container Runtime Meetup #1

2019-09-24

Description

https://runtime.connpass.com/event/145088/

Text

Page: 1

NOTIFY_SOCKET
環境変数について
2019-09-24
Container Runtime Meetup #1
うなすけ

Page: 2

自己紹介
名前 : うなすけ
仕事 : 株式会社バンク (業務委託)
インフラ寄りサーバーサイドエンジニア
Ruby, Rails, Kubernetes…
GitHub @unasuke
Mastodon @unasuke@mstdn.unasuke.com
Twitter @yu_suke1994

Page: 3

Excuse (僕の実力について)
Railsでweb API を作ってて、「コンテナ?便利じゃ
ん〜」くらいのレベルの開発者
image-specは読んだことがあります
@udzuraさんにそそのかされて来ました
https://twitter.com/udzura/status/
1166345876769394689

Page: 4

調査する対象を決めるまで
https://runtime.connpass.com/event/145088

Page: 5

調査する対象を決めるまで
とりあえず最新リリースを読む対象にするじゃないで
すか
https://github.com/opencontainers/runc/releases/
tag/v1.0.0-rc8

Page: 6

調査する対象を決めるまで
status, err := startContainer(context, spec, CT_ACT_RUN, nil)
https://github.com/opencontainers/runc/blob/v1.0.0-
rc8/run.go#L76
ああなんかこの辺でContainerが起動するんだな

Page: 7

調査する対象を決めるまで
notifySocket := newNotifySocket(context, os.Getenv("NOTIFY_SOCKET"), id)
if notifySocket != nil {
notifySocket.setupSpec(context, spec)
}
https://github.com/opencontainers/runc/blob/v1.0.0-
rc8/utils_linux.go#L411-L414
NOTIFY_SOCKET ← これなに?

Page: 8

調査する対象を決めるまで
この時点での認識
「環境変数がある状態で起動させると色々な通知
が飛んでくるのだろうか?」

Page: 9

Dive into code
func newNotifySocket(context *cli.Context, notifySocketHost string, id string) *notifySocket {
if notifySocketHost == "" {
return nil
}
root := filepath.Join(context.GlobalString("root"), id)
path := filepath.Join(root, "notify.sock")
notifySocket := &notifySocket{
socket:
nil,
host:
notifySocketHost,
socketPath: path,
}
return notifySocket
}
notifySocketのインスタンスはここで生成される
https://github.com/opencontainers/runc/blob/v1.0.0-
rc8/notify_socket.go#L23-L38

Page: 10

Dive into code
func newNotifySocket(context *cli.Context, notifySocketHost string, id string)
このcontext は https://godoc.org/github.com/urfave/
cli#Context を指す
なので /tmpfs/[container-id]/notify.sock があるはず

Page: 11

Dive into code
func (s *notifySocket) setupSpec(context *cli.Context, spec *specs.Spec) {
mount := specs.Mount{Destination: s.host, Source: s.socketPath, Options: []string{"bind"}}
spec.Mounts = append(spec.Mounts, mount)
spec.Process.Env = append(spec.Process.Env, fmt.Sprintf("NOTIFY_SOCKET=%s", s.host))
}
直後にsetupSpecを呼んでいる
https://github.com/opencontainers/runc/blob/v1.0.0-
rc8/notify_socket.go#L44-L50

Page: 12

Dive into code
If systemd is supporting sd_notify protocol, this
function will add support for sd_notify protocol
from within the container.
なるほどsystemd?
まあなんか色々やってるんだな (runtime-specのMount
あたり)

Page: 13

Dive into code
func (s *notifySocket) setupSocket() error {
addr := net.UnixAddr{
Name: s.socketPath,
Net: "unixgram",
}
createContainer後にsetupSocketが呼ばれる
https://github.com/opencontainers/runc/blob/v1.0.0-
rc8/notify_socket.go#L52-L56

Page: 14

Dive into code
https://golang.org/pkg/net/#UnixAddr
type UnixAddr struct {
Name string
Net string
}
unixgram ← datagram socket (UDP的な送りっぱなしの
プロトコル)
https://github.com/golang/go/blob/master/src/net/
unixsock_posix.go#L16-L27

Page: 15

Dive into code
ListenUnixgram acts like ListenPacket for Unix
networks.
https://golang.org/pkg/net/#ListenUnixgram
connectionを張るっぽい
そして runner 構造体の notifySocket field に
notifySocket 構造体のインスタンスが格納される

Page: 16

runner.run の中で……
// Setting up IO is a two stage process. We need to modify process to deal
// with detaching containers, and then we get a tty after the container has
// started.
handler := newSignalHandler(r.enableSubreaper, r.notifySocket)
SignalHandlerを作成している
https://github.com/opencontainers/runc/blob/v1.0.0-
rc8/utils_linux.go#L305-L308

Page: 17

ここまで
NOTIFY_SOCKET という環境変数をもとにsoket通信を
している?
これは unixgram によって通信するもの
systemd が何か関係しているようだ

Page: 18

NOTIFY_SOCKET をググる
https://www.freedesktop.org/software/systemd/
man/sd_notify.html#Notes
sd_notifyの通信方法 - Qiita
systemd(1) — Arch Linux マニュアルページ

Page: 19

freedesktop.org
These functions send a single datagram with the
state string as payload to the AF_UNIX socket
referenced
in
the
$NOTIFY_SOCKET
environment variable. If the first character of
$NOTIFY_SOCKET is “@”, the string is
understood as Linux abstract namespace socket.
https://www.freedesktop.org/software/systemd/man/
sd_notify.html#Notes

Page: 20

sd_notifyの通信方法 - Qiita
systemdのマネージャ(デーモンプロセス)は、起
動プロセスの最後の方でsd_notifyという関数を用
いて、起動が完了したことをsystemd本体(PID=1)
に通知する。(注:sd_notifyは実際にはもっと汎
用的なステータス通知に使える。)
https://qiita.com/ozaki-r/items/
ced43d5e32af67c7ae04

Page: 21

なるほどね
じゃあ、例えばDockerでも使われてるんだろうか?

Page: 22

Dockerではどうか
https://github.com/docker/cli にはない
と思ったら https://github.com/moby/moby にはあっ
た

Page: 23

Dockerでの NOTIFY_SOCKET
$ git grep NOTIFY_SOCKET
ある

Page: 24

手元のマシンでそれっぽいものが作成され
ているのか?
$ ss --family=unix | grep systemd | wc -l
110

Page: 25

手元のマシンでそれっぽいものが作成され
ているのか?
$ ss --family=unix | grep container
なんかおる

Page: 26

手元のマシンでそれっぽいものが作成され
ているのか?
$ ps aux --forest

Page: 27

環境変数を覗いてみる
dockerdの環境変数
$ sudo cat /proc/601/environ
LANG=ja_JP.UTF-8
PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/var/lib/snapd/snap/bin
NOTIFY_SOCKET=/run/systemd/notify
LISTEN_PID=601
LISTEN_FDS=1
LISTEN_FDNAMES=docker.socket
INVOCATION_ID=e65738cc4b8f461e968d23c6740a557e
JOURNAL_STREAM=9:22835

Page: 28

まとめと今後の目標
NOTIFY_SOCKET でsystemdとやりとりしているようだ
dockerd には NOTIFY_SOCKET が与えられていることが
確認できた
runc run を実行したときにもこれは与えられるの
か?
実際にどのようなデータがどのようなタイミングで
送られるのか?
dockerでは? runcでは?他では?

Other slides

CNDF2023 CNDF2023
2023-08-03
ruby30th-lt ruby30th-lt
2023-02-25