network namespace는 docker와 같은 container에서 host의 network를 나누기 위해서 사용한다.
container를 만들면, 각 container들끼리 격리되기를 원할 것이다. 즉, container에 동작하는 process는 다른, container와 격리되어 동작하기를 원한다는 말이다. 이를 가능하게 해주는 것이 바로 process namespace이다.
container에서 ps aux
를 통해서 process들을 확인하면 다음과 같다.
ps aux
USER PID ...
root 1 ...
container안에 동작하는 process의 ID가 1인 것을 확인할 수 있다.
그런데, 이러한 container를 구동하는 host에서 ps aux
로 같은 process를 확인하면 다음이 나온다.
ps aux
USER PID ...
root 3816
같은 process이지만, container안과 밖이 서로 다른 process ID를 가지게 되는 것이다. 이것이 가능한 이유는 process들끼리 container상에서는 격리되었지만, host에서는 통합되어 확인할 수 있기 때문이다.
network namespace 역시도 마찬가지이다. host는 자신의 network interface와 routing table, ARP table을 가지는데 다음과 같다.
------------Host----------
| |
| |
| eth0 --------------- LAN
| (192.168.1.2) (192.168.1.0)
|Routing Table, ARP table|
--------------------------
이제 container를 만드는데, container에 network namespace를 부여하도록 하자. network namespace가 부여된 container는 host에 network관련 정보에 대한 가시성이 사라지게 된다.
network namespace안에서의 container는 virtual network interface를 갖게되고, 각 routing table, ARP table도 갖게된다.
------------Host-----------
| ----Container---- |
| | | |
| | veth0 |
| | RT table, ARP | eth0 --------------- LAN
| ----------------- (192.168.1.2) (192.168.1.0)
|Routing Table, ARP table|
--------------------------
linux에서 network namespace를 만들기 위해서는 다음의 명령어를 사용한다.
ip netns add red
ip netns add blue
red
와 blue
network namespace를 만든 것이다.
ip netns
red
blue
host의 network interface를 확인하듯이 ip link
를 namespace에 쓰고 싶다면 다음과 같이 두 가지 방법으로 쓸 수 있다.
ip -n red link
ip netns exec red ip link
host의 ARP를 확인하는 방법은 다음과 같다.
arp
Address HWtype ...
172.17.0.21 ether ...
container 내의 ARP를 확인하는 방법은 다음과 같다.
ip netns exec red arp
그러나 아무것도 안나올 것이다.
route table역시도 마찬가지이다.
ip netns exec red route
왜 그럴까?? 이는 만들어진 network namespace는 어떠한 network interface를 가지고 있지 않으며, host network를 볼 수 없는 상태이다. 이들에게 network 연결성을 제공해주기 위해서 virtual한 network interface를 부여하도록 하자.
ip link add veth-red type veth peer name veth-blue
다음의 명령어로 veth-red
와 veth-blue
를 만들어, 다이렉트로 연결하기위한 준비를 한 것이다. virtual ethernet을 즉 생성을 한 것이다.
veth-red-----veth-blue
다음으로 각 veth들을 network namespace에 연결시켜주도록 하자.
ip link set veth-red netns red
ip link set veth-blue netns blue
다음과 같이 된다.
-----red----- -----red-----
| | | |
| veth-red-----veth-blue |
| | | |
------------- -------------
이제 각 veth에 주소값을 주기위해서 IP를 부여하도록 하자.
ip -n red addr add 192.168.15.1 dev veth-red
ip -n blue addr add 192.168.15.1 dev veth-blue
이제 각 veth마다 IP를 부여해주었다.
-----red----- -----red-----
| | | |
| veth-red-----veth-blue |
| (192.168.15.1) (192.168.15.2) |
------------- -------------
이제 두 veth를 실행시켜 link연결을 시켜주도록 하자.
ip -n red link set veth-red up
ip -n blue link set veth-blue up
잘 연결되었는 지 확인하기위해서 red
network namespace에서 veth-blue에 요청을 보내보도록 하자.
ip netns exec red ping 192.168.15.2
성공하는 것을 볼 수 있을 것이다.
이제 ARP와 route table을 보도록 하자.
ip netns exec red arp
Address HWtype ... Iface
192.168.15.2 ether ... veth-red
veth-red
에서 veth-blue
인 192.168.15.2
에 ARP 테이블이 만들어진 것을 볼 수 있다. 반대로 blue namespace에서 확인하면 다음과 같다.
ip netns exec blue arp
Address HWtype ... Iface
192.168.15.1 ether ... veth-blue
재밌는 것은 이 두 network namespace를 호스팅하고 있는 host에서는 arp
를 실행해도 이들의 정보가 나오지 않는다. 왜냐하면 연결되지 않았기 때문에 알 방도가 없는 것이다.
그런데, 이러한 network namespace가 많아지게 되면, 일일히 하나하나 veth를 만들고 각각을 연결해주기 어렵다. 그래서 virtual switch라는 개념이 나오게 된다.
host에 virtual switch를 만들고, virtual host를 각 namespace들에 연결하면 되는 것이다. 그런데 어떻게 만드는가? 여기에는 여러 solution들이 있는데, 가장 유명한 것이 Linux Bridge
이다.
생성하는 방법도 간단한데, 다음과 같다.
ip link add v-net-0 type bridge
이렇게 하면 v-net-0
라는 virtual switch가 만들어진다. 해당 virtual switch는 host에서도 확인 가능한데, ip link
를 통해서 확인해볼 수 있다.
이제 v-net-0
을 실행시켜주도록 하자.
ip link set dev v-net-0 up
다음으로 위에서 우리가 만든 veth
들은 이제 필요하지 않으므로 삭제시켜주도록 하자.
ip -n red link del veth-red
veth-blue
도 같이 사라진다. 이는 veth가 pair로 생성되었기 때문이다.
---red--- ---blue--
| | | |
| | | |
| | | |
--------- ---------
----v-net-0---
|192.168.15.0|
--------------
--green-- --bloack-
| | | |
| | | |
| | | |
--------- ---------
새로운 veth를 생성하여 virtual switch에 연결시켜주도록 하자.
ip link add veth-red type veth peer name veth-red-br
ip link add veth-blue type veth peer name veth-blue-br
각 veth 쌍을 만들어낸 것인데, 그림과 같다.
---------- -------------
|veth-red|-------|veth-red-br|
---------- -------------
----------- --------------
|veth-blue|-------|veth-blue-br|
----------- --------------
이제, veth-red
와 veth-blue
는 각가의 namespace로 연결시키고, veth-red-br
과 veth-blue-br
은 virtual switch인 v-net-0
에 연결시켜주도록 하자.
ip link set veth-red netns red
ip link set veth-red-br master v-net-0
ip link set veth-blue netns blue
ip link set veth-blue-br master v-net-0
그림과 같이 되는 것이다.
---red--- ---blue--
| | | |
| veth-red veth-blue |
| | | | | |
--------- | | ---------
| |
| |
--------|-v-net-0-|-------------
| | | |
| -----|---------|------------|
| |veth-red-br||veth-blue-br| |
| --------------------------- |
| 192.168.15.0 |
--------------------------------
--green-- --bloack-
| | | |
| | | |
| | | |
--------- ---------
이제 ip를 할당시켜주도록 하자.
ip -n red addr add 192.168.15.1 dev veth-red
ip -n blue addr add 192.168.15.2 dev veth-blue
ip -n red link set veth-red up
ip -n blue link set veth-blue up
ip를 virtual switch인 bridge와 같은 대역을 쓰는 것에 주의하자.
이제 virtual switch를 통해서 red와 blue namespace는 서로 연결된다.
192.168.15.1 192.168.15.2
---red--- ---blue--
| | | |
| veth-red veth-blue |
| | | | | |
--------- | | ---------
| |
| |
--------|-v-net-0-|-------------
| | | |
| -----|---------|------------|
| |veth-red-br||veth-blue-br| |
| --------------------------- |
| 192.168.15.0 |
--------------------------------
--green-- --bloack-
| | | |
| | | |
| | | |
--------- ---------
그런데, host에서도 그럼 red
namespace인 192.168.15.1
에 접근이 가능한가?? 시도해보면 실패할 것이다.
ping 192.168.15.1
Not Reachable!
virtual switch는 실제로 host의 network interface이다. 따라서, virtual switch에도 host가 IP를 달아주어 host와 연결해주어야 하는 것이다.
ip addr add 192.168.15.5/24 dev v-net-0
이제 ping을 보내보도록 하자.
ping 192.168.15.1
성공한 것을 볼 수 있다.
아래와 같이 virtual switch와 host A가 연결된 것을 볼 수 있다.
----------------Host A--------------------
| |
| 192.168.15.1 192.168.15.2 |
| ---red--- ---blue-- |
| | | | | |
| | veth-red veth-blue | |
| | | | | | | |
| --------- | | --------- |
| | | |
| | | |
| --------|-v-net-0-|------------- |
| | | | | |
| | -----|---------|------------| |
| | |veth-red-br||veth-blue-br| | |
| | --------------------------- | |
| | 192.168.15.5 | |
| -------------------------------- |
| | |
-------------------eth0-------------------
(192.168.1.2)
그런데, 다음과 같이 LAN을 타고 다른 network와 연결되고 싶은 경우는 어떻게해야할까??
다음의 Host A에서 Host B로 LAN을 통해 연결된다고 하자.
----------------Host A--------------------
| |
| 192.168.15.1 192.168.15.2 |
| ---red--- ---blue-- |
| | | | | |
| | veth-red veth-blue | |
| | | | | | | |
| --------- | | --------- |
| | | |
| | | |
| --------|-v-net-0-|------------- |
| | | | | |
| | -----|---------|------------| |
| | |veth-red-br||veth-blue-br| | |
| | --------------------------- | |
| | 192.168.15.5 | |
| -------------------------------- |
| | |
-------------------eth0-------------------
(192.168.1.2)
|
|
LAN
(192.168.1.0)
|
|
----Host B----
|192.168.1.3 |
--------------
Host A에 있는 blue에서 Host B에 연결하고 싶지만 route가 잡혀있지 않아 불가능하다. 즉, blue입장에서 알고 있는 network란 virtual switch인 192.168.15.0
뿐이다. 192.168.1.0
에 대해서는 모를 수 밖에 없다.
ip netns exec blue route
Destination Gateway ...
192.168.15.0 0.0.0.0 ...
라우팅 테이블이 셋팅되지 않은 상황이므로 ping이 가지 않을 것이다. 즉 192.168.1.0/24
을 못찾는 것이다. 따라서, 이를 해결하기 위해서 gateway에 연결하는 것인데, 이 gateway가 바로 virtual bridge이다. 이는 linux bridge가 host와 ARP, route table을 공유하기 때문에 virtual switch localhost로 전달된 packet이 host에서 처리되는 것처럼 쓸 수 있다. 따라서, 외부 LAN에도 접근이 가능한 것이다.
ip nets exec blue ip route add 192.168.1.0/24 via 192.168.15.5
192.168.15.5
를 gateway로 두어 192.168.1.0/24
로 향하는 모든 packet을 192.168.15.5
를 통하여 가도록 하고 있다. 그렇게되면 host와 연결된 브릿지에서 해당 packet을 받아 마치 host에서 처리해주는 것처럼 사용할 수 있다.
그러나 ping을 시켰을 때 unreachable이 나오진 않지만 packet이 전달되지 않을 것이다. 이는 NAT문제이다. 즉, network namespace안에서 쓰이는 192.168.15.0/24
ip는 host A에서만 쓰이는 private IP이므로 host B가 알턱이 없다. 따라서, 해당 IP로 응답을 보내지 못하는 것이다. 즉, 전달은 됐지만 어디로 응답을 전송할 지 모르겠는 것이다.
따라서, NAT를 통해서 192.168.15.5/24
에서의 packet 출발지 IP를 Host의 IP로 변환 설정하여 전달하도록 하는 것이다.
iptables -t nat -A POSTROUTING -s 192.168.15.0/24 -j MASQUERADE
이제 다시 blue에서 192.168.1.3
에 ping을 보내면 성공할 것이다.
ip netns exec blue ping 192.168.1.3
마지막으로 LAN
이 internet과 연결되어 있다고 하자.
----------------Host A--------------------
| |
| 192.168.15.1 192.168.15.2 |
| ---red--- ---blue-- |
| | | | | |
| | veth-red veth-blue | |
| | | | | | | |
| --------- | | --------- |
| | | |
| | | |
| --------|-v-net-0-|------------- |
| | | | | |
| | -----|---------|------------| |
| | |veth-red-br||veth-blue-br| | |
| | --------------------------- | |
| | 192.168.15.5 | |
| -------------------------------- |
| | |
-------------------eth0-------------------
(192.168.1.2)
|
|
LAN ------------- Internet
(192.168.1.0)
|
|
----Host B----
|192.168.1.3 |
--------------
문제는 각 namespace들이 Internet에 연결되지 않는다는 것이다.
ip netns exec blue ping 8.8.8.8
Connect: Network is unreachable
이는 route table에 default가 설정되어 있지 않기 때문에 발생한 문제이다. 따라서 다음의 명령어로 default를 추가시켜주도록 하자.
ip netns exec blue ip route add default via 192.168.15.5
Destination Gateway Genmask ... Iface
192.168.15.0 0.0.0.0 255.255.2550 ... veth-blue
192.168.1.0 192.168.15.5 255.255.2550 ... veth-blue
default 192.168.15.5 255.255.2550 ... veth-blue
즉 virtual switch를 gateway삼고 있는 것을 볼 수 있다.
이제 문제없이 internet에 접속할 수 있게 될 것이다.
단, 반대는 불가능하다. 즉, 192.168.1.3
에서 blue의 IP인 192.168.15.2
에 바로 요청을 보낼 순 없다.