OpenStack 2025.2 (Flamingo) 单节点一键部署
在 openEuler 25.09 上以源码方式一键部署 OpenStack 2025.2 (Flamingo) 单节点环境, 含 keystone / glance / placement / nova / neutron / horizon / cinder 七个服务, 支持单网卡环境下的实例流量出口(br0 + veth 打通物理局域网)。
本目录是从一台已实际验证通过的机器上整理出的可复现资产: 配置副本、systemd 单元、Apache vhost、部署脚本与验收脚本。
目录结构
deploy/
├── install/ # 一键部署脚本(按阶段执行)
│ ├── install.sh # 主控:全量 / 断点续装 / 干跑
│ ├── answers.conf # ★ 应答文件:所有可调参数集中于此
│ ├── lib.sh # 公共函数库(日志/幂等/配置渲染/等待/校验)
│ ├── 00-prepare.sh # 阶段0 基础环境(RPM、数据目录、venv)
│ ├── 10-keystone.sh # 阶段1 身份服务
│ ├── 20-glance-placement.sh # 阶段2 镜像 + 放置
│ ├── 30-nova.sh # 阶段3 计算服务
│ ├── 40-neutron.sh # 阶段4 网络服务
│ ├── 50-horizon-cinder.sh # 阶段5 仪表板 + 块存储
│ ├── 60-network.sh # 阶段6 虚拟网络 / 物理出口 / 规格 / 镜像
│ └── 90-verify.sh # 一键验收(57 项检查)
├── etc/ # 各服务配置副本(口令为占位符)
│ ├── keystone/glance/placement/nova/neutron/cinder/
│ ├── my.cnf # MariaDB 主配置(datadir → /data)
│ ├── rabbitmq/ sysconfig/ sysctl.d/
├── httpd/ # Apache vhost(mod_wsgi 入口)
├── systemd/ # 14 个自建 systemd 单元
└── scripts/ # 运维脚本(出口切换/看门狗/开机自检)
前置条件
| 项 | 要求 |
|---|---|
| 操作系统 | openEuler 25.09+(RHEL 系) |
| Python | 3.11 |
| 数据盘 | 挂载到 /data,SSD 优先,可用空间 ≥ 20 GB |
| 网卡 | 单网卡(本方案按单网卡设计;enp2s0 为例) |
| 硬件虚拟化 | 支持 vmx/svm(否则须把 VIRT_TYPE 改为 qemu) |
| 内存 | ≥ 8 GB(跑 cirros 测试实例建议 ≥ 16 GB) |
| 网络 | 可访问外网(下载源码包与 pip 依赖) |
| 权限 | root |
网络要求:本方案要求数据盘在 /data,部署前请确认:
df -h /data # 应显示已挂载
lsblk -o NAME,SIZE,ROTA,MOUNTPOINT | grep data # ROTA=0 表示 SSD
快速开始
1. 修改应答文件
cd deploy/install
vi answers.conf
必改项:
HOSTNAME_SHORT=open88 # 主机名
MANAGE_IP=192.168.3.88 # ★ 管理 IP(填本机实际地址)
MANAGE_PREFIX=24
MANAGE_GW=192.168.3.1 # ★ 网关
MANAGE_IF=enp2s0 # ★ 物理网卡名(ip link 查看)
# 口令(生产务必替换)
ADMIN_PASS=...
RABBIT_PASS=...
# 网络规划(默认即真实网段,按需调整)
EXTERNAL_NET_CIDR=192.168.3.0/24 # 须与物理 LAN 一致
EXTERNAL_NET_GW=192.168.3.1
EXTERNAL_POOL_START=192.168.3.200 # 浮动 IP 池(确认该段空闲)
EXTERNAL_POOL_END=192.168.3.249
SELFSERVICE_CIDR=10.0.0.0/24 # 实例私有网段
⚠️ 浮动 IP 池不能包含网关本身,也不能包含已被占用的地址。 扫描确认:
for i in $(seq 200 249); do ping -c1 -W1 192.168.3.$i & done; wait
可选开关:
WITH_EGRESS_SWITCH=no # ★ 是否打通物理出口(会秒级断网,默认关闭)
WITH_IMAGE=yes # 是否下载 cirros 镜像
WITH_FLOATING_IP=yes # 是否为测试实例绑定浮动 IP
WITH_HORIZON=yes # 是否部署仪表板
WITH_CINDER=yes # 是否部署块存储
2. 全量部署
sudo ./install.sh
执行时会打印部署摘要并要求确认(输入 yes)。全程约 20–40 分钟
(其中 pip 安装编译组件耗时最长)。
3. 验收
sudo ./90-verify.sh # 全部检查(含创建测试实例)
sudo ./90-verify.sh --quick # 只查服务与端口,不创建实例
常用命令
# 列出所有阶段
./install.sh --list
# 断点续装:从阶段 3 开始(修好问题后继续,无需重头来)
sudo ./install.sh 3
# 只执行阶段 0 到 2
sudo ./install.sh 0 2
# 干跑:只打印将执行的步骤,不实际改动系统
./install.sh --dry-run
# 单独重跑某阶段(等价于 ./install.sh <n> <n>)
sudo ./install.sh 6 6
部署阶段说明
| 阶段 | 脚本 | 内容 | 耗时 |
|---|---|---|---|
| 0 | 00-prepare.sh |
环境检查、10 个基础 RPM、数据目录指向 /data、MariaDB/RabbitMQ/memcached、venv 与公共依赖、内核参数 |
5–10 min |
| 1 | 10-keystone.sh |
建库、装包、fernet 密钥、schema、bootstrap、mod_wsgi vhost | 3–5 min |
| 2 | 20-glance-placement.sh |
glance(镜像存储 → /data)+ placement(含 oslo.policy 兼容补丁) |
3–5 min |
| 3 | 30-nova.sh |
三库同步、cell 规划、API vhost、计算节点(rootwrap/privsep/libvirt)、3 个单元 | 5–10 min |
| 4 | 40-neutron.sh |
ML2+OVS、5 个 agent、service 角色、rootwrap、OVS 网桥 | 5–10 min |
| 5 | 50-horizon-cinder.sh |
horizon(静态文件收集)+ cinder(loop 卷组于 /data) |
5–10 min |
| 6 | 60-network.sh |
物理出口打通(可选)、外部/私有网络、router、安全组、规格、镜像、端到端 | 3–8 min |
幂等性
所有脚本可重复执行:
- 建库建用户用
CREATE ... IF NOT EXISTS - 配置用
set_opt()按 section 精确改写(不会重复追加) - 服务注册失败时忽略(已存在)
- 源码包已下载/已安装则跳过
因此部署中断后直接重跑该阶段即可。
关键设计
1. 全部数据落 /data
| 组件 | 路径 |
|---|---|
| Python 运行时 | /data/openstack/venv |
| MariaDB datadir | /data/openstack/mysql |
| RabbitMQ mnesia | /data/openstack/rabbitmq |
| 镜像文件 | /data/openstack/glance/images |
| 实例磁盘 | /data/openstack/nova/instances |
| cinder 卷组后端 | /data/openstack/cinder/volumes.img(loop 文件) |
| 源码包 | /data/src |
2. 服务由 systemd 托管(14 个单元)
不使用 nohup 裸进程(宿主会话结束会被回收,重启也不会自启)。14 个单元全部 enable:
| 单元 | 作用 |
|---|---|
glance-api.service |
镜像 API |
nova-conductor.service |
nova 数据库代理 |
nova-scheduler.service |
nova 调度器 |
nova-compute.service |
nova 计算节点 |
nova-novncproxy.service |
noVNC 控制台代理(:6080) |
neutron-rpc-server.service |
neutron RPC 服务(2025.2 新增,缺失会导致 agent 不注册) |
neutron-openvswitch-agent.service |
OVS agent |
neutron-dhcp-agent.service |
DHCP agent |
neutron-l3-agent.service |
L3 agent(实例出网必需) |
neutron-metadata-agent.service |
metadata agent |
cinder-scheduler.service |
卷调度器 |
cinder-volume.service |
卷服务 |
cinder-volumes-loop.service |
loop 设备 + LVM 卷组(Type=oneshot,开机自愈) |
br0-provider-veth.service |
veth 打通 br0 ↔ br-provider(开机自愈) |
单元模板要点:
[Service]
Type=simple
User=root
# venv 里的二进制要在 PATH 中(如 cinder-volume 需 cinder-rtstool)
Environment=PATH=/data/openstack/venv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
ExecStart=/data/openstack/venv/bin/<service> --config-file /etc/<svc>/<svc>.conf
Restart=on-failure
RestartSec=5
keystone / placement / horizon / nova-api / neutron-api / cinder-api 不由独立单元承载, 它们经
mod_wsgi挂在httpd下(vhost 见httpd/目录)。
3. 版本锁定(2025.2 实测必需)
| 包 | 版本 | 原因 |
|---|---|---|
neutron-lib |
3.21.1 |
新版 neutron_lib.constants 缺少 ATTR_*,neutron API 500 |
XStatic-Font-Awesome |
4.7.0.0 |
horizon scss 找不到 font-awesome.scss |
eventlet |
0.41.2 |
降到 0.36.x 报 'HubThread' object has no attribute 'kill' |
4. 已内置的兼容补丁
脚本会自动应用以下补丁(幂等,重复执行不会重复打):
| 位置 | 补丁 | 原因 |
|---|---|---|
placement/deploy.py |
getattr(conf.oslo_policy, 'enforce_scope', True) |
新版 oslo.policy 已移除该选项,直接访问抛 NoSuchOptError |
horizon local_settings.py |
SESSION_SERIALIZER = JSONSerializer |
新版 Django 移除 PickleSerializer |
单网卡出口方案(br0 + veth)
物理交换机 / 局域网
│
enp2s0(纯 L2 桥端口)
│
┌──────── br0 (Linux 网桥, NM) ────────┐
│ 管理 IP + 默认路由 │
│ veth-br0 ●~~~● veth-brex │
└────────────────┬──────────────────────┘
│
┌─── br-provider (OVS, neutron) ───┐
│ phy-br-provider ↔ br-int │
└───────────────┬──────────────────┘
│
qrouter(qg/qr) → 实例浮动 IP / SNAT
为什么这样设计:enp2s0 是唯一网卡,直接把它挂进 OVS 网桥会让管理 IP 无处安放。
方案是先用 br0 承接管理 IP(enp2s0 降为桥端口),再用一对 veth 把
br0(NM 管理)与 br-provider(neutron 管理)在 L2 上连通,
两类流量各走各的网桥,只在 L2 交汇。
启用方式:
# answers.conf 中置 yes,然后重跑阶段 6
WITH_EGRESS_SWITCH=yes
sudo ./install.sh 6
⚠️ 会造成的秒级断网(管理 IP 从网卡迁移到网桥)。执行前必须:
- 确认有带外/现场操作条件(IPMI/iKVM/串口)
- 另开一个 SSH 窗口守候
- 脚本内置自检与失败自动回滚(
/root/phase1-br0.sh自带)
回滚:
ovs-vsctl --if-exists del-port br-provider veth-brex
ip link del veth-br0
systemctl disable --now br0-provider-veth
nmcli con down br0 && nmcli con delete br0-slave-enp2s0 && nmcli con delete br0
nmcli con mod enp2s0 connection.autoconnect yes && nmcli con up enp2s0
运维脚本
| 脚本 | 用途 |
|---|---|
scripts/br0-provider-veth.sh |
veth 打通(幂等,由 systemd 单元调用) |
scripts/phase1-br0.sh |
管理 IP 迁移(含自检+自动回滚) |
scripts/net-watchdog.sh |
通用回滚看门狗(N 秒未确认自动还原) |
scripts/net-switch-egress.sh |
出口切换一站式脚本(备选路径) |
scripts/boot-verify.sh |
开机后自检网络与服务 |
服务清单
| 服务 | 端口 | systemd 单元 |
|---|---|---|
| keystone | 5000 | (mod_wsgi,随 httpd) |
| glance | 9292 | glance-api |
| placement | 8778 | (mod_wsgi,随 httpd) |
| nova | 8774 / 8775 | nova-compute nova-conductor nova-scheduler |
| neutron | 9696 | neutron-rpc-server neutron-openvswitch-agent neutron-dhcp-agent neutron-l3-agent neutron-metadata-agent |
| horizon | 80 | (mod_wsgi,随 httpd) |
| cinder | 8776 | cinder-scheduler cinder-volume cinder-volumes-loop |
| 网络出口 | — | br0-provider-veth |
故障排查
部署日志
tail -100 /var/log/openstack-deploy.log # 部署全过程
服务日志
/data/openstack/nova/*.log
/data/openstack/neutron/*.log
/data/openstack/cinder/*.log
/data/openstack/glance/glance-api.log
/var/log/httpd/error_log # mod_wsgi 层
常见问题
实例一直 BUILD 然后 ERROR
openstack server show <实例> -f value -c fault
| fault 关键字 | 原因 |
|---|---|
Failed to allocate the network(s) |
neutron 未发 network-vif-plugged(检查 neutron.conf 的 [nova] 段与通知开关) |
Exhausted all hosts / PortBindingFailed |
neutron-rpc-server 启动时漏了 --config-file ml2_conf.ini,导致 ML2 驱动未加载 |
Compute driver option required |
nova.conf 缺 compute_driver |
实例 ACTIVE 但 ping 不通
# 1) 检查是否安全组拦了 ICMP(默认组只放行同组入向)
openstack security group rule list default
openstack security group rule create --ingress --proto icmp default
# 2) 检查实例是否真的起来了(内存是否足够)
virsh list --all
openstack flavor show <规格> -f value -c ram # cirros 需 >= 128MB
# 3) 检查 DHCP 是否发到地址
cat /var/lib/neutron/dhcp/*/leases
浮动 IP 不通
# 内网是否通(先排除实例自身问题)
ip netns exec qrouter-<id> ping -c2 <实例私有IP>
# 上游常过滤 ICMP,用 TCP 验证
bash -c 'exec 3<>/dev/tcp/<浮动IP>/22'
# 物理出口是否已打通
ovs-vsctl list-ports br-provider # 应含 veth-brex
重启后实例未自动拉起
# 现象:宿主机重启后实例为 SHUTOFF(libvirt 无 autostart)
openstack server start <实例>
# 永久启用(二选一)
virsh autostart instance-00000004
# 或 nova.conf: resume_guests_state_on_host_boot = True
已知限制
| 项 | 说明 |
|---|---|
| 单节点 | 未验证多计算节点、cell 扩容、HA |
| 存储非冗余 | cinder 用单机 loop 文件做卷组,无副本 |
| cinder-backup | 未部署(需对象存储后端) |
| 实例自启 | 宿主机重启后实例需手动启动(见上) |
| 出口切换有断网 | WITH_EGRESS_SWITCH=yes 时管理 IP 迁移会秒级断网 |
| 源码版本 | 依赖上游 stable-2025.2 分支的 tarball,上游变动可能影响 |
安全提示
answers.conf中的口令为文档一致占位值,生产环境务必替换为强口令。- 本方案不导出密钥材料:
fernet-keys/、credential-keys/、RabbitMQ cookie、 HorizonSECRET_KEY均不在仓库中,由部署脚本在目标机现场生成。 - 浮动 IP 直接暴露在物理局域网,安全组是唯一防线。
- 建议部署完成后限制
:5000、:9292等管理端口的来源(本方案为简化未做)。
版本信息
| 项 | 值 |
|---|---|
| OpenStack | 2025.2 (Flamingo) |
| 实测 OS | openEuler 25.09(内核 7.2.0) |
| Python | 3.11.13 |
| MariaDB | 11.8.3 |
| RabbitMQ | 3.12.10 |
| OVS | 3.3.1 |
| libvirt / QEMU | 9.10.0 / 8.2.0 |