↓ メインコンテンツへスキップ

Namespace を使った超ミニコンテナ|ネットワーク編

·2008 文字·5 分
わて
著者
わて

この記事はコンテナ自作のネットワーク編です。
コンテナからインターネットに通信できるようにすることが目的です。

ネットワーク以外のコンテナ自作についてはこの記事をご覧下さい。

先にまとめ
#

コンテナがインターネットと通信するために必要なこと

  • コンテナのネットワークの独立
  • 仮想ケーブルの作成と設定
  • ルーティング設定(NATとかデフォゲとか)

つまずいたところ
#

しょうもないですが、コンテナ側とホスト側でターミナルを分けていたのですが、どっちのターミナルで操作するべきかがごちゃごちゃになって、しばらく足踏みしました。

またネットワークのには疎いので、なんの操作であるのかも調べながら進めるのが一苦労でした。

作業の流れ
#

今回の作業の流れはだいたいこんな感じです。

  1. コンテナの作成
  2. 仮想的なケーブルを作成
  3. ルーティング設定
  4. 通信テスト

実行環境
#

VMにいれたUbuntu で動かしました。
また、コンテナとホストで二つのターミナルを用いると楽です。
どちらのターミナルでもsudo 権限が頻繁に必要になるので、筆者はルートユーザーで行ってます。

os-release
# $ cat /etc/os-release
PRETTY_NAME="Ubuntu 24.04.5 LTS"
NAME="Ubuntu"
VERSION_ID="24.04"
VERSION="24.04.5 LTS (Noble Numbat)"
VERSION_CODENAME=noble
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=noble
LOGO=ubuntu-logo

コンテナの作成
#

ネットワークについての独立のみならnetnsコマンドでよいのですが、ゆくゆくはdocker もどきを作ってみたいとも考えているので、今回はunshareコマンドを使用します。

引数に--netとすることでネットワークを独立させることができます。

コンテナ側
# ネットワークを独立させたNamespace の作成
unshare --net bash
echo $$

# これで出力されたPIDはホスト側で使うので、どこかに記録しておきます
# 以後この値は $PID と表記します

仮想的なケーブルを作成
#

ホスト側で仮想的なケーブル(veth)を作成し、先程作ったNamespace に接続します。
このとき、ホスト側のip は192.168.10.1とします。

ホスト側
ip link add veth-host type veth peer name veth-unshare
ip link set veth-unshare netns $PID
ip addr add 192.168.10.1/24 dev veth-host
ip link set veth-host up

コンテナ側のip は192.168.10.2とします。

ループバックもセットアップします。ループバックについては今回の記事ではDOWN のままでも良いのですが、今後、コンテナで色々やっていくときに必要になりそうなので、ついででやっておきます。

コンテナ側
ip addr add 192.168.10.2/24 dev veth-unshare
ip link set veth-unshare up
ip link set lo up

ここまででこのような状態にしました↓

cable.webp

またip aで仮想環境とホストで通信をする準備ができている様子が伺えます。

以下にip aの出力結果を載せます。コンテナ側でveth-unshare@if13というヴァーチャルなケーブルが確認できます。@if13はホスト側のケーブルの番号を示していて、実際にホストの13番のケーブルはveth-host@if12であるとわかります。こちらも@if12という名前がついており、再度コンテナ側のveth を確認すると、その番号は12であるとわかります。

このように、コンテナとホストはどのケーブルで通信しているかを判断できます。これはdocker でのコンテナ・ホスト間通信でも同様です。

コンテナ側
12: veth-unshare@if13: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 26:0c:fc:68:2a:d2 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 192.168.10.2/24 scope global veth-unshare
       valid_lft forever preferred_lft forever
    inet6 fe80::240c:fcff:fe68:2ad2/64 scope link
       valid_lft forever preferred_lft forever
ホスト側
13: veth-host@if12: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state LOWERLAYERDOWN group default qlen 1000
    link/ether 2e:e1:82:cb:dd:60 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 192.168.10.1/24 scope global veth-host
       valid_lft forever preferred_lft forever

ルーティング設定
#

Linux はセキュリティのために「自分宛てじゃないパケットが届いたら破棄する」という設定があるので、まずはそれを無効にします。

コンテナ → ホスト → 外

という流れで通信するので、コンテナからホストに向けて外向けのパケットが送られます。それを破棄しない設定をします。

同時にNAT と転送ルールを設定します。

具体的には、

  • veth-host から入ってきたコンテナの通信が、外のネットへ通過を許可
  • 外のネットから入ってくる通信のうち、「コンテナが自分からリクエストした通信の返事」だけをコンテナ側へ通すことを許可
ホスト側
# 自分以外の宛先のパケットを許可
sysctl -w net.ipv4.ip_forward=1

# 外に繋がっているインターフェース名の指定
# 環境によっては wlan0 だったり eth0 だったり
OUT_IF="ens33"


# NAT(マスカレード)と転送許可ルールを設定
# コンテナ(192.168.10.2)から出ていく通信の差出人を、「ホストOSのIP」に書き換え
iptables -t nat -A POSTROUTING -s 192.168.10.0/24 -o $OUT_IF -j MASQUERADE

# コンテナ → 外のネットへの許可
iptables -A FORWARD -i veth-host -o $OUT_IF -j ACCEPT

# 外のネット → コンテナ
# 自分からリクエストした通信の返事(RELATED / ESTABLISHED)」だけ許可
iptables -A FORWARD -i $OUT_IF -o veth-host -m state --state RELATED,ESTABLISHED -j ACCEPT

コンテナ側ではデフォルトゲートウェイを設定します。ホストのipがコンテナにとってのデフォゲになるべきなので、以下のようにします。

コンテナ側
ip route add default via 192.168.10.1

通信テスト
#

コンテナ側でインターネットにpingしてみます。

コンテナ側
ping 8.8.8.8

通ったら成功です。

まとめ
#

先にまとめに書いたとおりです。

次回以降は今回と前回とで作ったミニコンテナを作る仕組みを組み合わせたり、コンテナ間通信ができるようにしたいと思います。