近年、KubernetesやDockerなどのコンテナ関連のシステムは多くのPJで利用されています。
特にWeb業界においては必須知識と言っても過言ではないと思います。
ただ、その裏側の仕組みを理解するためには、Linuxカーネルの知識が不可欠です。
僕自身、度々Linuxカーネルの知識不足で苦労したので、改めてLinxuカーネルの学習をしました。
この記事ではコンテナ技術を中心にLinuxカーネルについて書いていきます。
なお、僕は以下の本で学習しました。多くのスクリプトが提供されていて、試しながら学ぶことができるので、結構楽しいです。
[試して理解]Linuxのしくみ ―実験と図解で学ぶOS、仮想マシン、コンテナの基礎知識【増補改訂版】
この記事ではほんの一部しか紹介できないので、気になった方はぜひ書籍を読んでみて下さい。
また、この記事の執筆においては、ChatGPT 4も利用しています。
大まかな概要については、ChatGPT 4を利用し、細かな内容については、書籍で学習した内容を説明します。
まず、Linuxカーネルについて基本的な概念を記載します。
Linuxカーネルは、Linuxオペレーティングシステムの中核部分で、ハードウェアとソフトウェア間の通信を制御するものです。
もう少し噛み砕いて説明すると、プロセス間でCPUやメモリなどのシステムリソースを公平に使えるように分配してくれるまとめ役のような存在です。
具体的には、Linuxカーネルは主に次の4つの主要なサブシステムで構成されています。
プロセス管理は、タスクのスケジューリングと実行、メモリ管理、プロセス間通信などを含みます。
Linuxカーネルは、実行中のプロセスにCPU時間を割り当て、他のリソースを管理します。
1つの論理CPU上で同時に実行できるプロセスは1つだけなので、それぞれのプロセスが公平にCPUを利用できるように管理してくれているわけですね。
メモリ管理システムは、物理メモリと仮想メモリの管理を担当します。
これにより、プロセスは安全で効率的な方法でメモリを利用できます。
使っていないメモリを解放したり、ディスクに退避(スワップアウト)したりして効率的に使ってくれるわけですね。
これらはハードウェアとカーネル間のインターフェースを提供します。
ハードウェアデバイス(ネットワークアダプター、ストレージデバイス、ディスプレイなど)は通常、デバイスドライバを通じて操作されます。
各プロセスが好き勝手にデバイスを操作すると予期せぬ動作を起こす可能性があるので、カーネルがデバイスへのアクセスを代行してくれるわけですね。
システムコールは、ユーザースペースからカーネルスペースに機能を要求するためのインターフェースです。
これにより、アプリケーションはファイルの操作、ネットワーク接続の確立、プロセスの作成など、オペレーティングシステムのサービスを利用できます。
前述したように、プロセス管理、メモリ管理、ハードウェア操作はカーネルが担っているので、プロセスがこれらをカーネルに依頼できるようにインターフェスが用意されているわけですね。
コンテナとは、アプリケーションとその依存関係をパッケージ化し、統一されたランタイムを提供する技術です。
これにより、開発者は様々な環境間でコンシステントなアプリケーションの動作を確保できます。
具体的には、「それは私のマシンでは動作します(なぜか本番サーバーでは動作しませんが)」という問題を解消します。
Linuxカーネルの2つの主要な機能、cgroupsとnamespacesが、コンテナ技術を支えています。
プロセスのリソース使用を制限し、管理するためのもので、メモリ、CPU、ディスクI/Oなどのリソースを制御できます。
これにより、特定のプロセスがシステム全体のリソースを使い果たすことを防ぎます。
cgroupでは、リソースごとに、コントローラというカーネル内プログラムが存在し、それぞれのリソースを制御します。
具体的には、以下のようなコントローラがあります。
コントローラ名 | 制御するリソース | 説明 |
cpuコントローラ | CPU | 単位時間あたりのCPU使用時間など |
memoryコントローラ | メモリ | メモリ使用量やOOM killerの影響範囲など |
blkioコントローラ | ブロックI/O | ストレージI/Oの帯域など |
ネットワークコントローラ | ネットワークI/O | ネットワークI/Oの帯域など |
例えば、DockerやKubernetesでは、Kubernetesのマニフェストファイルにリソースの情報を書いたり、dockerコマンドの引数に、コンテナに与えるリソースを書いたりすることで、リソース制御していますが、内部的にはカーネルのcgroupを使っています。
プロセスが見ることができるシステムリソースのビューを制限します。
これにより、それぞれのプロセス(またはプロセスグループ)が自分自身の独立した環境を持つかのように感じることができます。
これがコンテナの独立性を実現します。
namespacesには以下のようなものがあります。
以下は[試して理解]Linuxのしくみ ―実験と図解で学ぶOS、仮想マシン、コンテナの基礎知識【増補改訂版】から抜粋した図解です。
カーネルはオペレーティングシステムの一部であり、ハードウェアと直接通信する役割を果たします。
一方、オペレーティングシステムは、カーネルに加えて、ユーザーとアプリケーションがシステムと対話するために必要な他のすべてのソフトウェアコンポーネントを含みます。
物理CPU(または物理プロセッサ)は、コンピュータシステムのマザーボードに物理的に存在するマイクロプロセッサです。
これは、一つまたはそれ以上のコアを持つ可能性があり、それぞれのコアは個々の計算タスクを実行する能力を持っています。
一方、論理CPU(または論理プロセッサ)は、物理プロセッサ内の個々のコア、またはそれらの一部に対するオペレーティングシステムの抽象化です。
論理CPUは、ハイパースレッディング(または同様の技術)が有効な場合、物理コアごとに複数存在することがあります。
物理メモリは、コンピュータのRAM(Random Access Memory)を指します。
仮想メモリは、物理メモリが不足した場合に使用されるディスク上の空間(スワップ領域)を指します。
カーネルは使用されていないメモリページ(一時的にアクセスが不要なデータ)をディスクにスワップアウト(移動)することで、RAMを空けることができます。
ただし、仮想メモリは物理メモリよりもアクセス速度が遅いので、頻繁にディスクにスワップするとパフォーマンスが大幅に低下する可能性があります。これをスラッシングと呼びます。
オペレーティングシステムの設計では、主にセキュリティと安定性のために、メモリ空間は通常、ユーザースペースとカーネルスペースの2つに分割されます。
カーネルスペースは、カーネル(OSの中核部分)が存在し、実行される領域です。
カーネルスペースで実行されるコードは、ほとんどのリソースに対してフルアクセス権を持ち、強力な操作を行うことができます。
ユーザースペースは、カーネル以外のすべてのプログラムが実行される領域です。
ユーザースペースのプログラムは、限られた権限しか持たず、ハードウェアリソースに直接アクセスすることはできません。
これらのプログラムは、システムコールと呼ばれる特定のインターフェースを介してカーネルの機能を利用します。
この2つの領域の分離は、システムの安定性とセキュリティを保つために重要です。
ユーザースペースのプログラムが不適切な操作を行った場合でも、その影響はそのプログラムまたはそのプロセスに限定され、システム全体への影響を防ぎます。
また、悪意のあるプログラムやウイルスがシステム全体を侵害することも防ぎます。