回到:Ansible系列文章
各位讀者,請您:由於Ansible使用Jinja2模板,它的模板語法{% raw %} {{}} {% endraw %}和{% raw %} {%%} {% endraw %}和我博客系統hexo的模板使用的符號一樣,在渲染時會產生衝突,儘管我盡我努力地花了大量時間做了調整,但無法保證已經全部都調整。因此,如果各位閱讀時發現一些明顯的詭異的錯誤(比如像這樣的空的
行內代碼),請一定要回復我修正這些渲染錯誤。
2.初入Ansible世界:用法概覽和初體驗
Ansible是一個自動化管理工具,它的用法可以非常簡單,只需學幾個基本的模塊就能完成一些簡單的自動化任務,它的用法也可以非常難,不僅需要學習大量Ansible知識,還需要大量的實際應用去熟悉最優化、最完美的自動化管理邏輯。比較悲催的是Ansible的知識體系比較龐大,它的知識板塊也比較零散,想要構建一個比較完善的Ansible知識體系確實稍有難度。
但無論如何,千里之行始於足下,學習任何一個新知識,總歸要從最基本的用法循序漸進地深入,並輔以逐漸展開的宏觀結構,加上學習過程中的不斷練習,最終構建出完善的知識體系。
因Ansible的基礎知識內容較多,本文先介紹最基本的概念和最基本的用法,讓大家對Ansible的用法以及功能有一個基本且系統性的認識。之後的文章再逐步探討Ansible如何應用在配置管理上。
2.1 測試環境並配置ssh主機互信
Ansible的作用是批量控制其它遠程主機,並指揮遠程主機節點做一些操作、完成一些任務。
所以在這個結構中,分爲控制節點和被控制節點。Ansible是Agentless的軟件,只需在控制節點安裝Ansible,被控制節點一般不需額外安裝任何程序,就像一個普通的命令一樣,隨裝隨用。
Ansible的模塊是用Python來執行的,且默認遠程連接的方式是ssh,所以控制端和被控制端都需要有Python環境,並且被控制端需要啓動sshd服務,但通常這兩個條件在安裝Linux系統時就已經具備了。所以使用Ansible的安裝過程只有一個:在控制端安裝Ansible。
在本文中,將配置如下測試環境:包括一個Ansible控制節點和7個被控制節點。後面的文章中如果沒有特別說明,也都使用此處的主機環境。
主機描述 | IP地址 | 主機名 | 操作系統 | Ansible版本 |
---|---|---|---|---|
control_node | 192.168.200.26 | control_node | CentOS 7.2 | Ansible 2.9 |
node1 | 192.168.200.27 | node1 | CentOS 7.2 | - |
node2 | 192.168.200.28 | node2 | CentOS 7.2 | - |
node3 | 192.168.200.29 | node3 | CentOS 7.2 | - |
node4 | 192.168.200.30 | node4 | CentOS 7.2 | - |
node5 | 192.168.200.31 | node5 | CentOS 7.2 | - |
node6 | 192.168.200.32 | node6 | CentOS 7.2 | - |
node7 | 192.168.200.33 | node7 | CentOS 7.2 | - |
所有主機上都已啓動sshd服務並監聽在22端口上。
因爲有時候也會使用主機名去控制目標節點,所以這裏也在control_node節點上配置了其餘7個節點的DNS解析,可在control_node節點的/etc/hosts文件中加入如下內容:
$ cat >>/etc/hosts<<EOF
192.168.200.27 node1
192.168.200.28 node2
192.168.200.29 node3
192.168.200.30 node4
192.168.200.31 node5
192.168.200.32 node6
192.168.200.33 node7
EOF
2.1.1 安裝最新版的Ansible
Ansible依賴於SSH協議(默認),只需要在控制節點(即control_node主機上)安裝一次Ansible即可。
各種系統下安裝最新版Ansible的方式參見官方手冊:安裝手冊。
對於RHEL系列的系統來說,配置好epel鏡像即可安裝最新版的Ansible。在我當前寫文章的時候,最新版是Ansible 2.9版本。
$ cat >>/etc/yum.repos.d/epel.repo<<'EOF'
[epel]
name=epel repo
baseurl=https://mirrors.tuna.tsinghua.edu.cn/epel/7/$basearch
enabled=1
gpgcheck=0
EOF
然後安裝即可:
$ yum install ansible
Ansible每個版本釋放出來之後,都首先提交到Pypi,所以任何操作系統,都可以使用pip工具來安裝最新版的Ansible。
$ pip3 install ansible
但要注意,使用各系統的包管理工具(如yum)安裝Ansible時自動會提供一些配置文件,如/etc/ansible/ansible.cfg
。而使用pip安裝的Ansible默認不提供配置文件。
2.1.2 Ansible參數補全功能
從Ansible 2.9版本開始,它支持命令的選項補全功能,它依賴於python的argcomplete插件。
安裝argcomplete:
# CentOS/RHEL
yum -y install python-argcomplete
# 任何系統都可以使用pip工具安裝argcomplete
pip3 install argcomplete
安裝完成後,還需激活該插件:
# 要求bash版本大於等於4.2
sudo activate-global-python-argcomplete
# 如果bash版本低於4.2,則單獨爲每個ansible命令註冊補全功能
eval $(register-python-argcomplete ansible)
eval $(register-python-argcomplete ansible-config)
eval $(register-python-argcomplete ansible-console)
eval $(register-python-argcomplete ansible-doc)
eval $(register-python-argcomplete ansible-galaxy)
eval $(register-python-argcomplete ansible-inventory)
eval $(register-python-argcomplete ansible-playbook)
eval $(register-python-argcomplete ansible-pull)
eval $(register-python-argcomplete ansible-vault)
最後,退出當前Shell重新進入,或者簡單的直接執行如下命令即可:
exec $SHELL
然後就可以按tab一次或兩次補全參數或提示參數。例如,下面選項輸入到一半的時候,按一下tab鍵就會補全得到ansible --syntax-check
。
$ ansible --syn
2.1.3 配置主機互信
因爲Ansible默認是基於ssh連接的,所以要控制其它節點首先需要建立好ssh連接,而建立ssh連接要麼需要提供密碼,要麼需要配置好認證方式。爲了方便後文的測試,這裏先配置好control_node和其它被控節點之間的主機互信。
爲了避免配置主機互信過程中的交互式詢問,這裏使用ssh-keyscan
工具添加主機認證信息以及sshpass
工具(安裝Ansible時會自動安裝sshpass,也可以yum -y install sshpass
安裝)直接指定ssh連接密碼。
-
在control_node節點上生成密鑰對:
$ ssh-keygen -t rsa -f ~/.ssh/id_rsa -N ''
-
將各節點的主機信息(host key)寫入control_node的
~/.ssh/known_hosts
文件:for host in 192.168.200.{27..33} node{1..7};do ssh-keyscan $host >>~/.ssh/known_hosts 2>/dev/null done
-
將control_node上的ssh公鑰分發給各節點:
# sshpass -p選項指定的是密碼 for host in 192.168.200.{27..33} node{1..7};do sshpass -p'123456' ssh-copy-id root@$host &>/dev/null done
配置好ssh的主機互信之後,就可以開始體驗Ansible了。
2.2 Ansible初體驗
好了,第一個關於模塊的概念就先介紹這麼多,該是體驗一下Ansible是如何執行任務的時候了。
在控制節點上執行:
$ ansible localhost -m copy -a 'src=/etc/passwd dest=/tmp'
localhost | CHANGED => {
"changed": true,
"checksum": "41a051362a32be1ec4266cc64de2c6e4ad06bc73",
"dest": "/tmp/passwd",
"gid": 0,
"group": "root",
"md5sum": "f042f8f7c120afd8c54f437944db1108",
"mode": "0644",
"owner": "root",
"size": 1656,
"src": "/root/.ansible/tmp/ansible-tmp-1575571772.47-60786643831265/source",
"state": "file",
"uid": 0
}
該命令的作用是拷貝本機的/etc/passwd文件到本機的/tmp目錄下。
其中的ansible是一個命令,這個自不需要多做解釋。除ansible命令外,後面還會用到ansible-playbook命令。
localhost參數表示ansible要控制的節點,即ansible將指揮本機執行任務。
執行任務主要是執行模塊,模塊的執行可以還依賴一些模塊參數。在ansible命令行中,使用-m Module
來指定要執行哪個模塊,即執行什麼任務,使用-a ARGS
來指定模塊運行時的參數。
本示例中的模塊爲copy模塊,傳遞給copy模塊的參數包含兩項:
src=/etc/passwd
指定源文件dest=/tmp
指定拷貝的目標路徑
初學Ansible,可能會有兩個疑惑:
- Ansible的模塊那麼多,我如何知道某個功能要找哪個模塊
- 如何知道某個模塊的用法,比如參數有哪些
初學之時,只需學習一些常用的模塊,在本文以及後面的文章中會介紹一些常見模塊的功能以及用法。熟悉了Ansible之後,再按需求到官方手冊中去尋找:https://docs.ansible.com/ansible/latest/modules/modules_by_category.html。
有時候爲了方便快速尋找模塊,可以使用ansible-doc -l | grep 'xxx'
命令來篩選模塊。例如,想要篩選具有拷貝功能的模塊:
$ ansible-doc -l | grep 'copy'
vsphere_copy Copy a file to a VMware datastore
win_copy Copies files to remote locations on windows hosts
bigip_file_copy Manage files in datastores on a BIG-IP
ec2_ami_copy copies AMI between AWS regions, return new image id
win_robocopy Synchronizes the contents of two directories using Robocopy
copy Copy files to remote locations
na_ontap_lun_copy NetApp ONTAP copy LUNs
icx_copy Transfer files from or to remote Ruckus ICX 7000 series switches
unarchive Unpacks an archive after (optionally) copying it from the local machine
postgresql_copy Copy data between a file/program and a PostgreSQL table
ec2_snapshot_copy copies an EC2 snapshot and returns the new Snapshot ID
nxos_file_copy Copy a file to a remote NXOS device
netapp_e_volume_copy NetApp E-Series create volume copy pairs
根據描述,大概找出是否有想要的模塊。
找到模塊後,想要看看它的功能描述以及用法,可以繼續使用ansible-doc
命令。
# 詳細的模塊描述手冊
$ ansible-doc copy
# 只包含模塊參數用法的模塊描述手冊
$ ansible-doc -s copy
再來一個示例,通過Ansible刪除本地文件/tmp/passwd。需要使用的模塊是file
模塊,file模塊的主要作用是創建或刪除文件/目錄。
$ ansible localhost -m file -a 'path=/tmp/passwd state=absent'
參數path=
指定要操作的文件路徑,state=
參數指定執行何種操作,此處指定爲absent表示刪除操作。
Ansible的很多模塊都提供了一個state參數,它是一個非常重要的參數。它的值一般都會包含present和absent兩種狀態值(並非一定),不同模塊的present和absent狀態表示的含義不同,但通常來說,present狀態表示肯定、存在、會、成功等含義,absent則相反,表示否定、不存在、不會、失敗等含義。
例如這裏的file模塊,absent狀態表示遞歸刪除文件/目錄,類似於rm -r
命令,touch狀態和touch命令的功能一樣,directory狀態表示遞歸創建目錄,類似於mkdir -p
命令。
所以,在本地創建文件、創建目錄的命令如下:
# 創建文件
$ ansible localhost -m file -a 'path=/tmp/a.log state=touch'
# 創建目錄
$ ansible localhost -m file -a 'path=/tmp/dir1/dir2 state=directory'
例如,輸出"hello world",需要使用msg參數:
$ ansible localhost -m debug -a 'msg="hello world"'
localhost | SUCCESS => {
"msg": "hello world"
}
Ansible中也支持使用變量,這裏僅演示最簡單的設置變量和引用變量的方式。ansible命令的-e
選項或--extra-vars
選項可以設置變量,設置的方式爲-e 'var1="aaa" var2="bbb"'
。
例如,設置變量後使用debug的msg參數輸出:
$ ansible localhost -e 'str=world' -m debug -a 'msg="hello {{str}}"'
localhost | SUCCESS => {
"msg": "hello world"
}
注意上面示例中的{% raw %} msg="hello {{str}}" {% endraw %},Ansible的字符串是可以不用引號去包圍的,例如msg=hello
是允許的,但如果字符串中包含了特殊符號,則可能需要使用引號去包圍,例如此處的示例出現了會產生歧義的空格。此外,要區分變量名和普通的字符串,需要在變量名上加一點標註:用{% raw %} {{}} {% endraw %}包圍Ansible的變量,這其實是Jinja2模板(如果不知道,先別管這是什麼)的語法。其實不難理解,它的用法和Shell下引用變量使用$
符號或${}
是一樣的,例如echo "hello ${var}"
。
debug模塊除了msg參數,還有一個var參數,它只能用來輸出變量(還包括以後要介紹的Jinja2表達式),而且var參數引用變量的時候,不能使用{% raw %} {{}} {% endraw %}包圍,因爲var參數已經隱式地包圍了一層{% raw %} {{}} {% endraw %}。例如:
$ ansible localhost -e 'str="hello world"' -m debug -a 'var=str'
localhost | SUCCESS => {
"str": "hello world"
}
體驗完Ansible模塊的基本用法後,下面將簡單說說Ansible中的配置文件。
2.3 Ansible配置文件
通過操作系統自帶的包管理器(比如yum、dnf、apt)安裝的Ansible一般都會提供好Ansible的配置文件/etc/ansible/ansible.cfg。
這是Ansible默認的全局配置文件。實際上,Ansible支持4種方式指定配置文件,它們的解析順序從上到下:
ANSIBLE_CFG
: 環境變量指定的配置文件ansible.cfg
: 當前目錄下的ansible.cfg~/.ansible.cfg
: 家目錄下的ansible.cfg/etc/ansible/ansible.cfg
:默認的全局配置文件
Ansible配置文件採用ini風格進行配置,每一項配置都使用key=value
的方式進行配置。
例如,下面是我從默認的/etc/ansible/ansible.cfg中截取的[defaults]
段落和[inventory]
段落的部分配置信息。
[defaults]
# some basic default values...
#inventory = /etc/ansible/hosts
#library = /usr/share/my_modules/
#module_utils = /usr/share/my_module_utils/
#remote_tmp = ~/.ansible/tmp
#local_tmp = ~/.ansible/tmp
#plugin_filters_cfg = /etc/ansible/plugin_filters.yml
#forks = 5
[inventory]
#enable_plugins = host_list, virtualbox, yaml, constructed
#ignore_extensions = .pyc, .pyo, .swp, .bak, ~, .rpm, .md, .txt, ~, .orig, .ini, .cfg, .retry
#ignore_patterns=
#unparsed_is_failed=False
目前沒必要去了解配置文件裏的每項指令的含義,在後面需要的時候,我會介紹涉及到的相關配置項的含義。