简介:这份PDF是中国传媒大学《媒体数据库与云存储》课程的实验(四)报告,面向信息与通信工程等专业学生,围绕OpenStack云计算平台与Swift对象存储展开,适合正在学习云计算部署、云存储运维的初中级读者对照实践。报告完整记录了从VMware添加VMnet10虚拟网络、启动OpenStack主机、经Dashboard登录并创建Inst_demo等云主机实例,到查看public与private子网及router-demo路由接口的网络配置过程;Swift部分则涵盖CLI认证、容器新建、对象上传以及元数据查看、添加、修改、删除等操作,并附有Holidays、Sports容器与对象的思考题实践及OpenStack对比、云架构问答。整包共1个PDF文件,约3.66MB,图文并排呈现操作界面与命令结果,便于按步骤复现实验和复盘排错。目前已有84人学习,可作为课程实验参考与考前复习材料。
1. 从一份实验报告要解决的问题入手:OpenStack 加 Swift 能解决什么
把《媒体数据库与云存储》这份实验报告拆开看,真正有价值的部分是它串起来的一条完整链路:先在 VMware 里划出一张 VMnet10 虚拟网络,把 OpenStack 主机跑起来,用 Dashboard 以 demo 租户建实例、看路由,再切到 test 账户用 Swift CLI 完成认证、建容器、上传对象、改元数据。这条链路走通一次,云平台里计算、网络、存储三块是怎么结合的,就有实际操作的手感了。适合两类人:一类是要交实验报告、但只会照着截图敲命令的学生;另一类是第一次接手私有云、需要一套能照抄的最小验证流程的运维。下面按报告里的实际步骤复现,参数、命令、报错都按现场来,能不省略的地方尽量不省略。
2. 环境初始化:VMnet10、Dashboard 登录与第一台云主机
这一章把“能登进去”这件事做扎实。很多人卡在这一步,往往觉得自己对 OpenStack 不熟,其实是网络地址和用户角色没理清——虚拟网络选错模式、用 admin 去建实例、实例起在 private 网段却想直接从宿主机 ping,这三件事占了大半的返工。
2.1 虚拟网络与主机镜像:先把地址规划固定下来
VMware 里新建 VMnet10,这一步的重点不在“加一张网卡”,而在确定这张网用哪种模式。实验环境里 OpenStack 主机自己就是一台虚拟机,宿主机上的浏览器要访问它的 Dashboard,所以 VMnet10 通常按仅主机(Host-Only)模式配,网段和物理局域网错开,避免和公司或宿舍网段的 192.168.x 冲突。我一般先用ipconfig(Windows)或ifconfig(macOS/Linux)看一遍宿主机的现有网段,再决定 VMnet10 用 192.168.3.0/24,因为报告里主机的地址是 192.168.3.40,宿主机上再挂一张同网段的虚拟网卡,浏览器才访问得到。
虚拟机启动后用 test 账户登录,先不要急着开浏览器,先在终端把网络状态看清楚:
# 查看所有网卡的 IPv4 地址,确认 VMnet10 对应的那张网卡拿到了 192.168.3.40 ip -4 addr show | grep -E "inet " # 看默认路由,确认流量出口不是另一张 NAT 网卡 ip route show default # 确认 OpenStack 各服务端口在监听:keystone 5000、nova 8774、glance 9292 # neutron 9696、horizon 80/8080、novnc 6080 ss -lntp | grep -E ":(5000|8774|9292|9696|80|8080|6080)\b"代码的逻辑很直白:先确认地址,再确认路由,最后确认服务。参数上唯一需要改的是网段,如果实验环境给的地址不是 192.168.3.x,把 grep 的条件换掉即可。服务端口那段如果只出来一两个,说明 OpenStack 没起全,常见做法是systemctl status逐个看keystone、nova-api、glance-api、neutron-server,不要一上来就重装。纯血泪经验:端口没起来的时候去调 Horizon 的配置,等于把时间花在错误的地方。
镜像那一步,报告里是“从已有镜像生成 OpenStack 主机”,本质就是 VMware 打开一个已经装好 OpenStack 的 ovf/ova 或 vmdk,克隆时选择“我已复制该虚拟机”,否则网卡 MAC 冲突,地址会飘。克隆完第一件事是改主机名和静态地址,别让 DHCP 把 192.168.3.40 分给别人。
2.2 Dashboard 的三种角色:admin、demo、test 分别负责什么
这套实验用了三个账户,分工很明确,混用就会出各种“看不到资源”的问题。下面这张表是按报告实际流程整理的:
| 账户 | 认证域 | 主要用途 | 容易混淆的地方 |
|---|---|---|---|
| admin | Default | 管理镜像、卷、租户、用户 | 在 admin 下创建的实例,demo 用户默认看不见,原因是租户隔离 |
| demo | Default | 建实例、建网络、建路由,日常操作都在这一层 | 建实例前必须确认当前项目是 demo,否则资源建到了 admin 项目 |
| test | 系统本地用户 | 登录 Swift 客户端主机执行 CLI | 它不是 Keystone 里的租户用户,只是操作系统账户 |
理解了这张表,后面很多现象就说得通了。比如“我在 Dashboard 里建了实例,换个人登录就没了”,这通常不是数据丢失,而是项目(Project)切换了。OpenStack 的隔离单位是项目,不是用户,同一项目里的用户互相能看见资源,跨项目就是两套世界。认证这层由 Keystone 管,你登录拿到的是一张 token,后面所有 API 调用都带着它,token 默认有效期不长,实验做久了突然报 401,八成是过期,重新登录即可。
用浏览器访问http://192.168.3.40/(或带 8080 端口,看环境),输 demo 的凭据进去。上来先看三个位置:项目 → 计算 → 实例,看有没有遗留的旧实例;项目 → 网络 → 网络拓扑,看 public/private 两张网和 router-demo 是不是已经建好;管理员 → 系统 → 卷,看云存储里的卷信息。这三眼看下去,环境是不是干净就心里有数了,比上手就建实例靠谱。
2.3 从镜像到实例:Inst_demo 的创建参数与 Console 验证
建实例这一步,Dashboard 上填的东西不多,但每一项都会在后面出问题。按报告流程,用 demo 登录后进“计算 → 实例 → 创建实例”,关键参数这么填:
| 参数 | 取值 | 说明 |
|---|---|---|
| 实例名称 | Inst_demo / Inst_cirros / Inst_fedora | 名字带镜像特征,后面排错一眼能认出来 |
| 来源 | 镜像 → cirros / fedora | 选“创建新卷”会多占存储,实验环境选“否”直接起 |
| 规格 | m1.tiny 或自定义小规格 | 实验机内存有限,别选 m1.small 以上 |
| 网络 | private 网络 | 选 public 会直接占宿主机网段地址 |
| 安全组 | default | 默认组通常只放通同组内流量,后面要按需加规则 |
实例起来后不要只看状态是不是“运行中”,要点进 Inst_cirros 的 Console 控制台,用镜像自带的账号登进去看 IP。报告里 Inst_cirros 拿到 10.0.0.31、Inst_fedora 拿到 10.0.0.121,两个地址都在 10.0.0.0/24 段内,说明它们接的是 private 网络。在 Console 里互相 ping 通,只能证明同网段二层通,不能证明外网通,这一点后面第 3 章还要再拆一次。
创建过程中常见的失败有两种:一是实例卡在“创建中”,多半是 nova 调度不到计算节点,去/var/log/nova/nova-scheduler.log看有没有No valid host;二是实例起来了但 Console 黑屏,通常是镜像本身不支持 VNC 或者 novncproxy 没起,跟实例无关。判断方法很简单——同一个镜像建两台,两台都黑屏就查服务,只有一台黑屏就重建那一台。
3. 网络拓扑解析:public/private 双网与 router-demo 的连通方式
3.1 两张网的地址切分与各自角色
报告里的网络状态图信息量其实很大:整个云有一个 public 子网和一个 private 子网,private 里挂两台主机,通过 router-demo 连到 public。把它翻译成地址表就是:
| 对象 | 网段/地址 | 角色 |
|---|---|---|
| public 网络 | 192.168.3.0/24 | 对接宿主机所在网络,Dashboard 和管理流量走这里 |
| private 网络 | 10.0.0.0/24 | 实例的租户内网,实例地址从这里分配 |
| router-demo 在 public 的接口 | 192.168.3.77 | 路由器的外网网关地址 |
| router-demo 在 private 的接口 | 10.0.0.1 | 实例的默认网关 |
| Inst_cirros | 10.0.0.31 | private 网内的实例 |
| Inst_fedora | 10.0.0.121 | private 网内的实例 |
这张表的意义在于:实例的默认网关必须是 10.0.0.1,而不是 192.168.3.x。如果实例里ip route看到的默认路由指向别处,那它出网一定失败。另外 10.0.0.0/24 是 RFC1918 私有段,宿主机是访问不到的,宿主机能访问的是 router 的 192.168.3.77。很多人在这里搞混,以为建完实例就能从宿主机直连 10.0.0.31,然后反复怀疑防火墙。
3.2 router-demo 的两个接口与 SNAT/DNAT 的分工
路由器在 OpenStack 里不是一个硬件盒子,而是一个跑在网络节点上的网络命名空间,两个接口就是两个 veth 对。它的作用有两个方向:出方向做 SNAT,把 10.0.0.x 的源地址改成 192.168.3.77 发出去;入方向做 DNAT,把外部访问某个浮动 IP 的流量转给内部的 10.0.0.x。实验里只验证了内部互通,没有配浮动 IP,所以宿主机访问不到实例是正常的,不是配错了。
要在命令行把这件事看清楚,可以在控制节点上直接进路由器命名空间验证:
# 先拿到 router-demo 的 ID 和两个接口的地址 openstack router show router-demo -c id -c interfaces_info -f yaml # 列出所有网络命名空间,路由器的命名空间形如 qrouter-<router-id> ip netns list # 进入路由器命名空间,从 10.0.0.1 这一侧去 ping 实例 sudo ip netns exec qrouter-<router-id> ping -c 3 10.0.0.31 # 看命名空间里的 NAT 规则,确认 SNAT 是否真的生效 sudo ip netns exec qrouter-<router-id> iptables -t nat -L -n -v | grep -i snatip netns exec这个用法值得记住:它让你站到路由器内部去看世界,ping通了说明 private 网络和路由器之间没问题,不通就往实例的安全组和端口状态上查。iptables -t nat那行是看 SNAT 规则有没有下发,如果一条都没有,说明路由器的外部网关没设,去 Dashboard 的“网络 → 路由 → 设置网关”补上。参数里唯一要替换的是<router-id>,从第一条命令的输出里取。
顺带说一句,这套网络模型跟关系库里的事务边界有点像:public 和 private 是两张表,router 是那个外键,实例的地址是行数据。理解了层级关系,再看 Dashboard 的拓扑图就不会觉得是一堆线。
3.3 连通性验证的排查顺序
验证顺序我一般固定成四步,避免东一榔头西一棒子:先看实例的 IP 和默认路由,再看同网段互 ping,再看路由器命名空间里的连通性,最后才看跨网段和外网。按报告的场景,Inst_cirros 的 10.0.0.31 和 Inst_fedora 的 10.0.0.121 互 ping 通,只完成了第二步。
第一步在实例内部执行:
# cirros 镜像默认账号 cirros/gocubsgo,登进去先看地址和路由 ip -4 addr show eth0 ip route show # 默认路由应该指向 10.0.0.1,也就是 router-demo 的 private 接口第三步在控制节点上做,用上面那段ip netns exec的命令。第四步才涉及安全组:OpenStack 默认安全组只允许同组实例互访,ICMP 是放通的,但如果有人改过规则,ping 不通就先去“项目 → 网络 → 安全组”看有没有 ICMP 的入方向规则。这里常见的错误是给实例加了新安全组但没重启端口,规则不生效,把端口 detach 再 attach 一次就好。
4. Swift 对象存储实操:认证、容器、上传与元数据
4.1 认证与 CLI 环境:token 从哪来、活多久
Swift 的所有操作都要先过 Keystone 认证拿 token,这一步在 CLI 上表现为一组环境变量加一条swift auth。实验环境如果是较新的版本,用 v3 认证:
# Swift 客户端主机上用 test 账户登录后,先导入认证环境变量 export OS_AUTH_URL=http://192.168.3.40:5000/v3 export OS_PROJECT_NAME=demo export OS_PROJECT_DOMAIN_NAME=Default export OS_USERNAME=demo export OS_PASSWORD='<demo的口令>' export OS_USER_DOMAIN_NAME=Default # 认证并打印拿到的存储端点,成功会输出 storage_url 和 auth_token swift auth # 查看当前账户的统计信息:容器数、对象数、已用字节 swift stat参数里最容易出错的是OS_AUTH_URL的版本后缀。老环境是 v2.0,路径写成http://192.168.3.40:5000/v2.0,并且不需要OS_USER_DOMAIN_NAME和OS_PROJECT_DOMAIN_NAME这两个变量;新环境是 v3,不写域名会报401 Unauthorized,而且错误信息只说认证失败,不说缺哪个字段。这是典型的不容易看出来的问题,我的习惯是认证不通先把环境变量全打出来对一遍,别去翻服务端日志。
swift stat的输出值得留意:Account、Containers、Objects、Bytes四个字段。如果Containers一直是 0 但你确实建过容器,那多半是认证到了另一个账户——demo 和 admin 的 Swift 账户是独立的,名字看起来一样,实际是两套命名空间。
在媒体类项目里,对象存储和关系库的分工要分清:MySQL 那套(本地用 XAMPP 起的那种)存的是节目单、素材索引这类结构化字段,几 MB 的视频、音频、图片原始文件扔给 Swift 更合适。两边的关联靠对象名做外键,而不是把二进制塞进 BLOB 字段——那样备份和迁移都会很痛苦。
4.2 容器与对象:把本地文件变成云端对象
容器的概念接近文件夹,但没有真正的目录层级,对象名里的斜杠只是名字的一部分。操作序列按报告走一遍:
# 建一个容器,名字用业务语义,别用 test1/test2 swift post media-demo # 列出当前账户下所有容器 swift list # 造一个 4MB 的本地文件当作上传素材 dd if=/dev/urandom of=./holiday.bin bs=1M count=4 # 上传对象,第二个参数是容器名,第三个是本地文件路径 swift upload media-demo ./holiday.bin # 确认对象在容器里,能看到大小和最后修改时间 swift list media-demo -l # 看对象的详细元数据,包括 Content-Type、ETag、时间戳 swift stat media-demo holiday.bin # 下载回来做一致性校验,ETag 就是文件的 MD5 swift download media-demo holiday.bin -o ./holiday.download.bin md5sum ./holiday.bin ./holiday.download.binswift upload默认会按文件大小自动分段,小于 5GB 的文件一般不触发分段上传,超过阈值的会走 SLO/DLO 大对象机制,这是后面排错时容易忽略的一点。swift list -l的-l是 long 格式,多出来的是字节数和时间,排查“传上去了但大小不对”时必看。下载后的md5sum对比是我每次都要做的动作,因为 ETag 对普通对象就是 MD5,两者对不上说明传输过程出了问题,跟存储无关。
对象名建议带时间或业务前缀,比如2024/12/holiday.bin,这不会真的建出目录,但swift list media-demo --prefix=2024/12/能按前缀过滤,效果和目录浏览一样。这个技巧在对象数量上千以后特别有用,因为swift list一次列全量会很慢。
4.3 元数据:查看、添加、修改和删除的完整命令组
元数据是 Swift 里最容易被轻视、也最容易出错的部分。它分容器级和对象级两层,都以 HTTP header 的形式存在,容器级前缀是X-Container-Meta-,对象级是X-Object-Meta-。CLI 上用swift post -m设置:
# 给容器加元数据 year=2014 swift post -m "year:2014" media-demo # 给某个对象加元数据 time=0203 swift post -m "time:0203" media-demo holiday.bin # 查看容器元数据,只看 Meta 开头的行 swift stat media-demo | grep -i meta # 查看对象元数据 swift stat media-demo holiday.bin | grep -i meta # 修改就是同名 key 再写一次,后写的覆盖先写的 swift post -m "year:2015" media-demo # 删除某个元数据键:把值留空,部分版本有效 swift post -m "year:" media-demo # 如果上面那条不生效,用 curl 显式发 X-Remove-Container-Meta- 头 swift stat -v media-demo | head -n 20最后两条是关键。-m "year:"这种留空写法在部分 python-swiftclient 版本里会被当成“设置一个空值”,而不是删除,swift stat一看还在。稳妥做法是用 curl 带上X-Remove-Container-Meta-Year: x这个头,或者升级客户端到明确支持删除语义的版本。参数上注意-m后面的冒号是分隔 key 和 value 的,key 里不能带冒号,value 里可以。这一条踩过坑的人不少——元数据看着删了,第二天做批量清理脚本时发现全在。
元数据的实际用途是给对象打业务标签,比如素材的拍摄时间、版权方、审核状态。它不进对象的字节流,读取时走 HEAD 请求,所以开销很小;但也别塞太多,单个对象元数据总量有上限,超了会返回 400。
4.4 思考题复现:用脚本一次建完 Holidays 与 Sports
报告最后的思考题要求建两个容器、六个对象、逐层加元数据。手工敲 12 条命令很低效,写个脚本一把过:
#!/usr/bin/env bash # 用法:./init_lab.sh 2021211013036 set -euo pipefail SID="${1:?用法: $0 <学号>}" # 容器级元数据:year declare -A C_META=( ["Holidays_${SID}"]="2014" ["Sports_${SID}"]="2016" ) # 对象级元数据,格式 容器|对象|键|值 OBJECTS=( "Holidays_${SID}|Cambridge|time|0203" "Holidays_${SID}|Oxford|time|0204" "Holidays_${SID}|London|time|0205" "Sports_${SID}|Tennis|team|yellow" "Sports_${SID}|Badminton|team|blue" "Sports_${SID}|Soccer|team|red" ) # 第一步:建容器并写容器元数据 for c in "${!C_META[@]}"; do swift post "$c" swift post -m "year:${C_META[$c]}" "$c" echo "[容器] $c -> year:${C_META[$c]}" done # 第二步:建空对象占位并写对象元数据 for row in "${OBJECTS[@]}"; do IFS='|' read -r c o k v <<< "$row" # 用一个空文件上传,得到零字节对象;真实场景换成素材文件 : > "/tmp/${o}.txt" swift upload "$c" "/tmp/${o}.txt" --object-name "$o" swift post -m "${k}:${v}" "$c" "$o" echo "[对象] $c/$o -> ${k}:${v}" done # 第三步:回读验证,逐条打印元数据 for row in "${OBJECTS[@]}"; do IFS='|' read -r c o _ _ <<< "$row" printf '%-22s %s\n' "$c/$o" "$(swift stat "$c" "$o" | grep -i 'Meta' | tr '\n' ' ')" done脚本逻辑分三段:建容器写容器元数据、建对象写对象元数据、回读校验。参数上只接一个学号,用来拼容器名,避免多人共用一套环境时互相覆盖。--object-name是关键参数,它让你上传本地文件时指定云端对象名,不然对象名会带上/tmp/路径和.txt后缀,跟题目要求对不上。第三步的回读是必须的,因为swift post -m成功不代表元数据写对了键名,打出来看一眼比事后翻账单省事。
跑完对照检查:
| 容器 | 容器元数据 | 对象 | 对象元数据 |
|---|---|---|---|
| Holidays_学号 | year:2014 | Cambridge | time:0203 |
| Holidays_学号 | year:2014 | Oxford | time:0204 |
| Holidays_学号 | year:2014 | London | time:0205 |
| Sports_学号 | year:2016 | Tennis | team:yellow |
| Sports_学号 | year:2016 | Badminton | team:blue |
| Sports_学号 | year:2016 | Soccer | team:red |
清理的时候顺序反过来,先删对象再删容器,容器非空时swift delete会直接报 409,这是设计如此,不是 bug。批量删可以用swift delete <容器>加循环,但注意别在生产账户上试。
5. 排查手册:五个现场翻车记录
5.1 Dashboard 登录报 401,密码明明是对的
现象:浏览器输 demo 的账号密码,页面提示认证失败,终端swift auth也报 401,但同样的口令在别的机器上能用。原因八成是时间不同步——Keystone 签发的 token 有严格的生效和过期时间窗,控制节点和客户端的时间差超过几分钟,token 就会被判无效。解决:在控制节点和客户端都跑timedatectl看时间,用chronyc sources -v确认时间源同步正常,把两边拉齐后重登。虚拟机挂起再恢复特别容易触发这个,属于典型的不容易看出来的问题,查日志只会看到Unable to authenticate。
5.2 swift list 报 503,容器列表刷不出来
现象:认证成功,swift stat有输出,但swift list返回 503,Dashboard 上对象存储页面也转圈。原因通常是 proxy server 连不上 account server,或者 account 服务的 ring 文件跟实际设备对不上。解决顺序:先在 proxy 节点看/var/log/swift/proxy-server.log里的具体报错,如果是Timeout就去 account 节点查服务状态;如果是Ring file not found就用swift-ring-builder account.builder确认 ring 里注册的设备数和实际磁盘数一致,不一致要rebalance后把 ring 同步到所有节点。别急着重建 ring,先看设备列表。
5.3 实例之间 ping 不通,安全组看着没问题
现象:同 private 网段的两台实例,互 ping 超时,安全组里 ICMP 规则是放通的。原因多半是端口没真正绑定到网络,或者实例的默认网关指向了错误地址。解决:先在实例里ip route确认默认路由是 10.0.0.1;再在控制节点openstack port list --server <实例名>看端口状态是不是 ACTIVE、绑定的 IP 对不对;最后用ip netns exec qrouter-<id> ping从路由器侧验证。三步走完基本能定位到是实例侧还是网络侧。翻车最多的是克隆实例时带过来的旧网卡配置,/etc/network/interfaces里写死了别的地址。
5.4 对象元数据删不掉,stat 一看还在
现象:执行swift post -m "time:" media-demo holiday.bin没报错,但swift stat里time还在。原因是这个写法在部分客户端版本里被解释为“写入空值”。解决:改用显式的移除头,curl -X POST -H "X-Auth-Token: <token>" -H "X-Remove-Object-Meta-Time: x" <storage_url>/media-demo/holiday.bin,或者把客户端升级到明确支持删除语义的版本。容器级同理,把Object换成Container。这个问题的隐蔽之处在于命令返回码是 0,批量脚本里根本发现不了。
5.5 上传大文件卡住或超时
现象:几 MB 的文件秒传,几百 MB 的文件传到一半超时,重试还是断在差不多的地方。原因是单次 PUT 的时长超过了代理层或网关的超时阈值,或者是分段上传的分片大小没设对。解决:用swift upload --segment-size 1073741824 --segment-container media-segments media-demo bigfile.iso把分片设成 1GB,让客户端走 SLO;同时在 proxy 的配置里把node_timeout和client_timeout调大。参数上分片别设太小,几百 MB 一片会产生大量小对象,反而拖慢 manifest 的读取。传完用swift stat看Content-Length跟本地文件大小是否一致,不一致就是分段没合并完整。
6. 把思考题答成可验证的东西:Ring、副本与数据模型
最后落到报告里那几道需要“解释原理”的题。这部分光背文字没用,得能和前面的操作对上。Swift 的逻辑结构是三层:Account → Container → Object,访问路径形如/v1/AUTH_<account>/<container>/<object>,对象名里允许带斜杠,但存储层是扁平的,没有真正的目录。物理结构上则是另一套东西:proxy server 收请求,account/container/object server 管三类数据,ring 负责“数据放哪”,replicator、updater、auditor 三个后台进程负责副本同步、失败重试和完整性校验。
| 组件 | 职责 | 和实测现象的对应关系 |
|---|---|---|
| Proxy Server | 对外唯一入口,解析请求、查 ring、转发 | 5.2 里的 503 就出在这一层 |
| Account Server | 管容器列表 | 账户统计里的 Containers 字段来自这里 |
| Container Server | 管对象列表 | swift list的响应来自这里 |
| Object Server | 存实际字节 | ETag 与 MD5 一致说明这一层没动过数据 |
| Ring | 一致性哈希,决定副本落点 | 副本数、zone 分布都写在 ring 里 |
Ring 的工作原理,用一句话说就是“把存储空间摊到一个哈希环上,对象哈希后顺时针找第一个虚节点”。细节是:先对所有设备做一致性哈希,哈希值落在 0 到 2³²−1 之间,首尾相接成一个环;每个设备被切成若干虚节点(partition)散布在环上;对象名哈希后从落点顺时针走,遇到的第一个虚节点负责存它;为了副本,继续顺时针找下一个不同 zone/device 的虚节点,凑够 3 个。虚节点的作用是把数据打散,避免某个设备容量大就把热点全吸过来。
验证方法很直接:swift-ring-builder object.builder能看到 ring 的 partition 数、副本数、各 zone 的设备权重;swift-get-nodes object.ring.gz <account> <container> <object>能算出某个对象的三份副本落在哪几个节点上。我一般在上传完一个对象后跑一次这个命令,比对着看三份副本是不是分布在不同设备上——如果三份都落在同一个 zone,那高可用就是纸面上的,机器一挂数据全没。
副本数默认 3,这个数字不是随便定的:它能容忍两份副本同时失效还能读,代价是存储利用率只有三分之一。实验环境为了省空间会改成 1 或 2,改完一定记得重新 rebalance 并同步 ring,否则新对象还是按旧规则落盘,看着配置改了实际没生效。这一点跟前面 5.4 的元数据删除一样,属于“命令成功但行为没变”的坑。
从那以后我每次动 ring、动元数据、动安全组,都强制走一遍“改完回读”的动作:ring 用swift-get-nodes验证落点,元数据用swift stat验证键值,安全组用一次真实的 ping 验证连通。这三步多花不了两分钟,但能挡掉大部分“我以为改好了”的事故。希望帮到你。
本文还有配套的精品资源,点击获取