Ansible學習實戰手記-你想要知道的可能都在這里了


最近接觸了ansible工具,查找了一些資料,也做了一些總結。希望能給剛接觸的新手帶來一些幫助。

此總結有實際例子,大部分也是從實踐中用到才逐一總結的。

當然可能肯定一定會存在一些錯誤和紕漏,還望大家具體實踐時進一步熟悉了解。

ansible本身的模塊有幾百個,按照官網的建議來說,不建議一次性學完。

我們需要一邊學習一邊實踐一邊總結。要經常查找官方文檔。

官方文檔如下:

https://docs.ansible.com/ansible/latest/index.html

 

Ansible學習 安裝: pip install ansible==2.4.1.0 為什么要指定版本是:2.4.1.0
1、因為最新版本2.7.1在導入時,報錯: root@ubuntu:/etc/ansible# ansible --version
 Traceback (most recent call last): File "/usr/bin/ansible", line 41, in <module>
        from ansible.utils.unicode import to_unicode ImportError: cannot import name to_unicode 暫時解決辦法:將ansible版本降低到2.4.1.0問題解決 2、一些新特性只能在更高的ansible版本中使用,比如:include_tasks 在低版本中使用時會報錯: ERROR! no action detected in task The error appears to have been in '/etc/ansible/roles/newone/tasks/main.yml': line 8, column 4, but may be elsewhere in the file depending on the exact syntax problem. The offending line appears to be: - include_tasks: lala.yml ^ here 依賴包如下: jinja2 Jinja2 2.10 PyYAML PyYAML 3.11 paramiko paramiko 2.4.2 cryptography cryptography 2.3.1 setuptools setuptools 20.7.0 MarkupSafe>=0.23                             MarkupSafe   0.23 pyasn1>=0.1.7                                 pyasn1       0.4.4 bcrypt>=3.1.3                                 bcrypt       3.1.4 pynacl>=1.0.1                                 PyNaCl       1.3.0 enum34; python_version < "3"                 enum34       1.1.6 asn1crypto>=0.21.0                             asn1crypto   0.24.0 cffi!=1.11.3,>=1.7                             cffi         1.11.5 idna>=2.1                                     idna         2.7 six>=1.4.1                                     six          1.10.0 ipaddress; python_version < "3"             ipaddress    1.0.22 pycparser pycparser 2.19 ansible -h 參數解析 Usage: ansible <host-pattern> [options] Options: -a MODULE_ARGS, --args=MODULE_ARGS    模塊的參數,如果執行默認COMMAND的模塊,即是命令參數,如:“date”,"pwd"等等 module arguments 模塊參數 -k, --ask-pass        ask for SSH password 登錄密碼,提示輸入SSH密碼而不是假設基於密鑰的驗證 --ask-su-pass         ask for su password su切換密碼 -K, --ask-sudo-pass   ask for sudo password 提示密碼使用sudo,sudo表示提權操作 --ask-vault-pass      ask for vault password -B SECONDS, --background=SECONDS 后台運行超時時間 run asynchronously, failing after X seconds (default=N/A) -C, --check           don't make any changes; instead, try to predict some 
              只是測試一下會改變什么內容,不會真正去執行;相反,試圖預測一些可能發生的變化
of the changes that may occur -c CONNECTION, --connection=CONNECTION 連接類型使用。可能的選項是paramiko(SSH),SSH和地方。當地主要是用於crontab或啟動。 connection type to use (default=smart) -f FORKS, --forks=FORKS 並行任務數。NUM被指定為一個整數,默認是5 specify number of parallel processes to use (default=5) -h, --help show this help message and exit 打開幫助文檔API -i INVENTORY, --inventory-file=INVENTORY 指定庫存主機文件的路徑,默認為/etc/ansible/hosts specify inventory host file (default=/etc/ansible/hosts) -l SUBSET, --limit=SUBSET 進一步限制所選主機/組模式 --limit=192.168.91.135 只對這個ip執行 further limit selected hosts to an additional pattern --list-hosts outputs a list of matching hosts; does not execute anything else -m MODULE_NAME, --module-name=MODULE_NAME 執行模塊的名字,默認使用 command 模塊,所以如果是只執行單一命令可以不用 -m參數 module name to execute (default=command) -M MODULE_PATH, --module-path=MODULE_PATH 要執行的模塊的路徑,默認為/usr/share/ansible/ specify path(s) to module library (default=/usr/share/ansible/) -o, --one-line condense output 壓縮輸出,摘要輸出.嘗試一切都在一行上輸出。 -P POLL_INTERVAL, --poll=POLL_INTERVAL 調查背景工作每隔數秒。需要- b set the poll interval if using -B (default=15) --private-key=PRIVATE_KEY_FILE 私鑰路徑,使用這個文件來驗證連接 use this file to authenticate the connection -S, --su run operations with su 用 su 命令 -R SU_USER, --su-user=SU_USER 指定SU的用戶,默認是root用戶 run operations with su as this user (default=root) -s, --sudo run operations with sudo (nopasswd) -U SUDO_USER, --sudo-user=SUDO_USER sudo到哪個用戶,默認為 root desired sudo user (default=root) -T TIMEOUT, --timeout=TIMEOUT 指定SSH默認超時時間, 默認是10S override the SSH timeout in seconds (default=10) -t TREE, --tree=TREE log output to this directory 將日志內容保存在該輸出目錄,結果保存在一個文件中在每台主機上。 -u REMOTE_USER, --user=REMOTE_USER 遠程用戶, 默認是root用戶 connect as this user (default=root) --vault-password-file=VAULT_PASSWORD_FILE vault password file -v, --verbose verbose mode (-vvv for more, -vvvv to enable 如果命令執行成功,輸出詳細的結果 connection debugging)(-vv –vvv -vvvv) --version show program's version number and exit 輸出ansible的版本 ansible-playbook參數解析: Options: --ask-vault-pass #加密playbook文件時提示輸入密碼 -C, --check #模擬執行,不會真正在機器上執行(查看執行會產生什么變化) -D, --diff #當更新的文件數及內容較少時,該選項可顯示這些文件不同的地方,該選項結合-C用會有較好的效果 -e EXTRA_VARS, --extra-vars=EXTRA_VARS #在Playbook中引入外部參數變量 --flush-cache #將fact清除到的遠程主機緩存 --force-handlers #強制運行handlers的任務,即使在任務失敗的情況下 -f FORKS, --forks=FORKS #並行任務數。FORKS被指定為一個整數,默認是5 -h, --help #打開幫助文檔API -i INVENTORY, --inventory-file=INVENTORY #specify inventory host path (default=/etc/ansible/hosts) or comma separated host list. #指定要讀取的Inventory文件 -l SUBSET, --limit=SUBSET #further limit selected hosts to an additional pattern #限定執行的主機范圍 --list-hosts #outputs a list of matching hosts; does not execute anything else #列出執行匹配到的主機,但並不會執行 --list-tags #list all available tags #列出所有可用的tags --list-tasks #list all tasks that would be executed #列出所有即將被執行的任務 -M MODULE_PATH, --module-path=MODULE_PATH #specify path(s) to module library (default=None) #要執行的模塊的路徑 --new-vault-password-file=NEW_VAULT_PASSWORD_FILE #new vault password file for rekey # --output=OUTPUT_FILE #output file name for encrypt or decrypt; use - for stdout # --skip-tags=SKIP_TAGS #only run plays and tasks whose tags do not match these values #跳過指定的tags任務 --start-at-task=START_AT_TASK #start the playbook at the task matching this name #從第幾條任務(START_AT_TASK)開始執行 --step #one-step-at-a-time: confirm each task before running #逐步執行Playbook定義的任務,並經人工確認后繼續執行下一步任務 --syntax-check #perform a syntax check on the playbook, but do not execute it #檢查Playbook中的語法書寫,並不實際執行 -t TAGS, --tags=TAGS #only run plays and tasks tagged with these values #指定執行該tags的任務 --vault-password-file=VAULT_PASSWORD_FILE #vault password file # -v, --verbose #verbose mode (-vvv for more, -vvvv to enable connection debugging) #執行詳細輸出 --version #show program's version number and exit #顯示版本 Connection Options: control as whom and how to connect to hosts -k, --ask-pass #ask for connection password # --private-key=PRIVATE_KEY_FILE, --key-file=PRIVATE_KEY_FILE #use this file to authenticate the connection # -u REMOTE_USER, --user=REMOTE_USER #connect as this user (default=None) #指定遠程主機以USERNAME運行命令 -c CONNECTION, --connection=CONNECTION #connection type to use (default=smart) #指定連接方式,可用選項paramiko (SSH)、ssh、local,local方式常用於crontab和kickstarts -T TIMEOUT, --timeout=TIMEOUT #override the connection timeout in seconds(default=10) #SSH連接超時時間設定,默認10s --ssh-common-args=SSH_COMMON_ARGS #specify common arguments to pass to sftp/scp/ssh (e.g.ProxyCommand) # --sftp-extra-args=SFTP_EXTRA_ARGS #specify extra arguments to pass to sftp only (e.g. -f, -l) # --scp-extra-args=SCP_EXTRA_ARGS #specify extra arguments to pass to scp only (e.g. -l) # --ssh-extra-args=SSH_EXTRA_ARGS #specify extra arguments to pass to ssh only (e.g. -R) # Privilege Escalation Options: control how and which user you become as on target hosts -s, --sudo #run operations with sudo (nopasswd) (deprecated, use become) #相當於Linux系統下的sudo命令 -U SUDO_USER, --sudo-user=SUDO_USER #desired sudo user (default=root) (deprecated, use become) #使用sudo,相當於Linux下的sudo命令 -S, --su #run operations with su (deprecated, use become) # -R SU_USER, --su-user=SU_USER #run operations with su as this user (default=root)(deprecated, use become) -b, --become #run operations with become (does not imply password prompting) # --become-method=BECOME_METHOD #privilege escalation method to use (default=sudo),valid choices:
        [ sudo | su | pbrun | pfexec | doas |dzdo | ksu | runas ]
# --become-user=BECOME_USER #run operations as this user (default=root) # --ask-sudo-pass #ask for sudo password (deprecated, use become) #傳遞sudo密碼到遠程主機,來保證sudo命令的正常運行 --ask-su-pass #ask for su password (deprecated, use become) # -K, --ask-become-pass #ask for privilege escalation password # 當然,我們對於一些具體的學習還要參考一些文檔 https://docs.ansible.com/ansible/2.4/intro_installation.html Ansible學習實踐: 1.在A主機上創建密鑰對,實現對其他主機無密碼訪問,執行: # ssh-keygen -t rsa -f ~/.ssh/id_rsa.pub -P "" # ssh-copy-id -i /root/.ssh/id_rsa.pub root@172.18.19.188 此時會在遠程機的/root/.ssh/authorized_keys文件中,生成id_rsa.pub文件的內容。 執行具體回顯 root@docker-02:~# ssh-copy-id -i /root/.ssh/id_rsa.pub root@172.18.19.188 /usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/root/.ssh/id_rsa.pub" The authenticity of host '172.18.19.188 (172.18.19.188)' can't be established. ECDSA key fingerprint is SHA256:BLDdJTy5lNOuopbtXDVojySMfc1y2lmJSPwvKIyvSVM. Are you sure you want to continue connecting (yes/no)? yes /usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed /usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys root@172.18.19.188's password: Number of key(s) added: 1 Now try logging into the machine, with: "ssh 'root@172.18.19.188'" and check to make sure that only the key(s) you wanted were added. root@docker-02:~# ssh 'root@172.18.19.188' Welcome to Ubuntu 16.04 LTS (GNU/Linux 4.4.0-21-generic x86_64) * Documentation: https://help.ubuntu.com/ 177 packages can be updated. 24 updates are security updates. *** System restart required *** Last login: Wed Nov 22 19:39:52 2017 from 172.18.19.94 root@docker-01:~# exit logout Connection to 172.18.19.188 closed. 2.A主機管理清單的配置     # cd /etc/ansible     # cp hosts{,.bak}     # vim hosts 添加如下內容: [remote] 172.18.19.188 測試執行是否成功: # ansible remote -m command -a "ls" 172.18.19.188 | SUCCESS | rc=0 >> # ansible all -m command -a "ls" 172.18.19.188 | SUCCESS | rc=0 >> 兩種方法都可以解決執行問題。 3.常用模塊   1.command模塊:在遠程主機上執行的命令     相關選項:       creates:一個文件名,當該文件存在,則該命令不執行       free_form:要執行的linux指令       chdir:在執行指令之前,先切換到該目錄       removes:一個文件名,當該文件不存在,則該選項不執行       executable:切換shell來執行指令,該執行路徑必須是一個絕對路徑 /*示例*/ ansible remote -m command -a "ls" 值得留意的時,command模塊執行的命令是獲取不到$HOME這樣的環境變量的, 一些運算符,例如”<“ 、”>“ 在command模塊上也是不能使用的。   2.setup模塊:查看遠程主機的相關facts變量信息 /*示例*/ ansible all -m setup ansible 192.168.43.130 -m setup   3.shell模塊:讓遠程主機在shell進程下執行命令,從而支持shell的特性,如管道等 /*示例*/ ansible all -m shell -a "echo "test" | passwd --stdin test1" 相當於增強版的command   4.copy模塊:復制本地文件至遠程主機上     相關選項:       backup:在覆蓋之前,將源文件備份,備份文件包含時間信息。有兩個選項:yes|no       content:用於替代“src”,可以直接設定指定文件的值       dest:必選項。要將源文件復制到的遠程主機的絕對路徑,如果源文件是一個目錄,那么該路徑也必須是個目錄       directory_mode:遞歸設定目錄的權限,默認為系統默認權限       force:如果目標主機包含該文件,但內容不同,如果設置為yes,則強制覆蓋,如果為no,則只有當目標主機的
          目標位置不存在該文件時,才復制。默認為yes       others:所有的file模塊里的選項都可以在這里使用       src:被復制到遠程主機的本地文件,可以是絕對路徑,也可以是相對路徑。如果路徑是一個目錄,它將遞歸復制。
          在這種情況下,如果路徑使用“
/”來結尾,則只復制目錄里的內容,如果沒有使用“/”來結尾,
          則包含目錄在內的整個內容全部復制,類似於rsync。       owner,group,mode...
/*示例*/ ansible remote -m copy -a "src=/etc/fstab dest=/root/ owner=root group=root mode=0644"   5.file模塊:設置文件屬性     相關選項:       force:需要在兩種情況下強制創建軟鏈接,一種是源文件不存在,但之后會建立的情況下;另一種是目標軟鏈接已存在,
          需要先取消之前的軟鏈,然后創建新的軟鏈,有兩個選項:yes
|no       group:定義文件/目錄的屬組       mode:定義文件/目錄的權限       owner:定義文件/目錄的屬主       path:必選項,定義文件/目錄的路徑       recurse:遞歸設置文件的屬性,只對目錄有效       src:被鏈接的源文件路徑,只應用於state=link的情況       dest:被鏈接到的路徑,只應用於state=link的情況       state:        directory:如果目錄不存在,就創建目錄        file:即使文件不存在,也不會被創建        link:創建軟鏈接       hard:創建硬鏈接       touch:如果文件不存在,則會創建一個新的文件,如果文件或目錄已存在,則更新其最后修改時間        absent:刪除目錄、文件或者取消鏈接文件 /*示例*/ ansible remote -m file -a "path=/root/fstab owner=root group=root mode=600" ansible storm_cluster -m file -a "src=/etc/resolv.conf dest=/tmp/resolv.conf state=link"創建鏈接文件 ansible storm_cluster -m file -a "path=/tmp/resolv.conf state=absent" 刪除鏈接文件   6.cron模塊:計划任務的實現     相關選項:       minute=/hour=/day=/month=/weekday= 某個值不寫,默認就是*       name:必選項,任務描述信息       job:執行的任務,要加引號       state:present(創建)/absent(刪除) /*示例*/ ansible remote -m cron -a "minute=*/1 job='/usr/bin/echo 'hello'' name=hello"   7.yum模塊:管理安裝相關程序包     相關選項:       name:程序包名稱,可帶版本號       state:present、installed、latest(安裝)/absent、removed(刪除)   8.service模塊:管理服務     相關選項:       name:服務名稱       state:started/stopped/restarted       enabled:true/false       runlevel:運行級別 9.group模塊:管理用戶組模塊     相關選項:       name:組名稱       gid:指定GID       state:present/absent       system:yes/no /*示例*/ ansible all -m group -a "name=test_grp state=present"   10.user模塊:管理用戶模塊     相關選項:       由於user模塊的選項眾多,這里只介紹一些常用的選項:       name:用戶名       password:為用戶設置登陸密碼,此密碼是明文密碼加密后的密碼       update_password:always/on_create        always:只有當密碼不相同時才會更新密碼(默認)         on_create:只為新用戶設置密碼       shell:用戶的shell設定       groups:用戶組設定       home:指定用戶的家目錄       state:present/absent       append:yes/no         yes:增量添加group         no:全量變更group,只設置groups指定的group組(默認)       remove:配合state=absent使用,刪除用戶的家目錄->remove=yes       expires:設置用戶的過期時間,值是一個時間戳 /*示例*/ ansible all -m user -a "name=test2 state=present groups=test2,test_grp shell=/bin/bash append=yes" 11.ping 用來測試遠程主機的運行狀態 /*示例*/ ansible all -m ping 172.18.19.188 | SUCCESS => { "changed": false, "ping": "pong" } ansible-doc Usage: ansible-doc [-l|-s] [options] [-t <plugin type] [plugin] plugin documentation tool Options: -a, --all **For internal testing only** Show documentation for all plugins. -h, --help show this help message and exit -l, --list List available plugins -M MODULE_PATH, --module-path=MODULE_PATH prepend colon-separated path(s) to module library (default=[u'/root/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']) -s, --snippet Show playbook snippet for specified plugin(s) -t TYPE, --type=TYPE Choose which plugin type (defaults to "module") -v, --verbose verbose mode (-vvv for more, -vvvv to enable connection debugging) --version show program's version number and exit ansible-doc command ===>會打印出command模塊的使用幫助 4.Playbook playbook是由一個或多個“play”組成的列表,可以讓它們聯同起來按事先編排的機制執行;所謂task無非是調用ansible的
  一個module,而在模塊參數中可以使用變量;模塊執行是冪等的,這意味着多次執行是安全的,因為其結果均一致。 執行模型:task list中的各任務按次序逐個在hosts中指定的所有主機上執行,即在所有主機上完成第一個任務后再開始第二個。
  在順序運行某playbook時,如果中途發生錯誤,所有已執行任務都將回滾,因此,在修改playbook后重新執行一次即可; task組成:每個task都應該有其name,用於playbook的執行結果輸出,建議其內容盡可能清晰地描述任務執行步驟。
如果未提供name,則action的結果將用於輸出; notify指定handler的執行機制:“notify”這個action可用於在每個play的最后被觸發,在notify中列出的操作稱為handler,
僅在所有的變化發生完成后一次性地執行指定操作。
5.實踐ansible自動化安裝nginx 首先、配置 /etc/hosts: IP test 第二、配置 /etc/ansible/hosts: [hadoop] test 第三、創建目錄 mkdir -p /ansible/roles/nginx/{defaults,files,handlers,meta,tasks,templates,vars} 第四、編輯文件 在files目錄下創建shell安裝腳本,並將nginx的壓縮包也放到files下面 install_nginx.sh: #!/bin/bash yum -y install zlib zlib-devel openssl openssl-devel pcre-devel groupadd -r nginx useradd -s /sbin/nologin -g nginx -r nginx cd /tmp tar xf nginx-1.9.9.tar.gz;cd nginx-1.9.9 mkdir /var/run/nginx/;chown nginx.nginx /var/run/nginx/ ./configure \ --prefix=/usr \ --sbin-path=/usr/sbin/nginx \ --conf-path=/etc/nginx/nginx.conf \ --error-log-path=/var/log/nginx/error.log \ --pid-path=/var/run/nginx/nginx.pid \ --user=nginx \ --group=nginx \ --with-http_ssl_module make && make install sed "/^\s*index / i proxy_pass http://localhost:8080;" /etc/nginx/nginx.conf /usr/sbin/nginx 在tasks目錄中放置main.yml文件 main.yml: - name: copy nginx_tar_gz to client copy: src=nginx-1.9.9.tar.gz dest=/tmp/nginx-1.9.9.tar.gz - name: copy install_shell to client copy: src=install_nginx.sh dest=/tmp/install_nginx.sh - name: install nginx shell: /bin/bash /tmp/install_nginx.sh 在ansible目錄下放置webservice.yml文件 webservice.yml: - hosts: hadoop remote_user: root roles: - nginx 第五、執行 cd /ansible ansible-playbook webservice.yml 目錄結構解析如下: roles/ \\ ansible所有的信息都放到此目錄下面對應的目錄中 └── nginx \\ 角色名稱 ├── default \\ 為當前角色設定默認變量時使用此目錄,應當包含一個main.yml文件; ├── files \\ 存放有copy或script等模塊調用的文件 ├── handlers \\ 此目錄應當包含一個main.yml文件,用於定義各角色用到的各handler ├── meta \\ 應當包含一個main.yml,用於定義角色的特殊設定及其依賴關系;1.3及以后版本支持 ├── tasks \\ 至少包含一個名為main.yml的文件,定義了此角色的任務列表,可使用include指令 ├── templates \\ template模塊會自動在此目錄中尋找Jinja2模板文件 └── vars \\ 應當包含一個main.yml文件,用於定義此角色用到的變量 ├───────────├──────────────────────────────────────────────────────────────────│ │ 目錄名 │ 說明 │ ├───────────├──────────────────────────────────────────────────────────────────│ ├defaults │ 為當前角色設定默認變量時使用此目錄,應當包含一個main.yml文件 │ ├───────────├──────────────────────────────────────────────────────────────────│ ├handlers │此目錄中應當包含一個main.yml文件,用於定義此角色用到的各handler, │ │ │ 在handler中使用include包含的其它的handler文件也應該位於此目錄中 │ ├───────────├──────────────────────────────────────────────────────────────────│ ├meta │ 應當包含一個main.yml文件,用於定義此角色的特殊設定及其依賴關系 │ ├───────────├──────────────────────────────────────────────────────────────────│ ├tasks │ 至少應該包含一個名為main.yml的文件,其定義了此角色的任務列表, │ │ │ 此文件可以使用include包含其它的位於此目錄中的task文件 │ ├───────────├──────────────────────────────────────────────────────────────────│ ├templates │ template模塊會自動在此目錄中尋找Jinja2模板文件 │ ├───────────├──────────────────────────────────────────────────────────────────│ ├vars │ 定義當前角色使用的變量 │ ├───────────├──────────────────────────────────────────────────────────────────│ ├files │ 存放由copy或script等模塊調用的文件 │ ├───────────├──────────────────────────────────────────────────────────────────│ ├tests │ 在playbook中角色的使用樣例 │ ├───────────├──────────────────────────────────────────────────────────────────│ --- - name: create user hosts: remote remote_user: root gather_facts: false vars: - say: "tiger" tasks: - name: Copy file to client # copy: src=/tmp/tiger dest=/tmp/tigress template: src=/tmp/tiger dest=/tmp/{{ say }} ansible-playbook -i /root/xxx.cfg /root/app/main.yml --limit "lala_xxx" -e "user=wawo" 解析: -i 指定要運行的配置文件 --limit 指定運行的ip地址 -e 指定運行的外部參數 運行的控制 YAML 文件為: /root/app/main.yml --- - hosts: all roles: - xxx hosts指定所有(all)的主機,但是由於在外部已經指定了主機的配置,所以all由外部指定參數來進行 roles指定要執行的具體劇本 roles的任務執行順序 ### 首先執行meta下的main.yml文件內容 可以設置該role和其它role之前的關聯關系。 dependencies ### 然后執行tasks下的main.yml文件內容 ### 用到的變量,會直接加載defaults/vars目錄下的main.yml文件 ### 用到的需要拷貝到遠程機器的文件,會放到files目錄下 ### 用到模板文件,會放到 templates 目錄下 ### 在執行的task中,使用了notify后,會調用 handlers 目錄下的main.yml文件
記錄一些基本的使用模塊
1、ansible中的include, include_tasks 和 import_tasks 的差別 include 被 deprecated(不建議使用)了. 建議使用 include_tasks 和 import_tasks include_tasks 是動態的: 在運行時展開. when只應用一次. 被include的文件名可以使用變量. import_tasks 是靜態的: 在加載時展開. when在被import的文件里的每個task, 都會重新檢查一次. 因為是加載時展開的,
文件名的變量不能是動態設定的. 請確保文件名中使用到的變量被定義在vars中、vars_files中、或者extra
-vars中,靜態的import不支持其他方式傳入的變量。 When using static includes, ensure that any variables used in their names are defined in

vars/vars_files or extra-vars passed in from the command line. Static includes cannot use
variables from inventory sources like group or host vars. 除了上述不同之處,在使用"循環操作""條件判斷"時,"include_tasks""import_tasks"也有很多不同點需要注意,注意點如下。 如果想要對包含的任務列表進行循環操作,則只能使用"include_tasks"關鍵字,不能使用"import_tasks"關鍵字,
"import_tasks"並不支持循環操作, 也就是說,使用"loop"關鍵字或"with_items"關鍵字對include文件進行循環操作時,只能配合"include_tasks"才能正常運行。 when關鍵字對"include_tasks""import_tasks"的實際操作有着本質區別,區別如下: 當對"include_tasks"使用when進行條件判斷時,when對應的條件只會應用於"include_tasks"任務本身,
當執行被包含的任務時,不會對這些被包含的任務重新進行條件判斷。 當對
"import_tasks"使用when進行條件判斷時,when對應的條件會應用於被include的文件中的每一個任務,當執行被包含的任務時,
會對每一個被包含的任務進行同樣的條件判斷。
對於tags和handler 與
"include_tasks"不同,當為"import_tasks"添加標簽時,tags是針對被包含文件中的所有任務生效的,與"include"關鍵字的效果相同。 "include_tasks""import_tasks"都可以在handlers中使用,並沒有什么不同,不過在當前2.7.0版本中,如果在handlers

中使用"import_tasks"引用任務列表,會出現bug,期待修復。 tasks: - include_tasks: file: in.yml apply: tags: - t1 tags: always 2、setup模塊用於收集遠程主機的一些基本信息。而在playbook中,默認參數 ” gather_facts: True ” 的含義就是在遠程主機
運行setup模塊,並將收集的信息記錄起來。 gather_facts: False 不使用遠程主機的setup模塊, tasks:
- set_fact: mode=1 設置遠程主機的參數 mode=1 3、一點疑惑 --- - hosts: webserver vars: logserver: 10.127.2.170 gather_facts: True tasks: - name: add conf to config files to CentOS6 lineinfile: dest=/etc/rsyslog.conf line="*.* @{{ logserver }}" when: ansible_distribution == 'CentOS' and ansible_distribution_major_version == "6" - name: restart syslog @CentOS6 when: ansible_distribution == 'CentOS' and ansible_distribution_major_version == "6" service: name=rsyslog state=restarted - name: add conf to config files to RedHat 5 lineinfile: dest=/etc/syslog.conf line="*.* @{{ logserver }}" when: ansible_distribution == 'RedHat' and ansible_distribution_major_version == "5" - name: restart syslog @RedHat 5 when: ansible_distribution == 'RedHat' and ansible_distribution_major_version == "5" service: name=syslog state=restarted 有同學要問,為什么要進行四次when判斷,兩次不就夠了,寫成這樣 - name: restart syslog @CentOS6 when: ansible_distribution == 'CentOS' and ansible_distribution_major_version == "6" lineinfile: dest=/etc/rsyslog.conf line="*.* @{{ logserver }}" service: name=rsyslog state=restarted 這是不行的,ansible要求每一個play里面只能使用一個模塊,使用多個會報錯 ERROR: multiple actions specified in task 4、tasks/main.yml 里面有如下行: - name: Configure Tomcat server template: src=server.xml dest=/usr/share/tomcat/conf/ notify: restart tomcat - name: Configure Tomcat user template: src=tomcat-users.xml dest=/usr/share/tomcat/conf/ notify: restart tomcat template模塊官方的解釋為: Templates a file out to a remote server. 大概意思就是當 src=config_file
這些文件發生變化的時候,觸發notify的動作 templates目錄就是存放這些文件用的(一般都是一些配置文件) handlers目錄里有一個main.yml文件,就是用來執行notify動作的 大概的流程為: templates
/config_file 發生變化 --> 觸發notify: action --> action定義在 handlers/main.yml 中 notify后面的動作名字必須與handlers/main.yml里面的name后面的名字一致,例: - name: Configure Tomcat user template: src=tomcat-users.xml dest=/usr/share/tomcat/conf/ notify: restart tomcat handlers: - name: restart tomcat service: name=tomcat state=restart 而files目錄下存放的是一些腳本, 通過copy模塊可以transport到remote hosts上的,而后觸發notify動作之后執行的腳本 5、語法驗證 ● 在執行playbook之前,最后好進行驗證,確保內容無誤 $ ansible-playbook --syntax-check site.yml playbook: site.yml ● 語法失敗時將會報告錯誤(無法堅持模塊內參數是否正確) 6、執行空運行 ● -C選項。這會使ansible報告在執行該playbook時將會發生什么更改,但不會對受管主機進行任何實際更改 $ ansible-playbook -C site.yml 7、特權升級屬性: ● 特提供額外的屬性,從而在playbook內定義特權升級參數。 become布爾值參數可用於啟動或禁用特權升級,無論在ansible配置文件如何定義 become: Ture/False ● 如果啟用了特權升級,可以使用become_method屬性來定義play期間所要使用的特權升級的方法sudo become_method: sudo 此外,啟用特權升級時,become_user屬性可以定義play上下文內用於特權升級的用戶 become_user: root 8、用戶屬性: ● playbook中的任務通常通過網絡連接多受管主機執行。與臨時命令相同,用於這些任務執行的用戶賬號取決於ansible配置文件
/etc/ansible/ansible.cfg中的參數。執行任務的用戶可以通過remote_user參數定義,不過,如果啟用了特權升級,
become_user等其他參數也會發生作用 ● 如果用於任務執行的Ansible配置中定義的遠程用戶不合適,可以通過在play中使用remote_user屬性覆蓋 remote_user: devops
9、修改文件 lineinfile 用於檢測文件是否存在特殊行或者使用后端正則表達式來替換匹配到的特殊行 10、replace lineinfile的多行匹配版本,此模塊會在文件中插入一段內容,並在內容開始和結束位置設置標簽,后續可以使用標簽可以
對此塊內容進行操作 path參數:必須參數,指定要操作的文件,
2.3版本之前,只能使用dest, destfile, name指定要操作的文件,2.4版本中,
仍然可以使用這些參數名,這些參數名作為path參數的別名使用。 regexp參數:必須參數,指定一個python正則表達式,文件中與正則匹配的字符串將會被替換。 replace參數: 指定最終要替換成的字符串。 backup參數:是否在修改文件之前對文件進行備份,最好設置為yes。
### 在ml2_conf.ini文件的[ml2]和[ml2_type_vlan]字段之間插入一段內容 - name: Enable ovn in neutron-server replace: dest: "{{ node_config_directory }}/neutron-server/ml2_conf.ini" regexp: '\[ml2\][\S\s]*(?=\[ml2_type_vlan\])' replace: |+ [ml2] type_drivers = local,flat,vlan,geneve tenant_network_types = geneve mechanism_drivers = ovn extension_drivers = port_security overlay_ip_version = 4 [ml2_type_geneve] vni_ranges = 1:65536 max_header_size = 38 [ovn] ovn_nb_connection = tcp:{{ api_interface_address }}:{{ ovn_northdb_port }} ovn_sb_connection = tcp:{{ api_interface_address }}:{{ ovn_sourthdb_port }} ovn_l3_mode = False ovn_l3_scheduler = chance ovn_native_dhcp = True neutron_sync_mode = repair backup: yes when: - action == "deploy" - inventory_hostname in groups['network'] notify: - Restart neutron-server container 11、ini_file ini后綴格式文件修改 ini文件是十分常見的一種配置文件,ansible內置了ini配置文件的管理模塊,用於對文件進行配置項的管理。 Ø 修改配置文件/root/demo.ini,selection為cron的選項組的crontime選項,把cron的值修改為10。 ansible all –m ini_file –a “dest=/root/demo.ini section=cron option=crontime value=10### 設置l3_agent.ini文件[DEFAULT]字段的external_network_bridge選項值為br-ex - name: Set the external network bridge vars: agent: "{{ 'neutron-vpnaas-agent' if enable_neutron_vpnaas | bool else 'neutron-l3-agent' }}" ini_file: dest: "{{ node_config_directory }}/{{ agent }}/l3_agent.ini" section: "DEFAULT" option: "external_network_bridge" value: "{{ neutron_bridge_name | default('br-ex') }}" backup: yes when: - action == "deploy" - inventory_hostname in ovn_central_address delegate_to: "{{ item }}" with_items: "{{ groups['neutron-server'] }}" notify: - Restart {{ agent }} container 12、循環控制 with_items 標准循環,用於執行重復任務,{{ item }}類似宏展開 - name: add several users user: name: "{{ item.name }}" state: present groups: "{{ item.groups }}" with_items: - { name: 'testuser1', groups: 'wheel' } - { name: 'testuser2', groups: 'root' } with_nested 嵌套循環 ### 修改neutron-server組所有主機的ml2_conf.ini文件的對應字段值 - name: Enable ovn in neutron-server vars: params: - { section: 'ml2', option: 'type_drivers', value: 'local,flat,vlan,geneve' } - { section: 'ml2', option: 'tenant_network_types', value: 'geneve' } - { section: 'ml2', option: 'mechanism_drivers', value: 'ovn' } - { section: 'ml2', option: 'extension_drivers', value: 'port_security' } - { section: 'ml2', option: 'overlay_ip_version', value: '4' } - { section: 'securitygroup', option: 'enable_security_group', value: 'True' } ini_file: dest: "{{ node_config_directory }}/neutron-server/ml2_conf.ini" section: "{{ item[0].section }}" option: "{{ item[0].option }}" value: "{{ item[0].value }}" backup: yes when: - action == "deploy" - inventory_hostname in ovn_central_address delegate_to: "{{ item[1] }}" with_nested: - "{{ params }}" - "{{ groups['neutron-server'] }}" notify: - Restart neutron-server container 13、流程控制 tags 設置任務標簽 tasks: - yum: name={{ item }} state=installed with_items: - httpd - memcached tags: - packages - template: src=templates/src.j2 dest=/etc/foo.conf tags: - configuration ### 執行playbook可以指定只執行標簽對應任務或跳過標簽對應任務 # ansible-playbook example.yml --tags "configuration,packages" # ansible-playbook example.yml --skip-tags "notification" 14、failed_when 用來控制playbook退出 - name: Check if firewalld is installed command: rpm -q firewalld register: firewalld_check failed_when: firewalld_check.rc > 1 when: ansible_os_family == 'RedHat' 15、pre_tasks/post_tasks 用來設置在執行roles模塊之前和之后需要執行的任務 16、wait_for 等待一個端口變得可用或者等待一個文件變得可用 - local_action: wait_for port=22 host="{{ ansible_ssh_host | default(inventory_hostname) }}" search_regex=OpenSSH delay=10

#等待openssh啟動,10s檢查一次 - name: Wait for container ssh wait_for: port: "22" delay: "{{ ssh_delay }}" search_regex: "OpenSSH" host: "{{ ansible_host }}" delegate_to: "{{ physical_host }}" register: ssh_wait_check until: ssh_wait_check | success retries: 3 when: - (_mc is defined and _mc | changed) or (_ec is defined and _ec | changed) - not is_metal | bool tags: - common-lxc 17、執行shell命令 ### ignore_errors為true表示命令執行出錯也不會退出playbook - name: Check if clean is needed command: docker exec openvswitch_vswitchd ovs-vsctl br-exists br-tun register: result ignore_errors: True 18、切換用戶 ### 使用become會先切換成apache用戶,再執行command命令,默認become_user用戶為root ### (如果你ansible配置的就是root用戶的免密碼登入那就不需要become了) - name: Run a command as the apache user command: somecommand become: true become_user: apache 檢測鏈表是否為空 ### pip_wheel_install為鏈表變量 - name: Install wheel packages shell: cd /tmp/wheels && pip install {{ item }}* with_items: - "{{ pip_wheel_install | default([]) }}" when: pip_wheel_install > 0 19、when中使用jinja2 when表達式中不建議直接使用{{}}的方式來獲取變量值,如果變量是字符串可以使用管道操作| string來獲取變量值 - name: Checking free port for OVN vars: service: "{{ neutron_services[item.name] }}" wait_for: host: "{{ hostvars[inventory_hostname]['ansible_' + api_interface]['ipv4']['address'] }}" port: "{{ item.port }}" connect_timeout: 1 state: stopped when: - container_facts[ item.facts | string ] is not defined - service.enabled | bool - service.host_in_groups | bool with_items: - { name: "ovn-nb-db-server", port: "{{ ovn_northdb_port }}", facts: "ovn_nb_db" } - { name: "ovn-sb-db-server", port: "{{ ovn_sourthdb_port }}", facts: "ovn_sb_db" } 20、uri web訪問,類似執行curl命令 uri模塊主要用於發送HTTP協議,通過使用uri模塊,可以讓目標主機向指定的網站發送如Get、Post這樣的HTTP請求,
並且能得到返回的狀態碼。
- name: test proxy URL for connectivity uri: url: "{{ repo_pkg_cache_url }}/acng-report.html" method: "HEAD" register: proxy_check failed_when: false tags: - common-proxy 21、local_action 將任務放在ansible控制主機(運行ansible-playbook的主機)上執行 - name: Check if the git cache exists on deployment host local_action: module: stat path: "{{ repo_build_git_cache }}" register: _local_git_cache when: repo_build_git_cache is defined 22、When語句官方文檔 在有的時候play的結果依賴於變量、fact或者是前一個任務的執行結果,從而需要使用到條件語句。 有的時候在特定的主機需要跳過特定的步驟,例如在安裝包的時候,需要指定主機的操作系統類型,
或者是當操作系統的硬盤滿了之后,需要清空文件等 在ansible中,我們可以使用如下比較運算符:
== :比較兩個對象是否相等,相等為真 != :比較兩個對象是否不等,不等為真 > :比較兩個值的大小,如果左邊的值大於右邊的值,則為真 < :比較兩個值的大小,如果左邊的值小於右邊的值,則為真 >= :比較兩個值的大小,如果左邊的值大於右邊的值或左右相等,則為真 <= :比較兩個值的大小,如果左邊的值小於右邊的值或左右相等,則為真 邏輯運算符: and :邏輯與,當左邊與右邊同時為真,則返回真 or :邏輯或,當左邊與右邊有任意一個為真,則返回真 not :取反,對一個操作體取反 ( ) :組合,將一組操作體包裝在一起,形成一個較大的操作體 判斷變量 defined :判斷變量是否已經定義,已經定義則返回真 undefind :判斷變量是否已經定義,未定義則返回真 none :判斷變量值是否為空,如果變量已經定義,但是變量值為空,則返回真 判斷執行結果 success 或 succeeded:通過任務的返回信息判斷任務的執行狀態,任務執行成功則返回真 failure 或 failed:通過任務的返回信息判斷任務的執行狀態,任務執行失敗則返回真 change 或 changed:通過任務的返回信息判斷任務的執行狀態,任務執行狀態為changed則返回真 skip 或 skipped:通過任務的返回信息判斷任務的執行狀態,當任務沒有滿足條件,而被跳過執行時,則返回真 判斷路徑的使用方式: file : 判斷路徑是否是一個文件,如果路徑是一個文件則返回真 directory :判斷路徑是否是一個目錄,如果路徑是一個目錄則返回真 link :判斷路徑是否是一個軟鏈接,如果路徑是一個軟鏈接則返回真 mount:判斷路徑是否是一個掛載點,如果路徑是一個掛載點則返回真 exists:判斷路徑是否存在,如果路徑存在則返回真 在2.6及以后的版本,支持直接寫下面的關鍵字;2.5之前的版本需要在前面加 isis not 判斷字符串: lower:判斷包含字母的字符串中的字母是否是純小寫,字符串中的字母全部為小寫則返回真 upper:判斷包含字母的字符串中的字母是否是純大寫,字符串中的字母全部為大寫則返回真 判斷整除 even :判斷數值是否是偶數,是偶數則返回真 odd :判斷數值是否是奇數,是奇數則返回真 divisibleby(num) :判斷是否可以整除指定的數值,如果除以指定的值以后余數為0,則返回真 subset:判斷一個list是不是另一個list的子集,是另一個list的子集時返回真 superset: 判斷一個list是不是另一個list的父集,是另一個list的父集時返回真 string:判斷對象是否是一個字符串,是字符串則返回真 number:判斷對象是否是一個數字,是數字則返回真 下面的例子表示為使用when語句,如下: tasks: - name: "shutdown Debian flavored systems" command: /sbin/shutdown -t now when: ansible_os_family == "Debian" 也可以使用括號來表示一組條件,如下所示: tasks: - name: "shutdownCentOS6andDebian7systems" command: /sbin/shutdown -t now when: (ansible_distribution == "CentOS" and ansible_distribution_major_version == "6") or (ansible_distribution == "Debian" and ansible_distribution_major_version == "7") 假設需要忽略一個語句的錯誤,根據執行的結果是成功還是失敗從而執行不同的命令,如下(使用的是jinja2的過濾): tasks: - command: /bin/false 沒有 - name 時,此行將被默認成為標題-- TASK: [command: /bin/false] register: result ignore_errors: True - command: /bin/something when: result|failed - command: /bin/something_else when: result|succeeded - command: /bin/still/something_else when: result|skipped 當接收到一個變量是一個字符串的時候,然后想做一個數字的比較,那么可以使用如下的方式
(在這個例子中遠程主機上需要有lsb_package包): tasks:
- shell: echo "only on Red Hat 6, derivatives, and later" when: ansible_os_family == "RedHat" and ansible_lsb.major_release|int >= 6 在playbooks中或者inventory清單中定義的變量也是可以使用,假設任務的執行依賴於一個布爾變量,如下: vars: epic: true 條件執行如下所示: tasks: - shell: echo "This certainly is epic!" when: epic 或者使用如下形式: tasks: - shell: echo "This certainly isn't epic!" when: not epic 如果需要的變量沒有定義,那么可以skip或者使用jinja2的defined如下所示: tasks: - shell: echo "I've got '{{ foo }}' and am not afraid to use it!" when: foo is defined - fail: msg="Bailing out. this play requires 'bar'" when: bar is undefined 當結合使用when和with_items的時候,需要注意的是when語句會對每個item進行單獨的處理,如下所示: tasks: - command: echo {{ item }} with_items: [ 0,2,4,6,8,10 ] when: item > 5 3、在roles中和include中使用when 當幾個任務都是使用相同的條件的時候,那么可以將條件寫在include之中,那么當寫在include的時候,
每個任務都會去判斷條件,如下所示:
- include: tasks/sometasks.yml when: "'reticulatingsplines'inoutput" 或者在roles中使用,如下: - hosts: webservers roles: - { role:debian_stock_config,when:ansible_os_family == 'Debian' } 4、條件導入 在playbook中可能會根據一些特定的標准從而做不同的事情,在一個playbook中工作在不同的平台和os版本是最好的例子 如下的例子表示,在centos和debian中apache的包是不同的,從而可以使用以下: --- - hosts: all remote_user: root vars_files: - "vars/common.yml" - [ "vars/{{ansible_os_family}}.yml","vars/os_defaults.yml" ] tasks: - name: make sure apache is running service: name={{ apache }} state=running 另外,在變量文件中只包含key和values,如下: --- # for vars/CentOS.yml apache: httpd somethingelse: 42 如何工作的呢? 當操作系統為centos的時候,那么會加載變量/vars/centos.yml,當文件不存在的時候,那么會加載defaults.yml,

當沒有找到任何文件的時候,那么就會出錯。當操作系統為debian的時候,那么就會加載變量/vars/debian.yml,
沒有就加載defaults.yml 當使用整個功能的時候,在運行playbook之前必須先安裝facter或者ohai,也可以直接在playbook中使用如下所示:
# for facter ansible -m yum -a "pkg=facter state=present" ansible -m yum -a "pkg=ruby-json state=present" # for ohai ansible -m yum -a "pkg=ohai state=present" 5、基於變量選擇文件和模板 在有的時候,配置文件使用copy或者是template的時候,可能會依賴於變量。 下面的例子中表示使用template輸出一個配置文件,在centos和debian中不同,如下: - name: template a file template: src={{ item }} dest=/etc/myapp/foo.conf with_first_found: - files: - {{ ansible_distribution }}.conf - default.conf paths: - search_location_one/somedir/ - /opt/other_location/somedir/ 6、注冊變量 在playbook中可以使用變量的值便於其他的任務用到。 關鍵字register用來保存變量值,整個變量可以使用在template中,動作行中,或者是when語句中,如下所示: - name: test play hosts: all tasks: - shell: cat /etc/motd register: motd_contents - shell: echo "motd contains the word hi" when: motd_contents.stdout.find('hi') != -1 注冊的變量值可以用stdout得到,或者用with_items得到,也可以使用stdout_lines得到, 如下所示: - name: registered variable usage as a with_items list hosts: all tasks: - name: retrieve the list of home directories command: ls /home register: home_dirs - name: add home dirs to the backup spooler file: path=/mnt/bkspool/{{ item }} src=/home/{{ item }} state=link with_items: home_dirs.stdout_lines # same as with_items: home_dirs.stdout.split() 23、文件組裝模塊——assemble(主要用於把多份配置文件片段組裝成一個配置文件) Ø 將/root/demo下的片段組裝后放到/root/target目錄下 ansible all –m assemble –a “dest=/root/demo src=/root/target” 24、文件拉取模塊——fetch Ø 將遠端主機的/etc/salt/minion文件收集回服務器/root/demo目錄下 ansible all –m fetch –a “dest=/root/demo src=/etc/salt/minion “ 25、文件管理模塊——file file 如果是directory,那么則會創建文件夾 link 如果是file,則會創建文件 state 默認值:file directory 如果是link,則會創建鏈接 hard 如果是hard,則會創建硬鏈接 touch 如果是touch,則會創建文件 absent 如果是absent,則會刪除文件 26、unarchive模塊 用於解壓文件,模塊包含如下選項: copy:在解壓文件之前,是否先將文件復制到遠程主機,默認為yes。若為no,則要求目標主機上壓縮包必須存在。 creates:指定一個文件名,當該文件存在時,則解壓指令不執行 dest:遠程主機上的一個路徑,即文件解壓的路徑 grop:解壓后的目錄或文件的屬組 list_files:如果為yes,則會列出壓縮包里的文件,默認為no,2.0版本新增的選項 mode:解決后文件的權限 src:如果copy為yes,則需要指定壓縮文件的源路徑 owner:解壓后文件或目錄的屬主 示例如下: - unarchive: src=foo.tgz dest=/var/lib/foo - unarchive: src=/tmp/foo.zip dest=/usr/local/bin copy=no - unarchive: src=https://example.com/example.zip dest=/usr/local/bin copy=no 27、fail 用於終止當前playbook的執行,通常與條件語句組合使用,當滿足條件時,終止當前play的運行。可以直接由failed_when取代。 'changed_when'關鍵字的作用是在條件成立時,將對應任務的執行狀態設置為changed 選項只有一個: msg:終止前打印出信息 示例: - fail: msg="The system may not be provisioned according to the CMDB status." when: cmdb_status != "to-be-staged" 28、pause 在playbook執行的過程中暫停一定時間或者提示用戶進行某些操作 常用參數: minutes:暫停多少分鍾 seconds:暫停多少秒 prompt:打印一串信息提示用戶操作 示例: - name: wait on user input pause: prompt="Warning! Detected slight issue. ENTER to continue CTRL-C a to quit" - name: timed wait pause: seconds=30 29、關於 async 和 poll 有的任務執行起來卻不那么直接,可能會花比較長的時間,甚至可能會比ssh的超時時間還要長。這種情況任務是不是沒法執行了? ansible考慮到了這種情況,官方文檔介紹了這個問題的解決方法,就是讓下發的任務執行的連接變為異步:任務下發之后,
長連接不再保持,而是每隔一段時間輪詢結果,直到任務結束。 他們在playbook的任務中加入兩個參數: async和poll async參數值代表了這個任務執行時間的上限值。即任務執行所用時間如果超出這個時間,則認為任務失敗。
此參數若未設置,則為同步執行。 poll參數值代表了任務異步執行時輪詢的時間間隔。如果poll為0,就相當於一個不關心結果的任務。 官方給出例子:
---- hosts: all remote_user: root tasks: - name: simulate long running op (15 sec), wait for up to 45 sec, poll every 5 sec command: /bin/sleep 15 async: 45 poll: 5 如果還想要更方便地看輪詢結果,ansible還提供了這個模塊async_status。 --- # Requires ansible 1.8+ - name: 'YUM - fire and forget task' yum: name=docker-io state=installed async: 1000 poll: 0 register: yum_sleeper - name: 'YUM - check on fire and forget task' async_status: jid={{ yum_sleeper.ansible_job_id }} register: job_result until: job_result.finished retries: 30 第一個job執行異步任務,並且注冊了一個名字叫yum_sleeper,用於提供給第二個job作為輪詢對象,並且poll設為0,它自己不再輪詢。 第二個job使用async_status模塊,進行輪詢並返回輪詢結果。准備檢查30次。結果如下: PLAY [all] ********************************************************************* TASK [setup] ******************************************************************* ok: [cloudlab001] TASK [YUM - fire and forget task] ********************************************** ok: [cloudlab001] TASK [YUM - check on fire and forget task] ************************************* FAILED - RETRYING: TASK: YUM - check on fire and forget task (29 retries left). FAILED - RETRYING: TASK: YUM - check on fire and forget task (28 retries left). FAILED - RETRYING: TASK: YUM - check on fire and forget task (27 retries left). FAILED - RETRYING: TASK: YUM - check on fire and forget task (26 retries left). FAILED - RETRYING: TASK: YUM - check on fire and forget task (25 retries left). FAILED - RETRYING: TASK: YUM - check on fire and forget task (24 retries left). changed: [cloudlab001] PLAY RECAP ********************************************************************* cloudlab001 : ok=3 changed=1 unreachable=0 failed=0 --- - hosts: all gather_facts: no tasks: - shell: "ls /opt" register: returnvalue - debug: var: returnvalue 30、debug 調試模塊,用於在調試中輸出信息 常用參數: msg:調試輸出的消息,不能與var同時使用 var:將某個任務執行的輸出作為變量傳遞給debug模塊,debug會直接將其打印輸出,不能與msg同時使用 verbosity:debug的級別(默認是0級,全部顯示,如果設置為3時,會在 -vvv 時打印出信息) 31、內置變量 groups 配置文件 justtest 如下: 10.1.1.60 justtest.zsythink.net ansible_host=10.1.1.70 test71 anisble_host=10.1.1.71 [testA] test60 ansible_host=10.1.1.60 test61 ansible_host=10.1.1.61 [testB] justtest ansible_host=10.1.1.70 [test:children] testA testB 上述清單中,顯式的指定了三個組,testA組、testB組、test組,其中,testA組與testB組是test組的子組,
除了組中的主機,還有三台主機沒有任何分組,直接寫在了清單中。 現在,我們獲取一下groups變量的值,看看會返回哪些信息,隨便操作清單中的任意一台主機即可,示例如下
# ansible justtest -m debug -a "msg={{groups}}" justtest | SUCCESS => { "changed": false, "msg": { "all": [ "10.1.1.60", "justtest.zsythink.net", "test71", "test60", "test61", "justtest" ], "test": [ "test60", "test61", "justtest" ], "testA": [ "test60", "test61" ], "testB": [ "justtest" ], "ungrouped": [ "10.1.1.60", "justtest.zsythink.net", "test71" ] } } 從上述返回信息可以看出,所有主機默認被分成了組名為"all"的組,testA組中有兩台主機,testB組中有一台主機,

由於testA組和testB組都屬於test組的子組,所以testA組與testB組中的主機都屬於test組,
由於有三台主機在清單中並未分組,所以,ansible自動將沒有分組的主機分到了名為"ungrouped"的組中,即組名為"未分組"的組。 我們還能夠通過組名,獲取到指定組的分組信息,假設,我想要獲取到上例中test組中的主機名稱,則可以使用如下方法。 # ansible justtest -m debug -a "msg={{groups.test}}" # ansible justtest -m debug -a "msg={{groups['test']}}" # ansible justtest -m debug -a "msg={{groups.ungrouped}}" 32、handlers模塊 之 meta模塊 --- - hosts: justtest remote_user: root tasks: - name: task1 file: path=/testdir/testfile state=touch notify: handler1 - name: task2 file: path=/testdir/testfile2 state=touch notify: handler2 - meta: flush_handlers - name: task3 file: path=/testdir/testfile3 state=touch notify: handler3 handlers: - name: handler1 file: path=/testdir/ht1 state=touch - name: handler2 file: path=/testdir/ht2 state=touch - name: handler3 file: path=/testdir/ht3 state=touch 如上例所示,我在task1與task2之后寫入了一個任務,我並沒有為這個任務指定name屬性,這個任務使用meta模塊,
meta任務是一種特殊的任務,meta任務可以影響ansible的內部運行方式,上例中,meta任務的參數值為flush_handlers,
"meta: flush_handlers"表示立即執行之前的task所對應handler,什么意思呢? 意思就是,在當前meta任務之前,一共有兩個任務,task1與task2,它們都有對應的handler,
當執行完task1與task2以后,立即執行對應的handler,而不是像默認情況那樣在所有任務都執行完畢以后
才能執行各個handler,那么我們來實際運行一下上述劇本,運行結果如下
33、handlers模塊 之 立即執行 我們還可以在一個task中一次性notify多個handler,怎樣才能一次性notify多個handler呢? 你可能會嘗試將多個handler使用相同的name,但是這樣並不可行,因為當多個handler的name相同時,只有一個handler會被執行。 所以,我們並不能通過這種方式notify多個handler,如果想要一次notify多個handler,則需要借助另一個關鍵字,它就是'listen'。 你可以把listen理解成"組名",我們可以把多個handler分成"",當我們需要一次性notify多個handler時,

只要將多個handler分為"一組",使用相同的"組名"即可,當notify對應的值為"組名"時,""內的所有handler都會被notify,
這樣說可能還是不容易理解,我們來看個小示例,示例如下
--- - hosts: justtest remote_user: root tasks: - name: task1 file: path=/testdir/testfile state=touch notify: handler group1 handlers: - name: handler1 listen: handler group1 file: path=/testdir/ht1 state=touch - name: handler2 listen: handler group1 file: path=/testdir/ht2 state=touch 34、tags模塊 --- - hosts: justtest remote_user: root tags: httpd tasks: - name: install httpd package tags: ['package'] yum: name=httpd state=latest - name: start up httpd service tags: - service - always # 意思是service的tag總會被執行 service: name: httpd state: started 當tags寫在play中而非task中時,play中的所有task會繼承當前play中的tags,而上例中,兩個任務都會繼承httpd標簽,
同時還有擁有自己的標簽。 ansible
-playbook --tags package,service testhttpd.yml 或者 ansible-playbook --tags httpd testhttpd.yml 執行的結果是一樣的,都會將兩個標簽進行執行 其實,ansible還預置了5個特殊tag,這5個特殊tag分別為: always never(2.5后的版本才有) tagged untagged all always:當我們把任務的tags的值指定為always時,那么這個任務就總是會被執行,除非你使用'--skip-tags'
選項明確指定不執行對應的任務, --skip-tags always 如果存在多個tag標記了 always,我們只想跳過某一個, 那么可以使用 --skip-tags service never:在2.5版本的ansible中,引入了新的特殊標簽 'never', 從字面上理解,never的作用應該與always正好相反,由於我當前使用的ansible版本為2.4(還沒有引入never標簽),
所以當指定任務的標簽為never時,貌似被ansible當做了自定義標簽,所以如果你安裝了2.5版本的ansible,
可以嘗試一下never標簽的作用,由於還沒有實際使用過2.5版本,所以此處暫時不進行示例。 ansible
-playbook --tags tagged testtag.yml 上述命令表示只執行有標簽的任務,沒有任何標簽的任務不會被執行。 ansible-playbook --skip-tags tagged testtag.yml 上述命令表示跳過包含標簽的任務,即使對應的任務包含always標簽,也會被跳過。 ansible-playbook --tags untagged testtag.yml 上述命令表示只執行沒有標簽的任務,但是如果某些任務包含always標簽,那么這些任務也會被執行。 特殊標簽all表示所有任務會被執行,不用指定,默認情況下就是使用這個標簽。 35、變量-vars 使用 vars 可以在當前的play中設置變量 --- - hosts: all vars: v: wawo tasks: - name invoke v file: path: /home/{{ v }} state: touch 也可以定義屬性: --- - hosts: all vars: nginx: conf80: /etc/nginx/conf.d/80.conf conf8080: /etc/nginx/conf.d/8080.conf tasks: - name invoke v file: path: "{{ nginx.conf80 }}" == "{{ nginx['conf80'] }}" 兩個方式等價 state: touch 此處的變量增加了 " 引號,原因是使用變量是出於開頭的位置。 在playbook中參數賦值,可以使用 : 也可以使用 = 。當使用 = 進行賦值時,就不需要考慮使用 " 引號了。 但是要使用 : 冒號時,就需要在緊鄰參數的那個變量處添加 " 引號。 path={{ nginx.conf80 }} path: /home/{{ v }} 在實際使用中,我們提倡"變量文件分離",可以通過 vars_files 關鍵字引入文件 vars_files: - /testdir/ansible/nginx_vars.yml - yaml_file_path - include_vars: file: /testdir/ansible/testfile name: get_var 將文件中的所有變量賦值給get_var extensions: [yaml,yml,json,varfile] 指定包含的文件 depth: 1 指定目錄深度 files_matching: "^var_.*" 指定過濾條件 ignore_files: ["^var_.*",varintest.yaml] 忽略過濾條件 36、變量-注冊變量 register --- - hosts: justtest remote_user: root tasks: - name: test shell shell: "echo test > /var/testshellfile" register: testvar - name: shell module return values debug: var: testvar testvar會將shell執行的結果進行保存 37、提示用戶操作-交互操作 --- - hosts: 192.168.43.130 vars_prompt: - name: "your_name" prompt: "What is your name" private: no # 可以讓輸入可見,不加此屬性看不到對應的輸入信息,適用於密碼 - name: "your_age" prompt: "How old are you" tasks: - name: output vars debug: msg: Your name is {{your_name}},You are {{your_age}} years old. 還可以使用選擇的形式的如下: vars_prompt: - name: "solution" prompt: "Choose the solution you want \n A: solutionA\n B: solutionB\n C: solutionC\n" private: no default: A 38、外部設置變量-通過 -e 和 --extra-vars 通過對應的文件設置變量,調用時,需要使用 @file_path 進行引用 ansible-playbook cmdvar.yml -e "@/testdir/ansible/testvar.yml" 39、set_fact 定義變量 --- - hosts: justtest remote_user: root tasks: - set_fact: testvar: "testtest" - debug: msg: "{{testvar}}" 40、循環 with_list、with_items、with_flattened、with_together、with_cartesian --- - hosts: justtest remote_user: root gather_facts: no vars: dirs: - "/opt/a" - "/opt/b" - "/opt/c" - "/opt/d" tasks: - file: path: "{{item}}" state: touch with_items: "{{dirs}}" --- - hosts: justtest remote_user: root gather_facts: no tasks: - debug: msg: "{{item}}" with_items: [ 1, 2, 3 ] || with_items: - 1 - 2 - 3 with_list 經過with_list處理后,每個嵌套在大列表中的小列表都被當做一個整體存放在item變量中,最終被debug作為一個
小整體輸出了,而不會像with_items一樣將小列表
"展開拉平"后一並將小列表中的元素循環輸出。 with_flattened 拉平展開,與with_list基本一致 with_together with_together可以將兩個列表中的元素"對齊合並",單單用語言來描述,不是特別容易理解,不如來看一個小示例,
示例playbook如下:
--- - hosts: justtest remote_user: root gather_facts: no tasks: - debug: msg: "{{ item }}" with_together: - [ 1, 2, 3 ] - [ a, b, c ] 結果: TASK [debug] ****************************** ok: [justtest] => (item=[1, u'a']) => { "changed": false, "item": [ 1, "a" ], "msg": [ 1, "a" ] } ok: [justtest] => (item=[2, u'b']) => { "changed": false, "item": [ 2, "b" ], "msg": [ 2, "b" ] } ok: [justtest] => (item=[3, u'c']) => { "changed": false, "item": [ 3, "c" ], "msg": [ 3, "c" ] } with_cartesian 是將兩個列表進行笛卡爾積組合 與 with_nested 使用一致 --- - hosts: justtest remote_user: root gather_facts: no tasks: - file: state: directory path: "/testdir/testdir/{{ item.0 }}/{{ item.1 }}" with_cartesian: - [ a, b, c ] - [ test1, test2 ] "with_indexed_items"應該與"索引"有關,沒錯,"with_indexed_items"的作用就是在循環處理列表時為

列表中的每一項添加"數字索引""索引"從0開始 with_sequence start=1 end=5 stride=1 從1到5,每次增加1 --- - hosts: justtest remote_user: root gather_facts: no tasks: - debug: msg: "{{ item }}" with_sequence: start=1 end=5 stride=1 with_file 獲取主機文件的內容的 41、有幾種分隔符。默認的Jinja分隔符配置如下: {% ... %} 對於聲明 可以直接將 for等語句放到里面執行 {{ ... }} 對於表達式打印到模板輸出 {# ... #} for Comments不包含在模板輸出中 # ... ## 對於行語句 42、過濾器 變量可以通過過濾器修改。過濾器通過管道符號(|)與變量分隔,並且在括號中可以包含可選參數。可以鏈接
多個過濾器。一個過濾器的輸出應用於下一個過濾器。 abs 絕對值 capitalize 首字母大寫 center 將值集中在給定寬度的字段中 default 設置默認值 first 返回序列中第一項 float 轉化成浮點型的數據 join {{ [
1, 2, 3]|join('|') }} -> 1|2|3 {{ [1, 2, 3]|join }} -> 123 last 返回序列中最后一項 length == count 返回長度 max {{ [1, 2, 3]|max }} min {{ [1, 2, 3]|min }} pprint 優雅打印 replace(s, old, new, count=None) {{ "Hello World"|replace("Hello", "Goodbye") }} reverse 反向打印 round round(value,precision = 0,method ='common' ) 將數字四舍五入到給定的精度。 第一個參數指定精度(默認為0),第二個參數指定舍入方法: 'common' 向上或向下舍入 'ceil' 總是圍捕 'floor' 總是四舍五入 safe(值) 將值標記為安全,這意味着在啟用了自動轉義的環境中,此變量不會被轉義。 sort 對可迭代進行排序。 默認情況下,它會按升序排序,如果您將其作為第一個參數傳遞,它將反轉排序。 string 如果尚未創建字符串unicode。這樣,標記字符串不會轉換回unicode。 striptags(值) 剝離SGML / XML標記並將相鄰的空格替換為一個空格。 sum 返回數字序列的總和加上參數'start'的值(默認為0)。當序列為空時,它返回start。 title 返回值的標題版本。即單詞將以大寫字母開頭,所有剩余字符均為小寫。 trim #將字符串開頭和結尾的空格去除 truncate 返回字符串的截斷副本。 unique 去重 upper #將字符串轉換成純大寫 wordcount 計算該字符串中的單詞。 --- - hosts: justtest remote_user: root vars: testvar: "abc123ABC 666" testvar1: " abc " testvar2: '123456789' testvar3: "1a2b,@#$%^&" tasks: - debug: #將字符串轉換成純大寫 msg: "{{ testvar | upper }}" - debug: #將字符串轉換成純小寫 msg: "{{ testvar | lower }}" - debug: #將字符串變成首字母大寫,之后所有字母純小寫 msg: "{{ testvar | capitalize }}" - debug: #將字符串反轉 msg: "{{ testvar | reverse }}" - debug: #返回字符串的第一個字符 msg: "{{ testvar | first }}" - debug: #返回字符串的最后一個字符 msg: "{{ testvar | last }}" - debug: #將字符串開頭和結尾的空格去除 msg: "{{ testvar1 | trim }}" - debug: #將字符串放在中間,並且設置字符串的長度為30,字符串兩邊用空格補齊30位長 msg: "{{ testvar1 | center(width=30) }}" - debug: #返回字符串長度,length與count等效,可以寫為count msg: "{{ testvar2 | length }}" - debug: #將字符串轉換成列表,每個字符作為一個元素 msg: "{{ testvar3 | list }}" - debug: #將字符串轉換成列表,每個字符作為一個元素,並且隨機打亂順序 #shuffle的字面意思為洗牌 msg: "{{ testvar3 | shuffle }}" - debug: #將字符串轉換成列表,每個字符作為一個元素,並且隨機打亂順序 #在隨機打亂順序時,將ansible_date_time.epoch的值設置為隨機種子 #也可以使用其他值作為隨機種子,ansible_date_time.epoch是facts信息 msg: "{{ testvar3 | shuffle(seed=(ansible_date_time.epoch)) }}" 43、lookup 模塊 從2.5版本開始,官方加入了 loop 關鍵字進行循環操作,來代替 with_xxx 的風格 而實際上內部的操作,就是 loop + lookup 的操作 在說循環和lookup插件之間的關系,需要注意,不要錯誤的以為lookup插件只能實現循環操作, lookup插件有很多,有的lookup插件與"循環操作"完全沒有關系, lookup類型的插件的主要作用是訪問外部的數據源, 比如,獲取到外部數據並賦值給某個變量,以便之后使用這些數據,lookup插件的操作都是在ansible主機中進行的,
與目標主機沒有關系。 以
"with_"開頭的循環實際上就是"with_""lookup()"的組合,lookup插件可以作為循環的數據源,通過以上描述,
應該已經明白了我們之前總結的循環與各種lookup插件之間的關系了吧。
--- - hosts: justtest remote_user: root gather_facts: no tasks: - debug: msg: "{{ lookup('file','/testdir/testfile') }}" 獲得/testdir/testfile文件的內容 - debug: 可以獲得多個文件的內容 msg: "{{ lookup('file','/testdir/testfile','/testdir/testfile1') }}" - debug: 對每個文件內容單獨放在一個列表中 2.5 版本中添加的 msg: "{{ lookup('file','/testdir/testfile','/testdir/testfile1',wantlist=true) }}" - debug: 2.6版本中引入的錯誤判讀 msg: "{{ lookup('file','/testdir/testfile',errors='ignore') }}" #errors的值需要使用引號引起,errors的值可以設置為ignore、warn或者strict,缺省值為strict 通過ansible-doc -t lookup -l命令查看到如下列表, 我們可以通過使用lookup + 下面的列表信息進行組合使用。 cartesian 返回列表的笛卡爾積 chef_databag 從Chef的databag獲取數據 consul_kv 從consul鍵值存儲中獲取元數據。 credstash 在AWS上檢索信用卡的秘密 csvfile 從TSV或CSV文件讀取數據 cyberarkpassword 從CyberArk AIM獲得秘密 dict 從字典返回鍵/值對項 dig 使用DNSPython庫查詢DNS dnstxt 查詢域的DNS TXT字段 env 讀取環境變量的值 etcd 從ETCD服務器獲取信息 file 讀取文件內容 fileglob 與模式匹配的列表文件 filetree 遞歸地匹配目錄樹中的所有文件 first_found 返回從列表中找到的第一個文件 flattened 返回完全展開的單個列表 hashi_vault 從HasiHCORP拱頂中找回秘密 hiera 從HIELA數據獲取信息 indexed_items 重寫列表以返回“索引項目” ini 從INI文件讀取數據 inventory_hostnames 與主機模式匹配的庫存主機列表 items 項目清單 keyring 從操作系統鑰匙抓取秘密 lastpass 從LASTPASS獲取數據 lines 從命令讀取行 list 簡單地返回它所給予的。 mongodb 從MangGDB查找信息 nested 用其他列表的嵌套元素編寫列表 password 檢索或生成隨機密碼,存儲在文件中 passwordstore 使用passwordstore.org的通行工具管理密碼 pipe 從命令讀取輸出 random_choice 從列表返回隨機元素 redis_kv 從ReDIS獲取數據 sequence 基於數字序列生成列表 shelvefile 從Python擱置文件讀取密鑰 subelements 從字典列表中遍歷嵌套密鑰 template 用Jinja2 檢索模板后的文件內容 together 將列表合並為同步化列表 url 從URL返回內容 44、block rescue always的使用 block 塊,可以將多個任務整合到一起執行,可以添加條件判斷 when 進行判斷執行 --- - hosts: justtest remote_user: root tasks: - debug: msg: "task1 not in block" - block: - debug: msg: "task2 in block1" - debug: msg: "task3 in block1" when: 2 > 1 rescue 捕獲 block 的執行失敗任務(任意一個失敗都會觸發rescue) 執行block成功不會觸發rescue --- 不使用 rescue --- - hosts: justtest - hosts: justtest remote_user: root remote_user: root tasks: tasks: - block: - shell: 'ls /ooo' - shell: 'ls /ooo' register: return_value rescue: ignore_errors: true - debug: - debug: msg: 'I caught an error' msg: "I caught an error" when: return_value is failed 我們能從中看到,使用這種方式的便捷性 always 不管怎么樣都要執行的一部分 說到這里是不是感覺到了,有一種 try{}catch{}finally{} 的感覺了? --- - hosts: justtest remote_user: root tasks: - block: - debug: msg: 'I execute normally' - command: /bin/false - debug: msg: 'I never execute, due to the above task failing' rescue: - debug: msg: 'I caught an error' - command: /bin/false - debug: msg: 'I also never execute' always: - debug: msg: "This always executes" 44、關於使用Jinja2 template 模塊 為什么要用到 Jinja2 主要是為了根據變量動態生成適合的模板 比如多機器下的模板,可以根據不同的機器設置變量來生成對應的模板,並推送到對應的機器上。 就是為了方便快捷。 Jinja2的語法是由 variables (變量)和 statement (語句)組成,如下: 1、variables:可以輸出數據 ` my_variables ` {{ some_dudes_name | capitalize }} 可以使用過濾器進行變量使用 2、statements: 可以用來創建條件和循環等 if語句: {% if my_conditional %} 進行語句判斷 ... {% endif %} for 語句: {% for item in all_items %} `item` …… {% endfor %} 生成文件內容 # cat test.j2 # cat test jinja2 test jinja2 test {{ 3 + 2 }} 5 {{ 3 - 4 }} -1 {{ 3 * 5 }} 15 {{ 2 ** 3 }} 8 {{ 7 / 5 }} 1.4 {{ 7 // 5 }} 1 {{ 17 % 5 }} 2 生成文件內容 # cat test.j2 # cat test jinja2 test jinja2 test {{ 1 in [1,2,3,4] }} True {{ 1 not in [1,2,3,4] }} False 條件: {% if 條件一 %} ... {% elif 條件N %} ... {% else %} ... {% endif %} 設置值: {% set teststr='abc' %} {{ teststr }} 循環: {% for 迭代變量 in 可迭代對象 %} {{ 迭代變量 }} {% endfor %} # cat test.j2 jinja2 test {% for i in [3,1,7,8,2] %} {% for i in [3,1,7,8,2] -%} 在for結束前加 - {{ i }} {{ i }}{{ ' ' }} {% endfor %} {%- endfor %} endfor開始前加 - 可以避免換行 # cat test.j2 # cat test.j2 jinja2 test jinja2 test {% for i in [3,1,7,8,2] -%} {% for key,val in {'name':'bob','age':18}.iteritems() %} {{ i~' ' }} ~ 就是字符串連接符 {{ key ~ ':' ~ val }} {%- endfor %} {% endfor %} loop.index 當前循環操作為整個循環的第幾次循環,序號從1開始 loop.index0 當前循環操作為整個循環的第幾次循環,序號從0開始 loop.revindex 當前循環操作距離整個循環結束還有幾次,序號到1結束 loop.revindex0 當前循環操作距離整個循環結束還有幾次,序號到0結束 loop.first 當操作可迭代對象中的第一個元素時,此變量的值為true loop.last 當操作可迭代對象中的最后一個元素時,此變量的值為true loop.length 可迭代對象的長度 loop.depth 當使用遞歸的循環時,當前迭代所在的遞歸中的層級,層級序號從1開始 loop.depth0 當使用遞歸的循環時,當前迭代所在的遞歸中的層級,層級序號從0開始 loop.cycle() 這是一個輔助函數,通過這個函數我們可以在指定的一些值中進行輪詢取值,具體參考之后的示例 {% for i in [7,1,5,3,9] if i>3 %} {% for i in [7,1,5,3,9] %} {{ i ~'----'~ loop.index }} {% if loop.index is even %} {% endfor %} {%continue%} {%endif%} {% for i in [7,1,5,3,9] %} {{ i ~'----'~ loop.index }} {% if i>3 %} {% endfor %} {{ i ~'----'~ loop.index}} {% endif %} {% endfor %} ansible all -m template -a "src=test.j2 dest=/opt/test" 可以在結果:cat /opt/test jinja2 test False True abc 3 1 7 8 2

 參考的網站較多,不一一列舉了,但有一個重要的參考是下面的這位的。

http://www.zsythink.net/archives/category/%e8%bf%90%e7%bb%b4%e7%9b%b8%e5%85%b3/ansible/

 

 

 

 

 

===========================END===========================


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM