Ubuntu DHCP服务器配置与排错实战:地址池、租约与中继
2026/9/17 3:02:48 网站建设 项目流程

1. 先弄明白 Ubuntu 上这套 DHCP 服务到底要解决什么

上个月帮朋友收拾一个三十来人的小办公室,核心诉求其实很朴素:把 DHCP 从路由器上挪出来,交给一台 Ubuntu 服务器统一发地址。原来的做法是光猫下面接一台家用无线路由,无线路由开 DHCP,网络打印机、几台 NAS、二十多台电脑全都靠它随机分配。用了半年之后问题开始冒头——有人手动改了 IP,跟别人撞上;打印机重启后换了地址,所有人的驱动都得重配;路由器一重启,一屋子机器全断网好几分钟。

这就是“在 Ubuntu 上配置 DHCP 服务器”这件事的真实起点。它不是一个炫技的需求,而是你发现路由器自带的那套地址分配已经不够用了,想把控制权收回来,放到一台你能随时改配置、随时查日志、随时备份恢复的 Linux 主机上。

先说清楚这东西是什么。DHCP 是 Dynamic Host Configuration Protocol,动态主机配置协议,作用就一句话:客户端开机时不用知道自己的 IP 是多少,也不用管理员去挨个填,它自己广播一声,服务器把地址、掩码、网关、DNS 一起打包丢回来。Ubuntu 上常用来当这个服务器的软件有三类:ISC DHCP Server(软件包名isc-dhcp-server)、dnsmasq、以及更新一代的 Kea。前两个是绝大多数中小网络的主流选择。

它适合谁看这篇文章?三种人:一是手里有台闲置的 Ubuntu 机器或者虚拟机,想把它变成内网的地址分配出口;二是在做实验环境,需要模拟一个受控的地址分配过程;三是运维新人,天天听说 DHCP 但从来没自己从头配过一遍,想找个能照着敲的完整流程。如果你只是家里两三台设备上网,路由器自带的够用,没必要折腾;但只要设备上了两位数,或者你需要在地址分配上做策略,自建就是迟早的事。

1.1 DISCOVER、OFFER、REQUEST、ACK:四次交互里的关键字段

很多人配完 DHCP 就直接用了,但一旦客户端拿不到地址,脑子里没有报文交互的画面,排错基本靠猜。所以先把这段流程过一遍,后面排查环节会反复用到。

客户端刚启动,网卡还没有有效 IP,它唯一能做的就是广播。第一步它发出DHCPDISCOVER,源地址 0.0.0.0,目的地址 255.255.255.255,端口从 68 发到 67。这个包里最关键的是xid,一个随机事务号,用来把这一轮交互的所有报文串起来;还有chaddr,也就是客户端网卡 MAC,服务器最终认的就是它。

服务器收到 DISCOVER 之后,从地址池里挑一个空闲地址放到yiaddr字段里,回一个DHCPOFFER。注意这一步服务器还没真的把地址锁定给你,只是“报个价”。如果同一个网段里有多个 DHCP 服务器,客户端可能收到好几个 OFFER。

第三步客户端从收到的 OFFER 里挑一个,广播DHCPREQUEST,明确告诉所有人“我要这个地址,其他服务器可以撤了”。最后服务器回DHCPACK,客户端才正式把这个地址绑到网卡上,同时把租期、网关、DNS 一起解析进系统。

还有一个字段必须提:giaddr。同网段的情况下它是 0;跨网段的时候,DHCP 中继会把自己的接口地址填进去,服务器靠它判断“这个请求是从哪个子网过来的,该发哪个地址池的地址”。这是第 6 节讲中继时的核心依据。

1.2 什么时候该自己搭,什么时候老老实实用路由器

这一节看着像废话,但我见过太多人把力气花错地方。判断标准其实很简单:看你要的是“能上网”还是“可控”。

家庭场景、几台手机几台电脑,路由器自带的 DHCP 完全够用,还带图形界面,改一下地址池范围就行,自建纯属给自己找活。但如果出现下面任意一条,自建的价值就出来了:

  • 设备数量超过 30 台,路由器那点地址池和终端列表已经开始卡顿;
  • 需要给打印机、监控、NAS、门禁这类设备固定地址,而且固定规则经常变;
  • 需要按 MAC 做准入,比如只给登记过的设备发地址;
  • 需要按 VLAN 或者网段分别发地址,路由器的 DHCP 做不到这么细;
  • 需要查历史日志,搞清楚某台设备什么时候拿过哪个地址。

我做实验环境的时候几乎都会自己起一个,因为路由器的 DHCP 是黑盒,出问题只能靠猜;Ubuntu 上跑的服务,journalctl一敲,报文级别的日志全在眼前。

1.3 isc-dhcp-server、dnsmasq、Kea 该怎么挑

选型这块我踩过一次坑,值得单独说。

isc-dhcp-server是历史上最经典的实现,配置文件dhcpd.conf结构清晰,网上资料最多,遇到怪问题也最容易搜到答案。在 Ubuntu 22.04 及更早版本上装它最顺手。缺点是上游的 ISC 组织已经基本停止对这个分支做功能维护,转向 Kea 了。

dnsmasq是轻量派。一个进程同时干 DNS、DHCP、TFTP 三件事,配置文件不到十行就能跑起来,资源占用极低,特别适合放在树莓派、小主机、容器里。代价是它的 DHCP 不支持某些高级特性,比如复杂的分类策略、执行外部脚本这类需求。

Kea是新一代,配置用 JSON,支持通过 API 动态改配置、把租约存进数据库,适合设备量上千、需要自动化的场景。但它的配置心智负担明显更高,小网络用它有点杀鸡用牛刀。

我的建议是:实验环境和小办公室用 dnsmasq,需要精细化控制和大量固定绑定用 isc-dhcp-server。下面的实操两条线都跑一遍,你可以按自己的场景挑。


2. 动手前的规划:地址池、网关和保留段怎么切

配置写得再漂亮,规划错了照样出问题。DHCP 的坑大多不在命令上,而在地址段的划分上。这一节先把账算清楚,后面配置只是把这笔账翻译成配置语法。

先约定本文贯穿使用的示例环境,后面所有配置都基于它:

项目
Ubuntu 服务器地址192.168.10.10/24
服务器网卡ens18
网关(同一台 Ubuntu 兼任)192.168.10.1
网段192.168.10.0/24
动态地址池192.168.10.100 – 192.168.10.200
固定保留段192.168.10.2 – 192.168.10.99
广播地址192.168.10.255

2.1 为什么必须先把服务器自己的地址写死

这是新手最容易跳过的一步,也是后面“配好了但服务起不来”的第一个源头。DHCP 服务器自己的 IP 不能是动态获取的,因为它一旦重启后换了地址,所有客户端配置里的网关和 DNS 就全指向一个不存在的地址了。

在 Ubuntu 18.04 之后,网络的静态配置统一走 netplan,配置文件放在/etc/netplan/下,常见文件名是00-installer-config.yaml50-cloud-init.yaml。内容大概长这样:

network: version: 2 ethernets: ens18: dhcp4: false addresses: - 192.168.10.10/24 routes: - to: default via: 192.168.10.1 nameservers: addresses: [223.5.5.5, 119.29.29.29]

写完之后不要直接netplan apply就完事,先用sudo netplan try。这个命令会应用配置并且给你 120 秒反悔时间,如果你是通过 SSH 连上去操作的,配置写错导致网络断了,它会自动回滚。这个机制救过我至少两次。

注意:如果原来这台机器是用dhclient动态获取地址的,改完 netplan 后记得确认ip addr里目标网卡上只有一个地址,残留的旧地址会在后续启动 DHCP 服务时引发奇怪的监听失败。

2.2 保留段和动态池的切分逻辑

为什么要切出 192.168.10.2 到 192.168.10.99 这么一大段静态保留区?因为网关、交换机管理地址、打印机、NAS、AP、监控摄像头这类设备,你迟早会想给它们固定地址。如果动态池从 .2 开始发,早晚有一天会和手工配的一台设备撞车,而这种撞车症状极其隐蔽——表现为“某台机器偶尔上不了网”。

我的习惯是把 .2 到 .99 全部留空,动态池从 .100 起。如果设备多了动态池不够用,宁可把网段扩成 /23,也不要往回侵占保留段的尾巴。

还有一个细节:动态池的地址数量要留出余量。一个 100 个地址的池子,如果同时有 95 台设备在线,接近满的时候新设备就可能拿不到地址。经验值是把池子容量维持在峰值在线设备数的 1.5 到 2 倍。

2.3 租期设多长才算合适:一次可以算明白的账

租期(lease time)是地址池规划里唯一需要算的东西。设太长,设备搬走了地址还占着;设太短,网络里全是续租报文,客户端断网风险也增加。

粗略的估算方式是:地址池容量 ÷ 峰值同时在线设备数 ≥ 租期 ÷ 设备平均在线时长

举个具体的例子。办公室有 80 台设备需要上网,动态池是 101 个地址。设备平均每天在线 9 小时。如果租期设 12 小时(43200 秒),那么在 12 小时内,理论上会有 80 × 12 ÷ 9 ≈ 107 台次设备需要地址,但同一时刻在线的还是 80 台左右,池子够用但余量不多。这时候把租期压到 8 小时就够了,因为设备下班关机后地址很快回收。

反过来,如果池子只有 50 个地址但实际有 80 台设备,无论租期怎么调都救不了,只能扩网段。这种情况下客户端的症状是:一部分机器能上网,一部分反复拿不到地址,日志里能看到地址池耗尽的提示。

对于笔记本电脑、手机这类会频繁进出的设备,我把租期设成 6 到 8 小时;对于固定不动的打印机、摄像头,用 host 声明绑死,压根不参与动态分配。默认租期和最大租期一般按 1:2 的比例设置,比如 default 28800 秒、max 86400 秒。


3. isc-dhcp-server 的安装与 dhcpd.conf 逐段拆解

规划做完,进入实操。这一节的思路是把配置文件按功能切成四块讲:全局参数、subnet 段、host 声明、以及启动前的自检。这样你以后改配置的时候,知道该往哪个位置加东西。

3.1 装包、指定监听网卡,这两步顺序不能反

安装本身没有难度:

sudo apt update sudo apt install -y isc-dhcp-server

装完之后先别急着启动。Ubuntu 的isc-dhcp-server有一个单独的文件控制它监听哪块网卡,路径是/etc/default/isc-dhcp-server

INTERFACESv4="ens18" INTERFACESv6=""

这一行的作用比它看起来重要得多。如果不指定,服务会尝试在所有网卡上监听,包括回环、虚拟网桥、容器网卡。在多网卡机器上,这会导致客户端收到的 OFFER 里带的网关地址属于另一块网卡所在的网段,表现出来就是“拿到地址了但上不了网”。第 4 节会专门讲这个坑的排查过程。

3.2 dhcpd.conf 的全局参数:服务的默认行为

主配置在/etc/dhcp/dhcpd.conf。默认安装后这个文件里是一大段注释,实际生效的内容为空,直接启动会因为缺少子网声明而失败。我们从头写一个:

default-lease-time 28800; max-lease-time 86400; authoritative; ddns-update-style none; option domain-name "office.lan"; option domain-name-servers 192.168.10.1, 223.5.5.5; option subnet-mask 255.255.255.0;

逐行说。

default-lease-timemax-lease-time是默认租期和客户端能申请的上限,单位是秒。客户端在续租时如果没提要求,就用默认值。

authoritative这一行我强烈建议加上。它的含义是告诉服务器“这个网段的 DHCP 权威在我这里”。加上之后,如果客户端申请了一个并不存在的地址,服务器会直接回 DHCPNAK 让它重新申请;不加的话,服务器会选择沉默,客户端就会一直卡在原来的地址上不更新。早期的 DHCP 协议没有这个字段,很多排错教程里让服务器静默,反而掩盖了问题。

ddns-update-style none是关掉动态 DNS 更新。除非你同时维护着 DNS 服务器并且希望租约变更时自动更新记录,否则关掉,省得引入额外的故障面。

option domain-name-servers是发给客户端的 DNS 地址。这里我写了两条,客户端按顺序尝试。如果你这台 Ubuntu 同时跑着 dnsmasq 做内网 DNS,那就把 192.168.10.1 写在前面,公共 DNS 放后面兜底。

3.3 subnet 段:把地址池翻译成配置

全局参数写完之后,加子网声明:

subnet 192.168.10.0 netmask 255.255.255.0 { range 192.168.10.100 192.168.10.200; option routers 192.168.10.1; option broadcast-address 192.168.10.255; }

subnet这一行必须和配置服务器网卡所在的网段严格对应,写成 192.168.10.1 开头的地址会报错。range就是动态池,可以写多行,比如把 .100–.150 和 .180–.200 分成两段发,中间留出空隙给别的用途。

option routers是网关,必须在同一个子网里。这里有个容易忽略的点:如果这台 Ubuntu 本身不做路由,那这一项应该填真正干路由的那台设备的地址,不要想当然填自己。

option broadcast-address一般不用手写,服务器会算出来,但显式写出来可读性更好,也方便以后换网段时对照检查。

如果要按设备类型分类,还可以在 subnet 段里用 class:

class "printers" { match if substring(hardware, 1, 3) = 00:11:22; } subnet 192.168.10.0 netmask 255.255.255.0 { pool { range 192.168.10.50 192.168.10.80; allow members of "printers"; } pool { range 192.168.10.100 192.168.10.200; deny members of "printers"; } }

上面的substring(hardware, 1, 3)是按 MAC 前三段做前缀匹配,0 到 255 的十六进制表示是00ff,十六进制转换这块不用记,直接用交换机的 MAC 表复制就行。

3.4 host 声明:给固定设备钉死地址

打印机、NAS、交换机管理口这类设备,用 host 声明绑定最简单:

host printer-hp { hardware ethernet a4:5e:60:12:34:56; fixed-address 192.168.10.20; } host nas-synology { hardware ethernet 00:11:32:ab:cd:ef; fixed-address 192.168.10.21; }

这里有几个实操要点。

第一,fixed-address 要落在 subnet 段之外,也就是放在我前面划的保留区(.2 到 .99)里,别写进 range 范围,否则动态池可能会先把这个地址发出去。

第二,host 声明可以放在 subnet 上面,也可以放在里面,位置不影响功能,但放在上面更清晰。

第三,如果设备同时有有线和无线网卡,会有两个 MAC,需要写两条 host 声明并且给两个不同地址,否则会出现“插网线时是一个地址,拔了网线换 WiFi 又变成另一个地址”的混乱情况。

第四,也是最容易出错的:修改 MAC 绑定后必须清理租约文件,否则客户端可能还拿着旧的租约。清理方式见 3.5。

3.5 启动之前的自检:语法、租约、日志三件套

配置文件写完,不要直接 restart 然后祈祷。按顺序走这三步:

sudo dhcpd -t -cf /etc/dhcp/dhcpd.conf

这个命令只做语法检查,不会真的启动服务,输出会直接指出哪一行有问题。语法错误里最常见的是漏打分号、subnet和网卡网段不匹配、range 地址不在子网里。

第二步,确认租约文件存在并且有权限:

sudo touch /var/lib/dhcp/dhcpd.leases sudo chown dhcpd:dhcpd /var/lib/dhcp/dhcpd.leases

Ubuntu 的包安装时会自动创建,但如果你是从别处拷贝的配置或者做过迁移,这个文件缺失会导致服务启动后立刻退出,报错信息还特别模糊。

第三步,启动并看状态:

sudo systemctl restart isc-dhcp-server sudo systemctl status isc-dhcp-server --no-pager sudo journalctl -u isc-dhcp-server -n 50 --no-pager

status关注的是服务有没有活着,journalctl关注的是它启动过程中说了什么。只要这两条命令的输出里没有failedno subnet declaration,基本就成功了。


4. 服务起不来、客户端拿不到地址的排查链路

配置一次就成功的概率其实不高,尤其是第一次上手。这一节我把踩过的坑按“报错信息 → 根因 → 处理”的顺序整理出来,你可以直接对照自己的现象定位。

4.1 启动即失败的三类典型报错

报错一:No subnet declaration for ens18 (192.168.10.10)

这是最高频的一条。意思是服务器在这块网卡上找不到匹配的子网声明。检查两件事:/etc/default/isc-dhcp-serverINTERFACESv4填的是不是ens18dhcpd.conf里的subnet网段是不是和网卡地址一致。很多人网卡地址是 192.168.1.x,配置里却写了 192.168.10.0,直接对不上。

报错二:Can't open /var/lib/dhcp/dhcpd.leases

租约文件缺失或者权限不对。注意 Ubuntu 上这个文件所在的目录是/var/lib/dhcp/,不是某些老教程里写的/var/lib/dhcpd/,路径写错也会报这个。

报错三:Not configured to listen on any interfaces!

INTERFACESv4那一行被注释掉了,或者网卡名拼错了。查网卡名用ip -br link,别凭印象写eth0,现在新版本 Ubuntu 用的是可预测网卡命名,实际名字可能是ens18enp3s0

4.2 客户端卡在 169.254:一条完整的排查链路

客户端拿不到地址的典型症状是网卡上出现一个 169.254.x.x 的地址,这是系统自己给的临时地址(APIPA),表示“我努力过了但没人理我”。

排查链路我按从近到远走一遍:

第一步,看服务器有没有收到请求。在服务器上敲:

sudo journalctl -u isc-dhcp-server -f

然后在客户端上触发一次重新申请(Linux 用sudo dhclient -v ens18,Windows 用ipconfig /releaseipconfig /renew)。如果服务器日志里冒出DHCPDISCOVER from aa:bb:... via ens18,说明报文到了服务器,问题在服务器侧;如果日志纹丝不动,说明请求根本没到,问题在网络链路上。

第二步,区分是二层不通还是被拦了。报文不到服务器有两种可能:物理链路断了(交换机口没插好、VLAN 划分对不上),或者中间有设备把 DHCP 广播拦了。先看网卡灯,再用sudo tcpdump -i ens18 port 67 or port 68 -n抓包。tcpdump 里能看到 DISCOVER 说明广播还在传,只是服务器没响应;一个包都没有就是二层的问题。

第三步,看服务器为什么没响应。如果日志里看到了 DISCOVER 但没有 OFFER,常见原因是地址池耗尽,或者 MAC 不匹配任何 pool。用dhcpd -t检查配置,再看/var/lib/dhcp/dhcpd.leases里的地址占用情况。

第四步,检查防火墙。UFW 默认不拦 DHCP 的 67/68 端口,但如果之前手动加过规则就得确认。临时关掉 UFW 测试一下是最快的判断方式:

sudo ufw disable

确认是防火墙问题后再针对性放行,别一直关着。

这四步走完,90% 的“拿不到地址”都被定位了。

4.3 多网卡机器上的静默故障

这条坑我自己踩过,而且它不会报任何错,最难查。

某台 Ubuntu 服务器上除了 ens18 还有一张 ens19 连到测试网段。INTERFACESv4里只写了 ens18。服务能正常启动,看起来一切正常。但办公室的电脑拿到的地址虽然对,网关却是错的,表现是“IP 拿到了,ping 网关通,ping 外网不通”。

原因是服务在响应时选择了默认路由所在的接口来发 OFFER,而这个接口属于另一个网段。解决办法有两个:一是把INTERFACESv4里所有相关网卡都加进去,但要确保每个网段在配置里都有对应的 subnet 声明;二是干脆用bind-interfaces之类的机制限制发送接口(dnsmasq 有类似选项)。对于 ISC DHCP,更干净的做法是让服务器只保留一块业务网卡参与 DHCP。

排查这类问题的关键是:拿到地址之后,第一件事是ipconfig /all或者ip route看网关和 DNS 对不对,而不是急着 ping 外网。地址对不代表配置对。

4.4 日志和租约文件里值得盯的几个字段

journalctl的输出里,下面这几个关键词值得记住:

日志片段含义
DHCPDISCOVER from <mac> via <iface>收到客户端请求,且知道了从哪块网卡进来的
DHCPOFFER on <ip> to <mac>服务器给出了地址,但客户端还没确认
DHCPREQUEST for <ip> from <mac>客户端在确认要这个地址
DHCPACK on <ip> to <mac>分配完成,地址正式生效
DHCPNAK服务器拒绝了客户端的申请,客户端要重新来
no free leases地址池满了,需要扩池或者缩短租期

租约文件/var/lib/dhcp/dhcpd.leases是纯文本,想看某台设备现在拿的是哪个地址,直接搜它的 MAC:

grep -B3 'ab:cd:ef' /var/lib/dhcp/dhcpd.leases

每个 lease 块里有startsendsbinding state字段。binding state active表示正在用,free表示已回收。我排查地址冲突的时候,会把这个文件按ends排序,看看哪些地址被长期占着但实际设备早就不在了。


5. 上级光猫、路由器也在发地址时怎么办

这一节的场景非常现实:很多人是在光猫下面接交换机,交换机上再挂这台 Ubuntu。光猫自带的 DHCP 还开着,于是同一个网段里出现了两个地址分配者。搜索结果里“运营商给的光猫路由器不能关闭 DHCP”这类问题之所以高频,就是因为用户根本动不了那台设备的配置。

5.1 双 DHCP 环境下的典型症状

同网段存在两个 DHCP 服务器时,客户端可能随机接受任意一个服务器的 OFFER,出现“这台电脑今天拿的是 192.168.1.x,明天变成 192.168.10.x”。如果两个服务器发的网关不同,就会出现有的机器能上网、有的不能,而且每次重启后结果还会变。

识别方法是抓包看 OFFER 的来源:

sudo tcpdump -i ens18 -n port 67 or port 68 -e

-e会打印链路层信息,你能从输出里看到每个 OFFER 报文是从哪个 MAC 发出来的。如果看到两个不同的源 MAC 在发 OFFER,双服务器的事实就确认了。

5.2 光猫关不掉的现实处理方式

如果上级设备的 DHCP 确实关不掉,有三条路可以走,按代价从低到高排列:

路子一:错开网段。把 Ubuntu 所在的网络划到一个独立网段,用路由或者 NAT 跟光猫隔开。这要求多一台设备或者让 Ubuntu 兼任路由,属于变通方案。

路子二:在交换机上做隔离。把连接 Ubuntu 的端口和普通办公端口划到同一个 VLAN,把上联光猫的端口划到另一个 VLAN,从二层上把广播域切开。这是最干净的解法,但需要有网管交换机。

路子三:让客户端的请求发不出去。这个思路比较硬核,一般用在实验环境。原理是让 DHCP 广播在中间设备上被丢弃,只留下我们自己服务器的响应路径。生产环境不建议这么干,容易牵连其他依赖广播的协议。

5.3 DHCP Snooping:从交换机层面把非授权服务器拦掉

如果有可管理的交换机,DHCP Snooping 是标准做法。它的逻辑很直接:交换机的每个端口有“信任”和“非信任”两种状态。只有信任端口转发的 DHCP OFFER 和 ACK 才允许放行,非信任端口上收到的这两类报文直接丢弃。

配置时的关键是把连接合法 DHCP 服务器的端口和上联口设为信任,其他所有接终端的端口保持默认的非信任状态。这样即使有人把一台家用路由器插到办公网口上想“自己搭一个 DHCP”,它的 OFFER 也会被交换机吃掉,接在同一交换机上的同事不会受影响。

这个功能在国产交换机上基本是标配,命令写法各厂商略有差异,但概念完全一致:找dhcp snooping相关的开关,把信任口列出来。上线前先用一台测试机验证一下,确认合法服务器的响应没被误拦。

提示:启用 DHCP Snooping 之后,如果信任口配置漏了上联口,会出现整个楼层都拿不到地址的情况,而且服务端日志完全正常。改完配置先小范围验证。

5.4 上游关不掉时,用划分网段的方式一劳永逸

从长期看,最省心的做法还是把地址分配彻底分层:光猫只管自己那一层,办公网用自己的网段和自己的 DHCP。具体做法是在 Ubuntu 上开两块网卡或者两个 VLAN 子接口,一块接上游,一块接办公网,然后用 netplan 配置路由转发和 NAT。

这样做的额外好处是,你可以在 Ubuntu 上直接看到所有内网设备的进出流量,出问题的时候不用在两台设备之间来回切换登录。代价是这台机器成了单点,一旦挂了内网就断网,所以建议至少把配置和租约文件定期备份出来。备份的做法在第 6 节。


6. 从单网段走向多网段:中继、批量固定地址与备份

前面的配置只管一个网段。设备一多、楼层一分,就会出现一个网络里多个子网的情况,这时候 DHCP 有两条路:每个网段各放一台服务器,或者用一台服务器加中继。

6.1 DHCP 中继:让一台服务器管多个网段

DHCP 报文本身是广播的,路由器默认不会转发广播。中继(relay agent)的作用就是把本网段的广播收下来,单播转发到指定服务器,并且在包的 giaddr 字段里填上自己的接口地址,让服务器知道该发哪个地址池的地址。

在 Ubuntu 上装中继:

sudo apt install -y isc-dhcp-relay

配置文件/etc/default/isc-dhcp-relay

SERVERS="192.168.10.10" INTERFACES="ens19" OPTIONS=""

SERVERS填真正的 DHCP 服务器地址,INTERFACES填中继需要在哪些接口上监听(通常是朝下连接客户端的那张网卡)。改完重启服务:

sudo systemctl restart isc-dhcp-relay

服务器侧要同步加上第二个 subnet 声明:

subnet 192.168.20.0 netmask 255.255.255.0 { range 192.168.20.100 192.168.20.200; option routers 192.168.20.1; }

有个细节值得记下来:服务器上必须有对应网段的路由。如果服务器和 192.168.20.0/24 之间没有路由,它收到 giaddr 指向的请求也没法把响应发回去,日志里会出现“找不到路由”之类的提示。

6.2 固定地址多了以后怎么管

当 host 声明超过二三十条,配置文件会变得很难维护。我的做法是把固定绑定拆成单独文件,在主配置里 include 进来:

在主配置dhcpd.conf末尾加一行:

include "/etc/dhcp/fixed-hosts.conf";

然后所有 host 声明都放进/etc/dhcp/fixed-hosts.conf。这样改绑定的时候不会误碰 subnet 和全局参数,也方便用脚本从表格批量生成。我通常维护一张 CSV,字段是“设备名、MAC、IP、备注”,用一段简单的 awk 生成配置文件:

awk -F, 'NR>1 {printf "host %s {\n hardware ethernet %s;\n fixed-address %s;\n}\n\n", $1, $2, $3}' hosts.csv > /etc/dhcp/fixed-hosts.conf

生成完还是要跑一次dhcpd -t验证。格式化脚本出问题的时候,语法检查是最快的兜底。

6.3 配置和租约的备份与恢复

这部分内容常规文档几乎不写,但真出事的时候能救命。

需要备份的东西其实很少:

路径内容
/etc/dhcp/dhcpd.conf主配置
/etc/dhcp/fixed-hosts.conf固定绑定(如果拆分了)
/etc/default/isc-dhcp-server监听网卡声明
/var/lib/dhcp/dhcpd.leases当前租约,恢复后客户端不用重新申请
/etc/netplan/*.yaml本机网络配置

租约文件要不要备份看场景。恢复出厂环境的时候,如果连租约一起恢复,客户端开机后能直接续租原来的地址,网络恢复时间从几分钟降到几秒。备份方式我用最简单的 tar:

tar czf /root/dhcp-backup-$(date +%F).tar.gz \ /etc/dhcp /etc/default/isc-dhcp-server /var/lib/dhcp /etc/netplan

放到定时任务里每周跑一次,或者配置改完手动跑一次。恢复时注意停掉服务再解包,解完检查一遍权限——dhcpd.leases的属主必须是dhcpd,拷回来变成 root 的话服务又起不来。

6.4 换用 dnsmasq:十行配置搞定的轻量方案

如果你只是想要一个稳定发地址的服务,不做复杂策略,dnsmasq 更省事。装完之后的配置文件是/etc/dnsmasq.d/office.conf

interface=ens18 bind-interfaces dhcp-range=192.168.10.100,192.168.10.200,255.255.255.0,8h dhcp-option=3,192.168.10.1 dhcp-option=6,192.168.10.1 dhcp-host=a4:5e:60:12:34:56,192.168.10.20 dhcp-authoritative

bind-interfaces加上interface这两行的组合,是为了避免 4.3 节说的多网卡问题。dhcp-option=3对应网关,dhcp-option=6对应 DNS,这是 DHCP 选项号,记不住也没关系,注释写清楚就行。dhcp-host就是固定绑定,格式是 MAC 在前地址在后。

改完重启:

sudo systemctl restart dnsmasq sudo systemctl status dnsmasq --no-pager

dnsmasq 的日志默认写在系统日志里,用journalctl -u dnsmasq -f看。它输出的信息比 ISC DHCP 精简,但够用。有一个坑要提醒:dnsmasq 默认会同时开 DNS 服务并占用 53 端口,如果这台机器上已经跑了别的 DNS 服务,启动会失败,需要在配置里显式关掉 DNS 功能,或者换端口。


7. 上线之后容易被忽略的几处运维细节

配置能跑起来只是第一步,长期稳定运行还有几个点值得提前处理,这些都是我在实际维护中撞出来的。

7.1 租约文件会一直变大,日志也需要轮转

/var/lib/dhcp/dhcpd.leases不会无限增长,但它的伴生文件dhcpd.leases~会。这个文件是租约数据库的临时备份,正常情况下来回切换,体积和主文件差不多。如果发现它异常膨胀到几百 MB,通常是服务反复重启或者磁盘出过问题,值得查一下。

日志方面,journalctl的默认保留策略由 systemd 控制,可以看一下当前占用:

journalctl --disk-usage

如果打算长期保存 DHCP 日志用来追溯设备上线记录,建议把日志输出单独指向一个文件。在服务配置里加启动参数是其中一种方式,更简单的做法是在 rsyslog 里按程序名分流。日积月累的日志对排查“某个 MAC 什么时候第一次出现”这类问题非常有用。

7.2 时间不同步会让租约时间看起来莫名其妙

DHCP 的租约时间是基于服务器本地时间算的。如果服务器时间漂了几个小时,租约文件里的startsends就会看起来很怪,排查问题时会误导你。

在 Ubuntu 上确认时间同步状态:

timedatectl status

正常应该看到System clock synchronized: yes。如果这台服务器在内网、连不到外部时间源,可以在配置里把内网的时间服务器地址通过option ntp-servers下发给客户端:

option ntp-servers 192.168.10.10;

这样客户端拿到地址的同时也知道了该找谁对时,整个内网的时间会趋于一致,日志对照起来方便很多。

7.3 虚拟机克隆之后 MAC 和小主机名重复的麻烦

最后这条是虚拟化环境特有的。用模板克隆出来的虚拟机,网卡 MAC 如果不重新生成,会和新克隆出来的机器撞。DHCP 服务器按 MAC 分配地址,两台机器抢一个地址,租约文件里会看到同一个 MAC 短时间内反复出现不同绑定,客户端表现为地址时好时坏。

处理方式是克隆之后先重新生成 MAC,再开机。另外,用 host 声明做固定绑定的时候,如果克隆机沿用了模板的 MAC,它会直接拿到模板预留的那个固定地址,和原机器冲突,这个问题的隐蔽性比动态分配冲突更高。

提示:虚拟化平台上的 DHCP 排错,第一件事就是核对虚拟机的 MAC 是不是唯一的,别急着改服务器配置。

再补一个小的运维习惯。我一般会在服务器上留一个只读的核对脚本,把当前租约文件的活跃记录导出来,按地址排序打印,跟交换机的 MAC 地址表对一遍,差集部分就是要处理的异常设备:

awk '/^lease/ {ip=$2} /hardware ethernet/ {mac=$3} /binding state active/ {print ip, mac}' \ /var/lib/dhcp/dhcpd.leases | sort -t. -k4 -n

这条命令每次排查网络异常时都会用到,输出干净,比在日志里翻要快得多。地址分配的这套东西,真正花时间的从来不是配置本身,而是事后对账和追溯,能提前把这一环准备好,后面能省下大量时间。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询