Variables

已經存在的自動化技術使得重複做事變得更加容易,但你的所有系統有時則不會這樣. 在有些系統中你想設定一些行為或者配置,這與其它系統稍有不同.

並且,遠端系統的可視行為或狀態會影響我們配置這些系統.(比如你需要得到一個系統的IP地址,甚至用該值來配置另一個系統).

你可能有一些非常相似的模板或配置檔案,而有些變數則稍微不同. Ansible中的變數用來處理系統間的不同.

為了理解變數,你也需要深入閱讀 條件選擇迴圈.有用的模組(比如”group_by”模組和”when”條件)也可以結合變數使用,用於管理系統間的不同之處.

強烈建議你學習 ansible-examples github程式碼庫,裡面有大量使用變數的例子.

合法的變數名

在使用變數之前最好先知道什麼是合法的變數名. 變數名可以為字母,數字以及下劃線.變數始終應該以字母開頭. “foo_port”是個合法的變數名.”foo5”也是. “foo-port”, “foo port”, “foo.port” 和 “12”則不是合法的變數名.

很簡單吧,繼續往下看.

在Inventory中定義變數

我們已經在其它文件中覆蓋了大量關於使用變數的場景,所以這裡沒多少新的知識點,權當加深記憶.

通常你想基於一個機器位於哪個群組而設定變數.比如,位於波士頓的很多機器會使用 ‘boston.ntp.example.com’ 作為NTP伺服器.

請看 Inventory檔案 文件來學習在inventory中使用多種方式來定義變數.

在playbook中定義變數

在playbook中,可以直接定義變數,如下所示:

- hosts: webservers
  vars:
    http_port: 80

這種所見即所得的方式非常好.

在檔案和role中定義變數

事實上在其它地方我們也講過這點了. 正如在 Playbook Roles and Include Statements 描述的一樣,變數也可以通過檔案包含在playbook中,該變數可以作為或者不作為“Ansible Role”的一部分.使用role是首選,因為它提供了一個很好的組織體系.

使用變數: 關於Jinja2

我們已經知道很多關於定義變數的知識,那麼你知道如何使用它們嗎?

Ansible允許你使用Jinja2模板系統在playbook中引用變數.藉助Jinja你能做很多複雜的操作,首先你要學習基本使用. 例如,在簡單的模板中你可以這樣做:

My amp goes to {{ max_amp_value }}

這就是變數替換最基本的形式. 你也可以在playbook中直接這樣用,你偶爾想這樣做:

template: src=foo.cfg.j2 dest={{ remote_install_path }}/foo.cfg

In the above example, we used a variable to help decide where to place a file. 在上述的例子中,我們使用變數來決定檔案放置在哪裡. 在模板中你自動會獲取在主機範圍之內的所有變數的訪問權.事實上更多,你可以讀取其它主機的變數.我們將演示如何做.

Note

在模板中Jinja2可以用迴圈和條件語句,而在playbook中則不行.Ansible playbook是純粹的機器解析的YAML.這是一個非常重要的功能,這意味著根據檔案可以生成程式碼,或者其它系統工具能夠讀取Ansible檔案.雖然並不是所有人都需要這個功能,但我們不能封鎖可能性.

Jinja2過濾器

Note

這並不是常用的特性.只在合適的時候使用它們,這是一個附加知識點.

Jinja2中的過濾器可以把一個模板表示式轉換為另一個.Jinja2附帶了很多這樣的功能.請參見Jinja2官方模板文件中的 builtin filters.

另外,Ansible還支援其它特性.請看 playbooks_filters 文件中關於一系列可用的過濾器及示例.

YAML陷阱

YAML語法要求如果值以{{ foo }}開頭的話我們需要將整行用雙引號包起來.這是為了確認你不是想聲明一個YAML字典.該知識點在 YAML 語法 頁面有所講述.

這樣是不行的:

- hosts: app_servers
  vars:
      app_path: {{ base_path }}/22

你應該這麼做:

- hosts: app_servers
  vars:
       app_path: "{{ base_path }}/22"

使用Facts獲取的資訊

還有其它地方可以獲取變數,這些變數是自動發現的,而不是使用者自己設定的.

Facts通過訪問遠端系統獲取相應的資訊. 一個例子就是遠端主機的IP地址或者作業系統是什麼. 使用以下命令可以檢視哪些資訊是可用的:

ansible hostname -m setup

這會返回巨量的變數資料,比如對於Ubutu 12.04系統,Ansible 1.4獲取的資訊顯示如下:

"ansible_all_ipv4_addresses": [
    "REDACTED IP ADDRESS"
],
"ansible_all_ipv6_addresses": [
    "REDACTED IPV6 ADDRESS"
],
"ansible_architecture": "x86_64",
"ansible_bios_date": "09/20/2012",
"ansible_bios_version": "6.00",
"ansible_cmdline": {
    "BOOT_IMAGE": "/boot/vmlinuz-3.5.0-23-generic",
    "quiet": true,
    "ro": true,
    "root": "UUID=4195bff4-e157-4e41-8701-e93f0aec9e22",
    "splash": true
},
"ansible_date_time": {
    "date": "2013-10-02",
    "day": "02",
    "epoch": "1380756810",
    "hour": "19",
    "iso8601": "2013-10-02T23:33:30Z",
    "iso8601_micro": "2013-10-02T23:33:30.036070Z",
    "minute": "33",
    "month": "10",
    "second": "30",
    "time": "19:33:30",
    "tz": "EDT",
    "year": "2013"
},
"ansible_default_ipv4": {
    "address": "REDACTED",
    "alias": "eth0",
    "gateway": "REDACTED",
    "interface": "eth0",
    "macaddress": "REDACTED",
    "mtu": 1500,
    "netmask": "255.255.255.0",
    "network": "REDACTED",
    "type": "ether"
},
"ansible_default_ipv6": {},
"ansible_devices": {
    "fd0": {
        "holders": [],
        "host": "",
        "model": null,
        "partitions": {},
        "removable": "1",
        "rotational": "1",
        "scheduler_mode": "deadline",
        "sectors": "0",
        "sectorsize": "512",
        "size": "0.00 Bytes",
        "support_discard": "0",
        "vendor": null
    },
    "sda": {
        "holders": [],
        "host": "SCSI storage controller: LSI Logic / Symbios Logic 53c1030 PCI-X Fusion-MPT Dual Ultra320 SCSI (rev 01)",
        "model": "VMware Virtual S",
        "partitions": {
            "sda1": {
                "sectors": "39843840",
                "sectorsize": 512,
                "size": "19.00 GB",
                "start": "2048"
            },
            "sda2": {
                "sectors": "2",
                "sectorsize": 512,
                "size": "1.00 KB",
                "start": "39847934"
            },
            "sda5": {
                "sectors": "2093056",
                "sectorsize": 512,
                "size": "1022.00 MB",
                "start": "39847936"
            }
        },
        "removable": "0",
        "rotational": "1",
        "scheduler_mode": "deadline",
        "sectors": "41943040",
        "sectorsize": "512",
        "size": "20.00 GB",
        "support_discard": "0",
        "vendor": "VMware,"
    },
    "sr0": {
        "holders": [],
        "host": "IDE interface: Intel Corporation 82371AB/EB/MB PIIX4 IDE (rev 01)",
        "model": "VMware IDE CDR10",
        "partitions": {},
        "removable": "1",
        "rotational": "1",
        "scheduler_mode": "deadline",
        "sectors": "2097151",
        "sectorsize": "512",
        "size": "1024.00 MB",
        "support_discard": "0",
        "vendor": "NECVMWar"
    }
},
"ansible_distribution": "Ubuntu",
"ansible_distribution_release": "precise",
"ansible_distribution_version": "12.04",
"ansible_domain": "",
"ansible_env": {
    "COLORTERM": "gnome-terminal",
    "DISPLAY": ":0",
    "HOME": "/home/mdehaan",
    "LANG": "C",
    "LESSCLOSE": "/usr/bin/lesspipe %s %s",
    "LESSOPEN": "| /usr/bin/lesspipe %s",
    "LOGNAME": "root",
    "LS_COLORS": "rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lz=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.axa=00;36:*.oga=00;36:*.spx=00;36:*.xspf=00;36:",
    "MAIL": "/var/mail/root",
    "OLDPWD": "/root/ansible/docsite",
    "PATH": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
    "PWD": "/root/ansible",
    "SHELL": "/bin/bash",
    "SHLVL": "1",
    "SUDO_COMMAND": "/bin/bash",
    "SUDO_GID": "1000",
    "SUDO_UID": "1000",
    "SUDO_USER": "mdehaan",
    "TERM": "xterm",
    "USER": "root",
    "USERNAME": "root",
    "XAUTHORITY": "/home/mdehaan/.Xauthority",
    "_": "/usr/local/bin/ansible"
},
"ansible_eth0": {
    "active": true,
    "device": "eth0",
    "ipv4": {
        "address": "REDACTED",
        "netmask": "255.255.255.0",
        "network": "REDACTED"
    },
    "ipv6": [
        {
            "address": "REDACTED",
            "prefix": "64",
            "scope": "link"
        }
    ],
    "macaddress": "REDACTED",
    "module": "e1000",
    "mtu": 1500,
    "type": "ether"
},
"ansible_form_factor": "Other",
"ansible_fqdn": "ubuntu2.example.com",
"ansible_hostname": "ubuntu2",
"ansible_interfaces": [
    "lo",
    "eth0"
],
"ansible_kernel": "3.5.0-23-generic",
"ansible_lo": {
    "active": true,
    "device": "lo",
    "ipv4": {
        "address": "127.0.0.1",
        "netmask": "255.0.0.0",
        "network": "127.0.0.0"
    },
    "ipv6": [
        {
            "address": "::1",
            "prefix": "128",
            "scope": "host"
        }
    ],
    "mtu": 16436,
    "type": "loopback"
},
"ansible_lsb": {
    "codename": "precise",
    "description": "Ubuntu 12.04.2 LTS",
    "id": "Ubuntu",
    "major_release": "12",
    "release": "12.04"
},
"ansible_machine": "x86_64",
"ansible_memfree_mb": 74,
"ansible_memtotal_mb": 991,
"ansible_mounts": [
    {
        "device": "/dev/sda1",
        "fstype": "ext4",
        "mount": "/",
        "options": "rw,errors=remount-ro",
        "size_available": 15032406016,
        "size_total": 20079898624
    }
],
"ansible_nodename": "ubuntu2.example.com",
"ansible_os_family": "Debian",
"ansible_pkg_mgr": "apt",
"ansible_processor": [
    "Intel(R) Core(TM) i7 CPU         860  @ 2.80GHz"
],
"ansible_processor_cores": 1,
"ansible_processor_count": 1,
"ansible_processor_threads_per_core": 1,
"ansible_processor_vcpus": 1,
"ansible_product_name": "VMware Virtual Platform",
"ansible_product_serial": "REDACTED",
"ansible_product_uuid": "REDACTED",
"ansible_product_version": "None",
"ansible_python_version": "2.7.3",
"ansible_selinux": false,
"ansible_ssh_host_key_dsa_public": "REDACTED KEY VALUE"
"ansible_ssh_host_key_ecdsa_public": "REDACTED KEY VALUE"
"ansible_ssh_host_key_rsa_public": "REDACTED KEY VALUE"
"ansible_swapfree_mb": 665,
"ansible_swaptotal_mb": 1021,
"ansible_system": "Linux",
"ansible_system_vendor": "VMware, Inc.",
"ansible_user_id": "root",
"ansible_userspace_architecture": "x86_64",
"ansible_userspace_bits": "64",
"ansible_virtualization_role": "guest",
"ansible_virtualization_type": "VMware"

可以在playbook中這樣引用以上例子中第一個硬碟的模型:

{{ ansible_devices.sda.model }}

同樣,作為系統報告的主機名如以下所示:

{{ ansible_nodename }}

不合格的主機名顯示了句號(.)之前的字元串:

{{ ansible_hostname }}

在模板和條件判斷(請看 playbook_conditionals )中會經常使用Facts.

還可以使用Facts根據特定的條件動態建立主機群組,請檢視 模組相關 文件中的 ‘group_by’ 小節獲取詳細內容.以及參見 條件選擇 章節討論的廣義條件語句部分.

關閉Facts

如果你不需要使用你主機的任何fact資料,你已經知道了你係統的一切,那麼你可以關閉fact資料的獲取.這有利於增強Ansilbe面對大量系統的push模組,或者你在實驗性平臺中使用Ansible.在任何playbook中可以這樣做:

- hosts: whatever
  gather_facts: no

本地Facts(Facts.d)

New in version 1.3.

正如在playbook章節討論的一樣,Ansible facts主要用於獲取遠端系統的資料,從而可以在playbook中作為變數使用.

通常facts中的資料是由Ansible中的 ‘setup’模組自動發現的.使用者也可以自定義facts模組,在API文件中有說明.然而,如果不借助於fact模組,而是通過一個簡單的方式為Ansible變數提供系統或使用者資料?

比如,你想使用者能夠控制受他們管理的系統的一些切面,那麼應該怎麼做? “Facts.d”是這樣的一種機制.

Note

可能 “局部facts”有點用詞不當,它與 “中心供應的使用者值”相對應,為”局部供應的使用者值”,或者facts是 “局部動態測定的值”.

如果遠端受管理的機器有一個 “/etc/ansible/facts.d” 目錄,那麼在該目錄中任何以 ”.fact”結尾的檔案都可以在Ansible中提供局部facts.這些檔案可以是JSON,INI或者任何可以返回JSON的可執行檔案.

例如建設有一個 /etc/ansible/facts.d/perferences.fact檔案:

[general]
asdf=1
bar=2

這將產生一個名為 “general” 的雜湊表fact,裡面成員有 ‘asdf’ 和 ‘bar’. 可以這樣驗證:

ansible <hostname> -m setup -a "filter=ansible_local"

然後你會看到有以下fact被新增:

"ansible_local": {
        "preferences": {
            "general": {
                "asdf" : "1",
                "bar"  : "2"
            }
        }
 }

而且也可以在template或palybook中訪問該資料:

{{ ansible_local.preferences.general.asdf }}

本地名稱空間放置其它使用者提供的fact或者playbook中定義的變數覆蓋系統facts值.

如果你有個一個playook,它複製了一個自定義的fact,然後執行它,請顯式呼叫來重新執行setup模組,這樣可以讓我們在該playbook中使用這些fact.否則,在下一個play中才能獲取這些自定義的fact資訊.這裡有一個示例:

- hosts: webservers
  tasks:
    - name: create directory for ansible custom facts
      file: state=directory recurse=yes path=/etc/ansible/facts.d
    - name: install custom impi fact
      copy: src=ipmi.fact dest=/etc/ansible/facts.d
    - name: re-read facts after adding custom fact
      setup: filter=ansible_local

然而在該模式中你也可以編寫一個fact模組,這只不過是多了一個選項.

Fact快取

New in version 1.8.

正如該文件中其它地方所示,從一個伺服器引用另一個伺服器的變數是可行的.比如:

{{ hostvars['asdf.example.com']['ansible_os_family'] }}

如果禁用 “Fact Caching”,為了實現以上功能,Ansible在當前play之前已經與 ‘asdf.example.com’ 通訊過,或者在playbook有其它優先的play.這是ansible的預設配置.

為了避免這些,Ansible 1.8允許在playbook執行期間儲存facts.但該功能需要手動開啟.這有什麼用處那?

想象一下,如果我們有一個非常大的基礎設施,裡面有數千個主機.Fact快取可以配置在夜間執行,但小型伺服器叢集可以配置fact隨時執行,或者在白天定期執行.即使開啟了fact快取,也不需要訪問所有伺服器來引用它們的變數和資訊.

使用fact快取可以跨群組訪問變數,即使群組間在當前/user/bin/ansible-playbook執行中並沒有通訊過.

為了啟用fact快取,在大多數plays中你可以修改 ‘gathering’ 設定為 ‘smart’ 或者 ‘explicit’,也可以設定 ‘gather_facts’ 為False.

當前,Ansible可以使用兩種持久的快取外掛: redis和jsonfile.

可以在ansible.cfg中配置fact快取使用redis:

[defaults]
gathering = smart
fact_caching = redis
fact_caching_timeout = 86400
# seconds

請執行適當的系統命令來啟動和執行redis:

yum install redis
service redis start
pip install redis

請注意可以使用pip來安裝Python redis庫,在EPEL中的包版本對Ansible來說太舊了. 在當前Ansible版本中,該功能還處於試用狀態,Redis外掛還不支援埠或密碼配置,以後會改善這點. 在ansible.cfg中使用以下程式碼來配置fact快取使用jsonfile:

[defaults]
gathering = smart
fact_caching = jsonfile
fact_caching_connection = /path/to/cachedir
fact_caching_timeout = 86400
# seconds

fact_caching_connection 是一個放置在可讀目錄(如果目錄不存在,ansible會試圖建立它)中的本地檔案路徑.

註冊變數

變數的另一個主要用途是在執行命令時,把命令結果儲存到一個變數中.不同模組的執行結果是不同的.執行playbook時使用-v選項可以看到可能的結果值. 在ansible執行任務的結果值可以儲存在變數中,以便稍後使用它.在 條件選擇 章節有一些示例.

這裡有一個語法示例,在上面文件中也有所提及:

- hosts: web_servers

  tasks:

     - shell: /usr/bin/foo
       register: foo_result
       ignore_errors: True

     - shell: /usr/bin/bar
       when: foo_result.rc == 5

在當前主機接下來playbook執行過程中註冊的變數是有效地.這與Ansile中的 “facts” 生命週期一樣. 實際上註冊變數和facts很相似.

訪問複雜變數資料

在該文件中我們已經討論了一些與facts有關的高階特性.

有些提供的facts,比如網路資訊等,是一個巢狀的資料結構.訪問它們使用簡單的 {{ foo }} 語法並不夠用,當仍然很容易.如下所示:

{{ ansible_eth0["ipv4"]["address"] }}

或者這樣寫:

{{ ansible_eth0.ipv4.address }}

相似的,以下程式碼展示了我們如何訪問陣列的第一個元素:

{{ foo[0] }}

魔法變數,以及如何訪問其它主機的資訊

Ansible會自動提供給你一些變數,即使你並沒有定義過它們.這些變數中重要的有 ‘hostvars’,’group_names’,和 ‘groups’.由於這些變數名是預留的,所以使用者不應當覆蓋它們. ‘environmen’ 也是預留的. hostvars可以讓你訪問其它主機的變數,包括哪些主機中獲取到的facts.如果你還沒有在當前playbook或者一組playbook的任何play中訪問那個主機,那麼你可以獲取變數,但無法看到facts值. 如果資料庫伺服器想使用另一個節點的某個 ‘fact’ 值,或者賦值給該節點的一個inventory變數.可以在一個模板中甚至命令列中輕鬆實現:

{{ hostvars['test.example.com']['ansible_distribution'] }}

另外, group_names 是當前主機所在所有群組的列表(陣列).所以可以使用Jinja2語法在模板中根據該主機所在群組關係(或角色)來產生變化:

{% if 'webserver' in group_names %}
   # some part of a configuration file that only applies to webservers
{% endif %}

groups 是inventory中所有群組(主機)的列表.可用於列舉群組中的所有主機.例如:

{% for host in groups['app_servers'] %}
   # something that applies to all app servers.
{% endfor %}

一個經常使用的正規化是找出該群組中的所有IP地址:

{% for host in groups['app_servers'] %}
   {{ hostvars[host]['ansible_eth0']['ipv4']['address'] }}
{% endfor %}

比如,一個前端代理伺服器需要指向所有的應用伺服器,在伺服器間設定正確的防火牆規則等.你需要確保所有主機的facts在使用前都已被獲取到,例如執行一個play來檢查這些facts是否已經被快取起來(fact快取是Ansible 1.8中的新特性).

另外, inventory_hostname 是Ansible inventory主機檔案中配置的主機名稱.由於其它一些神祕原因你不想使用自發現的主機名 ansible_hostname 時,你可以使用 inventory_hostname .如果主機的FQDN很長,那麼*inventory_hostname_short*則會只包含域名第一個分號之前的部分,而捨棄其它部分.

play_hosts 是在當前play範圍中可用的一組主機名.比如可以為多個主機填寫模板,以便將這些主機注入負載均衡器規則.

delegate_to is the inventory hostname of the host that the current task has been delegated to using ‘delegate_to’.

delegate_to 是使用 ‘delegate_to’ 代理的任務中主機的inventory主機名.

不要擔心以上東西,除非你需要使用它們.你會知道什麼時候用它們.

inventory_dir 是儲存Ansible inventory主機檔案的目錄路徑, inventory_file 是指向Ansible inventory主機檔案的路徑和檔名.

最後, role_path 會返回當前role的目錄名(1.8及以後).只有在role中才能使用該變數.

變數檔案分割

把playbook置於原始碼管理之下是個很好的注意,當你可能會想把playbook源碼公開之餘還想保持某些重要的變數私有.有時你也想把某些資訊放置在不同的檔案中,遠離主playbook檔案.

你可以使用外部的變數檔案來實現:

---

- hosts: all
  remote_user: root
  vars:
    favcolor: blue
  vars_files:
    - /vars/external_vars.yml

  tasks:

  - name: this is just a placeholder
    command: /bin/echo foo

這可以保證你共享playbook源碼時隔離敏感資料的風險.

每個變數檔案的內容是一個簡單的YAML檔案,如下所示:

---
# in the above example, this would be vars/external_vars.yml
somevar: somevalue
password: magic

Note

It’s also possible to keep per-host and per-group variables in very similar files, this is covered in 分檔案定義 Host 和 Group 變數.

Note

保持每個主機和群組的變數在非常小的檔案中是可能,請參見 分檔案定義 Host 和 Group 變數.

命令列中傳遞變數

除了`vars_prompt`和`vars_files`也可以通過Ansible命令列傳送變數.如果你想編寫一個通用的釋出playbook時則特別有用,你可以傳遞應用的版本以便部署:

ansible-playbook release.yml --extra-vars "version=1.23.45 other_variable=foo"

其它場景中也很有用,比如為playbook設定主機群組或使用者.

Example:

---

- hosts: '{{ hosts }}'
  remote_user: '{{ user }}'

  tasks:
     - ...

ansible-playbook release.yml --extra-vars "hosts=vipers user=starbuck"

Ansible 1.2中你也可以給extra-vars傳遞JSON,比如:

--extra-vars '{"pacman":"mrs","ghosts":["inky","pinky","clyde","sue"]}'

key=value形式非常簡單,但很實用!

Ansible 1.3中,實用”@”語法可以為extra-vars傳遞JSON檔案:

--extra-vars "@some_file.json"

同樣在Ansible 1.3中,我們可以為extra-vars傳遞YAML格式,無論直接通過命令列還是放置在檔案中.

變數的優先順序: 我該在什麼地方放置變數?

很多人都在問變數過載的規則是怎麼樣的.最終Ansible的哲學是你最好知道哪裡放置變數,然後會簡化變數覆蓋的複雜度.

避免在47個地方定義 “x” 變數然後詢問 “那個x會被使用”. 為什麼那? 因為這不是Ansible做事的哲學.

世界上只有一個帝國大廈.也只有一個蒙娜麗莎.請弄明白在那裡定義變數,而不要把事情搞複雜.

然而,我們還是來討卵一下優先權的問題.它存在.你有可能會用到它.

如果同樣名稱的變數在多個地方都有定義,那麼採納是有個確定的順序,如下:

* extra vars (-e in the command line) always win
* then comes connection variables defined in inventory (ansible_ssh_user, etc)
* then comes "most everything else" (command line switches, vars in play, included vars, role vars, etc)
* then comes the rest of the variables defined in inventory
* then comes facts discovered about a system
* then "role defaults", which are the most "defaulty" and lose in priority to everything.

* extra vars (在命令列中使用 -e)優先順序最高
* 然後是在inventory中定義的連線變數(比如ansible_ssh_user)
* 接著是大多數的其它變數(命令列轉換,play中的變數,included的變數,role中的變數等)
* 然後是在inventory定義的其它變數
* 然後是由系統發現的facts
* 然後是 "role預設變數", 這個是最預設的值,很容易喪失優先權

Note

In versions prior to 1.5.4, facts discovered about a system were in the “most everything else” category above.

Note

在1.5.4版本級以後,關於系統的自發現的facts也包含在大多數的其它變數中.

這樣看起來太理論化了.讓我們來看一段示例,

First off, group variables are super powerful.

Site wide defaults should be defined as a ‘group_vars/all’ setting. Group variables are generally placed alongside your inventory file. They can also be returned by a dynamic inventory script (see 動態 Inventory) or defined in things like Ansible Tower from the UI or API:

---
# file: /etc/ansible/group_vars/all
# this is the site wide default
ntp_server: default-time.example.com

Regional information might be defined in a ‘group_vars/region’ variable. If this group is a child of the ‘all’ group (which it is, because all groups are), it will override the group that is higher up and more general:

---
# file: /etc/ansible/group_vars/boston
ntp_server: boston-time.example.com

If for some crazy reason we wanted to tell just a specific host to use a specific NTP server, it would then override the group variable!:

---
# file: /etc/ansible/host_vars/xyz.boston.example.com
ntp_server: override.example.com

So that covers inventory and what you would normally set there. It’s a great place for things that deal with geography or behavior. Since groups are frequently the entity that maps roles onto hosts, it is sometimes a shortcut to set variables on the group instead of defining them on a role. You could go either way.

Remember: Child groups override parent groups, and hosts always override their groups.

Next up: learning about role variable precedence.

We’ll pretty much assume you are using roles at this point. You should be using roles for sure. Roles are great. You are using roles aren’t you? Hint hint.

Ok, so if you are writing a redistributable role with reasonable defaults, put those in the ‘roles/x/defaults/main.yml’ file. This means the role will bring along a default value but ANYTHING in Ansible will override it. It’s just a default. That’s why it says “defaults” :) See Playbook Roles and Include Statements for more info about this:

---
# file: roles/x/defaults/main.yml
# if not overridden in inventory or as a parameter, this is the value that will be used
http_port: 80

if you are writing a role and want to ensure the value in the role is absolutely used in that role, and is not going to be overridden by inventory, you should put it in roles/x/vars/main.yml like so, and inventory values cannot override it. -e however, still will:

---
# file: roles/x/vars/main.yml
# this will absolutely be used in this role
http_port: 80

So the above is a great way to plug in constants about the role that are always true. If you are not sharing your role with others, app specific behaviors like ports is fine to put in here. But if you are sharing roles with others, putting variables in here might be bad. Nobody will be able to override them with inventory, but they still can by passing a parameter to the role.

Parameterized roles are useful.

If you are using a role and want to override a default, pass it as a parameter to the role like so:

roles:
   - { role: apache, http_port: 8080 }

This makes it clear to the playbook reader that you’ve made a conscious choice to override some default in the role, or pass in some configuration that the role can’t assume by itself. It also allows you to pass something site-specific that isn’t really part of the role you are sharing with others.

This can often be used for things that might apply to some hosts multiple times, like so:

roles:
   - { role: app_user, name: Ian    }
   - { role: app_user, name: Terry  }
   - { role: app_user, name: Graham }
   - { role: app_user, name: John   }

That’s a bit arbitrary, but you can see how the same role was invoked multiple Times. In that example it’s quite likely there was no default for ‘name’ supplied at all. Ansible can yell at you when variables aren’t defined – it’s the default behavior in fact.

So that’s a bit about roles.

There are a few bonus things that go on with roles.

Generally speaking, variables set in one role are available to others. This means if you have a “roles/common/vars/main.yml” you can set variables in there and make use of them in other roles and elsewhere in your playbook:

roles:
   - { role: common_settings }
   - { role: something, foo: 12 }
   - { role: something_else }

Note

There are some protections in place to avoid the need to namespace variables. In the above, variables defined in common_settings are most definitely available to ‘something’ and ‘something_else’ tasks, but if “something’s” guaranteed to have foo set at 12, even if somewhere deep in common settings it set foo to 20.

So, that’s precedence, explained in a more direct way. Don’t worry about precedence, just think about if your role is defining a variable that is a default, or a “live” variable you definitely want to use. Inventory lies in precedence right in the middle, and if you want to forcibly override something, use -e.

If you found that a little hard to understand, take a look at the ansible-examples repo on our github for a bit more about how all of these things can work together.

如果你還感覺有點難以理解,你可以學習我們放在github中的 ansible-examples 程式碼庫,來了解這些東西是如何一起協作的.

See also

Playbooks
An introduction to playbooks
條件選擇
Conditional statements in playbooks
playbooks_filters
Jinja2 filters and their uses
迴圈
Looping in playbooks
Playbook Roles and Include Statements
Playbook organization by roles
最佳實踐
Best practices in playbooks
User Mailing List
Have a question? Stop by the google group!
irc.freenode.net
#ansible IRC chat channel