Ansible playbook 編程詳解與各種小案例 主機規劃 添加用戶賬號 說明: 1、 運維人員使用的登錄賬號; 2、 所有的業務都放在 /app/ 下「yun用戶的家目錄」,避免業務數據亂放; 3、 該用戶也被 ansible 使用,因為幾乎所有的生產環境都是禁止 root 遠程登錄的(因此 ...
Ansible playbook 編程詳解與各種小案例
主機規劃
添加用戶賬號
說明:
1、 運維人員使用的登錄賬號;
2、 所有的業務都放在 /app/ 下「yun用戶的家目錄」,避免業務數據亂放;
3、 該用戶也被 ansible 使用,因為幾乎所有的生產環境都是禁止 root 遠程登錄的(因此該 yun 用戶也進行了 sudo 提權)。
1 # 使用一個專門的用戶,避免直接使用root用戶 2 # 添加用戶、指定家目錄並指定用戶密碼 3 # sudo提權 4 # 讓其它普通用戶可以進入該目錄查看信息 5 useradd -u 1050 -d /app yun && echo '123456' | /usr/bin/passwd --stdin yun 6 echo "yun ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers 7 chmod 755 /app/
Ansible 配置清單Inventory
之後文章都是如下主機配置清單
1 [yun@ansi-manager ansible_info]$ pwd 2 /app/ansible_info 3 [yun@ansi-manager ansible_info]$ cat hosts_key 4 # 方式1、主機 + 埠 + 密鑰 5 [manageservers] 6 172.16.1.180:22 7 8 [proxyservers] 9 172.16.1.18[1:2]:22 10 11 # 方式2:別名 + 主機 + 埠 + 密碼 12 [webservers] 13 web01 ansible_ssh_host=172.16.1.183 ansible_ssh_port=22 14 web02 ansible_ssh_host=172.16.1.184 ansible_ssh_port=22 15 web03 ansible_ssh_host=172.16.1.185 ansible_ssh_port=22
條件判斷-when
when 判斷在 ansible 任務中的使用頻率非常高。
例如判斷主機是否已經安裝指定的軟體包;對機器的操作系統進行判斷然後再根據不同的方法「yum或apt等」進行軟體包安裝;根據操作系統的版本判斷進行軟體包的安裝「是安裝MySQL還是Mariadb」等。
示例:根據主機名的不同,下載不同的文件
1 [yun@ansi-manager object04]$ pwd 2 /app/ansible_info/object04 3 [yun@ansi-manager object04]$ ll 4 total 4 5 -rw-rw-r-- 1 yun yun 950 Oct 26 10:22 test_when.yml 6 [yun@ansi-manager object04]$ cat test_when.yml 7 --- 8 # 根據 hostname 的不同下載不同的圖片 9 # 特殊組 all,對所有機器有效 10 - hosts: all 11 12 tasks: 13 - name: "download picture jvm-01-01.png" 14 get_url: 15 url: http://www.zhangblog.com/uploads/jvm/jvm-01-01.png 16 dest: /tmp/ 17 when: ansible_hostname == "ansi-haproxy01" 18 19 - name: "download picture jvm-01-02.png" 20 get_url: 21 url: http://www.zhangblog.com/uploads/jvm/jvm-01-02.png 22 dest: /tmp/ 23 when: ansible_hostname == "ansi-haproxy02" 24 25 - name: "other download picture jvm-01-03.png" 26 get_url: 27 url: http://www.zhangblog.com/uploads/jvm/jvm-01-03.png 28 dest: /tmp/ 29 # 從 facts 中獲取的變數,ansible_facts['ansible_hostname'] != "ansi-haproxy01" 錯誤寫法;ansible_hostname != "ansi-haproxy01" 正確寫法 30 #when: (ansible_hostname != "ansi-haproxy01") and (ansible_hostname != "ansi-haproxy02") # 寫法一 31 #或者如下3行 列表之間關係是 (and 與) 等同於上一行 32 #when: 33 # - ansible_hostname != "ansi-haproxy01" 34 # - ansible_hostname != "ansi-haproxy02" 35 #when: ansible_hostname is not match "ansi-haproxy0*" # 寫法二 36 when: (ansible_hostname is match "ansi-manager") or (ansible_hostname is match "ansi-web*") # 寫法三 37 38 [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key --syntax-check test_when.yml # 語法檢測 39 [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key -C test_when.yml # 預執行,測試執行 40 [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key test_when.yml # 執行 41 42 PLAY [all] ******************************************************************************************************* 43 44 TASK [Gathering Facts] ******************************************************************************************* 45 ok: [web01] 46 ok: [web02] 47 ok: [web03] 48 ok: [172.16.1.180] 49 ok: [172.16.1.181] 50 ok: [172.16.1.182] 51 52 TASK [download picture jvm-01-01.png] **************************************************************************** 53 skipping: [172.16.1.180] 54 skipping: [web01] 55 skipping: [web02] 56 skipping: [web03] 57 skipping: [172.16.1.182] 58 changed: [172.16.1.181] 59 60 TASK [download picture jvm-01-02.png] **************************************************************************** 61 skipping: [172.16.1.180] 62 skipping: [web01] 63 skipping: [web02] 64 skipping: [web03] 65 skipping: [172.16.1.181] 66 changed: [172.16.1.182] 67 68 TASK [other download picture jvm-01-03.png] ********************************************************************** 69 skipping: [172.16.1.181] 70 skipping: [172.16.1.182] 71 changed: [web02] 72 changed: [web01] 73 changed: [172.16.1.180] 74 changed: [web03] 75 76 PLAY RECAP ******************************************************************************************************* 77 172.16.1.180 : ok=2 changed=1 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0 78 172.16.1.181 : ok=2 changed=1 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0 79 172.16.1.182 : ok=2 changed=1 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0 80 web01 : ok=2 changed=1 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0 81 web02 : ok=2 changed=1 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0 82 web03 : ok=2 changed=1 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0
標準迴圈
註意:
1、迴圈語法有兩種:loop 和 with_。
2、loop 是在ansible 2.5 添加的,with_ 是一直存在的,推薦使用 loop。在未來 with_ 可能被棄用。
簡單列表迴圈
如果我們需要在 playbook 中啟動多個服務,或者下載多個文件;按照之前所學的,那麼我們需要寫多個 task。但這樣會使得 playbook 變得臃腫,因此這時我們就需要引進迴圈了。
示例:一次啟動多個服務,下載多個文件
使用 loop 方式【推薦】
1 [yun@ansi-manager object04]$ pwd 2 /app/ansible_info/object04 3 [yun@ansi-manager object04]$ ll 4 total 20 5 -rw-rw-r-- 1 yun yun 594 Aug 23 22:10 test_loop.yml 6 [yun@ansi-manager object04]$ cat test_loop.yml 7 --- 8 # 啟動多個服務 和下載多個文件 9 - hosts: proxyservers 10 11 tasks: 12 - name: "start httpd, rpcbind, network server" 13 service: 14 name: "{{ item }}" # 需要用引號引起來 15 state: started 16 loop: 17 - httpd 18 - rpcbind 19 - network 20 21 - name: "download multiple file" 22 get_url: 23 url: "{{ item }}" # 需要用引號引起來 24 dest: /tmp/ 25 loop: 26 - http://www.zhangblog.com/uploads/jvm/jvm-01-01.png 27 - http://www.zhangblog.com/uploads/jvm/jvm-01-02.png 28 - http://www.zhangblog.com/uploads/jvm/jvm-01-03.png 29 30 [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key --syntax-check test_loop.yml # 語法檢測 31 [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key -C test_loop.yml # 預執行,測試執行 32 [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key test_loop.yml # 執行
備註:以上方法可用在 yum 模塊中。
使用 with_items 方式
其中 playbook 文件中僅把 loop 變為了 with_items。
1 [yun@ansi-manager object04]$ pwd 2 /app/ansible_info/object04 3 [yun@ansi-manager object04]$ ll 4 total 20 5 -rw-rw-r-- 1 yun yun 594 Aug 23 22:10 test_with_items.yml 6 [yun@ansi-manager object04]$ cat test_with_items.yml 7 --- 8 # 啟動多個服務 和下載多個文件 9 - hosts: proxyservers 10 11 tasks: 12 - name: "start httpd, rpcbind, network server" 13 service: 14 name: "{{ item }}" # 需要用引號引起來 15 state: started 16 with_items: 17 - httpd 18 - rpcbind 19 - network 20 21 - name: "download multiple file" 22 get_url: 23 url: "{{ item }}" # 需要用引號引起來 24 dest: /tmp/ 25 with_items: 26 - http://www.zhangblog.com/uploads/jvm/jvm-01-01.png 27 - http://www.zhangblog.com/uploads/jvm/jvm-01-02.png 28 - http://www.zhangblog.com/uploads/jvm/jvm-01-03.png 29 30 [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key --syntax-check test_with_items.yml # 語法檢測 31 [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key -C test_with_items.yml # 預執行,測試執行 32 [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key test_with_items.yml # 執行
如果用在 yum 模塊中則會報如下棄用告警,因此該方法不適用於 yum 模塊。
遍歷哈希列表
如果我們需要創建多個用戶並且每個用戶都有指定的附加組;或者要創建多個文件,每個文件屬主、屬組、許可權不一樣;或者需要拷貝文件,但是每個文件的位置不一樣,且屬主、屬組、許可權不一樣等等;那之前所學的簡單迴圈就不能滿足我們的需求了。這時「哈希列表迴圈」就閃亮登場了。
示例:
使用 loop 方式【推薦】
1 [yun@ansi-manager object04]$ pwd 2 /app/ansible_info/object04 3 [yun@ansi-manager object04]$ ll 4 total 16 5 drwxrwxr-x 2 yun yun 56 Oct 26 16:03 file 6 -rw-rw-r-- 1 yun yun 1205 Oct 26 16:02 test_loop_hash.yml 7 [yun@ansi-manager object04]$ cat file/config_test.conf.j2 8 111 9 [yun@ansi-manager object04]$ cat file/yml_test_j2.yml 10 222 11 [yun@ansi-manager object04]$ cat test_loop_hash.yml 12 --- 13 # 使用迴圈字典創建多個用戶,創建多個文件,拷貝多個文件 14 - hosts: proxyservers 15 16 tasks: 17 - name: "Create multiple user" 18 user: 19 name: "{{ item.user }}" 20 groups: "{{ item.groups }}" 21 loop: 22 - { user: "testuser1", groups: "root" } 23 - { user: "testuser2", groups: "root,yun" } 24 25 - name: "Create multiple file or dir" 26 file: 27 path: "{{ item.path }}" 28 owner: "{{ item.owner }}" 29 group: "{{ item.group }}" 30 mode: "{{ item.mode }}" 31 state: "{{ item.state }}" 32 loop: 33 - { path: "/tmp/with_items_testdir", owner: "yun", group: "root", mode: "755", state: "directory" } 34 - { path: "/tmp/with_items_testfile", owner: "bin", group: "bin", mode: "644", state: "touch" } 35 36 - name: "copy multiple file" 37 copy: 38 src: "{{ item.src }}" 39 dest: "{{ item.dest }}" 40 owner: "{{ item.owner }}" 41 group: "{{ item.group }}" 42 loop: 43 - { src: "./file/config_test.conf.j2", dest: "/tmp/with_items_testdir/", owner: "yun", group: "root" } 44 - { src: "./file/yml_test_j2.yml", dest: "/tmp/yml_test.yml", owner: "yun", group: "yun" } 45 46 [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key --syntax-check test_loop_hash.yml # 語法檢測 47 [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key -C test_loop_hash.yml # 預執行,測試執行 48 [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key test_loop_hash.yml # 執行
使用 with_items 方式
其中 playbook 文件中僅把 loop 變為了 with_items。
1 [yun@ansi-manager object04]$ pwd 2 /app/ansible_info/object04 3 [yun@ansi-manager object04]$ ll 4 total 16 5 drwxrwxr-x 2 yun yun 56 Oct 26 16:03 file 6 -rw-rw-r-- 1 yun yun 1205 Oct 26 16:02 test_with_items_hash.yml 7 [yun@ansi-manager object04]$ cat file/config_test.conf.j2 8 111 9 [yun@ansi-manager object04]$ cat file/yml_test_j2.yml 10 222 11 [yun@ansi-manager object04]$ cat test_with_items_hash.yml 12 --- 13 # 使用迴圈字典創建多個用戶,創建多個文件,拷貝多個文件 14 - hosts: proxyservers 15 16 tasks: 17 - name: "Create multiple user" 18 user: 19 name: "{{ item.user }}" 20 groups: "{{ item.groups }}" 21 with_items: 22 - { user: "testuser1", groups: "root" } 23 - { user: "testuser2", groups: "root,yun" } 24 25 - name: "Create multiple file or dir" 26 file: 27 path: "{{ item.path }}" 28 owner: "{{ item.owner }}" 29 group: "{{ item.group }}" 30 mode: "{{ item.mode }}" 31 state: "{{ item.state }}" 32 with_items: 33 - { path: "/tmp/with_items_testdir", owner: "yun", group: "root", mode: "755", state: "directory" } 34 - { path: "/tmp/with_items_testfile", owner: "bin", group: "bin", mode: "644", state: "touch" } 35 36 - name: "copy multiple file" 37 copy: 38 src: "{{ item.src }}" 39 dest: "{{ item.dest }}" 40 owner: "{{ item.owner }}" 41 group: "{{ item.group }}" 42 with_items: 43 - { src: "./file/config_test.conf.j2", dest: "/tmp/with_items_testdir/", owner: "yun", group: "root" } 44 - { src: "./file/yml_test_j2.yml", dest: "/tmp/yml_test.yml", owner: "yun", group: "yun" } 45 46 [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key --syntax-check test_with_items_hash.yml # 語法檢測 47 [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key -C test_with_items_hash.yml # 預執行,測試執行 48 [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key test_with_items_hash.yml # 執行
遍歷字典
示例:
使用 loop 方式【推薦】
1 [yun@ansi-manager object04]$ pwd 2 /app/ansible_info/object04 3 [yun@ansi-manager object04]$ ll 4 total 28 5 -rw-rw-r-- 1 yun yun 452 Oct 26 16:46 test_loop_dict.yml 6 [yun@ansi-manager object04]$ cat test_loop_dict.yml 7 --- 8 # 列印信息 9 - hosts: manageservers 10 vars: 11 users: 12 alice: 13 name: Alice Appleworth 14 telephone: 123-456-7890 15 bob: 16 name: Bob Bananarama 17 telephone: 987-654-3210 18 19 tasks: 20 - name: "print user info" 21 debug: 22 msg: "User {{ item.key }}, userfullname: {{ item.value.name }} ({{ item.value.telephone }})" 23 # 將字典轉換為適合迴圈的項表 第一種方式推薦 24 loop: "{{ users|dict2items }}" 25 #loop: "{{ lookup('dict', users) }}" 26 27 [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key --syntax-check test_loop_dict.yml # 語法檢測 28 [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key -C test_loop_dict.yml # 預執行,測試執行 29 [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key test_loop_dict.yml # 執行
使用 with_items 方式
1 [yun@ansi-manager object04]$ pwd 2 /app/ansible_info/object04 3 [yun@ansi-manager object04]$ ll 4 total 28 5 -rw-rw-r-- 1 yun yun 458 Oct 26 16:47 test_with_items_dict.yml 6 [yun@ansi-manager object04]$ cat test_with_items_dict.yml 7 --- 8 # 列印信息 9 - hosts: manageservers 10 vars: 11 users: 12 alice: 13 name: Alice Appleworth 14 telephone: 123-456-7890 15 bob: 16 name: Bob Bananarama 17 telephone: 987-654-3210 18 19 tasks: 20 - name: "print user info" 21 debug: 22 msg: "User {{ item.key }}, userfullname: {{ item.value.name }} ({{ item.value.telephone }})" 23 # with_dict 會直接解析字典 24 with_dict: "{{ users }}" 25 26 [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key --syntax-check test_with_items_dict.yml # 語法檢測 27 [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key -C test_with_items_dict.yml # 預執行,測試執行 28 [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key test_with_items_dict.yml # 執行
變數迴圈-vars
針對yum 安裝多個包很有用,其他則會報出警告。
1 [yun@ansi-manager object04]$ pwd 2 /app/ansible_info/object04 3 [yun@ansi-manager object04]$ ll 4 total 36 5 -rw-rw-r-- 1 yun yun 252 Oct 26 17:46 test_cycle_vars.yml 6 [yun@ansi-manager object04]$ cat test_cycle_vars.yml 7 --- 8 # 批量包安裝 9 - hosts: proxyservers 10 11 tasks: 12 - name: "Install multiple packages" 13 yum: 14 name: "{{ multi_package }}" 15 state: present 16 vars: 17 multi_package: 18 - tree 19 - nc 20 - tcpdump 21 22 [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key --syntax-check test_cycle_vars.yml # 語法檢測 23 [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key -C test_cycle_vars.yml # 預執行,測試執行 24 [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key test_cycle_vars.yml # 執行
該方法不一定適用於其他模塊
觸發器-handlers
當我們修改了服務的配置文件時,這時我們需要去重啟服務,那麼 handlers 就可以派上用場了。
註意事項:
1、無論多少個 task 通知了相同的 handlers,handlers 僅會在所有 tasks 結束後運行一次。
2、只有 task 發生改變了才會通知 handlers,沒有改變則不會通知和觸發 handlers。
3、不能用 handlers 替代 task 。
1 [yun@ansi-manager object05]$ pwd 2 /app/ansible_info/object05 3 [yun@ansi-manager object05]$ ll 4 total 24 5 drwxrwxr-x 2 yun yun 129 Aug 24 11:41 file 6 -rw-rw-r-- 1 yun yun 1029 Aug 24 11:57 test_handlers.yml 7 [yun@ansi-manager object05]$ ll file/ # 涉及配置文件 8 total 20 9 -rw-r--r-- 1 yun yun 11767 Aug 24 11:41 httpd.conf.j2 10 [yun@ansi-manager object05]$ vim file/httpd.conf.j2 # 配置文件修改的地方 11 ………… 12 # Change this to Listen on specific IP addresses as shown below to 13 # prevent Apache from glomming onto all bound IP addresses. 14 # 15 #Listen 12.34.56.78:80 16 ###### 埠改為變數 17 Listen {{ httpd_port }} 18 19 ………… 20 [yun@ansi-manager object05]$ cat test_handlers.yml # yml 文件 21 --- 22 # 比如安裝配置啟動 httpd。當我們修改配置文件,重啟 httpd 服務 23 # 要求:修改配置,重啟一個或多個服務 24 - hosts: proxyservers 25 # 這裡為了演示方便,因此變數直接就寫在了該文件中 26 vars: 27 - httpd_port: 8081 28 29 tasks: 30 - name: "Install httpd" 31 yum: 32 name: "{{ packages }}" 33 state: present 34 vars: 35 packages: 36 - httpd 37 - httpd-tools 38 39 - name: "Httpd config" 40 template: 41 src: ./file/httpd.conf.j2 42 dest: /etc/httpd/conf/httpd.conf 43 # 一個通知 44 # notify: "Restart httpd server" 45 # 多個通知 46 notify: 47 - "Restart httpd server" 48 - "Restart crond server" 49