背景
公司項(xiàng)目虽缕,32臺云主機(jī),部署公司服務(wù)蒲稳,部署比較簡單氮趋,把部署包上傳然后解壓并配置,獲取機(jī)器信息并將機(jī)器信息文件發(fā)給同事進(jìn)行授權(quán)江耀,然后將授權(quán)文件放在指定目錄剩胁,拉起完事。然后測測接口可用就行祥国。
32臺云主機(jī)昵观,部署包倒是可以分發(fā),但是一想到要在32臺機(jī)器上執(zhí)行『解壓-->配置-->獲取指紋信息-->下載指紋信息-->將獲取到的license文件導(dǎo)入指定位置-->服務(wù)拉起-->驗(yàn)證接口』這流水線一般的操作舌稀,就頭疼啊犬。
所以想到了倆種方式
- 1> 可以用"ssh免密登錄+腳本刷機(jī)"的方式來完成,好處是簡單方便,壞處但是結(jié)果沒有冪等性壁查,如果腳本寫的不到位觉至,還得各種改。
- 2> 用ansible的劇本跑一下睡腿,好處是執(zhí)行結(jié)果具有冪等性语御,即使執(zhí)行多遍結(jié)果相同峻贮,壞處是對ansible不熟悉,花費(fèi)時間可能較長沃暗。(手動狗頭)
嗯月洛,不過一想到有這么好的機(jī)會,不用ansible刷機(jī)可惜了孽锥。腳本什么時候都可以寫嚼黔,但是ansible使用的機(jī)會不多啊,所以覺得練練我的ansible劇本惜辑。
需求拆分
以上唬涧,需求已經(jīng)大體描述過了,然后下面細(xì)化一下需求盛撑。
- 部署包分發(fā)問題
- 解壓配置問題
- 配置主機(jī)名
- 獲取機(jī)器信息
- 將獲取到的信息收集到管理節(jié)點(diǎn)
- 將license分發(fā)到每個節(jié)點(diǎn)
- 統(tǒng)一拉起服務(wù)
- 測試接口是否可用
- 設(shè)置服務(wù)健康檢查腳本并自動拉起
各個擊破
- 部署包分發(fā)問題
- 解壓配置問題
上面?zhèn)z個問題可以借助云主機(jī)的特性來完成碎节;
先申請一臺云主機(jī),然后將部署包上傳抵卫,解壓狮荔,配置(服務(wù)地址就用127.0.0.1就行,所以不需要寫機(jī)器的IP介粘,端口由于是新機(jī)器所以不存在占用)殖氏,然后就是分發(fā)了,這里使用主機(jī)鏡像姻采,類似于KVM的克隆雅采,將第一臺云主機(jī)制作鏡像,然后后面創(chuàng)建31臺云主機(jī)時使用此鏡像就OK(注意:制作鏡像時只會保存系統(tǒng)盤下的數(shù)據(jù)慨亲,所以部署包要放在系統(tǒng)盤下面)婚瓜。
- 配置主機(jī)名
這一步比較麻煩,為什么呢刑棵?因?yàn)槿绻菃为?dú)創(chuàng)建的云主機(jī)巴刻,創(chuàng)建時可以自定義主機(jī)名,但是批量創(chuàng)建時卻沒有定義主機(jī)名的地方铐望,所以還需要自己配置冈涧。
具體需求是:
節(jié)點(diǎn) | IP | hostname |
---|---|---|
節(jié)點(diǎn)1 | 172.16.0.6 | kbj-001 |
節(jié)點(diǎn)2 | 172.16.0.7 | kbj-002 |
... | .. | ... |
節(jié)點(diǎn)32 | 172.16.0.37 | kbj-032 |
如果用腳本
可以將IP的最后一段號碼與主機(jī)名做對應(yīng),然后用for循環(huán)來做正蛙,如下
for i in `seq 7 37`;do
IP="172.16.0.${i}"
for j in ${IP};do
((i=i-5))
if [ ${i} -lt 10 ];then
ssh ${j} "hostnamectl set-hostname kbj-00${i}"
elif [ ${i} -ge 10 ];then
ssh ${j} "hostnamectl set-hostname kbj-0${i}"
fi
done
done
用ansible要怎么做呢
由于創(chuàng)建云主機(jī)時,用的是統(tǒng)一的root密碼营曼,所以使用ansible時就懶得做免密登錄了乒验,就在ansible的配置文件里定義了ansible_ssh_pass='*****',然后將配置文件里的host_key_checking = False打開。
- ansible的主機(jī)清單如下:
# cat /etc/ansible/hosts
[master]
172.16.0.6 ansible_ssh_pass='****'
[kbj]
172.16.0.[7:9]
172.16.0.1[0:9]
172.16.0.2[0:9]
172.16.0.3[0:7]
[kbj:vars]
ansible_ssh_pass='****'
[all:children]
master
kbj
- 設(shè)置hostname的playbook如下:
# cat set-hostname.yaml
---
- hosts: all
gather_facts: true
tasks:
- name: set hostname
hostname: 'name={{ host_name }}'
- name: "add line"
lineinfile:
dest: /etc/hosts
line: "{{ ansible_all_ipv4_addresses[0] }} {{ host_name }}"
此時的問題是蒂阱,這個host_name變量我應(yīng)該怎么定義比較合適锻全。最初想用loop循環(huán)來做狂塘,但是應(yīng)了抖音那句話:『一看就會,一寫就廢』鳄厌,買了本《ansible權(quán)威指南》荞胡,里面倒是有例子,官網(wǎng)loop模塊也有例子了嚎,但看了好幾遍都不知道改怎么寫泪漂。
最后實(shí)在想不出來了,只能將hosts文件中的主機(jī)組單獨(dú)列出來歪泳,并單獨(dú)定義host_name變量萝勤,這樣就可以用這個劇本了。如下
172.16.0.7 host_name=kbj-002
172.16.0.8 host_name=kbj-003
172.16.0.9 host_name=kbj-004
172.16.0.10 host_name=kbj-005
172.16.0.11 host_name=kbj-006
172.16.0.12 host_name=kbj-007
172.16.0.13 host_name=kbj-008
172.16.0.14 host_name=kbj-009
172.16.0.15 host_name=kbj-010
172.16.0.16 host_name=kbj-011
172.16.0.17 host_name=kbj-012
172.16.0.18 host_name=kbj-013
172.16.0.19 host_name=kbj-014
172.16.0.20 host_name=kbj-015
172.16.0.21 host_name=kbj-016
172.16.0.22 host_name=kbj-017
172.16.0.23 host_name=kbj-018
172.16.0.24 host_name=kbj-019
172.16.0.25 host_name=kbj-020
172.16.0.26 host_name=kbj-021
172.16.0.27 host_name=kbj-022
172.16.0.28 host_name=kbj-023
172.16.0.29 host_name=kbj-024
172.16.0.30 host_name=kbj-025
172.16.0.31 host_name=kbj-026
172.16.0.32 host_name=kbj-027
172.16.0.33 host_name=kbj-028
172.16.0.34 host_name=kbj-029
172.16.0.35 host_name=kbj-030
172.16.0.36 host_name=kbj-031
172.16.0.37 host_name=kbj-032
嗯呐伞,這里可以借助Excel敌卓,要不然手動復(fù)制粘貼再修改還是比較麻煩的。vim也可以做到伶氢,但是之前用的不多趟径,還得查資料,沒Excel來的快癣防。本來想用精簡的代碼量來搞定這個問題蜗巧,沒想到還是被難住了,這還是30多臺劣砍,要是300多臺惧蛹,恐怕就要廢了。
PS:此處總結(jié)出一個道理刑枝,任何工具用得好的才是工具香嗓,如果要效率,還是用自己熟悉的工具比較靠譜(當(dāng)然装畅,練手除外靠娱,人總是要進(jìn)步的,要不然容易被淘汰掠兄,尤其是互聯(lián)網(wǎng)行業(yè))
- 獲取機(jī)器信息
這里需要執(zhí)行一個腳本來獲取硬件碼像云,然后將獲取到的results.txt文件發(fā)給RD來授權(quán),使用下面playbook來做的
# cat test.yaml
---
- hosts: all
tasks:
- name: stat /mnt/scriprtsh
stat: path=/mnt/script.sh
register: token_stat
- name: add execute to script
file:
path: /mnt/script.sh
mode: '0777'
when: token_stat.stat.exists
- name: run token to create results.txt
shell: /mnt/script.sh
when: token_stat.stat.exists
- name: stat if exists results.txt
stat: path=/mnt/results.txt
register: result_stat
- name: scp results.txt to master
fetch:
src: /mnt/results.txt
dest: /mnt/{{ ansible_hostname }}-results.txt
flat: yes
when: result_stat.stat.exists
講一下這個劇本的任務(wù):
第一個任務(wù):查看指定路徑下的文件是否存在并注冊一個變量(用于后面when引用)
第二個任務(wù):給此腳本增加執(zhí)行權(quán)限
第三個任務(wù):執(zhí)行腳本
第四個任務(wù):查看是否生成了results.txt文件并注冊變量
第五個任務(wù):使用fetch模塊將各個節(jié)點(diǎn)的文件拉取到管控節(jié)點(diǎn)蚂夕,由于文件名相同迅诬,所以需要在文件名前加一個節(jié)點(diǎn)的標(biāo)識'ansible_hostname'是主機(jī)變量facts里面的變量,此處可以直接引用而不用在inventory里定義婿牍。還有一個需要注意的是侈贷,如果不加flat:yes選項(xiàng)的話,拉過來的文件會存放在dest定義的目錄下的主機(jī)IP+src路徑下等脂。例如我上面如果沒有加flat參數(shù)俏蛮,則會放在
/mnt/kbj-002/172.16.0.7/mn/
/mnt/kbj-003/172.16.0.8/mn/
/mnt/kbj-004/172.16.0.9/mn/
...
此路徑下撑蚌,文件名還是節(jié)點(diǎn)上生成的文件名。
- 將獲取到的信息收集到管理節(jié)點(diǎn)
這個在上面的的劇本中一并定義了搏屑。
- 將license分發(fā)到每個節(jié)點(diǎn)
本來還以為是有32個license文件争涌,沒想到只有一個,這樣就好說了辣恋,也不用寫劇本了亮垫,直接用ansible命令就能搞定
ansible kbj -m copy -a "src=/mnt/license dest=/home/work/program/conf/license mode='0600'"
- 統(tǒng)一拉起服務(wù)
這里用shell模塊
ansible all -m shell -a "cd path/to/file;sh control start"
- 測試接口是否可用
也是使用的shell模塊做的,用netstat -tnlp|grep PORT
命令來對監(jiān)聽端口做檢測抑党,然后在執(zhí)行腳本包警。
其實(shí)6、7底靠、8這三步可以寫成一個劇本來著害晦,但是由于之前想刷主機(jī)名的劇本時用太多時間,導(dǎo)致現(xiàn)在不夠用了暑中,所以就直接在命令行執(zhí)行了壹瘟。
- 設(shè)置服務(wù)健康檢查腳本并自動拉起
嗯,這個沒什么好說的鳄逾,在本機(jī)crontab -e
寫個計劃任務(wù)稻轨,然后將計劃任務(wù)里執(zhí)行的腳本以及/var/spool/cron/root用copy模塊分發(fā)到各個節(jié)點(diǎn)就OK了。
總結(jié)
算是第一次在項(xiàng)目上使用ansible吧雕凹,有些模塊用的不是很熟練殴俱,例如register變量注冊,facts里的主機(jī)變量怎么調(diào)用枚抵,fetch模塊线欲,為什么不用scp(因?yàn)闆]做免密),定義主機(jī)名時怎么用loop循環(huán)或者怎么定義變量可以讓代碼變少汽摹,而且看著還高大上李丰。
2019-08-06更新
經(jīng)過幾天的測試以及同事給的思路,終于寫好了ansible的playbook
實(shí)現(xiàn)需求3.配置主機(jī)名
ansible的hosts文件內(nèi)容不變逼泣,盡量簡潔
# cat /etc/ansible/hosts
[master]
172.16.0.6 ansible_ssh_pass='****'
[kbj]
172.16.0.[7:9]
172.16.0.1[0:9]
172.16.0.2[0:9]
172.16.0.3[0:7]
[kbj:vars]
ansible_ssh_pass='****'
[all:children]
master
kbj
然后劇本如下:
# cat set_hostname.yml
---
- hosts: kbj
gather_facts: true
vars:
ip: "{{ ansible_all_ipv4_addresses[0] }}"
ip_end: "{{ ip.split('.')[3] | int - 5"
tasks:
- name: set hostname for ip end number less than 10
hostname: 'name=kbj-00{{ ip_end }}'
when: ip_end|int < 10
- name: set hostname for ip end number be equtal or greater than 10
hostname: 'name=kbj-0{{ ip_end }}'
when: ip_end|int >= 10
定義一個變量ip趴泌,是從主機(jī)變量里取出來的
然后定義一個變量ip_end,是取IP地址的最后一個'域'拉庶,然后減去5嗜憔,得到的這個數(shù)就可以用作用戶名的變量,例如kbj-002;
定義一個任務(wù)氏仗,使用hostname模塊痹筛,下面加了一個when判斷,因?yàn)槿绻?0以內(nèi)(不包括10)可以這么設(shè)置廓鞠,但如果是10或者更大帚稠,比如11,那么按照上面的hostname床佳,設(shè)置出來的主機(jī)名就是kbj-0011了滋早,而不是kbj-011;
所以需要判斷一下,10以下的用
hostanme: 'name=kbj-00{{ ip_end }}'
取值是10或者以上的用下面的方法設(shè)置主機(jī)名:
hostanme: 'name=kbj-0{{ ip_end }}'
而且上面的腳本也加了判斷砌们,一開始由于是在自己虛擬機(jī)上測試杆麸,忽略了這個問題。