Nova Metadata 迁移到 Kubernetes 技术方案文档

0 阅读12分钟

1. 文档概述

1.1 目标

将 OpenStack Nova 的 Metadata 服务迁移到 Kubernetes 环境,实现虚拟机实例的元数据管理功能。

1.2 迁移范围

  • ✅ 核心元数据(meta_data.json)
  • ✅ 用户初始化脚本(user_data)
  • ✅ SSH 密钥注入(public_keys)
  • ⚠️ 网络配置(network_data.json)- 简化实现
  • ❌ 供应商数据(vendor_data)- 可选
  • ❌ EC2 兼容格式 - 不实现

2. Nova Metadata 接口清单

2.1 OpenStack 格式接口

接口路径功能优先级迁移决策
/openstack/latest/meta_data.json实例基础信息P0 ⭐️⭐️⭐️⭐️⭐️必须实现
/openstack/latest/user_data初始化脚本P0 ⭐️⭐️⭐️⭐️⭐️必须实现
/openstack/latest/network_data.json网络配置P1 ⭐️⭐️⭐️⭐️简化实现
/openstack/latest/vendor_data.json供应商数据P2 ⭐️⭐️⭐️可选
/openstack/latest/password实例密码P2 ⭐️⭐️⭐️不实现

2.2 EC2 兼容格式接口(不实现)

接口路径功能迁移决策
/latest/meta-data/instance-id实例 ID不实现
/latest/meta-data/hostname主机名不实现
/latest/meta-data/local-ipv4内网 IP不实现
/latest/meta-data/public-keys/0/openssh-keySSH 公钥不实现

决策原因: EC2 格式主要用于兼容性,K8s 环境下可以简化为直接使用环境变量。


3. 核心接口详细分析

3.1 meta_data.json(实例元数据)

功能说明

提供虚拟机的基本身份信息,包括 UUID、名称、IP、SSH 密钥等。

Nova 实现流程

  1. 虚拟机内请求 http://169.254.169.254/openstack/latest/meta_data.json

  2. 请求到达 nova-api-metadata 服务 ↓

  3. 根据 IP 地址查询数据库:

    • instances 表:uuid, name, hostname, created_at
    • instance_metadata 表:用户自定义标签
    • keypairs 表:SSH 公钥 ↓
  4. 组装 JSON 数据返回

#### 输入数据(Nova 数据源)
- **数据库表**:instances, instance_metadata, keypairs
- **关键字段**:uuid, display_name, hostname, key_name, project_id, availability_zone
​
#### 返回数据示例
```json
{
  "uuid": "550e8400-e29b-41d4-a716-446655440000",
  "name": "my-instance",
  "hostname": "my-instance.novalocal",
  "availability_zone": "nova",
  "project_id": "f9d3a1e2b4c3d5e6f7a8b9c0d1e2f3a4",
  "public_keys": {
    "mykey": "ssh-rsa AAAAB3NzaC..."
  },
  "meta": {
    "role": "webserver",
    "environment": "production"
  }
}

K8s 实现方案

核心机制:Downward API

Kubernetes 的 Downward API 允许 Pod 自动获取自己的元数据,通过环境变量注入。

映射关系:

Nova 字段K8s 来源注入方式
uuidPod.metadata.uid环境变量
namePod.metadata.name环境变量
hostnamePod.metadata.name环境变量
project_idPod.metadata.namespace环境变量
availability_zonePod.spec.nodeName环境变量
local-ipv4Pod.status.podIP环境变量
metaPod.metadata.labels环境变量

实现方式: 创建 Pod 时,通过 Downward API 自动注入环境变量:

env:
- name: INSTANCE_UUID
  valueFrom:
    fieldRef:
      fieldPath: metadata.uid
- name: INSTANCE_NAME
  valueFrom:
    fieldRef:
      fieldPath: metadata.name
- name: INSTANCE_IP
  valueFrom:
    fieldRef:
      fieldPath: status.podIP

Pod 内访问:

echo $INSTANCE_UUID
echo $INSTANCE_NAME
echo $INSTANCE_IP

3.2 user_data(初始化脚本)

功能说明

用户在创建虚拟机时提供的初始化脚本,虚拟机启动后自动执行(通常由 cloud-init 处理)。

Nova 实现流程

1. 用户创建虚拟机时传入 user_data(base64 编码)
   POST /servers {"server": {"user_data": "IyEvYmluL2Jhc2g..."}}
   ↓
2. Nova 存储到数据库 instances.user_data 字段
   ↓
3. 虚拟机启动后,cloud-init 请求 metadata API
   ↓
4. nova-api-metadata 返回解码后的脚本
   ↓
5. cloud-init 执行脚本(安装软件、配置服务等)

输入数据

  • API 请求:base64 编码的脚本
  • 存储位置:数据库 instances.user_data

返回数据示例

#!/bin/bash
echo "Hello World"
apt-get update
apt-get install -y nginx
systemctl start nginx

K8s 实现方案

核心机制:ConfigMap + Init Container

流程:

1. API Gateway 接收 user_data
   ↓
2. 创建 ConfigMap 存储脚本
   ↓
3. Pod 使用 Init Container 执行脚本
   ↓
4. Init Container 完成后,主容器启动

实现步骤:

步骤 1:创建 ConfigMap

apiVersion: v1
kind: ConfigMap
metadata:
  name: my-instance-userdata
data:
  user-data.sh: |
    #!/bin/bash
    echo "Hello World"
    apt-get update
    apt-get install -y nginx

步骤 2:Pod 使用 Init Container

apiVersion: v1
kind: Pod
spec:
  initContainers:
  - name: init-userdata
    image: ubuntu:22.04
    command: ["/bin/bash", "/userdata/user-data.sh"]
    volumeMounts:
    - name: userdata
      mountPath: /userdata
  
  containers:
  - name: main
    image: ubuntu:22.04
  
  volumes:
  - name: userdata
    configMap:
      name: my-instance-userdata
      defaultMode: 0755  # 可执行权限

为什么用 Init Container?

  • ✅ 在主容器启动前执行(符合初始化语义)
  • ✅ 失败会阻止 Pod 启动(确保初始化成功)
  • ✅ 与主容器隔离(不影响运行环境)

3.3 public_keys(SSH 密钥)

功能说明

提供 SSH 公钥,写入 ~/.ssh/authorized_keys,允许用户 SSH 登录。

Nova 实现流程

1. 用户创建虚拟机时指定 key_name
   POST /servers {"server": {"key_name": "mykey"}}
   ↓
2. Nova 从 keypairs 表查询公钥内容
   ↓
3. 虚拟机启动,cloud-init 请求 metadata API
   ↓
4. nova-api-metadata 返回公钥
   ↓
5. cloud-init 写入 ~/.ssh/authorized_keys

输入数据

  • 数据库表:keypairs
  • 关键字段:name, public_key, user_id

返回数据示例

{
  "public_keys": {
    "mykey": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7... user@host"
  }
}

K8s 实现方案

核心机制:Secret 挂载

流程:

1. API Gateway 接收 SSH 公钥
   ↓
2. 创建 Secret 存储公钥
   ↓
3. Pod 挂载 Secret 到 ~/.ssh/authorized_keys
   ↓
4. SSH 服务读取密钥,允许登录

实现步骤:

步骤 1:创建 Secret

apiVersion: v1
kind: Secret
metadata:
  name: my-instance-ssh
type: Opaque
stringData:
  authorized_keys: |
    ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7... user@host

步骤 2:Pod 挂载 Secret

apiVersion: v1
kind: Pod
spec:
  containers:
  - name: main
    volumeMounts:
    - name: ssh-keys
      mountPath: /root/.ssh
      readOnly: true
  
  volumes:
  - name: ssh-keys
    secret:
      secretName: my-instance-ssh
      defaultMode: 0600  # 权限 600(SSH 要求)

为什么用 Secret?

  • ✅ 专为敏感数据设计(加密存储)
  • ✅ 支持文件挂载(直接写入文件系统)
  • ✅ 权限控制(可设置文件权限)

3.4 network_data.json(网络配置)

功能说明

提供网络接口配置信息(IP、网关、DNS 等),用于静态网络配置。

Nova 实现流程

1. 请求 http://169.254.169.254/openstack/latest/network_data.json
   ↓
2. nova-api-metadata 查询 Neutron
   - neutron.list_ports(device_id=instance_id)
   - 获取 IP、MAC、网关、路由等
   ↓
3. 组装 network_data.json 返回

返回数据示例

{
  "links": [
    {
      "ethernet_mac_address": "fa:16:3e:9c:bf:3d",
      "id": "tap1",
      "mtu": 1500
    }
  ],
  "networks": [
    {
      "id": "network0",
      "link": "tap1",
      "ip_address": "192.168.1.10",
      "netmask": "255.255.255.0",
      "gateway": "192.168.1.1"
    }
  ],
  "services": [
    {"type": "dns", "address": "8.8.8.8"}
  ]
}

K8s 实现方案

核心机制:CNI 自动管理

Kubernetes 通过 CNI(Container Network Interface)插件自动配置网络,不需要手动配置

自动提供:

  • ✅ IP 地址分配(IPAM)
  • ✅ 路由配置
  • ✅ DNS 解析(kube-dns/CoreDNS)
  • ✅ 网络隔离(NetworkPolicy)

简化实现: 如果需要提供 network_data.json,可以通过环境变量暴露基本信息:

env:
- name: POD_IP
  valueFrom:
    fieldRef:
      fieldPath: status.podIP
- name: HOST_IP
  valueFrom:
    fieldRef:
      fieldPath: status.hostIP

为什么简化?

  • ✅ K8s 网络自动管理,无需静态配置
  • ✅ 容器环境不需要复杂的网络配置文件
  • ✅ 减少实现复杂度

4. K8s 实现方案总结

4.1 核心技术栈

技术作用对应 Nova
Downward API注入 Pod 元数据Metadata API
ConfigMap存储配置数据user_data
Secret存储敏感数据SSH 密钥
Init Container初始化容器cloud-init
client-goK8s API 客户端Nova API 客户端

4.2 整体架构

┌─────────────────────────────────────────────┐
│         用户(发送 Nova API 请求)           │
└──────────────────┬──────────────────────────┘
                   ↓
┌─────────────────────────────────────────────┐
│       API Gateway(Go 程序)                │
│                                             │
│  1. 接收 Nova API 请求(POST /servers)     │
│  2. 转换为 K8s 资源                         │
│  3. 调用 K8s API 创建资源                   │
│  4. 返回 Nova 格式响应                      │
└──────────────────┬──────────────────────────┘
                   ↓
┌─────────────────────────────────────────────┐
│         Kubernetes API Server               │
└──────────────────┬──────────────────────────┘
                   ↓
┌─────────────────────────────────────────────┐
│              创建的资源                      │
│                                             │
│  ├─ ConfigMap(user_data 脚本)             │
│  ├─ Secret(SSH 公钥)                      │
│  └─ Pod                                     │
│      ├─ Init Container(执行 user_data)    │
│      └─ Main Container(注入元数据环境变量) │
└─────────────────────────────────────────────┘

4.3 创建虚拟机的完整流程

用户发送请求
POST /servers {
  "server": {
    "name": "my-vm",
    "imageRef": "ubuntu:22.04",
    "flavorRef": "2",
    "user_data": "IyEvYmluL2Jhc2g...",
    "key_name": "mykey"
  }
}
    ↓
API Gateway 处理
├─ 1. 解码 user_data(base64 → 文本)
├─ 2. 查询 SSH 公钥(从 keypairs)
├─ 3. 获取 Flavor 配置(CPU/内存)
    ↓
创建 K8s 资源
├─ 1. 创建 ConfigMap
│      name: my-vm-userdata
│      data: user-data.sh
├─ 2. 创建 Secret
│      name: my-vm-ssh
│      data: authorized_keys
├─ 3. 创建 Pod
│      ├─ Init Container
│      │   ├─ 挂载 ConfigMap
│      │   └─ 执行 user-data.sh
│      ├─ Main Container
│      │   ├─ 注入环境变量(Downward API)
│      │   ├─ 挂载 Secret(SSH 密钥)
│      │   └─ 资源限制(CPU/内存)
    ↓
返回响应
{
  "server": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "my-vm",
    "status": "BUILD"
  }
}

5. 关键概念解释

5.1 Downward API

定义: Kubernetes 的一个功能,允许 Pod 获取自己的元数据信息。

类比:

  • Nova Metadata:外部查询台(需要 HTTP 请求 169.254.169.254)
  • Downward API:内部注入器(自动写入环境变量或文件)

支持的字段:

  • Pod 名称:metadata.name
  • Pod UID:metadata.uid
  • Pod IP:status.podIP
  • 节点名:spec.nodeName
  • 命名空间:metadata.namespace
  • 标签:metadata.labels['key']

使用方式:

env:
- name: MY_POD_NAME
  valueFrom:
    fieldRef:
      fieldPath: metadata.name

5.2 Init Container

定义: 在主容器启动之前运行的特殊容器,用于执行初始化任务。

特点:

  • 按顺序执行(一个接一个)
  • 必须全部成功,主容器才启动
  • 可以包含主容器镜像中没有的工具

对应关系:

  • Nova:cloud-init 执行 user_data
  • K8s:Init Container 执行 user_data 脚本

执行流程:

Pod 创建 → Init Container 1 → Init Container 2 → 主容器启动

5.3 Secret vs ConfigMap

对比:

类型用途数据敏感性存储方式
Secret密码、密钥、证书敏感数据Base64 编码 + 加密
ConfigMap配置文件、脚本非敏感数据明文存储

使用场景:

  • Secret:SSH 密钥、数据库密码、API Token
  • ConfigMap:user_data 脚本、配置文件、环境变量

5.4 client-go

定义: Kubernetes 官方提供的 Go 语言客户端库,用于与 K8s API Server 交互。

核心功能:

  • 创建/查询/删除 K8s 资源(Pod、Service、ConfigMap 等)
  • 监听资源变化(Watch)
  • 批量操作

基本使用:

// 1. 创建客户端
config, _ := rest.InClusterConfig()
clientset, _ := kubernetes.NewForConfig(config)
​
// 2. 操作资源
// 创建 Pod
clientset.CoreV1().Pods(namespace).Create(ctx, pod, metav1.CreateOptions{})
​
// 查询 Pod
clientset.CoreV1().Pods(namespace).List(ctx, metav1.ListOptions{})
​
// 删除 Pod
clientset.CoreV1().Pods(namespace).Delete(ctx, name, metav1.DeleteOptions{})

6. 迁移决策说明

6.1 为什么不完全兼容 169.254.169.254?

方案对比:

方案优点缺点工作量
完全模拟 169.254.169.254100% 兼容传统应用需要特权容器、复杂网络配置1000+ 行代码
Downward API简单、K8s 原生、无需特权不兼容传统 cloud-init200 行代码

决策:使用 Downward API

理由:

  • ✅ K8s 原生支持,稳定可靠
  • ✅ 无需特权容器(安全)
  • ✅ 实现简单,维护成本低
  • ✅ 对于新应用足够用
  • ⚠️ 传统应用需要修改代码(读环境变量而不是 HTTP 请求)

6.2 为什么不实现 vendor_data?

原因:

  1. 使用频率低:大部分场景不需要
  2. 供应商特定:内容因部署环境而异
  3. 可延后实现:不影响核心功能

如果需要: 创建全局 ConfigMap 即可,工作量很小。


6.3 为什么不实现 EC2 格式?

原因:

  1. 主要用于兼容:AWS EC2 格式主要为了迁移旧应用
  2. OpenStack 格式够用:功能重叠
  3. 工作量较大:需要实现大量端点

决策:只实现 OpenStack 格式


7. 数据流对比

7.1 Nova Metadata 数据流

虚拟机内应用
    ↓ HTTP GET
http://169.254.169.254/openstack/latest/meta_data.json
    ↓
Neutron metadata proxy(网络层转发)
    ↓ 添加 X-Instance-ID header
nova-api-metadata 服务
    ↓ 查询数据库
MySQL(instances, keypairs, instance_metadata)
    ↓ 返回数据
nova-api-metadata 组装 JSON
    ↓ HTTP 200 + JSON
虚拟机内应用(解析使用)

7.2 K8s Downward API 数据流

Pod 创建时
    ↓
API Gateway 定义 Downward API
    ↓
Kubernetes API Server
    ↓ 调度 Pod
Kubelet(节点代理)
    ↓ 自动注入环境变量
Pod 内容器
    ↓ 读取环境变量
应用直接使用(无需 HTTP 请求)

关键区别:

  • Nova:运行时动态查询(需要网络和数据库)
  • K8s:启动时静态注入(无需额外请求)

8. 实施路线图

阶段 1:最小可行产品(2 周)

目标:实现核心虚拟机管理

✅ 功能清单:
1. POST /servers - 创建虚拟机
   ├─ 创建 Pod
   ├─ Downward API 注入元数据
   └─ 返回 Nova 格式响应
2. GET /servers - 列出虚拟机
3. GET /servers/{id} - 查看详情
4. DELETE /servers/{id} - 删除虚拟机

验收标准: 可以通过 API 创建、查询、删除"虚拟机"(实际是 Pod)。


阶段 2:Metadata 完整支持(1 周)

目标:实现元数据功能

✅ 功能清单:
1. user_data 支持
   ├─ 创建 ConfigMap
   └─ Init Container 执行脚本
2. SSH 密钥支持
   ├─ 创建 Secret
   └─ 挂载到 ~/.ssh/authorized_keys
3. 完善元数据环境变量
   └─ Downward API 所有字段

验收标准:

  • Pod 内能通过环境变量获取元数据
  • user_data 脚本正确执行
  • SSH 密钥正确挂载

阶段 3:增强功能(1 周,可选)

⚠️ 可选功能:
1. network_data.json 简化版
2. Flavor 管理 API
3. 错误处理和日志
4. 性能优化

9. 工作量评估

9.1 核心功能工作量

功能模块工作量代码量
API Gateway 框架1 天100 行
创建虚拟机(POST /servers)2 天200 行
查询和删除1 天100 行
user_data 支持1 天100 行
SSH 密钥支持1 天50 行
Downward API 配置0.5 天50 行
测试和调试2 天-
总计8.5 天~600 行

9.2 技能要求

  • ✅ Go 语言基础
  • ✅ Kubernetes 基本概念(Pod、ConfigMap、Secret)
  • ✅ HTTP API 开发经验
  • ⚠️ 不需要深入的 K8s 运维经验
  • ⚠️ 不需要深入的 OpenStack 开发经验

10. 总结

10.1 核心要点

方面NovaK8s迁移策略
元数据HTTP API (169.254.169.254)Downward API(环境变量)简化实现
初始化cloud-init + user_dataInit Container + ConfigMap功能等价
SSH 密钥Metadata APISecret 挂载功能等价
网络Neutron 动态配置CNI 自动管理K8s 原生
存储Cinder 卷服务PVC/PVK8s 原生

10.2 关键决策

  1. 使用 Downward API 代替 169.254.169.254

    • 理由:简单、原生、安全
    • 代价:不兼容传统应用
  2. 不实现 EC2 格式

    • 理由:工作量大、收益小
    • 代价:无法直接兼容 AWS 迁移应用
  3. 简化 network_data.json

    • 理由:K8s 自动管理网络
    • 代价:丢失部分网络配置能力

10.3 适用场景

适合:

  • ✅ 新开发的云原生应用
  • ✅ 容器化应用
  • ✅ 可以修改代码的应用

不适合:

  • ❌ 完全依赖 cloud-init 的传统应用
  • ❌ 无法修改的闭源应用
  • ❌ 需要完整 OpenStack 兼容性的场景

11. 参考资料

  1. Nova Metadata 官方文档 docs.openstack.org/nova/2025.2…
  2. Kubernetes Downward API kubernetes.io/docs/tasks/…
  3. Init Containers kubernetes.io/docs/concep…
  4. ConfigMap kubernetes.io/docs/concep…
  5. Secret kubernetes.io/docs/concep…
  6. client-go 示例 github.com/kubernetes/…

附录:快速参考

A. 接口迁移速查表

Nova 接口必须?K8s 方案
meta_data.jsonDownward API
user_dataConfigMap + Init Container
public_keysSecret 挂载
network_data.json⚠️简化或不实现
vendor_data.json不实现
password不实现
EC2 格式不实现

B. K8s 资源对照表

Nova 概念K8s 资源
虚拟机实例Pod / Deployment
规格(Flavor)ResourceRequirements
镜像Container Image
SSH 密钥Secret
user_dataConfigMap
元数据Downward API
网络Service / Ingress
PersistentVolume