从零搭建多子网 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)。我们要做到:

  1. 网卡地址配置持久化(否则 ifconfig / ip addr add 重启后会丢失)。
  2. 配置 dhcpd 来为两个子网提供地址池、网关、DNS、域名等信息。
  3. 启动并使 DHCP 服务随系统启动,同时给出测试方法和预期结果。

适用场景举例:

  • 大学实验室:教师与学生分网,教师网段用于教师机和服务器,学生网段用于学生终端。
  • 小公司:办公网和访客网共用一台机箱式设备提供 DHCP。
  • 虚拟化环境:一台虚拟机管理多张虚拟网卡,将多个 VLAN/子网的 DHCP 服务统一管理。

题解答案

要点如下:

  1. 持久配置网卡 :不要只用 ifconfig / ip 命令即时配置。编辑网卡配置文件(CentOS7/8 通常在 /etc/sysconfig/network-scripts/ifcfg-<ifname>)或用 nmcli/NetworkManager 来做持久化配置。
  2. 修正并写好 dhcpd.conf:原文中有语法和数字错误(例如 netmask 写错、逗号和点混用、括号不配对)。需要一份合法的、能同时定义两个 subnet 的配置文件。
  3. 启动并设置开机自启systemctl enable --now dhcpd;同时根据网络环境调整防火墙(允许 UDP 67/68)和 SELinux(必要时配置策略或设置 permissive)。
  4. 测试 :在客户端 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 配置替代)。

应用:

bash 复制代码
# 重新加载网卡配置(或重启网络服务)
nmcli connection reload  # 如果使用 NetworkManager
# 或
ifdown ens160 && ifup ens160
ifdown ens36 && ifup ens36
# 或重启网络(CentOS7+)
systemctl restart network

DHCP 主配置文件 /etc/dhcp/dhcpd.conf

下面是修正、格式良好的 dhcpd.conf

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 服务并设置开机自启

bash 复制代码
# 安装(如果还没装)
# 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

防火墙开放

bash 复制代码
# 允许 DHCP 服务端口(UDP 67, 客户端 68)
firewall-cmd --add-port=67/udp --permanent
firewall-cmd --add-port=68/udp --permanent
firewall-cmd --reload

或者使用服务名:

bash 复制代码
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 在不希望的接口上监听或者在系统上没有自动发现接口时失败。

示例测试及结果

下面给出实际验证步骤与预期输出示例(你可以在服务器和一台客户端上执行这些命令来验证)。

检查配置语法

bash 复制代码
# 语法检查(注意:dhcpd -t 需要 root 权限)
dhcpd -t -cf /etc/dhcp/dhcpd.conf

预期:没有输出且返回码 0;如果有语法错误会报告具体行号。

启动并查看日志

bash 复制代码
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

bash 复制代码
# 先释放再请求
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(路径可能因系统不同而异):

bash 复制代码
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 服务器在处理租约和请求时的伸缩性,主要关注以下操作:

  1. 查找已存在租约(按 MAC 或 IP):多数 DHCP 实现会把活跃租约放到哈希表或类似结构里以便快速查找。

    • 平均复杂度:O(1)(哈希查找)
    • 最坏情况:O(n)(极端哈希冲突或没有哈希结构时线性查找)
  2. 分配一个新地址:如果实现用位图或池并记录下一个可用指针,分配通常很快。

    • 平均复杂度:O(1)
    • 最坏情况:O(n)(如果需要扫描整个池来找一个空闲地址)
  3. 回收租约 / 清理过期租约:如果周期性进行线性扫描则为 O(n),若采用事件驱动或时间轮(priority queue),可做得更优。

    • 常见实现:保留一个时间索引或优先队列,回收操作是 O(log n)(优先队列)或 amortized O(1)。

总结:现实中对常见规模的网络(几百到几千台)来说,DHCP 的查找与分配操作基本上是常数平均时间;只有在数万设备且实现不佳时,性能问题才显现。

空间复杂度

空间开销主要来自以下几部分:

  1. 租约表:通常按客户端数(n)存储记录,每条记录包含 IP、MAC、租约时间、状态等信息。

    • 空间复杂度:O(n)。
  2. 地址池本身:地址池可以视作固定大小(m = 地址数量),通常是位图或区间列表。

    • 空间复杂度:O(m)。

总体:O(n + m)。在大多数小规模场景(几百台客户端、池大小数百)内,内存消耗极小。

总结

最后总结一些在实际部署时常遇到的问题和建议:

  1. 不要用即时命令替代持久化配置ifconfig/ ip addr add 仅临时,开机会丢。养成编辑 /etc/sysconfig/network-scripts/ifcfg-* 或使用 nmcli 的习惯。

  2. 配置文件语法检查 :配置 dhcpd.conf 后先用 dhcpd -tdhcpd -cf /etc/dhcp/dhcpd.conf -t 做语法检查,避免服务无法启动。

  3. 接口监听问题 :如果 dhcpd 没有在正确接口上监听,检查 /etc/sysconfig/dhcpdINTERFACES 配置,或系统日志里面的错误提示。

  4. 防火墙 / 中继(relay):如果客户端和服务器不在同一网段,需要在路由器上配置 DHCP 中继(DHCP relay),否则 DHCP 请求不会穿子网。并确保防火墙放通 UDP 67/68。

  5. 租期策略 :对访客网使用较短租期(例如 1 小时),对固定办公设备使用长租期或直接静态分配和保留(host 配置块)。

  6. 静态保留(按 MAC) :如果某些设备必须获得固定 IP,可以在 dhcpd.conf 中使用 host 块绑定 MAC 与 IP。示例:

    conf 复制代码
    host printer01 {
        hardware ethernet 00:11:22:33:44:66;
        fixed-address 192.168.10.20;
    }
  7. 日志排查journalctl -u dhcpd -f/var/log/messages(或系统对应日志)是最直接的排查点。检查常见错误比如 "no subnet declaration for " 表示请求来自未配置的子网。

相关推荐
头发那是一根不剩了8 小时前
Docker Desktop 安装教程和最佳实践
运维·docker·容器
路由侠内网穿透.9 小时前
本地部署轻量级持续集成工具 Drone CI 并实现外部访问
运维·服务器·ci/cd·远程工作
tianyuanwo10 小时前
K8s Dashboard运维技巧全面经验总结
linux·运维·kubernetes
无敌的牛11 小时前
Linux操作系统
linux·运维·服务器
顾小玙11 小时前
Linux : 进程概念
linux
半梦半醒*11 小时前
k8s——services资源+pod详解1
linux·运维·docker·kubernetes·centos·负载均衡
IndulgeCui11 小时前
【金仓数据库产品体验官】KSQL Developer Linux版安装使用体验
linux·运维·数据库
半旧夜夏11 小时前
【分布式缓存】Redis持久化和集群部署攻略
java·运维·redis·分布式·缓存
苹果醋312 小时前
element-ui源码阅读-样式
java·运维·spring boot·mysql·nginx