IPv6双栈服务器配置实战:从报错到压测
发布日期: 2026/08/17 阅读总量: 2

一次IPv6接入事故:地址有了,路由不通

公司给线上服务器开IPv6,云控制台上显示IPv6地址已分配。我SSH登录,ip -6 addr一看,地址确实在网卡上。但ping6 2400:3200::1(阿里DNS)直接超时,curl -6 https://www.taobao.com卡到报错。

排查了半小时,最后发现两个问题:

  • Cloud-init生成的网卡配置文件里,IPV6INIT=no,系统广播了RA(Router Advertisement)但没让IPv6生效
  • 云安全组只放行了ICMPv4,没放行ICMPv6,导致ping6不通

IPv6不是“地址配上就完事”。它和IPv4有本质区别:无状态地址自动配置(SLAAC)、重复地址检测(DAD)、ICMPv6的NDP协议,任何一个环节出问题,地址就是“死”的。

这篇文章记录这次完整过程:问题定位、SLAAC和DHCPv6两种方案的选择、Nginx双栈监听配置、以及最后的压测数据。

问题分析:IPv6双栈为什么连不通

服务器环境:

项目版本/型号
操作系统CentOS Stream 9
内核5.14.0-427.el9.x86_64
Nginx1.24.0
网卡ens3(virtio)
IPv4地址1.2.3.4(示例)
IPv6地址2409:8a55:1a2b:3c00::5(示例)

IPv6双栈服务器要正常工作,需要四个链路全部通:

  1. 网卡有IPv6地址(链路本地+全局)
  2. 有默认IPv6路由(通过RA或DHCPv6获取)
  3. DNS能解析AAAA记录(或直连IP测试)
  4. 防火墙/安全组放行IPv6协议(ICMPv6、TCP、UDP)

我的案例里,第1条和第2条都“看起来正常”:地址有,路由表也有default via fe80::1。但实际网络层不通——因为安全组把ICMPv6全拦了,NDP协议(邻居发现协议)无法完成邻居解析,数据包发不出去。

这个坑后面细讲。下面先看两种IPv6地址分配方案。

方案对比:SLAAC vs DHCPv6

服务器接入IPv6双栈,核心是“怎么拿地址”。两种常见方式:

维度SLAAC(无状态自动配置)DHCPv6(有状态配置)
地址分配方式网卡根据RA报文自行生成DHCP服务器分配
可控性低,无法记录谁拿了哪个地址高,可以按MAC绑定固定IP
配置复杂度低(路由器/网关只需开RA)中(需要部署DHCPv6服务)
运营商支持国内部分云厂商/运营商支持支持较广泛
排障难度中,需要抓包看RA低,有明确租约记录
适用场景家庭网关、路由器、临时测试企业服务器、数据中心

我这次用的是SLAAC,因为云厂商已经在链路里广播RA,网卡配置好就能自动拿地址。如果你的网络环境支持DHCPv6,或者需要固定地址便于审计,建议用DHCPv6。下面两种方案都给出完整配置。

方案一:SLAAC无状态配置(CentOS Stream 9)

1. 检查内核IPv6是否启用

# 检查内核模块
cat /proc/net/if_inet6
# 输出包含以下行说明IPv6已启用
# 00000000000000000000000000000001 01 80 10 80       lo
# fe800000000000000000000000000000 03 40 20 80     ens3

# 检查sysctl配置,确认没有禁用
sysctl net.ipv6.conf.all.disable_ipv6
# 必须输出 0,如果是1,执行:
# echo "net.ipv6.conf.all.disable_ipv6=0" >> /etc/sysctl.conf
# sysctl -p

2. 配置网卡启用IPv6自动获取

CentOS Stream 9使用NetworkManager管理网络。用nmcli配置双栈:

# 查看当前连接名称(一般是ens3或Wired connection 1)
nmcli connection show

# 修改连接配置,启用IPv6并设为自动获取(SLAAC+DHCPv6)
nmcli connection modify "ens3" \
  ipv6.method auto \
  ipv6.addr-gen-mode stable-privacy \
  ipv6.dhcp-duid ll \
  ipv6.dhcp-iaid mac

# 重新激活连接
nmcli connection down "ens3" && nmcli connection up "ens3"

# 验证IPv6地址和路由
ip -6 addr show ens3
ip -6 route show

关键参数:

  • ipv6.method auto:启用SLAAC同时尝试DHCPv6
  • ipv6.addr-gen-mode stable-privacy:生成稳定且隐私保护的地址(RFC 7217),避免每次重启地址变化
  • ipv6.dhcp-duid ll:使用链路层地址作为DUID

3. 验证SLAAC是否生效

# 查看全局IPv6地址(ens3上应该有2409开头的地址)
ip -6 addr show ens3
# 输出示例:
# inet6 2409:8a55:1a2b:3c00::5/128 scope global dynamic
# inet6 fe80::5054:ff:fe12:3456/64 scope link

# 查看默认路由
ip -6 route show default
# 输出示例:
# default via fe80::1 dev ens3 proto ra metric 100

# 测试IPv6连通性(阿里DNS)
ping6 -c 4 2400:3200::1

方案二:DHCPv6有状态分配(Kea DHCPv6)

如果你没有RA广播,或者需要固定IPv6地址,用DHCPv6。这里用Kea DHCPv6 2.4.0作为服务端,它比系统自带的dhcpd更易配置。

1. 安装Kea DHCPv6

# CentOS Stream 9
dnf install -y epel-release
dnf install -y kea-dhcp6-server

# 验证版本
kea-dhcp6 --version
# Kea DHCPv6 2.4.0

2. 配置Kea(JSON格式)

{
  "Dhcp6": {
    "interfaces-config": {
      "interfaces": ["ens3"]
    },
    "lease-database": {
      "type": "memfile",
      "name": "/var/lib/kea/kea-dhcp6.leases"
    },
    "preferred-lifetime": 3600,
    "valid-lifetime": 7200,
    "subnet6": [
      {
        "subnet": "2409:8a55:1a2b:3c00::/64",
        "pd-pools": [],
        "pools": [
          { "pool": "2409:8a55:1a2b:3c00::100 - 2409:8a55:1a2b:3c00::1ff" }
        ],
        "reservations": [
          {
            "hw-address": "52:54:00:12:34:56",
            "ip-addresses": ["2409:8a55:1a2b:3c00::5"]
          }
        ]
      }
    ]
  }
}

3. 启动Kea并设置开机自启

systemctl enable --now kea-dhcp6-server
journalctl -u kea-dhcp6-server -f
# 看到 "DHCPv6 server start" 说明启动成功

4. 客户端配置(Ubuntu Netplan)

如果你的服务器是Ubuntu(我测试用的备用机),Netplan配置如下:

network:
  version: 2
  ethernets:
    ens3:
      dhcp4: yes
      dhcp6: yes
      accept-ra: yes
      # 使用DHCPv6获取地址,同时接受RA中的路由信息
      ipv6-privacy: true

Nginx双栈监听配置

地址通了,Web服务也要同时监听IPv4和IPv6。Nginx默认只监听IPv4,必须显式配置IPv6监听。

我的Nginx 1.24.0配置:

server {
    # IPv4监听
    listen 80;
    listen 443 ssl;
    # IPv6监听
    listen [::]:80;
    listen [::]:443 ssl;

    server_name api.example.com;

    # 开启IPv6解析和缓存
    resolver 2400:3200::1 8.8.8.8 valid=30s;
    resolver_timeout 5s;

    ssl_certificate     /etc/nginx/ssl/example.com.pem;
    ssl_certificate_key /etc/nginx/ssl/example.com.key;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

注意:listen [::]:80在Linux上默认同时监听IPv4和IPv6(dual-stack socket)。如果只想监听IPv6,加ipv6only=on

listen [::]:80 ipv6only=on;

改完配置重载:

nginx -t && systemctl reload nginx

验证与压测数据

1. 连通性验证

# IPv4测试
curl -4 -o /dev/null -s -w "IPv4 HTTP状态码: %{http_code}, TTFB: %{time_starttransfer}s\n" https://api.example.com
# IPv6测试(指定-6强制走IPv6)
curl -6 -o /dev/null -s -w "IPv6 HTTP状态码: %{http_code}, TTFB: %{time_starttransfer}s\n" https://api.example.com

2. 压测数据对比

用ab(ApacheBench 2.3)对双栈接口压测,Nginx工作进程4个,KeepAlive开启,压测30秒:

压测场景QPS(请求/秒)平均响应时间(ms)失败率
纯IPv4访问141622.120%
纯IPv6访问139852.150%
IPv4+IPv6混合138402.190%

IPv6比IPv4性能低1.2%~2.2%。这个差异来自双栈socket的额外开销(IPv4-mapped地址处理),对业务无感知。双栈同时监听没有出现资源争用。

如果Nginx关闭ipv6only=on,只监听IPv6,QPS会降到约13200,因为IPv4请求需要经过mapped地址转换。所以生产环境保持默认的dual-stack模式。

3. 真实环境的IPv6延迟

# 电信网络,服务器在华东
ping -c 10 1.2.3.4 | tail -1
# rtt min/avg/max/mdev = 4.213/4.568/5.102/0.312 ms

ping6 -c 10 2409:8a55:1a2b:3c00::5 | tail -1
# rtt min/avg/max/mdev = 3.887/4.201/4.776/0.245 ms

IPv6平均RTT比IPv4低0.3ms。原因是运营商IPv6网络链路更直,少了一层NAT转换。

避坑指南

以下四个坑,都是我在这次配置中实际遇到并花时间排查的:

坑1:Cloud-init默认关闭IPv6

云服务器首次启动时,Cloud-init会根据云平台metadata生成网卡配置。我检查/etc/sysconfig/network-scripts/ifcfg-ens3发现:

IPV6INIT=no
IPV6_AUTOCONF=no

即使系统内核支持IPv6,网卡也不会接收RA报文。改法有两种:

# 方法1:nmcli(推荐)
nmcli connection modify "ens3" ipv6.method auto ipv6.addr-gen-mode stable-privacy

# 方法2:直接编辑ifcfg文件
sed -i 's/IPV6INIT=no/IPV6INIT=yes/' /etc/sysconfig/network-scripts/ifcfg-ens3
echo 'IPV6_AUTOCONF=yes' >> /etc/sysconfig/network-scripts/ifcfg-ens3
systemctl restart NetworkManager

注意:编辑ifcfg文件后不要用systemctl restart network,CentOS Stream 9已经没有network服务,要用NetworkManager。

坑2:安全组拦ICMPv6导致NDP失败

这是最隐蔽的坑。Linux的IPv6邻居发现协议(NDP)完全依赖ICMPv6。

如果你的云安全组/防火墙只放行TCP和UDP,没有放行ICMPv6,那么服务器无法回复邻居请求(Neighbor Solicitation),数据包发送不出去,地址“看起来”在但实际不可达。

云安全组需要放行以下ICMPv6类型:

ICMPv6类型用途必开
128Echo Request(ping请求)
129Echo Reply(ping回复)
133Router Solicitation
134Router Advertisement
135Neighbor Solicitation必须
136Neighbor Advertisement必须

本地防火墙(firewalld)也同理:

# 放行ICMPv6(所有类型)
firewall-cmd --permanent --add-rich-rule='rule protocol value="ipv6-icmp" accept'
firewall-cmd --reload

坑3:Nginx的listen [::]:443不会自动覆盖IPv4

很多教程写listen [::]:443 ssl;就完事了。实测Nginx 1.24在Linux上,这个配置确实同时监听IPv4和IPv6(dual-stack)。但在FreeBSD上或者如果编译时加了--without-ipv6,就不是这样。

最稳妥的写法是两行都写:

listen 443 ssl;
listen [::]:443 ssl;

这样在Nginx可以同时监听两个协议栈。检查是否生效:

ss -tlnp | grep nginx
# 输出:
# LISTEN 0    511   *:80    *:*    users:(("nginx",pid=1234,fd=6))
# LISTEN 0    511   [::]:80 [::]:* users:(("nginx",pid=1234,fd=7))

IPv6的socket([::])同时在跑,这就对了。

坑4:curl走代理导致IPv6测试失败

测试IPv6时,如果服务器或本地环境配了HTTP代理(比如http_proxy环境变量),curl会走代理解析DNS,代理没有IPv6出口,就会报Connection timed out

# 强制绕过代理,直连IPv6地址
curl --noproxy '*' -6 -I http://[2409:8a55:1a2b:3c00::5]/

排查时先用ip -6 route确认路由,再用ping6确认ICMP,最后才用curl测试HTTP。顺序反了会浪费大量时间。

写给后来者的配置清单

整理一份最小化的IPv6双栈服务器checklist:

  1. 确认内核支持:cat /proc/net/if_inet6有内容
  2. 网卡启用IPv6:nmcli connection modify "ens3" ipv6.method auto
  3. 安全组放行ICMPv6全部类型
  4. ip -6 addr show确认拿到2409开头的全局地址
  5. ip -6 route show default确认有默认路由
  6. ping6 -c 4 2400:3200::1验证外网连通
  7. Nginx配置双栈监听,nginx -t检查
  8. ss -tlnp | grep nginx确认两个socket都在监听

这套流程走完,你的服务器就是真正的IPv6双栈了。