TenForward

技術ブログ。はてなダイアリーから移転しました

cgroup の CPU コントローラーから設定する帯域幅制限のコードをちょっとだけ追ってみた

この記事は Linux Advent Calendar 2023 5 日目の記事です。前日は @mnishiguchi さんの「Linux US キーボードの CapsLock を Ctrl に変更する方法」ですね。私は setxkbmap でやってます。

さて、このブログ久々の技術的な内容です。Linux カーネルが持つ cgroup という機能のうち、タスクやタスクのグループに対して CPU の帯域制限(単位時間内にどれだけ CPU が使えるか)を設定できる機能のコードを少し追ってみたお話です。あとで紹介する gihyo.jp さんに掲載している記事とあわせて読んでいただけるとよりうれしいです。

もう 4 年くらいまえのブログなんですが、Indeed さんのエンジニアリングブログのこの 2 つの記事がすばらしいんです。わかりやすい。もう cgroup の CPU コントローラーから使う帯域幅制限は、これを読めば完全に理解できる!ってくらいすばらしいです。

理解できたのでうれしくて思わず記事にしちゃいました。このブログ記事と、カーネル付属文書 "CFS Bandwidth Control" とあわせて読むと完璧です。

記事の方はある程度確信があることだけ書いたのですが、このブログは、記事として書くには自信がない「たぶんそうちゃう?しらんけど」ということを、「知らんけど」というスタンスで書いてますw まあ、記事中にコードを追っかけた記録みたいなもんです。

もうひとつ、以前、カーネルのコードを読むには?みたいなテーマの登壇がありましたが、全部読まずに必要なところだけつまみ食いするにはこんな感じですよ、ってのもあわせて感じていただければと思います。

さて、元のブログはカーネルのバグ修正の結果、期待通りに動くようになったものの、CPU が無駄にスロットルされてしまって、特に(CPUコアが大量にあるような)大規模環境では問題になったのを修正した、というお話です。

こちらは上記のブログ記事と私の記事の(2)をご覧いただくと良く分かると思いますので、ここでは説明しませんが、ひとつだけ気になったところがあって、ブログ中ではサラッと書かれていただけなので、久々にカーネルのコードを追ってみました。

6.1 カーネルをベースに追ってますが、下書き時点で色々なバージョンが混じっててあとで合わせたので、何行目かの説明が間違ってたらごめんなさい。

気になったのは、Indeedの 2 つ目のブログで、2 つ目の説明のための表中の、

余っているクォータをグローバルなバケットにもどすようにタイマーが設定されます。ワーカー1が実行を停止した後、このタイマーは7ms に設定されます
スロットリングの解除: 有効な修正が不具合の原因になってしまった理由

というところです(30msのところ)。

この "7ms" ってのは固定値として 7ms なのか、それとも可変値なのか? ってのを、記事を書く上でも知りたくてちょっとだけ追ってみました。ちゃんと追ってないので、この後は間違っている可能性が高いです。カーネル賢者からの優しいツッコミをお待ちしています

CPU の帯域制御を行う大体の仕組みは、私の記事をご覧いただくとして、CPU に割り当てた実行時間(スライス)が使われないとわかった場合、その割り当てをグローバルプールに返却する処理があります。この際、割り当てたスライスの残りから 1ms だけを引いた分をグローバルプールに返却します。

この 1ms というのは、カーネルのコード内で定義されており、変更するにはソースコードを変更する必要があります。

/* a cfs_rq won't donate quota below this amount */
static const u64 min_cfs_rq_runtime = 1 * NSEC_PER_MSEC;
/* minimum remaining period time to redistribute slack quota */
static const u64 min_bandwidth_expiration = 2 * NSEC_PER_MSEC;
/* how long we wait to gather additional slack before distributing */
static const u64 cfs_bandwidth_slack_period = 5 * NSEC_PER_MSEC;

kernel/sched/fair.c 5425行目

この 1ms が定義された行に続く 2 行もあとで説明に使うので引用しています。

さて、実際に 1ms 引いて返しているところはというと、 __return_cfs_rq_runtime関数です。ここで残っている実行時間から1msを引いて、実行時間(クォータプール)に足しています(つまり戻している)。

static void __return_cfs_rq_runtime(struct cfs_rq *cfs_rq)
  :(snip)
    s64 slack_runtime = cfs_rq->runtime_remaining - min_cfs_rq_runtime;

kernel/sched/fair.c 5474行目

最初に返却する実行時間を計算していますが、ここで残っている実行時間からさきほどの min_cfs_rq_runtime を引いています。

static void __return_cfs_rq_runtime(struct cfs_rq *cfs_rq)
  :(snip)
    if (cfs_b->quota != RUNTIME_INF) {
        cfs_b->runtime += slack_runtime;

        /* we are under rq->lock, defer unthrottling using a timer */
        if (cfs_b->runtime > sched_cfs_bandwidth_slice() &&
            !list_empty(&cfs_b->throttled_cfs_rq))
            start_cfs_slack_bandwidth(cfs_b);
    }

kernel/sched/fair.c 5483行目

実際に返す処理は cfs_b->runtime += slack_runtime の部分で、1ms 引いて計算した値を構造体の実行時間(クォータプールですな)に足しています(たぶん)。つまり戻しているということです。

そして、その後の実際の返却処理であるstart_cfs_slack_bandwidth関数に飛ぶ前にはcfs_b->runtime > sched_cfs_bandwidth_slice()という条件判断があります。これは返却した後のクォータプールの残量がスライスより大きいかを判断しています。クォータはスライス単位でCPUに割り当てますので、当然返却してもクォータプールにスライス以下しか残量がなければCPUへの割り当てが行えません。

list_emptyで調べているのは、CPUで実行したいためにキューに入ってるタスクがあるかどうかを見てるのでしょうか(しらんけど)。

そして、実際に返す処理は呼び出している start_cfs_slack_bandwidth 関数です。

static void start_cfs_slack_bandwidth(struct cfs_bandwidth *cfs_b)
{
    u64 min_left = cfs_bandwidth_slack_period + min_bandwidth_expiration;

    /* if there's a quota refresh soon don't bother with slack */
    if (runtime_refresh_within(cfs_b, min_left))
        return;

    /* don't push forwards an existing deferred unthrottle */
    if (cfs_b->slack_started)
        return;
    cfs_b->slack_started = true;

    hrtimer_start(&cfs_b->slack_timer,
            ns_to_ktime(cfs_bandwidth_slack_period),
            HRTIMER_MODE_REL);
}

kernel/sched/fair.c 5455行目

5460 行目で、この時点で期間の残り時間が min_left 以内かどうかを調べています。min_left 以内であれば、返却したところで、再割り当てしても実行時間が残っていません。 この min_left は 5457 行目で計算していますが、この値は cfs_bandwidth_slack_period + min_bandwidth_expiration で 5+2 の 7ms です。

この 5ms は、cfs_bandwidth_slack_period で定義されており、割り当てられた CPU でクォータが使われない間待つ時間です。5ms CPU で何も実行されない場合返却しよう、ということです。

ここに 2ms 足しているのは、返却してから期間終了までが 2ms 以内しかない場合は実行時間がない(返しても効果がない)ということではないかと思われます(たぶん)。2ms は min_bandwidth_expiration で定義されています。コメントにも minimum remaining period time to redistribute slack quota なんで返却を行うのは、これ以上期間が残っているときだけにしようということだと思います。

ここで、先にIndeed ブログの引用として「タイマーは7msに設定」と書いてあったのは、7ms ではなく、実際は 5ms ではないかなと思います。実際に hrtimer_start でタイマーを設定しています(たぶん)が、ここに設定されているのは cfs_bandwidth_slack_period つまり 5ms です。

技術書典 15 で Linux Container Book (3) セキュリティ編を出しました

前回に引き続き、技術書典 15 でサークル名 lxc-jp として出展しました。

いつもながら何の飾りっ気もないブース

今回は、"Linux Container Book" の (1)、(2) に続き、「(3) セキュリティ編」を新刊として出しました。

techbookfest.org

Linux Container Book (3) セキュリティ編

第1巻、第2巻に続き、gihyo.jp での連載、「LXCで学ぶコンテナ入門 -軽量仮想化環境を実現する技術」をベースに書いていることには違いはありません。

ただ、今回の第3巻については、私自身は少し違う感覚で制作しました。

第1,2巻については、Namespace、cgroup v1について、基本的な機能についてはなるべく網羅的に取り上げるようにしました。

しかし、セキュリティに関しては、

  • すべて取り上げるとキリがない
  • 私自身に本として書く知識が十分でない機能が多い
  • コンテナで使うセキュリティ機能をすべて連載で取り上げていないのでベースとなる記事がない

という理由で、網羅的に仕上げるために新たに書き起こすことはせずに、あくまで連載をベースに、私が興味を持ったセキュリティ機能を中心に紹介するという考えで本を制作しました。

そういう理由で、単独の章としては

  • ケーパビリティ
  • Seccomp

しかありません。しかも、Seccomp は Seccomp Notify 機能を紹介するのが主眼の記事がベースですので、Seccomp 全体を詳細に説明しているわけではありません。

商業出版ではなく、同人誌として出していますので、私の興味を持った機能を中心に紹介する本であっても問題ないであろうという考えです。それに加えて、最近は「コンテナセキュリティ」というタイトルの本が複数商業出版として出版されており、網羅的な内容はそちらに任せられるという状況もありました。

もちろん、それだけではあまりにも偏りすぎると思ったので、第3章で、コンテナでよく使われるセキュリティ機能をひととおり紹介しています。ただ、概略であって、それぞれの機能を詳細に深掘りしているわけではありません。

ひとつ心配なのは、オフラインの際に「第1巻、第2巻が良かったので」ということで第3巻をお買い上げいただいた方が結構いたのですが、第1巻、第2巻と同様の網羅的な内容を期待してご購入いただいた方がいれば、期待はずれになってしまわないかな?というところです。

オフライン出展

11/12(日)のオフライン会場にも出展しました。

今回は、masami256 さんとの共著である「cgroupの歩き方」を出すためのサークルと隣同士でした。

techbookfest.org

かなりの方にブースを訪れていただきました。ありがとうございました。

じっくり見本誌を取っていただく方が結構いらっしゃいました。もちろん、全員がその場で購入いただけるわけではないのですが、じっくり見本誌をご覧いただくだけでも非常にうれしいことでした。ありがとうございました。

そして、「第1巻、第2巻が良かったので今回も買います」と言っていただく方が結構いらっしゃり、非常にうれしかったです。中には見本誌を全く見ずに「前のやつが良かったので」ということでご購入いただく方もいて、書いててよかったと思える瞬間でした。第3巻でがっかりしてませんように…

さらに、第1〜3巻すべてをセットでご購入いただく方もいて、これはまたうれしい誤算でした。1、2 巻については、今回はそれほど売れるとは思っていなかったので。

コンテナの基本を学びたいということで、第1巻をお買い上げいただいた方も結構いらっしゃいました。やっぱり技術者として使っている技術を理解したいという方がそれなりの割合でいるのだなと感じた瞬間でした。おかげさまで、今回も第1巻は結構な冊数が売れました。昨年(2022年)、技術書典13で初めて売って以来、今回で累計 400 冊以上お買い上げいただいています。

そして、さらにうれしかったのが、すでにお買い上げいただいた方が、「良い本なので会社の同僚・後輩に買っていく」「会社の先輩へのおみやげとして買っていく」などと以前すでに買っていただいている同じ本の2冊目をお買い上げいただいたことが何度かあったことです。本当にありがとうございました。こうやって草の根で広まっていくのは、商業誌に比べると広がるスピードや範囲は小さい・狭いのでしょうが、こういう動き自体がうれしいものです。

これを励みに続編も書いていきたいと思います。次回も網羅的ではなく、特定の機能にフォーカスを当てた内容になると思いますが、引き続きよろしくお願いいたします。

お買い上げいただいた方のエンジニアライフがより充実すること、コンテナの要素技術に興味を持っていただく方が増えて、一緒に技術について語ったり、新しい技術を教えていただける方が出てくることを期待しています。

今回お買い上げいただいた方、立ち寄って手にとっていただいた方、色々とお話や質問をしていただいた方、ありがとうございました。

そして、技術書典運営の方々、ありがとうございました&おつかれさまでした。

LXD の fork "Incus"

tenforward.hatenablog.com

先日、LXD の Canonical 社への移行について書きました。その後特に何も追っかけてなかったのですが、突如 linuxcontainers.org 以下に新しいページが作成されて気づきました。

Incus は完全な新規プロジェクトではありません。 Aleksa Sarai が作成した LXD のフォークです。

とのことです。Incus のメンテナーも LXD を作成したメンバーが名を連ねています。

さて、今後はどうなるでしょうね。

一応、lxc-jp プロジェクトとしては、linuxcontainers.org の各プロジェクトのページの翻訳はしていこうという考えですので、Incus についても現在あるコンテンツは日本語ページを作成しました。(distrobuilder の翻訳が完全ではないけど)

LXDのCanonical社への移行とlxc-jpでのLXD関連の翻訳について

私が主に翻訳で関わってきた linuxcontainers.org のプロジェクトであった LXD が、プロジェクトから Canonical 配下に移動しました。

MLや公式ページでアナウンスされています。

While the team behind Linux Containers regrets that decision

ここの表現は何か微妙な表現になってる気がしますね。

これまでプロジェクトを引っ張ってきた Stéphane Graber さんの Canonical 社からの退職を機に決定されたようです。このあたりのことについては、彼の個人ブログにも書かれています。

プロジェクトの "regrets" という表現も気になりましたが、ここではさらにまあ色々あるんだろうなというのを伺わせる記述です。

アナウンスにもありますが、これにより GitHubリポジトリcanonical 配下へ移動したり、フォーラムも Ubuntu のフォーラムに移行したり、ホームページも ubuntu.com 以下に移動したりしています。


さて、lxc-jp プロジェクトとして、これまで LXD 関連の日本語訳を行ってきましたが、こちらも影響を受けます。

  • プロジェクト公式ページであった https://linuxcontainers.org/lxd の翻訳は終了します
    • そもそも移行先の ubuntu.com には従来の LXD ページの痕跡がほぼなく、リリースアナウンスなども存在してないように見えるので翻訳の余地がないように見えます
  • LXD の公式ドキュメントの日本語訳を readthedocs で公開していますが、今後の翻訳は行いません。ただし、これまでのコンテンツの公開は継続しますので、現時点までの成果は今後もご覧いただけます
    • これは、主に翻訳を行っていた @hnakamur2 さんと相談の上決めました
  • Weblate で行っていた lxc コマンドのカタログ翻訳は私が個人的に継続するかもしれません

lxc-jp の次の作業は継続して行います。ご安心ください。


個人的な思いですが、結構苦労して翻訳したコンテンツが予告なくばっさり消されたのはちょっと残念に思います。まあ仕方がないことですが。

LXDについては、最近は「ずっと翻訳してるから」という理由で翻訳していたところはあります。翻訳を始めた当初は、結構コンテナ関連のトレンドを追っかけるにはLXC/LXDの情報を追っかけるのが一番!という感じのところはありましたが、最近はLXDもコンテナを動作させるプラットフォームとしては成熟しており、コンテナそのものよりはIaaS的な機能の充実に力を入れていたので、個人的な技術的興味からは離れていっており、これを翻訳する機会がなくなったからといって、残念に思うことはあまりありません。

LXDのドキュメントについては 2018 年ごろから、linuxcontainers.org の LXD 関連コンテンツは 2014 年とかから翻訳していたようなので、まあそういう面の寂しさはありますがね。

LXD が Canonical に移行することについては、まあそういうこともあるよねって感じですね。特にプロジェクトメンバーだったわけでもないので、そこまでなにかの思い入れがあるわけでもなく、淡々と受け入れている感じです。

技術書典 14 で Linux Container Book (2) cgroup v1 編を出しました

出展ブース

技術書典 13で Linux コンテナで使う Namespace と Network の機能を紹介する本を出しました。

tenforward.hatenablog.com

それに続く第 2 巻を技術書典 14 で出し、オフライン出展もしてきました。第 2 巻は "cgroup v1編" です。

techbookfest.org

オフラインでは、たくさんの方に手に取っていただきました。ありがとうございました。手にとっていただくだけでなく、お買い上げ頂いた方、ありがとうございました。今回はうまく流量制限されていて、いい感じの流量で人が流れていく感じで、ブースに来てくださった方とゆっくりお話しながら、大体常に人がブース前にいる感じで良かったです。

(1)を読んで良かったと言ってくださる方や、あらかじめツイートなどで見て良さそうと思い買おうと思って来ましたと言ってくださる方がいて、本当に書いてよかったと思える瞬間でした。そして知らずにブースに来て見本誌を見ていただいて買ってくださったっぽい方もいて、本当にありがとうございました。

今回の新刊は、gihyo.jp で連載している「LXCで学ぶコンテナ入門 -軽量仮想化環境を実現する技術」の序盤、第 3 ~ 5 回をベースにしています。加えて、第 30 回(pidsコントローラー)、第 1 巻では唯一取り上げなかった "cgroup Namespace" の回である第 34 回、第 48 回(miscコントローラー)の内容も加えています。

gihyo.jp gihyo.jp gihyo.jp gihyo.jp gihyo.jp gihyo.jp

さらには blkio コントローラーの章は、連載の最新記事から一部抜粋して説明を載せています。ただ、52 回は v2 の機能を紹介する回なので、文章自体は大幅に書き換えてますが。

gihyo.jp

序盤はそれこそ Ubuntu 14.04 とかの実行例が載っていたので、それはすべて新しいの Ubuntu 20.04 に…、と言いたい所ですが、cgroupfs のマウントなんかから説明したかったので、基本はすべて Alpine 3.17 の環境で実行し、一部 Ubuntu 20.04 の実行例も載せています。(なんで20.04? って思われる方いるかもしれませんが、22.04 は v1 ではなく v2 が使われています)

記事原稿があるから楽勝、と思ってたのですが、連載序盤は結構飛ばして説明してたので、色々ちゃんと説明し始めると、結局原稿はベースにはしたものの、ほぼ書き下ろしと言えるくらいに原型を留めていません。連載では説明していないことも多数追加しました。

v2 が主流となる中、今更の v1 ですが、色々試すのは簡単ですし、操作の基本的なところは共通するところも多いので、ぜひ本書で cgroup について学んでいただきたいと思います。

販売開始後は正誤表なんかもここに追加していきたいと思います。