【続】 Linux Kernel: cgroup 削除後も残り続ける slab キャッシュ についての調べ物 - upsteam は修正パッチが入って解決済み

以前に下記のエントリを書きました。勉強会に登壇するネタとして調べ直したところ 新しい Linux カーネルでは問題が解決されていました。

hiboma.hatenadiary.jp

hiboma.hatenadiary.jp

hiboma.hatenadiary.jp

hiboma.hatenadiary.jp

どんな問題ですか?

「cgroup を削除した後も sysfs の slab キャッシュ が残る」「slab キャッシュ が reclaim されるタイミングで必要のない uevent が送出される」 という二つの問題でした。

問題 と書いていますが、対象の slab キャッシュは inode っキャッシュや dentry キャッシュ であり、reclaimable な slab キャッシュです。 reclaimable な slab キャッシュは、カーネルにメモリプレッシャーがかかると自然に回収されため、大抵は問題として顕在化せずに気がつかないかと思います。送出される uevent も slab キャッシュの数が多くなければシステムへの負荷として観測されるほどにはなりません。

ところが、私が仕事で扱ってるサーバでは、以下のように問題を踏みました。

そもそも、このネタを調べていたのは「とある大量のコンテナを扱う production 環境」で、sysfs ファイル削除のタイミングで uevent が大量に送出され、それを処理するカーネルスレッドが競合を起こし TASK_UNINTERRUPTIBLE でブロックされ、ロードアベレージ上昇のアラートを招いていたのが発端でした。

Linux Kernel: cgroup, sysfs, kobject, uevent についての調べ物 - (3) slub_memcg_sysfs ブートパラメータについて - hibomaの日記

問題の回避・解決方法は?

1. slub_memcg_sysfs を指定する

ワークアラウンドとして、ブートオプションに slub_memcg_sysfs=0 を指定すると、cgroup によって sysfs のファイルが作成されなくなり slab っキャッシュも溜まらなくなり、「問題」を回避することができます。

2. 新しいカーネルにする

本エントリの本題になります。上記の新しいカーネルではパッチがあたり、問題が解決されています

パッチその1

cgroup 削除後も slab キャッシュが残ってしまうコードは下記で解決されたようです。5.9-rc1 で取り込まれているパッチです。

github.com

コミットログを見るに、memcg で使う kmem_cache を一つに統一する、という内容のようですがボリュームのでかいパッチで詳細まではちょっとわかりません

本エントリに関係するのは、diff で言うと下記のあたり。

@@ -5760,16 +5628,6 @@ static int sysfs_slab_add(struct kmem_cache *s)
    if (err)
        goto out_del_kobj;
 
-#ifdef CONFIG_MEMCG
-  if (is_root_cache(s) && memcg_sysfs_enabled) {
-      s->memcg_kset = kset_create_and_add("cgroup", NULL, &s->kobj);
-      if (!s->memcg_kset) {
-          err = -ENOMEM;
-          goto out_del_kobj;
-      }
-  }
-#endif
-
    if (!unmergeable) {
        /* Setup first alias */
        sysfs_slab_alias(s, s->name);

https://github.com/torvalds/linux/commit/10befea91b61c4e2c2d1df06a2e978d182fcf792#diff-4f86c03fe66c75bd50afc8e320349281L5763-L5772

パッチその2

cgroup が作る sysfs ファイルが udev イベントを飛ばしていたコードは下記のコミットで削除されました。5.8-rc1 で取り込まれています

github.com

「必要ないから消したよ」というコミットログですね。

I came across some unnecessary uevents once again which reminded me this. The patch seems to be lost in the leaves of the original discussion [1], so resending.


kmem_cache ( slub キャッシュ ) はカーネル内部の構造体なのに、ユーザ空間に通知を出すのは変だし、使ってるのみたことないというコメント

Kmem caches are internal kernel structures so it is strange that userspace notifiers would be needed. And I am not aware of any use of these notifiers.

https://lore.kernel.org/linux-mm/20200423115721.19821-1-mkoutny@suse.com/

先のエントリを書いた時に「 cgroup の sysfs が uevent を出して何に使うんだろう?」という疑問を抱いていたのですが、カーネルコミッタも同様に思っていたようですね。よかった 😀


These notifiers may just exist because in the initial slub release the sysfs code was copied from another subsystem.

さらに、「別のサブシステムからコピペして作った」という旨も書かれています ワハハ


イベントの宣伝

第12回 コンテナ技術の情報交換会@オンライン では、これらの話をまとめ直した内容を喋るつもりでいます

ct-study.connpass.com

TenForward id:defiant さん、こんな感じです!

八方ヶ原大間々台 🚴‍♂️

9/22 栃木県矢板市の八方ヶ原大間々台へヒルクライム。コロナのせいで遠方に出かけるのを控えていたので、ようやく今年初の山。

f:id:hiboma:20200928113242p:plain

f:id:hiboma:20200922161549j:plain

走行距離 61km, 獲得標高 1143m。在宅の合間で、ランニングやサイクルトレーナーでトレーニングしてものの、すっかり足が衰えていてしんどかった。

続きを読む

会社のテックブログに記事を書きました: ペパボ トラブルシュート伝 - TCP: out of memory -- consider tuning tcp_mem の dmesg から辿る 詳解 Linux net.ipv4.tcp_mem

以下の記事です。

tech.pepabo.com

  • TCP our of memory
  • memory pressure モード
  • net.ipv4.tcp_mem

以上の三つの詳細を扱ったエントリです。TCP で大規模なトラフィックを扱っているサーバを扱われている場合、問題がないかどうかを確かめてみるとよいかと思います。

本文長いです。何に気をつけたらいいんでしょう?

ラクティカルな話だけをまとめると、以下の4行です。

  • memory pressure モードに入ってしまうと warning です
  • TCP out of memory が出てしまうと critical です
  • 監視は /proc/ 以下のファイルを見ましょう
  • チューニングは net.ipv4.tcp_mem で行いましょう

LVS はどうなの?

LVS でロードバランシングしている場合は、TCP スタックを通らないため TCP oom や memory pressure モードは問題にならないと思います ( LVS のソースは呼んだことないので、確実ではないですが ... )

定量的な評価はしなかったのか?

Q「パフォーマンス」に触れているが、はたして、どれくらい性能に影響する問題なのですか?
A. すいません。測定してないです。

ソースを読んだ上での定性的な評価のみです。

定量的な評価はできませんでした。ツールの選定を始め、どのようにシミュレート環境を作るべきかが分からず 手を出せませんでした。力不足に尽きますね。

落穂拾い的な話

  • TCP/IP の理解が浅いところからスタートで、初歩の初歩から学び直しが必要だった。
  • ソースと書籍を読みあさって、お正月から 一ヶ月〜二ヶ月くらい費やしました。(コロナのせいでしばらく記事を寝かせていた)
  • 調査の過程で「TCP のパケットドロップってどういう条件で起きるんだ?」という長年の疑問が解決されたのもよかった

記事を書いた時に頻繁に参照したのが、以下の書籍です。

www.amazon.co.jp

TCP/IP Architecture, Design, and Implementation in Linux は、Linux カーネル 2.4系のソースを併記しながら TCP/IP スタックを細かく解説するカーネル本です。2.4 系であっても参考となる箇所は多く、この本がないと調べきるのは難しかったなと思います。編集や校正が詰めきれてないのか、ところどころアラが目立つ書籍ではあるので、ご注意を ... 。

ピアノと楽しい駆動 - 楽しいはあなた個人でつかむもの

ピアノを弾き始めた。二ヶ月半になる。楽しい


COVID-19 の緊急事態宣言で(あるいはその前から) 自宅での時間を持て余して、何か新しい趣味に手を伸ばした人は、相当の数いるだろう。私もその1人だ。


二ヶ月半前、最初は Handel の HWV 440 1. Allmenand の右手パートをなぞるところから初めてみた。録音があまりない曲。キースジャレットの演奏が楽しさに満ち溢れた感じで気持ちいい。

ちょっとずつ右手パートをなぞっていただけなのだが、自分でも信じられないくらい没頭できて、最初の休日は昼から夜まで数時間弾いて潰した。以来、毎日、練習している。

そうそう、会社がリモート化を推進してくれたおかげで、朝起きて30分、昼ご飯食べて15分、おやつ休憩時間に10分、お仕事を終わって寝るまでの時間を費やせている。

pepabo.com


練習の話

「その曲からピアノやるんですかい !? 」ってツッコミを方々から受けそうだ。当然、技術の未熟さで超えられない壁にブチ当たってるので、基礎の基礎を身に付けるための練習曲も並行して進めている。ドミファソラソファミド ♬

達成したい大きな課題を得てから、小さな問題解決に回帰するってのは、技術者の学習でもよくあるパターンなんじゃないだろうか (例: カーネルの本読み始めてから C 言語の勉強始める )


新しいことへのチャレンジ

独学でも、かなりのレベルでピアノを弾けるようになった友人や、 (かつての) 会社の後輩などを知っている。

そういう 先輩 がいることで「ほいじゃ、0 からでも自分もやれるかなぁ」という心理的安全性(?) を得ているようにも思う。平均律クラヴィア曲集 第1巻 1番のプレリュード を通して弾けるようになってから (ミスはたくさんするけどね)、もっとやれそうと自信がでてきた。

同じくピアノを始めた職場の後輩もいて こういう入り方もあるんだなぁと学びになっている。

www.fendo181.me


好きな曲の話

今は、フランス組曲の4番に挑戦している。この曲は好き過ぎすぎて結婚式の BGM にも選んだくらいなので、自分でそれを弾ける喜びで没頭できる。

奏者は、キースジャレットか、あるいはペライアがいいなぁ

ゆっくり、感傷的にふった解釈だと、ニコラーエワの演奏が白眉。


Bach - Tatiana Nikolayeva (1984) French Suite No.4 in E-flat major, BWV 815

グールドとアシュケナージのは、 随分とテンポが早いなと思う。もう少し抑えめがいいかな


たのしい駆動

趣味であれ仕事であれ、熱中的に取り組んでいる何かの「楽しい」に突き動かされて それが自身の成長や成功へと繋がった !!!! という話は、ほんとよく聞くものだけど、そこまでの 「楽しい」を見つられるかどうかは さてはて 難しいものだなと思う。

ただただ「楽しい」という内的なモチベーションに駆動されている精神状態は、得難い。

Web 系企業のソフトウェアエンジニアとして技術の研鑽に励んできた/いるが、「楽しい」ドリブンで取り組んできたものは、確実に自分の血となり肉となったように思う。例えば、Linux の低レイヤーのデバッグスキルなど。

しかしながら、かつて「楽しい」と取り組んでいた技術的なモノ・コトも、正直、最近は外的な要因にモチベーションを大きく左右されやすくなったようにも思う ( 例えば、会社での評価だとか目標だとかね ) 。

技術者として、「楽しい」を再発見する機会を改めて作り出さないとなぁとため息をつく。


この本に 成人の学習者が、学習(趣味) へのモチベーションをどう喚起するか!? な話が書いてあった気がするが、思い出せない。学術的な話を引用してカッコよく締めようと思ったのが。

会社のテックブログに記事を書きました : ペパボ トラブルシュート伝 - node プロセスの general protection fault を追う - abort(3) の意外な実装

以下のエントリです。

tech.pepabo.com

テーマがテーマなだけに「せいぜい 10ブックマークくらいかなぁと」思ったてけど、もうちょっと増えたのでニコニコ。


今後も、いままで本ブログに書いていたような Linux の低いレイヤでのトラブルシューティングや、デバッグログ、検証ログみたいな内容h できるだけ会社のテックブログに投稿しようと思います。

CD を処分した

CD を処分 (売却した)

f:id:hiboma:20200519210304j:plain

押入れに眠っていた大量の CD をせっせとインポートして、自宅待機・自宅勤務の空き時間を使い3ヶ月ほどかけてようやく全作業 (インポート、盤の掃除、箱詰め、業者へ送り出す) を終えた。

扱った CD のほとんどが、音楽サブスクリプションサービスでも聴けるものだった。「インポートする必要あったんかな?」 という気は拭えない (このご時世に 「CD」という単語を目にして そういう感想を抱く人は少なく無いと思う)

そう、音楽サブスクリプションサービスは言わずもがな便利で最高。

さて、しかしながら、いざ何か聴こうと思い検索をかける際に、アーティスト名やアルバム名を正確に思い出せない、すっかり忘れている、ということが多い。ジャケット写真だけ覚えているということもよくある。そんなんで、頭に浮かんできた単語を頼りに検索していると、自然、偏りがでてきて いつも同じようなものを視聴するようになる。

サブスクリプションサービスからのレコメンドも便利で、関連の音楽を辿ることでもポチポチと芋づる式にいろんなものを掘ってはいけるが、能動的に検索をかけていかないと辿り着けない音楽が多々ある。その際に、過去に自分が聴いてきたCD を元にしたアーティストやアルバムのインデックスが手元にあると、検索ワードを手繰り寄せるリファレンスになる。自分のための音楽ガイドとでもいったところ。

という点で、インポートしたデータも無駄ではないように思っている。

ECM catalog 増補改訂版/50th Anniversary

ECM catalog 増補改訂版/50th Anniversary

  • 作者:稲岡邦彌
  • 発売日: 2019/11/30
  • メディア: 単行本

レア・グルーヴ A to Z[3rd Edition]

レア・グルーヴ A to Z[3rd Edition]

  • 発売日: 2018/12/20
  • メディア: 単行本(ソフトカバー)

サブスクリプションによせたガイド本って出てないのかなぁ

Linux Kernel: cgroup 削除後も残り続ける slab キャッシュ についての調べ物

タイトルの通りで、コンテナの生成と削除が頻繁におこなわれているホストで、 cgroup 削除後も特定の slab キャッシュ ( + sysfs のファイル = kobject ) が残るという現象を調べていました

環境

vagrant@bionic:~$ uname -a
Linux bionic 5.4.1-050401-generic #201911290555 SMP Fri Nov 29 11:03:47 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux

再現の方法

  • cgroup v1 + memory コントローラーでグループを作る
  • そのグループ内で適当なファイルを作成する
  • cgroup のグループを削除する

とすれば ok です

#!/bin/bash

set -ex

for i in {1..10};
do
  mkdir /sys/fs/cgroup/memory/hogehoge-$i;
  bash -c "echo \$\$ > /sys/fs/cgroup/memory/hogehoge-$i/tasks; rm -rfv /tmp/$i; mkdir /tmp/$i; ps > /tmp/$i/hoge;"
done

for i in {1..10};
do
  rmdir /sys/fs/cgroup/memory/hogehoge-$i;
done

観察

スクリプトの中で cgroup を削除しているにも関わらず cgroup に属する slab キャッシュが残っており、その様子は /sysfs/kernel/slab/<キャッシュの名前>/cgroup/<キャッシュの名前(コンテナ名)> を通して把握できます

root@bionic:~# find /sys/kernel/ -type d | grep hogehoge
/sys/kernel/slab/radix_tree_node/cgroup/radix_tree_node(987:hogehoge-1)
/sys/kernel/slab/dentry/cgroup/dentry(1029:hogehoge-4)
/sys/kernel/slab/dentry/cgroup/dentry(1027:hogehoge-3)
/sys/kernel/slab/dentry/cgroup/dentry(1025:hogehoge-2)
/sys/kernel/slab/dentry/cgroup/dentry(1039:hogehoge-9)
/sys/kernel/slab/dentry/cgroup/dentry(1023:hogehoge-1)
/sys/kernel/slab/dentry/cgroup/dentry(1037:hogehoge-8)
/sys/kernel/slab/dentry/cgroup/dentry(987:hogehoge-1)
/sys/kernel/slab/dentry/cgroup/dentry(1035:hogehoge-7)
/sys/kernel/slab/dentry/cgroup/dentry(1033:hogehoge-6)
/sys/kernel/slab/dentry/cgroup/dentry(1031:hogehoge-5)
/sys/kernel/slab/dentry/cgroup/dentry(1041:hogehoge-10)
/sys/kernel/slab/proc_inode_cache/cgroup/proc_inode_cache(987:hogehoge-1)
/sys/kernel/slab/:a-0000104/cgroup/buffer_head(999:hogehoge-7)
/sys/kernel/slab/:a-0000104/cgroup/buffer_head(997:hogehoge-6)
/sys/kernel/slab/:a-0000104/cgroup/buffer_head(993:hogehoge-4)
/sys/kernel/slab/:a-0000104/cgroup/buffer_head(991:hogehoge-3)
/sys/kernel/slab/:a-0000104/cgroup/buffer_head(1029:hogehoge-4)
/sys/kernel/slab/:a-0000104/cgroup/buffer_head(1027:hogehoge-3)
/sys/kernel/slab/:a-0000104/cgroup/buffer_head(1025:hogehoge-2)
/sys/kernel/slab/:a-0000104/cgroup/buffer_head(1023:hogehoge-1)
/sys/kernel/slab/:a-0000104/cgroup/buffer_head(1037:hogehoge-8)
/sys/kernel/slab/:a-0000104/cgroup/buffer_head(1005:hogehoge-10)
/sys/kernel/slab/:a-0000104/cgroup/buffer_head(1035:hogehoge-7)
/sys/kernel/slab/:a-0000104/cgroup/buffer_head(1003:hogehoge-9)
/sys/kernel/slab/:a-0000104/cgroup/buffer_head(1001:hogehoge-8)
/sys/kernel/slab/:a-0000104/cgroup/buffer_head(1031:hogehoge-5)
/sys/kernel/slab/:a-0000104/cgroup/buffer_head(989:hogehoge-2)
/sys/kernel/slab/:a-0000104/cgroup/buffer_head(987:hogehoge-1)
/sys/kernel/slab/ext4_inode_cache/cgroup/ext4_inode_cache(1029:hogehoge-4)
/sys/kernel/slab/ext4_inode_cache/cgroup/ext4_inode_cache(1027:hogehoge-3)
/sys/kernel/slab/ext4_inode_cache/cgroup/ext4_inode_cache(1025:hogehoge-2)
/sys/kernel/slab/ext4_inode_cache/cgroup/ext4_inode_cache(1039:hogehoge-9)
/sys/kernel/slab/ext4_inode_cache/cgroup/ext4_inode_cache(1023:hogehoge-1)
/sys/kernel/slab/ext4_inode_cache/cgroup/ext4_inode_cache(1037:hogehoge-8)
/sys/kernel/slab/ext4_inode_cache/cgroup/ext4_inode_cache(1035:hogehoge-7)
/sys/kernel/slab/ext4_inode_cache/cgroup/ext4_inode_cache(1041:hogehoge-10)
/sys/kernel/slab/ext4_inode_cache/cgroup/ext4_inode_cache(1033:hogehoge-6)
/sys/kernel/slab/ext4_inode_cache/cgroup/ext4_inode_cache(1031:hogehoge-5)

buffer_head, ext4_inode_cache, dentry, radix_tree_node といったファイルシステムに関係してそうな slab が残ります

これらのファイルは sysctl vm.drop_caches=3 で強制的に削除できます. udevadm でその様子をモニターできます

root@bionic:~# udevadm monitor
monitor will print the received events for:
UDEV - the event which udev sends out after rule processing
KERNEL - the kernel uevent

KERNEL[9553.936313] remove   /buffer_head(1035:hogehoge-7) (cgroup)
KERNEL[9553.972915] remove   /buffer_head(1029:hogehoge-4) (cgroup)
UDEV  [9553.973513] remove   /buffer_head(1035:hogehoge-7) (cgroup)
UDEV  [9553.975710] remove   /buffer_head(1029:hogehoge-4) (cgroup)
KERNEL[9554.021867] remove   /dentry(1041:hogehoge-10) (cgroup)
KERNEL[9554.029926] remove   /dentry(1035:hogehoge-7) (cgroup)
UDEV  [9554.030095] remove   /dentry(1041:hogehoge-10) (cgroup)
KERNEL[9554.031050] remove   /dentry(1039:hogehoge-9) (cgroup)
UDEV  [9554.031120] remove   /dentry(1035:hogehoge-7) (cgroup)
UDEV  [9554.031872] remove   /dentry(1039:hogehoge-9) (cgroup)
KERNEL[9554.032554] remove   /dentry(1037:hogehoge-8) (cgroup)
KERNEL[9554.032571] remove   /dentry(1033:hogehoge-6) (cgroup)
KERNEL[9554.032580] remove   /dentry(1031:hogehoge-5) (cgroup)
KERNEL[9554.032587] remove   /dentry(1029:hogehoge-4) (cgroup)

...

もう一度 /sys/kernel 以下をみてみると、消えましたね

root@bionic:~# find /sys/kernel/ -type d | grep hogehoge
root@bionic:~# 

( 注: kswapd の background reclaim やタスクの direct reclaim で slab の回収をする際にも削除されると思います. ここでは観察のために vm.drop_caches を呼び出しています )

疑問と推測 ❓

cgroup を削除して プロセスと cgroup からの参照が無くなったにも関わらず slab が残ってしまうのかが謎でした。

推測で、cgroup 内で作られた dirty な inode やページキャッシュがある場合に slab が残るのかな? とアタリをつけました

が、結局わからないので LKML を調べました

ソースを調べる

LKML のパッチや投稿を探しているうちに、私が遭遇したのと内容が合致していそうな投稿を見つけます

lkml.org

どうも cgroup 内で作られた dirty な inode に対して wb = struct bdi_writeback が参照をもっていて、冒頭に書いた現象が起きるようです. なるほど〜

投稿されたパッチ

work_struct で定期的に起床するカーネルスレッドに 回収処理を任せるという実装を提案していますが、その後のレビューを見るに mainline には取り込まれていないようです

現状はなんともしようがないんですかねぇ

与太話

Facebookカーネルエンジニアさんらしいので、FB のユースケースでの問題を解決したいパッチ投稿みたいですね。

まとめ

  • コンテナの生成と削除が頻繁に行われる環境 (例: k8s )で、暗黙的に slab キャッシュが溜まるという現象で遭遇しそうです
  • slab の回収処理が走れば削除されるはずなので、気がついてないというケースもありそうです

関連のエントリ

hiboma.hatenadiary.jp