
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.2552.1 为什么必须先把服务器自己的地址写死这是新手最容易跳过的一步也是后面“配好了但服务起不来”的第一个源头。DHCP 服务器自己的 IP 不能是动态获取的因为它一旦重启后换了地址所有客户端配置里的网关和 DNS 就全指向一个不存在的地址了。在 Ubuntu 18.04 之后网络的静态配置统一走 netplan配置文件放在/etc/netplan/下常见文件名是00-installer-config.yaml或50-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-serverINTERFACESv4ens18 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-time和max-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 段里用 classclass 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 的十六进制表示是00到ff十六进制转换这块不用记直接用交换机的 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.leasesUbuntu 的包安装时会自动创建但如果你是从别处拷贝的配置或者做过迁移这个文件缺失会导致服务启动后立刻退出报错信息还特别模糊。第三步启动并看状态sudo systemctl restart isc-dhcp-server sudo systemctl status isc-dhcp-server --no-pager sudo journalctl -u isc-dhcp-server -n 50 --no-pagerstatus关注的是服务有没有活着journalctl关注的是它启动过程中说了什么。只要这两条命令的输出里没有failed和no subnet declaration基本就成功了。4. 服务起不来、客户端拿不到地址的排查链路配置一次就成功的概率其实不高尤其是第一次上手。这一节我把踩过的坑按“报错信息 → 根因 → 处理”的顺序整理出来你可以直接对照自己的现象定位。4.1 启动即失败的三类典型报错报错一No subnet declaration for ens18 (192.168.10.10)这是最高频的一条。意思是服务器在这块网卡上找不到匹配的子网声明。检查两件事/etc/default/isc-dhcp-server里INTERFACESv4填的是不是ens18dhcpd.conf里的subnet网段是不是和网卡地址一致。很多人网卡地址是 192.168.1.x配置里却写了 192.168.10.0直接对不上。报错二Cant 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 用的是可预测网卡命名实际名字可能是ens18、enp3s0。4.2 客户端卡在 169.254一条完整的排查链路客户端拿不到地址的典型症状是网卡上出现一个 169.254.x.x 的地址这是系统自己给的临时地址APIPA表示“我努力过了但没人理我”。排查链路我按从近到远走一遍第一步看服务器有没有收到请求。在服务器上敲sudo journalctl -u isc-dhcp-server -f然后在客户端上触发一次重新申请Linux 用sudo dhclient -v ens18Windows 用ipconfig /release加ipconfig /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是纯文本想看某台设备现在拿的是哪个地址直接搜它的 MACgrep -B3 ab:cd:ef /var/lib/dhcp/dhcpd.leases每个 lease 块里有starts、ends、binding 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-relaySERVERS192.168.10.10 INTERFACESens19 OPTIONSSERVERS填真正的 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, NR1 {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本机网络配置租约文件要不要备份看场景。恢复出厂环境的时候如果连租约一起恢复客户端开机后能直接续租原来的地址网络恢复时间从几分钟降到几秒。备份方式我用最简单的 tartar 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.confinterfaceens18 bind-interfaces dhcp-range192.168.10.100,192.168.10.200,255.255.255.0,8h dhcp-option3,192.168.10.1 dhcp-option6,192.168.10.1 dhcp-hosta4:5e:60:12:34:56,192.168.10.20 dhcp-authoritativebind-interfaces加上interface这两行的组合是为了避免 4.3 节说的多网卡问题。dhcp-option3对应网关dhcp-option6对应 DNS这是 DHCP 选项号记不住也没关系注释写清楚就行。dhcp-host就是固定绑定格式是 MAC 在前地址在后。改完重启sudo systemctl restart dnsmasq sudo systemctl status dnsmasq --no-pagerdnsmasq 的日志默认写在系统日志里用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 的租约时间是基于服务器本地时间算的。如果服务器时间漂了几个小时租约文件里的starts和ends就会看起来很怪排查问题时会误导你。在 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这条命令每次排查网络异常时都会用到输出干净比在日志里翻要快得多。地址分配的这套东西真正花时间的从来不是配置本身而是事后对账和追溯能提前把这一环准备好后面能省下大量时间。