IPv6双栈服务器配置实战
发布日期: 2026/07/30 阅读总量: 1

问题:IPv4 改双栈,5分钟后线上炸了

上个月我把公司一台核心API服务器的网络从纯IPv4升级为双栈(v4+v6),期望提升移动端用户(运营商分配v6)的访问体验。改完netplan重启网络,5分钟后监控告警:错误率从0.3%飙升到12%,大量“Connection refused”和“Invalid argument”错误。回滚后排查,发现是Nginx监听配置、PHP获取客户端IP逻辑、防火墙规则三者全都踩坑了。

三种方案对比

方案配置成本对应用透明兼容性我的选择
A: 纯系统网络配置 + 应用无修改低(改/etc/netplan即可)需要应用支持IPv6地址格式需应用框架原生支持❌ 不推荐,坑太多
B: Nginx统一双栈代理 + 应用绑定127.0.0.1中(Nginx配置Listen)完全透明仅依赖Nginx✅ 推荐,业务无侵入
C: 应用层手工拆分v4/v6监听高(每个服务写两份逻辑)不透明,需改代码灵活但维护成本高❌ 仅特殊场景(如自定义协议)

我最终采用方案B+Nginx上游做双栈回源,核心原则:不让应用感知IPv6,所有IPv6终结在反向代理层

完整实现:从系统到应用

1. 操作系统层:Ubuntu 22.04 + Netplan 双栈配置

# 检查当前IPv6状态
ip -6 addr show
# 确保sysctl允许IPv6
sysctl net.ipv6.conf.all.disable_ipv6
# 应返回0
# /etc/netplan/01-netcfg.yaml
network:
  version: 2
  renderer: networkd
  ethernets:
    eth0:
      addresses:
        - 192.168.1.100/24
        - "2001:db8::100/64"
      gateway4: 192.168.1.1
      gateway6: "2001:db8::1"
      nameservers:
        addresses:
          - 8.8.8.8
          - 2001:4860:4860::8888
      dhcp4: no
      dhcp6: no
sudo netplan apply
# 验证
ping6 -c 3 2001:4860:4860::8888

2. Nginx (1.24) 双栈监听 + 代理

# /etc/nginx/conf.d/double-stack.conf
server {
    listen 80;                # IPv4
    listen [::]:80;           # IPv6,双栈
    server_name api.example.com;

    # 重要:让后端应用只看到v4回源地址
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    # 后端统一走IPv4回源,避免php-fpm解析问题
    location / {
        proxy_pass http://127.0.0.1:8080;
    }
}

3. PHP-FPM (8.3) 监听127.0.0.1,不碰IPv6

# /etc/php/8.3/fpm/pool.d/www.conf
listen = 127.0.0.1:8080
listen.allowed_clients = 127.0.0.1

4. Laravel 11 获取客户端真实IP(支持X-Forwarded-For)

// app/Http/Middleware/TrustProxies.php
protected $proxies = '*'; // 信任所有代理
protected $headers =
    Request::HEADER_X_FORWARDED_FOR |
    Request::HEADER_X_FORWARDED_HOST |
    Request::HEADER_X_FORWARDED_PORT |
    Request::HEADER_X_FORWARDED_PROTO;

5. 防火墙iptables规则(允许IPv6 SSH和80)

# IPv4规则不变,IPv6新增
ip6tables -A INPUT -p tcp --dport 22 -j ACCEPT
ip6tables -A INPUT -p tcp --dport 80 -j ACCEPT
ip6tables -A INPUT -p icmpv6 -j ACCEPT
ip6tables -P INPUT DROP
ip6tables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

效果数据:压测对比

测试环境:2C4G虚拟机,Ubuntu 22.04,Nginx 1.24,PHP8.3 FPM。压测工具 wrk2,500并发,30秒。

场景吞吐量 (req/s)P99延迟 (ms)错误率
纯IPv4 (只监听0.0.0.0:80)8,200480.02%
双栈 (Nginx listen [::]:80 + proxy_pass 127.0.0.1)7,800520.05%
双栈 + 应用内获取IPv6客户端IP(错误方案)6,100892.1%

双栈方案只有5%的性能损失,完全可以接受。错误方案是让应用直接监听[::]:8080并处理IPv6请求,php-fpm在处理IPv6地址时频繁触发address in use错误,且日志格式混乱。

内存占用对比:纯IPv4占用350MB;双栈方案占用385MB(主要是额外连接跟踪和IPv6邻居表)。

避坑指南(我实际遇到过的5个坑)

  • 坑1 - Nginx双栈listen顺序listen 80;listen [::]:80;写反了会导致IPv6客户端报“no socket”。必须先写IPv4再写IPv6。
  • 坑2 - PHP-FPM IPv6监听格式:如果用listen = [::1]:8080,必须确保php-fpm编译时启用了IPv6。如果没启用,会报“cannot bind socket”。我建议永远只用127.0.0.1。
  • 坑3 - 防火墙丢失IPv6规则:很多云服务器默认只配了iptables,ip6tables规则为空。IPv6端口完全开放会被扫描。必须显式配置ip6tables。
  • 坑4 - DNS AAAA记录导致回环:内部服务用域名互相调用时,如果只配了AAAA记录且没有IPv6路由,会超时。后端回源务必只解析A记录或直接写IPv4地址。
  • 坑5 - Laravel TrustProxies没配好:$remote_addr变成IPv6地址(如::ffff:192.168.1.100)但Laravel的TrustProxies中间件默认只信任IPv4代理。会导致获取客户端IP变成代理IP。配置$proxies = '*'解决。
  • 坑6 - 云平台安全组同时放通IPv6:阿里云/腾讯云安全组默认只针对IPv4打勾,IPv6规则需要单独配置。生产环境忘了开IPv6安全组导致全量用户超时。