Rackspace 云指南
介绍
Ansible 包含一些与 Rackspace Cloud 交互的核心模块.
本节的目的是说明在 Rackspace Cloud 的环境下如何使用 Ansible 模块(和使用 inventory scripts).
使用其他模块的先决条件是最少的. 除 Ansible 之外, 所有的模块依赖 pyrax 1.5 或更高版本. 你需要将这个 Python 模块安装在执行主机上.
pyrax 在一些操作系统的包仓库中是不存在的, 所以你可能需要通过 pip 安装:
$ pip install pyrax
下面的步骤将会一直从控制机器通过 Rackspace Cloud API 执行, 所以将 localhost 添加到 inventory 文件中是有意义的. (在未来 Ansible 可能不在依赖这一步):
[localhost] localhost ansible_connection=local
在 playbook 中, 我们将会使用下面典型的模式:
- hosts: localhost connection: local gather_facts: False tasks:
凭证文件
这个 rax.py inventory 脚本和所有 rax 模块均支持一种标准的 pyrax 凭证文件, 它看起来像这样:
[rackspace_cloud] username = myraxusername api_key = d41d8cd98f00b204e9800998ecf8427e
设置环境变量 RAX_CREDS_FILE 到凭证文件的路径, 这将帮助 Ansible 找到它并加载这些信息.
更多关于凭证文件的信息可以参考这里 https://github.com/rackspace/pyrax/blob/master/docs/getting_started.md
在 Python 的虚拟环境中运行(可选)
大多数用户不喜欢使用虚拟环境, 但是有些用户喜欢, 特别是一些 Python 开发者.
当 Ansible 被安装到 Python 的虚拟环境时, 相比较默认安装到全局环境中, 需要有一些特殊考虑. Ansible 假定, 除非有其他明确的指定, python 二进制可执行文件为 /usr/bin/python. 这是通过模块中解释器一行确定的, 然而可以使用 inventory 变量 ‘ansible_python_interpreter’ 来重新指定, Ansible 将使用指定的路径去寻找 Python. 使用 Python 虚拟环境解析器, 这可能是模块在 ‘localhost’ 运行或者通过 ‘local_action’ 来运行的原因. 通过设置 inventory, 模块将会在虚拟环境中执行并且拥有单独的虚拟包, 特别是 pyrax. 如果使用虚拟环境, 你可能需要修改你本地 inventory 来定义这个虚拟位置, 像下面一样:
[localhost] 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 的简单实例:
$ ansible localhost -m rax -a "name=awx flavor=4 image=ubuntu-1204-lts-precise-pangolin wait=yes" -c local
这些内容转换成 playbook 像下面一样, 假设参数定义在变量中:
tasks: - name: Provision a set of instances local_action: module: rax name: "{{ rax_name }}" flavor: "{{ rax_flavor }}" image: "{{ rax_image }}" count: "{{ rax_count }}" group: "{{ group }}" wait: yes register: rax
rax 模块返回节点创建 instance 的数据, 像 IP 地址, 主机名, 和登陆密码. 通过注册返回值的步骤, 可以使用它动态添加到主机的 inventory 中(临时在内存中). 这有利于在新建的 instance 上进行配置操作. 在下面的示例中, 将会使用上面成功创建的服务器的信息, 通过每个节点的主机名, IP 地址, 和 root 密码动态添加到一个名为 raxhosts 组中.
- name: Add the instances we created (by public IP) to the group 'raxhosts' local_action: module: add_host hostname: "{{ item.name }}" ansible_ssh_host: "{{ item.rax_accessipv4 }}" ansible_ssh_pass: "{{ item.rax_adminpass }}" groups: raxhosts with_items: rax.success when: rax.action == 'create'
现在使用已经创建的主机组, 接下来将会使用下面的 playbook 配置 raxhosts 组中的服务器
- name: Configuration play hosts: raxhosts user: root roles: - ntp - 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
的文件放在这个目录下.
执行 ansible
或 ansible_playbook
并且指定一个包含 inventory
的目录而不是一个文件, ansible 将会读取这个目录下的所有文件.
让我们测试下我们的 inventory 脚本是否可以和 Reckspace Cloud 通信.
$ RAX_CREDS_FILE=~/.raxpub ansible all -i inventory/ -m setup
假设所有的属性配置都是正确的, 这个 rax.py
inventory 脚本将会输入类似于下面的信息, 这些将会被用作 inventory 和变量.
{ "ORD": [ "test" ], "_meta": { "hostvars": { "test": { "ansible_ssh_host": "1.1.1.1", "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": [ { "href": "https://ord.servers.api.rackspacecloud.com/111111/flavors/performance1-1", "rel": "bookmark" } ] }, "rax_hostid": "e7b6961a9bd943ee82b13816426f1563bfda6846aad84d52af45a4904660cde0", "rax_human_id": "test", "rax_id": "099a447b-a644-471f-87b9-a7f580eb0c2a", "rax_image": { "id": "b211c7bf-b5b4-4ede-a8de-a4368750c653", "links": [ { "href": "https://ord.servers.api.rackspacecloud.com/111111/images/b211c7bf-b5b4-4ede-a8de-a4368750c653", "rel": "bookmark" } ] }, "rax_key_name": null, "rax_links": [ { "href": "https://ord.servers.api.rackspacecloud.com/v2/111111/servers/099a447b-a644-471f-87b9-a7f580eb0c2a", "rel": "self" }, { "href": "https://ord.servers.api.rackspacecloud.com/111111/servers/099a447b-a644-471f-87b9-a7f580eb0c2a", "rel": "bookmark" } ], "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" } } } }
标准的 Inventory
当使用标准的 ini 格式的 inventory文件(相对于 inventory 插件), 它仍然可以从 Rackspace API 检索和发现 hostvar 信息.
这可以使用像下面 inventory 格式来实现类似于 rax_facts
的功能:
[test_servers] hostname1 rax_region=ORD hostname2 rax_region=ORD
- name: Gather info about servers hosts: test_servers gather_facts: False tasks: - name: Get facts about servers local_action: module: rax_facts credentials: ~/.raxpub name: "{{ inventory_hostname }}" region: "{{ rax_region }}" - name: Map some facts set_fact: ansible_ssh_host: "{{ rax_accessipv4 }}"
虽然你不需要知道它是如何工作的, 了解返回的变量这也将是有趣的.
这个 rax_facts
模块提供像下面内容的 facts, 这将匹配 rax.py
inventory 脚本:
.. 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”: [
- {
- “href”: “https://ord.servers.api.rackspacecloud.com/111111/flavors/performance1-1”, “rel”: “bookmark”
}
]
}, “rax_hostid”: “e7b6961a9bd943ee82b13816426f1563bfda6846aad84d52af45a4904660cde0”, “rax_human_id”: “test”, “rax_id”: “099a447b-a644-471f-87b9-a7f580eb0c2a”, “rax_image”: {
“id”: “b211c7bf-b5b4-4ede-a8de-a4368750c653”, “links”: [
- {
- “href”: “https://ord.servers.api.rackspacecloud.com/111111/images/b211c7bf-b5b4-4ede-a8de-a4368750c653”, “rel”: “bookmark”
}
]
}, “rax_key_name”: null, “rax_links”: [
- {
- “href”: “https://ord.servers.api.rackspacecloud.com/v2/111111/servers/099a447b-a644-471f-87b9-a7f580eb0c2a”, “rel”: “self”
}, {
“href”: “https://ord.servers.api.rackspacecloud.com/111111/servers/099a447b-a644-471f-87b9-a7f580eb0c2a”, “rel”: “bookmark”}
], “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
}
使用案例¶
本节涵盖了一些特定案例外及额外的使用案例.
网络和服务器
创建一个独立的云网络并且创建一台服务器
- name: Build Servers on an Isolated Network hosts: localhost connection: local gather_facts: False tasks: - name: Network create request local_action: module: rax_network credentials: ~/.raxpub label: my-net cidr: 192.168.3.0/24 region: IAD state: present - name: Server create request local_action: module: rax credentials: ~/.raxpub name: web%04d.example.org flavor: 2 image: ubuntu-1204-lts-precise-pangolin disk_config: manual networks: - public - my-net region: IAD state: present count: 5 exact_count: yes group: web wait: yes wait_timeout: 360 register: rax
完整的环境
使用服务器建立一个完整的 web 服务环境, 自定义网络和负载均衡, 安装 nginx 并且创建自定义的 index.html
--- - name: Build environment hosts: localhost connection: local gather_facts: False tasks: - name: Load Balancer create request local_action: module: rax_clb credentials: ~/.raxpub name: my-lb port: 80 protocol: HTTP algorithm: ROUND_ROBIN type: PUBLIC timeout: 30 region: IAD wait: yes state: present meta: app: my-cool-app register: clb - name: Network create request local_action: module: rax_network credentials: ~/.raxpub label: my-net cidr: 192.168.3.0/24 state: present region: IAD register: network - name: Server create request local_action: module: rax credentials: ~/.raxpub name: web%04d.example.org flavor: performance1-1 image: ubuntu-1204-lts-precise-pangolin disk_config: manual networks: - public - private - my-net region: IAD state: present count: 5 exact_count: yes group: web wait: yes register: rax - name: Add servers to web host group local_action: module: add_host hostname: "{{ item.name }}" ansible_ssh_host: "{{ item.rax_accessipv4 }}" ansible_ssh_pass: "{{ item.rax_adminpass }}" ansible_ssh_user: root groups: web with_items: rax.success when: rax.action == 'create' - name: Add servers to Load balancer local_action: module: rax_clb_nodes credentials: ~/.raxpub load_balancer_id: "{{ clb.balancer.id }}" address: "{{ item.rax_networks.private|first }}" port: 80 condition: enabled type: primary wait: yes region: IAD with_items: rax.success when: rax.action == 'create' - name: Configure servers hosts: web handlers: - name: restart nginx service: name=nginx state=restarted tasks: - name: Install nginx apt: pkg=nginx state=latest update_cache=yes cache_valid_time=86400 notify: - restart nginx - name: Ensure nginx starts on boot service: name=nginx state=started enabled=yes - name: Create custom index.html copy: content="{{ inventory_hostname }}" dest=/usr/share/nginx/www/index.html 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.
使用一台控制服务器
- name: Create an exact count of servers hosts: localhost connection: local gather_facts: False tasks: - name: Server build requests local_action: module: rax credentials: ~/.raxpub name: web%03d.example.org flavor: performance1-1 image: ubuntu-1204-lts-precise-pangolin disk_config: manual region: DFW state: present count: 1 exact_count: yes group: web wait: yes register: rax - name: Add servers to in memory groups local_action: module: add_host hostname: "{{ item.name }}" ansible_ssh_host: "{{ item.rax_accessipv4 }}" ansible_ssh_pass: "{{ item.rax_adminpass }}" ansible_ssh_user: root rax_id: "{{ item.rax_id }}" groups: web,new_web with_items: rax.success when: rax.action == 'create' - name: Wait for rackconnect and managed cloud automation to complete hosts: new_web gather_facts: false tasks: - name: Wait for rackconnnect automation to complete local_action: module: rax_facts credentials: ~/.raxpub id: "{{ rax_id }}" region: DFW register: rax_facts until: rax_facts.ansible_facts['rax_metadata']['rackconnect_automation_status']|default('') == 'DEPLOYED' retries: 30 delay: 10 - name: Wait for managed cloud automation to complete local_action: module: rax_facts credentials: ~/.raxpub id: "{{ rax_id }}" region: DFW register: rax_facts until: rax_facts.ansible_facts['rax_metadata']['rax_service_level_automation']|default('') == 'Complete' retries: 30 delay: 10 - name: Base Configure Servers hosts: web roles: - role: users - role: openssh opensshd_PermitRootLogin: "no" - role: ntp
利用 Ansible Pull
--- - name: Ensure Rackconnect and Managed Cloud Automation is complete hosts: all connection: local tasks: - name: Check for completed bootstrap stat: path: /etc/bootstrap_complete register: bootstrap - name: Get region command: xenstore-read vm-data/provider_data/region register: rax_region when: bootstrap.stat.exists != True - name: Wait for rackconnect automation to complete uri: url: "https://{{ rax_region.stdout|trim }}.api.rackconnect.rackspace.com/v1/automation_status?format=json" return_content: yes register: automation_status when: bootstrap.stat.exists != True until: automation_status['automation_status']|default('') == 'DEPLOYED' retries: 30 delay: 10 - name: Wait for managed cloud automation to complete wait_for: path: /tmp/rs_managed_cloud_automation_complete delay: 10 when: bootstrap.stat.exists != True - name: Set bootstrap completed file: path: /etc/bootstrap_complete state: touch owner: root group: root mode: 0400 - name: Base Configure Servers hosts: all connection: local roles: - role: users - role: openssh opensshd_PermitRootLogin: "no" - role: ntp
利用 Ansible 拉取 XenStore
--- - name: Ensure Rackconnect and Managed Cloud Automation is complete hosts: all connection: local tasks: - name: Check for completed bootstrap stat: path: /etc/bootstrap_complete register: bootstrap - name: Wait for rackconnect_automation_status xenstore key to exist command: xenstore-exists vm-data/user-metadata/rackconnect_automation_status register: rcas_exists when: bootstrap.stat.exists != True failed_when: rcas_exists.rc|int > 1 until: rcas_exists.rc|int == 0 retries: 30 delay: 10 - name: Wait for rackconnect automation to complete command: xenstore-read vm-data/user-metadata/rackconnect_automation_status register: rcas when: bootstrap.stat.exists != True until: rcas.stdout|replace('"', '') == 'DEPLOYED' retries: 30 delay: 10 - name: Wait for rax_service_level_automation xenstore key to exist command: xenstore-exists vm-data/user-metadata/rax_service_level_automation register: rsla_exists when: bootstrap.stat.exists != True failed_when: rsla_exists.rc|int > 1 until: rsla_exists.rc|int == 0 retries: 30 delay: 10 - name: Wait for managed cloud automation to complete command: xenstore-read vm-data/user-metadata/rackconnect_automation_status register: rsla when: bootstrap.stat.exists != True until: rsla.stdout|replace('"', '') == 'DEPLOYED' retries: 30 delay: 10 - name: Set bootstrap completed file: path: /etc/bootstrap_complete state: touch owner: root group: root mode: 0400 - name: Base Configure Servers hosts: all connection: local roles: - role: users - role: openssh opensshd_PermitRootLogin: "no" - role: ntp
高级用法
Tower 中的自动伸缩
Ansible Tower 中包含一个非常好的功能 自动伸缩. 在这种模式下, 一个简单的 curl 脚本可以调用定义的 URL, 通过这个请求, 服务器将会被 “dial out” 或者配置一个新的服务器并启动. 这对于临时节点的控制是非常伟大的. 查看 Tower 文档获取更多细节.
在 Tower 上使用回调的方式覆盖 Pull 模式的好处在于, 任务的结果被集中的存放, 避免了主机之间信息共享
Rackspace Cloud 中的流程
Ansible 是一个强大的编排工具, 搭配 rax 模块使你有机会完成复杂任务的部署和配置. 这里的关键是自动配置的基础设施, 就像一个环境中任何的服务软件. 复杂的部署以前可能需要手动配置负载均衡器或手动配置服务器. 利用 Ansible 和 rax 模块, 可以使其他节点参照当前运行的一些节点来部署, 或者一个集群的应用程序依赖于具有公共元数据的节点数量. 例如, 人们可以完成下列情况:
- 将服务器从云负载均衡器中一个一个的删除, 更新, 验证并且返回一个负载均衡池
- 对一个已存在的线上环境进行扩展, 哪些节点需要提供软件, 引导, 配置和安装
- 在节点下线之前将应用程序的日志上传至中心存储, 像云存储
- 关于服务器在负载均衡器中的 DNS 记录的创建和销毁