When it comes to the container network, the oci runtime spec does no more than creating or joining a network namespace. All the other workers are left to be dealt with by using hooks, which lets you inject into different stages of the container runtime and do some customization.
With the default
config.json, you will see only a
loop device, but not an
eth0that you normally see on the host that allows you talk to the outside world. But, we can set up a simple bridge network by using
netnsas the hook.
Go and get netns and copy the binary to
/usr/local/bin, where the following config.json assume. Worth to note that the hooks are executed in the runtime namespace, not the container namespace. That means, among other things, the hooks binary should reside in the host system, not the container. Hence, you don't need to put the netns into the container rootfs.
setup bridge network using netns
Make following chagnes to config.json. In addition to the hooks, we also need
CAP_NET_RAWcapability so that we can use
pinginside of the container to do some basic network checking.
start a container with this new config.
Inside of the container, we find an
eth0device, in addition to a
loopdevice that is always there.
And, you will be able to ping (one IP of google) outside world.
So, how it works?
Bridge, Veth, Route and iptable/NAT
Upon a hook being called, the container runtime will pass the hook the container's state, among other things, the pid of the container (in runtime namespace). The hook,
Netnsin this case, will use that pid to find out the network namespace the container is supposed to be run in. With that pid, the
netnswill do a few things:
- Create a linux bridge with the a default name
netns0(if there isn't already one). Also setup the MASQUERADE rule on the host.
- Create a veth pair, connecting one endpoint of the pair to the bridge
netns0and placing the another one (renamed to
eth0) into the container network namespaces.
- Allocate and Assign an IP to the container interface (
eth0) and setup the Route table for the container.
Soon we'll go over the stuff we metioned above in detail but let's start another container with the same config.json. Hopefully, it'll make things more clear and interesting than having only one container.
- bridge and interfaces
netns0is created and two interfaces are associated with it. The name of the interface follows the format of
As we explained before
netnsv0-8179is one endpoint of the veth pair, connecting to the bridge; the other endpoint is inside of the container 8179. Let's find it out.
- vthe pair
On the host, we can see the peer of
And in the container 8179, we can see the eth0's index is 7. It confirms that the
eth0in container 8179 is paired with
netnsv0-8179in the host. Same is true for
eth0in container 10577.
So far, we have seen how a container is connected to host virtul bridge using veth pair. We have the network interfaces but still need a few more setups: Route table and iptable.
Here is the route table for In container
We can see the all traffic will goes through
eth0to the gateway, which is the bridge
netns0, as shown by:
In the host:
192.168.1.1is the ip of my home route, which is a real bridge.
Piece together the route in the container, we can see when ping google from the container, the package will go to the virtual bridge created by the
netnsfirst, and then goes to the real route gateway at my home, and then into the wild internet and finally to one of the goole servers.
Another change made by the
netnsis to set up the MASQUERADE target, that means all traffic with a source of
172.19.0.0/16will be MASQUERADE or NAT-ed with the host address so that outside can only see the host (ip) but not the container (ip).
To put all those together, it looks like this:
Share network namespace
To join the network namespace of another container, set up the network namespace path pointing to the one you want to join. In our example, we'll join the network namespace of container 8179.
Remeber to remove the prestart hook, since we don't need to create new network interface (veth pair and route table) this time.
Start a new container, and we'll find that the new container has the same
eth0device (as well as same ip) with the container 8179 and the route table is same as the one in container 8179 since they are in the same network namespace.
So, despite being in different containers, they share same network device, route table, port numbers and all the other network resources. For example, if you start a webservice in container 8179 port 8100 and you will be able to access the service in this new container using localhost:8100.
We see how to use netns as the hook to setup a bridge network for our containers so that the containers can talk to the internet as well as each other.