【実務・中級編】 Linux Namespacesの種類(PID、Network、Mount、IPC、UTS、User、Cgroup)とコンテナ分離機構 – クラウドインフラと仮想化ネットワーク実践ガイド

コンテナは「魔法」ではない。Linux Namespacesを解剖してインフラの解像度を上げる

DockerやKubernetesを扱うとき、私たちはしばしば「コンテナという隔離された小宇宙」の中にいるような錯覚に陥ります。しかし、コンテナの本質は仮想マシン(VM)のようなハードウェアの抽象化ではありません。

コンテナの正体は、Linuxカーネルが提供する「プロセスの見え方を制限する機能」――すなわち、Linux Namespaces です。

今日は、クラウドエンジニアとして避けては通れない、この「隔離の正体」について、実務の現場でトラブルシューティングに役立つ視点から深掘りしていきましょう。

—

1. Linux Namespaces:プロセスを「箱」に閉じ込める仕組み

Namespaceは、カーネルからプロセスに対する「視界の制限」です。あるプロセスから見て「システム全体がどう見えているか」を切り替えることで、あたかも自分専用のOSを使っているかのように錯覚させます。

現在、主要なNamespaceは以下の7種類です。

  • PID (Process ID): PID空間を隔離。コンテナ内のPID 1は、ホストから見ると別の大きな整数値です。
  • Network (net): ネットワークスタック(IP、ルーティングテーブル、iptables等)を隔離。
  • Mount (mnt): マウントポイントのツリーを隔離。chrootの究極進化版です。
  • IPC (Inter-Process Communication): 共有メモリやメッセージキューの隔離。
  • UTS (UNIX Time-sharing System): ホスト名とドメイン名を隔離。
  • User: UID/GIDを隔離。ホストのroot権限をコンテナ内のrootと切り離せます(重要!)。
  • Cgroup: リソースの隔離。厳密にはNamespaceではありませんが、Namespaceとセットで語られるべき「計算資源の制限」機能です。

—

2. 現場で役立つ実践:unshareコマンドで「手作りコンテナ」を覗く

「コンテナの中はどうなっているのか?」を知る一番の近道は、自分で名前空間を分離してみることです。unshareコマンドを使えば、新しいNamespaceを持つプロセスを簡単に生成できます。

まずは、ネットワークとUTSを分離したシェルを立ち上げてみましょう。

# --netでネットワークを分離、--utsでホスト名を分離してシェルを起動
sudo unshare --net --uts /bin/bash

# 起動したシェルの中で確認
hostname my-private-container
ip addr

このとき、ip addrを実行しても、ループバックインターフェース(lo)しか表示されません。外の世界(ホスト側のNIC)からは完全に遮断されています。これが、KubernetesのPodが「空っぽのネットワーク」から始まる理由です。

—

3. Network NamespaceとWeb APIのリアル

Web APIの開発において、最も重要なのは Network Namespace です。

KubernetesのPod内部で curl を叩いたとき、パケットはどのように流れるのか?

1. PodのNetwork Namespace: パケットが生成される。ルーティングテーブルを見て、デフォルトゲートウェイへ送る。
2. veth pair: パケットは「仮想ケーブル」を通ってホスト側のNamespaceへ飛び出す。
3. Bridge / CNI: ホスト側のブリッジ(cni0など)が受け取り、iptables/IPVSのルールに従ってルーティングされる。
4. NIC: 最終的に物理NICから外の世界へ飛んでいく。

この途中で「パケットが消える」トラブルは、多くの場合、Namespace間を繋ぐ veth pair や、ホスト側の iptables の設定ミスに起因します。

Pythonによる疎通確認のヒント

コンテナ内で特定のポートが空いているか確認する際、requestsライブラリなどを使う前に、まずは低レイヤーのソケット接続を確認するのがSREの鉄則です。

import socket

# 特定のNamespace内で動いている想定の接続テスト
def check_api_connectivity(host, port):
    try:
        # ソケットを作成して接続確認
        with socket.create_connection((host, port), timeout=2) as sock:
            print(f"Connection to {host}:{port} successful!")
    except Exception as e:
        # ここでConnectionRefusedなのかTimeoutなのかを判別する
        print(f"Failed to connect: {e}")

# 実行
check_api_connectivity("127.0.0.1", 8080)

—

4. トラブルシューティングの極意:ホストからコンテナを覗く

コンテナの中で「ネットワークが遅い」「パケットが届かない」という時、コンテナ内から tcpdump を打つのは最終手段です。コンテナには tcpdump が入っていないことも多いからです。

そんな時は、ホスト側で対象プロセスのNamespaceに入り込んでキャプチャしましょう。

# 1. コンテナのPIDを特定
PID=$(docker inspect -f '{{.State.Pid}}' <container_name>)

# 2. そのPIDのNetwork Namespaceでtcpdumpを実行
sudo nsenter -t $PID -n tcpdump -i eth0 port 80

nsenter を使うと、既存のプロセスが所属するNamespaceに「飛び込む」ことができます。これは、KubernetesのPod内に入らずにデバッグする際、現場で最も重宝するスキルの一つです。

—

まとめ:魔法を解き明かす力

コンテナは、Linuxカーネルという巨大なOSの上に築かれた「プロセスの見え方の調節」に過ぎません。

  • Namespace: 何が見えるか(視界)
  • Cgroup: どれだけ使えるか(リソース量)

この2つを理解していれば、KubernetesのPodが CrashLoopBackOff を起こした時や、サービスメッシュでパケットが迷子になった時も、慌てる必要はありません。どこで視界が遮断され、どこでリソースが枯渇しているのか、論理的に追いかけることができるはずです。

次回の運用では、ぜひ unshare や nsenter を使って、その「隔離の壁」を自分の目で確かめてみてください。それが、真のクラウドエンジニアへの第一歩です。

コメント

タイトルとURLをコピーしました