我正在尝试使用 Linux 网络命名空间构建一个小项目,但对所有可用的 Linux 网络功能和容器化技术有点不知所措,因此不确定我是否以正确的方式解决这个问题。
问题/项目
我目前有一个简单的 Linux 机器,带有一个网络设备 ( eth0
),该设备分配有十几个静态公共 IPv4 地址 ( 1.1.1.1
, 1.1.1.2
, 1.1.1.3
, ..., 1.1.1.12
)。我希望创建一种情况,其中我有一堆(网络)名称空间,每个名称空间实际上都有自己的单个名称空间独家的公共 IPv4 地址/接口。
我的目标是旋转多个外壳,每个都隔离到自己的网络名称空间(还有 pid、ipc...命名空间)。例如,7号shell 将使用网络命名空间ns7
,该命名空间具有单个(虚拟)以太网接口,该接口具有静态 ip 1.1.1.7
。在该 shell 中,我可以(例如)启动 apache/nginx,让它监听 *:80,然后它将在1.1.1.7:80
.任何定向到任何其他 IPv4 地址上的端口 80 的任何流量都永远不会到达ns7
,同样,任何定向到端口 80 的流量也1.1.1.7
应该到达。仅有的到达命名空间中的进程ns7
。
基本思想是命名空间实际上是永久的。命名空间本身以及相关的虚拟网络设备将在系统启动时(重新)创建并启动。
潜在的解决方案(我走在正确的轨道上吗?)
根据我在没有亲自实验的情况下能够拼凑出来的内容,解决方案应该类似于下面概述的内容,我在这里走在正确的轨道上吗?
创建并启动(虚拟)二层桥接设备
br0
。确保我们在 L2(而不是 L3)运营。更改当前的以太网设备
eth0
配置,以便它仍然在启动时出现,但不再设置任何 IP 地址(既不是静态也不是 DHCP),也分配eth0
给br0
网桥。创建一些网络命名空间
ip netns add ns2
,ip netns add ns3
, ...,ip netns add ns12
。我将把 default/root 命名空间视为ns1
.创建多个虚拟以太网接口对
net2a
~net2b
,net3a
~net3b
,net4a
~net4b
, ... 对于每对,将 -version 连接a
到br0
Bridge,并将b
-version 分配给其各自的命名空间。在每个命名空间内,为本地 veth 设备(如
net2b
)分配ns2
适当的 IPv4 地址信息并启动该设备。我可能必须启用 IPv4 转发(?)(
/proc/sys/net/ipv4/ip_forward
) 和/或启用 ARP 过滤(?)(/proc/sys/net/ipv4/conf/all/arp_filter
)。每个命名空间都有自己的防火墙配置(据我所知),因此在每个命名空间内运行一些 iptables/nftables shell 脚本,设置一些合理的默认值并根据本地需求进行调整。
对于那些更熟悉这些虚拟 Linux 网络设备的人来说,这听起来像是一个(大致)可行的计划吗?
额外细节(如果重要的话)
操作系统信息:我运行 CentOS 8,其中特别包括
kernel 4.18
、systemd
和SELinux
,仅我用于nftables
手动防火墙配置(而不是 firewalld 的东西)。背景:所有提供的地址(如
1.1.1.1
)和接口名称(如eth0
)等都是希望显然这是虚构的,在某种程度上是出于隐私原因,但更重要的是为了简洁/简单起见,我所说的“在每个命名空间中运行一个 shell”的意图也是如此。实际要求:有一堆不同类型的软件将在这些命名空间环境中运行,每个命名空间将有一个独特的工作,并且通常涉及多个服务;我的主要愿望之一是使 IPv4 地址彼此完全隔离;另外,许多目标程序是服务器/守护程序(例如我的 apache httpd 示例),我希望它们能够绑定到实际的面向公众的接口/端口,而不是绑定到私有 ipv4 或 unixsocket 上的端口,然后让根命名空间中的软件充当反向代理中间件;
为什么不只是码头工人:(TL/DR)这是一个选项,但我想定制一些东西来好玩(更长的咆哮)几乎所有这些命名空间环境都是供个人使用的,其中一个将运行我的私人邮件服务器,一个用于托管一些个人低级别的网络服务器流量站点,一个用于一些实时网络开发工作的网络服务器,一对运行自己的 sshd+webstack 来充当一些业余爱好者朋友的免费迷你 VPS,一些运行一些基本上自动化的进程,诸如此类的东西。我知道我正在尝试做的事情和 docker 等常见容器化堆栈提供的内容之间有很大的重叠,事实上我描述的系统已经做了大部分事情,其中大部分使用 Podman (与 docker 几乎相同)和其余的大部分只是在公共根命名空间中并行运行。由于各种原因,我喜欢对其中的一些进行干净的微观管理,如果我可以将内容分离到多个永久命名空间中,这会容易得多,我发现我越是尝试根据自己的意愿调整内容,容器软件就越多妨碍我。由于容器软件提供的、我实际使用的唯一东西实际上是 Linux 内核功能,所以我觉得这是一个值得放弃的项目包装纸并弄清楚如何在没有它的情况下完成这些事情。我也很享受深入研究这些东西所带来的教育价值,因为这不是我日常工作通常涉及的内容。
工控机:我没有什么重要的理由让(单个)命名空间内的进程必须通过 IP 与其他命名空间(也不与默认命名空间)进行通信。不过,如果我改变主意(假设到目前为止我的想法大部分是正确的),我想我可以通过设置一个额外的 L2 桥来重复部分过程韦斯-为每个名称空间配对,并分配那些私有
192.168.x.x
样式的 IPv4 地址。VPS/云:该机器不是物理机,而是VPS/虚拟服务器/云服务器。计算机上当前存在的唯一/单个网络设备。这个
eth0
实际上被称为 的ens3
,甚至在完成 CentOS 安装之前就立即自动运行,因此本身已经是一个虚拟设备。lsmod
目前已显示veth
并virtio_net
已加载。我认为我的托管提供商使用 Qemu 提供此 VPS。我不确定这是否重要,我想它不应该。尽管我确实花了一些时间研究是否可以像当前那样克隆(或创建更多)接口ens3
,然后直接为它们分配一个 IPv4 和命名空间,从而消除对桥接设备和 veth 对的需求。这次搜索并没有真正得出任何结果,我最终认为如果没有托管提供商人员在虚拟机管理程序级别更改设置的帮助,这是不可能的。虽然它们通常很有帮助,而且我想可能会对此类操作持开放态度,但这将使我的解决方案对未来的变化不太灵活,所以如果可能的话,我更愿意在我可以完全控制的设备上处理这个问题。IPv6:为了简洁起见,我避免提及 IPv6,我也有一个 IPv6 地址块,并计划以类似的方式使用它们。但我认为最好先让 IPv4 正常工作,然后启用 IPv6 并从那里开始,我无法想象会有太大的不同。
我的实现:一旦我真正成功地第一次让事情开始工作,我还不完全确定如何实现这一切。我想对于该项目的网络部分,我将创建一个
network-setup.sh
shell 脚本来测试所有与网络相关的必需品是否存在(如命名空间、桥接设备、veth 设备等),然后重新创建或设置把缺少的东西补起来。然后伴随它系统单元文件运行该 shell 脚本,并标记它单元文件根据 的要求network-online.target
。然后另一个外壳脚本和单元文件稍后在(引导)过程中运行,使用unshare
或系统init
-实际启动相关进程的版本。但如果有人有更好的想法,我很想听听。不同的命名空间实现:我闻到的一个潜在的复杂情况是,我的印象是 util-linux (
man unshare
) 称之为网络命名空间man ip-netns
与同一术语的iproute2 ( ) 含义不同。然而,我仍然不确定前者是否是后者的超集/扩展,或者它是否是一个完全不同的不兼容的实现。事实上,在阅读容器相关技术时,这似乎是一个反复出现的问题。
答案1
使用 veth-pairs 桥接的方法可以工作,但还有一种更简单的方法:
使用 a macvlan
,参见例如这里或者这里一些细节和讨论。
这是一个虚拟接口,它使用物理接口(在您的情况下为eth0
)作为父接口(或“主接口”),对于使用同一父接口的其他设备完全透明,并且可以移动到网络命名空间中。
然后,您可以将命名空间内的 IPv4 或 IPv6 地址分配给该接口,它将像您拥有该容器专用的附加物理网络接口一样工作。
根据您是否希望容器相互通信,有不同的风格。阅读文档了解详细信息。
是的,如果您想要防火墙(iptables),您也必须在每个名称空间中执行此操作。
顺便说一句,Docker 和其他使用命名空间的虚拟化方法也使用macvlan
s,因此,如果您想要的只是 apache、nginx 等,请考虑使用 Docker 和/或 Docker Compose,它将完成所有其余的工作(不同的文件系统、本地 DNS、端口映射)。