ansible-playbook第一次實戰(zhàn)

背景

公司項目,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)大體描述過了,然后下面細化一下需求。

    1. 部署包分發(fā)問題
    1. 解壓配置問題
    1. 配置主機名
    1. 獲取機器信息
    1. 將獲取到的信息收集到管理節(jié)點
    1. 將license分發(fā)到每個節(jié)點
    1. 統(tǒng)一拉起服務(wù)
    1. 測試接口是否可用
    1. 設(shè)置服務(wù)健康檢查腳本并自動拉起

各個擊破

    1. 部署包分發(fā)問題
    1. 解壓配置問題

上面?zhèn)z個問題可以借助云主機的特性來完成;
先申請一臺云主機,然后將部署包上傳,解壓,配置(服務(wù)地址就用127.0.0.1就行,所以不需要寫機器的IP,端口由于是新機器所以不存在占用),然后就是分發(fā)了,這里使用主機鏡像,類似于KVM的克隆,將第一臺云主機制作鏡像,然后后面創(chuàng)建31臺云主機時使用此鏡像就OK(注意:制作鏡像時只會保存系統(tǒng)盤下的數(shù)據(jù),所以部署包要放在系統(tǒng)盤下面)。

    1. 配置主機名

這一步比較麻煩,為什么呢?因為如果是單獨創(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è))

    1. 獲取機器信息

這里需要執(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é)點上生成的文件名。

    1. 將獲取到的信息收集到管理節(jié)點

這個在上面的的劇本中一并定義了。

    1. 將license分發(fā)到每個節(jié)點

本來還以為是有32個license文件,沒想到只有一個,這樣就好說了,也不用寫劇本了,直接用ansible命令就能搞定

ansible kbj -m copy -a "src=/mnt/license dest=/home/work/program/conf/license mode='0600'"

    1. 統(tǒng)一拉起服務(wù)

這里用shell模塊

ansible all -m shell -a "cd path/to/file;sh control start"
    1. 測試接口是否可用

也是使用的shell模塊做的,用netstat -tnlp|grep PORT命令來對監(jiān)聽端口做檢測,然后在執(zhí)行腳本。
其實6、7、8這三步可以寫成一個劇本來著,但是由于之前想刷主機名的劇本時用太多時間,導致現(xiàn)在不夠用了,所以就直接在命令行執(zhí)行了。

    1. 設(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 }}'

而且上面的腳本也加了判斷,一開始由于是在自己虛擬機上測試,忽略了這個問題。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容