我有一组由systemd-nspawn容器应该与外部网络(半)隔离,但同时应该能够访问主机上运行的非容器化服务,如 DBMS(参见图表systemd-networkd
)。网络由两端管理。
我尝试过 systemd-nspawn 以下几种最常见的私有网络模式:
虚拟以太网
-n, --network-veth
为每个容器创建一对连接的虚拟以太网适配器,一个在主机端,另一个在容器内。这似乎没什么用,因为不清楚 DBMS 应该监听哪个地址以及容器应该连接到哪个地址。
桥
--network-bridge=
功能基本相同,但会将虚拟适配器添加到指定的网桥。网桥应该已经设置并分配了 IP:
# /etc/systemd/network/br0.netdev
[NetDev]
Name=br0
Kind=bridge
# /etc/systemd/network/br0.network
[Match]
Name=br0
[Network]
Address=169.254.1.1/16
该配置已被证明是有效的,但需要额外的桥接配置,并且在涉及容器的外部网络访问时通常会出现问题。
区
--network-zone=
功能相同,但会自动管理桥接接口。以下是桥接的默认设置:
[root@host ~]# ip addr show vz-containers
26: vz-containers: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
inet 169.254.120.107/16 brd 169.254.255.255 scope link vz-containers
valid_lft forever preferred_lft forever
inet 10.0.0.1/24 brd 10.0.0.255 scope global vz-containers
valid_lft forever preferred_lft forever
inet 10.0.1.1/24 brd 10.0.1.255 scope global vz-containers
valid_lft forever preferred_lft forever
似乎它被分配了一个随机地址169.254.0.0/16
,以及两个静态地址10.0.0.1
和10.0.1.1
。通过这两个地址可以从容器访问主机。但不清楚这些地址来自哪里,我不确定这在 systemd 的未来版本中不会改变。为了确保万无一失,我们可以为桥接接口分配一个额外的静态 IP:
# /etc/systemd/network/80-container-vz.network - full override, systemd < 232
[Match]
Name=vz-*
Driver=bridge
[Network]
# Default to using a /24 prefix, giving up to 253 addresses per virtual network.
Address=0.0.0.0/24
Address=169.254.1.1/24
# the rest is left as in the original /usr/lib/systemd/network/80-container-vz.network
# /etc/systemd/network/80-container-vz.network.d/override.conf - drop-in, systemd ≥ 232
[Network]
Address=169.254.1.1/24
这种方法效果很好,不会对外部网络连接造成问题,但也需要对桥接配置进行额外的调整。此外,这两种方法(桥接和区域)都需要配置静态条目/etc/hosts
,因为systemd-resolved
和相关模块(mymachines
和NSS 模块)在这里没有太大帮助:myhostname
resolve
# /etc/hosts
169.254.1.1 host
幸运的是,所有容器都共享相同的基本操作系统映像,因此这并不难。
问题是,以上所有操作能否以更简单的方式完成?还是我应该等到systemd-nspawn
出现一些神奇的选项,例如“使主机可通过 <foo> 主机名在容器中访问”?
答案1
遇到了同样的复杂情况 - 我的答案与 Kiraly 的答案大致相同,但配置 100% 由主机控制。
可以复制和修改库存/lib/systemd/network/80-container-ve.network
,以在特定的静态 IP 上设置主机地址(您还需要更新[Match]
)
然后 - 设置一个只有 1 个地址的 DHCP 池。这样它就可以预测了 - 并且容器不需要静态配置,它只需在启动时选择正确的地址。
所以 - 像这样
/etc/systemd/network/50-mycont.network
[Match]
# Hard match for the container interface name - no wildcards
Name=ve-mycont
Driver=veth
[Network]
# Static address for host
Address=10.0.60.49/28
LinkLocalAddressing=yes
DHCPServer=yes
IPMasquerade=yes
LLDP=yes
EmitLLDP=customer-bridge
[DHCPServer]
PoolOffset=2
PoolSize=1
# Little DHCP - container will have address 10.0.60.50
答案2
目前还不清楚这些地址来自哪里
如果您使用的是 systemd-networkd,则配置文件来自带有 .network 扩展名的配置文件。这些文件位于
/etc/systemd/network
或/run/systemd/network
或/usr/lib/systemd/network
- 如果您未在 /etc 中修改任何内容,则后者是默认设置。... systemd-networkd 的文档是这里。您的网络上可能有一个 DHCP 服务器,并且根据您使用的网络模式,IP 地址可能会从该 DHCP 服务器分配。
问题是,能否以更简单的方式完成上述所有工作?
大多数数据库都是基于网络的,它们监听主机上的特定端口。根据您的网络配置,您的主机应该有一个带有 IP 地址的接口,该接口位于主机端,但可以从容器访问。请记住,防火墙和伪装也可能发挥重要作用。建立连接后,请确保您的数据库服务器也在监听给定的接口。
使主机在容器中可以通过主机名访问
由于您可以控制主机端 IP 地址,因此您可以/etc/hosts
像以前一样将条目放入 中。我不知道有任何更简单的解决方案或任何外部解决方案。
答案3
由于(截至 2024 年 2 月)仍然没有适当的解决方案,我找到了一种相当优雅的方法,只需在容器内部进行最少的定制即可完成此工作。
添加以下两行
/etc/hosts
(地址稍后将被替换;“host”一词是分配给容器主机的符号主机名):0.0.0.0 host :: host
/etc/systemd/system/host-address.service
在容器内创建包含以下内容的文件:[Unit] Description=Add IP address of host system to /etc/hosts After=network-online.target [Service] Type=oneshot ExecStart=/bin/sh -c 'exec sed -i -r "s#^[0-9.]+(\\\\s*host)\\$#$(ip -4 route show default | cut -d" " -f3 | head -n1)\\1#" /etc/hosts' ExecStart=/bin/sh -c 'exec sed -i -r "s#^[0-9A-Fa-f:]+(\\\\s*host)\\$#$(ip -6 route show default | cut -d" " -f3 | head -n1)\\1#" /etc/hosts' RemainAfterExit=yes [Install] WantedBy=network-online.target
通过运行以下命令启用并启动添加的服务:
systemctl enable --now host-address.service
/etc/hosts
现在应该包含容器主机的 IPv4 和 IPv6 地址,并且ping host
等应该可以工作
请注意,这不需要在任何时候手动分配任何 IP 地址。它只是将 systemd-networkd 已添加的信息添加到容器的系统状态中,而大多数工具都比路由配置更容易访问该位置。由于该单元被命令在 之后运行network-online.target
,因此它将在 之后启动systemd-networkd-wait-online.service
,因此在 systemd-networkd 在接口上设置了网络信息之后host0
,使此配置重新启动安全并且希望非常可靠。