云原生运维笔记:打通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里创建两个网络:
Network_web_01,子网Subnet_web_01,网络地址192.168.1.0/24。Network_web_02,子网Subnet_web_02,网络地址192.168.2.0/24。
为了验证连通性,我分别基于 Img_web 镜像创建了三台虚拟机:
Instance_web_test-1,IP192.168.1.137(在web_01)Instance_web_test-2,IP192.168.1.140(在web_01)Instance_web_test-3,IP192.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)。
- 在 项目 -> 网络 -> 浮动IP 里,分配一个
External网络的IP(比如192.168.108.120)。 - 将它与
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 一看,由堆栈创建的虚拟机也一并被删除了!它是有资源依赖关系的,这就完美解决了"环境搭完之后忘了关虚机导致资源浪费"的终极难题。
四、 踩坑总结与碎碎念
今天这套组合拳打下来,踩得坑比前两天的总和还多,但收获也是巨大的。
- 安全组真的是"万恶之源":以为配了浮动IP就能连了?天真!忘了放通ICMP和TCP入站,你连神仙都连不进去。
- 外部网络和内部网络要分清:物理机网段、外部网络(External)、内部租户网络(Self-Service)之间的转换,靠的是路由器的SNAT和DNAT。搞清数据流向,网络拓扑就不难了。
- Swift不是拿来存文件的:一开始我想用FTP的思路去连它,后来发现它根本不支持。它是REST API方式的。配置底层的环的时候,一定要先建目录、改属主,不然Swift服务起不来。
- Heat是未来的趋势:习惯了点鼠标,再去写YAML模板,一开始会觉得繁琐。但当你把一整套环境(网络+路由+安全组+虚拟机)浓缩在一个文件里,一键拉起、一键删除的时候,你会发现,这他妈才是"云"。
从最初连OpenStack大门朝哪开都不知道,到现在能自己构建网络、挂载存储、编排资源,这种感觉像是在玩一场极其复杂的立体拼图。
网络和编排是OpenStack的灵魂,也是迈向"云原生运维"的关键一步。接下来,我要去挑战更坑的云原生Kubernetes了,希望能把这里学到的SDN思想、分布式存储概念、IaC理念,融会贯通到下一阶段的学习里。兄弟们,共勉!