
一、ansible介紹
Ansible Playbooks 提供了一個(gè)可重復(fù)、可重用、簡單的配置管理和多機(jī)部署系統(tǒng),非常適合部署復(fù)雜的應(yīng)用程序。Ansible Playbook 是自動(dòng)化任務(wù)的藍(lán)圖,這些任務(wù)是復(fù)雜的 IT 操作,在有限或沒有人為參與的情況下執(zhí)行。Ansible Playbook 在一組、組或分類的主機(jī)上執(zhí)行,它們共同構(gòu)成一個(gè) Ansible 清單。
Ansible Playbook 本質(zhì)上是框架,是預(yù)先編寫的代碼,開發(fā)人員可以使用 ad-hoc 或作為起始模板。Ansible Playbooks 經(jīng)常用于自動(dòng)化IT 基礎(chǔ)設(shè)施(例如操作系統(tǒng)和Kubernetes平臺(tái))、網(wǎng)絡(luò)、安全系統(tǒng)和開發(fā)人員角色(例如 Git)。
Ansible Playbooks 可幫助 IT 人員對(duì)應(yīng)用程序、服務(wù)、服務(wù)器節(jié)點(diǎn)或其他設(shè)備進(jìn)行編程,而無需從頭開始創(chuàng)建所有內(nèi)容的手動(dòng)開銷。Ansible Playbook 以及其中的條件、變量和任務(wù)可以無限期地保存、共享或重復(fù)使用。
- playbook 是由一個(gè)或多個(gè)play組成的列表。
 - play的主要功能在于將直線歸并為一組的主機(jī)裝扮實(shí)現(xiàn)通過ansible中的task定義好的角色。從根本來講,所謂的task無非是調(diào)用ansible的一個(gè)module。將多個(gè)play組織在一個(gè)playbook內(nèi),即可以讓它們聯(lián)動(dòng)起來按實(shí)現(xiàn)編排的機(jī)制唱一臺(tái)大戲。
 - playbook采用YAML語言編寫。
 
二、ansible playbook 如何工作?
Ansible 模塊執(zhí)行任務(wù)??梢越M合一個(gè)或多個(gè) Ansible 任務(wù)來進(jìn)行游戲??梢越M合兩個(gè)或多個(gè)劇本來創(chuàng)建 Ansible Playbook。Ansible Playbook 是針對(duì)主機(jī)自動(dòng)執(zhí)行的任務(wù)列表。主機(jī)組構(gòu)成您的 Ansible 清單。
Ansible Playbook 中的每個(gè)模塊都執(zhí)行特定的任務(wù)。每個(gè)模塊都包含元數(shù)據(jù),這些元數(shù)據(jù)確定執(zhí)行任務(wù)的時(shí)間和地點(diǎn),以及執(zhí)行任務(wù)的用戶。還有數(shù)以千計(jì)的其他 Ansible 模塊可以執(zhí)行各種 IT 任務(wù)。
三、什么是 yaml?
YAML是一個(gè)可讀性高的用來表達(dá)資料序列的格式,它實(shí)際上是一種標(biāo)記語言。不論是在運(yùn)維工作中還是開發(fā)工作中,yaml語言都是一個(gè)很普遍被使用的,比如:Kubernetes 中的部署清單文件、GitLab CICD、Python使用yaml格式做配置文件、json 格式的數(shù)據(jù)需要被轉(zhuǎn)成 yaml 格式的數(shù)據(jù)等等。這里不對(duì)yaml語法做更多的介紹,詳情可以自行和百度、谷歌合作了解。
四、yaml 語言的特性
- YAML的可讀性好。
 - YAML和腳本語言的交互性好。
 - YAML使用實(shí)現(xiàn)語言的數(shù)據(jù)類型。
 - YAML有一個(gè)一致的信息模型。
 - YAML易于實(shí)現(xiàn)。
 - YAML可以基于流來處理。
 - YAML表達(dá)能力強(qiáng),擴(kuò)展性好。
 
五、ansible-playbook的核心組成部分
- Hosts:執(zhí)行的遠(yuǎn)程主機(jī)列表。
 - Tasks:任務(wù)集。
 - Variables:內(nèi)置變量或自定義變量在playbook中調(diào)用。
 - Templates:模板,可替換模板中的變量并實(shí)現(xiàn)一些簡單的邏輯的文件。
 - Handlers 和 notify:兩者結(jié)合使用,由特定條件觸發(fā)的操作,滿足條件方才執(zhí)行,否則不執(zhí)行。
 - Tags:標(biāo)簽,用于制定某條任務(wù)執(zhí)行,用戶選擇運(yùn)行playbook中的部分代碼,ansible具有冪等性,因此會(huì)自動(dòng)跳過沒有辯護(hù)的部分,即便如此,有的代碼為測(cè)試其確實(shí)沒有發(fā)生變化的時(shí)間依然會(huì)非常的長,此時(shí)確信其沒有變化,就可以通過tags跳過這些代碼片段。
 
(1)ansible-playbook 的項(xiàng)目 目錄結(jié)構(gòu)
[root@ayunw ansible-project]# ll
total 28
-rw-r--r--. 1 root root 122 Jul  8 10:14 00_setup.yml
-rw-r--r--. 1 root root  84 Jul  7 14:42 01_publish_ssh_key.yml
-rw-r--r--. 1 root root  78 Jul  8 14:11 02_common.yml
-rw-r--r--. 1 root root  85 Jul  8 10:34 03_install_docker.yml
drwxr-xr-x. 3 root root 124 Jul 11 09:15 files
drwxr-xr-x. 2 root root  80 Jul  8 15:26 inventory
-rw-r--r--. 1 root root 778 Jul  7 15:16 README.md
drwxr-xr-x. 5 root root  57 Jul  7 18:30 roles
[root@ayunw ansible-project]# tree roles/ -L 3
roles/
├── docker
│   ├── defaults
│   │   └── main.yml
│   ├── handlers
│   │   └── main.yml
│   ├── meta
│   │   └── main.yml
│   ├── tasks
│   │   └── main.yml
│   ├── templates
│   │   ├── daemon.json.j2
│   │   └── docker-ce.repo.j2
│   └── vars
│       └── main.yml
以上是項(xiàng)目是docker批量安裝。這個(gè)目錄格式是我這邊安裝項(xiàng)目的目錄規(guī)范。但并不是說每個(gè)目錄下的main.yml?文件都存在內(nèi)容,其實(shí)很多時(shí)候我們可能用不到default/main.yml和meta/main.yml。
(2)Hosts 遠(yuǎn)程主機(jī)列表
playbook中的每一個(gè)play的目的都是為了讓特定主機(jī)以某個(gè)指定的用戶身份執(zhí)行任務(wù)。hosts用于指定要執(zhí)行指定任務(wù)的主機(jī),須事先定義在主機(jī)清單中。比如我們之前說的默認(rèn)在/etc/ansible/hosts文件中:
[root@ayunw ansible-example]# cat /etc/ansible/hosts
[websrvs]
10.10.108.[30:33]
[dbsrvs]
10.10.108.30
[appsrvs]
10.10.108.[30:33]
(3)remote_user 遠(yuǎn)程用戶
可用于Host和Task中,也可以通過指定其通過sudo的方式在遠(yuǎn)程主機(jī)上執(zhí)行任務(wù),其可用于play全局或某任務(wù)。此外,還可以在sudo時(shí)使用sudo_user指定sudo時(shí)切換的用戶。
[root@ayunw ansible-example]# cat demo-playbook.yml
- hosts: dbsrvs
  remote_user: root   
  tasks:
    - name: pingtest       
      ping:
      remote_user: ayunw
      sudo: yes        # 默認(rèn)sudo為root
      sudo_user: root  # sudo 為root
(4)Tasks 任務(wù)集
簡單來說,Tasks 任務(wù)集其實(shí)就是使用多個(gè)ansible支持的模塊組合起來的一組任務(wù)??梢岳斫鉃?ansible-playbook 中,一個(gè)name指定的就是一個(gè)task任務(wù)。各個(gè)task按次序逐個(gè)在hosts中指定的所有主機(jī)上執(zhí)行,即在所有主機(jī)上完成第一個(gè)task后,再開始第二個(gè)task。但是也可以使用異步模式。這個(gè)后面文章會(huì)說;
task的目的是使用指定的參數(shù)執(zhí)行模塊,而在模塊參數(shù)中可以使用變量。模塊執(zhí)行是冪等的,這意味著多次執(zhí)行是安全的,因?yàn)槠浣Y(jié)果均一致。
每個(gè)task都應(yīng)該有其name,用于playbook的執(zhí)行結(jié)果輸出,建議起一個(gè)見名知意的名稱。
task的兩種格式:
一種是key=value?的形式,另一種是key: value的形式。
注意: 后者冒號(hào)后面有一個(gè)空格。
[root@ayunw ansible-project]# cat install_httpd.yml
---
- hosts: dbsrvs
  remote_user: root
  tasks:
  - name: install httpd #描述信息
    yum: name=httpd     #調(diào)用yum模塊安裝httpd服務(wù)
  - name: start httpd #同樣是描述信息
    service: name=httpd state=started enabled=yes #調(diào)用service模塊啟動(dòng)httpd服務(wù)并設(shè)置開機(jī)自啟
[root@ayunw ansible-project]# cat copy_files.yml
---
- hosts: dbsrvs
  remote_user: root
  tasks:
  - name: install httpd
    yum: name=httpd
  - name: start httpd
    service:
      name: httpd
      state: started
      enabled: yes
以上示例中,我加了空行,存在空行和帶有 ??#?? 注釋的行不會(huì)影響 yaml 文件執(zhí)行,看上去更加美觀,而不是所有 task 擠在一起,看上去一團(tuán)糟。
(5)Variables 變量
通常我們會(huì)將變量信息放在roles/vars/main.yml中,格式如下:
[root@ayunw ansible-project]# cat roles/docker/vars/main.yml
EMQXNAME: emq_perf
DEPEND_PKG:
  - yum-utils
  - device-mapper-persistent-data
  - lvm2
  - bridge-utils
DOCKER_PKG:
  - containerd.io-1.6.6
  - docker-ce-20.10.17
  - docker-ce-cli-20.10.17
(6)Templates 模板
Templates 模板主要使用Jinjia2?模板語言,以 .j2結(jié)尾,里面其實(shí)就是一個(gè)配置文件,比如:
[root@ayunw ansible-project]# cat roles/docker/templates/daemon.json.j2
{
    "exec-opts": ["native.cgroupdriver=systemd"],
    "log-driver": "json-file",
    "log-level": "warn",
    "log-opts": {
       "max-size": "1g",
       "max-file": "4"
    },
    "data-root": "/data/docker",
    "storage-driver": "overlay2"
}
(7)handler 和 notify
這兩個(gè)通常結(jié)合使用,比如某一個(gè)服務(wù)配置變更后,需要重啟,那么就需要在配置變更后設(shè)置一個(gè) notify,然后 handlers 就會(huì)在playbook退出之前執(zhí)行重啟服務(wù)的操作。如果定義了handler重啟服務(wù),而沒有定義notify,那么所有task任務(wù)執(zhí)行完成后,也會(huì)觸發(fā)一次服務(wù)重啟操作。
兩個(gè)典型的示例:
示例來源于: https://docs.ansible.com/ansible/latest/user_guide/playbooks_handlers.html
示例一:?
---
- name: Verify apache installation
  hosts: webservers
  vars:
    http_port: 80
    max_clients: 200
  remote_user: root
  tasks:
    - name: Ensure apache is at the latest version
      ansible.builtin.yum:
        name: httpd
        state: latest
    - name: Write the apache config file
      ansible.builtin.template:
        src: /srv/httpd.j2
        dest: /etc/httpd.conf
      notify:
      - Restart apache
    - name: Ensure apache is running
      ansible.builtin.service:
        name: httpd
        state: started
  handlers:
    - name: Restart apache
      ansible.builtin.service:
        name: httpd
        state: restarted
示例二:
tasks:
- name: Template configuration file
  ansible.builtin.template:
    src: template.j2
    dest: /etc/foo.conf
  notify:
    - Restart apache
    - Restart memcached
handlers:
  - name: Restart memcached
    ansible.builtin.service:
      name: memcached
      state: restarted
  - name: Restart apache
    ansible.builtin.service:
      name: apache
      state: restarted
(8)Tags 標(biāo)簽
我們可以在ansible-playbook的每一個(gè)task任務(wù)上打上 tag 標(biāo)簽,可以用于區(qū)分某一種類型的任務(wù)。如果你想要單獨(dú)執(zhí)行這個(gè)獨(dú)有的tag標(biāo)簽的任務(wù),就可以在使用ansible-playbook命令加上 -t 參數(shù)來指定 tag 執(zhí)行劇本。如:
ansible-playbook -t ayunw install_docker.yml
?六、ansible-playbook 命令
命令格式:ansible-playbook <filename.yml>...[options]
常見的 options 選項(xiàng):
-C --check       # 只檢測(cè)可能會(huì)發(fā)生的改變,但不真正執(zhí)行操作
--list-hosts     # 列出運(yùn)行任務(wù)的主機(jī)
--limit          # 針對(duì)主機(jī)列表中的主機(jī)執(zhí)行
-v -vv -vvv      # 提示過程
示例:
ansible-playbook -C install_httpd.yaml
一個(gè)簡單示例:
[root@ayunw ansible-project]# cat copy_files.yml
---
- hosts: dbsrvs
  tasks:
  - name: copy multi files
    copy: src={{ item }} dest="/etc/yum.repos.d/" owner=root group=root mode=0644
    with_items:
      - "files/CentOS-Base.repo"
      - "files/epel.repo"
# 更推薦的方式:
[root@ayunw ansible-project]# cat copy_files.yml
---
- hosts: dbsrvs
  tasks:
  - name: copy multi files
    copy:
      src: "{{ item }}"
      dest: "/etc/yum.repos.d/"
      owner: root
      group: root
      mode: 0644
    with_items:
      - "files/CentOS-Base.repo"
      - "files/epel.repo"
執(zhí)行劇本:
[root@ayunw ansible-project]# ansible-playbook -C cf.yml
PLAY [dbsrvs] *******************************************************************************************************************************************************************
TASK [Gathering Facts] **********************************************************************************************************************************************************
ok: [10.10.108.30]
TASK [copy multi files] *********************************************************************************************************************************************************
ok: [10.10.108.30] => (item=files/CentOS-Base.repo)
ok: [10.10.108.30] => (item=files/epel.repo)
PLAY RECAP **********************************************************************************************************************************************************************
10.10.108.30               : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
[root@ayunw ansible-project]# ansible-playbook copy_files.yml
以上內(nèi)容就是針對(duì)ansible-playbook劇本的一個(gè)介紹和簡單的使用。當(dāng)然它的功能遠(yuǎn)不止于此。還有更多更高級(jí)的用法。