こんにちは!日々のインフラ運用やコンテナのオーケストレーション、本当にお疲れ様です。SREとして現場を走り回っていると、夜中に突然「なんだか特定のコンテナだけやけに動作が重いぞ……」「OOM Killer(メモリ不足による強制終了)が発動してアプリが落ちまくっている!」といったアラートに直面して、冷や汗をかいた経験はありませんか?
私たちが普段何気なく使っているDockerやKubernetesといったコンテナ技術の裏側では、実はLinuxカーネルの強力な機能が、限られたサーバーの資源(CPUやメモリ、ディスク)を奪い合いにならないよう、見えないところで交通整理を行っています。
今回は、そのコンテナの「リソース管理の心臓部」である Linux Cgroups(Control Groups v1とv2) の世界へ、一歩ずつ優しくご案内します!難しいカーネルのソースコードは一旦脇に置いて、私たちの身近な世界に置き換えて紐解いていきましょう。
—
1. なぜコンテナには「リソース制限」が必要なのか?
まずは、Cgroupsがなぜ必要なのかをイメージしてみましょう。
例えば、1つの大きなマンション(1台の物理Linuxサーバー)の中に、何世帯もの家族(複数のコンテナやプロセス)が暮らしていると想像してください。もし、ある一つの部屋の住人が、水道も電気も使い放題、リビングのテレビの音量も限界突破の爆音で生活し始めたらどうなるでしょうか? 同じマンションの他の住人は、お風呂の水が出なくなったり、眠れなくなったりして大迷惑ですよね。
インフラの世界でも全く同じことが起きます。もしリソースの制限をかけないと、暴走したバッチプログラムがサーバーのメモリをすべて食いつぶしてしまい、同じマシン上で動いている大切なWebサイトやデータベースまで巻き添えにしてシステム全体がクラッシュ(KernelPanicやOSのフリーズ)してしまいます。
そこで登場するのが、Linuxカーネルの機能である Cgroups(Control Groups) です。
Cgroupsは、いわばマンションの管理組合のようなものです。「A部屋は電気代の予算がここまで」「B部屋は水道をここまでしか使っちゃダメ」という風に、プロセス(住人)のグループごとに使える資源の上限をピシッと定めて管理してくれます。
—
2. 身近な例えで理解する「Cgroups」の仕組み
Cgroupsの働きを、もう少し具体的に3つのリソース(CPU・メモリ・I/O)に分けて、身近な例えで覗いてみましょう。
CPU制限:時間を上手に分け合う「タイムシェアリング」
CPUは、いわば「作業をするための時間」です。
CgroupsのCPU制限は、ストップウォッチを使って「1秒(1000ミリ秒)という時間のなかで、このグループのプロセスに作業させていいのは最大300ミリ秒までですよ」という風に割り振ります。これにより、どれだけCPUをフル稼働させたい重い処理であっても、サーバー全体のCPUを100%独占し続けることを防ぎます。
メモリ制限:物理的な「引き出しの大きさ」
メモリは、作業机の広さや引き出しの容量です。
「あなたのグループが使えるメモ帳はここまで!」と物理的な上限(容量)をビシッと決めます。もし、この決められた引き出しの容量を超えてメモを広げようとすると、Linuxカーネルの非情な監査人(OOM Killer)がやってきて、一番メモリを食っているプロセスを強制終了させます。「おっと、これ以上はオーバーだよ!」というわけですね。
I/O制限:道路の「車線数制限」
ディスクI/Oは、データを読み書きするための「配送トラックが通る道路の広さ(帯域幅)」です。
例えば、ログを大量に出力するバックグラウンドの処理が、ハードディスクへの書き込みレーンをすべて独占してしまうと、ユーザーからのリクエストを処理する肝心のデータベースが書き込み待ちでフリーズしてしまいます。CgroupsでI/O制限をかけることで、「このグループの配送トラックは、1秒間に最大10MBまでの荷物しか運んではいけない」という車線制限をかけることができます。
—
3. Cgroups v1 と v2 の違いをざっくり理解する
さて、このCgroupsには歴史があり、大きく分けて v1 と v2 という2つの世代が存在します。現場でインフラを触る上では、この違いを知っておくことが非常に重要です。
- Cgroups v1(これまで主流だったマルチツリー構造)
- CPU、メモリ、ディスクI/Oといったリソースごとに、管理する部署(階層構造のディレクトリ)がバラバラに独立していました。
- 例えるなら、「CPUの管理部署はAビル、メモリの管理部署はBビル、I/OはCビル」と役所が分かれているような状態です。そのため、複数のリソースをまとめて一人のユーザー(プロセスグループ)を管理しようとすると、設定が複雑になりがちでした。
- Cgroups v2(モダンな統合ツリー構造)
- Linuxカーネル5.x以降(現在の最新KubernetesやDocker環境の標準)でデフォルトとなっている最新の仕組みです。
- すべてのリソース管理が「ひとつの統一されたツリー構造」にまとめられました。
- 例えるなら、一つの統合窓口(総合受付)ができたイメージです。これにより、コンテナごとのリソース管理が非常に美しく、矛盾なく行えるようになりました。また、コンテナの中のプロセスが安全にメモリを使い切るための制御(Memory Pressureの検知など)も非常にスマートになっています。
—
4. 実際に触ってみよう!Cgroupsの仕組みと設定体験
百聞は一見に如かず。実際にLinux環境(今回はモダンなCgroups v2を想定)で、どのようにCgroupsが動いているのかを覗いてみましょう!
Cgroupsの設定は、特殊なコマンドを使わなくても、/sys/fs/cgroup という特別な仮想ファイルシステムを読み書きするだけで操作できます。
ステップ1: 専用の作業用グループ(ディレクトリ)を作る
まずは、自分だけの管理スペース(Cgroupディレクトリ)を作ってみましょう。
# root権限でCgroupsのルートディレクトリに移動し、新しいグループ「my_container_test」を作る
sudo mkdir /sys/fs/cgroup/my_container_test
# 作成されたディレクトリの中身を確認してみる
ls -la /sys/fs/cgroup/my_container_test
このディレクトリを作るだけで、カーネル内に新しいリソース管理の枠組みが一つ誕生します。
ステップ2: メモリの上限をガッツリ制限してみる
では、このグループに属するプロセスが使えるメモリの上限を「50MB」に厳しく制限してみましょう。
v2環境では、memory.max というファイルに上限値を書き込むだけで設定完了です。
# 最大メモリ量を 50メガバイト に設定する
echo "52428800" | sudo tee /sys/fs/cgroup/my_container_test/memory.max
# 設定が反映されたか確認
cat /sys/fs/cgroup/my_container_test/memory.max
# 出力結果: 52428800 (50MB) が返ってくれば成功です!
ステップ3: プロセスをそのグループに参加させる
次に、「今から起動するシェル(あるいは任意のプロセス)」を、先ほど作ったグループの管理下に置きましょう。
Cgroupsの管理下にプロセスを入れるには、そのプロセスのプロセスID(PID)を cgroup.procs というファイルに書き込みます。
# 現在動いているシェル自身のプロセスID(PID)を登録する
echo $$ | sudo tee /sys/fs/cgroup/my_container_test/cgroup.procs
# 本当にそのグループに入ったか、現在のシェルがどのcgroupにいるか確認してみる
cat /proc/self/cgroup
これで、現在のシェルセッション上で動かすプログラムは、すべて「メモリ50MBの制限」という厳しい監視の網の目の中に置かれました。この状態でもし50MBを超えるような巨大なデータをメモリ上に展開しようものなら、容赦なくOOM Killerの洗礼を受けることになります。
—
5. 実務におけるSRE・インフラエンジニアの知見
ここまでCgroupsの基本と簡単な手動設定を見てきましたが、実際の現場(DockerやKubernetes)では、私たちが直接 mkdir や echo を叩くことは滅多にありません。Dockerやコンテナランタイム(containerdなど)が、裏側で自動的にこのCgroupsの仕組みをゴリゴリと呼び出して、綺麗にリソースを区切ってくれています。
しかし、「裏側で何が起きているか」を知っているかいないかで、障害対応のスピードが劇的に変わります。
- 「なんだかコンテナが突然シャットダウンする……」
- そんなときは、直感で悩む前にカーネルのログ(
dmesgや/var/log/messages)を確認し、Out of memory: Kill process ...というログが出ていないか(=Cgroupsのメモリ制限に引っかかっていないか)を確認するのが第一歩です。 - 「CPUのスロットリング(制限による足踏み)が起きているか?」
- Cgroups v2では
cpu.statというファイルを見ることで、CPUの割り当て時間を使い切ってしまい、プロセスが足止めを食らった回数(nr_throttled)を正確にモニタリングできます。KubernetesのPodのCPUリクエスト/リミットのチューニングを行う際、このメトリクスは非常に強力な武器になります。
—
まとめ
いかがでしたでしょうか?
Linux Cgroupsは、一見すると難解なカーネルの専門用語の塊のように見えますが、要するに「限られたサーバー資源をみんなで公平に、かつ安全にシェアするための交通整理の仕組み」です。
- コンテナが暴走してサーバー全体を巻き添えにしないための「防波堤」であること
- CPU、メモリ、ディスクI/Oごとにそれぞれのルール(ファイル)で細かく制御できること
- モダンなLinux環境では、Cgroups v2によってよりシンプルに、スマートに管理されていること
このイメージさえ頭の中にしっかりと描けていれば、明日からKubernetesのマニフェスト(resources.limits や resources.requests)を書くときも、「おっ、いま裏側ではカーネルのこの設定が動いているんだな」と鮮明にイメージできるようになるはずです。
日々のインフラ・コンテナ運用の引き出しを増やす第一歩として、ぜひご自身の検証環境でも触ってみてくださいね。それでは、また次回の技術解説でお会いしましょう!
コメント