天天看點

Ansilbe Playbook和Roles

Playbook 核心元件

官方文檔

https://docs.ansible.com/ansible/latest/reference_appendices/playbooks_keywords.
html#playbook-keywords      

一個playbook 中有多個元件組成,其中所用到的常見組進類型如下:

  • Hosts 執行的遠端主機清單
  • Tasks 任務集,由多個task的元素組成的清單實作,每個task是一個字典,一個完整的代碼塊功能最少需包括 name和task,一個name隻能包括一個task
  • Variables 内置變量或自定義變量在playbook中調用
  • Templates 模闆,可替換模闆檔案中的變量并實作一些簡單邏輯的檔案
  • Handlers 和 notify 結合使用,由特定條件觸發的操作,滿足條件才執行,否則不執行
  • tags 标簽,指定某條任務執行,用于選擇運作playbook中的部分代碼。ansible具有幂等性,是以會自動跳過沒有變化的部分,即便如此,有些代碼為測試其确實沒有發生變化的時間依然會非常的長。此時,如果确信其沒有變化,就可以通過tags跳過此代碼片段

1.1 playbook 元件

1.1.1 hosts 元件

Hosts:playbook中的每一個play的目的都是為了讓特定主機以某個指定的使用者身份執行任務,hosts用于指定要執行指定任務的主機,須事先定義在主機清單中

Websrvs:dbsrvs #或者,兩個組的并集
Websrvs:&dbsrvs #與,兩個組的交集
webservers:!dbsrvs #在websrvs組,但不在dbsrvs組
#案例
- hosts: websrvs:appsrvs      

1.1.2 remote_user 元件

remote_user:可用于host和task中,也可以通過指定其通過sudo的方式在遠端主機上執行任務,其可用于play全局或某個任務;此外,甚至可以在sudo時使用sudo_user指定sudo時切換的使用者

- hosts: websrvs
  remote_user: root
  
  tasks:
    - name: test connection
      ping:
      remote_user: lee
      sudo: yes       #預設sudo為root
      sudo_user: lee    #sudo為lee      

1.1.3 task清單和action元件

play的主體部分是task list,task list中有一個或多個task,各個task按次序逐個在hosts中指定的所有主機上執行,即在所有主機上完成第一個task後,再開始第二個task

task的目的是使用指定的參數執行子產品,而在子產品參數中可以使用變量,子產品執行是幂等的,這意味着多次執行是安全的,因為結果均一緻

每個task都應該有其name,用于playbook的執行結果輸出,具有其内容能清晰地描述任務執行步驟,如果未提供name,則action的結果将用于輸出。

task的兩種格式:

acion: module arguments     # action: shell echo hello
module: arguments       #建議使用此方式, shell: echo hello      

注意: shell和command子產品後面跟指令,而非key=value

範例:

[root@ansible playbook]#cat hello.yml 
---
- hosts: web
  remote_user: root
  gather_facts: no      #不收集系統資訊,提高執行效率

  tasks:
    - name: test network connection
      ping:
    - name: Excute command
      command: wall "hello,world, yaml!"
      
[root@ansible playbook]#cat install_httpd.yml 
---
- hosts: web:!10.0.0.202
  remote_user: root
  gather_facts: no

  tasks:
    - name: install httpd
      yum: name=httpd
    - name: start httpd
      service: name=httpd state=started enabled=yes
      
[root@ansible playbook]#cat remove_httpd.yaml 
---
 - hosts: web:!10.0.0.202
   remote_user: root
   gather_facts: no

   tasks: 
     - name: remove httpd
       yum: name=httpd state=absent      

1.1.4 其他元件說明

某任務的狀态再運作後為changed時,可通過 "notify" 通知給相應的handlers任務,還可以通過 "tags" 給task 打标簽,可在ansible-playbook指令上使用 -t 指定進行調用

1.1.5 Shell 腳本 VS playbook 案例

#SHELL腳本實作
#!/bin/bash
# 安裝Apache
yum install --quiet -y httpd
# 複制配置檔案
cp /tmp/httpd.conf /etc/httpd/conf/httpd.conf
cp/tmp/vhosts.conf /etc/httpd/conf.d/
# 啟動Apache,并設定開機啟動
systemctl enable --now httpd

#Playbook實作
---
- hosts: websrvs
  remote_user: root
  gather_facts: no
  
  tasks:
    - name: "安裝Apache"
      yum: name=httpd
    - name: "複制配置檔案"
      copy: src=/tmp/httpd.conf dest=/etc/httpd/conf/
    - name: "複制配置檔案"
      copy: src=/tmp/vhosts.conf dest=/etc/httpd/conf.d/
    - name: "啟動Apache,并設定開機啟動"
      service: name=httpd state=started enabled=yes      

1.1.6 playbook 指令

格式

ansible-playbook [options] <filename.yml>
#常見選項
--syntax-check            #文法檢查,可縮寫成--syntax,相當于bash -n
-C --check                #模拟執行,隻檢測可能發生的改變,但不真正執行操作
--list-hosts              #列出運作任務的主機
--list-tags               #列出tag
--list-tasks              #列出task
--limit 主機清單           #隻針對主機清單中的特定主機執行
-i  inventory           #指定主機清單檔案,通常一個項目對應一個主機清單檔案
--start-at-task start_at_task    #從指定task開始執行,而非從頭開始,start_at_task為任務的name
-v -vv -vvv             #顯示過程      

1.2 Playbook 初步

1.2.1 利用 playbook 建立mysql使用者

---
- hosts: db
  remote_user: root
  gather_facts: no

  tasks:
    - name: create group
      group: name=mysql system=yes gid=306
    - name: create user
      user: name=mysql shell=/sbin/nologin system=yes group=mysql uid=306 home/data/mysql create_home=no      

1.2.2 利用playbook 安裝 / 解除安裝 nginx

---
# install nginx
- hosts: web
  remote_user: root

  tasks:
    - name: add group nginx
      group: name=nginx state=present
    - name: add user nginx
      user: name=nginx state=present group=nginx
    - name: Install Nginx
      yum: name=nginx state=present
      # 目前目錄拷貝nginx.conf到files,并且建立index.html頁面檔案
    - name: Config file
      copy: src=files/nginx.conf dest=/etc/nginx/nginx.conf
    - name: web page
      copy: src=files/index.html dest=/usr/share/nginx/html/index.html
    - name: start Nginx
      service: name=nginx state=started enabled=yes
    
   
   ---
# remove nginx
- hosts: web
  remote_user: root

  tasks:
    - name: stop nginx
      service: name=nginx state=stopped
    - name: del user nginx
      user: name=nginx state=absent
    - name: remove Nginx
      yum: name=nginx state=absent
    - name: remove web page
      file: name=/usr/share/nginx/index.html state=absent
    - name: remove Config file
      file: name=/etc/nginx/nginx.conf state=absent      

1.2.3 忽略錯誤 ignore_errors

如果一個task出錯,預設将不會繼續執行後續的其他task

利用 ignore_errors: yes 可以忽略此task錯誤,繼續向下執行其他task

---
- hosts: web
  remote_user: root
  ignore_errors: yes  #忽略錯誤
 
  tasks:
    - name: error
      command: /bin/false
    - name: continue
      command: wall continue      

1.2.4 playbook 中使用handlers 和notify

Handlers 主要用于當關注的資源發生變化時,才會采取一定的操作,而notify對應的action可用于在每個play的最後被觸發,這樣可避免多次有改變發生時每次都執行指定的操作,僅在所有的變化發生完成後一次性的執行指定操作。

注意:

  • 如果多個task通知了相同的handlers,此handlers僅會在所有task結束後運作一次
  • 隻有notifly對應的task發生改變了才會通知hangdlers,沒有改變則不會觸發handlers
  • handlers 是在所有前面的task都成功執行才會執行,如果前面任何一個task失敗,會導緻handlers跳過執行,可以使用 force_handlers: yes 強制執行handler
---
# install nginx
- hosts: web:!10.0.0.202
  remote_user: root
  gather_facts: no
  force_handlers: yes    #  task中任何一個action失敗,仍強制執行 handlers
  
  tasks:
    - name: add group nginx
      group: name=nginx state=present
    - name: add user nginx
      user: name=nginx state=present group=nginx
    - name: Install Nginx
      yum: name=nginx state=present
    - name: Config file
      copy: src=files/nginx.conf dest=/etc/nginx/nginx.conf
      notify:
        - Restart Nginx
    - name: web page
      copy: src=files/index1.html dest=/usr/share/nginx/html/index.html
    - name: start Nginx
      service: name=nginx state=started enabled=yes

  handlers:
    - name: Restart Nginx
      service: name=nginx state=restarted      

1.2.5 playbook中使用tags

在playbook檔案中,可以利用tags元件,為特定 task 指定标簽, 當在執行playbook時,可以隻執行特定tags的task,而非整個 playbook檔案

可以一個task對應多個tag,也可以多個task對應一個tag

---
# install nginx
- hosts: web:!10.0.0.202
  remote_user: root
  gather_facts: no
  force_handlers: yes
  tasks:
    - name: add group nginx
      group: name=nginx state=present
    - name: add user nginx
      user: name=nginx state=present group=nginx
    - name: Install Nginx
      yum: name=nginx state=present
    - name: Config file
      copy: src=files/nginx.conf dest=/etc/nginx/nginx.conf
      tags: conf    # 指定tag 标簽
      notify:
        - Restart Nginx
    - name: web page
      copy: src=files/index.html dest=/usr/share/nginx/html/index.html
    - name: start Nginx
      service: name=nginx state=started enabled=yes

  handlers:
    - name: Restart Nginx
      service: name=nginx state=restarted

# 指定執行 tag标簽 conf, 其他的task不會重複執行
[root@ansible playbook]#ansible-playbook -t conf install_nginx.yml 
PLAY [web:!10.0.0.202] *********************************************************************************************************************<strong>

TASK [Config file] </strong>***********************************************************************************************************************<strong>
changed: [10.0.0.7]
changed: [10.0.0.17]

RUNNING HANDLER [Restart Nginx] </strong>**********************************************************************************************************<strong>
changed: [10.0.0.7]
changed: [10.0.0.17]

PLAY RECAP </strong>*********************************************************************************************************************************
10.0.0.17                  : ok=2    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
10.0.0.7                   : ok=2    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0      

1.2.6 playbook中使用變量

playbook中同樣支援變量

變量名: 僅能由字母,數字和下劃線組成,且隻能以字母開頭

變量調用方式:

通過 {{ variable_name }} 調用變量,且變量名前後建議加空格,有時用"{{ variable_name }}" 才生效

變量來源:

  1. ansible 的 setup子產品 遠端主機的所有變量都可以直接調用
  1. 通過指令行指定變量,優先級最高
ansible-playbook -e varname=value test.yml      
  1. 在playbook檔案中定義
  1. 在獨立的變量yaml 檔案中定義
  1. 在主機清單檔案中定義
  1. 在項目中針對主機和主機組定義
  1. 在role中定義

變量的優先級從高到低如下

-e 選項定義變量  --> playbook中vars_files --> playbook中vars變量定義 --> host_vars/主機名檔案 --> 主機清單中主機變量 --> group_vars/主機組名檔案 --> group_vars/all檔案 --> 主機清單組檔案      

1.2.6.1 使用setup 子產品中的變量

[root@ansible ~]#ansible 10.0.0.27 -m setup -a "filter=ansible_default_ipv4"
10.0.0.27 | SUCCESS => {
    "ansible_facts": {
        "ansible_default_ipv4": {
            "address": "10.0.0.27", 
            "alias": "eth0", 
            "broadcast": "10.0.0.255", 
            "gateway": "10.0.0.2", 
            "interface": "eth0", 
            "macaddress": "00:0c:29:aa:78:a9", 
            "mtu": 1500, 
            "netmask": "255.255.255.0", 
            "network": "10.0.0.0", 
            "type": "ether"
        }, 
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false
}

[root@ansible playbook]#vim var1.yml
---
- hosts: web
  remote_user: root
  gather_facts: yes  # 預設 yes,可不寫

  tasks:
   - name: create log file
     file: name=/data/{{ ansible_nodename }}.log state=touch      

1.2.6.2 在playbook指令行中定義變量,-e指定變量

[root@ansible playbook]#vim var2.yml 
---
- hosts: web:!10.0.0.202
  remote_user: root

  tasks:
    - name: install package
      yum: name={{ pkname }} state=present

[root@ansible playbook]#ansible-playbook -e pkname=vsftpd var2.yml      

1.2.6.3 在playbook檔案中定義變量

[root@ansible playbook]#vim var3.yml 
---
- hosts: web:!10.0.0.202
  remote_user: root
  vars:
    username: user1
    groupname: group1

  tasks:
    - name: Create {{ groupname }}
      group: name={{ groupname }} state=present
    - name: Create {{ username }}
      user: name={{ username }} group={{ groupname }} state=present
 # -e 指令行指定的變量優先級更高
[root@ansible playbook]#ansible-playbook -e "username=user2 groupname=group2" var3.yml      

1.2.6.4 使用變量檔案

可以在一個獨立的playbook檔案中定義變量,在另一個playbook檔案中引用檔案中的變量,比playbook中定義的變量優先級高

[root@ansible playbook]#vim vars.yml
---
var1: vsftpd
var2: httpd

#  var4 中引用 vars檔案中的變量
[root@ansible playbook]#vim var4.yml    
---
- hosts: web:!10.0.0.202
  vars_files: vars.yml

  tasks:
    - name: touch apps dir
      file: path=/apps state=directory
    - name: Create vsftpd log
      file: name=/apps/{{ var1 }}.log state=touch
    - name: Create httpd log
      file: name=/apps/{{ var2 }}.log state=touch      

1.2.6.5 針對主機和主機組的變量

  • 在主機清單中針對所有項目的主機和主機組定義變量

所有項目的主機變量

在inventory 主機清單檔案中為指定的主機定義變量,在playbook中使用

[root@ansible playbook]#vim /etc/ansible/hosts
[web]
10.0.0.7 hname=www1 domain=magedu.io     # 為單個主機設定變量
10.0.0.17 hname=www2
10.0.0.202

[web:vars]    # 給web組設定變量
mark="-"

[all:vars]    # 為所有組設定變量
domain=magedu.org
[root@ansible playbook]#ansible web -m hostname -a 'name={{ hname }}{{ mark }}{{ domain }}'
10.0.0.202 | FAILED! => {
    "msg": "The task includes an option with an undefined variable. The error was: 'hname' is undefined"
}
10.0.0.17 | CHANGED => {
    "ansible_facts": {
        "ansible_domain": "org", 
        "ansible_fqdn": "www2-magedu.org", 
        "ansible_hostname": "www2-magedu", 
        "ansible_nodename": "www2-magedu.org", 
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "name": "www2-magedu.org"
}
10.0.0.7 | CHANGED => {
    "ansible_facts": {
        "ansible_domain": "io", 
        "ansible_fqdn": "www1-magedu.io", 
        "ansible_hostname": "www1-magedu", 
        "ansible_nodename": "www1-magedu.io", 
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "name": "www1-magedu.io"
}      
  • 針對目前項目的主機和主機組的變量

生産建議在項目目錄中建立額外的兩個變量目錄,分别是 host_vars 和 group_vars

host_vars下面的檔案名和主機清單主機名一緻,針對單個主機進行變量定義,格式:host_vars/hostname

group_vars下面的檔案名和主機清單中組名一緻,針對單個組進行變量定義,格式:group_vars/groupname

group_vars/all 檔案内定義的變量對所有組都有效

[root@ansible ansible]#tree
.
├── group_vars
│   ├── all
│   └── web
├── host_vars
│   ├── 10.0.0.17
│   └── 10.0.0.7
└── project_vars.yml

2 directories, 5 files

[root@ansible ansible]#cat host_vars/10.0.0.17
id: 2
[root@ansible ansible]#cat host_vars/10.0.0.7
id: 1
[root@ansible ansible]#cat group_vars/web 
name: web
[root@ansible ansible]#cat group_vars/all 
domain: magedu.cn

[root@ansible ansible]#vim project_vars.yml 
---
- hosts: web:!10.0.0.202
  remote_user: root

  tasks:
    - name: Get variable
      command: echo "{{name}}{{id}}.{{domain}}"
      register: result              # 定義一個名為 result變量
    - name: print variable
      debug:
        msg: "{{result.cmd}}"
        
[root@ansible ansible]#ansible-playbook project_vars.yml 
PLAY [web:!10.0.0.202] *********************************************************************************************************************<strong>

TASK [Gathering Facts] </strong>*******************************************************************************************************************<strong>
ok: [10.0.0.17]
ok: [10.0.0.7]

TASK [Get variable] </strong>**********************************************************************************************************************<strong>
changed: [10.0.0.7]
changed: [10.0.0.17]

TASK [print variable] </strong>********************************************************************************************************************<strong>
ok: [10.0.0.7] => {
    "msg": [
        "echo", 
        "web1.magedu.cn"
    ]
}
ok: [10.0.0.17] => {
    "msg": [
        "echo", 
        "web2.magedu.cn"
    ]
}

PLAY RECAP </strong>*********************************************************************************************************************************
10.0.0.17     : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
10.0.0.7      : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0      

1.2.6.6 register 注冊變量

在playbook中可以使用register将捕獲的輸出儲存在臨時變量中,然後使用 debug 子產品進行顯示輸出

範例: 利用debug 子產品輸出變量

[root@ansible ansible]#vim register.yml

---
- hosts: web:!10.0.0.202

  tasks:
    - name: get variable
      shell: hostname
      register: name
    - name: print variable
      debug:
        msg: "{{ name }}"         #輸出register注冊的name變量的全部資訊,注意變量要加" "引起來
       #msg: "{{ name.cmd }}"       #顯示指令
       #msg: "{{ name.rc }}"        #顯示指令成功與否
       #msg: "{{ name.stdout }}"      #顯示指令的輸出結果為字元串形式
       #msg: "{{ name.stdout_lines }}"    #顯示指令的輸出結果為清單形式
       #msg: "{{ name.stdout_lines[0] }}"    #顯示指令的輸出結果為清單中的第一個元素
       
#說明
第一個 task中,使用了 register注冊變量名為 name,當shell子產品執行完畢後,會将資料放到該變量中
第二個 task中,使用了 debug子產品,并從變量 name中擷取資料

[root@ansible ansible]#ansible-playbook register.yml 

PLAY [web:!10.0.0.202] *********************************************************************************************************************<strong>

TASK [Gathering Facts] </strong>*******************************************************************************************************************<strong>
ok: [10.0.0.17]
ok: [10.0.0.7]

TASK [get variable] </strong>**********************************************************************************************************************<strong>
changed: [10.0.0.7]
changed: [10.0.0.17]

TASK [print variable] </strong>********************************************************************************************************************<strong>
ok: [10.0.0.7] => {
    "msg": {
        "changed": true, 
        "cmd": "hostname",                #顯示指令
        "delta": "0:00:00.005647", 
        "end": "2022-06-12 22:12:28.347194", 
        "failed": false, 
        "rc": 0,                    #顯示指令成功與否
        "start": "2022-06-12 22:12:28.341547", 
        "stderr": "",                   #顯示指令的錯誤輸出結果,為字元串形式
        "stderr_lines": [],               #顯示指令的錯誤輸出結果,為清單形式
        "stdout": "www1-magedu.io",            #顯示指令的輸出結果,為字元串形式
        "stdout_lines": [               #顯示指令的輸出結果,為清單形式
            "www1-magedu.io"
        ]
    }
}
ok: [10.0.0.17] => {
    "msg": {
        "changed": true, 
        "cmd": "hostname", 
        "delta": "0:00:00.004464", 
        "end": "2022-06-12 22:12:28.440166", 
        "failed": false, 
        "rc": 0, 
        "start": "2022-06-12 22:12:28.435702", 
        "stderr": "", 
        "stderr_lines": [], 
        "stdout": "www2-magedu.org", 
        "stdout_lines": [
            "www2-magedu.org"
        ]
    }
}

PLAY RECAP </strong>*********************************************************************************************************************************
10.0.0.17     : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
10.0.0.7      : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0      

範例: 使用 register 注冊變量建立檔案

[root@ansible ansible]#vim register1.yml 

---
- hosts: 10.0.0.7

  tasks:
    - name: get variable
      shell: hostname
      register: name
    - name: create file
      file: dest=/data/{{ name.stdout }}.log state=touch
[root@ansible ansible]#ansible-playbook register1.yml

# register 和 debug 子產品
[root@ansible apps]#vim debug_register.yml
---
- hosts: 10.0.0.202

  tasks:
    - name: get var1
      shell: echo hello world
      register: say_hi
    - name: get var2
      shell: "awk -F: 'NR==1{print $1}' /etc/passwd"
      register: user
    - debug: var=say_hi.stdout        #自定義輸出變量代替msg
    - debug: var=user.stdout               #自定義輸出變量代替msg
 [root@ansible apps]#ansible-playbook debug_register.yml 

PLAY [10.0.0.202] **************************************************************************************************************************<strong>

TASK [Gathering Facts] </strong>*******************************************************************************************************************<strong>
ok: [10.0.0.202]

TASK [get var1] </strong>**************************************************************************************************************************<strong>
changed: [10.0.0.202]

TASK [get var2] </strong>**************************************************************************************************************************<strong>
changed: [10.0.0.202]

TASK [debug] </strong>*****************************************************************************************************************************<strong>
ok: [10.0.0.202] => {
    "say_hi.stdout": "hello world"
}

TASK [debug] </strong>*****************************************************************************************************************************<strong>
ok: [10.0.0.202] => {
    "user.stdout": "root"
}

PLAY RECAP </strong>*********************************************************************************************************************************
10.0.0.202    : ok=5    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0      

1.2.6.7 案例: 利用變量批量部署MySQL5.7 或8.0

[root@ansible ansible]#cat install_mysql5.7or8.0.yml 
---
# install mysql-5.7.17-linux-glibc2.5-x86_64.tar.gz
# install mysql-8.0.19-linux-glibc2.12-x86_64.tar.xz
#
- hosts: db
  remote_user: root
  gather_facts: yes
  vars:
    mysql_version: 5.7.17
    mysql_file: mysql-{{mysql_version}}-linux-glibc2.5-x86_64.tar.gz
    mysql_root_password: 123456

  tasks:
     - name: install packages
       yum: name=libaio,numactl-libs state=latest
     - name: create mysql group
       group: name=mysql gid=306
     - name: create mysql user
       user: name=mysql uid=306 group=mysql shell=/sbin/nologin system=yes create_home=no home=/data/mysql
     - name: 複制MySQL壓縮包到遠端主機并解壓縮到指定目錄
       unarchive: src=/data/files/{{ mysql_file }} dest=/usr/local/ owner=root group=root
     - name: 建立軟連結 /usr/local/mysql
       file: src=/usr/local/mysql-{{mysql_version}}-linux-glibc2.5-x86_64 dest=/usr/local/mysql state=link
     - name: data dir
       shell: /usr/local/mysql/bin/mysqld --initialize-insecure --user=mysql --datadir=/data/mysql
       tags: data
     - name: Config my.cnf
       copy: src=/data/files/my.cnf dest=/etc/my.cnf
     - name: service script
       shell: /bin/cp /usr/local/mysql/support-files/mysql.server /etc/init.d/mysqld
     - name: Path variable
       copy: content='PATH=/usr/local/mysql/bin:$PATH' dest=/etc/profile.d/mysql.sh
     - name:  使 Path variable生效
       shell: /bin/source /etc/profile.d/mysql.sh
     - name: enable service
       shell: chkconfig --add mysqld;/etc/init.d/mysqld start
       tags: service
     - name: change password
       shell: /usr/local/mysql/bin/mysqladmin -uroot password {{mysql_root_password}}      

1.3 template 模闆

子產品是一個文本檔案,可以做為生成檔案的模闆,并且模闆中還可嵌套jinja2文法

template功能:可以根據和參考子產品檔案,動态生成相類似的配置檔案

template檔案必須存放于template目錄下,且命名為 .j2 結尾

yaml / yml 檔案需和template目錄平級,目錄結構如下示例:

./

├── temnginx.yml

└── templates

└── nginx.conf.j2

範例: 利用template 同步nginx配置檔案

#準備 templates/nginx.conf.j2檔案
[root@ansible ansible]#mkdir templates
[root@ansible ansible]#cp files/nginx.conf templates/nginx.conf.j2
[root@ansible ansible]#tree
.
├── ansible.cfg
├── files
│   ├── index.html
│   └── nginx.conf
├── install_nginx.yml
├── remove_nginx.yml
└── templates
    └── nginx.conf.j2
[root@ansible ansible]#vim install_nginx.yml 

---
# install nginx
- hosts: web:!10.0.0.202
  remote_user: root
  gather_facts: yes
  force_handlers: yes
  
  tasks:
    - name: add group nginx
      group: name=nginx state=present
    - name: add user nginx
      user: name=nginx state=present group=nginx
    - name: Install Nginx
      yum: name=nginx state=present
    - name: Config file
      template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf
      tags: conf
      notify:
        - Restart Nginx
    - name: web page
      copy: src=files/index.html dest=/usr/share/nginx/html/index.html
    - name: start Nginx
      service: name=nginx state=started enabled=yes

  handlers:
    - name: Restart Nginx
      service: name=nginx state=restarted
[root@ansible ansible]#vim templates/nginx.conf.j2 
...
worker_processes {{ ansible_processor_vcpus+2 }};
...      

1.3.1 template中使用流程控制 for 和 if

1.3.1.1 for 循環

格式

{% for i in EXPR %}
  ...
{% endfor %}      

範例:

#templates/nginx.conf1.j2
{% for vhost in nginx_vhosts %}
server {
    listen {{ vhost }}
}
{% endfor %}

#for.yml 
---
- hosts: web
  remote_user: root
  vars:
    nginx_vhosts:
      - 81
      - 82
      - 83
  tasks:
    - name: template config
      template: src=nginx.conf1.j2 dest=/data/nginx.conf
      
[root@ubuntu2004 data]#cat nginx.conf 
server {
    listen 81
}
server {
    listen 82
}
server {
    listen 83
}      

範例: for 通過key=value讀取變量值

# 
[root@ansible ansible]#vim for1.yml

---
- hosts: web
  remote_user: root
  vars:
    nginx_vhosts:
      - listen: 8080
        server_name: "web1.abc.com"
        root: "/var/www/nginx/web1/"
      - listen: 8081
        server_name: "web2.abc.com"
        root: "/var/www/nginx/web2/"
      - {listen: 8082, server_name: "web3.abc.com",root: "/var/www/nginx/web3/"}

  tasks:
    - name: template config
      template: src=for1.j2 dest=/data/for1.conf

[root@ansible ansible]#vim templates/for1.j2 

{% for vhost in nginx_vhosts %}
server {
    listen {{ vhost.listen }}
    server_name {{ vhost.server_name }}
    root {{ vhost.root }}
}
{% endfor %}

#執行結果
[root@ansible ansible]#ansible-playbook for1.yml --limit 10.0.0.202
[root@ubuntu2004 ~]#cat /data/for1.conf 
server {
    listen 8080
    server_name web1.abc.com
    root /var/www/nginx/web1/
}
server {
    listen 8081
    server_name web2.abc.com
    root /var/www/nginx/web2/
}
server {
    listen 8082
    server_name web3.abc.com
    root /var/www/nginx/web3/
}      

1.3.1.2 if 條件判斷

在模闆檔案使用 if 條件判斷, 決定是否生成相關的配置資訊

範例:

[root@ansible ansible]#vim if.yml

---
 - hosts: web
   remote_user: root
   gather_facts: yes
   vars:
     nginx_vhosts:
       - web1:
         listen: 8080
         root: "/var/www/nginx/web1/"
       - web2:
         listen: 8080
         root: "/var/www/nginx/web2/"
       - web3:
         listen: 8080
         server_name: "web3.abc.com"
         root: "/var/www/nginx/web3/"

   tasks:
     - name: template config
       template: src=if.conf.j2 dest=/data/if.conf

# if.conf.j2
[root@ansible ansible]#vim templates/if.conf.j2

{% for vhost in nginx_vhosts %}
server {
    listen {{ vhosts.listen }}
    {% if vhosts.server_name is defined %}
server_name {{ vhosts.server_name }}      #注意不縮進,頂格
    {% endif %}
root {{ vhosts.root }}                 #注意不縮進,頂格
}
{% endfor %}

#生成的結果
[root@ubuntu2004 ~]#cat /data/if.conf 
server {
    listen 8080
    root /var/www/nginx/web1/
}
server {
    listen 8080
    root /var/www/nginx/web2/
}
server {
    listen 8080
    server_name web3.abc.com
    root /var/www/nginx/web3/
}      

1.4 使用循環疊代

疊代:當有需要重複性執行的任務時,可以使用疊代機制

1.4.1 疊代 with_items (loop)

對疊代項的引用,固定内置變量名為 "item"

要在task中使用 with_items給定要疊代的元素類别

注意:ansible2.5版本後,可以用loop代替with_items

清單元素格式:

  • 字元串
  • 字典

範例:

# loop.yml
---
- hosts: web
  remote_user: root
  
  tasks:
    - name: add server users
      user: name={{ item }} state=present
      loop: # ansible2.5版本前使用 with_items
        - user1
        - user2
        - user3
#上面語句功能等同于下面的語句
- name: add server users
  user: name=testuser1 state=present
- name: add several users
  user: name=testuser2 state=present
- name: add several users
  user: name=testuser3 state=present      

1.4.2 疊代嵌套子變量

在疊代中,還可以嵌套子變量,關聯多個變量在一起使用

示例: 批量建立使用者

# 通過item 鍵值對取子變量
[root@ansible ansible]#vim items_user.yml 

---
- hosts: web

  tasks:
    - name: add groups
      group: name={{ item }} state=present
      with_items:
        - nginx
        - mysql
        - redis
    - name: add users
      user: name={{ item.user }} group={{ item.group }} uid={{ item.uid }} state=present
      with_items:
        - { user: 'nginx', group: 'nginx', uid: '80' }
        - { user: 'mysql', group: 'mysql', uid: '3306' }
        - { user: 'redis', group: 'redis', uid: '6379' }      

1.4.3 until 循環

範例:

# until 為false時才會執行循環,為true則 退出循環

[root@ansible ansible]#vim until.yml 

---
- hosts: localhost
  gather_facts: false

  tasks:
    - debug: msg="until"
      until: false
      retries: 3    #預設值,重複3次
      delay: 1      #延遲1S      

1.4.4 with_lines 逐行處理

# with_lines 逐行處理
[root@ansible ansible]#vim with_lines.yml 

- hosts: localhost

  tasks:
    - debug: msg={{ item }}
      with_lines: free -h   #類似 with_items集合      

1.5 playbook 中使用 when

when語句可以實作條件測試

# when的清單形式表示and關系
[root@ansible ansible]#vim when.yml 

---
#關閉Ubuntu版本的主機
- hosts: web

  tasks:
    - name: 關閉 Ubuntu 主機
      reboot:
      when:
        - ansible_facts['distribution'] == "Ubuntu"
        - ansible_facts['distribution_major_version'] == "20"   # and關系      

1.6 分組 block

當想在滿足一個條件下,執行多個任務時,就需要分組,而不再每個任務都用when

[root@ansible ansible]#vim block.yml 

---
- hosts: web

  tasks:
    - block:
        - debug: msg="first"
        - debug: msg="second"
      when:
        - ansible_facts['distribution'] == "CentOS"
        - ansible_facts['distribution_major_version'] == "7"
[root@ansible ansible]#ansible-playbook block.yml 
...
TASK [debug] *******************************************************************************************************************************<strong>
ok: [10.0.0.7] => {
    "msg": "first"
}
ok: [10.0.0.17] => {
    "msg": "first"
}
skipping: [10.0.0.202]

TASK [debug] </strong>*******************************************************************************************************************************
ok: [10.0.0.7] => {
    "msg": "second"
}
ok: [10.0.0.17] => {
    "msg": "second"
}
skipping: [10.0.0.202]
...      

1.7 changed_when

檢查task傳回結果,決定是否繼續向下執行

[root@ansible ansible]#vim changed_when.yml 

---
- hosts: 10.0.0.17
  remote_user: root
  gather_facts: yes

  tasks:
    - name: install nginx
      yum: name=nginx
    - name: config file
      template: src=nginx.conf.j2 dest="/etc/nginx/nginx.conf"
      notify: restart nginx
    - name: check config
      shell: /usr/sbin/nginx -t
      register: check_nginx_config
      changed_when:
        - (check_nginx_config.stdout.find('successful'))  #如果/usr/sbin/nginx -t執行結果中有successful字元串,則繼續執行,沒有則停止向下執行
        - false       #/usr/sbin/nginx -t 每次成功執行是changed狀态,關閉此change狀态
    - name: start service
      service: name=nginx state=started enabled=yes

  handlers:
    - name: restart nginx
      service: name=nginx state=restarted      

1.8 yaml 檔案的互相調用

利用include 或 include_tasks 可以在某個task中調用其他的隻有task内容的yaml 檔案

[root@ansible ansible]#vim a.yml 
---
- hosts: web

  tasks:
    - name: run a job
      command: wall run a job
    - name: join b.yml    
      include: b.yml      #調用另一個隻有task内容的yaml檔案
[root@ansible ansible]#vim b.yml 
- name: run b job
  command: wall run b job      

使用 import_playbook 将多個包含完整内容的yml檔案由一個yml統一調用

[root@ansible ansible]#cat total_tasks.yml 
- import_playbook: task1.yml
- import_playbook: task2.yml
[root@ansible ansible]#cat task1.yml task2.yml 
---
- hosts: web
  gather_facts: false

  tasks:
    - name: run task1 job
      command: wall run task1 job
---
- hosts: web
  gather_facts: false

  tasks:
    - name: run task2 job
      command: wall run task2 job      

2 roles 角色

roles就是通過分别将變量、檔案、任務、模闆及處理器放置在單獨的目錄中,并可以使用便捷地include它們的一種機制。角色一般用于基于主機建構服務的場景中,也可以用于建構守護程序等場景中。

roles: 多個角色的集合目錄,可以将多個role,分别放在roles目錄下的獨立子目錄中,代碼複用度高

[root@ansible ansible]#tree roles/
roles/
├── mysql
│   ├── files
│   │   └── main.yml
│   ├── handlers
│   │   └── main.yml
│   ├── services
│   │   └── main.yml
│   ├── tasks
│   │   └── main.yml
│   ├── templates
│   │   └── main.yml
│   └── vars
│       └── main.yml
└── nginx
    ├── files
    │   └── main.yml
    ├── handlers
    │   └── main.yml
    ├── services
    │   └── main.yml
    ├── tasks
    │   └── main.yml
    ├── templates
    │   └── main.yml
    └── vars
        └── main.yml      

預設存放roles路徑

/root/.ansible/roles
/usr/share/ansible/roles
/etc/ansible/roles      

2.1 建立 role

  1. 建立roles的目錄結構,在以roles命名目錄下分别建立以各角色名稱命名的目錄,如nginx,mysql等,在每個角色命名的目錄中分别建立相關目錄和檔案,如tasks、files、handlers、templates、vars等目錄;
  2. 編寫和準備role的功能檔案
  3. 編寫playbook檔案調用需要的角色應用于指定的主機
[root@ansible ansible]#mkdir roles/{nginx,mysql}/{tasks,files,templates,vars,handlers} -pv
[root@ansible ansible]#touch  roles/{nginx,mysql}/{tasks,vars,templates,files,handlers}/main.yml      

2.2 playbook 調用角色

[root@ansible ansible]#vim role.yml   # 和roles目錄平級建立調用角色檔案
---
- hosts: localhost
  roles:
    - nginx
    - mysql      

2.3 案例: nginx 角色

#建立task檔案
[root@ansible nginx]#cat tasks/main.yml 
- include: group.yml
- include: user.yml
- include: install.yml
- include: config.yml
- include: data.yml
- include: service.yml
[root@ansible nginx]#cat tasks/user.yml 
- name: add user nginx
  user: name=nginx state=present group=nginx
[root@ansible nginx]#cat tasks/install.yml 
- name: Install Nginx
  yum: name=nginx state=present
[root@ansible nginx]#cat tasks/config.yml 
- name: Config file
  template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf 
  tags: conf
  notify:
    - Restart Nginx
[root@ansible nginx]#cat tasks/data.yml 
- name: web page
  copy: src=index.html dest=/usr/share/nginx/html/index.html
[root@ansible nginx]#cat tasks/service.yml 
- name: start Nginx
  service: name=nginx state=started

#建立handlers檔案
[root@ansible nginx]#cat handlers/main.yml 
- name: Restart Nginx
  service: name=nginx state=restarted
#建立file檔案,也可以跨角色調用檔案
[root@ansible nginx]#cat files/index.html 
<h1> nginx websize </h1>
#建立templates檔案
[root@ansible nginx]#vim templates/nginx.conf.j2 
...省略...
user {{ web_user }};
worker_processes {{ ansible_processor_vcpus+2 }};
...省略...
#建立vars變量檔案
[root@ansible nginx]#cat vars/main.yml 
web_user: nginx

#目錄結構如下
[root@ansible nginx]#tree
.
├── files
│   └── index.html
├── handlers
│   └── main.yml
├── tasks
│   ├── config.yml
│   ├── data.yml
│   ├── group.yml
│   ├── install.yml
│   ├── main.yml
│   ├── service.yml
│   └── user.yml
├── templates
│   └── nginx.conf.j2
└── vars
    └── main.yml

#在playbook中調用角色
[root@ansible ~]#vim /data/ansible/role_nginx.yml
---
# nginx role
- hosts: web

  roles:
    - nginx      

繼續閱讀