LoginSignup
8
4

More than 1 year has passed since last update.

コンテナの基盤技術 Namespace 入門

Last updated at Posted at 2022-05-01

コンテナは Namespace と cgroups という Linux の仕組みで構成されます1

コンテナ毎にプロセスやファイルシステムを分離したり、
CPU やメモリなどのリソースを分け合えたりできるのは、これらの技術のおかげです。

この記事では、Namespace を取り上げ、概要説明を交えつつ、実際の動きにも触れてみます。

概要

コンテナを複数立てたとき、プロセスはコンテナ間で分離されます。
コンテナ A で動くプロセスは、コンテナ B では見ることはできません。
同様にファイルシステムにおいても、コンテナ A からコンテナ B のファイルを見ることはできません。

これを実現しているのが Namespace という機能です。
Docker がコンテナを作成すると、下記のような Namespace を作成します。

  • IPC Namespace
  • Mount Namespace
  • Network Namespace
  • PID Namespace
  • User Namespace
  • UTS Namespace

以下では、ubuntu のドキュメント2を参考に Namespace を掘り下げてみます。

Namespace とは

名前空間は、 グローバルシステムリソースを抽象化層で覆うことで、 名前空間内のプロセスに対し
て、 自分たちが専用の分離されたグローバルリソースを持っているかのように見せる仕組みであ
る。 グローバルリソースへの変更は、 名前空間のメンバーである他のプロセスには見えるが、 そ
れ以外のプロセスには見えない。 名前空間の一つの利用方法はコンテナーの実装である。

「グローバルシステムリソース」がホストマシンが持つリソースのことになります。
Namespace という”部屋”を分けることによって、互いに独立したリソースを持つことができます。

これはシェアハウスのイメージに近いかもしれません。
互いの部屋の壁が厚いので、まるで一軒家と勘違いしてしまうような感じです。

Namespace の種類

Namespace 分離対象
IPC System V IPC, POSIX メッセージキュー
Network ネットワークデバイス、スタック、ポートなど
Mount マウントポイント
PID プロセス ID
User ユーザー ID とグループ ID
UTS ホスト名と NIS ドメイン名

中でも PID は直感的に理解しやすい Namespace かと思います。
コンテナ上のプロセスは基本的に PID1 が振られますが、コンテナ A でも コンテナ B でも PID1 を利用できます。
Namespace が別であるため、通常一意であるべき PID が重複していても問題がないわけです。

Namespace を操作する API

システムコール 内容
clone(2) プロセスを生成し呼び出し元プロセスと同じ Namespace に所属させる。フラグを使って新しい Namespace を作成することもできる。
setns(2) 呼び出したプロセスを指定した既存 Namespace に参加させることができる。
unshare(2) 呼び出したプロセスを新しい Namespace に移動する。

Namespace の確認方法

/proc/[pid]/ns/ ディレクトリを見に行くことで、
現在のプロセスが所属している Namespace を確認できます。

$ ls -l /proc/$$/ns
total 0
lrwxrwxrwx. 1 mtk mtk 0 Jan 14 01:20 ipc -> ipc:[4026531839]
lrwxrwxrwx. 1 mtk mtk 0 Jan 14 01:20 mnt -> mnt:[4026531840]
lrwxrwxrwx. 1 mtk mtk 0 Jan 14 01:20 net -> net:[4026531956]
lrwxrwxrwx. 1 mtk mtk 0 Jan 14 01:20 pid -> pid:[4026531836]
lrwxrwxrwx. 1 mtk mtk 0 Jan 14 01:20 user -> user:[4026531837]
lrwxrwxrwx. 1 mtk mtk 0 Jan 14 01:20 uts -> uts:[4026531838]

実際に触ってみる

試しに PID Namespace を分離してみます。

環境準備

Linux 環境を用意します。
今回は ubuntu の公式イメージを利用します。

$ docker pull ubuntu
$ docker run --privileged --name ubuntu -it ubuntu /bin/bash # 特権モードで起動

現在のプロセスの状態を確認してみる

bash が起動したら、現在のプロセスが所属している Namespace を確認します。
/proc/self/ns 配下のファイルを参照して確認できます。

$ cd /proc/self/ns
$ ls -la
total 0
dr-x--x--x 2 root root 0 May  1 06:04 .
dr-xr-xr-x 9 root root 0 May  1 05:46 ..
lrwxrwxrwx 1 root root 0 May  1 06:04 cgroup -> 'cgroup:[4026531835]'
lrwxrwxrwx 1 root root 0 May  1 06:04 ipc -> 'ipc:[4026532376]'
lrwxrwxrwx 1 root root 0 May  1 06:04 mnt -> 'mnt:[4026532374]'
lrwxrwxrwx 1 root root 0 May  1 06:04 net -> 'net:[4026532379]'
lrwxrwxrwx 1 root root 0 May  1 06:04 pid -> 'pid:[4026532377]'
lrwxrwxrwx 1 root root 0 May  1 06:04 pid_for_children -> 'pid:[4026532377]'
lrwxrwxrwx 1 root root 0 May  1 06:04 time -> 'time:[4026531834]'
lrwxrwxrwx 1 root root 0 May  1 06:04 time_for_children -> 'time:[4026531834]'
lrwxrwxrwx 1 root root 0 May  1 06:04 user -> 'user:[4026531837]'
lrwxrwxrwx 1 root root 0 May  1 06:04 uts -> 'uts:[4026532375]'

pid に注目すると 4026532377 という Namespace に所属していると分かります。

続いて、現在のプロセスを確認します。

$ ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 05:46 pts/0    00:00:00 /bin/bash
root        15     1  0 06:05 pts/0    00:00:00 ps -ef

PID1 で /bin/bash が動いています。

PID Namespace を分離してみる

次に、PID Namespace を分離してみます3
分離には unshare4 という新しい Namespace を作るためのコマンドを使用します5

$ unshare --pid --fork --mount-proc sh

先程と同様に Namespace を確認します。

$ ls -la /proc/self/ns
total 0
dr-x--x--x 2 root root 0 May  1 06:04 .
dr-xr-xr-x 9 root root 0 May  1 06:04 ..
lrwxrwxrwx 1 root root 0 May  1 06:04 cgroup -> 'cgroup:[4026531835]'
lrwxrwxrwx 1 root root 0 May  1 06:04 ipc -> 'ipc:[4026532376]'
lrwxrwxrwx 1 root root 0 May  1 06:04 mnt -> 'mnt:[4026532455]'
lrwxrwxrwx 1 root root 0 May  1 06:04 net -> 'net:[4026532379]'
lrwxrwxrwx 1 root root 0 May  1 06:04 pid -> 'pid:[4026532456]'
lrwxrwxrwx 1 root root 0 May  1 06:04 pid_for_children -> 'pid:[4026532456]'
lrwxrwxrwx 1 root root 0 May  1 06:04 time -> 'time:[4026531834]'
lrwxrwxrwx 1 root root 0 May  1 06:04 time_for_children -> 'time:[4026531834]'
lrwxrwxrwx 1 root root 0 May  1 06:04 user -> 'user:[4026531837]'
lrwxrwxrwx 1 root root 0 May  1 06:04 uts -> 'uts:[4026532375]'

すると、pid は 4026532456 となっており、さっきと異なっていることが分かります。

プロセスも見てみましょう。

$ ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 06:04 pts/0    00:00:00 sh
root         3     1  0 06:05 pts/0    00:00:00 ps -ef

すると、こちらでは PID1 で sh が動いており、元々あった bash が見えなくなっています。

以上から、PID Namespace を分離することで

  • 別 Namespace のプロセスが見えないこと
  • Namespace 間で PID を重複して利用できること

の2点を確認できました。

  1. https://github.com/docker/docker.github.io/blob/v17.06-release/engine/docker-overview.md#the-underlying-technology

  2. http://manpages.ubuntu.com/manpages/jammy/ja/man7/namespaces.7.html

  3. 付属オプションはこちらを参考にしました。

  4. http://manpages.ubuntu.com/manpages/jammy/ja/man2/unshare.2.html

  5. docker run に --privileged を付けて特権モードで起動したのはこのコマンドを打つためです。

8
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
4