1. 程式人生 > 其它 >運維自動化之ANSIBLE

運維自動化之ANSIBLE

運維自動化之ANSIBLE

本章內容

  • 運維自動化發展歷程及技術應用

  • Ansible命令使用

  • Ansible常用模組詳解

  • YAML語法簡介

  • Ansible playbook基礎

  • Playbook變數、tags、handlers使用

  • Playbook模板templates

  • Playbook條件判斷 when

  • Playbook字典 with_items

  • Ansible Roles

運維自動化發展歷程及技術應用

企業實際應用場景分析

Dev開發環境
使用者:程式設計師
功能:程式設計師開發軟體,測試BUG的環境
管理者:程式設計師

測試環境
使用者:QA測試工程師
功能:測試經過Dev環境測試通過的軟體的功能
管理者:運維

說明:測試環境往往有多套,測試環境滿足測試功能即可,不宜過多
1、測試人員希望測試環境有多套,公司的產品多產品線併發,即多個版本,意味著多個版本同步測試
2、通常測試環境有多少套和產品線數量保持一樣

釋出環境:程式碼釋出機,有些公司為堡壘機(安全屏障)
使用者:運維
功能:釋出程式碼至生產環境
管理者:運維(有經驗)
釋出機:往往需要有2臺(主備)

生產環境
使用者:運維,少數情況開放許可權給核心開發人員,極少數公司將許可權完全
開放給開發人員並其維護
功能:對使用者提供公司產品的服務

管理者:只能是運維
生產環境伺服器數量:一般比較多,且應用非常重要。往往需要自動工具協助部署配置應用

灰度環境(生產環境的一部分)
使用者:運維
功能:在全量釋出程式碼前將程式碼的功能面向少量精準使用者釋出的環境,可基
於主機或使用者執行灰度釋出
案例:共100臺生產伺服器,先發布其中的10臺伺服器,這10臺伺服器就是灰度伺服器
管理者:運維
灰度環境:往往該版本功能變更較大,為保險起見特意先讓一部分使用者優化體驗該功能,
待這部分使用者使用沒有重大問題的時候,再全量釋出至所有伺服器

程式釋出

程式釋出要求:
不能導致系統故障或造成系統完全不可用
不能影響使用者體驗
預釋出驗證:
新版本的程式碼先發布到伺服器(跟線上環境配置完全相同,只是未接入到排程器)
灰度釋出:
基於主機,使用者,業務
釋出路徑:
/webapp/tuangou
/webapp/tuangou-1.1
/webapp/tuangou-1.2
釋出過程:在排程器上下線一批主機(標記為maintanance狀態) --> 關閉服務 -->
部署新版本的應用程式 --> 啟動服務 --> 在排程器上啟用這一批伺服器
自動化灰度釋出:指令碼、釋出平臺

運維自動化發展歷程及技術應用

自動化運維應用場景

檔案傳輸
應用部署
配置管理
任務流編排

常用自動化運維工具

Ansible:python,Agentless,中小型應用環境
Saltstack:python,一般需部署agent,執行效率更高
Puppet:ruby, 功能強大,配置複雜,重型,適合大型環境
Fabric:python,agentless
Chef:ruby,國內應用少
Cfengine
func

企業級自動化運維工具應用實戰ansible

公司計劃在年底做一次大型市場促銷活動,全面衝刺下交易額,為明年的上市做準備。
公司要求各業務組對年底大促做準備,運維部要求所有業務容量進行三倍的擴容,
並搭建出多套環境可以共開發和測試人員做測試,運維老大為了在年底有所表現,
要求運維部門同學儘快實現,當你接到這個任務時,有沒有更快的解決方案?

Ansible發展史

Ansible
Michael DeHaan( Cobbler 與 Func 作者)
名稱來自《安德的遊戲》中跨越時空的即時通訊工具
2012-03-09,釋出0.0.1版,2015-10-17,Red Hat宣佈收購
官網:https://www.ansible.com/
官方文件:https://docs.ansible.com/
同類自動化工具GitHub關注程度(2016-07-10)

特性

1> 模組化:呼叫特定的模組,完成特定任務
2> Paramiko(python對ssh的實現),PyYAML,Jinja2(模板語言)三個關鍵模組
3> 支援自定義模組
4> 基於Python語言實現
5> 部署簡單,基於python和SSH(預設已安裝),agentless
6> 安全,基於OpenSSH
7> 支援playbook編排任務
8> 冪等性:一個任務執行1遍和執行n遍效果一樣,不因重複執行帶來意外情況
9> 無需代理不依賴PKI(無需ssl)
10> 可使用任何程式語言寫模組
11> YAML格式,編排任務,支援豐富的資料結構
12> 較強大的多層解決方案

Ansible架構

ansible的作用以及工作結構
1、ansible簡介:
ansible是新出現的自動化運維工具,基於Python開發,
集合了眾多運維工具(puppet、cfengine、chef、func、fabric)的優點,
實現了批量系統配置、批量程式部署、批量執行命令等功能。
ansible是基於模組工作的,本身沒有批量部署的能力。
真正具有批量部署的是ansible所執行的模組,ansible只是提供一種框架。
主要包括:
(1)、連線外掛connection plugins:負責和被監控端實現通訊;
(2)、host inventory:指定操作的主機,是一個配置檔案裡面定義監控的主機;
(3)、各種模組核心模組、command模組、自定義模組;
(4)、藉助於外掛完成記錄日誌郵件等功能;
(5)、playbook:劇本執行多個任務時,非必需可以讓節點一次性執行多個任務。

2、ansible的架構:連線其他主機預設使用ssh協議

Ansible工作原理

Ansible主要組成部分

ANSIBLE PLAYBOOKS:任務劇本(任務集),編排定義Ansible任務集的配置檔案,
                   由Ansible順序依次執行,通常是JSON格式的YML檔案
INVENTORY:Ansible管理主機的清單  /etc/anaible/hosts
MODULES:  Ansible執行命令的功能模組,多數為內建核心模組,也可自定義
PLUGINS:  模組功能的補充,如連線型別外掛、迴圈外掛、變數外掛、過濾外掛等,該功能不常用
API:      供第三方程式呼叫的應用程式程式設計介面 
ANSIBLE:  組合INVENTORY、API、MODULES、PLUGINS的綠框,可以理解為是ansible命令工具,其為核心執行工具
Ansible命令執行來源:
    1> USER,普通使用者,即SYSTEM ADMINISTRATOR
    2> CMDB(配置管理資料庫) API 呼叫
    3> PUBLIC/PRIVATE CLOUD API呼叫  (公有私有云的API介面呼叫)
    4> USER-> Ansible Playbook -> Ansibile

利用ansible實現管理的方式:
    1> Ad-Hoc 即ansible單條命令,主要用於臨時命令使用場景
    2> Ansible-playbook 主要用於長期規劃好的,大型專案的場景,需要有前期的規劃過程
Ansible-playbook(劇本)執行過程
    將已有編排好的任務集寫入Ansible-Playbook
    通過ansible-playbook命令分拆任務集至逐條ansible命令,按預定規則逐條執行

Ansible主要操作物件
   HOSTS主機
   NETWORKING網路裝置

注意事項:
   執行ansible的主機一般稱為主控端,中控,master或堡壘機
   主控端Python版本需要2.6或以上
   被控端Python版本小於2.4需要安裝python-simplejson
   被控端如開啟SELinux需要安裝libselinux-python
   windows不能做為主控端
   ansible不是服務,不會一直啟動,只是需要的時候啟動

安裝

rpm包安裝: EPEL源
    yum install ansible

編譯安裝:
    yum -y install python-jinja2 PyYAML python-paramiko python-babel
    python-crypto
    tar xf ansible-1.5.4.tar.gz
    cd ansible-1.5.4
    python setup.py build
    python setup.py install
    mkdir /etc/ansible
    cp -r examples/* /etc/ansible


Git方式:
    git clone git://github.com/ansible/ansible.git --recursive
    cd ./ansible
    source ./hacking/env-setup

pip安裝: pip是安裝Python包的管理器,類似yum
    yum install python-pip python-devel
    yum install gcc glibc-devel zibl-devel rpm-bulid openssl-devel
    pip install --upgrade pip
    pip install ansible --upgrade

確認安裝:
    ansible --version

相關檔案

配置檔案
    /etc/ansible/ansible.cfg  主配置檔案,配置ansible工作特性(一般無需修改)
    /etc/ansible/hosts        主機清單(將被管理的主機放到此檔案)
    /etc/ansible/roles/       存放角色的目錄

程式
    /usr/bin/ansible          主程式,臨時命令執行工具
    /usr/bin/ansible-doc      檢視配置文件,模組功能檢視工具
    /usr/bin/ansible-galaxy   下載/上傳優秀程式碼或Roles模組的官網平臺
    /usr/bin/ansible-playbook 定製自動化任務,編排劇本工具
    /usr/bin/ansible-pull     遠端執行命令的工具
    /usr/bin/ansible-vault    檔案加密工具
    /usr/bin/ansible-console  基於Console介面與使用者互動的執行工具

主機清單inventory

Inventory 主機清單
1> ansible的主要功用在於批量主機操作,為了便捷地使用其中的部分主機,可以在inventory file中將其分組命名 
2> 預設的inventory file為/etc/ansible/hosts
3> inventory file可以有多個,且也可以通過Dynamic Inventory來動態生成

/etc/ansible/hosts檔案格式
inventory檔案遵循INI檔案風格,中括號中的字元為組名。
可以將同一個主機同時歸併到多個不同的組中;
此外,當如若目標主機使用了非預設的SSH埠,還可以在主機名稱之後使用冒號加埠號來標明
    ntp.magedu.com   不分組,直接加
    
    [webservers]     webservers組
    www1.magedu.com:2222  可以指定埠
    www2.magedu.com
    
    [dbservers]
    db1.magedu.com
    db2.magedu.com
    db3.magedu.com

如果主機名稱遵循相似的命名模式,還可以使用列表的方式標識各主機
示例:
    [websrvs]
    www[1:100].example.com   ip: 1-100
    
    [dbsrvs]
    db-[a:f].example.com     dba-dbff

ansible 配置檔案

Ansible 配置檔案/etc/ansible/ansible.cfg (一般保持預設)

vim /etc/ansible/ansible.cfg

[defaults]
#inventory     = /etc/ansible/hosts      # 主機列表配置檔案
#library       = /usr/share/my_modules/  # 庫檔案存放目錄
#remote_tmp    = $HOME/.ansible/tmp      # 臨時py命令檔案存放在遠端主機目錄
#local_tmp     = $HOME/.ansible/tmp      # 本機的臨時命令執行目錄  
#forks         = 5                       # 預設併發數,同時可以執行5次
#sudo_user     = root                    # 預設sudo 使用者
#ask_sudo_pass = True                    # 每次執行ansible命令是否詢問ssh密碼
#ask_pass      = True                    # 每次執行ansible命令是否詢問ssh口令
#remote_port   = 22                      # 遠端主機的埠號(預設22)

建議優化項: 
host_key_checking = False               # 檢查對應伺服器的host_key,建議取消註釋
log_path=/var/log/ansible.log           # 日誌檔案,建議取消註釋
module_name   = command                 # 預設模組

ansible系列命令

Ansible系列命令
    ansible ansible-doc ansible-playbook ansible-vault ansible-console
    ansible-galaxy ansible-pull

ansible-doc: 顯示模組幫助
    ansible-doc [options] [module...]
        -a            顯示所有模組的文件
        -l, --list    列出可用模組
        -s, --snippet 顯示指定模組的playbook片段(簡化版,便於查詢語法)

示例:
    ansible-doc -l      列出所有模組
    ansible-doc ping    檢視指定模組幫助用法
    ansible-doc -s ping 檢視指定模組幫助用法

ansible

ansible通過ssh實現配置管理、應用部署、任務執行等功能,
建議配置ansible端能基於金鑰認證的方式聯絡各被管理節點

ansible <host-pattern> [-m module_name] [-a args]
ansible +被管理的主機(ALL) +模組  +引數
    --version              顯示版本
    -m module              指定模組,預設為command
    -v                     詳細過程 –vv -vvv更詳細
    --list-hosts           顯示主機列表,可簡寫 --list
    -k, --ask-pass         提示輸入ssh連線密碼,預設Key驗證
    -C, --check            檢查,並不執行
    -T, --timeout=TIMEOUT  執行命令的超時時間,預設10s
    -u, --user=REMOTE_USER 執行遠端執行的使用者
    -b, --become           代替舊版的sudo切換
        --become-user=USERNAME 指定sudo的runas使用者,預設為root
    -K, --ask-become-pass  提示輸入sudo時的口令
ansible all --list  列出所有主機
ping模組: 探測網路中被管理主機是否能夠正常使用  走ssh協議
          如果對方主機網路正常,返回pong
ansible-doc -s ping   檢視ping模組的語法 

檢測所有主機的網路狀態
1>  預設情況下連線被管理的主機是ssh基於key驗證,如果沒有配置key,許可權將會被拒絕
    因此需要指定以誰的身份連線,輸入使用者密碼,必須保證被管理主機使用者密碼一致
    ansible all -m ping -k

2> 或者實現基於key驗證 將公鑰ssh-copy-id到被管理的主機上 , 實現免密登入
   ansible all -m ping

ansible的Host-pattern

ansible的Host-pattern
匹配主機的列表
    All :表示所有Inventory中的所有主機
        ansible all –m ping
    * :萬用字元
        ansible "*" -m ping  (*表示所有主機)
        ansible 192.168.1.* -m ping
        ansible "*srvs" -m ping
    或關係 ":"
        ansible "websrvs:appsrvs" -m ping
        ansible “192.168.1.10:192.168.1.20” -m ping
    邏輯與 ":&"
        ansible "websrvs:&dbsrvs" –m ping
        在websrvs組並且在dbsrvs組中的主機
    邏輯非 ":!"
        ansible 'websrvs:!dbsrvs' –m ping
        在websrvs組,但不在dbsrvs組中的主機
        注意:此處為單引號
    綜合邏輯
        ansible 'websrvs:dbsrvs:&appsrvs:!ftpsrvs' –m ping
    正則表示式
        ansible "websrvs:&dbsrvs" –m ping
        ansible "~(web|db).*\.magedu\.com" –m ping

ansible命令執行過程

ansible命令執行過程
    1. 載入自己的配置檔案 預設/etc/ansible/ansible.cfg
    2. 載入自己對應的模組檔案,如command
    3. 通過ansible將模組或命令生成對應的臨時py檔案,
       並將該檔案傳輸至遠端伺服器的對應執行使用者$HOME/.ansible/tmp/ansible-tmp-數字/XXX.PY檔案
    4. 給檔案+x執行
    5. 執行並返回結果
    6. 刪除臨時py檔案,sleep 0退出

執行狀態:
    綠色:執行成功並且不需要做改變的操作
    黃色:執行成功並且對目標主機做變更
    紅色:執行失敗

ansible使用示例

示例
    以wang使用者執行ping存活檢測
        ansible all -m ping -u wang -k
    以wang sudo至root執行ping存活檢測
        ansible all -m ping -u wang -k -b
    以wang sudo至mage使用者執行ping存活檢測
        ansible all -m ping -u wang -k -b --become-user=mage
    以wang sudo至root使用者執行ls
        ansible all -m command -u wang -a 'ls /root' -b --become-user=root -k -K

ansible ping模組測試連線
    ansible 192.168.38.126,192.168.38.127 -m ping -k 

ansible常用模組

模組文件:https://docs.ansible.com/ansible/latest/modules/modules_by_category.html

Command:在遠端主機執行命令,預設模組,可忽略-m選項
    > ansible srvs -m command -a 'service vsftpd start'
    > ansible srvs -m command -a 'echo adong |passwd --stdin 123456'
此命令不支援 $VARNAME < > | ; & 等,用shell模組實現

    chdir:   進入到被管理主機目錄
    creates: 如果有一個目錄是存在的,步驟將不會執行Command命令
    ansible websrvs -a 'chdir=/data/ ls'

Shell:和command相似,用shell執行命令
    > ansible all -m shell  -a 'getenforce'  檢視SELINUX狀態
    >  ansible all -m shell  -a "sed -i 's/SELINUX=.*/SELINUX=disabled' /etc/selinux/config"
    > ansible srv -m shell -a 'echo magedu |passwd –stdin wang'
      
    呼叫bash執行命令 類似 cat /tmp/stanley.md | awk -F'|' '{print $1,$2}' &> /tmp/example.txt     
    這些複雜命令,即使使用shell也可能會失敗,
    解決辦法:寫到指令碼時,copy到遠端執行,再把需要的結果拉回執行命令的機器

    修改配置檔案,使shell作為預設模組    
        vim /etc/ansible/ansible.cfg
        module_name = shell

Script:在遠端主機上執行ansible伺服器上的指令碼
    > -a "/PATH/TO/SCRIPT_FILE"
    > ansible websrvs -m script -a /data/test.sh

Copy:從主控端複製檔案到遠端主機
      src : 原始檔  指定拷貝檔案的本地路徑  (如果有/ 則拷貝目錄內容,比拷貝目錄本身)
      dest: 指定目標路徑
      mode: 設定許可權
      backup: 備份原始檔
      content: 代替src  指定本機檔案內容,生成目標主機檔案
      
      > ansible websrvs -m copy -a "src=/root/test1.sh dest=/tmp/test2.showner=wang mode=600 backup=yes"
        如果目標存在,預設覆蓋,此處指定先備份
      > ansible websrvs -m copy -a "content='test content\nxxx' dest=/tmp/test.txt"
        指定內容,直接生成目標檔案

Fetch:從遠端主機提取檔案至主控端,copy相反,目前不支援目錄,可以先打包,再提取檔案
     > ansible websrvs -m fetch -a 'src=/root/test.sh dest=/data/scripts'
     會生成每個被管理主機不同編號的目錄,不會發生檔名衝突
     
     > ansible all -m shell -a 'tar jxvf test.tar.gz /root/test.sh'
     > ansible all -m fetch -a 'src=/root/test.tar.gz dest=/data/'

File:設定檔案屬性
    path: 要管理的檔案路徑 (強制新增)
    recurse: 遞迴,資料夾要用遞迴
    src:  建立硬連結,軟連結時,指定源目標,配合'state=link' 'state=hard' 設定軟連結,硬連結
    state: 狀態
          absent 缺席,刪除
          
    > ansible websrvs -m file -a 'path=/app/test.txt state=touch'       建立檔案
    > ansible websrvs -m file -a "path=/data/testdir state=directory"   建立目錄    
    > ansible websrvs -m file -a "path=/root/test.sh owner=wang mode=755"  設定許可權755
    > ansible websrvs -m file -a 'src=/data/testfile dest=/data/testfile-link state=link' 建立軟連結
    
    
unarchive:解包解壓縮,有兩種用法:
    1、將ansible主機上的壓縮包傳到遠端主機後解壓縮至特定目錄,設定copy=yes.
    2、將遠端主機上的某個壓縮包解壓縮到指定路徑下,設定copy=no

    常見引數:
        copy:預設為yes,當copy=yes,拷貝的檔案是從ansible主機複製到遠端主機上,
              如果設定為copy=no,會在遠端主機上尋找src原始檔
        src: 源路徑,可以是ansible主機上的路徑,也可以是遠端主機上的路徑,
              如果是遠端主機上的路徑,則需要設定copy=no
        dest:遠端主機上的目標路徑
        mode:設定解壓縮後的檔案許可權
    
    示例:
        ansible websrvs -m unarchive -a 'src=foo.tgz dest=/var/lib/foo'  
          #預設copy為yes ,將本機目錄檔案解壓到目標主機對應目錄下
        ansible websrvs -m unarchive -a 'src=/tmp/foo.zip dest=/data copy=no mode=0777'
          # 解壓被管理主機的foo.zip到data目錄下, 並設定許可權777
        ansible websrvs -m unarchive -a 'src=https://example.com/example.zip dest=/data copy=no'

Archive:打包壓縮
    > ansible all -m archive -a 'path=/etc/sysconfig dest=/data/sysconfig.tar.bz2 format=bz2 owner=wang mode=0777'
    將遠端主機目錄打包 
        path:   指定路徑
        dest:   指定目標檔案
        format: 指定打包格式
        owner:  指定所屬者
        mode:   設定許可權

Hostname:管理主機名
    ansible appsrvs -m hostname -a "name=app.adong.com"  更改一組的主機名
    ansible 192.168.38.103 -m hostname -a "name=app2.adong.com" 更改單個主機名

Cron:計劃任務
    支援時間:minute,hour,day,month,weekday
    > ansible websrvs -m cron -a "minute=*/5 job='/usr/sbin/ntpdate 172.16.0.1 &>/dev/null' name=Synctime" 
    建立任務
    > ansible websrvs -m cron -a 'state=absent name=Synctime' 
    刪除任務
    > ansible websrvs -m cron -a 'minute=*/10 job='/usr/sbin/ntpdate 172.30.0.100" name=synctime disabled=yes'
    註釋任務,不在生效

Yum:管理包
    ansible websrvs -m yum -a 'list=httpd'  檢視程式列表
    
    ansible websrvs -m yum -a 'name=httpd state=present' 安裝
    ansible websrvs -m yum -a 'name=httpd state=absent'  刪除
    可以同時安裝多個程式包
    
Service:管理服務
    ansible srv -m service -a 'name=httpd state=stopped'  停止服務
    ansible srv -m service -a 'name=httpd state=started enabled=yes' 啟動服務,並設為開機自啟
    ansible srv -m service -a 'name=httpd state=reloaded'  重新載入
    ansible srv -m service -a 'name=httpd state=restarted' 重啟服務

User:管理使用者
    home   指定家目錄路徑
    system 指定系統賬號
    group  指定組
    remove 清除賬戶
    shell  指定shell型別
    
    ansible websrvs -m user -a 'name=user1 comment="test user" uid=2048 home=/app/user1 group=root'
    ansible websrvs -m user -a 'name=sysuser1 system=yes home=/app/sysuser1'
    ansible websrvs -m user -a 'name=user1 state=absent remove=yes'  清空使用者所有資料
    ansible websrvs -m user -a 'name=app uid=88 system=yes home=/app groups=root shell=/sbin/nologin password="$1$zfVojmPy$ZILcvxnXljvTI2PhP2Iqv1"'  建立使用者
    ansible websrvs -m user -a 'name=app state=absent'  不會刪除家目錄
    
    安裝mkpasswd 
    yum insatll expect 
    mkpasswd 生成口令
    openssl passwd -1  生成加密口令
    

刪除使用者及家目錄等資料
    Group:管理組
        ansible srv -m group -a "name=testgroup system=yes"   建立組
        ansible srv -m group -a "name=testgroup state=absent" 刪除組

ansible系列命令

可以通過網上寫好的
ansible-galaxy
    > 連線 https://galaxy.ansible.com 
      下載相應的roles(角色)
    
    > 列出所有已安裝的galaxy
        ansible-galaxy list
    
    > 安裝galaxy
        ansible-galaxy install geerlingguy.redis
    
    > 刪除galaxy
        ansible-galaxy remove geerlingguy.redis
        
ansible-pull
    推送命令至遠端,效率無限提升,對運維要求較高
    

ansible-playbook  可以引用按照標準的yml語言寫的指令碼
    執行playbook
    示例:ansible-playbook hello.yml
        cat hello.yml
        #hello world yml file
        - hosts: websrvs
          remote_user: root
          tasks:
            - name: hello world
              command: /usr/bin/wall hello world

ansible-vault  (瞭解)
功能:管理加密解密yml檔案
    ansible-vault [create|decrypt|edit|encrypt|rekey|view]
        ansible-vault encrypt hello.yml 加密
        ansible-vault decrypt hello.yml 解密
        ansible-vault view hello.yml    檢視
        ansible-vault edit hello.yml    編輯加密檔案
        ansible-vault rekey hello.yml   修改口令
        ansible-vault create new.yml    建立新檔案


Ansible-console:2.0+新增,可互動執行命令,支援tab  (瞭解)

    root@test (2)[f:10] $
    執行使用者@當前操作的主機組 (當前組的主機數量)[f:併發數]$

    設定併發數:         forks n   例如: forks 10
    切換組:             cd 主機組 例如: cd web
    列出當前組主機列表: list
    列出所有的內建命令: ?或help
    示例:
        root@all (2)[f:5]$ list
        root@all (2)[f:5]$ cd appsrvs
        root@appsrvs (2)[f:5]$ list
        root@appsrvs (2)[f:5]$ yum name=httpd state=present
        root@appsrvs (2)[f:5]$ service name=httpd state=started

playbook

> playbook是由一個或多個"play"組成的列表
> play的主要功能在於將預定義的一組主機,裝扮成事先通過ansible中的task定義好的角色。
  Task實際是呼叫ansible的一個module,將多個play組織在一個playbook中,
  即可以讓它們聯合起來,按事先編排的機制執行預定義的動作
> Playbook採用YAML語言編寫

playbook圖解

使用者通過ansible命令直接呼叫yml語言寫好的playbook,playbook由多條play組成
每條play都有一個任務(task)相對應的操作,然後呼叫模組modules,應用在主機清單上,通過ssh遠端連線
從而控制遠端主機或者網路裝置

YAML介紹

YAML是一個可讀性高的用來表達資料序列的格式。
    YAML參考了其他多種語言,包括:XML、C語言、Python、Perl以及電子郵件格式RFC2822等。
    Clark Evans在2001年在首次發表了這種語言,另外Ingy döt Net與Oren Ben-Kiki也是這語言的共同設計者

YAML Ain't Markup Language,即YAML不是XML。
不過,在開發的這種語言時,YAML的意思其實是:"Yet Another Markup Language"(仍是一種標記語言)

特性
    YAML的可讀性好
    YAML和指令碼語言的互動性好
    YAML使用實現語言的資料型別
    YAML有一個一致的資訊模型
    YAML易於實現
    YAML可以基於流來處理
    YAML表達能力強,擴充套件性好

更多的內容及規範參見:http://www.yaml.org

YAML語法簡介

> 在單一檔案中,可用連續三個連字號(——)區分多個檔案。
  另外,還有選擇性的連續三個點號( ... )用來表示檔案結尾
> 次行開始正常寫Playbook的內容,一般建議寫明該Playbook的功能
> 使用#號註釋程式碼
> 縮排必須是統一的,不能空格和tab混用
> 縮排的級別也必須是一致的,同樣的縮排代表同樣的級別,程式判別配置的級別是通過縮排結合換行來實現的
> YAML檔案內容是區別大小寫的,k/v的值均需大小寫敏感
> 多個k/v可同行寫也可換行寫,同行使用:分隔
> v可是個字串,也可是另一個列表[]
> 一個完整的程式碼塊功能需最少元素需包括 name 和 task
> 一個name只能包括一個task
> YAML副檔名通常為yml或yaml

YAML語法簡介

List:列表,其所有元素均使用“-”打頭
      列表代表同一型別的元素
示例:
# A list of tasty fruits
- Apple
- Orange
- Strawberry
- Mango

Dictionary:字典,通常由多個key與value構成 鍵值對
示例:
---
# An employee record
name: Example Developer
job: Developer
skill: Elite

也可以將key:value放置於{}中進行表示,用,分隔多個key:value
示例:
---
# An employee record
{name: Example Developer, job: Developer, skill: Elite}  有空格

YAML語法

YAML的語法和其他高階語言類似,並且可以簡單表達清單、散列表、標量等資料結構。
其結構(Structure)通過空格來展示,序列(Sequence)裡的項用"-"來代表,Map裡的鍵值對用":"分隔
示例
    name: John Smith
    age: 41
    gender: Male
    spouse:
      name: Jane Smith
      age: 37
      gender: Female
    children:
      - name: Jimmy Smith
        age: 17
        gender: Male
      - name: Jenny Smith
        age 13
        gender: Female

三種常見的資料交換格式

Playbook核心元素

Hosts          執行的遠端主機列表(應用在哪些主機上)

Tasks          任務集

Variables      內建變數或自定義變數在playbook中呼叫

Templates模板  可替換模板檔案中的變數並實現一些簡單邏輯的檔案

Handlers和notify結合使用,由特定條件觸發的操作,滿足條件方才執行,否則不執行

tags標籤       指定某條任務執行,用於選擇執行playbook中的部分程式碼。
                ansible具有冪等性,因此會自動跳過沒有變化的部分,
                即便如此,有些程式碼為測試其確實沒有發生變化的時間依然會非常地長。
                此時,如果確信其沒有變化,就可以通過tags跳過此些程式碼片斷
                ansible-playbook -t tagsname useradd.yml

playbook基礎元件

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

    > 可以是如下形式:
        one.example.com
        one.example.com:two.example.com
        192.168.1.50
        192.168.1.*
    > Websrvs:dbsrvs       或者,兩個組的並集
    > Websrvs:&dbsrvs      與,兩個組的交集
    > webservers:!phoenix  在websrvs組,但不在dbsrvs組
    示例: - hosts: websrvs:dbsrvs

remote_user: 
    可用於Host和task中。
    也可以通過指定其通過sudo的方式在遠端主機上執行任務,其可用於play全域性或某任務;
    此外,甚至可以在sudo時使用sudo_user指定sudo時切換的使用者
    - hosts: websrvs
        remote_user: root   (可省略,預設為root)  以root身份連線
      tasks:    指定任務
    - name: test connection
        ping:
        remote_user: magedu
        sudo: yes           預設sudo為root
        sudo_user:wang      sudo為wang
    
task列表和action
    任務列表task:由多個動作,多個任務組合起來的,每個任務都呼叫的模組,一個模組一個模組執行
    1> play的主體部分是task list,task list中的各任務按次序逐個在hosts中指定的所有主機上執行,
       即在所有主機上完成第一個任務後,再開始第二個任務

    2> task的目的是使用指定的引數執行模組,而在模組引數中可以使用變數。
       模組執行是冪等的,這意味著多次執行是安全的,因為其結果均一致

    3> 每個task都應該有其name,用於playbook的執行結果輸出,建議其內容能清晰地描述任務執行步驟。
       如果未提供name,則action的結果將用於輸出
tasks:任務列表
兩種格式:
    (1) action: module arguments
    (2) module: arguments 建議使用  模組: 引數
    注意:shell和command模組後面跟命令,而非key=value

某任務的狀態在執行後為changed時,可通過"notify"通知給相應的handlers

任務可以通過"tags"打標籤,可在ansible-playbook命令上使用-t指定進行呼叫
示例:
tasks:
  - name: disable selinux   描述
    command: /sbin/setenforce 0   模組名: 模組對應的引數
如果命令或指令碼的退出碼不為零,可以使用如下方式替代
tasks:
  - name: run this command and ignore the result
    shell: /usr/bin/somecommand || /bin/true  
    轉錯為正  如果命令失敗則執行 true

或者使用ignore_errors來忽略錯誤資訊
tasks:
  - name: run this command and ignore the result
    shell: /usr/bin/somecommand
    ignore_errors: True  忽略錯誤

執行playbook

執行playbook的方式
    ansible-playbook <filename.yml> ... [options]

常見選項
    --check -C       只檢測可能會發生的改變,但不真正執行操作 
                     (只檢查語法,如果執行過程中出現問題,-C無法檢測出來)
                     (執行playbook生成的檔案不存在,後面的程式如果依賴這些檔案,也會導致檢測失敗)
    --list-hosts     列出執行任務的主機
    --list-tags      列出tag  (列出標籤)
    --list-tasks     列出task (列出任務)
    --limit 主機列表 只針對主機列表中的主機執行
    -v -vv -vvv      顯示過程

示例
    ansible-playbook hello.yml --check 只檢測
    ansible-playbook hello.yml --list-hosts  顯示執行任務的主機
    ansible-playbook hello.yml --limit websrvs  限制主機

Playbook VS ShellScripts

安裝httpd

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,並設定開機啟動
service httpd start
chkconfig httpd on
Playbook定義
---
- hosts: all
  remote_user: root
  
  tasks:
    - name: "安裝Apache"
      yum: name=httpd       yum模組:安裝httpd
    - name: "複製配置檔案"
      copy: src=/tmp/httpd.conf dest=/etc/httpd/conf/  copy模組: 拷貝檔案
    - name: "複製配置檔案"
      copy: src=/tmp/vhosts.conf dest=/etc/httpd/conf.d/  
    - name: "啟動Apache,並設定開機啟動"
      service: name=httpd state=started enabled=yes   service模組: 啟動服務 

示例:Playbook 建立使用者

示例:sysuser.yml
---
- hosts: all
  remote_user: root

  tasks:
    - name: create mysql user
      user: name=mysql system=yes uid=36
    - name: create a group
      group: name=httpd system=yes

Playbook示例 安裝httpd服務

示例:httpd.yml
- hosts: websrvs
  remote_user: root

  tasks:
    - name: Install httpd
      yum: name=httpd state=present
    - name: Install configure file
      copy: src=files/httpd.conf dest=/etc/httpd/conf/
    - name: start service
      service: name=httpd state=started enabled=yes

Playbook示例 安裝nginx服務

示例 nginx.yml
- hosts: all
  remote_user: root

  tasks:
    - name: add group nginx
      user: name=nginx state=present
    - name: add user nginx
      user: name=nginx state=present group=nginx
    - name: Install Nginx
      yum: name=nginx state=present
    - name: Start Nginx
      service: name=nginx state=started enabled=yes

handlers和notify結合使用觸發條件

Handlers 實際上就是一個觸發器
是task列表,這些task與前述的task並沒有本質上的不同,用於當關注的資源發生變化時,才會採取一定的操作

Notify此action可用於在每個play的最後被觸發,
這樣可避免多次有改變發生時每次都執行指定的操作,僅在所有的變化發生完成後一次性地執行指定操作。
在notify中列出的操作稱為handler,也即notify中呼叫handler中定義的操作

Playbook中handlers使用

- hosts: websrvs
  remote_user: root

  tasks:
    - name: Install httpd
      yum: name=httpd state=present
    - name: Install configure file
      copy: src=files/httpd.conf dest=/etc/httpd/conf/
      notify: restart httpd
    - name: ensure apache is running
      service: name=httpd state=started enabled=yes
  
  handlers:
    - name: restart httpd
      service: name=httpd state=restarted

示例

- hosts: webnodes
  vars:
    http_port: 80
    max_clients: 256
  remote_user: root
  
  tasks:
    - name: ensure apache is at the latest version
      yum: name=httpd state=latest
    - name: ensure apache is running
      service: name=httpd state=started
    - name: Install configure file
      copy: src=files/httpd.conf dest=/etc/httpd/conf/
      notify: restart httpd
  
  handlers:
      - name: restart httpd 
        service: name=httpd state=restarted

示例

- hosts: websrvs
  remote_user: root
  
  tasks:
    - name: add group nginx
      tags: user
      user: 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
      copy: src=/root/config.txt dest=/etc/nginx/nginx.conf
      notify:
        - Restart Nginx
        - Check Nginx Process
  
  handlers:
    - name: Restart Nginx
      service: name=nginx state=restarted enabled=yes
    - name: Check Nginx process
      shell: killall -0 nginx > /tmp/nginx.log

Playbook中tags使用

tage: 新增標籤 
可以指定某一個任務新增一個標籤,新增標籤以後,想執行某個動作可以做出挑選來執行
多個動作可以使用同一個標籤

示例:httpd.yml
- hosts: websrvs
  remote_user: root
  
  tasks:
    - name: Install httpd
      yum: name=httpd state=present
      tage: install 
    - name: Install configure file
      copy: src=files/httpd.conf dest=/etc/httpd/conf/
      tags: conf
    - name: start httpd service
      tags: service
      service: name=httpd state=started enabled=yes

ansible-playbook –t install,conf httpd.yml   指定執行install,conf 兩個標籤

示例

//heartbeat.yaml
- hosts: hbhosts
  remote_user: root
  
  tasks:
    - name: ensure heartbeat latest version
      yum: name=heartbeat state=present
    - name: authkeys configure file
      copy: src=/root/hb_conf/authkeys dest=/etc/ha.d/authkeys
    - name: authkeys mode 600
      file: path=/etc/ha.d/authkeys mode=600
      notify:
        - restart heartbeat
    - name: ha.cf configure file
      copy: src=/root/hb_conf/ha.cf dest=/etc/ha.d/ha.cf
      notify:
        - restart heartbeat
  handlers:
    - name: restart heartbeat
      service: name=heartbeat state=restarted

Playbook中tags使用

- hosts: testsrv
  remote_user: root
  tags: inshttpd   針對整個playbook新增tage
  tasks:
    - name: Install httpd
      yum: name=httpd state=present
    - name: Install configure file
      copy: src=files/httpd.conf dest=/etc/httpd/conf/
      tags: rshttpd
      notify: restart httpd
  handlers:
    - name: restart httpd
      service: name=httpd status=restarted
     
ansible-playbook –t rshttpd httpd2.yml

Playbook中變數的使用

變數名:僅能由字母、數字和下劃線組成,且只能以字母開頭
變數來源:
    1> ansible setup facts 遠端主機的所有變數都可直接呼叫 (系統自帶變數)
       setup模組可以實現系統中很多系統資訊的顯示
                可以返回每個主機的系統資訊包括:版本、主機名、cpu、記憶體
       ansible all -m setup -a 'filter="ansible_nodename"'     查詢主機名
       ansible all -m setup -a 'filter="ansible_memtotal_mb"'  查詢主機記憶體大小
       ansible all -m setup -a 'filter="ansible_distribution_major_version"'  查詢系統版本
       ansible all -m setup -a 'filter="ansible_processor_vcpus"' 查詢主機cpu個數
    
    2> 在/etc/ansible/hosts(主機清單)中定義變數
        普通變數:主機組中主機單獨定義,優先順序高於公共變數(單個主機 )
        公共(組)變數:針對主機組中所有主機定義統一變數(一組主機的同一類別)
    
    3> 通過命令列指定變數,優先順序最高
       ansible-playbook –e varname=value
    
    4> 在playbook中定義
       vars:
        - var1: value1
        - var2: value2
    
    5> 在獨立的變數YAML檔案中定義
    
    6> 在role中定義

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

變數定義:key=value
    示例:http_port=80

變數呼叫方式:
    1> 通過{{ variable_name }} 呼叫變數,且變數名前後必須有空格,有時用“{{ variable_name }}”才生效

    2> ansible-playbook –e 選項指定
       ansible-playbook test.yml -e "hosts=www user=magedu"
在主機清單中定義變數,在ansible中使用變數
vim /etc/ansible/hosts
[appsrvs]
192.168.38.17 http_port=817 name=www
192.168.38.27 http_port=827 name=web

呼叫變數
ansible appsrvs -m hostname -a'name={{name}}'  更改主機名為各自被定義的變數 

針對一組設定變數
[appsrvs:vars]
make="-"

ansible appsrvs -m hostname -a 'name={{name}}{{mark}}{{http_port}}'  ansible呼叫變數
將變數寫進單獨的配置檔案中引用
vim vars.yml
pack: vsftpd
service: vsftpd

引用變數檔案
vars_files:
  - vars.yml 
    

Ansible基礎元素

Facts:是由正在通訊的遠端目標主機發回的資訊,這些資訊被儲存在ansible變數中。
       要獲取指定的遠端主機所支援的所有facts,可使用如下命令進行
       ansible websrvs -m setup

通過命令列傳遞變數
    在執行playbook的時候也可以傳遞一些變數供playbook使用
    示例:
        ansible-playbook test.yml -e "hosts=www user=magedu"
        
register
把任務的輸出定義為變數,然後用於其他任務

示例:
tasks:
- shell: /usr/bin/foo
  register: foo_result
  ignore_errors: True

示例:使用setup變數

示例:var.yml
- hosts: websrvs
  remote_user: root
  tasks:
    - name: create log file
      file: name=/var/log/ {{ ansible_fqdn }} state=touch

ansible-playbook var.yml

示例:變數

示例:var.yml
- hosts: websrvs
  remote_user: root
  tasks:
    - name: install package
      yum: name={{ pkname }} state=present
      
ansible-playbook –e pkname=httpd var.yml

示例:變數

示例:var.yml
- hosts: websrvs
  remote_user: root
vars:
  - username: user1
  - groupname: group1
tasks:
  - name: create group
    group: name={{ groupname }} state=present
  - name: create user
    user: name={{ username }} state=present

ansible-playbook var.yml
ansible-playbook -e "username=user2 groupname=group2” var2.yml

變數

主機變數
可以在inventory中定義主機時為其新增主機變數以便於在playbook中使用

示例:
[websrvs]
www1.magedu.com http_port=80 maxRequestsPerChild=808
www2.magedu.com http_port=8080 maxRequestsPerChild=909

組變數
組變數是指賦予給指定組內所有主機上的在playbook中可用的變數

示例:
    [websrvs]
    www1.magedu.com
    www2.magedu.com

    [websrvs:vars]
    ntp_server=ntp.magedu.com
    nfs_server=nfs.magedu.com

示例:變數

普通變數
    [websrvs]
    192.168.99.101 http_port=8080 hname=www1
    192.168.99.102 http_port=80 hname=www2

公共(組)變數
    [websvrs:vars]
    http_port=808
    mark="_"
    [websrvs]
    192.168.99.101 http_port=8080 hname=www1
    192.168.99.102 http_port=80 hname=www2
    ansible websvrs –m hostname –a ‘name={{ hname }}{{ mark }}{{ http_port }}’

命令列指定變數:
    ansible websvrs –e http_port=8000 –m hostname –a'name={{ hname }}{{ mark }}{{ http_port }}'

使用變數檔案

cat vars.yml
var1: httpd
var2: nginx

cat var.yml
- hosts: web
  remote_user: root
  vars_files:
    - vars.yml
  tasks:
    - name: create httpd log
      file: name=/app/{{ var1 }}.log state=touch
    - name: create nginx log
      file: name=/app/{{ var2 }}.log state=touch
      
hostname app_81.magedu.com  hostname 不支援"_",認為"_"是非法字元
hostnamectl set-hostname app_80.magedu.com  可以更改主機名

變數

組巢狀
inventory中,組還可以包含其它的組,並且也可以向組中的主機指定變數。
這些變數只能在ansible-playbook中使用,而ansible命令不支援

示例:
    [apache]
    httpd1.magedu.com
    httpd2.magedu.com
    
    [nginx]
    ngx1.magedu.com
    ngx2.magedu.com
    
    [websrvs:children]
    apache
    nginx
    
    [webservers:vars]
    ntp_server=ntp.magedu.com

invertory引數

invertory引數:用於定義ansible遠端連線目標主機時使用的引數,而非傳遞給playbook的變數
    ansible_ssh_host
    ansible_ssh_port
    ansible_ssh_user
    ansible_ssh_pass
    ansbile_sudo_pass

示例:
    cat /etc/ansible/hosts
    [websrvs]
    192.168.0.1 ansible_ssh_user=root ansible_ssh_pass=magedu
    192.168.0.2 ansible_ssh_user=root ansible_ssh_pass=magedu

invertory引數

inventory引數
ansible基於ssh連線inventory中指定的遠端主機時,還可以通過引數指定其互動方式;
這些引數如下所示:
ansible_ssh_host
The name of the host to connect to, if different from the alias you wishto give to it.

ansible_ssh_port
The ssh port number, if not 22

ansible_ssh_user
The default ssh user name to use.

ansible_ssh_pass
The ssh password to use (this is insecure, we strongly recommendusing --ask-pass or SSH keys)

ansible_sudo_pass
The sudo password to use (this is insecure, we strongly recommendusing --ask-sudo-pass)

ansible_connection
Connection type of the host. Candidates are local, ssh or paramiko.
The default is paramiko before Ansible 1.2, and 'smart' afterwards which
detects whether usage of 'ssh' would be feasible based on whether
ControlPersist is supported.

ansible_ssh_private_key_file
Private key file used by ssh. Useful if using multiple keys and you don't want to use SSH agent.

ansible_shell_type
The shell type of the target system. By default commands are formatted
using 'sh'-style syntax by default. Setting this to 'csh' or 'fish' will cause
commands executed on target systems to follow those shell's syntax instead.

ansible_python_interpreter
The target host python path. This is useful for systems with more
than one Python or not located at "/usr/bin/python" such as \*BSD, or where /usr/bin/python

is not a 2.X series Python. We do not use the "/usr/bin/env" mechanism as that requires the remote user's

path to be set right and also assumes the "python" executable is named python,where the executable might

be named something like "python26".
ansible\_\*\_interpreter

Works for anything such as ruby or perl and works just like ansible_python_interpreter.

This replaces shebang of modules which will run on that host.

模板templates

文字檔案,巢狀有指令碼(使用模板程式語言編寫) 藉助模板生成真正的檔案
Jinja2語言,使用字面量,有下面形式
    字串:使用單引號或雙引號
    數字:整數,浮點數
    列表:[item1, item2, ...]
    元組:(item1, item2, ...)
    字典:{key1:value1, key2:value2, ...}
    布林型:true/false
算術運算:+, -, *, /, //, %, **
比較操作:==, !=, >, >=, <, <=
邏輯運算:and,or,not
流表示式:For,If,When

Jinja2相關

字面量
    1> 表示式最簡單的形式就是字面量。字面量表示諸如字串和數值的 Python物件。如“Hello World”
    雙引號或單引號中間的一切都是字串。
    2> 無論何時你需要在模板中使用一個字串(比如函式呼叫、過濾器或只是包含或繼承一個模板的引數),如4242.23
    3> 數值可以為整數和浮點數。如果有小數點,則為浮點數,否則為整數。在Python 裡, 42 和 42.0 是不一樣的

Jinja2:算術運算

算術運算
Jinja 允許你用計算值。這在模板中很少用到,但為了完整性允許其存在
支援下面的運算子
    +:把兩個物件加到一起。
       通常物件是素質,但是如果兩者是字串或列表,你可以用這 種方式來銜接它們。
       無論如何這不是首選的連線字串的方式!連線字串見 ~ 運算子。 {{ 1 + 1 }} 等於 2
    -:用第一個數減去第二個數。 {{ 3 - 2 }} 等於 1
    /:對兩個數做除法。返回值會是一個浮點數。 {{ 1 / 2 }} 等於 {{ 0.5 }}
    //:對兩個數做除法,返回整數商。 {{ 20 // 7 }} 等於 2
    %:計算整數除法的餘數。 {{ 11 % 7 }} 等於 4
    *:用右邊的數乘左邊的運算元。 {{ 2 * 2 }} 會返回 4 。
       也可以用於重 復一個字串多次。{{ ‘=’ * 80 }} 會列印 80 個等號的橫條
    **:取左運算元的右運算元次冪。 {{ 2**3 }} 會返回 8

Jinja2

比較操作符
== 比較兩個物件是否相等
!= 比較兩個物件是否不等
> 如果左邊大於右邊,返回 true
>= 如果左邊大於等於右邊,返回 true
< 如果左邊小於右邊,返回 true
<= 如果左邊小於等於右邊,返回 true

邏輯運算子
對於 if 語句,在 for 過濾或 if 表示式中,它可以用於聯合多個表示式
and
    如果左運算元和右運算元同為真,返回 true
or
    如果左運算元和右運算元有一個為真,返回 true
not
    對一個表示式取反(見下)
(expr)
    表示式組

['list', 'of', 'objects']:
一對中括號括起來的東西是一個列表。列表用於儲存和迭代序列化的資料。
例如 你可以容易地在 for迴圈中用列表和元組建立一個連結的列表
    <ul>
    {% for href, caption in [('index.html', 'Index'), ('about.html', 'About'), ('downloads.html',
'Downloads')] %}
        <li><a href="{{ href }}">{{ caption }}</a></li>
    {% endfor %}
    </ul>
    ('tuple', 'of', 'values'):

元組與列表類似,只是你不能修改元組。
如果元組中只有一個項,你需要以逗號結尾它。
元組通常用於表示兩個或更多元素的項。更多細節見上面的例子
    {'dict': 'of', 'key': 'and', 'value': 'pairs'}:

Python 中的字典是一種關聯鍵和值的結構。
鍵必須是唯一的,並且鍵必須只有一個 值。
字典在模板中很少使用,罕用於諸如 xmlattr() 過濾器之類
    true / false:
    true 永遠是 true ,而 false 始終是 false

template 的使用

template功能:根據模組檔案動態生成對應的配置檔案
   > template檔案必須存放於templates目錄下,且命名為 .j2 結尾
   > yaml/yml 檔案需和templates目錄平級,目錄結構如下:
    ./
     ├── temnginx.yml
     └── templates
        └── nginx.conf.j2

template示例

示例:利用template 同步nginx配置檔案
準備templates/nginx.conf.j2檔案
vim temnginx.yml
- hosts: websrvs
  remote_user: root
  
  tasks:
    - name: template config to remote hosts
      template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf

ansible-playbook temnginx.yml

Playbook中template變更替換

修改檔案nginx.conf.j2 下面行為
worker_processes {{ ansible_processor_vcpus }};

cat temnginx2.yml
- hosts: websrvs
  remote_user: root
  tasks:
    - name: template config to remote hosts
      template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf

ansible-playbook temnginx2.yml

Playbook中template算術運算

演算法運算:
示例:
    vim nginx.conf.j2
    worker_processes {{ ansible_processor_vcpus**2 }};
    worker_processes {{ ansible_processor_vcpus+2 }};

when 實現條件判斷

條件測試:如果需要根據變數、facts或此前任務的執行結果來做為某task執行與否的前提時要用到條件測試,
通過when語句實現,在task中使用,jinja2的語法格式

when語句
    在task後新增when子句即可使用條件測試;when語句支援Jinja2表示式語法
示例:
tasks:
  - name: "shutdown RedHat flavored systems"
    command: /sbin/shutdown -h now
    when: ansible_os_family == "RedHat"  當系統屬於紅帽系列,執行command模組 
 
when語句中還可以使用Jinja2的大多"filter",
例如要忽略此前某語句的錯誤並基於其結果(failed或者success)執行後面指定的語句,
可使用類似如下形式:
tasks:
  - command: /bin/false
    register: result
    ignore_errors: True
  - command: /bin/something
    when: result|failed
  - command: /bin/something_else
    when: result|success
  - command: /bin/still/something_else
    when: result|skipped

此外,when語句中還可以使用facts或playbook中定義的變數

示例:when條件判斷

- hosts: websrvs
  remote_user: root
  tasks:
    - name: add group nginx
      tags: user
      user: name=nginx state=present
    - name: add user nginx
      user: name=nginx state=present group=nginx
    - name: Install Nginx
      yum: name=nginx state=present
    - name: restart Nginx
      service: name=nginx state=restarted
      when: ansible_distribution_major_version == "6"

示例:when條件判斷

示例:
tasks:
  - name: install conf file to centos7
    template: src=nginx.conf.c7.j2 dest=/etc/nginx/nginx.conf
    when: ansible_distribution_major_version == "7"
  - name: install conf file to centos6
    template: src=nginx.conf.c6.j2 dest=/etc/nginx/nginx.conf
    when: ansible_distribution_major_version == "6"

Playbook中when條件判斷

---
- hosts: srv120
  remote_user: root
  tasks:
    - name:
      template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf
      when: ansible_distribution_major_version == "7"

迭代:with_items

迭代:當有需要重複性執行的任務時,可以使用迭代機制
    > 對迭代項的引用,固定變數名為"item"
    > 要在task中使用with_items給定要迭代的元素列表
    > 列表格式:
         字串
         字典

示例

示例: 建立使用者
- name: add several users
  user: name={{ item }} state=present groups=wheel   #{{ item }} 系統自定義變數
  with_items:       # 定義{{ item }} 的值和個數
    - testuser1
    - testuser2

上面語句的功能等同於下面的語句:
- name: add user testuser1
  user: name=testuser1 state=present groups=wheel
- name: add user testuser2
  user: name=testuser2 state=present groups=wheel
  
with_items中可以使用元素還可為hashes
示例:
- name: add several users
  user: name={{ item.name }} state=present groups={{ item.groups }}
  with_items:
    - { name: 'testuser1', groups: 'wheel' }
    - { name: 'testuser2', groups: 'root' }

ansible的迴圈機制還有更多的高階功能,具體請參見官方文件
http://docs.ansible.com/playbooks_loops.html

示例:迭代

示例:將多個檔案進行copy到被控端
---
- hosts: testsrv
  remote_user: root
  tasks
  - name: Create rsyncd config
    copy: src={{ item }} dest=/etc/{{ item }}
    with_items:
  - rsyncd.secrets
  - rsyncd.conf

示例:迭代

- hosts: websrvs
  remote_user: root
  tasks:
    - name: copy file
      copy: src={{ item }} dest=/tmp/{{ item }}
      with_items:
    - file1
    - file2
    - file3
- name: yum install httpd
  yum: name={{ item }} state=present
  with_items:
    - apr
    - apr-util
    - httpd

示例:迭代

- hosts:websrvs
  remote_user: root
  tasks
    - name: install some packages
      yum: name={{ item }} state=present
      with_items:
        - nginx
        - memcached
        - php-fpm

示例:迭代巢狀子變數

- hosts:websrvs
  remote_user: root
  
  tasks:
    - name: add some groups
      group: name={{ item }} state=present
      with_items:
        - group1
        - group2
        - group3
    - name: add some users
      user: name={{ item.name }} group={{ item.group }} state=present
      with_items:
        - { name: 'user1', group: 'group1' }
        - { name: 'user2', group: 'group2' }
        - { name: 'user3', group: 'group3' }

with_itmes 巢狀子變數

with_itmes 巢狀子變數
示例
---
- hosts: testweb
  remote_user: root
  tasks:
    - name: add several users
      user: name={{ item.name }} state=present groups={{ item.groups }}
      with_items:
    - { name: 'testuser1' , groups: 'wheel'}
    - { name: 'testuser2' , groups: 'root'}

Playbook字典 with_items

- name: 使用ufw模組來管理哪些埠需要開啟
  ufw:
  rule: “{{ item.rule }}”
  port: “{{ item.port }}”
  proto: “{{ item.proto }}”
  with_items:
    - { rule: 'allow', port: 22, proto: 'tcp' }
    - { rule: 'allow', port: 80, proto: 'tcp' }
    - { rule: 'allow', port: 123, proto: 'udp' }

- name: 配置網路進出方向的預設規則
  ufw:
  direction: "{{ item.direction }}"
  policy: "{{ item.policy }}"
  state: enabled
  with_items:
    - { direction: outgoing, policy: allow }
    - { direction: incoming, policy: deny }

Playbook中template for if when迴圈

{% for vhost in nginx_vhosts %}

server {    #重複執行server程式碼
listen {{ vhost.listen | default('80 default_server') }};

{% if vhost.server_name is defined %}
server_name {{ vhost.server_name }};
{% endif %}

{% if vhost.root is defined %}
root  {{ vhost.root }};
{% endif %}

{% endfor %}

示例

// temnginx.yml
---
- hosts: testweb
  remote_user: root
  vars:      # 呼叫變數
    nginx_vhosts:
      - listen: 8080  #列表 鍵值對


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

生成的結果
server {
  listen 8080
}

示例

// temnginx.yml
---
- hosts: mageduweb
  remote_user: root
  vars:
    nginx_vhosts:
      - web1
      - web2
      - web3
  tasks:
    - name: template config
      template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf

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

生成的結果:
server {
    listen web1
}
server {
    listen web2
}
server {
    listen web3
}

roles

roles
    ansible自1.2版本引入的新特性,用於層次性、結構化地組織playbook。
    roles能夠根據層次型結構自動裝載變數檔案、tasks以及handlers等。
    要使用roles只需要在playbook中使用include指令即可。
    簡單來講,roles就是通過分別將變數、檔案、任務、模板及處理器放置於單獨的目錄中,
    並可以便捷地include它們的一種機制。
    角色一般用於基於主機構建服務的場景中,但也可以是用於構建守護程序等場景中

複雜場景:建議使用roles,程式碼複用度高
    變更指定主機或主機組
    如命名不規範維護和傳承成本大
    某些功能需多個Playbook,通過includes即可實現

Roles

角色(roles):角色集合
roles/
    mysql/
    httpd/
    nginx/
    memcached/
    
可以互相呼叫

Ansible Roles目錄編排

roles目錄結構

每個角色,以特定的層級目錄結構進行組織
roles目錄結構:

playbook.yml  呼叫角色
roles/
  project/ (角色名稱)
    tasks/
    files/
    vars/
    templates/
    handlers/
    default/ 不常用
    meta/    不常用

Roles各目錄作用

/roles/project/ :專案名稱,有以下子目錄
    files/ :存放由copy或script模組等呼叫的檔案
    templates/:template模組查詢所需要模板檔案的目錄
    tasks/:定義task,role的基本元素,至少應該包含一個名為main.yml的檔案;
            其它的檔案需要在此檔案中通過include進行包含
    handlers/:至少應該包含一個名為main.yml的檔案;
               其它的檔案需要在此檔案中通過include進行包含
    vars/:定義變數,至少應該包含一個名為main.yml的檔案;
           其它的檔案需要在此檔案中通過include進行包含
    meta/:定義當前角色的特殊設定及其依賴關係,至少應該包含一個名為main.yml的檔案,
           其它檔案需在此檔案中通過include進行包含
    default/:設定預設變數時使用此目錄中的main.yml檔案
    
roles/appname 目錄結構
    tasks目錄:至少應該包含一個名為main.yml的檔案,其定義了此角色的任務列表;
               此檔案可以使用include包含其它的位於此目錄中的task檔案
    files目錄:存放由copy或script等模組呼叫的檔案;
    templates目錄:template模組會自動在此目錄中尋找Jinja2模板檔案
    handlers目錄:此目錄中應當包含一個main.yml檔案,用於定義此角色用到的各handler;
                  在handler中使用include包含的其它的handler檔案也應該位於此目錄中;
    vars目錄:應當包含一個main.yml檔案,用於定義此角色用到的變數;
    meta目錄:應當包含一個main.yml檔案,用於定義此角色的特殊設定及其依賴關係;
              ansible1.3及其以後的版本才支援;
    default目錄:為當前角色設定預設變數時使用此目錄;應當包含一個main.yml檔案

roles/example_role/files/             所有檔案,都將可存放在這裡
roles/example_role/templates/         所有模板都存放在這裡
roles/example_role/tasks/main.yml:   主函式,包括在其中的所有任務將被執行
roles/example_role/handlers/main.yml:所有包括其中的 handlers 將被執行
roles/example_role/vars/main.yml:    所有包括在其中的變數將在roles中生效
roles/example_role/meta/main.yml:    roles所有依賴將被正常登入

建立role

建立role的步驟
(1) 建立以roles命名的目錄
(2) 在roles目錄中分別建立以各角色名稱命名的目錄,如webservers等
(3) 在每個角色命名的目錄中分別建立files、handlers、meta、tasks、templates和vars目錄;
    用不到的目錄可以建立為空目錄,也可以不建立
(4) 在playbook檔案中,呼叫各角色

實驗: 建立httpd角色

1> 建立roles目錄
   mkdir roles/{httpd,mysql,redis}/tasks -pv
   mkdir  roles/httpd/{handlers,files}

檢視目錄結構
tree roles/
    roles/
    ├── httpd
    │   ├── files
    │   ├── handlers
    │   └── tasks
    ├── mysql
    │   └── tasks
    └── redis
        └── tasks

2> 建立目標檔案
   cd roles/httpd/tasks/
   touch install.yml config.yml service.yml

3> vim install.yml
   - name: install httpd package
     yum: name=httpd
     
   vim config.yml
   - name: config file  
     copy: src=httpd.conf dest=/etc/httpd/conf/ backup=yes 
   
   vim service.yml
   - name: start service 
     service: name=httpd state=started enabled=yes
     
4> 建立main.yml主控檔案,呼叫以上單獨的yml檔案,
   main.yml定義了誰先執行誰後執行的順序
   vim main.yml
   - include: install.yml
   - include: config.yml
   - include: service.yml
   
5> 準備httpd.conf檔案,放到httpd單獨的檔案目錄下
   cp /app/ansible/flies/httpd.conf ../files/
   
6> 建立一個網頁
   vim flies/index.html
   <h1> welcome to weixiaodong home <\h1>

7> 建立網頁的yml檔案
   vim tasks/index.yml
   - name: index.html
     copy: src=index.html dest=/var/www/html 

8> 將網頁的yml檔案寫進mian.yml檔案中
   vim mian.yml
   - include: install.yml
   - include: config.yml
   - include: index.yml
   - include: service.yml

9> 在handlers目錄下建立handler檔案mian.yml
   vim handlers/main.yml
   - name: restart service httpd
     service: name=httpd state=restarted

10> 建立檔案呼叫httpd角色
    cd /app/ansidle/roles
    vim role_httpd.yml
    ---
    # httpd role
    - hosts: appsrvs
      remote_user: root 

      roles:       #呼叫角色
        - role: httpd  
        
11> 檢視目錄結構
    tree 
    .
    httpd
    ├── files
    │   ├── httpd.conf
    │   └── index.html
    ├── handlers
    │   └── main.yml
    └── tasks
        ├── config.yml
        ├── index.yml
        ├── install.yml
        ├── main.yml
        └── service.yml

12> ansible-playbook role_httpd.yml

針對大型專案使用Roles進行編排

roles目錄結構:
playbook.yml
roles/
  project/
    tasks/
    files/
    vars/
    templates/
    handlers/
    default/ # 不經常用
    meta/    # 不經常用

示例:
nginx-role.yml
roles/
└── nginx
    ├── files
    │ └── main.yml
    ├── tasks
    │ ├── groupadd.yml
    │ ├── install.yml
    │ ├── main.yml
    │ ├── restart.yml
    │ └── useradd.yml
    └── vars
        └── main.yml

示例

roles的示例如下所示:
site.yml
webservers.yml
dbservers.yml
roles/
  common/
    files/
    templates/
    tasks/
    handlers/
    vars/
    meta/
  webservers/
    files/
    templates/
    tasks/
  handlers/
    vars/
    meta/

實驗: 建立一個nginx角色

建立nginx角色在多臺主機上來部署nginx需要安裝 建立賬號
1> 建立nginx角色目錄
     cd /app/ansible/role
     mkdir nginx{tesks,templates,hanslers} -pv

2> 建立任務目錄
     cd tasks/
     touch insatll.yml config.yml service.yml file.yml user.yml
   建立main.yml檔案定義任務執行順序
     vim main.yml
     - include: user.yml
     - include: insatll.yml
     - include: config.yml
     - include: file.yml
     - include: service.yml

  
3> 準備配置檔案(centos7、8)
   ll /app/ansible/role/nginx/templates/
   nginx7.conf.j2
   nginx8.conf.j2


4> 定義任務
   vim tasks/install.yml
   - name: install
     yum: name=nginx
     
   vim tasks/config.yml
    - name: config file
      template: src=nginx7.conf.j2 dest=/etc/nginx/nginx.conf
      when: ansible_distribution_major_version=="7"
      notify: restrat
      
    - name: config file
      template: src=nginx8.conf.j2 dest=/etc/nginx/nginx.conf
      when: ansible_distribution_major_version=="8"
      notify: restrat
      
    vim tasks/file.yml   跨角色呼叫file.yum檔案,實現檔案複用
    - name: index.html
      copy: src=roles/httpd/files/index.html dest=/usr/share/nginx/html/ 
   
    vim tasks/service.yml
    - nmae: start service
      service: name=nginx state=started enabled=yes
      
    vim handlers/main.yml
    - name: restrat
      service: name=nginx state=restarted
      
    vim roles/role_nginix.yml
    --- 
    #test rcle
    - hosts: appsrvs
    
      roles: 
        - role: nginx
        
5> 測試安裝
   ansible-playbook role_nginx.yml

Roles案例

Roles目錄編排

Playbook中呼叫

playbook呼叫角色

呼叫角色方法1:
- hosts: websrvs
  remote_user: root
  
  roles:
    - mysql
    - memcached
    - nginx
    
呼叫角色方法2:
傳遞變數給角色
- hosts:
  remote_user:
  roles:
    - mysql
    - { role: nginx, username: nginx }   #不同的角色呼叫不同的變數  
    鍵role用於指定角色名稱
    後續的k/v用於傳遞變數給角色

呼叫角色方法3:還可基於條件測試實現角色呼叫
roles:
  - { role: nginx, username: nginx, when: ansible_distribution_major_version == '7' }

通過roles傳遞變數

通過roles傳遞變數
當給一個主機應用角色的時候可以傳遞變數,然後在角色內使用這些變數
示例:
- hosts: webservers
  roles:
    - common
    - { role: foo_app_instance, dir: '/web/htdocs/a.com', port: 8080 }

向roles傳遞引數

而在playbook中,可以這樣使用roles:
---
- hosts: webservers
  roles:
    - common
    - webservers

也可以向roles傳遞引數
示例:
---
- hosts: webservers
  roles:
    - common
    - { role: foo_app_instance, dir: '/opt/a', port: 5000 }
    - { role: foo_app_instance, dir: '/opt/b', port: 5001 }

條件式地使用roles

甚至也可以條件式地使用roles
示例:
---
- hosts: webservers
  roles:
    - { role: some_role, when: "ansible_os_family == 'RedHat'" }

Roles條件及變數等案例

When條件
    roles:
      - {role: nginx, when: "ansible_distribution_major_version == '7' " ,username: nginx }
變數呼叫
- hosts: zabbix-proxy
  sudo: yes
  roles:
    - { role: geerlingguy.php-mysql }
    - { role: dj-wasabi.zabbix-proxy, zabbix_server_host: 192.168.37.167 }

完整的roles架構

// nginx-role.yml 頂層任務呼叫yml檔案
---
- hosts: testweb
  remote_user: root
  roles:
    - role: nginx
    - role: httpd 可執行多個role

cat roles/nginx/tasks/main.yml
---
- include: groupadd.yml
- include: useradd.yml
- include: install.yml
- include: restart.yml
- include: filecp.yml

// roles/nginx/tasks/groupadd.yml
---
- name: add group nginx
  user: name=nginx state=present

cat roles/nginx/tasks/filecp.yml
---
- name: file copy
  copy: src=tom.conf dest=/tmp/tom.conf

以下檔案格式類似:
useradd.yml,install.yml,restart.yml

ls roles/nginx/files/
tom.conf

roles playbook tags使用

roles playbook tags使用
    ansible-playbook --tags="nginx,httpd,mysql" nginx-role.yml  對標籤進行挑選執行

// nginx-role.yml
---
- hosts: testweb
  remote_user: root
  roles:
    - { role: nginx ,tags: [ 'nginx', 'web' ] ,when: ansible_distribution_major_version == "6“ }
    - { role: httpd ,tags: [ 'httpd', 'web' ] }
    - { role: mysql ,tags: [ 'mysql', 'db' ] }
    - { role: marridb ,tags: [ 'mysql', 'db' ] }
    - { role: php }

實驗: 建立角色memcached

memcacched 當做快取用,會在記憶體中開啟一塊空間充當快取
cat /etc/sysconfig/memcached 
    PORT="11211"
    USER="memcached"
    MAXCONN="1024"
    CACHESIZE="64"    # 快取空間預設64M 
    OPTIONS=""


1> 建立對用目錄
   cd /app/ansible
   mkdir roles/memcached/{tasks,templates} -pv
   
2> 拷貝memcached配置檔案模板
   cp /etc/sysconfig/memcached  templates/memcached.j2
   vim templates/memcached.j2
   CACHESIZE="{{ansible_memtotal_mb//4}}"   #實體記憶體的1/4用做快取
   
3> 建立對應yml檔案,並做相應配置
   cd tasks/
   touch install.yml config.yml service.yml
   建立main.yml檔案定義任務執行順序
   vim main.yml
   - include: install.yml
   - include: config.yml
   - include: service.yml  
   
   vim install.yml
   - name: install 
     yum: name=memcached
     
   vim config.yml
   - name: config file
     template: src=memcached.j2 dets=/etc/sysconfig/memcached

   vim service.yml
   - name: service
     service: name=memcached state=started enabled=yes

4> 建立呼叫角色檔案
   cd /app/ansible/roles/
   vim role_memcached.yml
    ---
    - hosts: appsrvs
    
      roles: 
        - role: memcached

5> 安裝
   ansible-playbook  role_memcached.yml 
   memcached埠號11211

其它功能

委任(指定某一臺機器做某一個task)
    delegate_to
    local_action (專指標對ansible命令執行的機器做的變更操作)
互動提示
    prompt
*暫停(java)
    wait_for
Debug
    debug: msg="This always executes."
Include
Template 多值合併
Template 動態變數配置

Ansible Roles

委任
    delegate_to
互動提示
    prompt
暫停
    wait_for
Debug
    debug: msg="This always executes."
Include
Template 多值合併
Template 動態變數配置

推薦資料

http://galaxy.ansible.com
https://galaxy.ansible.com/explore#/
http://github.com/
http://ansible.com.cn/
https://github.com/ansible/ansible
https://github.com/ansible/ansible-examples

實驗: 實現二進位制安裝mysql的解除安裝

cat remove_mysql.yml 
---
# install mariadb server 
- hosts: appsrvs:!192.168.38.108
  remote_user: root

  tasks:
    - name: stop service 
      shell: /etc/init.d/mysqld stop
    - name: delete user 
      user: name=mysql state=absent remove=yes
    - name: delete
      file: path={{item}} state=absent
      with_items: 
        - /usr/local/mysql
        - /usr/local/mariadb-10.2.27-linux-x86_64
        - /etc/init.d/mysqld
        - /etc/profile.d/mysql.sh
        - /etc/my.cnf
        - /data/mysql

ansible-playbook  remove_mysql.yml