第348篇:数据中心 IP 地址重规划与迁移实战案例

关键词

IP 规划、地址重编、子网划分、IPAM、路由汇总、迁移、平滑割接、DNS 更新


一、案例背景

1.1 现状与问题

某企业数据中心 IP 地址重规划:

企业现状:

数据中心:3 个(DC1/DC2/DC3) 服务器:2,000+ 台 现有 IP:多个散乱的 C 段 IP 来源:历史遗留(不同时期申请) 子网大小:/24 ~ /28 混合

当前 IP 使用现状(混乱):

DC1: 10.1.1.0/24 (Web) 172.16.1.0/24 (DB) 192.168.1.0/24 (管理) 10.5.1.0/24 (API) DC2: 10.2.1.0/24 (Web) 172.18.1.0/24 (DB) 10.6.1.0/24 (API) DC3: 10.3.1.0/24 (Web) 10.7.1.0/24 (API) 问题: └─ 不可汇总(路由表条目 200+) └─ 无法扩展(/24 不够用) └─ 无统一规划(多个网段散落) └─ 跨 DC 子网冲突

1.2 重规划需求

IP 重规划目标:

业务需求:

统一规划:三层 DC 统一编址 可扩展:预留 3-5 年增长空间 可汇总:减少路由表条目 易运维:IP 段标识业务类型 可迁移:平滑迁移,不影响业务

约束条件:

└─ 不能中断现网运行 └─ 不能更改现有公网 IP └─ 不能同时更改大量配置 └─ 需要保留部分现有 IP 便于 DNS 解析 └─ 安全设备(防火墙 ACL)需同步更新


二、重规划方案设计

2.1 新 IP 规划

新 IP 地址规划(10.0.0.0/8 私有地址段):

┌──────────────────────────────────────────┐ │ 10.[DC].[功能].[设备].0/24 │ │ │ │ 编码规则: │ │ DC: 1=DC1, 2=DC2, 3=DC3 │ │ 功能: │ │ 0=网络设备管理 │ │ 1=Web/前端 │ │ 2=应用/API │ │ 3=数据库 │ │ 4=存储 │ │ 5=管理 │ │ 8=负载均衡 VIP │ │ 9=预留 │ │ 设备: 0-255 按业务分配 │ └──────────────────────────────────────────┘

规划示例(DC1):

用途 子网 说明 网络设备 10.1.0.0/24 管理地址 Web 服务器 10.1.1.0/24 前端服务 API 服务器 10.1.2.0/24 应用服务 数据库 10.1.3.0/24 MySQL/Redis 存储网络 10.1.4.0/24 SAN/NAS 管理网络 10.1.5.0/24 带外管理 VIP 地址 10.1.8.0/24 负载均衡 预留 10.1.9.0/24 未来扩展 预留 10.1.10-15 长期预留

整体规划(三个 DC): ┌──────────────────────────────────────────┐ │ DC1: 10.1.0.0/16 (汇总) │ │ DC2: 10.2.0.0/16 (汇总) │ │ DC3: 10.3.0.0/16 (汇总) │ │ │ │ 路由汇总: │ │ └─ DC1 出口路由:10.1.0.0/16 │ │ └─ DC2 出口路由:10.2.0.0/16 │ │ └─ DC3 出口路由:10.3.0.0/16 │ │ └─ 总路由条目:3 条(原来 200+ 条) │ └──────────────────────────────────────────┘

2.2 迁移策略

四阶段迁移策略:

Phase 1:准备工作(2 周)

□ 新 IP 段分配完成 □ 部署 IPAM 系统(PHPIPAM/Netbox) □ DNS 中为新 IP 添加记录(TTL 设短) □ 防火墙预配新 IP ACL □ 监控系统预配新 IP

Phase 2:非关键业务迁移(2 周)

□ 管理网络(带外/带内管理)迁移 □ 监控系统迁移 □ 备份系统迁移 □ 日志系统迁移 □ CI/CD 系统迁移

Phase 3:关键业务迁移(4 周)

□ 存储网络迁移(谨慎,分批) □ 数据库迁移(主备切换方式) □ 应用服务器迁移 □ Web 服务器迁移(负载均衡先切) □ API 网关迁移

Phase 4:收尾(2 周)

□ 回收旧 IP 地址 □ 清理旧 DNS 记录 □ 更新防火墙 ACL(移除旧 IP) □ 优化路由汇总 □ 更新 CMDB □ 更新文档


三、迁移工具与自动化

3.1 IP 规划生成器

#!/usr/bin/env python3
"""
IP 规划生成器
根据编码规则自动生成子网分配方案
"""

import ipaddress
from dataclasses import dataclass
from typing import List, Dict


@dataclass
class DCSite:
    """数据中心站点"""
    name: str
    dc_id: int
    subnets: Dict[str, str]  # 用途 -> CIDR


class IPPlanner:
    """IP 规划器"""

    FUNCTION_CODES = {
        "network": 0,
        "web": 1,
        "api": 2,
        "database": 3,
        "storage": 4,
        "management": 5,
        "vip": 8,
        "reserved": 9,
    }

    def __init__(self, base_prefix="10.0.0.0/8"):
        self.base = ipaddress.IPv4Network(base_prefix)

    def generate_site_subnets(self, dc_id: int, size_per_func: int = 24):
        """
        生成站点的子网分配
        dc_id: DC 编号 (1-255)
        size_per_func: 每功能的子网大小(默认 /24)
        """
        subnets = {}

        for func_name, func_code in self.FUNCTION_CODES.items():
            # 计算子网:10.DC.Func.0/size
            third_octet = dc_id
            fourth_octet = func_code * 32  # 每功能预留 32 个 /24

            # 实际上我们使用 10.DC.Func.0/24 的格式
            subnet_str = f"10.{dc_id}.{func_code}.0/{size_per_func}"
            try:
                subnet = ipaddress.IPv4Network(subnet_str, strict=False)
                subnets[func_name] = subnet
            except ValueError as e:
                print(f"警告: 生成子网 {subnet_str} 失败: {e}")

        return subnets

    def print_planning_table(self, sites: List[DCSite]):
        """打印规划表格"""
        print(f"\nIP 地址重规划方案")
        print("=" * 70)

        for site in sites:
            print(f"\n{site.name} (10.{site.dc_id}.0.0/16):")
            print("-" * 50)
            print(f"{'用途':<12} {'子网':<18} {'可分配':<10} {'说明'}")
            print("-" * 50)

            for func_name, subnet in site.subnets.items():
                usable = subnet.num_addresses - 2  # 减去网络地址和广播
                desc = {
                    "network": "网络设备管理",
                    "web": "Web 前端服务器",
                    "api": "应用/API 服务",
                    "database": "数据库服务器",
                    "storage": "存储网络",
                    "management": "带外管理",
                    "vip": "负载均衡 VIP",
                    "reserved": "未来预留",
                }.get(func_name, "")
                print(f"{func_name:<12} {str(subnet):<18} {usable:<10} {desc}")

    def generate_summary_routes(self, sites: List[DCSite]):
        """生成汇总路由"""
        print("\n汇总路由配置:")
        print("=" * 50)

        for site in sites:
            summary = ipaddress.IPv4Network(f"10.{site.dc_id}.0.0/16")
            print(f"  ip route-static {summary} Null0  # {site.name}")

    def check_overlap(self, sites: List[DCSite]):
        """检查是否有子网重叠"""
        all_subnets = []
        for site in sites:
            for func_name, subnet in site.subnets.items():
                all_subnets.append((site.name, func_name, subnet))

        overlaps = []
        for i in range(len(all_subnets)):
            for j in range(i + 1, len(all_subnets)):
                s1_name, f1_name, s1 = all_subnets[i]
                s2_name, f2_name, s2 = all_subnets[j]
                if s1.overlaps(s2):
                    overlaps.append(
                        f"重叠: {s1_name}/{f1_name} ({s1}) "
                        f"<-> {s2_name}/{f2_name} ({s2})"
                    )

        return overlaps


def main():
    """主函数"""
    planner = IPPlanner()

    # 定义三个 DC
    sites = [
        DCSite("DC1-主数据中心", 1, {}),
        DCSite("DC2-灾备中心", 2, {}),
        DCSite("DC3-灾备中心", 3, {}),
    ]

    # 生成子网
    for site in sites:
        site.subnets = planner.generate_site_subnets(site.dc_id)

    # 打印规划
    planner.print_planning_table(sites)

    # 汇总路由
    planner.generate_summary_routes(sites)

    # 检查重叠
    overlaps = planner.check_overlap(sites)
    if overlaps:
        print("\n⚠ 子网重叠检测:")
        for o in overlaps:
            print(f"  ❌ {o}")
    else:
        print("\n✅ 无子网重叠")

    # 统计
    total_ips = sum(
        site.subnets[func].num_addresses
        for site in sites
        for func in site.subnets
    )
    print(f"\n统计:")
    print(f"  站点数量: {len(sites)}")
    print(f"  总子网数: {sum(len(s.subnets) for s in sites)}")
    print(f"  总 IP 数: {total_ips}")


if __name__ == "__main__":
    main()

3.2 IP 迁移辅助工具

#!/usr/bin/env python3
"""
IP 迁移辅助工具
生成迁移脚本和检查清单
"""

from dataclasses import dataclass
from typing import List, Dict
from datetime import datetime, timedelta
import json


@dataclass
class MigrationTask:
    """迁移任务"""
    task_id: str
    hostname: str
    old_ip: str
    new_ip: str
    service_type: str
    priority: int  # 1-5, 1=最高
    dependencies: List[str]
    status: str = "pending"  # pending/in_progress/completed/rolled_back


class MigrationPlanner:
    """迁移规划器"""

    def __init__(self):
        self.tasks: List[MigrationTask] = []
        self.change_windows = []

    def add_task(self, task: MigrationTask):
        self.tasks.append(task)

    def sort_by_dependency(self):
        """按依赖关系排序"""
        sorted_tasks = []
        visited = set()

        def dfs(task_id):
            if task_id in visited:
                return
            visited.add(task_id)
            task = next(
                t for t in self.tasks if t.task_id == task_id
            )
            for dep_id in task.dependencies:
                dfs(dep_id)
            sorted_tasks.append(task)

        for task in self.tasks:
            dfs(task.task_id)

        self.tasks = sorted_tasks

    def generate_migration_script(self, task: MigrationTask):
        """生成迁移脚本模板"""
        scripts = f"""
# ==========================================
# 迁移任务: {task.task_id}
# 主机: {task.hostname}
# 旧 IP: {task.old_ip} → 新 IP: {task.new_ip}
# 业务类型: {task.service_type}
# 优先级: {task.priority}
# ==========================================

# 步骤 1: 备份配置
# cp /etc/network/interfaces /etc/network/interfaces.bak

# 步骤 2: 修改 IP 配置
# sed -i 's/{task.old_ip}/{task.new_ip}/g' /etc/network/interfaces

# 步骤 3: 更新 DNS 记录
# nsupdate -k /etc/rndc.key << EOF
# server dns-server.example.com
# zone example.com
# update delete {task.hostname}.example.com. A {task.old_ip}
# update add {task.hostname}.example.com. 300 A {task.new_ip}
# send
# EOF

# 步骤 4: 重启网络服务
# systemctl restart networking

# 步骤 5: 验证
# ping -c 3 {task.new_ip}
# curl -I http://{task.new_ip}:80

# 步骤 6: 更新防火墙策略
# firewall-cmd --permanent --remove-source={task.old_ip}/32
# firewall-cmd --permanent --add-source={task.new_ip}/32
# firewall-cmd --reload

# 步骤 7: 更新监控告警 IP
# ...
"""
        return scripts

    def generate_checklist(self):
        """生成迁移检查清单"""
        self.sort_by_dependency()

        checklist = f"""
IP 地址迁移检查清单
{'=' * 60}

生成时间: {datetime.now().isoformat()}

前置条件(必须在迁移前完成):
  □ DNS 新记录已添加(TTL 设置为 300 秒)
  □ 防火墙已预配新 IP ACL
  □ 监控系统已添加新 IP
  □ 备份系统已配置新 IP
  □ CMDB 记录已更新
  □ 相关方已通知(变更窗口确认)

迁移顺序:
"""
        for i, task in enumerate(self.tasks, 1):
            status_map = {
                "pending": "☐",
                "in_progress": "◐",
                "completed": "☑",
                "rolled_back": "☒"
            }
            symbol = status_map.get(task.status, "☐")
            checklist += (
                f"  {symbol} {i}. [{task.task_id}] "
                f"{task.hostname}: {task.old_ip} → {task.new_ip} "
                f"(优先级: {task.priority})\n"
            )

        checklist += """
验证清单(迁移后):
  □ 新 IP 可达性测试
  □ 业务端口可用性测试
  □ DNS 解析是否正确
  □ 防火墙策略是否生效
  □ 监控告警是否正常
  □ 日志是否正常记录
  □ 负载均衡是否正常转发

回滚预案:
  □ 保留旧 IP 配置(至少保留 7 天)
  □ 旧 DNS 记录保留(TTL 调低)
  □ 防火墙保留旧 ACL
  □ 回滚操作必须在 30 分钟内完成
"""
        return checklist

    def estimate_duration(self):
        """估算迁移总时间"""
        total_minutes = sum(
            15 if t.priority <= 2 else 30
            for t in self.tasks
        )
        hours = total_minutes // 60
        minutes = total_minutes % 60
        return f"{hours} 小时 {minutes} 分钟"


def main():
    """主函数"""
    planner = MigrationPlanner()

    # 模拟迁移任务
    tasks = [
        MigrationTask("M001", "web-01", "10.1.1.10",
                      "10.1.1.10", "Web", 1, []),
        MigrationTask("M002", "web-02", "10.1.1.11",
                      "10.1.1.11", "Web", 1, ["M001"]),
        MigrationTask("M003", "api-01", "172.16.1.10",
                      "10.1.2.10", "API", 2, ["M001", "M002"]),
        MigrationTask("M004", "db-01", "192.168.1.10",
                      "10.1.3.10", "Database", 1, ["M003"]),
        MigrationTask("M005", "storage-01", "10.5.1.10",
                      "10.1.4.10", "Storage", 3, []),
    ]

    for task in tasks:
        planner.add_task(task)

    # 打印清单
    print(planner.generate_checklist())

    # 打印第一个任务的迁移脚本示例
    print("\n迁移脚本示例(Web-01):")
    print("-" * 50)
    print(planner.generate_migration_script(tasks[0]))

    print(f"\n预计总迁移时间: {planner.estimate_duration()}")


if __name__ == "__main__":
    main()

四、迁移后验证与注意事项

迁移风险与应对:

常见风险:

风险 应对策略 DNS 缓存未更新 TTL 设短(300秒) 防火墙规则遗漏 IPAM 自动生成 ACL 应用硬编码 IP 代码扫描工具 存储连接中断 多路径保留旧 IP 数据库连接池失效 重启应用 监控告警失效 双 IP 监控过渡 配置文件未更新 Ansible 批量推送

迁移后检查项: ┌──────────────────────────────────────────┐ │ 即时检查(迁移后 30 分钟内): │ │ □ 所有核心业务正常运行 │ │ □ 监控系统告警正常 │ │ □ 日志无异常错误 │ │ □ 负载均衡正常转发 │ │ □ DNS 正确解析 │ │ │ │ 持续检查(迁移后 1 周内): │ │ □ 性能无退化 │ │ □ 无间歇性连接问题 │ │ □ 旧 IP 流量持续下降至 0 │ │ □ 用户无投诉 │ └──────────────────────────────────────────┘


五、总结

IP 地址重规划关键要点:

  1. 规划先行
     └─ 制定统一的编码规则
     └─ 预留足够的扩展空间
     └─ 确保子网可汇总
     └─ 不同 DC 地址段不重叠

  2. 逐步迁移
     └─ 先管理后业务
     └─ 先非关键后关键
     └─ 按依赖关系排序
     └─ 每批迁移后充分验证

  3. 工具辅助
     └─ IPAM 系统管理 IP 分配
     └─ 自动化脚本减少人工错误
     └─ 检查清单确保无遗漏
     └─ 回滚预案随时可用

  4. 风险控制
     └─ DNS TTL 设短(快速切换)
     └─ 防火墙预配新 IP
     └─ 保留旧 IP 一段时间
     └─ 监控覆盖新旧 IP

下篇预告:第349篇《核心网络设备替换与在网升级实战案例》——讲解核心网络设备替换和在网升级的方案设计、风险控制和回滚策略。


下篇预告:第349篇《核心网络设备替换与在网升级实战案例》——讲解核心网络设备替换和在网升级的方案设计、风险控制和回滚策略。