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#authenticating

在 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 的檔案放在這個目錄下.

執行 ansibleansible_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”: [

]

}, “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

}

使用案例

本節涵蓋了一些特定案例外及額外的使用案例.

網路和伺服器

建立一個獨立的雲網絡並且建立一臺伺服器

- 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 記錄的建立和銷燬