Container Networking With Netkit: The BPF Programmable Network Device
Watch on YouTubeVideo summary
The video introduces Netkit as a specialized network device developed by Meta to address significant performance bottlenecks associated with traditional container networking using virtual Ethernet (veth) pairs and network namespaces. Historically, isolating containers required creating two separate virtual interfaces—one inside the container and one on the host—which forced every packet transmitted from a container to traverse these devices twice before reaching the physical hardware. This double transmission caused excessive CPU activity due to additional interrupt handling, limiting throughput to roughly 60% of the theoretical maximum even with high-speed adapters like those capable of 100 megabits per second. Netkit solves this by creating a single primary device on the host and a corresponding "pier" device that can be moved directly into the container's network namespace, effectively establishing a shared memory space between them to eliminate redundant data copying.
To achieve full hardware performance while maintaining security isolation, Meta integrated Netkit with BPF (Berkeley Packet Filter) programs within their Twine scheduler infrastructure. When a job is launched via the Twine agent, it automatically provisions these adapters and configures networking without requiring any changes from service owners or application developers; to the container itself, the device appears as a standard network interface like eth0. The rollout strategy prioritized less critical services first using feature flags before gradually expanding to higher-priority tasks, ensuring stability while demonstrating dramatic latency improvements in real-world scenarios where stacking multiple jobs on a single machine reduced response times from over 12 seconds down to approximately 100 milliseconds once Netkit was enabled.
Looking toward future enhancements and broader compatibility, the team is currently developing an IP router system designed to replace thousands of individual BPF programs with a single optimized program that manages routing maps for all containers on a host, further reducing latency overhead. Additionally, efforts are underway to implement NUMA-aware networking to ensure data stays within specific memory nodes on GPU hosts and extend these performance benefits to virtual machines using the AFXDP technology. While debugging remains the primary downside compared to traditional veth pairs due to the unique architecture of the shared device link, Netkit offers a transparent upgrade path that allows teams to reap substantial network throughput gains without altering their existing application code or deployment workflows.
Read the full video transcript
Uh uh thank you everyone. Um so thanks
for coming to my talk. Uh today I'm
going to be talking about container
networking uh using Netkit. Um so just
really quickly a little bit about me. Uh
my name is Mike. I'm a software engineer
at Meta. I work on the containers team.
Um I live in Brooklyn, New York City. Um
there's just a photo of my weekend
hobby. I like to do um rally racing. So
happy to talk about that as well. Um,
but today I'm here to talk about uh the
Netkit device. Um, so I'm going to
quickly go over, you know, kind of
introducing container networking. Uh,
talk about how the Netkit device came to
be. Um, then I want to talk a little bit
about how we implemented Netkit at Meta.
Uh, kind of some of the issues we ran
into as we rolled it out and some of the
benefits that we saw. Um, and then at
the end I'll try to leave some time for
questions.
Uh, so
uh, just kind of introduce like
container networking in general. Um so
at Meta we have obviously a lot of
network traffic coming into our data
centers. Um and if you look at a typical
data center you would see rows and rows
of machines like this. Um on each
machine uh we have of course a desire to
run like many jobs at the same time. Um
so this is kind of logically how it
might look. Um so there's a network
switch at the top and it's connected to
uh multiple hosts in a single rack. And
on each of those hosts we use containers
to run multiple services on the same
system. Um so a typical uh host might
have you know sometimes
uh multiple even thousands of uh
services running at the same time. Um
and of course we want to be able to
isolate the networking um between these
services. So before we had network
namespaces we would have this issue. Uh
if there's two containers running on the
same host and they both want to use like
a similar IP address in port, there
would be a conflict. They're all kind of
like sharing the same um network
environment. Uh this has, you know, kind
of practical implications like all of
these services would have to coordinate
uh their networking setup. Uh but of
course there's also security
implications there. Um you know, a
service could come and like snoop on
another uh service's network traffic,
things like that. Um so network
namespaces was introduced kind of to
solve this problem and introduce
additional layer of security into the
networking. Uh so with network
namespaces once you um put a service
into a network namespace it has
basically its own view of the network.
Um you could almost think of it as if
it's running on a completely separate
machine um on a different network. Um so
here we see that this you know problem
with like trying to bind to the same
port. Uh this is solved because each uh
service on this machine um has its own
list of ports, its own interfaces uh and
it can now bind to that same port. Uh so
like service A in this case, it's not
aware of service B at all and the
networking uh can cooperate together. Um
they don't need to kind of like uh talk
to each other at all. Uh and the way
this is set up um initially was using
these like virtual Ethernet devices. Uh
so when you create a network namespace,
you would create uh in this case the
virtual Ethernet A device and connect it
to another virtual Ethernet device
inside network namespace A and you would
create virtual Ethernet B device and
connect it to the network namespace B.
Um, and this would kind of act similar
uh to if there's like a virtual Ethernet
cable wired from the network namespace
uh to the host name space. Um, so this
works for security, but it turns out it
introduces a performance impact. Um, and
the reason is that every time you
transmit now a packet from the container
to the outside world, it has to
essentially get transmitted twice. uh so
in the beginning it gets transmitted
from the virtual Ethernet device inside
the container uh to the one outside in
the host and then it gets transmitted
again from the host network adapter uh
out to the outside world. Um so what's
happening here is essentially there's a
lot of extra um activity going on on the
CPU. It has to handle these additional
IRQ um interrupts. Uh so the netkit
device was introduced essentially to
solve this problem. Um and the way it
works with netkit is when you create the
first adapter on the host side uh it
actually creates two devices. There's
what they call a primary and a pier. So
the primary exists in the host network
namespace and the pier can then be moved
inside the task network namespace. Um,
and this allows the traffic inside the
container to immediately be available in
the host network namespace. And if you
have BPF programs that you want to
filter the traffic, they can um run it
like essentially they're running inside
the container. Um, but the packets are
able to be processed immediately.
Uh so in like practical terms what this
means is with V you can see on the far
left here um if we use kind of an
imaginary 100 megabit uh adapter um with
V uh we were only able to get about like
60 megabits per second out of that
adapter. So essentially it's about 60%
of the performance of the host um uh
kind of baseline performance. Um and we
see here uh using like netkit and bpf
programs we can get the full uh
performance. So we get the uh equal to
the host performance. Um so that's kind
of the the headline here is that once we
enable netkit uh we can use network name
spaces now we get that isolation between
the tasks um but without any performance
implications.
Um, so that's kind of a summary of kind
of why we wanted to use Netkit and how
it works. Um, so I'd like to just
quickly describe how we rolled it out at
Meta, like what went into implementing
this. Um, so uh, quickly how we work on
um, containers at Meta, we have this uh,
service called the Twine um, scheduler.
So whenever a user requests to start a
service, the request goes to this um
scheduler program uh which is kind of a
a back-end service that um will assign
that job to a particular machine. Um the
the scheduler sends a request down to
our Twine agent which will um create the
container for the job and do all of this
networking setup. So this is where we
implemented um the netkit device. So
every time a job starts up, it
automatically creates these adapters and
then does the networking configuration
for them. Um, and this agent, it's
responsible for the resource control for
these jobs, like assigning how much
memory and how much CPU they're able to
use. Uh, of course, like creating the
actual container and setting up the
isolation between the jobs so these
services don't kind of step on each
other's toes.
Uh so in terms of where we're at with
the rollout status today, um about 66%
of all of the containers that we create
or containers with root or without root.
Um either case it should work. So um
yeah, I guess kind of to follow up on
some of the previous questions. So the
the way that this appears Yeah. the way
that this appears to this um task is the
same as kind of any other network
adapter. Uh and in fact inside the
container if you enter the container it
looks like east e east zero in the
container and there was like no changes
that the tasks need to make. Um they can
just send like normal network traffic.
Um and then under the hood it's it's
implemented as this netkit device but to
the container it just looks like a
normal network device. So they don't
have to kind of do any special setup. Um
the setup is all done from the host side
and then inside the container it just is
like a normal networking setup.
All right. So yeah so let me uh finish
the slides quickly then we can do more
questions. Um so yeah as I was saying
the uh networking space at this point we
have enabled about 66%. Um the way that
we roll this out is via a feature flag.
So as I mentioned like there's kind of
no work for the task to do. We turn this
on in the back end and next time they
restart their task they will just start
using netkit. Um we have it rolled out
based on the criticality of the job. So
when we say 66% of the tasks we started
with like the less critical tasks. So uh
every service owner at meta will specify
the criticality of their service. If
it's a tier zero, this would represent
like if the service goes down, the whole
website goes down and it goes all the
way down 1, two, three, four. Um, so we
started with the less critical ones and
we're working our way up. Um, and you
know, as we get to the end here, you can
see it kind of slows down. We want to be
more careful with these like final
highly critical jobs. Um, but yeah,
importantly like this is fully
transparent to these service owners. Um,
they don't need to do any any special
work here. um once we turn it on for
their job, it just starts using this new
device and they get the performance
benefits. Uh so just to go through like
a real world example here, um this is
one team that was trying to enable um
what we call like stacking of their
jobs. So what that means is um instead
of like one single large task that takes
up an entire machine, they can run uh
four copies uh of their task at the same
time. Um which is it's better for the
service owner. they don't really need to
specify um any of the particulars of the
hardware. It's also better for our like
operations in that sense like there's no
specific um like hardware requirements
that we have. We can kind of move the
jobs around more easily. But what we
were finding was when they tried to do
this like stacking their latency was was
just going way up. So uh in the
beginning here like without Netkit the
latency was going up to around like 12
seconds. Um, and once we turned on
netkit, that latency immediately dropped
to like around 100 milliseconds. Um, so
this was really kind of an extreme
example, but it does uh show how the um
that like soft IRQ usage that I
mentioned before, it can really kind of
add up and start to affect these tasks
if they are really like heavily using
the CPU and um if they're affected by
this um like performance. Uh so it's not
every single um task that is going to
have this like uh heavy network usage,
but for tasks that do do have a
performance dependency, it really does
uh make a big difference. So I wanted to
kind of quickly go through some of the
next things that the team's working on
um at the moment for this. Um so one of
them is called uh IP router system. So
uh I mentioned like the ability to like
uh attach BPF programs in the container
to do like traffic filtering. Um today
the way we have it set up each container
um will get its own like new BPF
program. So if we have a thousand tasks
on a host there would be a thousand BPF
programs kind of in a chain. And this
starts to eventually affect latency as
well because in the worst case the
traffic that we're trying to send has to
go through each one of those BPF
programs. Um, so at the moment we're
working on this IP router system which
would replace all of that with a single
uh a single BPF program that just has
the map of all of the containers on the
system and it just knows where to route
that traffic. Uh, another thing that
we're working on is NUMA aware
networking. Um, so when we have these
containers running on the host, uh, for
performance reasons it's very beneficial
if we can keep all of the networking in
the same NUMA node. Um and this is uh
you know very important as we have like
GPU hosts where the system is kind of
divided up into these numa nodes and any
time you have to copy any kind of data
outside of the numa node there's a big
performance impact. Um so we're working
on making sure the container is aligned
within the same numa node all the way
kind of end to end. Um and the final one
I wanted to mention is uh the next step
for netkit um would be to use it also
with like VMs. So not just a container
but if there's a VM running on a machine
um we could have the same kind of
performance benefits uh using this
system called AFXDP.
Uh essentially what this does is create
a shared address space inside the VM and
the host and then um the like similarly
any traffic being sent out of the VM
will immediately be available in the
host. Um so so we can take the same kind
of uh performance that we've applied to
containers and then apply it also to
VMs.
Um, so in closing, uh, I just wanted to
mention quickly that like there was a
lot of, uh, open source community
support here. Um, the, uh, you know, we
couldn't have done this without all of
the authors, um, uh, the developers
working at different companies who who
helped write this code. Um, at Meta, I
wanted to mention um, Tukshock, Ramen,
and Martin who really helped with um,
you know, implementing this and like
working out some of the bugs. Uh and at
isovalent in particular there's a lot of
really helpful blog posts out there
other talks that were given uh Daniel
Borgman um Nicolay Nico and Bill
Mulligan um they've really put a lot of
helpful useful information out there. Um
and then finally um I know there's a few
questions about like the particulars of
um how this works. So, if you're
interested to learn more, I've put up
some uh demos um and some additional
like links to other resources um up here
uh uh and at that link. Um and yeah,
that was all I had. So, wonder if
there's any questions. Good.
>> Yeah. So, um a lot of people use like
nspawn for for launching containers.
what is the status of integrating netkin
and net spawn so we get that instead of
vs from now on.
>> Um yeah, that's a great question. So
that's actually you know a task that we
have in our backlog um which we're
hoping to get somebody to work on
probably in the beginning of next year
to add this into like uh systemd
networkd. Um so it would be available as
another um like kind of like device type
flag that you could add into the uh
systemd network setup. Um, and then
yeah, so at the link here, there's kind
of like the step-by-step walkthrough. So
once you create the container, um, as of
today, you could you could create the
devices using just like, you know, IP
link add, um, those type of commands,
but eventually we'd like to have this
like support in like systemd, network.
>> Uh, yeah, phone here.
You kept mentioning BPF programs, but uh
do they you did not explain how uh
frames are moved from the external Netk
device to the actual network interface.
Is this the purpose of the BPF programs?
Are they provided by your orchestrator
by the driver? How does this part work?
Um yeah, so the question was around um
like how does how do the BPF programs
actually manage moving the data between
the network name spaces? Um the reason
that this works is essentially it's a
single driver code um and then in the
driver code in the setup it's creating
two devices and those two devices have
access to the same memory under the
hood. So with ves there's actually like
it's you know you have to create both
separate devices and then the only way
that they can communicate with each
other is you know by sending like
network traffic. Um but with netkit th
those BPF programs can can access the
data you know kind of instantly in the
host name space. Um since it's just like
a it's just a single device that just
has like two ends basically. Um does
that make sense?
>> We'll talk about later.
Can netkit be used with uh SIOV or is
there any relation between SOV and
netkit?
Uh
>> I'm not not that familiar with with
that. Um
>> the ability to like defer the
virtualization on the network card.
>> Um I I think it it should be Yeah, it
should be compatible. Well, I'm not sure
that there's any like I don't know of
any reason that it that it wouldn't work
that way. It kind of should just work
like any other um network adapter, but
yeah, I'm not too sure.
>> Thanks.
>> Are there any downsides with this
approach compared to uh traditional VHD?
>> Um yeah, that's a good question. So, I
would say so far the only real downside
that we've seen is around like
debugging. So, one of the nice things
about Ves is like because it just works
like any other network card on the
network, all of the normal networking
stack, you know, kind of applies. So,
debugging like if there's a network
connectivity issue, it works just like
any other like debugging on any other
machine. Um, once you introduce Netkit,
it uh because it's kind of creating this
like short circuit from the container to
the host. um like you kind of have to be
aware of that um like that extra link
that's in there. Uh so yeah, I'd say the
only real downside that we've seen so
far where like teams have asked us to go
back to Ves is if they're like trying to
debug uh some particular network issue
um and they just want to go go back to V
which is like more a more familiar
solution um and they can kind of get get
additional uh debugging output that way.
Uh so it seems that the operation model
for we and netkit is roughly the same.
So the service owner shouldn't really be
able to tell the difference except he
has the better performance.
>> So why uh was it why there was a reason
for netkit in the first place? Wasn't it
possible to change the v or add like an
additional mode to v e or something like
that so anyone can reap the benefits
without changing anything really? I mean
who is using we today? Yeah, I'm not I'm
not too sure the technical reasons like
why this couldn't be done kind of as an
upgrade. I think the the main reason is
like um like with V you you generally
have to like create two devices um and
then like move the second one into the
container network name space. Um Netkit
it's kind of a different like from the
container creation standpoint it's a
little different because you just create
a single device it automatically creates
the peer device which you move. Um, so I
think it's like in the driver code
setup, it's doing that like creation of
the second device. Um, I'm not sure why
it was decided, you know, to be done
that way. I assume it's because um like
it they wanted it to be like a specific
choice that the like container creator
is choosing to make this um netkit pair.
Um, and that that allows existing ves
use cases to keep working the same way.
So where does ebpf come in the in the
game there? Do you absolutely need to to
add ebpf programs to the containers to
make this networking at all or is it
just enabling this as an additional fil
filtering features and you you don't
actually need them?
>> Um yeah, so technically you don't you
don't absolutely need the BP the BPF
programs um to to be there to work. You
could use it um just like uh any other
network adapter. Um but I think the the
BPF um that it's kind of an additional
like hook point uh that they have added
for the netkit device um which allows it
to work much faster. Uh it kind of like
hooks the packets at an earlier spot um
uh in the transmitting process. Um, and
so in the best case scenario, um, if you
if you detect in the BPF program that a
packet is going off of the host, you can
just immediately redirect that packet to
the host Ethernet adapter and there's no
additional processing that needs to be
done. It just immediately gets
transmitted out. Um, so like that's like
the best case uh performance scenario.
Um, you know, that being said, like for
other cases that the BPF program doesn't
handle the traffic, like there still is
a performance benefit. Um, but I think
yeah, like to get the absolute best
performance, it's like the combination
of the netkit with the BPF programs like
picking up the traffic earlier.
>> Any more questions? One last questions.
>> No. Okay. Well, thank you very much,
Mike, again.
>> Cool. Thank you.
[Applause]
[Music]