Apache Libcloud 常用 API 与示例详解

基于 apache-libcloud 3.9.1 实测详解 Compute/Storage/DNS/LoadBalancer/Containers 五大模块 API,含 Dummy 驱动节点全生命周期、本地存储对象读写、DNS 域名记录演练的真实输出。

Apache LibcloudApache Libcloud2026/10/12

Apache Libcloud 常用 API 与示例详解

目标:Libcloud 用统一抽象屏蔽各家云 API 差异,五大子模块(Compute/Storage/DNS/LoadBalancer/Containers)均遵循「Provider 枚举 + get_driver 驱动类」模式。本文在 Ubuntu 22.04 + Python 3.10 + apache-libcloud 3.9.1 下逐模块实测:Compute 与 DNS 用内置 Dummy 驱动完成全生命周期演练,Storage 用本地文件驱动完成对象读写,LoadBalancer/Containers 给出数据模型与驱动形态。已在 Ubuntu 22.04.5 LTS(x86_64)实测通过。

一、环境信息

  • 操作系统:Ubuntu 22.04.5 LTS,Kernel 6.8.0-138-generic,架构 x86_64(amd64)
  • Python:3.10.12
  • apache-libcloud:3.9.1(pip 安装,位置 /usr/local/lib/python3.10/dist-packages/libcloud)
  • 依赖:requests 2.34.2、certifi 2026.7.22、fasteners 0.20(存储本地驱动)
  • 演示驱动:Dummy 驱动(compute/dns)、本地驱动(storage)、离线数据模型(loadbalancer/container)
  • 示例凭据约定:演示用账号 admin、密码 q1w2e3r4(Dummy 驱动不校验,仅示意参数位)

二、模块概览

Libcloud 五大子模块与驱动数量(实测统计):

模块Provider 常量数可用驱动数统一入口
Compute(计算节点)7549libcloud.compute.providers.get_driver
Storage(对象存储)4137libcloud.storage.providers.get_driver
DNS(域名解析)2928libcloud.dns.providers.get_driver
LoadBalancer(负载均衡)1210libcloud.loadbalancer.providers.get_driver
Containers(应用工作负载)77libcloud.container.providers.get_driver

统计脚本与真实回显:

from libcloud.compute.providers import Provider as CP, DRIVERS as CDRIVERS
from libcloud.storage.providers import Provider as SP, DRIVERS as SDRIVERS
from libcloud.dns.providers import Provider as DP, DRIVERS as DDRIVERS
from libcloud.loadbalancer.providers import Provider as LBP, DRIVERS as LBDRIVERS
from libcloud.container.providers import Provider as KP, DRIVERS as KDRIVERS

print("libcloud.__version__ =", __import__("libcloud").__version__)
print("compute drivers   :", len(CDRIVERS))
print("storage drivers   :", len(SDRIVERS))
print("dns drivers       :", len(DDRIVERS))
print("loadbalancer drivers:", len(LBDRIVERS))
print("container drivers :", len(KDRIVERS))

真实回显:

libcloud.__version__ = 3.9.1
compute drivers   : 49
storage drivers   : 37
dns drivers       : 28
loadbalancer drivers: 10
container drivers : 7

统一使用范式(五大模块一致):

from libcloud.<module>.types import Provider
from libcloud.<module>.providers import get_driver

DriverCls = get_driver(Provider.<厂商常量>)
driver = DriverCls("admin", "q1w2e3r4")   # 凭据按厂商要求传 key/secret

Compute 模块的 Provider 常量全表(实测枚举):

ABIQUO, ALIYUN_ECS, AURORACOMPUTE, AZURE, AZURE_ARM, BRIGHTBOX, CISCOCCS, CLOUDFRAMES, CLOUDSCALE, CLOUDSIGMA, CLOUDSIGMA_US, CLOUDSTACK, DIGITAL_OCEAN, DIMENSIONDATA, DUMMY, EC2, EC2_AP_NORTHEAST, EC2_AP_NORTHEAST1, EC2_AP_NORTHEAST2, EC2_AP_SOUTHEAST, EC2_AP_SOUTHEAST2, EC2_CA_CENTRAL1, EC2_EU, EC2_EU_WEST2, EC2_SA_EAST, EC2_US_EAST, EC2_US_EAST_OHIO, EC2_US_WEST, EC2_US_WEST_OREGON, EQUINIXMETAL, EUCALYPTUS, EXOSCALE, GANDI, GCE, GIG_G8, GRIDSCALE, HPCLOUD, IBM, IKOULA, INTERNETSOLUTIONS, KAMATERA, KTUCLOUD, KUBEVIRT, LIBVIRT, LINODE, MAXIHOST, NIMBUS, NINEFOLD, NTTA, NTTCIS, ONAPP, OPENNEBULA, OPENSTACK, OPSOURCE, OUTSCALE, OUTSCALE_INC, OUTSCALE_SAS, OVH, RACKSPACE, RACKSPACE_FIRST_GEN, RACKSPACE_NOVA_BETA, RACKSPACE_NOVA_DFW, RACKSPACE_NOVA_LON, RACKSPACE_NOVA_ORD, RACKSPACE_UK, RIMUHOSTING, RUNABOVE, SCALEWAY, TERREMARK, UPCLOUD, VCL, VCLOUD, VPSNET, VSPHERE, VULTR

三、前置条件

  • 已按《Ubuntu 22.04 安装 Apache Libcloud 指南》完成 3.9.1 安装并可 import libcloud。
  • Storage 本地驱动额外依赖 fasteners:pip3 install fasteners。
  • 无需真实云账号:Compute/DNS 用 Dummy 驱动(纯内存模拟),Storage 用本地文件驱动;接入真实厂商时把 Provider 常量与凭据换成实际值即可。

四、Compute API 详解

4.1 驱动获取与资源查询(镜像/规格/位置)

from libcloud.compute.types import Provider
from libcloud.compute.providers import get_driver

cls = get_driver(Provider.DUMMY)
driver = cls("admin")

images = driver.list_images()
sizes = driver.list_sizes()
locations = driver.list_locations()
print("images:", [(i.id, i.name) for i in images])
print("sizes :", [(s.id, s.name, s.ram, s.disk, s.price) for s in sizes])
print("locations:", [(l.id, l.name, l.country) for l in locations])

真实回显:

images: [('1', 'Ubuntu 9.10'), ('2', 'Ubuntu 9.04'), ('3', 'Slackware 4')]
sizes : [('1', 'Small', 128, 4, 4), ('2', 'Medium', 512, 16, 8), ('3', 'Big', 4096, 32, 32), ('4', 'XXL Big', 8192, 128, 64)]
locations: [('1', "Paul's Room", 'US'), ('2', 'London Loft', 'GB'), ('3', 'Island Datacenter', 'FJ')]

单个资源的完整属性(NodeSize 六要素、NodeImage 与 NodeLocation):

s = driver.list_sizes()[2]
print("size attrs:", s.id, s.name, s.ram, s.disk, s.bandwidth, s.price)
img = driver.list_images()[0]
print("image attrs:", img.id, img.name, img.extra)
loc = driver.list_locations()[0]
print("location attrs:", loc.id, loc.name, loc.country, loc.extra)

真实回显:

size attrs: 3 Big 4096 32 2500 32
image attrs: 1 Ubuntu 9.10 {}
location attrs: 1 Paul's Room US {}

4.2 创建节点(create_node)

size = sizes[0]
image = images[0]
node = driver.create_node(name="libcloud-demo-01", image=image, size=size)
print("created node:", node)
print("node.id:", node.id, "| node.name:", node.name)
print("node.state:", node.state, "| type:", type(node.state))
print("public_ips:", node.public_ips, "| private_ips:", node.private_ips)
print("node.extra:", node.extra)
print("node.size.name/image.name:", node.size.name, "/", node.image.name)

真实回显:

created node: <Node: uuid=aef321fde331680846014b121ff377dcb8477731, name=dummy-3, state=RUNNING, public_ips=['127.0.0.3'], private_ips=[], provider=Dummy Node Provider ...>
node.id: 3 | node.name: dummy-3
node.state: running | type: <enum 'NodeState'>
public_ips: ['127.0.0.3'] | private_ips: []
node.extra: {'foo': 'bar'}
node.size.name/image.name: foo / image

💡 说明:两点驱动行为值得注意——Dummy 驱动忽略自定义 name,统一命名为 dummy-<序号>;node.state 是 NodeState 枚举(str 子类),打印为小写 running,repr 显示 RUNNING。node.size.name/node.image.name 显示占位值是该模拟驱动的固有行为,真实厂商驱动会返回实际属性。

4.3 节点列表与状态(list_nodes)

nodes = driver.list_nodes()
print("list_nodes:", [(n.id, n.name, str(n.state)) for n in nodes])

真实回显(Dummy 驱动内置 2 个预置节点 + 新建的 1 个):

list_nodes: [('1', 'dummy-1', 'running'), ('2', 'dummy-2', 'running'), ('3', 'dummy-3', 'running')]

4.4 重启与销毁(reboot_node / destroy_node)

print("reboot_node ->", driver.reboot_node(node))
print("destroy_node ->", driver.destroy_node(node))
print("list_nodes after destroy:", [(n.id, n.name) for n in driver.list_nodes()])

真实回显:

reboot_node -> True
destroy_node -> True
list_nodes after destroy: [('1', 'dummy-1'), ('2', 'dummy-2')]

节点全生命周期:create_node → list_nodes → reboot_node → destroy_node,全部返回布尔或 Node 对象;start_node/stop_node 等扩展动作由具体厂商驱动实现(Dummy 驱动未实现,见 FAQ 10.1)。

4.5 密钥对导入(import_key_pair_from_string)

kp = driver.import_key_pair_from_string(name="demo-key", key_material="ssh-rsa AAAAB3Nza demo@server")
print("kp.name:", kp.name)
print("kp.fingerprint:", kp.fingerprint)
print("kp.private_key:", kp.private_key)

真实回显(Dummy 驱动原样回显占位字段):

kp.name: demo-key
kp.fingerprint: fingerprint
kp.private_key: private_key

五、Storage API 详解

5.1 本地驱动初始化与存储桶管理

本地驱动(Provider.LOCAL)把文件系统目录模拟成对象存储,key 参数为根目录:

from libcloud.storage.types import Provider
from libcloud.storage.providers import get_driver

cls = get_driver(Provider.LOCAL)
driver = cls(key="/opt/libcloud-storage")   # key = 本地根目录

container = driver.create_container(container_name="backup")
print("container:", container.name, "| extra:", container.extra)

真实回显:

container: backup | extra: {'creation_time': 1791726488.6805296, 'access_time': 1791726488.6805296, 'modify_time': 1791726488.6805296}

5.2 上传对象(文件 / 流式)

# 文件路径上传(先准备样例文件: echo "hello libcloud storage" > /tmp/upload-sample.txt)
obj = driver.upload_object("/tmp/upload-sample.txt", container, object_name="sample.txt")
print("uploaded:", obj.name, "| size:", obj.size, "| hash:", obj.hash)

# 迭代器流式上传
iterator = iter([b"libcloud local driver line 1\n", b"line 2 of demo\n"])
obj2 = driver.upload_object_via_stream(iterator, container, object_name="stream.txt")
print("uploaded via stream:", obj2.name, "| size:", obj2.size)

真实回显:

uploaded: sample.txt | size: 23 | hash: e815cb84bc28048d3c59a71a39a77670
uploaded via stream: stream.txt | size: 44

5.3 列举存储桶与对象(list_containers / list_objects)

print("containers:", [c.name for c in driver.list_containers()])
print("objects:", [(o.name, o.size) for o in container.list_objects()])

真实回显:

containers: ['backup']
objects: [('sample.txt', 23), ('stream.txt', 44)]

5.4 下载对象(文件 / 流式)

driver.download_object(obj, "/tmp/downloaded-sample.txt", overwrite_existing=True)
print("download ok, content:", open("/tmp/downloaded-sample.txt").read().strip())

stream = driver.download_object_as_stream(container.list_objects()[1])
print("as stream:", [chunk.decode() for chunk in stream])

真实回显:

download ok, content: hello libcloud storage
as stream: ['libcloud local driver line 1\nline 2 of demo\n']

按名称取回对象元数据:

fetched = driver.get_object("backup", "sample.txt")
print("get_object:", fetched.name, fetched.size)

真实回显:

get_object: sample.txt 23

5.5 删除对象与存储桶(delete_object / delete_container)

print("delete_object:", driver.delete_object(obj))
print("delete_object2:", driver.delete_object(obj2))
print("delete_container:", driver.delete_container(container))
print("containers after cleanup:", [c.name for c in driver.list_containers()])

真实回显:

delete_object: True
delete_object2: True
delete_container: True
containers after cleanup: []

💡 说明:Storage 模块统一抽象为「存储桶(Container)→ 对象(Object)」两级;S3/OSS 等对象存储的 bucket 即存储桶,本地驱动落盘为「根目录/存储桶名/对象名」的文件树。

六、DNS API 详解

6.1 创建 Zone 与记录

from libcloud.dns.types import Provider, RecordType
from libcloud.dns.providers import get_driver

driver = get_driver(Provider.DUMMY)("admin", "q1w2e3r4")

zone = driver.create_zone(domain="example.com", type="master", ttl=3600)
print("zone:", zone.domain, zone.type, "ttl:", zone.ttl)

rec_a = zone.create_record(name="www", type=RecordType.A, data="127.0.0.3")
rec_mx = zone.create_record(name="", type=RecordType.MX, data="10 mail.example.com")
print("record A:", rec_a.name, rec_a.type, rec_a.data)
print("record MX:", rec_mx.name, rec_mx.type, rec_mx.data)

真实回显:

zone: example.com master ttl: 3600
record A: www A 127.0.0.3
record MX:  MX 10 mail.example.com

6.2 列举与查询(list_zones / list_records / get_zone / get_record)

print("list_zones:", [(z.domain, z.ttl) for z in driver.list_zones()])
print("list_records:", [(r.name, r.type, r.data) for r in zone.list_records()])

fetched = driver.get_zone(zone.id)
print("get_zone:", fetched.domain)
got_rec = driver.get_record(zone.id, rec_a.id)
print("get_record:", got_rec.name, got_rec.data)

真实回显:

list_zones: [('example.com', 3600)]
list_records: [('www', 'A', '127.0.0.3'), ('', 'MX', '10 mail.example.com')]
get_zone: example.com
get_record: www 127.0.0.3

6.3 删除记录与 Zone

print("delete_record:", rec_a.delete())
print("delete_record MX:", rec_mx.delete())
print("delete_zone:", zone.delete())
print("zones after cleanup:", driver.list_zones())

真实回显:

delete_record: True
delete_record MX: True
delete_zone: True
zones after cleanup: []

💡 说明:记录类型用 libcloud.dns.types.RecordType 枚举(A/AAAA/CNAME/MX/TXT 等);RecordType.MX 的 data 需带优先级前缀(如 10 mail.example.com)。

七、LoadBalancer API 详解

7.1 Provider 与驱动类

LoadBalancer 模块没有离线 Dummy 驱动,Provider 常量全表与 ELB 驱动类实测:

from libcloud.loadbalancer.types import Provider
from libcloud.loadbalancer.providers import get_driver

names = [n for n in dir(Provider) if not n.startswith("_")]
print("LoadBalancer providers:", ", ".join(names))

driver_cls = get_driver(Provider.ELB)
print("ELB driver class:", driver_cls.__name__, "| module:", driver_cls.__module__)

真实回显:

LoadBalancer providers: ALB, ALIYUN_SLB, BRIGHTBOX, CLOUDSTACK, DIMENSIONDATA, ELB, GCE, NINEFOLD, NTTCIS, RACKSPACE, RACKSPACE_UK, RACKSPACE_US
ELB driver class: ElasticLBDriver | module: libcloud.loadbalancer.drivers.elb

7.2 数据模型与算法常量(离线可构造)

LoadBalancer、Member、Algorithm/State 常量可离线构造,用于业务侧建模与单元测试:

from libcloud.loadbalancer.base import Member, LoadBalancer, Algorithm
from libcloud.loadbalancer.types import Provider, State
from libcloud.loadbalancer.providers import get_driver

driver_cls = get_driver(Provider.ELB)
lb = LoadBalancer(id="lb-001", name="demo-lb", state=State.RUNNING,
                  ip="203.0.113.10", port=80,
                  driver=driver_cls("admin", "q1w2e3r4", region="us-east-1"))
m1 = Member(id="m-001", ip="10.0.0.11", port=8080)
m2 = Member(id="m-002", ip="10.0.0.12", port=8080)
lb.members = [m1, m2]
print("lb:", lb.id, lb.name, "| ip:", lb.ip, "| port:", lb.port)
print("members:", [(m.id, m.ip, m.port) for m in lb.members])
print("algorithms:", Algorithm.RANDOM, Algorithm.ROUND_ROBIN, Algorithm.LEAST_CONNECTIONS, Algorithm.WEIGHTED_ROUND_ROBIN)
print("State constants:", State.PENDING, State.RUNNING, State.ERROR)

真实回显:

lb: lb-001 demo-lb | ip: 203.0.113.10 | port: 80
members: [('m-001', '10.0.0.11', 8080), ('m-002', '10.0.0.12', 8080)]
algorithms: 0 1 2 3
State constants: 1 0 3

接入真实厂商的标准流程(以 ELB 为例,需真实凭据):driver = get_driver(Provider.ELB)("ACCESS_KEY", "SECRET_KEY", region="us-east-1") → driver.create_balancer(name, port, protocol, algorithm, members) → balancer.attach_compute_node(node) / attach_member(member) → driver.list_balancers() → balancer.detach_member(member) / driver.destroy_balancer(balancer)。

八、Containers API 详解

8.1 驱动形态与数据模型

Containers 模块的 Dummy 驱动是一个接口占位实现(仅含驱动名称等元信息,动作方法需由真实驱动提供),Provider 常量为 DOCKER、DUMMY、ECS、GKE、KUBERNETES、LXD、RANCHER:

from libcloud.container.types import Provider
from libcloud.container.providers import get_driver
from libcloud.container.base import ContainerImage, Container

driver = get_driver(Provider.DUMMY)("admin", "q1w2e3r4")
print("driver name:", driver.name)
print("driver website:", driver.website)

image = ContainerImage(id="img-001", name="nginx", path="registry.example.com/library/nginx:1.27",
                       driver=driver, version="1.27")
ctn = Container(id="ctn-001", name="demo-web", state="running", image=image,
                driver=driver, ip_addresses=["10.0.0.15"], extra={"env": "demo"})
print("image:", image.id, image.name, image.path, "version:", image.version)
print("container:", ctn.id, ctn.name, ctn.state, "| image:", ctn.image.name, "| extra:", ctn.extra)

真实回显:

driver name: Dummy Container Provider
driver website: http://example.com
image: img-001 nginx registry.example.com/library/nginx:1.27 version: 1.27
container: ctn-001 demo-web running | image: nginx | extra: {'env': 'demo'}

ContainerDriver 基类定义的完整接口(真实驱动需实现):

create_cluster, deploy_container, destroy_cluster, destroy_container, get_cluster, get_container, install_image, list_clusters, list_containers, list_images, list_locations, restart_container, start_container, stop_container

💡 说明:Containers 模块统一抽象为「集群(Cluster)→ 工作负载(Container)」,镜像抽象为 ContainerImage(含 path/version);对接真实环境时把 Provider 换成 DOCKER/KUBERNETES 等并传入对应凭据(如 Kubernetes 的 kubeconfig)。

九、注意事项

  • 五大模块均遵循「Provider 常量 + get_driver」范式,切换厂商只需替换常量与凭据,业务代码基本不动。
  • Dummy/本地驱动仅用于开发演练,结果不代表真实厂商行为;上线前务必用目标厂商驱动回归。
  • 驱动方法并非全部实现:调用前可用 hasattr(driver, "stop_node") 并捕获 NotImplementedError 兜底(见 FAQ 10.1)。
  • 凭据严禁硬编码进代码库;从环境变量或密钥管理系统注入(配置细节见《Apache Libcloud 配置参数详解》)。

十、常见问题(FAQ)

Q1:调用 driver.stop_node(node) 报 NotImplementedError?

真实报错:

NotImplementedError: stop_node not implemented for this driver

定位:Dummy 驱动只实现了 list/reboot/destroy/create 等核心动作,stop_node/start_node 未实现。真实厂商驱动同样可能缺省个别方法,业务侧按能力探测处理:

try:
    driver.stop_node(node)
except NotImplementedError:
    print("该驱动不支持此动作")

Q2:DNS 更新记录报 NotImplementedError: update_record not implemented for this driver?

同上,Dummy DNS 驱动未实现 update_record;真实厂商(Route53/Cloudflare 等)支持记录更新,改用对应驱动即可。

Q3:create_node 传了 name 为什么节点名变成 dummy-N?

Dummy 驱动忽略入参 name,按内置计数器命名(dummy-1、dummy-2、dummy-3…),属于模拟驱动的固定行为,真实驱动会采用传入名称。

Q4:导入本地存储驱动报 Missing fasteners dependency?

安装缺失依赖后重试:

pip3 install fasteners

真实报错全文:

ImportError: Missing fasteners dependency, you can install it using pip: pip install fasteners

Q5:真实厂商驱动报认证/权限错误?

核对三点:凭据是否属于对应 Provider 常量(EC2 用 AWS AccessKey,GCE 用服务账号 JSON 路径等);secure/host/port 是否需要覆盖为私有化接入点;服务器时间偏差是否导致签名失效。排障时可开启 LIBCLOUD_DEBUG 查看实际请求(见《Apache Libcloud 配置参数详解》)。

十一、参考链接