我叫張賀,貪財好色。一名合格的LINUX運維工程師,專注於LINUX的學習和研究,曾負責某中型企業的網站運維工作,愛好佛學和跑步。
個人博客:傳送陣
筆者微信:zhanghe15069028807
,非誠勿擾。
ansible-playbook其實就是把ad-hoc寫成腳本的方式,一定要注意格式,playbook的格式非常嚴格
一、playbook基礎
語法
縮進:使用固定的網絡表示層級結構,每個縮進兩個空格,不能用tab
冒號:以冒號結尾除外,其他的冒號后面必須要有空格
短橫線:表示列表項,使用一個短橫加一個空格,多個項使用同樣的縮進級別作為同一列表
模塊在描述的下面就行,不能同級或超過,我們最好用兩個空格
示例
//示例
[root@ansible ansible]# vim test1.yml
- hosts: nfs #頂格杠,空格,hosts,冒號,空格,nfs
tasks: #開頭兩空格
- name: Install NFS #四空格
yum: name=nfs-utils state=present #六個空格
- name: Copy File exports
copy: src=/root/exports.template dest=/etc/exports
- name: Start Nfs Server
service: name=nfs-server state=started enabled=yes
//語法檢查
[root@ansible ansible]# ansible-playbook --syntax-check test1.yml
playbook: test1.yml
//模擬執行
[root@ansible ansible]# ansible-playbook -C test1.yml #+C模擬執行
//正式執行
[root@ansible ansible]# ansible-playbook test2.yml #這樣才會執行
PLAY [nfs] **************************************************************************************************************************************
TASK [Gathering Facts] **************************************************************************************************************************
ok: [192.168.80.188]
TASK [Create New File] **************************************************************************************************************************
changed: [192.168.80.188]
PLAY RECAP **************************************************************************************************************************************
192.168.80.188 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
示例2
一個play單個任務
[root@ansible ansible]# vim test2.yml
- hosts: nfs
tasks:
- name: Create New File
file: path=/tmp/test.txt state=touch owner=root group=root mode=400
//檢查語法,如果什么都不提示,那就說明沒有錯誤
[root@ansible ansible]# ansible-playbook --syntax-check test2.yml
playbook: test2.yml #只檢查語法,不會檢測內容
一個play多個任務
[root@ansible ansible]# cat test2.yml
- hosts: nfs
tasks:
- name: Create New File
file: path=/tmp/test.txt state=touch owner=root group=root mode=400
- name: Create New Directory
file: path=/tmp/oldboy_dir state=directory owner=root group=root mode=700
多個play,多個任務(不建議這樣用,建立多建一個文件)
[root@ansible ansible]# cat test2.yml
- hosts: nfs
tasks:
- name: Create New File
file: path=/tmp/test.txt state=touch owner=root group=root mode=400
- name: Create New Directory
file: path=/tmp/oldboy_dir state=directory owner=root group=root mode=700
- hosts: web
tasks:
- name: Create Web New File
file: path=/opt/test.txt owner=root group=root mode=400 state=touch
- name: Create Web New Directory
file: path=/opt/oldboy_dir state=directory owner=root group=root mode=700
[root@ansible ansible]# ansible-playbook --syntax-check test2.yml
playbook: test2.yml
二、playbook變量
變量定義
像上面,我們寫的腳本太長了,關鍵是因為路徑太長了,我們可以把路徑定義到變量,然后下面再引用, 這樣就好看多了。
變量的定義有三種方式:
-
在playbook文件里面定義;
-
外部定義,然后傳進來;
-
在hosts主機清單里面定義
-
單獨定義一個變量文件
在playbook文件里面定義:
[root@ansible ansible]# vim test2.yml #路徑變量
- hosts: nfs
vars: #這里定義了一個變量
file_name: /tmp/tt
tasks:
- name: Create New File
file: path={{ file_name }} state=touch owner=root group=root mode=400 #在這里引用了一下,雙花括號,兩邊有空格,注意這個格式
- name: Create New Directory #在name里面也可以引用變量,在執行的時候也會翻譯出來
file: path=/tmp/tt.dir state=directory owner=root group=root mode=700
主機清單里面定義:
[root@ansible ansible]# cat hosts #在主機清單里面的web再定一個同樣的變量
[web]
192.168.80.166
[web:vars] #注意這個格式,很少在這里定義,知道即可
file_name=temp.hosts
外部定義
//執行的時候會有一個報警,提示讓你不要用shell模塊
[root@ansible ansible]# ansible-playbook test4.yml --extra-vars "file_name=temp_extra"
在web上查看,外置優先生效,共次是playbook生效,最后才是主機清單生效
單獨用一個文件定義
[root@ansible ansible]# cat vars.yml
file_name: temp.vars
[root@ansible ansible]# cat test4.yml
- hosts: web
vars_files: #用關鍵字引用一下,在大型項目當中很常見
./vars.yml
tasks:
- name: Create New File name:{{ file_name }}
file: name=/opt/{{ file_name }} state=touch
變量注冊
假設我想在ansible上通過playbook查看web服務器當中開了哪些端口,怎么寫呢?先寫一個簡單的,如下所示:
[root@ansible ansible]# cat test5.yml
---
- hosts: web
tasks:
- name: show "ss -tnlp"
shell: ss -tnlp
[root@ansible ansible]# ansible-playbook test5.yml #並沒有輸出結果,只是顯示執行成功了。。
沒有輸入結果,只是顯示執行成功了,那怎么辦呢?其實方法很簡單,就將結果賦值給一個變量,然后我們把變量打印出來就好了,如下所示:
[root@ansible ansible]# cat test5.yml
---
- hosts: web
tasks:
- name: show "ss -tnlp"
shell: ss -tnlp | grep 22
register: Net_Status #將結果保存在Net_Status變量當中
- name: Output Status
debug: msg={{ Net_Status }} #debug模塊用於輸出函數
下面這是輸出,通過輸出我們看到還是比較亂的:
我們可以僅將標准輸出打印出來,別的東西都不要打印了,就在debug模塊后面的變量當中加一個stdout即可,如下所示:
[root@ansible ansible]# cat test5.yml
---
- hosts: web
tasks:
- name: show "ss -tnlp"
shell: ss -tnlp | grep 22
register: Net_Status
- name: Output Status
debug: msg={{ Net_Status.stdout }} #加在了這里
下面是輸出,沒有那么難看復雜了,但還是看着有點難看,如下所示:
再加一個按行顯示:
[root@ansible ansible]# cat test5.yml
---
- hosts: web
tasks:
- name: show "ss -tnlp"
shell: ss -tnlp | grep 22
register: Net_Status
- name: Output Status
debug: msg={{ Net_Status.stdout_lines }} #debug不是調試的意思,而是輸出的意思
用到什么地方呢?我們在啟動服務了之后,用這種方法檢查端口是否起來了
三、條件語句
playbook中的條件判斷用when
[root@ansible ansible]# ansible nfs -m setup #在ansible上取其它主機的變量
[root@ansible ansible]# vim test6.yml
---
- hosts: all
tasks:
- name: Create a File
file: path=/tmp/this_is_{{ ansible_hostname }} state=touch
when: (ansible_hostname == 'BACKUP') or (ansible_hostname == 'web1') #只有兩台主機才會執行
四、循環
假設說現在我們要安裝兩個軟件,給web1這個服務器上,怎樣用ansible-playbook寫呢?
[root@ansible ansible]# cat test7.yml #安裝兩個軟件,這么寫有點亂,我們下面再換一種寫法
---
- hosts: web
tasks:
- name: Install Wget Tree
yum:
name:
- wget
- tree
state: present
[root@ansible ansible]# cat test8.yml #這么寫看着舒服一點
---
- hosts: web
tasks:
- name: Install Wget Tree
yum: name=wget,tree state=present
除了這兩種寫法,我們還可以使用循環的方式去寫:
[root@ansible ansible]# cat test9.yml
---
- hosts: web
tasks:
- name: Install Wget Tree
yum: name={{ item }} state=present
with_items: #軟件可以專門用一個列表
- wget
- tree
---
- hosts: web
remote_user: root
tasks:
- name: Add Users
user: name={{ item.name }} groups={{ item.groups }} state=present
with_items:
- { name: 'test1',groups: 'bin' }
- { name: 'test2',groups: 'root' }
五、異常處理
默認playbook會檢查命令和模塊的返回狀態,如果遇到錯誤就中斷執行,加入參數ignore_errors:yes
會忽略錯誤,繼續向下執行。
[root@ansible ansible]# cat test8.yml
---
- hosts: web
tasks:
- name: Ignore False
command: /bin/false
ignore_errors: yes
- name: Service Nfs Server
service: name=nfs-server state=started enabled=yes
tags: start_nfs-server
六、打標記
假設說我們已經在playbook里面寫了20個任務,一執行在第15個報錯了,我們調試完了之后執行,又開始從頭開始執行,其實這樣沒有必要,我們只需要執行一下第15個就行了或者只需要從第15個之后開始執行,這就需要控制,這個控制就要通過打標記來實現。
[root@ansible ansible]# cat test8.yml
---
- hosts: web
tasks:
- name: Install Nfs
yum: name=nfs-utils state=present
tags: install_nfs
- name: Service Nfs Server
service: name=nfs-server state=started enabled=yes
tags: start_nfs-server
//正常執行
[root@ansible ansible]# ansible-playbook test8.yml
//使用-t指定tag執行,多個tag可以用逗號隔開
[root@ansible ansible]# ansible-playbook -t install_nfs test8.yml
//使用--skip-tags排除不執行的tags
[root@ansible ansible]# ansible-playbook --skip-tags install_nfs test8.yml
七、handlers
假如我們通過playbook給web更改了一個端口,更改端口當然是在配置文件里面更改,那么配置文件一更改,我們要通過cp模塊推送,推送過去之后因為當前目標服務已經是啟動狀態,並不會重啟,那你說我們直接在playbook里面寫一個重啟不就完了嗎?但是如果在playbook里面寫了重啟的話,以后每次觸發這個playbook都會重啟,這不合適,這時候就我們就需要通過notify
模塊監控配置文件如果改變了的話,就會觸發handlers任務,如下所示:
[root@ansible ansible]# cat test8.yml
---
- hosts: web
tasks:
- name: Install Httpd
yum: name=httpd state=present
tags: install_nfs
- name: configure httpd
copy: src=./httpd.conf.template dest=/etc/httpd/conf/httpd.conf backup=yes
notify: Restart Httpd
- name: Started Htttpd
service: name=httpd state=started enabled=yes
handlers:
- name: Restart Httpd
service: name=httpd state=restarted
八、template
上面我們是手工在ansible上面更改了httpd的配置文件的端口之后,然后推送到web,有沒有辦法使得我們不用手工更改,只需要在playbook里面定義一個變量,然后在在配置文件的端口的地方引用這個變量, 這時我們就要通過template模塊替換cp模塊,cp模塊是見什么推送什么,而template模塊就把配置文件里面的變量先翻譯過來,然后再推送到web端。
[root@ansible ansible]# cat test8.yml
---
- hosts: web
#1、定義一個變量,在httpd的配置文件當中引用
vars:
http_port: 8080
#2.安裝httpd
tasks:
- name: Install Httpd
yum: name=httpd state=present
tags: install_nfs
#3、使用template模板,翻譯引用上述定義的變量
- name: configure httpd
template: src=./httpd.conf.template dest=/etc/httpd/conf/httpd.conf backup=yes
notify: Restart Httpd
#4、啟動httpd
- name: Started Htttpd
service: name=httpd state=started enabled=yes
#5、檢查httpd的啟動和當前狀態
- name: Get httpd status
shell: netstat -tnlp|grep httpd
register: Httpd_Port
#6、輸出變量到面板
- name: output httpd status
debug: msg={{ Httpd_Port.stdout_lines }}
ignore_errors: yes
#7、如果配置文件發生變化會調用handlers下面的模板
handlers:
- name: Restart Httpd
service: name=httpd state=restarted
[root@ansible ansible]# vim ./httpd.conf.template
Listen {{ http_port }}
九、include
include是調用任務的方式,把多個playbook名字的引用到一個文件里面,這樣一執行這個文件,就會把引用到的所有的文件給執行了,如下所示:
[root@ansible ansible]# cat main.yml
- hosts: all
tasks:
- include_tasks: test1.yml
- include_tasks: test2.yml
[root@ansible ansible]# cat test1.yml
- name: create file1
command: touch file1
[root@ansible ansible]# cat test2.yml
- name: create file2
command: touch file2