背景
公司項目,32臺云主機,部署公司服務(wù),部署比較簡單,把部署包上傳然后解壓并配置,獲取機器信息并將機器信息文件發(fā)給同事進行授權(quán),然后將授權(quán)文件放在指定目錄,拉起完事。然后測測接口可用就行。
32臺云主機,部署包倒是可以分發(fā),但是一想到要在32臺機器上執(zhí)行『解壓-->配置-->獲取指紋信息-->下載指紋信息-->將獲取到的license文件導入指定位置-->服務(wù)拉起-->驗證接口』這流水線一般的操作,就頭疼。
所以想到了倆種方式
- 1> 可以用"ssh免密登錄+腳本刷機"的方式來完成,好處是簡單方便,壞處但是結(jié)果沒有冪等性,如果腳本寫的不到位,還得各種改。
- 2> 用ansible的劇本跑一下,好處是執(zhí)行結(jié)果具有冪等性,即使執(zhí)行多遍結(jié)果相同,壞處是對ansible不熟悉,花費時間可能較長。(手動狗頭)
嗯,不過一想到有這么好的機會,不用ansible刷機可惜了。腳本什么時候都可以寫,但是ansible使用的機會不多啊,所以覺得練練我的ansible劇本。
需求拆分
以上,需求已經(jīng)大體描述過了,然后下面細化一下需求。
- 部署包分發(fā)問題
- 解壓配置問題
- 配置主機名
- 獲取機器信息
- 將獲取到的信息收集到管理節(jié)點
- 將license分發(fā)到每個節(jié)點
- 統(tǒng)一拉起服務(wù)
- 測試接口是否可用
- 設(shè)置服務(wù)健康檢查腳本并自動拉起
各個擊破
- 部署包分發(fā)問題
- 解壓配置問題
上面?zhèn)z個問題可以借助云主機的特性來完成;
先申請一臺云主機,然后將部署包上傳,解壓,配置(服務(wù)地址就用127.0.0.1就行,所以不需要寫機器的IP,端口由于是新機器所以不存在占用),然后就是分發(fā)了,這里使用主機鏡像,類似于KVM的克隆,將第一臺云主機制作鏡像,然后后面創(chuàng)建31臺云主機時使用此鏡像就OK(注意:制作鏡像時只會保存系統(tǒng)盤下的數(shù)據(jù),所以部署包要放在系統(tǒng)盤下面)。
- 配置主機名
這一步比較麻煩,為什么呢?因為如果是單獨創(chuàng)建的云主機,創(chuàng)建時可以自定義主機名,但是批量創(chuàng)建時卻沒有定義主機名的地方,所以還需要自己配置。
具體需求是:
| 節(jié)點 | IP | hostname |
|---|---|---|
| 節(jié)點1 | 172.16.0.6 | kbj-001 |
| 節(jié)點2 | 172.16.0.7 | kbj-002 |
| ... | .. | ... |
| 節(jié)點32 | 172.16.0.37 | kbj-032 |
如果用腳本
可以將IP的最后一段號碼與主機名做對應,然后用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)建云主機時,用的是統(tǒng)一的root密碼,所以使用ansible時就懶得做免密登錄了,就在ansible的配置文件里定義了ansible_ssh_pass='*****',然后將配置文件里的host_key_checking = False打開。
- ansible的主機清單如下:
# 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變量我應該怎么定義比較合適。最初想用loop循環(huán)來做,但是應了抖音那句話:『一看就會,一寫就廢』,買了本《ansible權(quán)威指南》,里面倒是有例子,官網(wǎng)loop模塊也有例子,但看了好幾遍都不知道改怎么寫。
最后實在想不出來了,只能將hosts文件中的主機組單獨列出來,并單獨定義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,要不然手動復制粘貼再修改還是比較麻煩的。vim也可以做到,但是之前用的不多,還得查資料,沒Excel來的快。本來想用精簡的代碼量來搞定這個問題,沒想到還是被難住了,這還是30多臺,要是300多臺,恐怕就要廢了。
PS:此處總結(jié)出一個道理,任何工具用得好的才是工具,如果要效率,還是用自己熟悉的工具比較靠譜(當然,練手除外,人總是要進步的,要不然容易被淘汰,尤其是互聯(lián)網(wǎng)行業(yè))
- 獲取機器信息
這里需要執(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é)點的文件拉取到管控節(jié)點,由于文件名相同,所以需要在文件名前加一個節(jié)點的標識'ansible_hostname'是主機變量facts里面的變量,此處可以直接引用而不用在inventory里定義。還有一個需要注意的是,如果不加flat:yes選項的話,拉過來的文件會存放在dest定義的目錄下的主機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é)點上生成的文件名。
- 將獲取到的信息收集到管理節(jié)點
這個在上面的的劇本中一并定義了。
- 將license分發(fā)到每個節(jié)點
本來還以為是有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í)行腳本。
其實6、7、8這三步可以寫成一個劇本來著,但是由于之前想刷主機名的劇本時用太多時間,導致現(xiàn)在不夠用了,所以就直接在命令行執(zhí)行了。
- 設(shè)置服務(wù)健康檢查腳本并自動拉起
嗯,這個沒什么好說的,在本機crontab -e寫個計劃任務(wù),然后將計劃任務(wù)里執(zhí)行的腳本以及/var/spool/cron/root用copy模塊分發(fā)到各個節(jié)點就OK了。
總結(jié)
算是第一次在項目上使用ansible吧,有些模塊用的不是很熟練,例如register變量注冊,facts里的主機變量怎么調(diào)用,fetch模塊,為什么不用scp(因為沒做免密),定義主機名時怎么用loop循環(huán)或者怎么定義變量可以讓代碼變少,而且看著還高大上。
2019-08-06更新
經(jīng)過幾天的測試以及同事給的思路,終于寫好了ansible的playbook
實現(xiàn)需求3.配置主機名
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,是從主機變量里取出來的
然后定義一個變量ip_end,是取IP地址的最后一個'域',然后減去5,得到的這個數(shù)就可以用作用戶名的變量,例如kbj-002;
定義一個任務(wù),使用hostname模塊,下面加了一個when判斷,因為如果是10以內(nèi)(不包括10)可以這么設(shè)置,但如果是10或者更大,比如11,那么按照上面的hostname,設(shè)置出來的主機名就是kbj-0011了,而不是kbj-011;
所以需要判斷一下,10以下的用
hostanme: 'name=kbj-00{{ ip_end }}'
取值是10或者以上的用下面的方法設(shè)置主機名:
hostanme: 'name=kbj-0{{ ip_end }}'
而且上面的腳本也加了判斷,一開始由于是在自己虛擬機上測試,忽略了這個問題。