从零搭建多子网 DHCP 服务:CentOS 双网卡多作用域实战与原理解析

摘要
本文把你给出的片段整理成一篇完整的技术文章:在一台 CentOS/RHEL 系列服务器上部署一个支持两个子网(192.168.10.0/24 和 192.168.100.0/24)的 DHCP 服务。文章会给出实用场景、完整且修正后的 dhcpd.conf 配置、网卡持久化配置方法、逐行代码解析、测试示例与结果,以及 DHCP 服务在运行时的复杂度分析。语言尽量口语化,像在写给同组同学的运维笔记——能看懂、能上手、能复现。
描述
假设你在一个院系实验室或一个小型公司里,需要把两片网络同时交给同一台 DHCP 服务器管理:
- 子网 A(教师/管理网络):
192.168.10.0/24,网关192.168.10.2,DHCP 分配范围192.168.10.5–192.168.10.254。 - 子网 B(学生/访客网络):
192.168.100.0/24,网关192.168.100.2,DHCP 分配范围192.168.100.5–192.168.100.254。
服务器有两块网卡 ens160(连接子网 A)和 ens36(连接子网 B)。我们要做到:
- 网卡地址配置持久化(否则
ifconfig/ip addr add重启后会丢失)。 - 配置
dhcpd来为两个子网提供地址池、网关、DNS、域名等信息。 - 启动并使 DHCP 服务随系统启动,同时给出测试方法和预期结果。
适用场景举例:
- 大学实验室:教师与学生分网,教师网段用于教师机和服务器,学生网段用于学生终端。
- 小公司:办公网和访客网共用一台机箱式设备提供 DHCP。
- 虚拟化环境:一台虚拟机管理多张虚拟网卡,将多个 VLAN/子网的 DHCP 服务统一管理。
题解答案
要点如下:
- 持久配置网卡:不要只用
ifconfig/ip命令即时配置。编辑网卡配置文件(CentOS7/8 通常在/etc/sysconfig/network-scripts/ifcfg-<ifname>)或用nmcli/NetworkManager 来做持久化配置。 - 修正并写好
dhcpd.conf:原文中有语法和数字错误(例如 netmask 写错、逗号和点混用、括号不配对)。需要一份合法的、能同时定义两个 subnet 的配置文件。 - 启动并设置开机自启:
systemctl enable --now dhcpd;同时根据网络环境调整防火墙(允许 UDP 67/68)和 SELinux(必要时配置策略或设置 permissive)。 - 测试:在客户端
dhclient -v或在服务器上用dhcpd -t测试配置语法,用journalctl -u dhcpd -f查看运行日志。
接下来给出完整示例和逐行分析。
题解代码
下面包含:网卡持久化配置示例(CentOS 风格)、dhcpd.conf 文件内容、启动与防火墙操作、测试命令。
网卡持久化配置(CentOS 系列,/etc/sysconfig/network-scripts/ifcfg-*)
为 ens160(192.168.10.1) 和 ens36(192.168.100.1)做持久配置:
/etc/sysconfig/network-scripts/ifcfg-ens160
TYPE=Ethernet
BOOTPROTO=none
NAME=ens160
DEVICE=ens160
ONBOOT=yes
IPADDR=192.168.10.1
PREFIX=24
GATEWAY=192.168.10.2
DNS1=192.168.10.2
NM_CONTROLLED=no
/etc/sysconfig/network-scripts/ifcfg-ens36
TYPE=Ethernet
BOOTPROTO=none
NAME=ens36
DEVICE=ens36
ONBOOT=yes
IPADDR=192.168.100.1
PREFIX=24
GATEWAY=192.168.100.2
DNS1=192.168.100.2
NM_CONTROLLED=no
说明:
BOOTPROTO=none:静态地址。ONBOOT=yes:开机激活。PREFIX=24等同netmask=255.255.255.0。NM_CONTROLLED=no表示不被 NetworkManager 管控(如果使用 NetworkManager 可用nmcli配置替代)。
应用:
# 重新加载网卡配置(或重启网络服务)
nmcli connection reload # 如果使用 NetworkManager
# 或
ifdown ens160 && ifup ens160
ifdown ens36 && ifup ens36
# 或重启网络(CentOS7+)
systemctl restart network
DHCP 主配置文件 /etc/dhcp/dhcpd.conf
下面是修正、格式良好的 dhcpd.conf:
ddns-update-style none;
ignore client-updates;option domain-name "test.org";
option domain-name-servers 192.168.10.2, 192.168.100.2;default-lease-time 21600; # 默认租约 6 小时
max-lease-time 43200; # 最大租约 12 小时# 子网 A:192.168.10.0/24
subnet 192.168.10.0 netmask 255.255.255.0 {option routers 192.168.10.2;option subnet-mask 255.255.255.0;option nis-domain "test.org";option time-offset -18000; # 时间偏移,示例值range dynamic-bootp 192.168.10.5 192.168.10.254;
}# 子网 B:192.168.100.0/24
subnet 192.168.100.0 netmask 255.255.255.0 {option routers 192.168.100.2;option subnet-mask 255.255.255.0;option nis-domain "test.org";option time-offset -18000;range dynamic-bootp 192.168.100.5 192.168.100.254;
}
注意点:
- 保证括号
{}成对出现并语法正确。 option domain-name-servers我用两个地址,用逗号分隔。range的 IP 要写成192.168.x.5形式,之前原文有逗号或错别字。- 如果服务器只有一块网卡并且通过 VLAN/路由去管理多个子网,则需要确保 DHCP 请求能到达服务器(通常需要中继 agent / DHCP relay)。本示例假设服务器同时直连两个子网(两张网卡)。
启动 DHCP 服务并设置开机自启
# 安装(如果还没装)
# yum install -y dhcp-server # CentOS/RHEL
# 或 dnf install -y dhcp-server# 启动并设置开机自启
systemctl enable --now dhcpd# 指定监听接口(可选):
# 编辑 /etc/sysconfig/dhcpd,将 INTERFACES="ens160 ens36" 写入
echo 'INTERFACES="ens160 ens36"' > /etc/sysconfig/dhcpd
systemctl restart dhcpd
防火墙开放
# 允许 DHCP 服务端口(UDP 67, 客户端 68)
firewall-cmd --add-port=67/udp --permanent
firewall-cmd --add-port=68/udp --permanent
firewall-cmd --reload
或者使用服务名:
firewall-cmd --add-service=dhcp --permanent
firewall-cmd --reload
SELinux 注意
如果 SELinux 启用并阻止 dhcpd,可以查看 audit.log 并针对性放行或临时设为 permissive 测试。不要轻易永久关闭 SELinux,推荐按审计告警配置策略。
题解代码分析
下面把关键片段逐项解释,方便理解每项配置的作用与为什么要这样写。
ifcfg 文件
TYPE=Ethernet:接口类型。BOOTPROTO=none:不使用 DHCP 客户端,使用静态 IP。DEVICE=ens160:设备名。ONBOOT=yes:系统启动时启用该接口。IPADDR/PREFIX:IP 与前缀长度。PREFIX=24等同NETMASK=255.255.255.0。GATEWAY:默认网关;如果机器本身不做路由,可省略或配置在路由表。NM_CONTROLLED=no:如果这个设置为yes,NetworkManager 会管理该接口;根据你的运维习惯选用。
为什么这样?直接 ifconfig/ip addr add 的改动只在内存中,重启后丢失。写入系统的配置文件可以保证重启生效。
dhcpd.conf 主体
ddns-update-style none;:禁用动态 DNS 更新(如果没有 DNS 动态更新需求可以关)。ignore client-updates;:服务器忽略客户端试图更新 DNS 的请求。option domain-name "test.org";:下发给客户端的域名配置。option domain-name-servers 192.168.10.2, 192.168.100.2;:DNS 服务器地址(多个用逗号)。default-lease-time/max-lease-time:默认与最大租期,以秒计。根据使用场景调整(比如访客网用短租期,管理网用长租期)。subnet ... { ... }:定义一个子网作用域。每个subnet块里可以指定路由器(网关)、子网掩码、时间偏移、地址分配范围等。range dynamic-bootp x.x.x.5 x.x.x.254;:地址池,这里给出从.5到.254的可分配地址。.1、.2等通常留给静态设备(服务器、网关)。
常见错误来源:
- 写错 netmask(例如
255.285.495.0)或把点改成逗号(192,168,100.5),会导致 dhcpd 启动失败。启动前用dhcpd -t检查语法。
/etc/sysconfig/dhcpd 的 INTERFACES
有时 dhcpd 会监听所有接口或指定接口。把 INTERFACES="ens160 ens36" 写进去可以避免 dhcpd 在不希望的接口上监听或者在系统上没有自动发现接口时失败。
示例测试及结果
下面给出实际验证步骤与预期输出示例(你可以在服务器和一台客户端上执行这些命令来验证)。
检查配置语法
# 语法检查(注意:dhcpd -t 需要 root 权限)
dhcpd -t -cf /etc/dhcp/dhcpd.conf
预期:没有输出且返回码 0;如果有语法错误会报告具体行号。
启动并查看日志
systemctl restart dhcpd
journalctl -u dhcpd -f
预期日志片段(大概类似):
dhcpd[1234]: DHCPDISCOVER from 00:11:22:33:44:55 via ens160
dhcpd[1234]: DHCPOFFER on 192.168.10.10 to 00:11:22:33:44:55 via ens160
dhcpd[1234]: DHCPREQUEST for 192.168.10.10 (192.168.10.1) from 00:11:22:33:44:55 via ens160
dhcpd[1234]: DHCPACK on 192.168.10.10 to 00:11:22:33:44:55 via ens160
这说明客户端通过 ens160 成功申请到 IP。
在客户端请求 DHCP
# 先释放再请求
dhclient -r
dhclient -v
预期输出片段:
DHCPDISCOVER on eth0 to 255.255.255.255 port 67 interval 3
DHCPOFFER from 192.168.10.1
DHCPREQUEST on eth0 to 255.255.255.255 port 67
DHCPACK from 192.168.10.1
bound to 192.168.10.10 -- renewal in 10800 seconds.
然后用 ip addr show eth0 能看到分配到的 IP。
查看租约文件
DHCP 的租约通常保存在 /var/lib/dhcpd/dhcpd.leases(路径可能因系统不同而异):
cat /var/lib/dhcpd/dhcpd.leases
你会看到类似下面的块:
lease 192.168.10.10 {starts 2 2025/11/01 10:00:00;ends 2 2025/11/01 16:00:00;tstp 2 2025/11/01 16:00:00;cltt 2 2025/11/01 10:00:00;binding state active;next binding state free;hardware ethernet 00:11:22:33:44:55;
}
这能帮助你核查租约分配历史。
时间复杂度
在这里的“算法复杂度”我们用来衡量 DHCP 服务器在处理租约和请求时的伸缩性,主要关注以下操作:
-
查找已存在租约(按 MAC 或 IP):多数 DHCP 实现会把活跃租约放到哈希表或类似结构里以便快速查找。
- 平均复杂度:O(1)(哈希查找)
- 最坏情况:O(n)(极端哈希冲突或没有哈希结构时线性查找)
-
分配一个新地址:如果实现用位图或池并记录下一个可用指针,分配通常很快。
- 平均复杂度:O(1)
- 最坏情况:O(n)(如果需要扫描整个池来找一个空闲地址)
-
回收租约 / 清理过期租约:如果周期性进行线性扫描则为 O(n),若采用事件驱动或时间轮(priority queue),可做得更优。
- 常见实现:保留一个时间索引或优先队列,回收操作是 O(log n)(优先队列)或 amortized O(1)。
总结:现实中对常见规模的网络(几百到几千台)来说,DHCP 的查找与分配操作基本上是常数平均时间;只有在数万设备且实现不佳时,性能问题才显现。
空间复杂度
空间开销主要来自以下几部分:
-
租约表:通常按客户端数(n)存储记录,每条记录包含 IP、MAC、租约时间、状态等信息。
- 空间复杂度:O(n)。
-
地址池本身:地址池可以视作固定大小(m = 地址数量),通常是位图或区间列表。
- 空间复杂度:O(m)。
总体:O(n + m)。在大多数小规模场景(几百台客户端、池大小数百)内,内存消耗极小。
总结
最后总结一些在实际部署时常遇到的问题和建议:
-
不要用即时命令替代持久化配置:
ifconfig/ ip addr add仅临时,开机会丢。养成编辑/etc/sysconfig/network-scripts/ifcfg-*或使用nmcli的习惯。 -
配置文件语法检查:配置
dhcpd.conf后先用dhcpd -t或dhcpd -cf /etc/dhcp/dhcpd.conf -t做语法检查,避免服务无法启动。 -
接口监听问题:如果 dhcpd 没有在正确接口上监听,检查
/etc/sysconfig/dhcpd的INTERFACES配置,或系统日志里面的错误提示。 -
防火墙 / 中继(relay):如果客户端和服务器不在同一网段,需要在路由器上配置 DHCP 中继(DHCP relay),否则 DHCP 请求不会穿子网。并确保防火墙放通 UDP 67/68。
-
租期策略:对访客网使用较短租期(例如 1 小时),对固定办公设备使用长租期或直接静态分配和保留(
host配置块)。 -
静态保留(按 MAC):如果某些设备必须获得固定 IP,可以在
dhcpd.conf中使用host块绑定 MAC 与 IP。示例:host printer01 {hardware ethernet 00:11:22:33:44:66;fixed-address 192.168.10.20; } -
日志排查:
journalctl -u dhcpd -f与/var/log/messages(或系统对应日志)是最直接的排查点。检查常见错误比如 “no subnet declaration for ” 表示请求来自未配置的子网。
