Follow One Request In, and Name Every Translation on the Way
Last timeWho the Process Thinks It Is
A container holds a network stack of its own, so the same address means two different things inside and outside. One request is followed across the join and every hop priced.
A stack of its own, not a cable into yours
The network view is the one of the six separated views that people reason about
with the wrong picture, and the wrong picture is a cable. A cable would mean the
container is sitting in the host's network holding one more address on it. That
is not what happens. The process is handed a second stack.
A stack is more than an address. It is the set of network interfaces that
exist, the routing table that decides which of them a packet leaves by, the table of which link-layer address belongs to which neighbour, and the
65,535 ports that can be listened on. Hand a process a
view of its own and all of that is new and almost entirely empty. Two
interfaces usually exist in it: one end of a pair that reaches out of the box,
and a loopback.
That loopback is where most of the trouble starts, because it is not the host's.
The standards are unambiguous that the whole of 127.0.0.0/8 is internal to one
host and never appears on a network, and a network view is, for this purpose, a
host. So the host running the service has one 127.0.0.1 and the container has
another, and nothing can get from one to the other, because getting there would
require a packet addressed to a loopback to cross a network and that is the one
thing the address is defined not to do.
Count them for the running example. Ten containers from the 180 MB image, each
with a view of its own, on one machine. That is distinct loopback
interfaces, all answering to the same four numbers, each reaching exactly one
thing. Ten of the eleven are invisible from the host and from each other.
The ports work the same way, and this is the half of it that saves work rather
than causing it. Each view has its own 65,535, so all ten containers can listen
on port 8080 at once with nothing to arrange, because there are ten separate
port 8080s. The collision people expect does not happen inside. It happens on
the host, and only for the containers whose ports are published, which is the
subject two sections down.
What has to exist before a packet can cross
Two stacks with no relationship do not reach each other. Whatever connects them
is built, and it is built out of three parts.
The first is a pair of virtual interfaces, created together, where everything
written into one end comes out of the other. One end is put into the container's
view and the other stays in the host's. That is the only physical fact in the
arrangement, and it is why the container's traffic is visible on the host at all.
The second is a bridge in the host's view with the outside ends of all those
pairs attached to it. A bridge forwards by link-layer address, so once the
containers are on it they are neighbours on one subnet and find each other the
ordinary way, by asking who holds a given address and caching the answer.
Nothing rewrites anything on that path. Container to container is the plainest
leg in the system and the one people assume is complicated.
The third is a translation at the edge, and it is needed because of the
addresses. A bridge network is cut from the private blocks, which are defined
as not routed on the public internet. Take 172.18.0.0/16 as the running one,
with the host holding 172.18.0.1 on its end of the bridge, the service on
172.18.0.4 and a data store on 172.18.0.5. A client out on the internet cannot
address 172.18.0.4, not because something forbids it but because no router will
carry it anywhere. So the only address the outside can use is one of the host's,
and something has to rewrite the host's address and port into the container's.
networks:
app_net:
subnet: 172.18.0.0/16 # 2 to the 16th addresses in it
gateway: 172.18.0.1 # the host end of the bridge
services:
web:
address: 172.18.0.4
listens: 0.0.0.0:8080 # every address in its own view, not only its loopback
published: 198.51.100.7:80 # the one pair the outside can use
store:
address: 172.18.0.5
listens: 0.0.0.0:5432
published: none # reachable by name from web, by nothing from outsideThe subnet fixes how many containers the bridge will hold, and the arithmetic is
worth doing once because the answer surprises people in both directions. A /16
is addresses. Three are not available to a container: the
network address itself, the broadcast address, and the gateway the host holds.
Ten containers are running. So addresses are
still free. Nobody runs out of addresses on a bridge. People do run out on a
/24, which is addresses and 253 usable, and that is a limit a busy
machine can actually reach.
The lesson stops here
5 more paragraphs to go
You have read the opening. The rest of the argument, the problems that check whether it landed, and the lines worth keeping at the end all come with a plan.
The first lesson of every course in the library reads the whole way through, free, so you can see exactly what the rest of them are.
See the planThe contentsThis is the reading half
Starting the course gives you your own copy of it. Every idea on every page has problems standing under it, marked with a reason rather than a tick, and any sentence you do not believe can be opened and argued with. None of that can happen on a page nobody owns.
The contents