一:ansible劇本 1:簡介 一系列ansible命令的集合,使用yaml語言進行編寫的,從上往下的執行,支持很多的特性,比如,將某個命令的狀態作為變數給其他的任務執行,變數,迴圈,判斷,錯誤糾正,可以是一個playbook或者是多個playbook執行 2:yaml基本語法 1、yaml約束 ...
一:ansible劇本
1:簡介
一系列ansible命令的集合,使用yaml語言進行編寫的,從上往下的執行,支持很多的特性,比如,將某個命令的狀態作為變數給其他的任務執行,變數,迴圈,判斷,錯誤糾正,可以是一個playbook或者是多個playbook執行
2:yaml基本語法
1、yaml約束
- 編寫yaml的時候,不能使用tab鍵,只能使用空格(vim版本有關係,vim會自動將tab轉化為4個空格鍵)
- 大小寫嚴格區別,大寫是大寫,小寫是小寫;大寫的變數和小寫的變數是不一樣的
- 使用縮進來表示一個層級關係
- 使用空格來表示層級關係,空格的數量沒有限制,使用#表示註釋
2、yaml數據類型
- 純量:類似於變數的值,最小的單位。無法進行切割
- 數組(序列、列表):一組有次序的值,每一個 ‘-’ 就是一個列表
- 對象(鍵值對)字典、哈希、映射:key=value
3:playbook
1、簡單的案例
[root@server mnt]# cat file1.yaml - name: touch file1 hosts: all tasks: - name: file: path: /opt/file1 state: touch
2、輸出的信息解讀
[root@server mnt]# ansible-playbook file1.yaml ##這個是playbook的名字 PLAY [touch file1] ************************************************************* #這個是playbook第一個任務,預設的任務。用於收集遠程主機的各種信息,Ip等 TASK [Gathering Facts] ********************************************************* ok: [client] #工作任務 TASK [file] ******************************************************************** changed: [client] #這個就是執行命令後的總結 PLAY RECAP ********************************************************************* client : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 #rc為0的話代表這個執行成功,不為0的話,代表這執行失敗了
可以使用-v查看詳細的信息,但是最多是4個v
3、playbook執行之前的檢查
在執行之前使用命令檢查一下
#檢查語法的問題(正常的情況下) [root@server mnt]# ansible-playbook file1.yaml --syntax-check playbook: file1.yaml #錯誤的情況 [root@server mnt]# ansible-playbook file1.yaml --syntax-check ERROR! A malformed block was encountered while loading tasks: {'-name': {'file': {'path': '/opt/file1', 'state': 'touch'}}} should be a list or None but is <class 'ansible.parsing.yaml.objects.AnsibleMapping'> The error appears to be in '/mnt/file1.yaml': line 1, column 3, but may be elsewhere in the file depending on the exact syntax problem. The offending line appears to be: - name: touch file1 ^ here #還有一個就是語法正確但是輸出有問題的情況下 使用-C就是模擬執行的,但不是真正的執行 [root@server mnt]# ansible-playbook file1.yaml -C PLAY [touch file1] ************************************************************************ TASK [Gathering Facts] ******************************************************************** ok: [client] TASK [file] ******************************************************************************* ok: [client] PLAY RECAP ******************************************************************************** client : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
4、多個劇本
很多個劇本寫在了一起,有很多的任務
[root@server mnt]# cat file1.yaml - name: touch file1 hosts: client tasks: - name: file: path: /opt/file1 state: touch - name: touch file2 hosts: server tasks: - name: file: path: /opt/file2 state: touch
5、playbook結構上
1)主機或者主機組
定義play的名字,遠程操作的主機,用戶,提權等相關的配置
2)變數
定義變數,然後輸出
[root@server mnt]# cat var.yaml - name: var hosts: client vars: RHCE: rhel9 tasks: - name: debug: var: RHCE
3)任務列表
就是tasks裡面的
4)handler(特殊的任務)
通過監聽某個task或者幾個task,然後這個tasks執行成功後,並且狀態是chaged,所有的任務執行成功後,最後再來執行
就是要先觸發再來執行
[root@server mnt]# cat var.yaml - name: var hosts: client tasks: - name: file1 file: path: /mnt/file1 state: touch notify: get_status handlers: - name: get_status file: path: /mnt/file2 state: touch #要使用notify這個參數,來進行監聽,類似與鍵值對的
必須是chaged才執行,並且其他的任務也沒有出現錯誤,才行,
6、劇本出現了錯誤
現象:
[root@server mnt]# cat var.yaml - name: var hosts: client tasks: - name: file1 shell: ls /opt/qwqwqwq - name: file2 file: path: /mnt/file2 state: touch #後面的命令不會執行
總結:
1、就是運行劇本到一半的時候,出現了錯誤,會立刻的停止(對於當前任務的主機二樣),不會影響到其他任務的主機(正常的運行),下次執行的話,就是從錯誤的地方開始執行
2、按照主機劃分,執行任務
3、具有冪等性,就是一次和多次執行的結果都一樣
如果期望值和執行的結果一致的話,則任務就是執行成功
如果任務執行的前後,內容上的修改(文件的時間戳也算),那麼再次執行的任務的話,就會發生覆蓋
解決的方法
1)命令模塊報錯
使用||,配置/usr/bin/true,讓rc的返回值為0
[root@server mnt]# cat var.yaml - name: var hosts: client tasks: - name: file1 shell: ls /opt/qwqwqwq || /usr/bin/true - name: file4 file: path: /mnt/file4 state: touch
2)其他模塊報錯
使用ignore_errors: true跳過錯誤的任務,繼續執行後面的任務
[root@server mnt]# cat var.yaml - name: var hosts: client tasks: - name: file1 shell: ls /opt/qwqwqwq ignore_errors: true - name: file3 file: path: /mnt/file3 state: touch 執行後,會報錯,但是執行了後面的創建file3的操作
3)handler方法
使用handler來規避錯誤,如果有任務錯誤的話,handler也不會執行,所有的任務執行完成後,才會執行handler的task任務,強制去執行handler任務,加上一個欄位force_handlers:yes即可
#中間有錯誤,依然執行 [root@server mnt]# cat var.yaml - name: var hosts: client force_handlers: yes tasks: - name: touch file10 file: path: /opt/file10 state: touch notify: get_status - name: chakan shell: ls /opt/121212 handlers: - name: get_status file: path: /opt/file11 state: touch #如果監聽的是錯誤了 那就無法執行了
二:ansibel變數的定義和引用
1:為什麼需要變數
ansible管理很多個主機的時候,就是他們的主機名或者順序不一樣什麼的,就需要使用變數來進行一個統一的管理,或者埠什麼的都可以使用變數來進行定義
1、變數有數字,字母,下劃線,組成,
2、變數名可以是字母,不能以數字開頭,嚴格區分大小寫
3、在自定義變數的時候,不要以ansible開頭,因為系統中有ansible開頭的變數
案例:
[root@server mnt]# cat v1.yml - name: use vars hosts: client vars: name_rhel: rhel9 tasks: - shell: touch "/opt/{{name_rhel}}" #使用vars來定義變數,然後使用{{}}來引用變數
4、調試變數的方式
需要使用debug模塊來進行調試,有2個參數來進行調試的,一個是var,另外一個是msg,2個不能一起使用
可以顯示出變數的內容,但是其他的模塊在執行劇本後,看不到返回的消息
msg:使用{{變數名}},來進行輸出內容
var:name_rhel,age_rhel可以輸出多個變數名
#專門列印變數的內容,無法列印信息 [root@server mnt]# cat v1.yml - name: use vars hosts: client vars: name_rhel: rhel9 tasks: - name: debug debug: var: name_rhel #mes可以列印內容,也可以列印信息 [root@server mnt]# cat v1.yml - name: use vars hosts: client vars: name_rhel: rhel9 tasks: - name: debug debug: msg: "this is a {{name_rhel}}"
2:主機清單中定義變數
內置變數:就是這些變數ansible都自定義好了的
ansible_become類似的,這種主機清單的優先順序比配置文件的優先順序高
1、定義主機變數
[root@server ansible]# cat hosts client webserver=nginx #使用ad-hoc調用debug模塊,列印變數 client | SUCCESS => { "webserver": "nginx" }
2、定義主機組變數
#使用[主機組:vars]這樣的方式來進行定義 [root@server ansible]# cat hosts client webserver=nginx [servers] client [servers:vars] rhelname=rhce 當然,如果主機組的變數和主機發生了衝突的話,以主機的優先順序高為主 [root@server ansible]# cat hosts client rhelname=nginx [servers] client [servers:vars] rhelname=rhce client | SUCCESS => { "rhelname": "nginx" }
3、通過主機和主機組的目錄文件定義變數
裡面定義的都是鍵值對的方式來定義的,註意格式的規範
與hosts文件在同一個路徑下創建2個目錄,然後主機名為命名的文件即可
主機的目錄hosts_vars,以主機名為命名的文件,只有該主機能夠引用變數
mkdir /etc/ansible/host_vars [root@server host_vars]# cat client name: rhel9 引用變數: [root@server host_vars]# ansible client -m shell -a 'echo "{{name}}"' client | CHANGED | rc=0 >> rhel9
主機組的目錄為group_vars,只有該主機組能引用變數
mkdir /etc/ansible/group_vars [root@server group_vars]# cat servers age: 100 #引用變數 [root@server group_vars]# ansible servers -m shell -a 'echo "{{age}}"' client | CHANGED | rc=0 >> 100
2、劇本中定義變數
1:vars來定義
就是在task任務之前定義即可,可以定義多次變數
就是定義了這個變數之後,然後可以不用重覆的寫這個內容,直接調用這個變數即可,代碼就省略了很多,但是呢,這個變數是已經寫死了的
#Vars定義,輸出出來 [root@server mnt]# cat v1.yaml - name: use vars hosts: client vars: rname: rhel9 rage: 80 tasks: - name: use shell shell: echo "{{rname}} {{rage}}" > /opt/file111 #使用debug模塊來進行引用變數 使用var來引用變數 [root@server mnt]# cat v1.yaml - name: use vars hosts: client vars: rname: rhel9 rage: rrr tasks: - name: use debug debug: var: rname,rage #使用msg來引用變數 [root@server mnt]# cat v1.yaml - name: use vars hosts: client vars: rname: rhel9 rage: rrr tasks: - name: use debug debug: msg: this is "{{rname}} {{rage}}\n"
2:vars_files來定義
引入外部的變數文件,外部的變數文件的內容是字典的形式,不能使用列表的形式(-)
使用vars_files這個參數
引用的話使用 鍵.鍵的方式來引用變數即可
文件的寫法 第一種寫法 [root@server mnt]# cat file1.yaml user: name: zhangshan age: 18 sex: boy 引用變數 #使用{{user.name}}這中鍵的方式來獲取值 [root@server mnt]# cat file1.yaml user: name: zhangshan age: 18 sex: boy [root@server mnt]# cat v1.yaml - name: use vars hosts: client vars_files: - /mnt/file1.yaml tasks: - name: shell shell: echo "{{user.name}}" >> /opt/file111 #第二種寫法,就是一個大的字典 [root@server mnt]# cat file2.yaml users: job: name: aaa age: 90 joe: name: bbb age: 80 #輸出 [root@server mnt]# cat v1.yaml - name: use vars hosts: client vars_files: - /mnt/file2.yaml tasks: - name: shell shell: echo "{{users.joe.name}}" >> /opt/file111
3:註冊變數
就是將一個任務的執行結果註冊為一個變數
使用關鍵字register去得到任務的執行結果
#就是如果使用劇本來執行任務的話,就是不顯示詳細的信息,可以使用註冊變數來讓其顯示詳細的信息,並且也可以按照指定的變數來進行輸出 [root@server mnt]# cat v1.yaml - name: use vars hosts: client tasks: - name: shell shell: ls /etc/passwd register: get_status - debug: var: get_status #執行這個劇本 ok: [client] => { "get_status": { "changed": true, "cmd": "ls /etc/passwd", "delta": "0:00:00.002357", "end": "2024-03-26 16:43:34.086058", "failed": false, "rc": 0, "start": "2024-03-26 16:43:34.083701", "stderr": "", "stderr_lines": [], "stdout": "/etc/passwd", "stdout_lines": [ "/etc/passwd" ] } } 可以指定要的變數 [root@server mnt]# cat v1.yaml - name: use vars hosts: client tasks: - name: shell shell: ls /etc/passwd register: get_status - debug: var: get_status.rc #一般應用的場景就是需要收集被控節點的信息,並且將其保存到主控節點上面 #使用這個變數來對其進行操作 [root@server mnt]# cat v1.yaml - name: use vars hosts: client tasks: - name: shell shell: ls /etc/passwd register: get_status - copy: content: "{{get_status.rc}}" dest: /opt/file222
案例:就是收集被控節點的信息,然後拷貝到主控節點上面去
先使用copy模塊,將信息存放到被控節點上面,在使用fetch模塊,將信息存放到主控節點上面
4:命令模式來定義變數
使用臨時定義的變數,非常的有用
[root@server mnt]# ansible --help|grep EXTRA_VARS [-e EXTRA_VARS] [--vault-id VAULT_IDS] -e EXTRA_VARS, --extra-vars EXTRA_VARS #使用劇本的方式,臨時定義變數 [root@server mnt]# cat v1.yaml - name: use vars hosts: client tasks: - name: file file: path: "{{path}}" state: touch #如果有多個變數的話,就使用雙引號加上逗號 [root@server mnt]# ansible-playbook v1.yaml -e "path=/opt/eeee" #使用ad-hoc來定義變數 [root@server mnt]# ansible client -m debug -a 'var=rhel_name' -e rhel_name=90 client | SUCCESS => { "rhel_name": "90" }
5:fact變數
事實變數,就是收集被控節點的主機的信息,然後定義一個變數,通過setup模塊可以收集被控節點的主機信息,然後通過setup模塊中中的facts參數,專門用來進行收集主機信息,通過這種方式收集到的信息稱為facts變數(事實變數)
ansible_facts變數中有很多的信息,主機名、網卡設備、ip地址、磁碟和磁碟空間、文件系統bios版本,架構等
[root@controller ansible]# ansible node1 -m setup| head -n 10 node1 | SUCCESS => { "ansible_facts": { "ansible_all_ipv4_addresses": [ "172.25.250.20" ], "ansible_all_ipv6_addresses": [ "fe80::20c:29ff:fea3:688c" ], "ansible_apparmor": { "status": "disabled" 很多的信息會被輸出來
執行playbook的時候,預設會收集被控節點的主機信息(gather facts)
1、可以使用filter進行過濾
註意的就是只能過濾出ansible_facts的下一層級的變數,如果有多個層級的話,不能進行收集
1)通過指定的方式進行收集
使用的變數名就是精確的
[root@controller ansible]# ansible node1 -m setup -a "filter=ansible_all_ipv4_addresses" node1 | SUCCESS => { "ansible_facts": { "ansible_all_ipv4_addresses": [ "172.25.250.20" ], "discovered_interpreter_python": "/usr/bin/python" }, "changed": false }
2)通過通配符來收集信息
[root@controller ansible]# ansible node1 -m setup -a "filter=ansible_*addresses" node1 | SUCCESS => { "ansible_facts": { "ansible_all_ipv4_addresses": [ "172.25.250.20" ], "ansible_all_ipv6_addresses": [ "fe80::20c:29ff:fea3:688c" ], "discovered_interpreter_python": "/usr/bin/python" }, "changed": false }
2、導出facts變數到文件,引用facts變數
就是將變數文件導出來,然後存放到被控節點上面去
直接的引用fact變數,因為的話,就是在執行劇本的時候,會預設的收集這些信息,直接輸出想要的信息即可
#導出facts變數 2鐘的方式進行導出 //保存到主控節點上面鵝,但是都是一行,不容易的看 [root@controller nod1-setup.txt]# ansible node1 -m setup --tree /opt/nod1-setup.txt 第二種方式直接使用重定向的,直接重定向到一個文件裡面去即可 [root@controller opt]# ansible node1 -m setup > /opt/node1setup.txt 內容不在同一行上,可以使用搜索 #引用變數 [root@controller opt]# cat facts.yml - name: facts hosts: node1 tasks: - debug: var: ansible_all_ipv4_addresses 直接使用debug模塊來進行引用
3、禁用facts變數的收集
預設是開啟的收集,禁用收集
[root@controller opt]# cat facts.yml - name: facts hosts: node1 gather_facts: no tasks: - debug: var: ansible_all_ipv4_addresses #再次執行這個劇本的時候,就會報錯,說沒有定義這個變數 ok: [node1] => { "ansible_all_ipv4_addresses": "VARIABLE IS NOT DEFINED!" }
使用模塊,還是能夠收集信息,但是這種情況不常見
[root@controller opt]# cat facts.yml - name: facts hosts: node1 gather_facts: no tasks: - setup: - debug: var: ansible_all_ipv4_addresses #執行劇本 ok: [node1] => { "ansible_all_ipv4_addresses": [ "172.25.250.20" ] }
4、自定義的facts事實變數
讓每一個主機都有自己的facts變數,每一個facts變數都是存放到/etc/ansible/facts.d目錄下
定義的格式有要求的:ini或者yaml,文件的尾碼必須是.fact結尾(ini格式的話就是網卡的配置文件的格式)
自定義facts事實變數
#創建一個自定義變數的文件,尾碼為fact結尾的 [root@controller mnt]# cat userinfo.fact [user] name = zhangsan age = 18 sex = boy #創建yaml文件 #並將變數文件文件拷貝過去 [root@controller mnt]# cat userinfo.yaml - name: user fact hosts: node1 tasks: - name: create dir file: path: /etc/ansible/facts.d state: directory - name: copy copy: src: /mnt/userinfo.fact dest: /etc/ansible/facts.d #引用變數自定義變數文件 #自定義變數文件使用ansible_local [root@controller mnt]# ansible node1 -m setup -a "filter=ansible_local" node1 | SUCCESS => { "ansible_facts": { "ansible_local": { "userinfo": { "user": { "age": "18", "name": "zhangsan", "sex": "boy" } } }, "discovered_interpreter_python": "/usr/bin/python" }, "changed": false }
6:set_fact模塊
可以生成一個變數,獲取不同主機的版本,然後可以使用shell模塊,來將這個變數的值保存到本地的主機上面去
將rhel和版本拼接在一起,然後賦值給另外的一個變數,直接引用這個fact事實變數,然後進行拼接
使用setup模塊,來進行拼接,賦值給一個變數,使用debug模塊來進行輸出 [root@controller mnt]# cat set.yaml - name: setfact hosts: node1 tasks: - set_fact: get_version: "{{ ansible_distribution }}--{{ansible_distribution_version}}" - debug: var: get_version ok: [node1] => { "get_version": "RedHat--9.0" }
7:lookup變數
都還是就需要set_fact這個模塊,來生成一個變數,來使用lookup這個參數,為這個變數賦值
在某些的情況下,需要引用外部的內容來作為內容,可以將主控節點的公鑰作為變數,然後傳輸到被控節上面去
lookup變數能夠從文件,命令,變數中作為變數的值
1、從文件中賦值變數
格式: get_passwd:"{{loopup('file','/etc/passwd')}}" 就是將主控節點這個值賦給了get_passwd這個變數
案例:
#將一個/etc/passwd這個文件拷貝到被控節點上面 #這個就是將這個文件裡面的內容都拷貝到這個被控節點的文件上面去了 [root@controller mnt]# cat look.yml - name: look hosts: node1 tasks: - set_fact: fff: "{{lookup('file','/etc/passwd')}}" - name: copy copy: content: "{{fff}}" dest: /opt/passwd
2、從命令中賦值給變數
格式:get_passwd:"{{lookup('pipe','date +%T')}}"
就是將命令的結果作為變數的值
#天劍一個用戶,並且密碼是redhat #使用這個來進行一個密碼的加密,然後賦值給pd這個變數名,最後使用user模塊,來引用這個變數 [root@controller mnt]# cat user.yml - name: create user hosts: node1 tasks: - name: passwd set_fact: pd: "{{lookup('pipe','openssl passwd -6 redhat')}}" - name: created user user: name: q1000 password: "{{pd}}"
3、從變數中賦值
就是從環境變數中進行賦值
#直接引用這個環境變數 #最後使用debug模塊來進行輸出 [root@controller mnt]# cat env.yml - name: env hosts: node1 tasks: - name: env set_fact: get_env: "{{lookup('env','HOME')}}" - name: debug debug: msg: "{{get_env}}" ok: [node1] => { "msg": "/root" }
8:魔法變數
就是內置的變數, 內置變數有特殊含義的就被稱為魔法變數
fact變數只有運行的主機才能夠調用,就是只想要所有的被控節點都調用node1的主機的主機名
1、hostvars
獲取指定主機的變數信息,可以使用主機名或者主機ip地址(主機清單和- hosts中指定都是ip地址才可以使用ip地址),讓所有的主機都獲取主機node1的主機名,說到底還是獲取的是facts變數的內容,setup模塊中的
案例:
#在很多的主機名中指定輸出一個主機名即可 [root@controller mnt]# cat magic.yaml - name: magic hosts: node1 tasks: - debug: msg: "{{hostvars['node1'].ansible_default_ipv4.address}}"
2、inventory_hostname
列出當前運行的任務的主機(常常和when判斷使用)
when是一個判斷語句,判斷是不是當前的主機名,如果不是則不執行任務,是的話,就執行任務
#當前的主機名是node1的話就執行 - name: magic hosts: node1 tasks: - debug: var: ansible_hostname when: inventory_hostname == 'node1' ok: [node1] => { "ansible_hostname": "node1" }
3、groups
groups列出當前主機清單中的所有的主機組,groups.all列出所有的主機(常用於迴圈)
groups.web列出web主機組的主機
案例:
#列出主機清單中的所有的主機組和主機 #groups就能列出所有的 - name: magic hosts: node1 tasks: - debug: var: groups #列出所有的主機 - name: magic hosts: node1 tasks: - debug: var: groups.all #列出web中的主機 - name: magic hosts: node1 tasks: - debug: var: groups.web
總結:
1、錯誤的補救的方法
ignore_errors,忽略這個錯誤,然後繼續執行下一個任務
handlers這個強制的使用,通過監聽的方式
2、變數
vars和vars_files,命令模式來定義變數這些都是只能定義一些普通的變數
facts就是收集被控節點的主機的信息
register註冊變數就是收集命令的執行結果返回的數據,可以指定結果的輸出
set_fact變數:就是可以生成一個變數,
可以根據fact變數直接調用,然後賦值
lookup直接賦值,就是直接使用一個值
自定義賦值
魔法變數的話:也是內置的變數,只不過有特殊含義的變數,可以輸出指定的主機名,指定的主機組,以及和when常常使用的能輸出當前的是哪一個主機組