Rackspace 云指南

介绍

Ansible 包含一些与 Rackspace Cloud 交互的核心模块.

本节的目的是说明在 Rackspace Cloud 的环境下如何使用 Ansible 模块(和使用 inventory scripts).

使用其他模块的先决条件是最少的. 除 Ansible 之外, 所有的模块依赖 pyrax 1.5 或更高版本. 你需要将这个 Python 模块安装在执行主机上.

pyrax 在一些操作系统的包仓库中是不存在的, 所以你可能需要通过 pip 安装:

  1. $ pip install pyrax

下面的步骤将会一直从控制机器通过 Rackspace Cloud API 执行, 所以将 localhost 添加到 inventory 文件中是有意义的. (在未来 Ansible 可能不在依赖这一步):

  1. [localhost]
  2. localhost ansible_connection=local

在 playbook 中, 我们将会使用下面典型的模式:

  1. - hosts: localhost
  2. connection: local
  3. gather_facts: False
  4. tasks:

凭证文件

这个 rax.py inventory 脚本和所有 rax 模块均支持一种标准的 pyrax 凭证文件, 它看起来像这样:

  1. [rackspace_cloud]
  2. username = myraxusername
  3. api_key = d41d8cd98f00b204e9800998ecf8427e

设置环境变量 RAX_CREDS_FILE 到凭证文件的路径, 这将帮助 Ansible 找到它并加载这些信息.

更多关于凭证文件的信息可以参考这里https://github.com/rackspace/pyrax/blob/master/docs/getting_started.md#authenticating

在 Python 的虚拟环境中运行(可选)

大多数用户不喜欢使用虚拟环境, 但是有些用户喜欢, 特别是一些 Python 开发者.

当 Ansible 被安装到 Python 的虚拟环境时, 相比较默认安装到全局环境中, 需要有一些特殊考虑. Ansible 假定, 除非有其他明确的指定, python 二进制可执行文件为 /usr/bin/python. 这是通过模块中解释器一行确定的, 然而可以使用 inventory 变量 ‘ansible_python_interpreter’ 来重新指定, Ansible 将使用指定的路径去寻找 Python. 使用 Python 虚拟环境解析器, 这可能是模块在 ‘localhost’ 运行或者通过 ‘local_action’ 来运行的原因. 通过设置 inventory, 模块将会在虚拟环境中执行并且拥有单独的虚拟包, 特别是 pyrax. 如果使用虚拟环境, 你可能需要修改你本地 inventory 来定义这个虚拟位置, 像下面一样:

  1. [localhost]
  2. localhost ansible_connection=local ansible_python_interpreter=/path/to/ansible_venv/bin/python

配置

现在到了有趣的部分.

这个 ‘rax’ 模块在 Rackspace Cloud 中具有提供 instances 的能力. 典型的配置任务将通过你的 Ansible 控制服务器(在我们的例子中, localhost)请求 Rackspace cloud API. 这是因为这几个原因:

  • 避免 pyrax 库安装在远程节点
  • 无需加密和分发凭证到远程节点
  • 快且简单

下面是一个在 ad-hoc 模式下配置 instance 的简单实例:

  1. $ ansible localhost -m rax -a "name=awx flavor=4 image=ubuntu-1204-lts-precise-pangolin wait=yes" -c local

这些内容转换成 playbook 像下面一样, 假设参数定义在变量中:

  1. tasks:
  2. - name: Provision a set of instances
  3. local_action:
  4. module: rax
  5. name: "{{ rax_name }}"
  6. flavor: "{{ rax_flavor }}"
  7. image: "{{ rax_image }}"
  8. count: "{{ rax_count }}"
  9. group: "{{ group }}"
  10. wait: yes
  11. register: rax

rax 模块返回节点创建 instance 的数据, 像 IP 地址, 主机名, 和登陆密码. 通过注册返回值的步骤, 可以使用它动态添加到主机的 inventory 中(临时在内存中). 这有利于在新建的 instance 上进行配置操作. 在下面的示例中, 将会使用上面成功创建的服务器的信息, 通过每个节点的主机名, IP 地址, 和 root 密码动态添加到一个名为 raxhosts 组中.

  1. - name: Add the instances we created (by public IP) to the group 'raxhosts'
  2. local_action:
  3. module: add_host
  4. hostname: "{{ item.name }}"
  5. ansible_ssh_host: "{{ item.rax_accessipv4 }}"
  6. ansible_ssh_pass: "{{ item.rax_adminpass }}"
  7. groups: raxhosts
  8. with_items: rax.success
  9. when: rax.action == 'create'

现在使用已经创建的主机组, 接下来将会使用下面的 playbook 配置 raxhosts 组中的服务器

  1. - name: Configuration play
  2. hosts: raxhosts
  3. user: root
  4. roles:
  5. - ntp
  6. - webserver

上面的方法将提供的主机配置在一起. 这并不总是你想要了, 那么让我们进入下一章节.

主机 Inventory

一旦你的节点被创建启动, 你很可能会多次和他们进行通讯. 最好的方法是通过 “rax” inventory 插件, 动态查询 Rackspace Cloud 告诉 Ansible 哪些节点需要被管理. 你可能会使用 Ansible 启动的这些 event 来管理其他的工具, 包含 Rackspace 云用户接口. 这个 inventory 插件可以通过元数据, 区域, OS, 配置等来进行分组. 在 “rax” 中高度推荐使用元数据, 它可以很容易的在主机组和 roles 之间排序. 如果你不想使用 “rax.py” 这个动态 inventory 脚本, 你仍然可以选择手动管理你的 INI inventory 文件, 尽管这是不被推荐的.

Ansible 可以使用多个动态 inventory 插件和 INI 数据文件. 仅仅需要将他们放在一个目录下, 并确保脚本添加了执行权限, INI 文件则不需要.

rax.py

使用 rackspace 动态 inventory 脚本, 复制 rax.py 到你的 inventory 目录下并且赋予执行权限. 你可以为 rax.py 指定一个凭证文件利用 RAX_CREDS_FILE 环境变量.

rax.py 也接收 RAX_REGION 环境变量, 其中可以包含单个区域或者用逗号隔开的区域列表.

当使用 rax.py, 你将不需要在 inventory 中定义 ‘localhost’.

正如前面所提到的, 你将经常在主机循环之外运行这些模块, 并且需要定义 ‘localhost’. 这里推荐这样做, 创建一个 inventory 目录, 并且将 rax.py 和包含 localhost 的文件放在这个目录下.

执行 ansibleansible_playbook 并且指定一个包含 inventory 的目录而不是一个文件, ansible 将会读取这个目录下的所有文件.

让我们测试下我们的 inventory 脚本是否可以和 Reckspace Cloud 通信.

  1. $ RAX_CREDS_FILE=~/.raxpub ansible all -i inventory/ -m setup

假设所有的属性配置都是正确的, 这个 rax.py inventory 脚本将会输入类似于下面的信息, 这些将会被用作 inventory 和变量.

  1. {
  2. "ORD": [
  3. "test"
  4. ],
  5. "_meta": {
  6. "hostvars": {
  7. "test": {
  8. "ansible_ssh_host": "1.1.1.1",
  9. "rax_accessipv4": "1.1.1.1",
  10. "rax_accessipv6": "2607:f0d0:1002:51::4",
  11. "rax_addresses": {
  12. "private": [
  13. {
  14. "addr": "2.2.2.2",
  15. "version": 4
  16. }
  17. ],
  18. "public": [
  19. {
  20. "addr": "1.1.1.1",
  21. "version": 4
  22. },
  23. {
  24. "addr": "2607:f0d0:1002:51::4",
  25. "version": 6
  26. }
  27. ]
  28. },
  29. "rax_config_drive": "",
  30. "rax_created": "2013-11-14T20:48:22Z",
  31. "rax_flavor": {
  32. "id": "performance1-1",
  33. "links": [
  34. {
  35. "href": "https://ord.servers.api.rackspacecloud.com/111111/flavors/performance1-1",
  36. "rel": "bookmark"
  37. }
  38. ]
  39. },
  40. "rax_hostid": "e7b6961a9bd943ee82b13816426f1563bfda6846aad84d52af45a4904660cde0",
  41. "rax_human_id": "test",
  42. "rax_id": "099a447b-a644-471f-87b9-a7f580eb0c2a",
  43. "rax_image": {
  44. "id": "b211c7bf-b5b4-4ede-a8de-a4368750c653",
  45. "links": [
  46. {
  47. "href": "https://ord.servers.api.rackspacecloud.com/111111/images/b211c7bf-b5b4-4ede-a8de-a4368750c653",
  48. "rel": "bookmark"
  49. }
  50. ]
  51. },
  52. "rax_key_name": null,
  53. "rax_links": [
  54. {
  55. "href": "https://ord.servers.api.rackspacecloud.com/v2/111111/servers/099a447b-a644-471f-87b9-a7f580eb0c2a",
  56. "rel": "self"
  57. },
  58. {
  59. "href": "https://ord.servers.api.rackspacecloud.com/111111/servers/099a447b-a644-471f-87b9-a7f580eb0c2a",
  60. "rel": "bookmark"
  61. }
  62. ],
  63. "rax_metadata": {
  64. "foo": "bar"
  65. },
  66. "rax_name": "test",
  67. "rax_name_attr": "name",
  68. "rax_networks": {
  69. "private": [
  70. "2.2.2.2"
  71. ],
  72. "public": [
  73. "1.1.1.1",
  74. "2607:f0d0:1002:51::4"
  75. ]
  76. },
  77. "rax_os-dcf_diskconfig": "AUTO",
  78. "rax_os-ext-sts_power_state": 1,
  79. "rax_os-ext-sts_task_state": null,
  80. "rax_os-ext-sts_vm_state": "active",
  81. "rax_progress": 100,
  82. "rax_status": "ACTIVE",
  83. "rax_tenant_id": "111111",
  84. "rax_updated": "2013-11-14T20:49:27Z",
  85. "rax_user_id": "22222"
  86. }
  87. }
  88. }
  89. }

标准的 Inventory

当使用标准的 ini 格式的 inventory文件(相对于 inventory 插件), 它仍然可以从 Rackspace API 检索和发现 hostvar 信息.

这可以使用像下面 inventory 格式来实现类似于 rax_facts 的功能:

  1. [test_servers]
  2. hostname1 rax_region=ORD
  3. hostname2 rax_region=ORD
  1. - name: Gather info about servers
  2. hosts: test_servers
  3. gather_facts: False
  4. tasks:
  5. - name: Get facts about servers
  6. local_action:
  7. module: rax_facts
  8. credentials: ~/.raxpub
  9. name: "{{ inventory_hostname }}"
  10. region: "{{ rax_region }}"
  11. - name: Map some facts
  12. set_fact:
  13. ansible_ssh_host: "{{ rax_accessipv4 }}"

虽然你不需要知道它是如何工作的, 了解返回的变量这也将是有趣的.

这个 rax_facts 模块提供像下面内容的 facts, 这将匹配 rax.py inventory 脚本:

  1. .. code-block:: json
{
“ansible_facts”: {

“rax_accessipv4”: “1.1.1.1”,“rax_accessipv6”: “2607:f0d0:1002:51::4”,“rax_addresses”: {

“private”: [
{
“addr”: “2.2.2.2”,“version”: 4

}

],“public”: [

{
“addr”: “1.1.1.1”,“version”: 4

},{

“addr”: “2607:f0d0:1002:51::4”,“version”: 6

}

]

},“rax_config_drive”: “”,“rax_created”: “2013-11-14T20:48:22Z”,“rax_flavor”: {

“id”: “performance1-1”,“links”: [

]

},“rax_hostid”: “e7b6961a9bd943ee82b13816426f1563bfda6846aad84d52af45a4904660cde0”,“rax_human_id”: “test”,“rax_id”: “099a447b-a644-471f-87b9-a7f580eb0c2a”,“rax_image”: {

“id”: “b211c7bf-b5b4-4ede-a8de-a4368750c653”,“links”: [

]

},“rax_key_name”: null,“rax_links”: [

],“rax_metadata”: {

“foo”: “bar”

},“rax_name”: “test”,“rax_name_attr”: “name”,“rax_networks”: {

“private”: [
“2.2.2.2”

],“public”: [

“1.1.1.1”,“2607:f0d0:1002:51::4”

]

},“rax_os-dcf_diskconfig”: “AUTO”,“rax_os-ext-sts_power_state”: 1,“rax_os-ext-sts_task_state”: null,“rax_os-ext-sts_vm_state”: “active”,“rax_progress”: 100,“rax_status”: “ACTIVE”,“rax_tenant_id”: “111111”,“rax_updated”: “2013-11-14T20:49:27Z”,“rax_user_id”: “22222”

},“changed”: false

}

使用案例

本节涵盖了一些特定案例外及额外的使用案例.

网络和服务器

创建一个独立的云网络并且创建一台服务器

  1. - name: Build Servers on an Isolated Network
  2. hosts: localhost
  3. connection: local
  4. gather_facts: False
  5. tasks:
  6. - name: Network create request
  7. local_action:
  8. module: rax_network
  9. credentials: ~/.raxpub
  10. label: my-net
  11. cidr: 192.168.3.0/24
  12. region: IAD
  13. state: present
  14.  
  15. - name: Server create request
  16. local_action:
  17. module: rax
  18. credentials: ~/.raxpub
  19. name: web%04d.example.org
  20. flavor: 2
  21. image: ubuntu-1204-lts-precise-pangolin
  22. disk_config: manual
  23. networks:
  24. - public
  25. - my-net
  26. region: IAD
  27. state: present
  28. count: 5
  29. exact_count: yes
  30. group: web
  31. wait: yes
  32. wait_timeout: 360
  33. register: rax

完整的环境

使用服务器建立一个完整的 web 服务环境, 自定义网络和负载均衡, 安装 nginx 并且创建自定义的 index.html

  1. ---
  2. - name: Build environment
  3. hosts: localhost
  4. connection: local
  5. gather_facts: False
  6. tasks:
  7. - name: Load Balancer create request
  8. local_action:
  9. module: rax_clb
  10. credentials: ~/.raxpub
  11. name: my-lb
  12. port: 80
  13. protocol: HTTP
  14. algorithm: ROUND_ROBIN
  15. type: PUBLIC
  16. timeout: 30
  17. region: IAD
  18. wait: yes
  19. state: present
  20. meta:
  21. app: my-cool-app
  22. register: clb
  23.  
  24. - name: Network create request
  25. local_action:
  26. module: rax_network
  27. credentials: ~/.raxpub
  28. label: my-net
  29. cidr: 192.168.3.0/24
  30. state: present
  31. region: IAD
  32. register: network
  33.  
  34. - name: Server create request
  35. local_action:
  36. module: rax
  37. credentials: ~/.raxpub
  38. name: web%04d.example.org
  39. flavor: performance1-1
  40. image: ubuntu-1204-lts-precise-pangolin
  41. disk_config: manual
  42. networks:
  43. - public
  44. - private
  45. - my-net
  46. region: IAD
  47. state: present
  48. count: 5
  49. exact_count: yes
  50. group: web
  51. wait: yes
  52. register: rax
  53.  
  54. - name: Add servers to web host group
  55. local_action:
  56. module: add_host
  57. hostname: "{{ item.name }}"
  58. ansible_ssh_host: "{{ item.rax_accessipv4 }}"
  59. ansible_ssh_pass: "{{ item.rax_adminpass }}"
  60. ansible_ssh_user: root
  61. groups: web
  62. with_items: rax.success
  63. when: rax.action == 'create'
  64.  
  65. - name: Add servers to Load balancer
  66. local_action:
  67. module: rax_clb_nodes
  68. credentials: ~/.raxpub
  69. load_balancer_id: "{{ clb.balancer.id }}"
  70. address: "{{ item.rax_networks.private|first }}"
  71. port: 80
  72. condition: enabled
  73. type: primary
  74. wait: yes
  75. region: IAD
  76. with_items: rax.success
  77. when: rax.action == 'create'
  78.  
  79. - name: Configure servers
  80. hosts: web
  81. handlers:
  82. - name: restart nginx
  83. service: name=nginx state=restarted
  84.  
  85. tasks:
  86. - name: Install nginx
  87. apt: pkg=nginx state=latest update_cache=yes cache_valid_time=86400
  88. notify:
  89. - restart nginx
  90.  
  91. - name: Ensure nginx starts on boot
  92. service: name=nginx state=started enabled=yes
  93.  
  94. - name: Create custom index.html
  95. copy: content="{{ inventory_hostname }}" dest=/usr/share/nginx/www/index.html
  96. owner=root group=root mode=0644

RackConnect 和 Managed Cloud

当使用 RackConnect version 2 或者 Rackspace Managed Cloud, Rackspace 将在成功创建的服务器上自动执行这些任务. 如果你在 RackConnect 或 Managed Cloud 自动执行之前执行了, 你可能会获得错误或者不可用的服务器.

这些例子展示了创建服务器并且确保 Rackspace 自动执行完成之前将会继续执行这些任务.

为了简单, 这些例子将会被连接起来, 但是都只需要使用 RackConnect. 当仅使用 Managed Cloud, RackConnect 将会忽略这部分.

RackConnect 部分只适用于 RackConnect 版本 2.

使用一台控制服务器

  1. - name: Create an exact count of servers
  2. hosts: localhost
  3. connection: local
  4. gather_facts: False
  5. tasks:
  6. - name: Server build requests
  7. local_action:
  8. module: rax
  9. credentials: ~/.raxpub
  10. name: web%03d.example.org
  11. flavor: performance1-1
  12. image: ubuntu-1204-lts-precise-pangolin
  13. disk_config: manual
  14. region: DFW
  15. state: present
  16. count: 1
  17. exact_count: yes
  18. group: web
  19. wait: yes
  20. register: rax
  21.  
  22. - name: Add servers to in memory groups
  23. local_action:
  24. module: add_host
  25. hostname: "{{ item.name }}"
  26. ansible_ssh_host: "{{ item.rax_accessipv4 }}"
  27. ansible_ssh_pass: "{{ item.rax_adminpass }}"
  28. ansible_ssh_user: root
  29. rax_id: "{{ item.rax_id }}"
  30. groups: web,new_web
  31. with_items: rax.success
  32. when: rax.action == 'create'
  33.  
  34. - name: Wait for rackconnect and managed cloud automation to complete
  35. hosts: new_web
  36. gather_facts: false
  37. tasks:
  38. - name: Wait for rackconnnect automation to complete
  39. local_action:
  40. module: rax_facts
  41. credentials: ~/.raxpub
  42. id: "{{ rax_id }}"
  43. region: DFW
  44. register: rax_facts
  45. until: rax_facts.ansible_facts['rax_metadata']['rackconnect_automation_status']|default('') == 'DEPLOYED'
  46. retries: 30
  47. delay: 10
  48.  
  49. - name: Wait for managed cloud automation to complete
  50. local_action:
  51. module: rax_facts
  52. credentials: ~/.raxpub
  53. id: "{{ rax_id }}"
  54. region: DFW
  55. register: rax_facts
  56. until: rax_facts.ansible_facts['rax_metadata']['rax_service_level_automation']|default('') == 'Complete'
  57. retries: 30
  58. delay: 10
  59.  
  60. - name: Base Configure Servers
  61. hosts: web
  62. roles:
  63. - role: users
  64.  
  65. - role: openssh
  66. opensshd_PermitRootLogin: "no"
  67.  
  68. - role: ntp

利用 Ansible Pull

  1. ---
  2. - name: Ensure Rackconnect and Managed Cloud Automation is complete
  3. hosts: all
  4. connection: local
  5. tasks:
  6. - name: Check for completed bootstrap
  7. stat:
  8. path: /etc/bootstrap_complete
  9. register: bootstrap
  10.  
  11. - name: Get region
  12. command: xenstore-read vm-data/provider_data/region
  13. register: rax_region
  14. when: bootstrap.stat.exists != True
  15.  
  16. - name: Wait for rackconnect automation to complete
  17. uri:
  18. url: "https://{{ rax_region.stdout|trim }}.api.rackconnect.rackspace.com/v1/automation_status?format=json"
  19. return_content: yes
  20. register: automation_status
  21. when: bootstrap.stat.exists != True
  22. until: automation_status['automation_status']|default('') == 'DEPLOYED'
  23. retries: 30
  24. delay: 10
  25.  
  26. - name: Wait for managed cloud automation to complete
  27. wait_for:
  28. path: /tmp/rs_managed_cloud_automation_complete
  29. delay: 10
  30. when: bootstrap.stat.exists != True
  31.  
  32. - name: Set bootstrap completed
  33. file:
  34. path: /etc/bootstrap_complete
  35. state: touch
  36. owner: root
  37. group: root
  38. mode: 0400
  39.  
  40. - name: Base Configure Servers
  41. hosts: all
  42. connection: local
  43. roles:
  44. - role: users
  45.  
  46. - role: openssh
  47. opensshd_PermitRootLogin: "no"
  48.  
  49. - role: ntp

利用 Ansible 拉取 XenStore

  1. ---
  2. - name: Ensure Rackconnect and Managed Cloud Automation is complete
  3. hosts: all
  4. connection: local
  5. tasks:
  6. - name: Check for completed bootstrap
  7. stat:
  8. path: /etc/bootstrap_complete
  9. register: bootstrap
  10.  
  11. - name: Wait for rackconnect_automation_status xenstore key to exist
  12. command: xenstore-exists vm-data/user-metadata/rackconnect_automation_status
  13. register: rcas_exists
  14. when: bootstrap.stat.exists != True
  15. failed_when: rcas_exists.rc|int > 1
  16. until: rcas_exists.rc|int == 0
  17. retries: 30
  18. delay: 10
  19.  
  20. - name: Wait for rackconnect automation to complete
  21. command: xenstore-read vm-data/user-metadata/rackconnect_automation_status
  22. register: rcas
  23. when: bootstrap.stat.exists != True
  24. until: rcas.stdout|replace('"', '') == 'DEPLOYED'
  25. retries: 30
  26. delay: 10
  27.  
  28. - name: Wait for rax_service_level_automation xenstore key to exist
  29. command: xenstore-exists vm-data/user-metadata/rax_service_level_automation
  30. register: rsla_exists
  31. when: bootstrap.stat.exists != True
  32. failed_when: rsla_exists.rc|int > 1
  33. until: rsla_exists.rc|int == 0
  34. retries: 30
  35. delay: 10
  36.  
  37. - name: Wait for managed cloud automation to complete
  38. command: xenstore-read vm-data/user-metadata/rackconnect_automation_status
  39. register: rsla
  40. when: bootstrap.stat.exists != True
  41. until: rsla.stdout|replace('"', '') == 'DEPLOYED'
  42. retries: 30
  43. delay: 10
  44.  
  45. - name: Set bootstrap completed
  46. file:
  47. path: /etc/bootstrap_complete
  48. state: touch
  49. owner: root
  50. group: root
  51. mode: 0400
  52.  
  53. - name: Base Configure Servers
  54. hosts: all
  55. connection: local
  56. roles:
  57. - role: users
  58.  
  59. - role: openssh
  60. opensshd_PermitRootLogin: "no"
  61.  
  62. - role: ntp

高级用法

Tower 中的自动伸缩

Ansible Tower 中包含一个非常好的功能 自动伸缩. 在这种模式下, 一个简单的 curl 脚本可以调用定义的 URL, 通过这个请求, 服务器将会被 “dial out” 或者配置一个新的服务器并启动. 这对于临时节点的控制是非常伟大的. 查看 Tower 文档获取更多细节.

在 Tower 上使用回调的方式覆盖 Pull 模式的好处在于, 任务的结果被集中的存放, 避免了主机之间信息共享

Rackspace Cloud 中的流程

Ansible 是一个强大的编排工具, 搭配 rax 模块使你有机会完成复杂任务的部署和配置. 这里的关键是自动配置的基础设施, 就像一个环境中任何的服务软件. 复杂的部署以前可能需要手动配置负载均衡器或手动配置服务器. 利用 Ansible 和 rax 模块, 可以使其他节点参照当前运行的一些节点来部署, 或者一个集群的应用程序依赖于具有公共元数据的节点数量. 例如, 人们可以完成下列情况:

  • 将服务器从云负载均衡器中一个一个的删除, 更新, 验证并且返回一个负载均衡池
  • 对一个已存在的线上环境进行扩展, 哪些节点需要提供软件, 引导, 配置和安装
  • 在节点下线之前将应用程序的日志上传至中心存储, 像云存储
  • 关于服务器在负载均衡器中的 DNS 记录的创建和销毁