四、Ansible Playbooks:
1、Playbook、YAML和Jinja2簡介:
Playbook:包含Ansible指令的YAML格式的文件,#爲註釋,ansbile-playbook命令根據自上而下的順序依次執行。
YAML:YAML Ain't a Markup Language(YAML不是一種標記語言)的遞歸縮寫。在開發這種語言時,YAML的意思其實是Yet Another Markup Language(仍是一種標記語言),但爲了強調這種語言以數據爲中心,而不是以標記語言爲中心,採用反向縮略語重命名。YAML的語法和其它高級語言類似,並且可以簡單表達清單、散列表、標量等數據形態。它使用空白符號縮進和大量依賴外觀的特色,特別適合用來表達或編輯數據結構、各種配置文件、傾印除錯內容、文件大綱。對於Ansible,每一個YAML文件都是從一個列表開始,列表中的每一項都是一個鍵值對,通常它們被稱爲一個“哈希”或“字典”。列表中的所有成員都開始於相同的縮進級別,並且使用一個“-”作爲開頭(一個橫槓和一個空格)。一個字典是由一個簡單的“鍵: 值”形式組成(冒號後必須是一個空格)。
Jinja2:Python中被廣泛應用的模版引擎,它的設計思想來源於Django的模板引擎,並擴展了其語法和一系列強大的功能,包括:
(1)字面量:
Ø 字符串:使用單引號或雙引號
Ø 數字:整數、浮點數
Ø 列表:[item1, item2, ...]
Ø 元組:(item1, item2, ...)
Ø 字典:{key1:value1, key2:value2, ...}
Ø 布爾型:true、false
(2)算術運算符:+、-、*、/、//、%、**
(3)比較運算符:==、!=、>、>=、<、<=
(4)邏輯運算符:and、or、not、()
2、Playbook中的元素:
(1)hosts:執行指定任務的、定義在hosts文件中的被管控主機
192.168.1.144
[websrvs]
apache ansible_host=192.168.1.145
nginx ansible_host=192.168.1.220
備註:
Ø 如果定義了主機組名(websrvs)或主機別名(apache或nginx),hosts可以使用組名或別名
Ø 如果只定義了IP地址(192.168.1.144),則hosts只能使用IP地址
(2)remote_user:在被管控主機上執行任務的用戶
(3)tasks:定義要在被管控主機上執行的任務列表
(4)handlers:可以把handler理解成另一種形式的task,handler中的任務會被task中的任務進行調用,但是被調用並不意味着一定會執行,只有當task中的任務真正執行以後(真正的進行實際操作,造成了實際的改變),handler中被調用的任務纔會執行。如果task中的任務並沒有做出任何實際的操作,那麼handler中的任務即使被調用,也不會執行,另外handler執行的順序與handler在playbook中定義的順序是相同的,與handler被notify的順序無關。默認情況下,所有task執行完畢後,纔會執行各個handler,並不是執行完某個task後,立即執行與其對應的handler
(5)vars:定義playbook運行時需要使用的變量
(6)templates:包含了模板語法的文件
(7)tags:用於讓用戶選擇運行或略過playbook中的部分代碼
3、ansible-playbook的使用格式:# ansible-playbook [options] playbook.yml [playbook2 ...]
[options]:
Ø -C:嘗試預測可能發生的一些變化,並不真正執行
Ø -e 'EXTRA_VARS':設置key=value的自定義變量
Ø --syntax-check:對playbook執行語法檢查,並不真正執行
Ø --list-hosts:列出匹配的遠程主機列表,並不執行任何其它操作
Ø --list-tags:列出所有可用標籤
Ø --list-tasks:列出所有要執行的任務
Ø -t TAGS:僅執行指定標籤標記的任務
Ø --skip-tags 'SKIP_TAGS':僅執行與指定標籤不匹配的標記的任務
Ø --start-at-task 'START_AT_TASK':從匹配指定標籤標記的任務開始執行
Ø -f FORKS:指定一次批量管控多少臺主機,默認一批管控5臺主機
Ø -u REMOTE_USER:以哪個用戶連接至被管控主機,默認爲None
Ø -T TIMEOUT:連接至被管控主機的超時時長,默認10秒
Ø --version:查看ansible主配置文件、ansible配置的模塊搜索路徑、ansible python模塊位置、可執行程序位置和python版本信息
Ø -h:顯示幫助信息
4、playbook示例:所有被管控主機安裝memcached軟件包,啓動並實現開機自啓
# mkdir -pv /playbooks
# vim /playbooks/memcached.yml
- hosts: all
remote_user: root
tasks:
- name: install memcached
yum: name=memcached state=latest
- name: start memcached service
service: name=memcached state=started enabled=yes
# ansible-playbook --syntax-check /playbooks/memcached.yml
# ansible-playbook -C /playbooks/memcached.yml
# ansible-playbook /playbooks/memcached.yml
五、Ansible Variables:
1、內置變量:
(1)hostvars:獲取被管控主機的主機變量信息
# ansible all -m debug -a 'msg={{hostvars}}'
(2)ansible_version:獲取ansible的版本號
# ansible all -m debug -a 'msg={{ansible_version}}'
# ansible all -m debug -a 'msg={{ansible_version.full}}'
(3)group_names:獲取當前被管控主機所在分組的組名
# ansible all -m debug -a 'msg={{group_names}}'
(4)groups:獲取主機清單中所有主機及主機組的分組信息
# ansible all -m debug -a 'msg={{groups}}'
# ansible all -m debug -a 'msg={{groups.websrvs}}'
備註:所有主機默認被分成了組名爲“all”的組,沒有分組的主機分到了名爲“ungrouped”的組,即組名爲“未分組”的組
(5)inventory_file:獲取主機清單文件存放的路徑和名稱
# ansible all -m debug -a 'msg={{inventory_file}}'
(6)inventory_hostname:獲取被管控主機的主機名,此處的主機名並非系統的主機名,而是對應主機在主機清單中配置的名稱
# ansible all -m debug -a 'msg={{inventory_hostname}}'
備註:如果使用IP配置主機,inventory_hostname返回的值就是IP,如果使用別名配置主機,返回的值就是別名
2、ansible_facts變量:
(1)ansible_distribution:獲取被管控主機的系統類型
(2)ansible_distribution_major_version:獲取被管控主機的系統主版本號
(3)ansible_hostname:獲取被管控主機的主機名
(4)ansible_os_family:獲取被管控主機的系統系列名稱
(5)ansible_pkg_mgr:獲取被管控主機的包管理器名稱
# vim /playbooks/facts.yml
- hosts: all
remote_user: root
tasks:
- name: show distribution
debug: msg={{ansible_distribution}}
- name: show distribution major version
debug: msg={{ansible_distribution_major_version}}
- name: show hostname
debug: msg={{ansible_hostname}}
- name: show os family
debug: msg={{ansible_os_family}}
- name: show package manager
debug: msg={{ansible_pkg_mgr}}
# ansible-playbook --syntax-check /playbooks/facts.yml
# ansible-playbook -C /playbooks/facts.yml
# ansible-playbook /playbooks/facts.yml
備註:
Ø 使用命令# ansible all -m setup | less查看到的所有ansible_facts變量
Ø 所有ansible_facts變量只能在playbook中使用,不能在ad-hoc中直接使用,會提示沒有定義
3、通過主機清單文件定義的主機及主機組變量:
# vim /etc/ansible/hosts
(1)主機變量:
192.168.1.144
[websrvs]
192.168.1.145 ansible_user=keyso ansible_ssh_pass=123456
192.168.1.220 ansible_user=keyso ansible_ssh_pass=123456
[dbsrvs]
146 ansible_host=192.168.1.146
[test:children]
websrvs
dbsrvs
備註:
Ø 定義了3個組,websrvs、dbsrvs和test組,且test組中包含了2個子組websrvs和dbsrvs,當操作test組時就相當於操作了這2個子組中的所有主機
Ø 146 ansible_host=192.168.1.146:146爲主機別名,配置了主機別名,IP前需要增加參數ansible_host
Ø 常用主機變量:
² ansible_host:要遠程連接的主機IP
² ansible_port:SSH使用的端口號
² ansible_user:SSH連接時默認使用的用戶名
² ansible_ssh_pass:SSH連接時的密碼
² ansbile_sudo_pass:使用sudo連接用戶時的密碼
(2)主機組變量:
192.168.1.144
[websrvs]
192.168.1.145
192.168.1.220
[dbsrvs]
146 ansible_host=192.168.1.146
[test:children]
websrvs
dbsrvs
[websrvs:vars]
ansible_user=keyso
ansible_ssh_pass=123456
備註:在配置密鑰認證的情況下,無需配置ansible_user和ansible_ssh_pass兩個參數
4、通過ansible-playbook命令傳入變量:
# vim /playbooks/test.yml
- hosts: all
remote_user: root
tasks:
- name: command line variable
debug: msg={{cmdvar}}
# ansible-playbook --syntax-check -e cmdvar=hello /playbooks/test.yml
# ansible-playbook -C -e cmdvar=hello /playbooks/test.yml
# ansible-playbook -e cmdvar=hello /playbooks/test.yml
備註:命令行傳入的變量優先級最高
5、循環中的固定變量item:
(1)示例:如果被管控主機是RedHat系列的系統,則安裝tomcat、memcached和varnish三個軟件包:
# vim /playbooks/pkgs.yml
- hosts: all
remote_user: root
tasks:
- name: install packages
yum: name={{item}} state=latest
loop:
- tomcat
- memcached
- varnish
when: ansible_os_family=="RedHat"
# ansible-playbook --syntax-check /playbooks/pkgs.yml
# ansible-playbook -C /playbooks/pkgs.yml
# ansible-playbook /playbooks/pkgs.yml
備註:
Ø 循環:迭代,需要重複執行的任務。對迭代項的引用,固定變量名爲item,並在tasks中使用loop給定要迭代的元素列表。列表表示方式:字符串和字典。
Ø when語句:在tasks中使用,表示條件判斷,如果符合條件,則執行,jinja2的語法格式
(2)示例:所有被管控主機按如下要求創建用戶組和用戶,並設置用戶系統登錄密碼
用戶user1,基本組group1,密碼111111
用戶user2,基本組group2,密碼222222
用戶user3,基本組group3,密碼333333
# vim /playbooks/addusers.yml
- hosts: all
remote_user: root
tasks:
- name: add groups
group: name={{item}} state=present
loop:
- group1
- group2
- group3
- name: add users
user: name={{item.user}} group={{item.group}} state=present
loop:
- {user: user1, group: group1}
- {user: user2, group: group2}
- {user: user3, group: group3}
- name: set password
shell: echo {{item.passwd}} | passwd --stdin {{item.user}}
loop:
- {passwd: 111111, user: user1}
- {passwd: 222222, user: user2}
- {passwd: 333333, user: user3}
# ansible-playbook --syntax-check /playbooks/addusers.yml
# ansible-playbook -C /playbooks/addusers.yml
# ansible-playbook /playbooks/addusers.yml
6、自定義變量:
(1)playbook中使用vars關鍵字定義變量
(2)playbook中使用set_fact模塊定義變量
(3)vars_files:將文件中的變量引入playbook,以便在task中使用
(4)include_vars:將文件中的變量動態引入playbook,以便在task中使用
# vim /playbooks/uservar.yml
- hosts: all
remote_user: root
vars:
- var1: test1
vars_files:
- /tmp/uservar.txt
tasks:
- name: show var1
debug: msg={{var1}}
- name: show var2
debug: msg={{var2}}
- name: set var3
set_fact: var3=test3
- name: show var3
debug: msg={{var3}}
- name: modify uservar file
shell: sed -i 's/var2/var4/g' /tmp/uservar.txt
- name: set var4
include_vars: /tmp/uservar.txt
- name: show var4
debug: msg={{var4}}
# cat /tmp/uservar.txt
var2: test2
# ansible-playbook --syntax-check /playbooks/uservar.yml
# ansible-playbook -C /playbooks/uservar.yml
# ansible-playbook /playbooks/uservar.yml
備註:
Ø set_fact模塊定義的變量在其它playbook中也能被正常引用,但vars關鍵字定義的變量不行
Ø 在變量文件/tmp/uservar.txt中通過sed命令將var2替換成了變量var4,include_vars模塊能夠重新加載變量文件,並動態的以任務的方式引用變量var4
Ø 變量文件/tmp/uservar.txt位於Ansible主機,與被管控主機無關
7、 綜合示例:
(1)如果被管控主機是CentOS 7.x系列的系統,則根據提供的模板文件redis.conf.j2安裝、配置並啓動redis
# vim /playbooks/redis.yml
- hosts: all
remote_user: root
tasks:
- name: install redis
yum: name=redis state=latest
- name: install configuration file
template: src=/playbooks/redis.conf.j2 dest=/etc/redis.conf owner=redis group=root mode=0640 backup=yes
when: ansible_distribution=="CentOS" and ansible_distribution_major_version=="7"
notify: restart redis service
tags: redis configuration file
- name: start redis service
service: name=redis state=started enabled=yes
handlers:
- name: restart redis service
service: name=redis state=restarted
# cat /playbooks/redis.conf.j2
bind {{ansible_host}}
requirepass keyso0728
# ansible-playbook --syntax-check /playbooks/redis.yml
# ansible-playbook -C /playbooks/redis.yml
# ansible-playbook /playbooks/redis.yml
備註:
Ø 在Ansible節點中先安裝redis,提取redis.conf,並修改爲模板文件
Ø CentOS 7.6和CentOS 6.10中redis.conf原始配置文件內容一致
Ø redis啓動後監聽的端口爲6379
Ø 修改模板文件redis.conf.j2中的內容,然後將模板文件複製至被管控節點,重啓redis生效,可以執行命令:# ansible-playbook -t 'redis configuration file' /playbooks/redis.yml
(2)安全加固後的被管控主機不允許直接以root用戶身份登錄,需要通過普通用戶testuser連接至被管控主機,然後su到root用戶,執行那些需要root用戶權限才能執行的命令
# vim /etc/ansible/hosts
192.168.1.144
[websrvs]
192.168.1.145
192.168.1.220
[dbsrvs]
146 ansible_host=192.168.1.146
[test:children]
websrvs
dbsrvs
# ansible all -a 'whoami' --> root用戶
# ansible all -m user -a 'name=testuser state=present'
# ansible all -m shell -a 'echo 123456 | passwd --stdin testuser'
禁止root用戶直接登錄遠程被管控主機:
# ansible all -m lineinfile -a 'path=/etc/ssh/sshd_config regexp="^#PermitRootLogin yes" line="PermitRootLogin no" state=present backup=yes'
# ansible all -m service -a 'name=sshd state=restarted'
再使用root用戶連接至被管控節點會報錯
# vim /etc/ansible/hosts
192.168.1.144
[websrvs]
192.168.1.145
192.168.1.220
[dbsrvs]
146 ansible_host=192.168.1.146
[test:children]
websrvs
dbsrvs
[websrvs:vars]
ansible_user=testuser
ansible_ssh_pass=123456
ansible_become_pass=hellolinux
備註:
Ø ansible_user:定義普通用戶名稱
Ø ansible_ssh_pass:定義普通用戶密碼
Ø ansible_become_pass:定義切換root用戶時需要的登錄密碼
# ansible all -a 'whoami' --> testuser用戶
嘗試以testuser用戶身份在每臺被管控主機的/root目錄下創建a.txt文件,結果Permission denied
# ansible all -m file -a 'content= path=/root/a.txt state=touch'
# ansible all --become-method su -b -a 'whoami' --> root用戶
備註:
Ø --become-method:將權限提升的方式設置爲su,默認爲sudo
Ø -b:使用become執行操作(不提示密碼輸入)
# ansible all --become-method su -b -m file -a 'content= path=/root/a.txt state=touch'