云原生运维笔记:打通OpenStack的任督二脉——从Neutron网络踩坑到Swift与Heat硬核实战

云原生运维笔记:打通OpenStack的任督二脉------从Neutron网络踩坑到Swift与Heat硬核实战

前面我们搞定了计算(Nova)和块存储(Cinder),甚至把虚拟机创建背后的那"29步暗箱操作"给扒了个底朝天。按理说,有了算力、有了镜像、有了存储,虚拟机应该能跑起来了吧?但现实是,没有网络的云就是一座孤岛。

今天的学习内容,可以说是整个OpenStack里最绕、最容易让人崩溃的"深水区"------网络管理(Neutron) 。此外,还加了两个非常有意思的组件:对象存储(Swift)和编排管理(Heat)。

说实话,刚看到Neutron那一堆Network、Subnet、Port、Router、Floating IP的概念时,脑壳是嗡嗡的。但跟着实验手册一步步敲,看着两台虚拟机从"老死不相往来"到能跨网段Ping通,再到最后能访问外网,那种"网络通了"的豁然开朗,真的非常解压。今天这篇复盘,我会把Neutron的网络互联、安全组外网访问,连同Swift的环机制和Heat的YAML编排,一次性揉碎了讲清楚。


一、 探秘 Neutron:把物理网络软件化

在传统物理网络里,我们要搞连通性,得去拉网线、配交换机VLAN、配路由器。而在OpenStack里,Neutron通过软件定义网络(SDN),把这些硬件功能全部抽象成了软件资源。

1.1 核心概念与网络隔离

在Neutron里,几个核心概念必须先印在脑子里:

  • Network(网络):一个隔离的二层广播域。默认情况下,不同Network之间是互相隔离的(二层不通)。
  • Subnet(子网) :定义IP地址段,比如 192.168.1.0/24。
  • Port(端口):虚拟交换机上的一个接口,绑定MAC和IP。当虚拟机的虚拟网卡(VIF)绑定到Port时,虚拟机就有了网络身份。
  • Router(路由器):实现不同子网之间的三层路由互通,也能做NAT网关。

1.2 踩坑实验:同网段互通,跨网段隔离

我们先在Dashboard里创建两个网络:

  1. Network_web_01,子网 Subnet_web_01,网络地址 192.168.1.0/24。
  2. Network_web_02,子网 Subnet_web_02,网络地址 192.168.2.0/24。

为了验证连通性,我分别基于 Img_web 镜像创建了三台虚拟机:

  • Instance_web_test-1,IP 192.168.1.137(在web_01)
  • Instance_web_test-2,IP 192.168.1.140(在web_01)
  • Instance_web_test-3,IP 192.168.2.139(在web_02)

测试结果(意料之中):

我在控制台(VNC)里分别登录这三台机器。test-1 Ping test-2(同网段),秒通;但 test-3 Ping test-1,直接 100% packet loss。

结论:不同Network之间在二层是绝对隔离的,想跨网段通信?必须上路由器。

1.3 路由打通与外部网络配置

我反手就在网络拓扑里建了一个路由器 Router_web,并在它的"接口"里,把 Subnet_web_01 和 Subnet_web_02 都加了进去。

有了这玩意儿,test-1 和 test-3 再次互Ping,终于通了。

但这还不够,虚拟机目前还在内网"自嗨",我要让它上外网,还要让外部能SSH连进去。

在 管理员 -> 网络 -> 网络 里,我创建了一个外部网络 External:

  • 供应商网络类型:Flat
  • 物理网络:extnet(取决于我们的环境配置)
  • 勾选"外部网络"。

接着,去路由器 Router_web 的"设置网关"里,把外部网络选为 External,并启用SNAT。

此时,我们在Dashboard里登录 test-3 的控制台,执行 ping www.qq.com,终于能看到正常的回包了。

1.4 让外部访问:浮动IP与安全组

此时外部能访问虚拟机了吗?我回到宿主机Ping虚拟机的IP,还是不通。

为啥?因为宿主机在 192.168.108.0/24,而虚拟机在 192.168.2.0/24(内部网络)。我们需要浮动IP(Floating IP)。

  1. 在 项目 -> 网络 -> 浮动IP 里,分配一个 External 网络的IP(比如 192.168.108.120)。
  2. 将它与 test-3 绑定。
    再Ping,还是不通!

深坑来了 :安全组 。默认安全组只允许来自同一安全组内实例的入站流量。我新建了安全组 SG_web,添加了两条规则:

  • 所有ICMP规则,入口,CIDR 0.0.0.0/0
  • 所有TCP规则,入口,CIDR 0.0.0.0/0

将 test-3 的安全组由 default 改为 SG_web。再Ping,通了!用 SSH 连也成功进去了。

1.5 命令行实操(CLI)

在CLI下,逻辑是一样的。为了创建复杂的拓扑,我们经常用以下命令:

bash 复制代码
# 创建网络和子网
[root@controller ~(keystone_admin)]# openstack network create --share Network_cli_01
[root@controller ~(keystone_admin)]# openstack subnet create --network Network_cli_01 --subnet-range 192.168.3.0/24 --allocation-pool start=192.168.3.100,end=192.168.3.200 --gateway 192.168.3.1 Subnet_cli_01

# 创建路由器
[root@controller ~(keystone_admin)]# openstack router create --project admin Router_cli
# 设置外部网关
[root@controller ~(keystone_admin)]# openstack router set --external-gateway External Router_cli
# 添加子网接口
[root@controller ~(keystone_admin)]# openstack router add subnet Router_cli Subnet_cli_01

# 创建安全组和规则
[root@controller ~(keystone_admin)]# openstack security group create SG_cli
[root@controller ~(keystone_admin)]# openstack security group rule create --protocol icmp --ingress --remote-ip 0.0.0.0/0 SG_cli
[root@controller ~(keystone_admin)]# openstack security group rule create --protocol tcp --ingress --remote-ip 0.0.0.0/0 SG_cli

# 绑定安全组,移除默认安全组
[root@controller ~(keystone_admin)]# openstack server remove security group Instance_cli_test-3 default
[root@controller ~(keystone_admin)]# openstack server add security group Instance_cli_test-3 SG_cli

# 创建并绑定浮动IP(通过端口)
[root@controller ~(keystone_admin)]# openstack port list --server Instance_cli_test-3
# 记录PORT_ID
[root@controller ~(keystone_admin)]# openstack floating ip create --port PORT_ID External

背后技术揭秘 :现在的底层架构很多改用了 OVN 。我们可以使用 ovn-nbctl show 命令,从更底层看网络拓扑的映射。当你看到 localport、router-port 和 nat 配置时,你会发现,原本抽象的网络,在此刻变成了具体的数据结构和流表。

bash 复制代码
[root@controller ~]# ovn-nbctl show
switch 9e269a0c-5049-4c20-b602-ce42d722017c (neutron-c5a25fa7-559f-4e62-8bf0-7ebcd8c8a1f1) (aka neiwang-1)
    port 3b975511-7c63-495e-969c-1424e69770dc
        type: localport
        addresses: ["fa:16:3e:dd:ea:9d 192.168.11.2"]
# ... 此处省略部分输出
router 9374d9e7-b8b0-4d6e-924d-c9fdef6662e5 (neutron-a75adc8f-9668-4fe1-b6eb-b4573d574ae0) (aka router1)
    port 1rp-9da6f7af-22e3-44fc-8465-90334f8abeea
        mac: "fa:16:3e:78:18:6c"
        networks: ["192.168.11.1/24"]
    port 1rp-c0354999-1e15-436f-b15f-be2d3b17c5b4
        mac: "fa:16:3e:82:b8:23"
        networks: ["192.168.108.185/24"]
        gateway chassis: [46cd64c9-2f35-4dfb-9351-e0b1693daf8b]
    nat d6fae190-e948-475c-b5b1-a9258a8b7c65
        external ip: "192.168.108.185"
        logical ip: "192.168.11.0/24"
        type: "snat"

看着这底层映射,是不是感觉以前网络出问题只能瞎猜,现在终于能直击病灶了?


二、 对象存储(Swift):不是文件系统,是数据池

搞定了网络,我们来看一种新的存储:对象存储(Swift) 。

前面学的Cinder是块存储(虚拟磁盘),用来挂载给虚拟机;而Swift是对象存储,用来存静态数据,比如虚拟机镜像、图片、备份。它的特点是无中心架构、高扩展性。

2.1 Swift核心概念与Ring机制

在Swift里,没有目录,只有三个层级:Account(账户) -> Container(容器) -> Object(对象) 。

它为什么能无限扩容?核心在于 Ring(一致性哈希环)。数据不放在一个中心服务器,而是通过哈希计算,把数据分散到由Zone和Device组成的环上。默认是3副本策略,哪怕坏了一两个节点,数据也不会丢。

2.2 动手配置Swift底层存储

要玩Swift,我们得先给它准备底层硬盘。在虚拟机设置里添加了一块20GB的硬盘(/dev/sdb)。

bash 复制代码
# 1. 磁盘分区(分为两个10G的区,对应两个Zone/OBS)
[root@controller ~]# fdisk /dev/sdb
# (过程省略,输入n, p, 1, 回车, +10G,再输入n, p, 2, 回车, 回车,最后输入w保存)

# 2. 格式化为XFS
[root@controller ~]# mkfs.xfs /dev/sdb1
[root@controller ~]# mkfs.xfs /dev/sdb2

# 3. 挂载并赋权
[root@controller ~]# vim /etc/fstab
# 添加如下两行
/dev/sdb1 /srv/node/obs1 xfs defaults 0 0
/dev/sdb2 /srv/node/obs2 xfs defaults 0 0

[root@controller ~]# mount -a
[root@controller ~]# chown swift:swift /srv/node/obs1 /srv/node/obs2

# 4. 重建Ring环
[root@controller ~]# cd /etc/swift/
# 查看服务器端口(account:6002, container:6001, object:6000)
[root@controller swift]# swift-ring-builder account.builder create 12 2 1
[root@controller swift]# swift-ring-builder container.builder create 12 2 1
[root@controller swift]# swift-ring-builder object.builder create 12 2 1

# 5. 添加映射(将两个挂载点加入不同的Zone)
[root@controller swift]# swift-ring-builder account.builder add z1-192.168.108.10:6002/obs1 100
[root@controller swift]# swift-ring-builder account.builder add z2-192.168.108.10:6002/obs2 100
# (container 和 object 类似,此处略)
# 6. 再平衡(Rebalance)
[root@controller swift]# swift-ring-builder account.builder rebalance
Reassigned 8192 (200.00%) partitions. Balance is now 0.00. Dispersion is now 0.00
# ...

2.3 体验对象存储

登录Dashboard,进入 项目 -> 对象存储 -> 容器 ,创建一个名为 test 的容器,并上传一张 cirros-0.5.2-x86_64-disk.img。

上传后,我们回到宿主机,去底层目录里验证一下(2副本机制):

bash 复制代码
[root@controller ~]# find /srv/node -name *.data
/srv/node/obs1/objects/317/0fe/13d34c83bb5eed122321759e90f6e0fe/1727591964.07735.data
/srv/node/obs2/objects/317/0fe/13d34c83bb5eed122321759e90f6e0fe/1727591964.07735.data

看到文件在两个OBS目录各存了一份,这就是分布式存储最直观的体现。


三、 编排管理(Heat):用代码定义基础设施

前面所有组件,无论是建网、建云盘还是发虚拟机,我们都是要么点界面,要么敲CLI。如果我的系统需要100台虚拟机、50个网络、20个路由器呢?

这时候就需要 Heat 。它是OpenStack的"总包工头",帮你实现基础设施即代码(IaC)。

3.1 HOT模板长什么样?

Heat通过HOT(Heat Orchestration Template) 模板来工作,格式是YAML。我们写一个最简单的 demo-template.yaml 试试水。

yaml 复制代码
heat_template_version: 2015-10-15
parameters:
  NetID:
    type: string
    description: Network ID to use for the instance.
resources:
  server:
    type: OS::Nova::Server
    properties:
      image: Img_cli
      flavor: Flavor_cli
      key_name: KeyPair_cli
      networks:
        - network: {get_param: NetID}
outputs:
  instance_name:
    description: Name of the instance.
    value: {get_attr: [server,name]}
  instance_ip:
    description: IP address of the instance.
    value: {get_attr: [server,first_address]}

这个模板定义了:我要建一台虚拟机,镜像、规格、密钥对都写死了,网络需要传参进去,最后把虚拟机的名字和IP打印出来。

3.2 高阶玩法:自定义与嵌套

简单的模板很容易,但在真实生产环境里,我们可能需要注入用户数据、开放端口。手册里有个稍微复杂的 hello_world.yaml 模板:

  • 允许定义 admin_pass 密码。
  • 通过 user_data 注入一段 Shell 脚本。
  • 甚至加入了端口号 db_port 的约束。

更绝的是 嵌套模板 ,我们定义一个 my_nova.yaml 作为子模板(专门写虚拟机的属性),再用 main.yaml 作为主模板去调用它。main.yaml 只需要传参,这就实现了模板的复用。

yaml 复制代码
# main.yaml
heat_template_version: 2015-04-30
resources: 
  my_server: 
    type: my_nova.yaml 
    properties: 
      key_name: KeyPair_cli
      NetID: Network_cli_01

3.3 创建与删除堆栈

有了模板,我们直接调用CLI创建堆栈:

bash 复制代码
# 导入环境变量
[root@controller ~(keystone_admin)]# source keystonerc_admin

# 创建一个简单堆栈
[root@controller ~(keystone_admin)]# export NET_ID=$(openstack network show shared -c id -f value)
[root@controller ~(keystone_admin)]# openstack stack create -t demo-template.yaml --parameter "NetID=$NET_ID" Stack_demo

# 查看堆栈状态和详细信息
[root@controller ~(keystone_admin)]# openstack stack list
+--------------------------------------+------------+----------------------------------+-----------------+----------------------+--------------+
| ID                                   | Stack Name | Project                          | Stack Status    | Creation Time        | Updated Time |
+--------------------------------------+------------+----------------------------------+-----------------+----------------------+--------------+
| 0d2c3fa2-a1a1-4b03-8f67-f2881c1d86c8 | Stack_demo | 03e49a46342d48ab9607b4919925e42c | CREATE_COMPLETE | 2024-08-11T06:10:25Z | None         |
+--------------------------------------+------------+----------------------------------+-----------------+----------------------+--------------+

# 查看堆栈输出结果
[root@controller ~(keystone_admin)]# openstack stack output show --all Stack_demo
+---------------+----------------------------------------------------+
| output_key    | output_value                                       |
+---------------+----------------------------------------------------+
| instance_ip   | {                                                  |
|               |   "description": "IP address of the instance.",    |
|               |   "output_value": "192.168.233.186"                |
|               | }                                                  |
| instance_name | {                                                  |
|               |   "description": "Name of the instance.",          |
|               |   "output_value": "stack-demo-server-7l2s3b5m6j"   |
|               | }                                                  |
+---------------+----------------------------------------------------+

非常丝滑。最爽的是删除的时候:

bash 复制代码
[root@controller ~(keystone_admin)]# openstack stack delete Stack_demo
# 确认时输入 y
Are you sure you want to delete this stack(s) [y/N]? y

几秒钟后,你去 openstack server list 一看,由堆栈创建的虚拟机也一并被删除了!它是有资源依赖关系的,这就完美解决了"环境搭完之后忘了关虚机导致资源浪费"的终极难题。


四、 踩坑总结与碎碎念

今天这套组合拳打下来,踩得坑比前两天的总和还多,但收获也是巨大的。

  1. 安全组真的是"万恶之源":以为配了浮动IP就能连了?天真!忘了放通ICMP和TCP入站,你连神仙都连不进去。
  2. 外部网络和内部网络要分清:物理机网段、外部网络(External)、内部租户网络(Self-Service)之间的转换,靠的是路由器的SNAT和DNAT。搞清数据流向,网络拓扑就不难了。
  3. Swift不是拿来存文件的:一开始我想用FTP的思路去连它,后来发现它根本不支持。它是REST API方式的。配置底层的环的时候,一定要先建目录、改属主,不然Swift服务起不来。
  4. Heat是未来的趋势:习惯了点鼠标,再去写YAML模板,一开始会觉得繁琐。但当你把一整套环境(网络+路由+安全组+虚拟机)浓缩在一个文件里,一键拉起、一键删除的时候,你会发现,这他妈才是"云"。

从最初连OpenStack大门朝哪开都不知道,到现在能自己构建网络、挂载存储、编排资源,这种感觉像是在玩一场极其复杂的立体拼图。

网络和编排是OpenStack的灵魂,也是迈向"云原生运维"的关键一步。接下来,我要去挑战更坑的云原生Kubernetes了,希望能把这里学到的SDN思想、分布式存储概念、IaC理念,融会贯通到下一阶段的学习里。兄弟们,共勉!

相关推荐
旖旎夜光1 小时前
LCR 170:交易逆序对的总数(归并排序) —— 题解
c++·学习·算法·leetcode·排序算法
广州灵眸科技有限公司9 小时前
4G 模组开机自连 systemd 服务方案:基于灵眸科技EAI1106--Core-T
运维·服务器·科技
摇滚侠9 小时前
《On Java 中文版 基础卷》阅读笔记 实现隐藏 12
java·数据库·笔记
小向是个Der10 小时前
嵌入式进阶——MCU上电后的动作
c语言·单片机·mcu·学习·链接脚本
天远数科10 小时前
零信任架构实战:基于天远企业股权变更构建自动化商业银行对公授信存续期股权异动与资产抽逃预警网关
运维·人工智能·架构·自动化
qq_4017004110 小时前
Linux设备树详解
linux·运维·服务器
奇树谦11 小时前
Windows CMD 下 mc 运维指南:SeaweedFS 安装验收与 MinIO 数据迁移
运维·windows
刚入门的大一新生11 小时前
Linux-信号
linux·运维·服务器
车软学堂11 小时前
CANoe自动化:跑起第一个测试用例
运维·车载系统·自动化·汽车·测试用例