OpenStack Instance ID 映射机制详解

一、概述

OpenStack 采用 UUID 作为实例的原生标识符,但为了兼容 AWS EC2 API,实现了一套 UUID 到短格式 EC2 ID 的映射机制。


二、两种 ID 格式

2.1 OpenStack 原生格式(UUID)

  • 格式:标准 UUID(36字符)
  • 示例4daf043f-cc30-4513-b639-84906a4278e1
  • 特点:全局唯一、易于分布式系统使用
  • 存储位置instances 表的主键

2.2 EC2 兼容格式

  • 格式i- + 8位十六进制数
  • 示例i-00000004
  • 特点:短小、易读,符合 AWS EC2 规范
  • 生成方式:通过映射表的自增整数转换而来

三、映射表结构

3.1 数据库表定义

sql 复制代码
-- 表名:instance_id_mappings
-- 位置:nova/db/main/models.py:1093-1102

CREATE TABLE instance_id_mappings (
    id INT AUTO_INCREMENT PRIMARY KEY,  -- 自增整数,用于生成 EC2 ID
    uuid VARCHAR(36) NOT NULL,          -- 指向 instances.uuid
    created_at DATETIME,
    updated_at DATETIME,
    deleted_at DATETIME,
    deleted INT DEFAULT 0,
    INDEX ix_instance_id_mappings_uuid (uuid)
);

3.2 实际数据示例

css 复制代码
+----+--------------------------------------+
| id | uuid                                 |
+----+--------------------------------------+
|  1 | a0364b6e-61c7-477a-85e5-721074cfbbf3 |  → i-00000001
|  2 | 4daf043f-cc30-4513-b639-84906a4278e1 |  → i-00000002
|  4 | 66baa731-8d64-4d0b-85ad-45d404cd67f9 |  → i-00000004
+----+--------------------------------------+

注意:ID 是自增的,可能不连续(如这里没有 3)


四、转换流程详解

4.1 UUID → EC2 ID(正向转换)

步骤 1:虚拟机访问元数据服务

ruby 复制代码
# 虚拟机内部执行
curl http://169.254.169.254/latest/meta-data/instance-id

步骤 2:元数据服务处理请求

ruby 复制代码
# 文件:nova/api/metadata/base.py:254-262

def get_ec2_metadata(self, version):
    meta_data = {
        'instance-id': self.instance.ec2_ids.instance_id,  # ← 调用 ec2_ids
        # ...
    }

步骤 3:获取 EC2 ID

python 复制代码
# 文件:nova/objects/ec2.py:214-217

@staticmethod
def _get_ec2_ids(context, instance):
    ec2_ids = {}
    # 将 UUID 转换为 EC2 格式的 instance-id
    ec2_ids['instance_id'] = id_to_ec2_inst_id(context, instance.uuid)

步骤 4:UUID 转整数 ID

python 复制代码
# 文件:nova/objects/ec2.py:52-60

def id_to_ec2_inst_id(context, instance_id):
    """从 UUID 获取 EC2 格式的 instance-id"""
    if uuidutils.is_uuid_like(instance_id):
        # 查询或创建映射记录
        int_id = get_int_id_from_instance_uuid(context, instance_id)
        return id_to_ec2_id(int_id)  # 转换为 i-xxxxxxxx 格式

步骤 5:查询或创建映射(懒加载)

python 复制代码
# 文件:nova/objects/ec2.py:64-74

@memoize  # 缓存7天
def get_int_id_from_instance_uuid(context, instance_uuid):
    try:
        # 先尝试查询已有映射
        imap = EC2InstanceMapping.get_by_uuid(context, instance_uuid)
        return imap.id  # 返回整数 ID,如 4
    except exception.NotFound:
        # 首次访问,创建新映射(自增 ID)
        imap = EC2InstanceMapping(context)
        imap.uuid = instance_uuid
        imap.create()  # INSERT,数据库自动分配 ID
        return imap.id

步骤 6:格式化为 EC2 ID

python 复制代码
# 文件:nova/objects/ec2.py:47-49

def id_to_ec2_id(instance_id, template='i-%08x'):
    """将整数 ID 转换为 EC2 格式"""
    return template % int(instance_id)  # 4 → 'i-00000004'

步骤 7:返回结果

css 复制代码
i-00000004

4.2 EC2 ID → UUID(反向查询)

python 复制代码
# 文件:nova/objects/ec2.py:150-153

@base.remotable_classmethod
def get_by_id(cls, context, ec2_id):
    """通过 EC2 整数 ID 查询 UUID"""
    # ec2_id = 4
    db_imap = db.ec2_instance_get_by_id(context, ec2_id)
    return cls._from_db_object(context, cls(), db_imap)

流程:

ini 复制代码
'i-00000004' → 解析为 4 → 查询 instance_id_mappings WHERE id=4 
→ 得到 uuid='4daf043f-cc30-4513-b639-84906a4278e1'

五、为什么需要映射?

5.1 历史原因

  • 早期 OpenStack(Essex 之前):使用整数 ID
  • Essex 版本开始:改用 UUID,但需要保持 EC2 兼容性
  • 映射表:作为兼容层,连接新旧系统

5.2 EC2 兼容性

AWS EC2 的元数据服务使用 i-xxxxxxxx 格式,许多 cloud-init、监控工具、脚本依赖这个格式。

5.3 API 兼容性

OpenStack 的 EC2 API(已弃用但仍有用户)需要短格式 ID。


六、两种元数据端点对比

6.1 EC2 兼容端点

ruby 复制代码
curl http://169.254.169.254/latest/meta-data/instance-id
# 输出:i-00000004

特点

  • 返回短格式 EC2 ID
  • 兼容 AWS EC2 工具
  • 需要映射表支持

6.2 OpenStack 原生端点

ruby 复制代码
curl http://169.254.169.254/openstack/latest/meta_data.json

输出

json 复制代码
{
    "uuid": "4daf043f-cc30-4513-b639-84906a4278e1",
    "name": "demo2",
    "hostname": "demo2.novalocal",
    "project_id": "95a4115a122d4184ba5b8c7836fcc770",
    ...
}

特点

  • 返回真实 UUID
  • 包含更多 OpenStack 原生信息
  • 不需要映射表

七、关键代码位置

功能 文件路径 行号 说明
映射表模型 nova/db/main/models.py 1093-1102 InstanceIdMapping 表定义
EC2 ID 格式化 nova/objects/ec2.py 47-49 id_to_ec2_id() 函数
UUID→整数 nova/objects/ec2.py 64-74 get_int_id_from_instance_uuid()
元数据返回 nova/api/metadata/base.py 258 instance-id 字段设置
数据库操作 nova/db/main/api.py 4106-4142 EC2 映射 CRUD

八、性能优化

8.1 缓存机制

python 复制代码
# nova/objects/ec2.py:26-27
# 映射结果缓存 7 天
_CACHE_TIME = 7 * 24 * 60 * 60

@memoize  # 使用装饰器缓存
def get_int_id_from_instance_uuid(context, instance_uuid):
    ...

8.2 懒加载

  • 映射记录在首次访问时才创建
  • 不访问 EC2 端点的实例不会创建映射
  • 减少数据库写入压力

九、实际应用建议

9.1 如果你是 OpenStack 管理员

  • 查询实例:优先使用 UUID(OpenStack CLI/API)
  • 监控系统:使用 UUID 更稳定
  • 数据库维护instance_id_mappings 表只增不减,可定期清理已删除实例的记录

9.2 如果你在虚拟机内部

ruby 复制代码
# 获取 EC2 格式 ID(兼容性好)
curl http://169.254.169.254/latest/meta-data/instance-id

# 获取真实 UUID(推荐)
curl -s http://169.254.169.254/openstack/latest/meta_data.json | jq -r '.uuid'

9.3 如果你在开发应用

  • 推荐:使用 OpenStack 原生 UUID
  • 兼容性需求:如需支持 AWS EC2,使用 EC2 格式
  • 双重支持:同时提供两种格式的支持

十、总结

项目 OpenStack UUID EC2 ID
格式 4daf043f-cc30-4513-b639-84906a4278e1 i-00000004
长度 36 字符 10 字符
原生性 ✅ 原生主键 ❌ 兼容层
全局唯一 ✅ 是 ⚠️ 仅单集群内
需要映射表 ❌ 否 ✅ 是
获取端点 /openstack/latest/meta_data.json /latest/meta-data/instance-id
使用场景 OpenStack API、数据库查询 EC2 兼容工具

核心观点 :OpenStack 原生使用 UUID,映射表仅用于生成 EC2 兼容的短 ID,不是将 EC2 ID 转回 UUID,而是将 UUID 转为 EC2 ID。

相关推荐
无责任此方_修行中3 小时前
拒绝 AI 焦虑!一个普通程序员的真实 AI 工作流(附成本账单)
后端·程序员·ai编程
Assby3 小时前
从洋葱模型看Java与Go的设计哲学:为什么它们如此不同?
java·后端·架构
命运石之门的选择3 小时前
Flink 并行度调优"黄金三步法"
后端
泰式大师3 小时前
在 AI Agent 场景下,我们如何优雅地处理长文本?
后端
命运石之门的选择3 小时前
Flink和CheckPoint简单了解
后端
Java水解3 小时前
Python开发从入门到精通:Web框架Django实战
后端·python
回家路上绕了弯4 小时前
OpenClaw 本地 AI 智能体全解析
后端·agent
我爱娃哈哈5 小时前
Spring Cloud Gateway + 请求聚合(GraphQL-like):一次调用合并多个微服务响应
后端
用户298698530145 小时前
C#:三行代码,给 Word 文档的文本框“一键清空”
后端·c#·.net