多云演示¶
本文档包含以 presentty 格式呈现的内容。 如果您想像演示文稿一样浏览它,请安装 presentty 并运行
presentty doc/source/user/multi-cloud-demo.rst
即使没有旁白,内容也应该是有帮助的,因此它被包含在 openstacksdk 文档中。
我是谁?¶
蒙蒂·泰勒
OpenStack Infra Core
irc: mordred
twitter: @e_monty
我们要谈论什么?¶
OpenStackSDK
一个任务和用户导向的 Python 库
抽象部署差异
专为多云设计
易于使用
大规模
可选的高级功能,可处理每天 20,000 台服务器
初始逻辑/设计从 nodepool 提取
库化以便在 Ansible 中重用
OpenStackSDK 是自由软件¶
#openstack-sdks on oftc
这个演讲也是自由软件¶
为 presentty (https://pypi.ac.cn/project/presentty) 编写
doc/source/user/multi-cloud-demo.rst
examples 目录中的示例:examples/cloud
路径可能会更改 - 这是树中的第一个演示文稿!
完整示例¶
from openstack import cloud as openstack
# Initialize and turn on debug logging
openstack.enable_logging(debug=True)
for cloud_name, region_name in [
('my-vexxhost', 'ca-ymq-1'),
('my-citycloud', 'Buf1'),
('my-internap', 'ams01'),
]:
# Initialize cloud
cloud = openstack.connect(cloud=cloud_name, region_name=region_name)
# Upload an image to the cloud
image = cloud.create_image(
'devuan-jessie',
filename='devuan-jessie.qcow2',
wait=True,
)
# Find a flavor with at least 512M of RAM
flavor = cloud.get_flavor_by_ram(512)
# Boot a server, wait for it to boot, and then do whatever is needed
# to get a public ip for it.
cloud.create_server(
'my-server',
image=image,
flavor=flavor,
wait=True,
auto_ip=True,
)
让我们退几步¶
多云很容易,但您需要了解一些事情。
术语
Config
OpenStackSDK API
云术语¶
让我们定义一些术语,以便我们可以轻松地使用它们
云 - 逻辑相关的服务集合
区域 - 给定云的完全独立的子集
用户 - 拥有帐户的人
用户 - 云上的帐户
项目 - 云资源的逻辑集合
域 - 用户和项目的集合
云术语关系¶
一个 云 有一个或多个 区域
一个 用户 有一个或多个 用户
一个 用户 有一个或多个 项目
一个 云 有一个或多个 域
在一个 云 中只有一个 域 时,它的名称为“default”
每个 用户 都可以拥有自己的 域
每个 用户 都在一个 域 中
每个 项目 都在一个 域 中
一个 用户 在一个或多个 项目 上拥有一个或多个 角色
HTTP 会话¶
HTTP 交互通过 keystone 进行身份验证
身份验证返回一个 令牌
经过身份验证的 HTTP 会话在 区域 中共享
云区域¶
一个 云区域 是 REST 交互的基本单元。
一个 云 有一个 服务目录
服务目录 在 令牌 中返回
服务目录 列出了每个 区域 中每个 服务 的 端点
一个 区域 是完全自治的
用户、项目和域¶
在具有多个域的云中,项目和用户名仅在区域内唯一。
名称需要 域 信息才能保证唯一性。 ID 不需要。
在不需要时提供 域 信息是可以的。
project_name 需要 project_domain_name 或 project_domain_id
project_id 不需要
username 需要 user_domain_name 或 user_domain_id
user_id 不需要
困惑了吗?¶
不用担心 - 您不必处理大部分内容。
每个云进行身份验证,按区域选择¶
通常,您需要了解的是
为每个 云 配置身份验证
按 云 和 区域 选择要使用的配置
clouds.yaml¶
有关您想要连接的云的信息存储在一个名为 clouds.yaml 的文件中。
clouds.yaml 可以在您的主目录中:~/.config/openstack/clouds.yaml 或系统范围内:/etc/openstack/clouds.yaml。
如果存在,您的主目录中的信息优先。
关于 clouds.yaml 的完整文档位于 https://docs.openstack.org/os-client-config/2025.2/
Mac 和 Windows 呢?¶
USER_CONFIG_DIR 在 Linux、OSX 和 Windows 上不同。
Linux: ~/.config/openstack
OSX: ~/Library/Application Support/openstack
Windows: C:\Users\USERNAME\AppData\Local\OpenStack\openstack
SITE_CONFIG_DIR 在 Linux、OSX 和 Windows 上不同。
Linux: /etc/openstack
OSX: /Library/Application Support/openstack
Windows: C:\ProgramData\OpenStack\openstack
配置术语¶
对于多云,请考虑两种类型
profile - 关于 云 的事实,对每个人都是真实的
云 - 特定于给定 用户 的信息
为两次使用 云 表示歉意。
环境变量和简单用法¶
以 OS_ 开头的环境变量进入一个名为 envvars 的云
如果您只有一个云,则不必指定它
OS_CLOUD 和 OS_REGION_NAME 是 云 和 region_name 的默认值
说得太多了 - 代码不够¶
示例代码的基本 clouds.yaml¶
clouds.yaml 的简单示例
为名为“my-citycloud”的 云 配置
引用一个众所周知的“命名”配置文件:citycloud
os-client-config 在 https://docs.openstack.org/openstacksdk/2025.2/user/config/vendor-support.html 上有一个内置的配置文件列表
供应商配置文件包含各种高级配置
云 名称可以与 profile 名称匹配(为清晰起见使用不同的名称)
clouds:
my-citycloud:
profile: citycloud
auth:
username: mordred
project_id: 65222a4d09ea4c68934fa1028c77f394
user_domain_id: d0919bd5e8d74e49adf0e145807ffc38
project_domain_id: d0919bd5e8d74e49adf0e145807ffc38
密码在哪里?
secure.yaml¶
可选的附加文件,就像 clouds.yaml 一样
覆盖 clouds.yaml 中的值
如果您想更严格地保护机密,这将很有用
示例 secure.yaml¶
不,我的密码不是 XXXXXXXX
云 名称应与 clouds.yaml 匹配
可选 - 我实际上将我的密码保存在我的 clouds.yaml 中
clouds:
my-citycloud:
auth:
password: XXXXXXXX
更多 clouds.yaml¶
可以提供更多信息。
使用 identity API 的 v3 - 即使存在其他 API
使用 https://image-ca-ymq-1.vexxhost.net/v2 作为 image API,而不是目录中的内容
my-vexxhost:
identity_api_version: 3
image_endpoint_override: https://image-ca-ymq-1.vexxhost.net/v2
profile: vexxhost
auth:
user_domain_id: default
project_domain_id: default
project_name: d8af8a8f-a573-48e6-898a-af333b970a2d
username: 0b8c435b-cc4d-4e05-8a47-a2ada0539af1
更复杂的 clouds.yaml 示例¶
不使用配置文件 - 包含所有设置
在 ams01 区域 中,有两个网络具有不可发现的属性
每个网络都标记在这里,以便做出选择
如果需要,任何设置都可以特定于 区域
区域 设置覆盖 云 设置
云 不支持 floating-ips
my-internap:
auth:
auth_url: https://identity.api.cloud.inap.com
username: api-55f9a00fb2619
project_name: inap-17037
identity_api_version: 3
floating_ip_source: None
regions:
- name: ams01
values:
networks:
- name: inap-17037-WAN1654
routes_externally: true
default_interface: true
- name: inap-17037-LAN3631
routes_externally: false
完整示例再次¶
from openstack import cloud as openstack
# Initialize and turn on debug logging
openstack.enable_logging(debug=True)
for cloud_name, region_name in [
('my-vexxhost', 'ca-ymq-1'),
('my-citycloud', 'Buf1'),
('my-internap', 'ams01')]:
# Initialize cloud
cloud = openstack.connect(cloud=cloud_name, region_name=region_name)
# Upload an image to the cloud
image = cloud.create_image(
'devuan-jessie', filename='devuan-jessie.qcow2', wait=True)
# Find a flavor with at least 512M of RAM
flavor = cloud.get_flavor_by_ram(512)
# Boot a server, wait for it to boot, and then do whatever is needed
# to get a public ip for it.
cloud.create_server(
'my-server', image=image, flavor=flavor, wait=True, auto_ip=True)
逐步¶
导入库¶
from openstack import cloud as openstack
日志¶
openstacksdk 使用标准的 python 日志记录
openstack.enable_logging执行简单的默认设置消除一些无意义的警告
debug
以 debug 级别记录 openstacksdk 日志记录器
http_debug 意味着 debug,启用 HTTP 跟踪
# Initialize and turn on debug logging
openstack.enable_logging(debug=True)
带有调试日志记录的示例¶
examples/cloud/debug-logging.py
from openstack import cloud as openstack
openstack.enable_logging(debug=True)
cloud = openstack.connect(cloud='my-vexxhost', region_name='ca-ymq-1')
cloud.get_image('Ubuntu 16.04.1 LTS [2017-03-03]')
带有 HTTP 调试日志记录的示例¶
examples/cloud/http-debug-logging.py
from openstack import cloud as openstack
openstack.enable_logging(http_debug=True)
cloud = openstack.connect(
cloud='my-vexxhost', region_name='ca-ymq-1')
cloud.get_image('Ubuntu 16.04.1 LTS [2017-03-03]')
云区域¶
云 构造函数需要 云 和 region_name
openstack.connect 是一个辅助工厂函数
for cloud_name, region_name in [
('my-vexxhost', 'ca-ymq-1'),
('my-citycloud', 'Buf1'),
('my-internap', 'ams01')
]:
# Initialize cloud
cloud = openstack.connect(cloud=cloud_name, region_name=region_name)
上传镜像¶
选择正确的上传机制
建议 始终上传您自己的基本镜像
# Upload an image to the cloud
image = cloud.create_image(
'devuan-jessie',
filename='devuan-jessie.qcow2',
wait=True,
)
始终上传镜像¶
好的。 您不必这样做。 但是,对于多云…
具有相同内容的镜像在不同的云中命名不同
在不同的云中具有相同名称的镜像可能具有不同的内容
将您自己的镜像上传到所有云,这两个问题都消失了
从 OS 供应商下载或使用 diskimage-builder 构建
查找风味¶
风味在云中都命名不同
可以通过 RAM 找到风味
get_flavor_by_ram 找到最小匹配的风味
# Find a flavor with at least 512M of RAM
flavor = cloud.get_flavor_by_ram(512)
创建服务器¶
my-vexxhost
启动服务器
等待 status==ACTIVE
my-internap
在网络 inap-17037-WAN1654 上启动服务器
等待 status==ACTIVE
my-citycloud
启动服务器
等待 status==ACTIVE
查找 fixed_ip 的 服务器 的 端口
在该 端口 上创建 floating-ip
等待 floating-ip 附加
# Boot a server, wait for it to boot, and then do whatever is needed
# to get a public ip for it.
cloud.create_server(
'my-server', image=image, flavor=flavor, wait=True, auto_ip=True)
哇。 我们甚至没有部署 Wordpress!¶
按名称或 ID 镜像和风味¶
将字符串传递给镜像/风味
镜像/风味将按名称或 ID 找到
常见模式
examples/cloud/create-server-name-or-id.py
from openstack import cloud as openstack
# Initialize and turn on debug logging
openstack.enable_logging(debug=True)
for cloud_name, region_name, image, flavor in [
('my-vexxhost', 'ca-ymq-1',
'Ubuntu 16.04.1 LTS [2017-03-03]', 'v1-standard-4'),
('my-citycloud', 'Buf1',
'Ubuntu 16.04 Xenial Xerus', '4C-4GB-100GB'),
('my-internap', 'ams01',
'Ubuntu 16.04 LTS (Xenial Xerus)', 'A1.4')]:
# Initialize cloud
cloud = openstack.connect(cloud=cloud_name, region_name=region_name)
# Boot a server, wait for it to boot, and then do whatever is needed
# to get a public ip for it.
server = cloud.create_server(
'my-server', image=image, flavor=flavor, wait=True, auto_ip=True)
print(server.name)
print(server['name'])
cloud.pprint(server)
# Delete it - this is a demo
cloud.delete_server(server, wait=True, delete_ips=True)
删除服务器¶
delete_ips 删除服务器可能拥有的任何 floating_ips
cloud.delete_server('my-server', wait=True, delete_ips=True)
按字典镜像和风味¶
将字典传递给镜像/风味
如果您知道该值是名称还是 ID
常见模式
examples/cloud/create-server-dict.py
from openstack import cloud as openstack
# Initialize and turn on debug logging
openstack.enable_logging(debug=True)
for cloud_name, region_name, image, flavor_id in [
('my-vexxhost', 'ca-ymq-1', 'Ubuntu 16.04.1 LTS [2017-03-03]',
'5cf64088-893b-46b5-9bb1-ee020277635d'),
('my-citycloud', 'Buf1', 'Ubuntu 16.04 Xenial Xerus',
'0dab10b5-42a2-438e-be7b-505741a7ffcc'),
('my-internap', 'ams01', 'Ubuntu 16.04 LTS (Xenial Xerus)',
'A1.4')]:
# Initialize cloud
cloud = openstack.connect(cloud=cloud_name, region_name=region_name)
# Boot a server, wait for it to boot, and then do whatever is needed
# to get a public ip for it.
server = cloud.create_server(
'my-server', image=image, flavor=dict(id=flavor_id),
wait=True, auto_ip=True)
# Delete it - this is a demo
cloud.delete_server(server, wait=True, delete_ips=True)
Munch 对象¶
表现得像字典和对象
examples/cloud/munch-dict-object.py
from openstack import cloud as openstack
openstack.enable_logging(debug=True)
cloud = openstack.connect(cloud='zetta', region_name='no-osl1')
image = cloud.get_image('Ubuntu 14.04 (AMD64) [Local Storage]')
print(image.name)
print(image['name'])
按逻辑资源组织 API¶
list_servers
search_servers
get_server
create_server
delete_server
update_server
对于其他内容,仍然是 {verb}_{noun}
attach_volume
wait_for_server
add_auto_ip
清理脚本¶
有时我的示例代码有错误
examples/cloud/cleanup-servers.py
from openstack import cloud as openstack
# Initialize and turn on debug logging
openstack.enable_logging(debug=True)
for cloud_name, region_name in [
('my-vexxhost', 'ca-ymq-1'),
('my-citycloud', 'Buf1'),
('my-internap', 'ams01')]:
# Initialize cloud
cloud = openstack.connect(cloud=cloud_name, region_name=region_name)
for server in cloud.search_servers('my-server'):
cloud.delete_server(server, wait=True, delete_ips=True)
规范化¶
examples/cloud/normalization.py
from openstack import cloud as openstack
openstack.enable_logging()
cloud = openstack.connect(cloud='fuga', region_name='cystack')
image = cloud.get_image(
'Ubuntu 16.04 LTS - Xenial Xerus - 64-bit - Fuga Cloud Based Image')
cloud.pprint(image)
严格规范化的结果¶
仅返回声明的模型
examples/cloud/strict-mode.py
from openstack import cloud as openstack
openstack.enable_logging()
cloud = openstack.connect(
cloud='fuga', region_name='cystack', strict=True)
image = cloud.get_image(
'Ubuntu 16.04 LTS - Xenial Xerus - 64-bit - Fuga Cloud Based Image')
cloud.pprint(image)
我如何找到最后一个示例中的镜像名称?¶
我经常制作一些愚蠢的小实用脚本
examples/cloud/find-an-image.py
from openstack import cloud as openstack
openstack.enable_logging()
cloud = openstack.connect(cloud='fuga', region_name='cystack')
cloud.pprint([
image for image in cloud.list_images()
if 'ubuntu' in image.name.lower()])
添加/修改的信息¶
服务器需要更多额外的帮助
从 neutron 获取地址字典
找出哪些 IP 地址有效
detailed - 默认为 True,添加所有内容
bare - 没有额外的调用 - 甚至不要修复损坏的东西
bare 仍然是规范化的
examples/cloud/server-information.py
from openstack import cloud as openstack
openstack.enable_logging(debug=True)
cloud = openstack.connect(cloud='my-citycloud', region_name='Buf1')
try:
server = cloud.create_server(
'my-server', image='Ubuntu 16.04 Xenial Xerus',
flavor=dict(id='0dab10b5-42a2-438e-be7b-505741a7ffcc'),
wait=True, auto_ip=True)
print("\n\nFull Server\n\n")
cloud.pprint(server)
print("\n\nTurn Detailed Off\n\n")
cloud.pprint(cloud.get_server('my-server', detailed=False))
print("\n\nBare Server\n\n")
cloud.pprint(cloud.get_server('my-server', bare=True))
finally:
# Delete it - this is a demo
cloud.delete_server(server, wait=True, delete_ips=True)
异常¶
所有 openstacksdk 异常都是 OpenStackCloudException 的子类
直接 REST 调用会引发 OpenStackCloudHTTPError
OpenStackCloudHTTPError 继承自 OpenStackCloudException 和 requests.exceptions.HTTPError
OpenStackCloudURINotFound 用于 404
OpenStackCloudBadRequest 用于 400
用户代理信息¶
设置用户代理的 app_name 和 app_version
(ssh … region_name 是可选的,如果云只有一个区域)
examples/cloud/user-agent.py
from openstack import cloud as openstack
openstack.enable_logging(http_debug=True)
cloud = openstack.connect(
cloud='datacentred',
app_name='AmazingApp',
app_version='1.0',
)
cloud.list_networks()
上传大对象¶
swift 具有最大对象大小
大对象以特殊方式上传
openstacksdk 发现这一点并执行
多线程
examples/cloud/upload-object.py
from openstack import cloud as openstack
openstack.enable_logging(debug=True)
cloud = openstack.connect(cloud='ovh', region_name='SBG1')
cloud.create_object(
container='my-container',
name='my-object',
filename='/home/mordred/briarcliff.sh3d',
)
cloud.delete_object('my-container', 'my-object')
cloud.delete_container('my-container')
上传大对象¶
默认 max_file_size 为 5G
这是一个会议演示
让我们强制一个 segment_size
一百万字节
examples/cloud/upload-object.py
from openstack import cloud as openstack
openstack.enable_logging(debug=True)
cloud = openstack.connect(cloud='ovh', region_name='SBG1')
cloud.create_object(
container='my-container',
name='my-object',
filename='/home/mordred/briarcliff.sh3d',
segment_size=1000000,
)
cloud.delete_object('my-container', 'my-object')
cloud.delete_container('my-container')
服务条件¶
from openstack import cloud as openstack
openstack.enable_logging(debug=True)
cloud = openstack.connect(cloud='kiss', region_name='region1')
print(cloud.has_service('network'))
print(cloud.has_service('container-orchestration'))
服务条件覆盖¶
有时云很奇怪,并且弄清楚这一点不起作用
from openstack import cloud as openstack
openstack.enable_logging(debug=True)
cloud = openstack.connect(cloud='rax', region_name='DFW')
print(cloud.has_service('network'))
clouds:
rax:
profile: rackspace
auth:
username: mordred
project_id: 245018
# This is already in profile: rackspace
has_network: false