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 := ¬ifySocket{
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では?他では?