多云演示

本文档包含以 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 是自由软件

这个演讲也是自由软件

  • 为 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_nameproject_domain_id

  • project_id 不需要

  • username 需要 user_domain_nameuser_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_CLOUDOS_REGION_NAMEregion_name 的默认值

说得太多了 - 代码不够

示例代码的基本 clouds.yaml

clouds.yaml 的简单示例

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 继承自 OpenStackCloudExceptionrequests.exceptions.HTTPError

  • OpenStackCloudURINotFound 用于 404

  • OpenStackCloudBadRequest 用于 400

用户代理信息

  • 设置用户代理的 app_nameapp_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

结束