Linux Network Namespace(netns)でEC2のENIごとに異なるネットワーク設定
ども、大瀧です。
EC2インスタンスに追加のENI(Elastic Network Interface)を設定すると、ルートテーブルの不整合でうまく通信できなかったり、アプリケーションを追加のENI経由で通信させるのに苦戦したという経験はありませんか。自分は悶々とトラブルシューティングしていた時期があり、極力使わないのがいいなという結論をブログで吐露していました。時を経てひょんなことからnetns(Network Namespace)に触れる機会があり、追加のENIの管理と相性が良かったのでご紹介してみたいと思います。
Linux Network Namespaceとは
Network Namespace(netns)は、OSのネットワークスタック(ルーティングテーブルやiptablesを含むネットワーク設定)を分離して管理できるLinuxカーネルの機能です。古くはXenやKVMなどLinuxをハイパーバイザーのホストOSとして動作させるための仮想ネットワークの中核コンポーネントとして、最近はOpenStackやDockerでの利用が有名ですね。十分枯れた機能と見て良いと思います。
netnsを利用することで、同一のインスタンス/OS上でアプリケーションやNIC単位のネットワーク設定を持たせることができます。サーバーアプリケーションを用途ごとに複数実行するケースや、ルーティングなど複雑な設定になりがちな構成をシンプルにまとめられるのがメリットかなと思っています。
動作確認環境
- AMI/OS : ami-6be57d0d/Amazon Linux 2 RC
- インスタンスタイプ : i3.large
- 追加したENI : eth1,eth2の2つ
特にLinuxディストリビューションやEC2インスタンスタイプの制限はありませんが、インスタンスタイプによって追加のENI数の上限が異なる点にだけ留意ください。
Namespaceの作成とENIの紐づけ
Amazon Linuxでは追加したENIのネットワークインターフェースが自動で活性化、DHCPクライアントが動作するようになっています。今回はコマンドでnetnsへのインターフェースの登録や構成を行うので、追加したENIに対応するインターフェースをダウンさせます。
$ sudo ip link set eth1 down $ sudo ip link set eth2 down
インターフェース一覧を見てみましょう。
$ ip link 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT qlen 1 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc mq state UP mode DEFAULT qlen 1000 link/ether 06:d7:23:3e:db:94 brd ff:ff:ff:ff:ff:ff 3: eth1: <BROADCAST,MULTICAST> mtu 9001 qdisc mq state DOWN mode DEFAULT qlen 1000 link/ether 06:05:a2:18:8f:be brd ff:ff:ff:ff:ff:ff 4: eth2: <BROADCAST,MULTICAST> mtu 9001 qdisc mq state DOWN mode DEFAULT qlen 1000 link/ether 06:21:b2:b5:ad:16 brd ff:ff:ff:ff:ff:ff $
では、netnsを構成していきます。netnsでは、Namespaceという論理単位で前述のネットワークスタックを持ちます。今回はeth1とeth2で別のスタックとするために「blue」と「green」の2つのNamespaceを作成します *1。netnsの操作はiproute2に統合されているので、ip
コマンドで操作します。設定には/var/run/netns/
以下のファイルの書き込み権が必要なので、基本的にはsudo
コマンドなどrootの権限で操作します。
$ sudo ip netns add blue $ sudo ip netns add green
一般的なnetnsの解説ではおもむろにveth(仮想イーサネット)を作成して繋ぐというケースが多いのですが、今回はNamespaceさえ分けられれば要件としては十分なので、ENIに対応するネットワークインターフェース(eth*
)をNamespaceに直接割り当てます。blueにeth1
、greenにeth2
をアサインします。
$ sudo ip link set eth1 netns blue $ sudo ip link set eth2 netns green
図に書くとこんな感じ。
シンプルですね。ip link
コマンドで再度、インターフェースの居所を確認してみましょう。
$ ip link 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT qlen 1 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc mq state UP mode DEFAULT qlen 1000 link/ether 06:d7:23:3e:db:94 brd ff:ff:ff:ff:ff:ff $
eth1
とeth2
が姿を消し、それぞれのNamespaceに移っていることがわかります。Namespaceの状態を確認するためにはip netns exec <Namespace名> <コマンド>
で、Namespace上で任意のコマンドを実行するのが簡単です。
$ sudo ip netns exec blue ip link 1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT qlen 1 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 3: eth1: <BROADCAST,MULTICAST> mtu 9001 qdisc noop state DOWN mode DEFAULT qlen 1000 link/ether 06:05:a2:18:8f:be brd ff:ff:ff:ff:ff:ff $ sudo ip netns exec green ip link 1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT qlen 1 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 4: eth2: <BROADCAST,MULTICAST> mtu 9001 qdisc noop state DOWN mode DEFAULT qlen 1000 link/ether 06:21:b2:b5:ad:16 brd ff:ff:ff:ff:ff:ff $
各Namespaceごとにループバックインターフェースlo
とアサインしたネットワークインターフェースeth*
が確認できますね。ループバックインターフェースは初期状態だと不活性でループバックアドレスも付与されていないので、以下のコマンドで対応しましょう。blueとgreenの両方に行うので、ここでは-all
オプション(全Namespaceでのコマンド実行)で実行します。
$ sudo ip -all netns exec ip addr add 127.0.0.1 dev lo $ sudo ip -all netns exec ip link set lo up
個別のNamespace上でいろいろ試すのであればip netns exec
のコマンドにbash
を指定してシェルを起動するのが便利です。sudo
コマンドで実行するので、rootユーザーの権限でのコマンド操作になることに注意してください。
$ sudo ip netns exec blue bash #
先ほどアサインしたネットワークインターフェースを活性化しましょう。IPアドレスはDHCPもしくは静的に付与すればOKです。今回はdhclient
コマンドでDHCPを動作させます。DHCPではデフォルトルートが設定されなかったので、デフォルトルートを追加で付与しました。
# dhclient eth1 ^C # ip route add default via <ENIのデフォルトゲートウェイのIPアドレス> # ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/32 scope host lo valid_lft forever preferred_lft forever inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc mq state UP qlen 1000 link/ether 06:05:a2:18:8f:be brd ff:ff:ff:ff:ff:ff inet 172.31.31.158/20 brd 172.31.31.255 scope global dynamic eth1 valid_lft 3593sec preferred_lft 3593sec inet6 fe80::405:a2ff:fe18:8fbe/64 scope link valid_lft forever preferred_lft forever # exit $
これでblue Namespaceのインターフェースの疎通はOKです。greenも同様に対応します。
$ sudo ip netns exec green bash # dhclient eth2 ^C # ip route add default via <ENIのデフォルトゲートウェイのIPアドレス> # ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/32 scope host lo valid_lft forever preferred_lft forever inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 4: eth2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc mq state UP qlen 1000 link/ether 06:21:b2:b5:ad:16 brd ff:ff:ff:ff:ff:ff inet 172.31.19.81/20 brd 172.31.31.255 scope global dynamic eth2 valid_lft 3547sec preferred_lft 3547sec inet6 fe80::421:b2ff:feb5:ad16/64 scope link valid_lft forever preferred_lft forever
OKですね。ルーティングテーブルやNetfilter(iptables)がNamespaceごと別々になっている様子を確認します。
# ip route default via 172.31.16.1 dev eth2 172.31.16.0/20 dev eth2 proto kernel scope link src 172.31.19.81 # iptables -L Chain INPUT (policy ACCEPT) target prot opt source destination Chain FORWARD (policy ACCEPT) target prot opt source destination Chain OUTPUT (policy ACCEPT) target prot opt source destination #
今回は特に必要なかったのですが、netnsにはresolv.conf
とhosts
をNamespaceごとに/etc/netns/<Netspace名>/{resolv.conf|hosts}
というファイル名で設定することもできます。デフォルトでは/etc/resolv.conf
、/etc/hosts
を参照します。
アプリケーションの実行
では、用意したNamespaceでサーバーアプリケーションを実行してみましょう。やり方自体は、前述のip netns <Namespace名> exec
コマンドをそのまま利用します。今回はWebサーバーのNginxを実行してみました。
$ sudo amazon-linux-extras install nginx1.12 $ sudo ip -all netns exec nginx
サーバーアプリケーションの種類によって、実行するコマンドラインやオプションは調整してください。では、cURLで試してみます。
非Namespace→Namespace(ENI間通信)
$ curl -I <eth1のPrivate IP> HTTP/1.1 200 OK Server: nginx/1.12.2 Date: Wed, 17 Jan 2018 08:23:55 GMT Content-Type: text/html Content-Length: 3520 Last-Modified: Wed, 13 Dec 2017 18:36:55 GMT Connection: keep-alive ETag: "5a317347-dc0" Accept-Ranges: bytes $ curl -I <eth2のPrivate IP> HTTP/1.1 200 OK Server: nginx/1.12.2 Date: Wed, 17 Jan 2018 08:24:07 GMT Content-Type: text/html Content-Length: 3520 Last-Modified: Wed, 13 Dec 2017 18:36:55 GMT Connection: keep-alive ETag: "5a317347-dc0" Accept-Ranges: bytes $
ローカルホスト
非NamespaceではNginxを実行していないのでエラー、各Namespace上でローカルホストへのリクエストが通るはずです。
$ curl -I localhost curl: (7) Failed to connect to localhost port 80: Connection refused $ sudo ip netns exec blue curl -I localhost HTTP/1.1 200 OK Server: nginx/1.12.2 Date: Wed, 17 Jan 2018 08:26:34 GMT Content-Type: text/html Content-Length: 3520 Last-Modified: Wed, 13 Dec 2017 18:36:55 GMT Connection: keep-alive ETag: "5a317347-dc0" Accept-Ranges: bytes $ sudo ip netns exec green curl -I localhost HTTP/1.1 200 OK Server: nginx/1.12.2 Date: Wed, 17 Jan 2018 08:26:40 GMT Content-Type: text/html Content-Length: 3520 Last-Modified: Wed, 13 Dec 2017 18:36:55 GMT Connection: keep-alive ETag: "5a317347-dc0" Accept-Ranges: bytes
想定通りですね。今回はわかりやすくするためにサーバーアプリケーションを動作させる例でしたが、NATやVPNなどネットワークサービスをNamespaceごとに提供するユースケースにも有効だと思います。
OSの起動に合わせてnetnsおよびアプリケーションを稼働させる場合は、SysVinitやsystemdでの設定を行うことになります(ざっくりrc.localでもいいかもしれませんが)。systemdについてはGitHubにテンプレートが公開されているので、参考になるかもしれません。
- Jamesits/systemd-named-netns: Use named netns with systemd services! Access mounted (ip netns ...) network namespaces. · Issue #2741 · systemd/systemd
まとめ
netnsを使ってENIごとにネットワーク設定を持たせる構成をご紹介しました。EC2であれば複数インスタンス構成にしてしまうのが最もシンプルですが、なんらかの事情で複数ENIでえんやこらとやるときに参考になれば幸いです。
参考URL
- ip-netns(8) - Linux manual page
- Multiple network interfaces with multiple public IPs in an EC2 instance with different outbound source using network namespaces | El Hombre que Reventó de Información
- ip netns コマンドが意外にきめ細やかにコンテナを作ってくれる - TenForward
- Dockerのネットワーク管理とnetnsの関係 - めもめも
脚注
- ネーミングは、Blue/Greenデプロイメントとは特に関係ありません。 ↩