本地執行
如果希望在控制主機本地運行一個特定的任務,可以使用local_action語句。
假設我們需要配置的遠程主機剛剛啟動,如果我們直接運行playbook,可能會因為sshd服務尚未開始監聽而導致失敗,我們可以在控制主機上使用如下示例來等待被控端sshd端口監聽:
- name: wait for ssh server to be running
wait_for
port: 22
host: "{{ inventory_hostname }}"
search_regex: OpenSSH
connection: local
任務委托
在有些時候,我們希望運行與選定的主機或主機組相關聯的task,但是這個task又不需要在選定的主機或主機組上執行,而需要在另一台服務器上執行。
這種特性適用於以下場景:
- 在告警系統中啟用基於主機的告警
- 向負載均衡器中添加或移除一台主機
- 在dns上添加或修改針對某個主機的解析
- 在存儲節點上創建一個存儲以用於主機掛載
- 使用一個外部程序來檢測主機上的服務是否正常
可以使用delegate_to語句來在另一台主機上運行task:
- name: enable alerts for web servers
hosts: webservers
tasks:
- name: enable alerts
nagios: action=enable_alerts service=web host="{{ inventory_hostname }}"
delegate_to: nagios.example.com
如果delegate_to: 127.0.0.1的時候,等價於local_action
任務暫停
有些情況下,一些任務的運行需要等待一些狀態的恢復,比如某一台主機或者應用剛剛重啟,我們需要需要等待它上面的某個端口開啟,此時就需要將正在運行的任務暫停,直到其狀態滿足要求。
Ansible提供了wait_for模塊以實現任務暫停的需求
wait_for模塊常用參數:
- connect_timeout:在下一個任務執行之前等待連接的超時時間
- delay:等待一個端口或者文件或者連接到指定的狀態時,默認超時時間為300秒,在這等待的300s的時間里,wait_for模塊會一直輪詢指定的對象是否到達指定的狀態,delay即為多長時間輪詢一次狀態。
- host:wait_for模塊等待的主機的地址,默認為127.0.0.1
- port:wait_for模塊待待的主機的端口
- path:文件路徑,只有當這個文件存在時,下一任務才開始執行,即等待該文件創建完成
- state:等待的狀態,即等待的文件或端口或者連接狀態達到指定的狀態時,下一個任務開始執行。當等的對象為端口時,狀態有started,stoped,即端口已經監聽或者端口已經關閉;當等待的對象為文件時,狀態有present或者started,absent,即文件已創建或者刪除;當等待的對象為一個連接時,狀態有drained,即連接已建立。默認為started
- timeout:wait_for的等待的超時時間,默認為300秒
示例:
#等待8080端口已正常監聽,才開始下一個任務,直到超時
- wait_for:
port: 8080
state: started
#等待8000端口正常監聽,每隔10s檢查一次,直至等待超時
- wait_for:
port: 8000
delay: 10
#等待8000端口直至有連接建立
- wait_for:
host: 0.0.0.0
port: 8000
delay: 10
state: drained
#等待8000端口有連接建立,如果連接來自10.2.1.2或者10.2.1.3,則忽略。
- wait_for:
host: 0.0.0.0
port: 8000
state: drained
exclude_hosts: 10.2.1.2,10.2.1.3
#等待/tmp/foo文件已創建
- wait_for:
path: /tmp/foo
#等待/tmp/foo文件已創建,而且該文件中需要包含completed字符串
- wait_for:
path: /tmp/foo
search_regex: completed
#等待/var/lock/file.lock被刪除
- wait_for:
path: /var/lock/file.lock
state: absent
#等待指定的進程被銷毀
- wait_for:
path: /proc/3466/status
state: absent
#等待openssh啟動,10s檢查一次
- wait_for:
port: 22
host: "{{ ansible_ssh_host | default(inventory_hostname) }}" search_regex: OpenSSH
delay: 10
滾動執行
默認情況下,ansible會並行的在所有選定的主機或主機組上執行每一個task,但有的時候,我們會希望能夠逐台運行。最典型的例子就是對負載均衡器后面的應用服務器進行更新時。通常來講,我們會將應用服務器逐台從負載均衡器上摘除,更新,然后再添加回去。我們可以在play中使用serial語句來告訴ansible限制並行執行play的主機數量。
下面是一個在amazon EC2的負載均衡器中移除主機,更新軟件包,再添加回負載均衡的配置示例:
- name: upgrade pkgs on servers behind load balancer
hosts: myhosts
serial: 1
tasks:
- name: get the ec2 instance id and elastic load balancer id
ec2_facts:
- name: take the host out of the elastic load balancer id
local_action: ec2_elb
args:
instance_id: "{{ ansible_ec2_instance_id }}"
state: absent
- name: upgrade pkgs
apt:
update_cache: yes
upgrade: yes
- name: put the host back n the elastic load balancer
local_action: ec2_elb
args:
instance_id: "{{ ansible_ec2_instance_id }}"
state: present
ec2_elbs: "{{ items }}"
with_items: ec2_elbs
在上述示例中,serial的值為1,即表示在某一個時間段內,play只在一台主機上執行。如果為2,則同時有2台主機運行play。
一般來講,當task失敗時,ansible會停止執行失敗的那台主機上的任務,但是繼續對其他 主機執行。在負載均衡的場景中,我們會更希望ansible在所有主機執行失敗之前就讓play停止,否則很可能會面臨所有主機都從負載均衡器上摘除並且都執行失敗導致服務不可用的場景。這個時候,我們可以使用serial語句配合max_fail_percentage語句使用。max_fail_percentage
表示當最大失敗主機的比例達到多少時,ansible就讓整個play失敗。示例如下:
- name: upgrade pkgs on fservers behind load balancer
hosts: myhosts
serial: 1
max_fail_percentage: 25
tasks:
......
假如負載均衡后面有4台主機,並且有一台主機執行失敗,這時ansible還會繼續運行,要讓Play停止運行,則必須超過25%,所以如果想一台失敗就停止執行,我們可以將max_fail_percentage的值設為24。如果我們希望只要有執行失敗,就放棄執行,我們可以將max_fail_percentage的值設為0。
只執行一次
某些時候,我們希望某個task只執行一次,即使它被綁定到了多個主機上。例如在一個負載均衡器后面有多台應用服務器,我們希望執行一個數據庫遷移,只需要在一個應用服務器上執行操作即可。
可以使用run_once語句來處理:
- name: run the database migrateions
command: /opt/run_migrateions
run_once: true
還可以與local_action配合使用,如下:
- name: run the task locally, only once
command: /opt/my-custom-command
connection: local
run_once: true
還可以與delegate_to配合使用,讓這個只執行一次的任務在指定的機器上運行:
- name: run the task locally, only once
command: /opt/my-custom-command
run_once: true
delegate_to: app.a1-61-105.dev.unp
設置環境變量
我們在命令行下執行某些命令的時候,這些命令可能會需要依賴環境變量。比如在安裝某些包的時候,可能需要通過代理才能完成完裝。或者某個腳本可能需要調用某個環境變量才能完成運行。
ansible 支持通過environment
關鍵字來定義一些環境變量。
在如下場景中可能需要用到環境變量:
- 運行shell的時候,需要設置path變量
- 需要加載一些庫,這些庫不在系統的標准庫路徑當中
下面是一個簡單示例:
---
- name: upload a remote file to aws s3
hosts: test
tasks:
- name: install pip
yum:
name: python-pip
state: installed
- name: install the aws tools
pip:
name: awscli
state: present
- name upload file to s3
shell aws s3 put-object --bucket=my-test-bucket --key={{ ansible_hostname }}/fstab --body=/etc/fstab --region=eu-west-1
environment:
AWS_ACCESS_KEY_ID: xxxxxx
AWS_SECRET_ACCESS_KEY: xxxxxx
事實上,environment也可以存儲在變量當中:
- hosts: all
remote_user: root
vars:
proxy_env:
http_proxy: http://proxy.example.com:8080
https_proxy: http://proxy.bos.example.com:8080
tasks:
- apt: name=cobbler state=installed
environment: proxy_env
交互式提示
在少數情況下,ansible任務運行的過程中需要用戶輸入一些數據,這些數據要么比較秘密不方便,或者數據是動態的,不同的用戶有不同的需求,比如輸入用戶自己的賬戶和密碼或者輸入不同的版本號會觸發不同的后續操作等。ansible的vars_prompt關鍵字就是用來處理上述這種與用戶交互的情況的。
- hosts: all
remote_user: root
vars_prompt:
- name: share_user
prompt: "what is your network username?"
private: yes
- name: share_pass
prompt: "what is your network password"
private: yes
tasks:
- debug:
var: share_user
- debug:
var: share_pass
vars_prompt常用選項說明:
- private: 默認為yes,表示用戶輸入的值在命令行不可見
- default:定義默認值,當用戶未輸入時則使用默認值
- confirm:如果設置為yes,則會要求用戶輸入兩次,適合輸入密碼的情況