自動化利器 Ansible - 從瞭解到應用


本文說明

本系列使用 ansible 2.9.27 版本來說明和彙總相關信息。

# cat /etc/system-release
Red Hat Enterprise Linux Server release 7.8 (Maipo)
# 
# uname -a
Linux test01 3.10.0-1160.36.2.el7.x86_64 #1 SMP Wed Jul 21 11:57:15 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
# 
# ansible --version  # ansible版本
ansible 2.9.27
  config file = /etc/ansible/ansible.cfg
  configured module search path = [u'/home/vipxf/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python2.7/site-packages/ansible
  executable location = /bin/ansible
  python version = 2.7.5 (default, Nov 16 2020, 22:23:17) [GCC 4.8.5 20150623 (Red Hat 4.8.5-44)]
# 
# yum info ansible  # 包信息
Loaded plugins: fastestmirror, product-id, search-disabled-repos, subscription-manager
Repodata is over 2 weeks old. Install yum-cron? Or run: yum makecache fast
Loading mirror speeds from cached hostfile
Installed Packages
Name        : ansible
Arch        : noarch
Version     : 2.9.27
Release     : 1.el7
Size        : 103 M
Repo        : installed
Summary     : SSH-based configuration management, deployment, and task execution system
URL         : http://ansible.com
License     : GPLv3+
Description : Ansible is a radically simple model-driven configuration management,
            : multi-node deployment, and remote task execution system. Ansible works
            : over SSH and does not require any software or daemons to be installed
            : on remote nodes. Extension modules can be written in any language and
            : are transferred to managed machines automatically.

#

Ansible 概念介紹

Ansible是自由開源的配置和自動化工具。

主要特點

- 無客戶端模式(agentless),無侵入性,只需在主控端部署Ansible環境,被控端無需做任何操作
- 豐富的內置模塊滿足絕大部分場景需求,調用特定模塊可以完成特定任務
- 基於Python研發,支持API及自定義模塊,二次開發相對容易
- 默認使用SSH協議和節點進行通信
- 強大的playbook機制,配置語法使用yaml和Jinja模板語言,便於配置管理和實現複雜功能
- 具有冪等性,自動跳過沒有變化的部分,也就是一個操作執行一次和多次的結果相同
- 對雲計算平臺和大數據提供了支持

Ansible 使用模式

- 結合CMDB:通過CMDB下發指令調用Ansible完成相關操作      
- API調用:遠程調用Ansible提供豐富的API接口
- Ad-Hoc命令:直接通過Ad-Hoc命令集執行相關操作,多用於非固化需求、臨時性操作和二次開發接口調用的場景
- playbook執行:運行Playbook按序執行任務集

Ansible由控制主機對被管節點的操作方式爲 Ad-Hoc 和 Playbook。

  • Ad-Hoc模式使用單個模塊,支持批量執行單條命令。
  • Playbook模式是Ansible主要管理方式,Playbook通過多個Task集合完成一類功能,可以簡單地把 Playbook理解爲通過組合多條Ad-Hoc操作的配置文件。

Ansible 架構及流程

# Ansible 主要由五部分組成
- Ansible      Ansible核心
- Modules      包括核心模塊和自定義模塊
- Plugins      插件,對模塊功能的補充,包含連接插件、郵件插件等
- Playbooks    劇本,定義任務的配置文件
- Inventory    定義管理主機的清單

# ansible 命令執行過程
1. Ansible加載配置文件,默認爲/etc/ansible/ansible.cfg
2. Ansible讀取Inventory文件(默認爲/etc/ansible/hosts),獲取管理主機列表及相關變量信息
3. 根據參數調用對應配置和模塊,產生臨時py文件
4. 通過SSH方式傳輸到目標主機(默認爲~/.ansible/tmp目錄)
5. 設置臨時py文件的執行權限,執行操作並返回結果
6. 刪除臨時py文件並退出

Ansible 基礎信息

Ansible 主要目錄

# 配置文件目錄
/etc/ansible/
- ansible.cfg  # Ansible 主配置文件
- hosts  # Inventory,定義目標主機清單的文件
- roles  # 角色目錄,用於定義角色

# 執行文件目錄
/usr/bin/

# Lib庫依賴目錄
/usr/lib/python2.7/site-packages/ansible

# Help文檔目錄
/usr/share/doc/ansible-2.9.27/

# Ansible模塊目錄
/usr/share/ansible/
- collections  # 目錄
- plugins      # 插件模塊目錄
- roles        # 角色模塊目錄

Ansible 配置文件

默認Ansible配置文件存放在 /etc/ansible/ansible.cfg

# 常見參數
inventory = /etc/ansible/hosts     # 主機清單inventory文件的位置
library = /usr/share/ansible       # 存放Ansible模塊的目錄,支持用“:”隔開多個目錄
forks = 5                          # 併發連接數,默認爲5
remote_port = 22                   # 連接主機節點的端口,默認爲22端口,建議修改
host_key_checking = False          # 是否檢查SSH主機的密鑰,值爲True/False
timeout = 60                       # SSH連接的超時時間,單位爲秒
log_path = /var/log/ansible.log    # 存儲ansible日誌的文件(默認不開啓)

Ansible 執行文件

$ ll /usr/bin/*ansible*  # 相關執行文件
lrwxrwxrwx 1 root root    20 Mar 17  2022 /usr/bin/ansible -> /usr/bin/ansible-2.7
lrwxrwxrwx 1 root root    20 Mar 17  2022 /usr/bin/ansible-2 -> /usr/bin/ansible-2.7
-rwxr-xr-x 1 root root  5933 Jan 16  2022 /usr/bin/ansible-2.7
lrwxrwxrwx 1 root root     7 Mar 17  2022 /usr/bin/ansible-config -> ansible
-rwxr-xr-x 1 root root 13432 Jan 16  2022 /usr/bin/ansible-connection
lrwxrwxrwx 1 root root    28 Mar 17  2022 /usr/bin/ansible-console -> /usr/bin/ansible-console-2.7
lrwxrwxrwx 1 root root    28 Mar 17  2022 /usr/bin/ansible-console-2 -> /usr/bin/ansible-console-2.7
lrwxrwxrwx 1 root root     7 Mar 17  2022 /usr/bin/ansible-console-2.7 -> ansible
lrwxrwxrwx 1 root root    24 Mar 17  2022 /usr/bin/ansible-doc -> /usr/bin/ansible-doc-2.7
lrwxrwxrwx 1 root root    24 Mar 17  2022 /usr/bin/ansible-doc-2 -> /usr/bin/ansible-doc-2.7
lrwxrwxrwx 1 root root     7 Mar 17  2022 /usr/bin/ansible-doc-2.7 -> ansible
lrwxrwxrwx 1 root root    27 Mar 17  2022 /usr/bin/ansible-galaxy -> /usr/bin/ansible-galaxy-2.7
lrwxrwxrwx 1 root root    27 Mar 17  2022 /usr/bin/ansible-galaxy-2 -> /usr/bin/ansible-galaxy-2.7
lrwxrwxrwx 1 root root     7 Mar 17  2022 /usr/bin/ansible-galaxy-2.7 -> ansible
lrwxrwxrwx 1 root root     7 Mar 17  2022 /usr/bin/ansible-inventory -> ansible
lrwxrwxrwx 1 root root    29 Mar 17  2022 /usr/bin/ansible-playbook -> /usr/bin/ansible-playbook-2.7
lrwxrwxrwx 1 root root    29 Mar 17  2022 /usr/bin/ansible-playbook-2 -> /usr/bin/ansible-playbook-2.7
lrwxrwxrwx 1 root root     7 Mar 17  2022 /usr/bin/ansible-playbook-2.7 -> ansible
lrwxrwxrwx 1 root root    25 Mar 17  2022 /usr/bin/ansible-pull -> /usr/bin/ansible-pull-2.7
lrwxrwxrwx 1 root root    25 Mar 17  2022 /usr/bin/ansible-pull-2 -> /usr/bin/ansible-pull-2.7
lrwxrwxrwx 1 root root     7 Mar 17  2022 /usr/bin/ansible-pull-2.7 -> ansible
lrwxrwxrwx 1 root root    26 Mar 17  2022 /usr/bin/ansible-vault -> /usr/bin/ansible-vault-2.7
lrwxrwxrwx 1 root root    26 Mar 17  2022 /usr/bin/ansible-vault-2 -> /usr/bin/ansible-vault-2.7
lrwxrwxrwx 1 root root     7 Mar 17  2022 /usr/bin/ansible-vault-2.7 -> ansible
$ 

Ansible 常見模塊

Ansible基於模塊工作,本身沒有批量部署的能力。
可以理解爲Ansilbe提供了框架來運行具備批量部署能力的指定功能模塊。

# 通過 ansilbe-doc 命令查看模塊幫助信息,類似man命令
ansible-doc -l                  # List available plugins
ansible-doc -l | grep "copy"    # 查找名稱包含copy字符的模塊
ansible-doc <module_name>       # 顯示模塊的說明信息
ansible-doc -s <module_name>    # Show playbook snippet for specified plugin(s)


# 常見模塊
## command     在指定主機上執行命令(默認模塊),不支持變量、管道、重定向等shell特性
## ping        測試指定主機連接性
## shell       在指定主機上執行命令或運行腳本,打開遠程主機的shell進程的一個子shell運行命令,支持shell的變量、管道、重定向等特性
## script      調用本地腳本在遠程主機執行
## stat        獲取文件信息
## setup       主機系統信息,收集facts
## copy        複製文件到遠程主機的指定位置
## fetch       複製指定主機的文件到本地
## get_url     在遠程主機下載指定的url地址,支持文件校驗
## file        設置文件屬性
## cron        設置計劃任務
## group       管理用戶組
## user        管理用戶
## service     管理服務狀態
## yum         管理程序包
## hostname    管理主機名稱

## git         代碼及版本管理
## B           後臺管理
## assemble    文件組裝,可以將多份配置文件組裝成一份配置文件
## ini         ini文件管理模塊
## url         web請求,發送HTTP協議請求並得到返回的狀態碼
## apt         APT包管理模塊

Ansible 命令講解

Ansible 命令集

# 以下所有命令均可使用`-h`參數獲取幫助信息
ansible               # Define and run a single task 'playbook' against a set of hosts  # 常用命令
ansible-config        # View ansible configuration.
ansible-console       # REPL console for executing Ansible tasks.  # 交互式工具,相當linux中的shell,不常用
ansible-doc           # plugin documentation tool  # 查詢模塊的文檔說明,類似linux中的man,常用命令
ansible-galaxy        # Perform various Role and Collection related operations.
ansible-inventory     # Show Ansible inventory information, by default it uses the inventory script JSON format
ansible-playbook      # Runs Ansible playbooks, executing the defined tasks on the targeted hosts.  # 執行系列任務,類似linux中的sh,常用命令
ansible-pull          # pulls playbooks from a VCS repo and executes them for the local host
ansible-vault         # encryption/decryption utility for Ansible data files

Ansible 常用命令集

# 常用命令集

ansible
# Ansible指令的核心部分,主要用於執行ad-hoc命令,也就是單條命令
# 默認使用command模塊,默認的使用模塊可以在ansible.cfg中進行定義

ansible-doc
# 用於查看模塊信息,
# 參數 -l 列出可用的插件
# 參數 -s 用於查看指定模塊的用法,例如“ansible-doc -s shell”

ansible-playbook
# 讀取playbook文件內容並執行定義的任務
# 對於固化的需求通常採用playbook方式定義和實現

ansible-vault
# 主要應用於包含敏感信息的場景,可以加密和解密敏感信息
# 經過加密後的playbook,編輯時需要輸入事先設定的密碼才能打開
# 使用 --ask-vault-pass 參數執行加密後的playbook文件

ansible-config
# 查看ansible的配置
# 參數 list 打印所有配置參數,參數 view 查看配置文件
# 結合 grep 使用可以獲取具體配置信息,例如“ansible-config view |grep -v "#" |grep host_key_checking”

Ansible ad-hoc命令

# ansible <host-pattern> [-f forks] [-m module_name] [-a args]
# <host-pattern>      命令生效的主機: <inventory_group_name>/<inventory_host_ip>/all 

# 常用選項  通過 ansible -h 命令獲取詳細信息
-v                    # 輸出詳細執行過程信息  verbose mode (-vvv for more, -vvvv to enable connection debugging)
-i inventory_file     # 指定inventory文件,默認爲/etc/ansible/hosts
-f forks              # 指定併發線程數(一次處理多少個主機),默認5個線程
-m module_name        # 指定執行使用的模塊
-M directory          # 指定模塊存放路徑,默認爲/usr/share/ansible
-a args               # 指定模塊參數
-l hosts_ip           # 限定主機(只在限定主機上執行任務)

--become-method BECOME_METHOD    # 提權方法,默認爲sudo,一般爲su
--become-user BECOME_USER        # 切換用戶,默認爲root
-K, --ask-become-pass            # 請求提權密碼

--list-hosts        # 列出此次的主機列表,但不做任何改變
--syntax-check      # 語法檢查
-C, --check         # 檢驗可能的錯誤,但不做任何改變


# 主機名文件保留
- "-t"或"--tree"選項可以將ansible的執行結果按主機名保存在指定目錄下的文件中
- 使用"-t"選項可以將第一次執行的結果按主機名保存在文件中, 此後即使不使用"-t"選項也能提升執行速度


# 命令示例
ansible 192.168.56.3 -a 'hostname'  # 默認使用command模塊(不支持變量、管道等)
ansible 192.168.56.3 -m command -a 'hostname'
ansible Test --list  # 列出Test組所有的主機列表

ansible Test:Test2 -m ping  # 通過ping模塊檢查Test組和Test2組的主機是否存活
ansible Test:!Test2 -m ping  # 在Test組但不在Test2組的主機
ansible Test:&Test2 -m ping  # 同時存在Test組和Test2組的主機
ansible Tes* -m ping  # 組名開頭爲Tes的主機

ansible all -m shell -a 'hostname && date'  # 通過shell模塊在所有主機試行shell命令
ansible all -f 10 -m shell -a 'hostname && date;cat /etc/system-release'  # shell模塊支持變量、管道、if判斷等複雜命令

部分參數如果不指定,將採用ansible.cfg中的設置值,或者採用原始默認值。

Ansible ad-hoc常用命令

# ping        測試指定主機連接性
ansible ta -m ping
ansible -i /etc/ansible/hosts ta -m ping  #  指定inventory文件

# command     在指定主機上執行命令(默認模塊),不支持變量、管道、重定向等shell特性
ansible ta -m command -a "chdir=/home/ ls ./"  # 在遠程主機上切換到home目錄執行ls命令 

# shell       在指定主機上執行命令或運行腳本,打開遠程主機的shell進程的一個子shell運行命令,支持shell的變量、管道、重定向等特性
ansible ta -m shell -a 'hostname && date;cat /etc/system-release'  # 支持變量、管道、if判斷等複雜命令
ansible ta -m shell -a "echo 'this is a test' > test.txt" -C  # 驗證可能的錯誤,但不做任何改變
ansible ta -m shell -a 'echo "123456"|passwd --stdin root' -K  # 更改root密碼

# script      調用本地腳本在遠程主機執行,但只能執行腳本,不能調用其他指令,且不支持管道命令
ansible ta -m script -a "/home/vipxf/test.sh"
ansible ta -m script -a "removes=/home/vipxf/test.sh /home/vipxf/test2.sh" -o  # 如果遠程主機上test.sh文件存在就執行本地腳本test2.sh,否則就不執行
ansible ta -m script -a "creates=/home/vipxf/test.sh /home/vipxf/test2.sh" -o  # 如果遠程主機上test.sh文件存在就不執行本地腳本test2.sh,否則就執行

# raw         類似shell,支持管道命令,可用於沒有python環境的遠程主機
ansible ta -m raw -a "cd /home;pwd"

# stat        獲取文件信息
ansible ta -m stat -a "path=/etc/ansible/hosts" 

# setup       系統信息,收集facts
ansible ta -m setup
ansible ta -m setup | grep "xxx"  # 獲取主機所有facts然後結合grep命令過濾
ansible ta -m setup -a "filter=ansible_os_family"  # 通過filter參數來查看指定信息
ansible ta -m setup -a 'filter="*mem*"' --tree ./facts  # 通過filter參數查看指定信息並按主機名保存到facts目錄

# copy        複製文件到遠程主機的指定位置
ansible ta -m copy -a "src=/etc/hosts dest=~/test.hosts mode=664 owner=vipxf group=vipxf"  # 複製本地文件到遠程主機並設置權限
ansible ta -m copy -a "content="this is a test" dest=~/test.hosts mode=664 owner=vipxf group=vipxf"  # 在遠程主機創建指定內容文件並設置權限

# fetch       複製指定主機的文件到本地
ansible ta -m fetch -a "src=~/testfile.txt dest=~/"  # 將在指定目錄下創建對應IP文件夾來存放文件,保留了原目錄結構

# get_url     在遠程主機下載網絡文件,支持文件校驗
ansible ta -m get_url -a "url=http://172.20.5.3/pub/soft/docker/docker-19.03.9.tgz dest=~/ mode=655 owner=vipxf"  # 下載文件到遠程主機指定目錄並設置權限

# file        設置文件屬性
ansible ta -m file -a "path=~/testfile.txt mode=755 owner=vipxf group=vipxf"  # 更新文件屬性
ansible ta -m file -a "path=~/testfile.txt state=touch mode=644"  # 創建文件
ansible ta -m file -a "path=~/testfile.txt state=absent"  # 刪除文件
ansible ta -m file -a "path=~/testdir mode=755 state=directory"  # 創建目錄

# cron        設置計劃任務
ansible ta -m cron -a "minute=0 hour=22 job='/home/vipxf/testcron.sh' name='test cron'"  # 創建計劃任務
ansible ta -m cron -a "name='test cron' state=absent"  # 刪除計劃任務

# group       管理用戶組
ansible ta -b --become-user root --become-method su -m group -a "name=test system=yes" --ask-become-pass  # 創建用戶組
ansible ta -b --become-user root --become-method su -m group -a "name=test state=absent" --ask-become-pass  # 刪除用戶組

# user        管理用戶
ansible ta -b --become-user root --become-method su -m user -a "name=test system=yes group=test password=pw@123" -K  # 創建用戶
ansible ta -b --become-user root --become-method su -m user -a "name=test state=absent force=yes" -K  # 刪除用戶

# service     管理服務
ansible ta -b --become-user root --become-method su -m service -a "name=sshd enabled=yes state=started" -K  # 啓動httpd服務並且不設置開機啓動
ansible ta -b --become-user root --become-method su -m service -a "name=sshd state=stopped" -K  # 停止httpd服務並且不設置開機啓動

# yum         管理程序包
ansible ta -b --become-user root --become-method su -m yum -a "name=httpd state=present" -K  # 安裝包,也可以將state設爲installed
ansible ta -b --become-user root --become-method su -m yum -a "name=httpd state=latest disable_gpg_check=no" -K  # 關閉完整性校驗,安裝最新版包
ansible ta -b --become-user root --become-method su -m yum -a "name=httpd state=absent" -K  # 卸載包,也可以將state設爲removed
ansible ta -b --become-user root --become-method su -m yum -a "name=nginx state=latest enablerepo=local" -K  # 啓用本地yum源安裝最新版軟件包

# hostname    管理主機名稱
ansible ta -b --become-user root --become-method su -m hostname -a "name=test" --ask-become-pass  # 更改遠程主機名


Ansible 變量使用

  • Ansible的變量名僅能由字母、數字和下劃線組成,且只能以字母開頭。
  • Python關鍵字和playbook關鍵字都不能作爲有效的變量名。
  • Ansible的變量可以被定義在playbook、inventory、yaml格式文件、角色和命令行中,也可以將任務的輸出定義爲變量。

Ansible 自定義變量

# 方式1:在Inventory中爲主機或組定義專用變量
[groupname]
192.168.56.1 variable_name=value
[groupname:vars]
variable_name=value
[all:vars]
ansible_ssh_port="2222"

# 方式2:在playbook中通過關鍵字vars或vars_files定義變量
vars:
  - var_name: value
  - var_name: value
vars_files:
  - ./external_vars.yml  # 變量文件內容遵循yml格式

# 方式3:在playbook中把任務的輸出通過關鍵字register定義爲變量,然後用於其他任務
tasks:
    - name: test
      shell: /usr/bin/foo
      register: foo_result

# 方式4:在playbook中使用變量,執行palybook時通過命令行傳遞變量,通過“-e”或“--extra-vars”參數傳遞的變量優先級更高
ansible-playbook test.yml --extra-vars "hosts=Test user=anliven"

# 方式5:指定主機角色時通過 roles 傳遞變量,並在角色內通過` var_name `調用
roles:
  - { role: ROLE_NAME, var: value, ...}

Ansible 系統變量

# 遠程主機的系統信息統稱爲facts
# facts信息是JSON格式的數據結構,ansible_facts是最上層,可以使用 ansible_facts 變量查看所有內容
# 此外默認情況下使用 ansible_ 前綴可以將一些fact作爲頂級變量訪問
# 通過 setup 模塊可以查看指定主機的所有facts信息,通過使用filter參數來查看指定信息

ansible <host> -m setup  # 查看指定主機的facts信息
ansible ta -m setup | grep "xxx"  # 獲取主機所有facts然後結合grep命令過濾
ansible ta -m setup -a "filter=ansible_os_family"  # 通過filter參數來查看指定信息
ansible ta -m setup -a 'filter="*mem*"' --tree ./facts  # 通過filter參數查看指定信息並按主機名保存到facts目錄

Ansible 引用變量

# 普通變量
通過 {{ var_name }} 方式引用

# 關鍵字register或系統fact變量
- 返回結果集通常是嵌套yaml或者json的數據結構
- 通過  {{ var_name.stdout }}  或 {{ ansible_facts["eth0"]["ipv4"]["address"] }} 的方式引用

Ansible 變量優先級

# 優先級
1. 命令行中定義的變量(用-e或--extra-vars定義的變量)
2. 在Inventory中定義的連接變量(比如:ansible_ssh_user)
3. 普通變量(命令行轉換、play中的變量、included的變量、role中的變量等)
4. 在Inventory中定義的其他變量
5. Facts變量

Ansible 清單定義

Ansible 主機清單(Inventory)

Ansible通過Inventory(可管理的主機集合)對遠端服務器或者主機進行統一操作和管理,默認將主機列在一個文本文件中,這個文件稱爲Inventory文件。

  • 默認的路徑和文件爲:/etc/ansible/hosts
  • 可以通過ANSIBLE_HOSTS環境變量、ansible.cfg文件中inventory參數來指定,或者運行ansible和ansible-playbook時使用-i參數臨時指定
  • 可以有多個 inventory 文件,也可以通過 Dynamic Inventory 動態生成
  • inventory文件爲 INI文件格式,中括號中的字符爲組名。可以將同一個主機同時歸併到多個不同的組中
  • 組名建議使用"_"來連接字符,例如“aaa_bbb_ccc”

Ansible Inventory文件

192.168.56.1    # 未分組
test.example.com  
127.0.0.1 ansible_connection=local  # 直接訪問本地主機,而不是通過SSH

[Test]  # 已分組名稱
192.168.56.1
192.168.56.2
192.168.56.[3:4]  # 通過列表方式通配地址
192.168.56.[5:6]  # 多個通配地址
anliven-[a:d].example.com  # 通配主機域名

[Test:vars]  # 定義組變量
ansible_ssh_user="anliven"
ansible_ssh_pass="anliven"
ansible_ssh_port="22"

[Test2]  # 在分組中指定變量
192.168.56.[1:2] ansible_ssh_user=anliven ansible_ssh_pass=Anliven
192.168.56.[3:4] ansible_ssh_user=anliven ansible_ssh_pass=Anliven
192.168.56.[3:4]

[Test3]  # 在分組中切換root用戶 
192.168.56.[1:2] ansible_ssh_port="22" ansible_ssh_user="anliven" ansible_ssh_pass="anliven"  ansible_become_pass="Anliven" 

[Temp]
192.168.56.1:2222  # 指定端口
192.168.56.2 http_port=8080 var1=test1 var2=test2   # 定義主機時爲其添加主機變量以便於在playbook中使用

[Temp:vars]
ansible_ssh_user="anliven"
ansible_ssh_pass="anliven"

[TestGroup:children]  # 嵌套組,TestGroup爲名稱,其他爲固定格式 
Test1
Test2
Test3

[TestGroup:vars]  # 嵌套組的變量只能在ansible-playbook中使用
var1="test1"
var2="123"

[all:vars]  # 類似全局變量,對inventory文件中所有節點都有效
ansible_ssh_port="22"

Ansible Inventory內置參數

Ansible Inventory文件中可使用的行爲參數

可以在ansible.cfg中的[defaults]部分更改一些Inventory行爲參數的默認值

Ansible Dynamic Inventory

  • 動態Inventory也就是Ansible所有的Inventory文件裏面的主機列表和變量信息都支持從外部拉取,例如CMDB等。
  • 避免主機列表不準確和頻繁大量的手動更新的問題
  • 也可以通過設置ansible.cfg文件中的inventory參數爲一個可執行腳本,將CMDB等其他系統的主機信息同步至Ansible中。
  • 對動態Inventory腳本的參數和輸出必須遵循格式規則

Ansible Inventory分割

通過Inventory分割滿足如下需要

  • 按不同業務/系統分割成的多個Inventory文件
  • 同時使用常規Inventory文件和動態Inventory腳本

實現方式

  • 配置ansible.cfg文件的hostfile參數,指定放置inventory文件目錄作爲Inventory,Ansible將合併目錄裏所有文件爲一個完整的Inventory
  • 也可以在命令行中使用-i命令來指定特定的Inventory文件
[defaults]

hostfile = /etc/ansible/inventory  # 指定目錄作爲Inventory

Ansible 劇本執行

Ansible Playbook 基礎介紹

劇本(Playbook) 是 ansible 用於配置,部署,和管理被控節點的劇本。

  • playbook類似Linux的shell腳本,用於實現和管理大量的、規律的、複雜的操作任務
  • playbook方便代碼和配置的重用、移植性好,同時也易於管理

Playbook格式

# playbook文件由YMAL語言編寫,需遵循yaml格式要求
  1. 第一行以 "---" 開始,表明YMAL文件的開始(非playbook強制要求,沒有也能通過語法檢查)
  2. 列表元素以”-”開頭,後面帶有一個空格,然後元素內容
  3. 對象的鍵與值以":"和一個空格來分隔
  4. 相同層次內容必須保持相同的縮進和對齊,否則會報錯
  5. 在同一行中,“#” 之後的內容表示註釋
  6. 文件名應該以.yml或.yaml結尾

# 編寫完成後可以通過 --syntax-check 子命令檢查語法錯誤
ansible-playbook --syntax-check sample.yaml

Playbook組成

  • 一個Playbook可以包括一個或多個Play。
  • 一個Play由Host的無序集合與Task的有序列表組成。
  • 每一個Task僅由一個模塊構成。
# play
- 一個Playbook包括一個或多個Play
- 一個Play由Host的無序集合與Task的有序列表組成

# 目標定義部分:host
- 執行playbook的主機信息

# 變量定義部分:variable
- 執行playbook需要的變量
- 設置的ansible配置變量,例如gather_facts

# 任務定義部分:tasks & module
- 在目標主機執行的任務列表和調用的模塊
- Play的主體部分,Task列表中的各任務按次序逐個在Hosts中的指定主機上執行
- 每一個Task僅由一個模塊構成
- 建議爲每一個Task設置一個Name,便於在運行Playbook時從輸出結果辨別task信息
- Ansible的自帶模塊Command模塊和Shell模塊無需使用key=value格式,可以直接編寫要執行的命令

# 觸發器定義部分:handlers
- task執行完成後需要調用的任務,通常結合關鍵字notify
- 由通知者進行的Notify,如果沒有被Notify,則Handlers不會執行,如果被Notify了,則Handlers被執行
- 不管有多少個通知者進行了Notify,等到Play中的所有Task都執行完成之後,Handlers也只會被執行一次
- 實質上也是Task的列表

Ansible Playbook 示例

[root@test01 ansible-test]# pwd
/root/ansible-test
[root@test01 ansible-test]# 
[root@test01 ansible-test]# ll
total 4
drwxr-xr-x 2 root root  24 Oct 17 09:47 conf
-rw-r--r-- 1 root root 922 Oct 14 11:24 sample.yaml
[root@test01 ansible-test]# 
[root@test01 ansible-test]# tree
.
├── conf
│   └── httpd.conf
└── sample.yaml

1 directory, 2 files
[root@test01 ansible-test]# 
[root@test01 ansible-test]# cat conf/httpd.conf 
MaxClients        {{ maxClients }}
Listen        {{ httpd_port }}
[root@test01 ansible-test]# 
[root@test01 ansible-test]# cat sample.yaml 
- hosts: ta
  remote_user: vipxf
  gather_facts: no
  vars:
    - package: vim
  tasks:
    - name: install vim 
      yum: name={{ package }} state=latest
    - name: install configuration file
      become: yes
      become_method: su
      become_flags: "-"
      become_user: root    
      template: src=/root/ansible-test/conf/httpd.conf dest=/root/test-template.conf
      notify: 
      - check-hostname-date
    - name: copy file
      copy: src=/root/ansible-test/conf/httpd.conf dest=/home/vipxf/test-copy.conf
  handlers:
    - name: check-hostname-date
      shell: hostname && date
      ignore_errors: True
- hosts: ta
  remote_user: vipxf
  gather_facts: no
  become: yes
  become_method: su
  become_flags: "-"
  become_user: root
  vars:
    - newgroup: testgroup
    - newuser: testuser
  tasks:
    - name: create new group
      group: name={{ newgroup }} system=yes
    - name: create new user
      user: name={{ newuser }} group=testgroup system=yes
[root@test01 ansible-test]# 
[root@test01 ansible-test]# cat /etc/ansible/hosts
[ta]
172.20.8.247 ansible_ssh_port=2222 ansible_ssh_user=vipxf ansible_ssh_pass=Anliven09! 
[ta:vars]
httpd_port=80 maxClients=10
[root@test01 ansible-test]# 

Ansible playbook 示例說明

- hosts: ta  # 指定執行指定任務的主機,可以通過一個或多個由冒號分隔主機組
  remote_user: vipxf  # 指定在遠程主機執行任務的用戶,也能用在task中
  gather_facts: no
  vars:
    - package: vim
  tasks:  # 任務列表,在指定主機上按順序執行各任務
    - name: install vim  # 每個任務的名稱,用於輸出執行結果
      yum: name={{ package }} state=latest
    - name: install configuration file
      become: yes
      become_method: su
      become_flags: "-"
      become_user: root    
      template: src=/root/ansible-test/conf/httpd.conf dest=/root/test-template.conf
      notify:  # 此任務狀態爲changed時採取的操作,在handlers中定義
      - check-hostname-date
    - name: copy file
      copy: src=/root/ansible-test/conf/httpd.conf dest=/home/vipxf/test-copy.conf
  handlers:  # 觸發器,在被指定任務通知和所有任務完成的情況下只執行一次,實質上也是按序執行的任務列表
    - name: check-hostname-date
      shell: hostname && date
      ignore_errors: True  # 如果命令或腳本的退出碼不爲零,使用ignore_errors來忽略錯誤信息

- hosts: ta  # playbook文件可以有多個play
  remote_user: vipxf
  gather_facts: no
  become: yes
  become_method: su
  become_flags: "-"
  become_user: root
  vars:
    - newgroup: testgroup
    - newuser: testuser
  tasks:
    - name: create new group
      group: name={{ newgroup }} system=yes
    - name: create new user
      user: name={{ newuser }} group=testgroup system=yes

相關命令

# Ansible具有冪等性,會自動跳過沒有變化的部分
# 建議使用絕對路徑來執行playbook的yaml文件
# 語法檢查 和 預測試 不能保證結果絕對正確,實際的運行測試是有必要的
# 特別注意yml文件的內容格式(空格、縮進)、特殊字符等,必要時重寫重建

--------------------------------------------------------------------------------
## 列出主機列表、任務列表和標籤列表
ansible-playbook --list-hosts --list-tasks --list-tags sample.yaml

--------------------------------------------------------------------------------
## 檢查內容語法錯誤
ansible-playbook --syntax-check sample.yaml
## 預測試(不改變目標主機的任何設置)
ansible-playbook --check sample.yaml --ask-become-pass
ansible-playbook -C sample.yaml --ask-become-pass

--------------------------------------------------------------------------------
## 選擇playbook開始執行的任務
ansible-playbook sample.yaml --start-at-task="common"  # 從名稱爲common的任務開始執行playbook
## 詳細模式下執行playbook,根據提示輸入root密碼
ansible-playbook -v sample.yaml --ask-become-pass
## verbose mode (-vvv for more, -vvvv to enable connection debugging)
ansible-playbook -vvv sample.yaml --ask-become-pass  

返回結果

# 一般以紅色、黃色、綠色來表示執行結果

- 綠色:執行成功,未更改目標主機狀態
- 黃色:執行成功,對目標主機完成變更,目標主機狀態有變化
- 紫色:執行結果出現警告或提示信息
- 紅色:執行失敗,結果出現異常

# 在playbook執行後,會明確列出任務執行狀態的彙總信息
# 任務執行狀態包括:ok、changed、unreachable、failed、skipped、rescued、ignored  

結果示例

[root@test02 ~]# ip addr show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: ens192: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 00:50:56:a1:07:78 brd ff:ff:ff:ff:ff:ff
    inet 172.20.8.247/24 brd 172.20.8.255 scope global noprefixroute ens192
       valid_lft forever preferred_lft forever
    inet6 fe80::250:56ff:fea1:778/64 scope link 
       valid_lft forever preferred_lft forever
[root@test02 ~]# 
[root@test02 ~]# vim -h | head -n 1
VIM - Vi IMproved 7.4 (2013 Aug 10, compiled Jun 14 2019 10:43:10)
[root@test02 ~]# 
[root@test02 ~]# pwd
/root
[root@test02 ~]# ll test-template.conf 
-rw-r--r-- 1 root root 38 Oct 17 18:18 test-template.conf
[root@test02 ~]# 
[root@test02 ~]# ll /home/vipxf/test-copy.conf 
-rw-rw-r-- 1 vipxf vipxf 66 Oct 17 18:18 /home/vipxf/test-copy.conf
[root@test02 ~]# 
[root@test02 ~]# cat /etc/group |grep test
testgroup:x:995:
testuser:x:994:
[root@test02 ~]# cat /etc/passwd |grep test
testuser:x:997:995::/home/testuser:/bin/bash
[root@test02 ~]# 

Ansible Playbook 條件判斷(When)

when語句可以將變量、facts或此前任務的執行結果作爲指定task是否執行的前提條件,也可以判斷變量是否被定義。

示例-1:when

- hosts: ta
  remote_user: vipxf
  gather_facts: yes
  tasks:
    - name: test
      shell: echo "test"
      when: ansible_os_family == "RedHat" and ansible_distribution_version == "7.8" # 可以使用facts、playbook 或Inventory中定義的變量
    - name: test1
      shell: echo "step1"
      register: result  # 將執行結果定義爲變量
    - name: test2
      shell: echo "step2"
      when: result.rc == 0  # 基於先前任務的結果來執行
    - name: test3
      shell: echo "step3"
      when: result.stderr != ""
    - name: test4
      shell: echo "step4"
      when: result.changed == "true"

示例-2:結合fail語句

test.sh
[vipxf@test02 ~]$ pwd
/home/vipxf
[vipxf@test02 ~]$ 
[vipxf@test02 ~]$ ll test.sh 
-rw-rw-r-- 1 vipxf vipxf 127 Oct 21 00:11 test.sh
[vipxf@test02 ~]$ 
[vipxf@test02 ~]$ cat test.sh
#!/bin/bash

  if [ "$1" = "Anliven" ];then
     echo "Success"
  else
     echo "Failed"
  fi

[vipxf@test02 ~]$ 

when2.yaml

[root@test01 ansible-test]# cat when2.yaml 
- hosts: ta
  remote_user: vipxf
  gather_facts: yes
  vars:
    - teststring: Failed
  tasks:
    - name: test1
      shell: sh /home/vipxf/test.sh {{ testvar }} 
      register: result 
    - name: test2
      shell: echo "rc 0"
      when: result.rc == 0  # 基於先前任務的結果來執行
    - name: test3
      shell: echo "Success"
      when: '"Success" in result.stdout'  # 對stdout結果進行判斷
    - name: test4
      fail: msg="Check Failed"  # 任務報錯並拋出msg信息
      when: result.stdout == (( teststring ))  # 在when使用playbook中定義的變量
[root@test01 ansible-test]# 
[root@test01 ansible-test]# ansible-playbook -v when2.yaml -e testvar=Anliven
Using /etc/ansible/ansible.cfg as config file

PLAY [ta] *********************************************************************************************************************************************************************************************

TASK [Gathering Facts] ********************************************************************************************************************************************************************************
ok: [172.20.8.247]

TASK [test1] ******************************************************************************************************************************************************************************************
changed: [172.20.8.247] => {"changed": true, "cmd": "sh /home/vipxf/test.sh Anliven", "delta": "0:00:00.003638", "end": "2022-10-21 18:05:00.273596", "rc": 0, "start": "2022-10-21 18:05:00.269958", "stderr": "", "stderr_lines": [], "stdout": "Success", "stdout_lines": ["Success"]}

TASK [test2] ******************************************************************************************************************************************************************************************
changed: [172.20.8.247] => {"changed": true, "cmd": "echo \"rc 0\"", "delta": "0:00:00.002893", "end": "2022-10-21 18:05:00.722340", "rc": 0, "start": "2022-10-21 18:05:00.719447", "stderr": "", "stderr_lines": [], "stdout": "rc 0", "stdout_lines": ["rc 0"]}

TASK [test3] ******************************************************************************************************************************************************************************************
changed: [172.20.8.247] => {"changed": true, "cmd": "echo \"Success\"", "delta": "0:00:00.002632", "end": "2022-10-21 18:05:01.172659", "rc": 0, "start": "2022-10-21 18:05:01.170027", "stderr": "", "stderr_lines": [], "stdout": "Success", "stdout_lines": ["Success"]}

TASK [test4] ******************************************************************************************************************************************************************************************
skipping: [172.20.8.247] => {"changed": false, "skip_reason": "Conditional result was False"}

PLAY RECAP ********************************************************************************************************************************************************************************************
172.20.8.247               : ok=4    changed=3    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0   

[root@test01 ansible-test]# 
[root@test01 ansible-test]# ansible-playbook -v when2.yaml -e testvar="test"
Using /etc/ansible/ansible.cfg as config file

PLAY [ta] *********************************************************************************************************************************************************************************************

TASK [Gathering Facts] ********************************************************************************************************************************************************************************
ok: [172.20.8.247]

TASK [test1] ******************************************************************************************************************************************************************************************
changed: [172.20.8.247] => {"changed": true, "cmd": "sh /home/vipxf/test.sh test", "delta": "0:00:00.003612", "end": "2022-10-21 18:06:49.907767", "rc": 0, "start": "2022-10-21 18:06:49.904155", "stderr": "", "stderr_lines": [], "stdout": "Failed", "stdout_lines": ["Failed"]}

TASK [test2] ******************************************************************************************************************************************************************************************
changed: [172.20.8.247] => {"changed": true, "cmd": "echo \"rc 0\"", "delta": "0:00:00.002721", "end": "2022-10-21 18:06:50.338001", "rc": 0, "start": "2022-10-21 18:06:50.335280", "stderr": "", "stderr_lines": [], "stdout": "rc 0", "stdout_lines": ["rc 0"]}

TASK [test3] ******************************************************************************************************************************************************************************************
skipping: [172.20.8.247] => {"changed": false, "skip_reason": "Conditional result was False"}

TASK [test4] ******************************************************************************************************************************************************************************************
fatal: [172.20.8.247]: FAILED! => {"changed": false, "msg": "Check Failed"}

PLAY RECAP ********************************************************************************************************************************************************************************************
172.20.8.247               : ok=3    changed=2    unreachable=0    failed=1    skipped=1    rescued=0    ignored=0   

[root@test01 ansible-test]# 

changed_when  與 failed_when

# changed_when
# 在條件成立時,將對應任務的執行狀態設置爲changed
# 先執行task,並對task返回的值進行判斷,當滿足changed_when指定的條件時說明是執行成功的
# 默認情況下執行命令完成的主機狀態都爲changed

    - name: all host run this task
      shell: hostname
      register: info
      changed_when: '"webserver01" in info.stdout'   # 輸出包含某個特定字符才能將主機狀態設爲changed

# failed_when
# 在條件成立時,將對應任務的執行狀態設置爲失敗
# 當執行失敗後會將信息存在register的stderr中,通過判斷指定的字符是否在stderr中來確定是否真的失敗
# 其實是ansible的一種錯誤處理機制:由fail模塊組合了when條件語句的效果

    - name: this command prints FAILED when it fails
      command: echo "FAILED"
      register: command_result
      failed_when: "'FAILED' in command_result.stdout"

Ansible Playbook 循環迭代(with_items)

用來在playbook中實現循環迭代的功能。
支持元素列表、文件名(with_fileglob)、複合(with_together)、步進(with_sequence)、隨機(with_random_choice)、until等多種類型循環。

- name: test with_items
  hosts: ta
  remote_user: vipxf
  gather_facts: yes
  tasks:
    - name: test1
      shell: echo {{ item }}  # 通過引用item變量來迭代
      with_items:
        - one
        - two
        - three
    - name: test2
      shell: echo {{ item.name }} with {{ item.value }}  # 通過字典來迭代
      with_items: 
        - { name: 'one', value: '111'}
        - { name: 'two', value: '222'}     
        - { name: 'three', value: '333'}  
    - name: test3
      copy: src={{ item }} dest=~/ mode=664 owner=vipxf group=vipxf  # 把文件名作爲變量循環
      with_fileglob: 
        - /playbook/files/*

Ansible Playbook 角色(Roles)

  • 角色(roles)用於層次性、結構化地組織playbook
  • 通過規範的目錄存儲結構將變量、文件、任務、模板以及觸發器等放置於單獨目錄
  • 通過獨立的role目錄將不同的playbook任務單一化,從而實現高效的代碼複用
  • 在playbook中調用roles,根據層次型結構自動轉載roles的變量文件、tasks以及handlers等要素信息
  • 一般用於基於主機構建服務和進程的場景
  • 可以在 /etc/ansible/ansible.cfg 中設置 roles_path 來設置roles文件路徑

創建role

1. 創建 roles 名稱的目錄
2. 在 roles 目錄中分別創建各角色命名的目錄,如webserver等
3. 在每個角色命名的目錄中分別創建defaults、files、handlers、meta、tasks、templates和vars等目錄;如果爲空目錄則不被引用
4. 在roles的整體編排文件playbook中調用各角色

目錄結構說明

site.yml  # playbook文件,roles的整體編排文件
roles/  # 定義各角色的總目錄
    common/  # 角色role模塊名,在playbook中需要調用時使用的名稱
        default/  # 默認變量文件目錄(定義此角色的默認變量),至少包含一個main.yml文件,在main.yml文件中可以用include指令將其他yml文件包含進來
        files/  # 存放由copy或script等模塊調用的文件
        handlers/  # 觸發器文件目錄,至少包含一個main.yml文件,在main.yml文件中可以用include指令將其他yml文件包含進來
        meta/  # 此角色的元數據(特殊設定及其依賴關係),至少包含一個main.yml文件,在main.yml文件中可以用include指令將其他yml文件包含進來
        tasks/  # 任務文件目錄,至少包含一個main.yml文件,在main.yml文件中可以用include指令將其他yml文件包含進來
        templates/  # 模板文件目錄,template模塊會自動在此目錄中尋找
        vars/  # 變量文件目錄,至少包含一個main.yml文件,在main.yml文件中可以用include指令將其他yml文件包含進來

    webserver/  # 角色 webserver 目錄
        default/
        files/  
        handlers/
        meta/
        tasks/
        templates/
        vars/

調用方式
在playbook中可以通過關鍵字 roles 來調用角色role。

- hosts: webserver
  remote_user: anliven
  roles:  # 使用關鍵字roles調用角色role
    - common  # 調用role
    - webserver  # 調研多個role

- hosts: webserver
  remote_user: anliven
  roles:
    - common
    - { role: foo_app_instance, dir:'/opt/a',port:5000}  # 可以向roles傳遞參數
    - { role: foo_app_instance, dir:'/opt/b',port:5001}

- hosts:webserver
  remote_user: anliven
  roles:
    - { role: some_role, when: "ansible_so_family == 'RedHat" } # 指定調用roles的條件 

Ansible Playbook 標籤(Tags)

  • 通過標籤(tags)可以選擇運行或跳過playbook中的指定任務
  • 在playbook爲任務定義一個"標籤",在通過ansible-playbook命令執行此playbook時使用 --tags 或 --skip-tags選項選擇運行或跳過這個任務
  • 可以使用系統特殊標籤,也可以自定義標籤
  • 可以爲同一個任務設定多個標籤,也可以爲不同的任務設定相同的標籤

語法格式

# 格式1
tags:
 - testtag
 - t2

# 格式2
tags: testtag,t2
 
# 格式3
tags: ['testtag','t2']

特殊標籤

always    除非--skip-tags選項指定,否則 always 標籤的task會一直執行
never     除非--tags選項指定,否則 never 標籤的task都不會執行
tagged    不包括never的所有標籤
untagged  所有無標籤和always標籤
all       包括非never標籤和無標籤

選項

# 如果執行 ansible-playbook 時不指定標籤,則會執行所有非 never 標籤的任務

--tags "tag1,tag2..."   執行指定標籤和always標籤的tasks

--tags always      只執行always標籤的tasks
--tags all          執行所有非never標籤和無標籤的tasks
--tags never        執行always和never標籤的tasks
--tags tagged       執行所有標籤的tasks,但不包括never標籤的tasks
--tags untagged     執行所有無標籤和always標籤的tasks

--skip-tags "tag1,tag2..."  跳過指定標籤的tasks

--list-tags   查看playbook中哪些tags會被執行

tags示例

  tasks:
  - name: install package
    yum: name={{ packagename }} state=latest
    tags:
    - always
  - name: copy configuration file
    copy: src=/root/conf/httpd.conf dest=/etc/httpd/conf/httpd.conf
    tags: conf,http

命令示例

ansible-playbook nginx_tags.yaml  --tags "testtag,t2"

Ansible Playbook 調試(Debug)

  • Print statements during execution
  • 在Ansible Playbook中常使用debug模塊,可以在Playbook執行過程打印調試信息
  • 結合when條件語句一起使用時,可以調試特定條件下的執行過程

注意:在setup模塊中查詢出來的變量,直接可以在debug中直接作爲變量引用。

# msg
輸出自定義信息,如果不指定或不寫msg的話,默認也會輸出“null”

# var
- 指定要打印的變量名,與msg參數互斥,二者只能有一個
- var參數中的變量不需要使用{{}}表達式,而msg中需要

# verbosity
- debug的調試級別,默認0是全部顯示,級別調整到3是忽略內容不顯示
- 在命令中使用-vvv參數,可以在設置爲3情況下仍然顯示debug內容

示例

  tasks:
    - name: Host run this task
      debug: 'msg="{{ ansible_fqdn }} and {{ ansible_default_ipv4.address }}"'  # 打印必要信息
      when: ansible_memtotal_mb < 500 and ansible_processor_cores == 2  # 結合when使用
    - name: all host run this task
      shell: hostname
      register: info
    - name: Hostname is webserver01 Machie run this task
      debug: 'msg="{{ ansible_fqdn }}"'
      when: info['stdout'].startswith('Success') 
    - name: Show debug info
      debug: var=info verbosity=1    # 打印var變量信息,調試級別爲1

Ansilbe 加密解密

ansible-vault 用途

  • encryption/decryption utility for Ansible data files
  • 主要應用於包含敏感信息的場景,可以加密和解密敏感信息
  • See 'ansible-vault --help' for more information on a specific command.
# ansible-vault -h
usage: ansible-vault [-h] [--version] [-v]
                     {create,decrypt,edit,view,encrypt,encrypt_string,rekey}
                     ...

encryption/decryption utility for Ansible data files

positional arguments:
  {create,decrypt,edit,view,encrypt,encrypt_string,rekey}
    create              Create new vault encrypted file
    decrypt             Decrypt vault encrypted file
    edit                Edit vault encrypted file
    view                View vault encrypted file
    encrypt             Encrypt YAML file
    encrypt_string      Encrypt a string
    rekey               Re-key a vault encrypted file

optional arguments:
  --version             show program's version number, config file location, configured module search path, module location, executable location and exit
  -h, --help            show this help message and exit
  -v, --verbose         verbose mode (-vvv for more, -vvvv to enable connection debugging)

See 'ansible-vault <command> --help' for more information on a specific command.

ansible-vault 常用命令

# 加密文件
ansible-vault encrypt test-vault.yml
ansible-vault encrypt test-vault.yml --vault-password-file pwdfile

# 解密文件
ansible-vault decrypt test-vault.yml 
ansible-vault decrypt test-vault.yml --vault-password-file pwdfile

# 查看文件
ansible-vault view test-vault.yml
ansible-vault view test-vault.yml --vault-password-file pwdfile

# 重置文件密碼
ansible-vault rekey test-vault.yml
ansible-vault rekey test-vault.yml --vault-password-file pwdfile --new-vault-password-file pwdfilenew

# 創建加密文件
ansible-vault create test-vault.yml
ansible-vault create test-vault.yml --vault-password-file pwdfile

# 編輯加密文件
ansible-vault edit test-vault.yml
ansible-vault edit test-vault.yml --vault-password-file pwdfile

# 加密字符串
ansible-vault encrypt_string 'test123456'
ansible-vault encrypt_string 'test123456' --name 'ansible_ssh_pass'
ansible-vault encrypt_string 'test123456' --name 'ansible_ssh_pass' --vault-id anliven@pwdfile

ansible-vault "--vault-id"選項

# 從ansible2.4版本開始,官方推薦使用"--vault-id"選項代替"--vault-password-file"選項指定密碼文件
# “--vault-id prompt”功能上等同於"--ask-vault-pass"選項
# 支持同時使用多個密碼文件進行解密,適用於“引用其他文件”的場景
# 可以在被加密文件中包含特定字符“做記號”

ansible-vault encrypt_string 'test123456' --name 'ansible_ssh_pass' --vault-id pwdfile  # 加密字符串

ansible-vault encrypt test-vault.yml --vault-id pwdfile  # 加密文件
ansible-vault encrypt test-vault.yml --vault-id anliven@pwdfile  # 加密完成後的文件內容包含anliven字符

ansible-vault decrypt test-vault.yml --vault-id pwdfile  # 解密文件
ansible-vault view test-vault.yml --vault-id pwdfile  # 查看文件
ansible-vault edit test-vault.yml --vault-id pwdfile  # 編輯文件

ansible-vault rekey test-vault.yml --vault-id pwdfile  # 交互式密碼重置
ansible-vault rekey test-vault.yml --vault-id pwdfile  --new-vault-id pwdfilenew  # 通過新密碼文件重置

ansible-playbook test-vault.yml --vault-id pwdfile  # 運行playbook
ansible-playbook test-vault.yml --vault-id pwdfile1 --vault-id pwdfile2   # 提供多個密碼文件來解密,test-vault.yml中引用其他vault加密文件
ansible-playbook test-vault1.yml test-vault2.yml --vault-id pwdfile1 --vault-id pwdfile2  # 提供多個加密文件來解密多個文件

ansible-vault 示例

示例-1 交互式密碼

[root@test01 ansible-test]# cat test-vault.yml 
- hosts: ta
  gather_facts: no
  tasks:
  - debug:
      msg: "test ansible-vault"
[root@test01 ansible-test]# 
[root@test01 ansible-test]# ansible-playbook test-vault.yml

PLAY [ta] *********************************************************************************************************************************************************************************************

TASK [debug] ******************************************************************************************************************************************************************************************
ok: [172.20.8.247] => {
    "msg": "test ansible-vault"
}

PLAY RECAP ********************************************************************************************************************************************************************************************
172.20.8.247               : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@test01 ansible-test]# 
[root@test01 ansible-test]# ansible-vault encrypt test-vault.yml 
New Vault password: 
Confirm New Vault password: 
Encryption successful
[root@test01 ansible-test]# 
[root@test01 ansible-test]# cat test-vault.yml 
$ANSIBLE_VAULT;1.1;AES256
32656239643632646139633938613430326139636636333235346361643161393131396661366534
6636386331316239616632316137316266316266646432360a366366643232313033343835346638
38616331636639643731633766333335613763623636333363336238353931616263313637313834
3135656632343034340a316238656238336432386638373236653738306530383232626231333438
38666338346130333561316535353637616230633634346162303730393166396230616533396435
38346536306433653566373438303565373036663138366330313836356666656639393438396134
35333465623365636531653562363366323065316238333333353863376236373362373832633636
62613732666263306531653231353931326635303533623934633235396239613838613230323862
3134
[root@test01 ansible-test]# 
[root@test01 ansible-test]# ansible-vault view test-vault.yml 
Vault password: 
- hosts: ta
  gather_facts: no
  tasks:
  - debug:
      msg: "test ansible-vault"
[root@test01 ansible-test]# 
[root@test01 ansible-test]# ansible-playbook --ask-vault-pass test-vault.yml 
Vault password: 

PLAY [ta] *********************************************************************************************************************************************************************************************

TASK [debug] ******************************************************************************************************************************************************************************************
ok: [172.20.8.247] => {
    "msg": "test ansible-vault"
}

PLAY RECAP ********************************************************************************************************************************************************************************************
172.20.8.247               : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@test01 ansible-test]# 
[root@test01 ansible-test]# ansible-vault decrypt test-vault.yml 
Vault password: 
Decryption successful
[root@test01 ansible-test]# 
[root@test01 ansible-test]# cat test-vault.yml 
- hosts: ta
  gather_facts: no
  tasks:
  - debug:
      msg: "test ansible-vault"
[root@test01 ansible-test]# 

示例-2 密碼文件

[root@test01 ansible-test]# echo "This-is_a#Test!2o22" > pwdfile
echo "This-is_a#Testhistoryo22" > pwdfile
[root@test01 ansible-test]# 
[root@test01 ansible-test]# cat pwdfile 
This-is_a#Testhistoryo22
[root@test01 ansible-test]# 
[root@test01 ansible-test]# ansible-vault encrypt test-vault.yml --vault-password-file pwdfile
Encryption successful
[root@test01 ansible-test]# 
[root@test01 ansible-test]# cat test-vault.yml 
$ANSIBLE_VAULT;1.1;AES256
63343030376661643237653266366133313735363630353564363631376563613236383863346264
6163303562643831636237633038373265616334343234630a613466663138396334303463623665
30353632396236306435633062383864646466616261393064313633373635353633656161393266
3234326635323438610a376631323634316663313130356466306238306638613261663138333663
30363461616433643530656562313139303831346365346531303530666236373038306435636338
39666432326465313834613164356436653366656138613634303339346130353033313330303733
30643934383363333261646366396330343164393236633138383137316166643966393838396464
64323863306539333534663938393962326231373137613630623635313534356163363261626262
3765
[root@test01 ansible-test]# 
[root@test01 ansible-test]# ansible-vault view  test-vault.yml --vault-password-file pwdfile
- hosts: ta
  gather_facts: no
  tasks:
  - debug:
      msg: "test ansible-vault"
[root@test01 ansible-test]# 
[root@test01 ansible-test]# ansible-playbook test-vault.yml --vault-password-file pwdfile

PLAY [ta] *********************************************************************************************************************************************************************************************

TASK [debug] ******************************************************************************************************************************************************************************************
ok: [172.20.8.247] => {
    "msg": "test ansible-vault"
}

PLAY RECAP ********************************************************************************************************************************************************************************************
172.20.8.247               : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@test01 ansible-test]# 
[root@test01 ansible-test]# ansible-vault decrypt test-vault.yml --vault-password-file pwdfile
Decryption successful
[root@test01 ansible-test]# 
[root@test01 ansible-test]# cat test-vault.yml 
- hosts: ta
  gather_facts: no
  tasks:
  - debug:
      msg: "test ansible-vault"
[root@test01 ansible-test]# 

示例-3 加密字符串

[root@test01 ansible-test]# ansible-vault encrypt_string "test123456"
New Vault password: 
Confirm New Vault password: 
!vault |
          $ANSIBLE_VAULT;1.1;AES256
          33383336353737346430653165326665393430346539376334396335336530613330643764313962
          3438366538366262316666353962663564666532393333300a333934633664393262653065343864
          63653361666133363862353061323238376335666165313130393664623761393033343136343265
          6166663630353038380a666164643565343336373062323135643038363436343938383363303632
          6230
Encryption successful
[root@test01 ansible-test]# 
[root@test01 ansible-test]# vim test-encrypt_string.yaml 
[root@test01 ansible-test]# 
[root@test01 ansible-test]# cat test-encrypt_string.yaml 
- hosts: ta
  gather_facts: no
  vars:
    test_user: "testuser"
    test_passwd: !vault |
          $ANSIBLE_VAULT;1.1;AES256
          33383336353737346430653165326665393430346539376334396335336530613330643764313962
          3438366538366262316666353962663564666532393333300a333934633664393262653065343864
          63653361666133363862353061323238376335666165313130393664623761393033343136343265
          6166663630353038380a666164643565343336373062323135643038363436343938383363303632
          6230
  tasks:
  - debug:
      msg: "{{test_user}}"
  - debug:
      msg: "{{test_passwd}}"
[root@test01 ansible-test]# 
[root@test01 ansible-test]# ansible-playbook test-encrypt_string.yaml --ask-vault-pass
Vault password: 

PLAY [ta] *********************************************************************************************************************************************************************************************

TASK [debug] ******************************************************************************************************************************************************************************************
ok: [172.20.8.247] => {
    "msg": "testuser"
}

TASK [debug] ******************************************************************************************************************************************************************************************
ok: [172.20.8.247] => {
    "msg": "test123456"
}

PLAY RECAP ********************************************************************************************************************************************************************************************
172.20.8.247               : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@test01 ansible-test]# 

示例-4 通過密碼文件加密字符串

[root@test01 ansible-test]# ansible-vault encrypt_string "test123456" --name "test_passwd" --vault-id anliven@pwdfile
test_passwd: !vault |
          $ANSIBLE_VAULT;1.2;AES256;anliven
          61646130623833333634646632393432326431383864663134356530323536663165303061313661
          3365343837623564343037663236316635666565613730350a393731646238376638363365363561
          35383465336137313134306363363139386537633839393363653465333161303634313832383136
          3038326464613935350a383565343261363833333631663862336464303538323561363237326637
          3431
Encryption successful
[root@test01 ansible-test]# 
[root@test01 ansible-test]# vim test-encrypt_string.yaml
[root@test01 ansible-test]# cat test-encrypt_string.yaml
- hosts: ta
  gather_facts: no
  vars:
    test_user: "testuser"
    test_passwd: !vault |
          $ANSIBLE_VAULT;1.2;AES256;anliven
          61646130623833333634646632393432326431383864663134356530323536663165303061313661
          3365343837623564343037663236316635666565613730350a393731646238376638363365363561
          35383465336137313134306363363139386537633839393363653465333161303634313832383136
          3038326464613935350a383565343261363833333631663862336464303538323561363237326637
          3431
  tasks:
  - debug:
      msg: "{{test_user}}"
  - debug:
      msg: "{{test_passwd}}"
[root@test01 ansible-test]# 
[root@test01 ansible-test]# ansible-playbook test-encrypt_string.yaml --vault-id pwdfile

PLAY [ta] *********************************************************************************************************************************************************************************************

TASK [debug] ******************************************************************************************************************************************************************************************
ok: [172.20.8.247] => {
    "msg": "testuser"
}

TASK [debug] ******************************************************************************************************************************************************************************************
ok: [172.20.8.247] => {
    "msg": "test123456"
}

PLAY RECAP ********************************************************************************************************************************************************************************************
172.20.8.247               : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@test01 ansible-test]# 

Ansible 性能優化

在默認設置的情況下,Ansible的執行效率已經可以滿足大多數場景。
面對巨量目標主機時,可以通過一些配置優化去再提高ansible的執行效率。

Ansible 基本設置

# 通過 time 命令統計執行時間
time <ansible-command>


# 關閉SSH密鑰檢測
- 以SSH登錄遠程設備時,默認該設備會檢查遠程主機的公鑰,並且將該公鑰記錄在~/.ssh/known_hosts文件中
- 當下次該主機訪問時會覈對公鑰,如果公鑰不同則會發出警告,如果公鑰相同,則會提示輸入密碼
- 對主機公鑰的檢查是根據StrictHostKeyChecking變量的檢查級別確定:no(不檢查)、ask(是否檢查要詢問)、yes(每次都檢查)、False(關閉檢查)
- 設置 /etc/ansible/ansible.cfg 中參數 host_key_checking = False


# 併發數forks
- Ansible默認只會創建5個進程併發執行任務,所以在任務中只能同時控制5臺主機執行
- forks是線程數,受限於CPU的核心數,加大forks值能讓CPU儘量提升併發數量,一定程度上提高執行效率
- 設置 /etc/ansible/ansible.cfg 中 forks 參數,默認值爲5
- 在執行命令時通過 -f 參數指定併發數


# 關閉 gather_facts
- playbook默認執行第一個tasks是gather_facts,用於收集主機信息
- 如果任務並不需要調用主機信息變量,建議關閉可以減少Ansible在收集客戶端信息的時間,提高運行速度
- 可以在playbook文件中設置gather_facts參數爲false或no(不區分大小寫)
gather_facts: False 
gather_facts: no
- 也可以設置 /etc/ansible/ansible.cfg 中參數
gather_facts: no


# 設置 facts緩存
- 不過禁用facts,可以採用facts緩存保證一定的運行效率來使用facts信息
- 在ansible.cfg中可以配置多種facts緩存的方式:json文件,redis,memcache等
- 使用redis存儲facts文件需安裝redis和響應的python庫
- 以json文件方式爲例
gathering=smart
fact_caching_timeout=86400
fact_caching=jsonfile
fact_caching_connection=/path/to/ansible_fact_cache


# serial滾動更新
- 併發可以在ansible.cfg裏修改配置,也可以在playbook中限制服務端的併發數量
- Ansible默認將嘗試並行管理playbook中所有的機器,可以使用serial關鍵字定義一次應管理多少主機
- 常應用與負載均衡環境下,不能一次性停止所有主機的場景
- 在play或者task下都可以設置serial參數,但其值對應的主機數不應大於forks值 
serial: "30%"  # 按百分比執行
serial: 2      # 指定單次執行數量


# OpenSSH鏈接優化(非必要,不建議)
- 默認情況下,使用OpenSSH時,服務器端會根據客戶端的IP地址進行DNS反向解析,然後根據獲取到的主機名再次進行DNS查詢得到IP地址,比較這兩個IP地址是否一致
- 可以在一定程度上提高安全性,但會花費更多時間,可以關閉這一特性來實現加速SSH鏈接速度
- 設置 /etc/ssh/sshd_config 中參數 UseDNS no


# 開啓pipeling
- 發送執行命令代替命令臨時文件(針對不使用sudo的場景,建議以單獨ansible.cfg文件方式開啓)
- 設置 /etc/ansible/ansible.cfg 中參數 pipelining = True

Ansible 異步async與輪詢polling特性

# Ansible的異步async與輪詢polling特性
# 適合使用到ansible的polling特性的場景
# - task需要運行很長的時間,這個task很可能會達到timeout
# - 任務需要在大量的機器上面運行
# - 任務是不需要等待它完成的

# 不適合使用polling特性的場景
# - task任務是需要運行完後才能繼續另外的任務的
# - task任務能很快的完成

# 注意:需在ansible服務端的/etc/ansible/ansible.cfg文件裏配置"host_key_checking = False"  

- hosts: all
  max_fail_percentage: 30  # 主機數最大失敗百分比,只有在大於時中止tasks的執行,結合serial參數則以serial值爲基準
  serial: 7  # 定義同時執行操作的主機數,一般要小於forks的值,否則會按forks值進行“分片”執行任務
  tasks:
    - name: test-1
      shell: sleep 5 && echo "test-1 done" && hostname && date
      async: 9  # 任務執行時間的上限值,超時則任務失敗,建議設置爲略大於任務正常實際執行時長,設置爲0時表示一直等待任務結束
      poll: 2  # 檢查任務結果的間隔時長,建議設置爲小於任務實際執行時長,設置爲0表示不用等待結果繼續執行後續任務
      register: script_result
    - name: test-2
      async_status: jid={{ script_result.ansible_job_id }}  # 通過模塊async_status查看輪詢結果
      register: job_result
      until: job_result.finished  #  
      retries: 10  # 重試10次
    - name: test-3  # 將在所有主機執行完成後一次性返回結果
      shell: echo "test-3 done" && hostname && date

Ansible 圖形界面

Ansible Web UI目前主要有官方商業版Tower、官方開源版AWX 和 開源免費版semaphore。

Ansible Tower

Ansible AWX

semaphore


Ansible 技巧提示

1 - Ansible 免密登錄

# 通過祕鑰方式連接
ssh-keygen -t rsa 
ssh-copy-id -i /vipxf/.ssh/id_rsa.pub [email protected]
ssh-copy-id -i /vipxf/.ssh/id_rsa.pub [email protected]

2 - Ansible playbook輸入root密碼

[root@test01 ansible-test]# cat test-temp.yaml 
---
- name: test-root
  hosts: ta
  remote_user: vipxf
  gather_facts: no
  become: yes
  become_user: root
  become_method: su
  become_flags: "-"
  vars:
    ansible_become_pass: "{{ root_pass_input }}"
  tasks:
    - name: test
      shell: date && sleep 3
      ignore_errors: True
[root@test01 ansible-test]# 
[root@test01 ansible-test]# ansible-playbook  test-temp.yaml -e root_pass_input=redhat

PLAY [test-root] **************************************************************************************************************************************************************************************************************

TASK [test] *******************************************************************************************************************************************************************************************************************
changed: [172.20.8.247]

PLAY RECAP ********************************************************************************************************************************************************************************************************************
172.20.8.247               : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@test01 ansible-test]# 

3 - Ansible 簡單的複用方式

通過關鍵字include可以實現簡單的代碼複用。

# 在playbook中,Include 指令可以跟task 混合在一起使用
  - tasks:
      - include: tasks/foo.yml  # 引用配置文件

# 參數化的include
  - tasks: 
      - include: aaa.yml user=anliven  # 引用配置文件的同時傳入變量
      - include: bbb.yml pwd=P@ssw0rd

4 - Ansible task通知多個handlers任務

通過關鍵字listen爲handlers中的一個或多個觸發器任務指定主題,task就可以按主題名進行通知。
觸發順序按照handler定義的先後順序執行。

---
- hosts: ta
  remote_user: vipxf
  gather_facts: no
  vars:
    - package: vim
  tasks:
    - name: test-step one
      shell: echo "111" 
      notify: "test listen"  # task通知handlers主題
    - name: test-step two
      shell: echo "222"
  handlers:
    - name: check-hostname-date
      shell: hostname && date
      ignore_errors: True
      listen: "test listen"  # 按照定義順序被觸發
    - name: who-am-i
      shell: whoami && date
      ignore_errors: True
      listen: "test listen"  # 按照定義順序被觸發

獨立章節索引


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章