fabric 是什么
項目地址在: https://gitee.com/xiaofeipapa/fabric-tutorial
建議先下載代碼, 邊看邊運行.
fabric 是python的一個組件, 能夠在遠程服務器運行你寫好的腳本, 也就是說這是一個運維自動化工具.
以前運維人員在遠程服務器上安裝軟件, 要先登錄 -> 寫腳本(如果習慣本地寫程序, 那么還要上傳程序到服務器) -> 針對服務器反饋輸入數(shù)據(jù) -> 調(diào)試 ..
如果用fabric, 這個流程就是全部自動化了. fabric的程序開發(fā)流程一般是這樣:
- 先在遠程服務器試驗你的腳本/想法.
- 用python fabric 實現(xiàn)它們.
- 運行結(jié)果, 觀察是否正確.
如果只需要操作一兩臺服務器, 那么手動操作肯定比寫程序快. 如果要操作幾十上百臺機器, 那么當然是寫好程序, 然后讓他們自動在幾十臺服務器上面跑更方便. (程序員就是要學會偷懶! )
2021版教程說明
網(wǎng)上能找到的fabric 大多東摘西抄, 程序不能運行. 鍋主要由fabric 來背, 一開始他們并不支持 python3, 所以當python3 流行之后, 以前針對 python2 的fabric教程就基本不能用了; 于是有熱心程序員們搞了個非官方的fabric3 , 寫了一些教程(很少且很多文章是錯的), 但后來fabric 官方支持了 python3 , 也出了個教程....
于是在互聯(lián)網(wǎng)上至少存在3種fabric 教程. 針對python2的, 針對非官方python3版本的, 還有官方python3 官方版本的, 初學者大概會被弄得要發(fā)瘋了吧....
所以我將這份教程命名為2021版, 表示這是今年最新的版本. 同時這是針對官方python3版本的教材, 如果你不熟悉fabric, 就不用看網(wǎng)上其他教程了, 看這一篇即可.
最后, 這篇教程用的是 python3 的fabric 版本. 因此你本機要先安裝python3. 如果是linux 版本, 一般是這樣:
sudo apt install python3-pip
安裝完成之后, 只要用:
sudo pip3 install xxx
所安裝的包/組件就都是python3版本了.
如果是windows系統(tǒng), 假設你下載安裝的就是python3的軟件, 那么當你使用 pip install 的時候, 這個pip 就是3的版本. 所以不需要再多設置.
如何安裝
linux:
sudo pip3 install fabric
windows:
pip install fabric
實驗環(huán)境
如果你很熟悉docker 或者自己就有云服務器, 那么試驗環(huán)境就有了. 如果你沒有(特別是windows 用戶, 很難裝好docker) 怎么辦.
你可以使用我的這個虛擬機鏡像. 首先你要安裝 virtualbox, 下載地址在這里: https://www.virtualbox.org/wiki/Downloads
在某云盤下載我的這個虛擬機鏡像:
這是個 xubuntu 系統(tǒng)(ubuntu的變種), 我最愛的系統(tǒng)之一.
安裝之后用virtualbox 把它導入, 一步步操作如下:
打開virtualbox之后, 選擇New
Type選擇Linux, Version 選擇Ubuntu(64bit) , 然后點擊Next.
內(nèi)存默認 1024 就夠了, 點Next
選擇"Use an existing ... " , 然后點右邊那個文件夾圖標
在打開的界面中, 選擇 Add
然后選擇剛才下載的xub.vdi文件, 如圖:
然后點 choose
這時候你會看到 create 已經(jīng)變得可選了, 點它. 就會看到虛擬機已經(jīng)成功創(chuàng)建.
點擊右上方的Settings, 然后在彈出的界面設置網(wǎng)卡:
(中文名字應該是橋接網(wǎng)絡) . 這樣選了之后, 你的電腦(稱為宿主機) 就可以訪問虛擬機里的操作系統(tǒng)了.
雙擊xub 啟動系統(tǒng), 你會看到 xubuntu的界面. 這個系統(tǒng)有兩個用戶的信息, 分別是:
xiaofeipapa / 密碼 aa
root / 密碼 test
在xubuntu的桌面點擊鼠標右鍵, 選擇 Open Terminal Here, 打開命令行, 輸入:
ifconfig
查看自己的ip是多少. 在我這里是 192.168.135.24 .
然后在宿主機里打開命令行, 輸入:
ssh root@192.168.135.24
然后輸入root的密碼(test ) , 就可以看到ssh 登錄成功了. 好了, 你擁有一個實驗環(huán)境, 可以在上面試驗fabric了.
實驗環(huán)境信息總結(jié)
ip : 192.168.135.24 ( 也許你的機器會不同, 在虛擬機里用ifconfig來查看)
用戶: root / test
fabric 基礎
以下程序的參數(shù)都針對我那個虛擬機鏡像, 如果你有自己的虛擬機或者云服務器, 改相應參數(shù)即可.
起步: 連接服務器, 執(zhí)行命令
#! /usr/bin/python3
# -*- coding: UTF-8 -*-
"""
作者: 小肥爬爬
簡書: http://www.reibang.com/u/db796a501972
gitee: https://gitee.com/xiaofeipapa/python-toolkit
您可以自由轉(zhuǎn)載此博客文章, 懇請保留原鏈接, 謝謝!
"""
from fabric import Connection
def do_it():
host = '192.168.135.24'
user = 'root'
password = 'test'
# ssh 連接的正確姿勢
conn = Connection(host=host, user=user, connect_kwargs={'password': password})
# 在遠程機器運行命令(用run方法), 并獲得返回結(jié)果
# hide 表示隱藏遠程機器在控制臺的輸出, 達到靜默的效果
# 默認 warn是False, 如果遠程機器運行命令出錯, 那么本地會拋出異常堆棧. 設為True 則不顯示這堆棧.
cmd = 'ls /tmp'
result = conn.run(cmd, hide=True, warn=True, encoding='utf-8')
# 正常運行時, 信息在 stdout里
print('-------- 下面是 stdout 信息')
print(result.stdout.strip())
# 出錯時, 信息在 stderr 里
print('-------- 下面是 stderr 信息')
print(result.stderr.strip())
if __name__ == '__main__':
do_it()
注釋里已經(jīng)把api用法說得很清楚了. 請閱讀并運行程序加深印象.
封裝工具類 fab_utils.py
在我們寫程序的時候, 其實大部分時候都運行正常. 如果每次run之后都去判斷 stdout, stderr ... 代碼很快變得混亂不堪. 所以我的選擇是將之封裝成方法, 使之更加輕便. 這個文件命名為 fab_utils.py, 內(nèi)容如下:
#! /usr/bin/python3
# -*- coding: UTF-8 -*-
"""
作者: 小肥爬爬
簡書: http://www.reibang.com/u/db796a501972
gitee: https://gitee.com/xiaofeipapa/python-toolkit
您可以自由轉(zhuǎn)載此博客文章, 懇請保留原鏈接, 謝謝!
將fabric api 再封裝一層, 使之更好用
"""
class FabException(Exception):
def __init__(self, msg):
Exception.__init__(self, msg)
# 運行遠程命令
def run(conn, cmd, hide=True, warn=True):
r = conn.run(cmd, encoding='utf8', hide=hide, warn=warn)
result, err = r.stdout.strip(), r.stderr.strip()
if err:
raise FabException(err)
return result
以后, 我會把這個工具庫逐漸擴大, 加入更多有用的方法. 經(jīng)過這種方式, fabric的大多數(shù)功能就變成你的工具庫, 可以輕松寫出大多數(shù)的運維程序.
寫一個程序 s2_test_fab_utils.py 來測試:
#! /usr/bin/python3
# -*- coding: UTF-8 -*-
"""
作者: 小肥爬爬
簡書: http://www.reibang.com/u/db796a501972
gitee: https://gitee.com/xiaofeipapa/python-toolkit
您可以自由轉(zhuǎn)載此博客文章, 懇請保留原鏈接, 謝謝!
"""
from fabric import Connection
import fab_utils
def do_it():
host = '192.168.135.24'
user = 'root'
password = 'test'
# ssh 連接的正確姿勢
conn = Connection(host=host, user=user, connect_kwargs={'password': password})
# 結(jié)果變得更簡單了
cmd = 'ls /tmp'
result = fab_utils.run(conn, cmd)
# 正常運行時, 信息在 stdout里
print('-------- 下面是結(jié)果')
print(result)
# 出錯時, 程序會拋出異常
# 來一個出錯的例子, tmp/xiaofeipapa 目錄不存在
cmd = 'ls /tmp/xiaofeipapa'
result = fab_utils.run(conn, cmd)
print(result)
if __name__ == '__main__':
do_it()
運行這個結(jié)果, 你能看到第一個例子成功輸出結(jié)果, 第二個例子拋出異常, 這就是我想要的效果.
ssh無密碼登錄ssh
登錄密碼寫在參數(shù)里當然不安全. 用linux 系統(tǒng)的同學都知道, 可以配置無密碼方式訪問服務器, 避免暴露密碼明文在參數(shù)里. (windows我暫時不知道怎么辦, 有待請教. )
首先在宿主機生成密鑰, 一路按回車:
ssh-keygen -t rsa
然后運行這個腳本:
ssh-copy-id root@192.168.135.24
輸入root的登錄密碼 test, 以后就可以無密碼訪問服務器了. 相應地, fabric的連接變成如下:
conn = Connection(host=host, user=user)
不用輸入密碼了.
運行自定義shell腳本
像ls, cd 都是bash 預先提供的命令. 如果要運行自己寫的shell怎么辦? 有兩個辦法: 如果sh 文件很大, 那么先在本地寫好, 然后傳到服務器上運行. 如果shell很少, 直接用這個方式運行:
conn.client.exec_command(cmd)
下面的這個例子演示了如何運行短的shell腳本:
#! /usr/bin/python3
# -*- coding: UTF-8 -*-
"""
作者: 小肥爬爬
簡書: http://www.reibang.com/u/db796a501972
gitee: https://gitee.com/xiaofeipapa/python-toolkit
您可以自由轉(zhuǎn)載此博客文章, 懇請保留原鏈接, 謝謝!
"""
from fabric import Connection
import fab_utils
def do_it():
host = '192.168.0.12'
user = 'root'
# ssh 連接的正確姿勢
conn = Connection(host=host, user=user)
# 運行shell之前, 要隨便運行一個命令, 獲得運行環(huán)境
fab_utils.run(conn, 'uname -a')
# 運行shell腳本
# 在shell里 [ -d xxx ] 表示檢查文件夾是否存在
# [ -d xxx ] 表示檢查文件是否存在
cmd = '[ -d /tmp ] && echo ok'
_stdin, _stdout, _stderr = conn.client.exec_command(cmd)
result = _stdout.read().strip().decode('utf8')
print('--- std out: ')
print(result)
if __name__ == '__main__':
do_it()
檢查文件/夾是否存在
bash沒有提供檢查文件夾是否存在, 文件是否存在的方法, 而這兩個方法是經(jīng)常需要使用的. 所以我們可以用簡單的shell腳本來封裝這兩個方法. 在 fab_utils.py 里加入如下方法:
# 檢查文件夾是否存在
def is_dir_exists(conn, dir_path):
cmd = '[ -d ' + dir_path + ' ] && echo ok'
_stdin, _stdout, _stderr = conn.client.exec_command(cmd)
result = _stdout.read().strip().decode('utf8')
return result == 'ok'
# 檢查文件文件是否存在
def is_file_exists(conn, file_path):
cmd = '[ -f ' + file_path + ' ] && echo ok'
_stdin, _stdout, _stderr = conn.client.exec_command(cmd)
result = _stdout.read().strip().decode('utf8')
return result == 'ok'
# 判斷是否有該文件夾, 不存在時可以通過參數(shù)控制生成文件夾
def check_has_dir(conn, dir_path, create_if_not_exist=True):
if not is_dir_exists(conn, dir_path):
if create_if_not_exist:
run(conn, 'mkdir -p ' + dir_path)
# 判斷是否有該文件, 不存在時可以通過參數(shù)控制生成文件夾
def check_has_file(conn, file_path, create_if_not_exist=True):
if not is_dir_exists(conn, file_path):
if create_if_not_exist:
run(conn, 'touch ' + file_path)
然后寫一個python文件來檢查:
#! /usr/bin/python3
# -*- coding: UTF-8 -*-
"""
作者: 小肥爬爬
簡書: http://www.reibang.com/u/db796a501972
gitee: https://gitee.com/xiaofeipapa/python-toolkit
您可以自由轉(zhuǎn)載此博客文章, 懇請保留原鏈接, 謝謝!
"""
from fabric import Connection
import fab_utils
import os, os.path
def do_it():
host = '192.168.0.12'
user = 'root'
# ssh 連接的正確姿勢
conn = Connection(host=host, user=user)
# 運行shell之前, 要隨便運行一個命令, 獲得運行環(huán)境
fab_utils.run(conn, 'uname -a')
# 檢查文件夾
dir_path = '/root/data'
fab_utils.check_has_dir(conn, dir_path)
# 查看創(chuàng)建文件夾結(jié)果
result = fab_utils.run(conn, 'ls ' + dir_path)
print(result)
# 檢查文件
file_path = os.path.join(dir_path, 'done.txt')
fab_utils.check_has_file(conn, file_path)
# 查看創(chuàng)建文件結(jié)果
result = fab_utils.run(conn, 'ls ' + file_path)
print(result)
if __name__ == '__main__':
do_it()
安全的刪除
我們都知道在服務器上盡量不要用rm命令, 否則刪錯文件那就完了. 所以在用fabric的時候我們同樣要注意這個問題. 在fab_utils.py 里加入這個方法:
# 在服務器安全刪除文件/文件夾的方法
def safe_rm(conn, file_path):
# 先檢查是否存在
try:
run(conn, 'ls ' + file_path)
except FabException:
print(file_path + ' 不存在')
return
# 在/tmp下生成當天目錄
result = run(conn, 'date +%Y-%m-%d')
trash_dir = '/tmp/my_trash/' + result
check_has_dir(conn, trash_dir)
# 在當天下生成uuid, 確保mv 不會失敗
result = run(conn, 'uuidgen')
trash_dir = trash_dir + '/' + result
check_has_dir(conn, trash_dir)
run(conn, 'mv ' + file_path + ' ' + trash_dir)
寫一個文件來測試. 程序在 s5_rm_test.py
#! /usr/bin/python3
# -*- coding: UTF-8 -*-
"""
作者: 小肥爬爬
簡書: http://www.reibang.com/u/db796a501972
gitee: https://gitee.com/xiaofeipapa/python-toolkit
您可以自由轉(zhuǎn)載此博客文章, 懇請保留原鏈接, 謝謝!
"""
from fabric import Connection
import fab_utils
import os, os.path
def do_it():
host = '192.168.0.12'
user = 'root'
# ssh 連接的正確姿勢
conn = Connection(host=host, user=user)
# 運行shell之前, 要隨便運行一個命令, 獲得運行環(huán)境
fab_utils.run(conn, 'uname -a')
# 刪除之前創(chuàng)建的文件夾 /root/data
dir_path = '/root/data'
fab_utils.safe_rm(conn, dir_path)
if fab_utils.is_dir_exists(conn, dir_path):
print('--- 文件夾還在')
else:
print('--- 文件夾已經(jīng)刪除')
if __name__ == '__main__':
do_it()
寫入文本文件
在服務器上我們通常會有寫文件的任務, 例如寫入環(huán)境變量到配置文件, 在fabric 里可以用簡單的shell命令來完成:
conn.run("echo 'hello' >> /tmp/test.txt")
這個命令又是雙引號又是單引號, 所以還是封裝起來更好用. 在fab_utils.py 里加入如下方法:
# 追加一行到文件末尾
def append_line(conn, file_path, content):
if not is_file_exists(conn, file_path):
return
cmd = "echo '" + content + "' >> " + file_path
run(conn, cmd)
然后寫一個文件來測試, 代碼在 s6_env_var.py
#! /usr/bin/python3
# -*- coding: UTF-8 -*-
"""
作者: 小肥爬爬
簡書: http://www.reibang.com/u/db796a501972
gitee: https://gitee.com/xiaofeipapa/python-toolkit
您可以自由轉(zhuǎn)載此博客文章, 懇請保留原鏈接, 謝謝!
"""
from fabric import Connection
import fab_utils
def do_it():
host = '192.168.0.12'
user = 'root'
# ssh 連接的正確姿勢
conn = Connection(host=host, user=user)
# 運行shell之前, 要隨便運行一個命令, 獲得運行環(huán)境
fab_utils.run(conn, 'uname -a')
# 在/tmp 下生成 test_rc 文件,
file_path = '/root/test_rc'
fab_utils.check_has_file(conn, file_path)
# 寫入內(nèi)容到文件里
fab_utils.append_line(conn, file_path, "#==其他配置信息1")
fab_utils.append_line(conn, file_path, "#==其他配置信息2")
fab_utils.append_line(conn, file_path, "#==其他配置信息3")
fab_utils.append_line(conn, file_path, "\n")
fab_utils.append_line(conn, file_path, "#==JAVA安裝信息")
fab_utils.append_line(conn, file_path, "JAVA_HOME=XXX")
fab_utils.append_line(conn, file_path, "CLASS_PATH=YYY")
fab_utils.append_line(conn, file_path, "export PATH=$PATH:JAVA_HOME/bin")
# 查看文件內(nèi)容
result = fab_utils.run(conn, 'cat ' + file_path)
print(result)
if __name__ == '__main__':
do_it()
自動響應
有些命令如 passwd , adduser 會等候你繼續(xù)輸入下一步. 像這種需要手動響應的自動化程序應該怎么寫? 在fabric 里這樣做:
from invoke import Responder
# 其他代碼
resp1 = Responder(
pattern=r'Enter new UNIX password:',
response=pwd + '\n'
)
resp2 = Responder(
pattern=r'Retype new UNIX password:',
response=pwd + '\n'
)
conn.run('passwd ' + username, pty=True, hide=True, watchers=[resp1, resp2])
Responder 是 invoke 框架的一部分(fabric 已經(jīng)包括invoke框架) . 它的使用方法如下:
- pattern: 正則表達式, 用來匹配命令行的輸出. 如果里面有需要轉(zhuǎn)義的字符, 千萬要記得轉(zhuǎn)義.
- response: 需要輸入的內(nèi)容.
這兩個參數(shù)結(jié)合起來的意思是: 如果匹配到命令輸出, 需要輸入信息的時候, 就自動輸入response定好的字符串.
如果不確定某些命令的pattern和resonse 是什么. 可以先在服務器手動輸入命令看看結(jié)果, 然后再寫程序. 如果程序多次需要輸入, 那么就要寫多個responder, 用個數(shù)組包起來.
注意: 如果程序長時間沒有反饋, 證明你的pattern 寫得有問題.
最常見的原因就是轉(zhuǎn)義字符沒有進行處理. 下面這是一個例子:
# 在yum上安裝軟件
def yum_install(conn, cmd):
resp = Responder(
# pattern=r'Is this ok [y/d/N]:', # 錯誤例子: 沒有正則轉(zhuǎn)義
pattern=r'Is this ok \[y/d/N\]:',
response='y\n'
)
conn.run(cmd, pty=True, watchers=[resp], hide=True)
下面是一個完整的例子: 我們將用戶xiaofeipapa的密碼改為 bb, 整個過程是自動化無人工干預的:
#! /usr/bin/python3
# -*- coding: UTF-8 -*-
"""
作者: 小肥爬爬
簡書: http://www.reibang.com/u/db796a501972
gitee: https://gitee.com/xiaofeipapa/python-toolkit
您可以自由轉(zhuǎn)載此博客文章, 懇請保留原鏈接, 謝謝!
"""
from fabric import Connection
import fab_utils
from invoke import Responder
def do_it():
host = '192.168.0.12'
user = 'root'
# ssh 連接的正確姿勢
conn = Connection(host=host, user=user)
# 運行shell之前, 要隨便運行一個命令, 獲得運行環(huán)境
fab_utils.run(conn, 'uname -a')
# 將密碼改成bb
username = 'xiaofeipapa'
pwd = 'bb'
resp1 = Responder(
pattern=r'Enter new UNIX password:',
response=pwd + '\n'
)
resp2 = Responder(
pattern=r'Retype new UNIX password:',
response=pwd + '\n'
)
conn.run('passwd ' + username, pty=True, hide=True, watchers=[resp1, resp2])
if __name__ == '__main__':
do_it()
fabric 實戰(zhàn)案例: 在遠程服務器上安裝 jdk環(huán)境
前面零零散散地介紹了很多fabric的用法, 現(xiàn)在用這個案例把它綜合起來: 在遠程服務器(虛擬機) 里安裝jdk環(huán)境.
這個任務做開發(fā)/運維的同學應該都不陌生. java技術棧的運維架構通常是1個nginx, 后端掛若干個安裝jdk服務器, 以此達到負載均衡的目的.它的任務可以分解為:
- 在遠程服務器上下載解壓jdk包.
- 在配置文件寫入若干個環(huán)境變量(如JAVA_HOME, CLASSPATH) , 指向jdk的解壓路徑.
- 創(chuàng)建新的登錄用戶, 開發(fā)者用它來登錄機器, 發(fā)布jar 包.
當然其中還包括一些彎彎繞繞的必要任務. 例如怎么判斷任務已經(jīng)運行過, 所以不會再重復運行? 怎么判斷jdk包已經(jīng)存在, 不用再重新下載? 怎么判斷環(huán)境變量已經(jīng)寫入到配置文件, 不用重復寫入?
這些技巧一一列舉如下.
怎么知道任務是否第一次執(zhí)行?
思路: 給任務一個名字, 例如 install_jdk. 第一次在目標機器運行之后, 就會在 /root 目錄下生成這個文件: /root/data/install_jdk/done.txt . 所以程序可以判斷這個文件是否存在, 如果不存在表示此任務還沒執(zhí)行過. 如果存在, 那么可以通過命令行參數(shù)來指定是否再次運行任務.
代碼如下:
def _prepare(self, rerun):
"""
檢查任務是否已經(jīng)啟動過
:return:
"""
# 必須要先run連接遠程服務器, 否則 conn.client.exec_command 會報錯
fab_utils.run(self.conn, 'ls /tmp')
# 生成 /root/data 目錄
base_dir = '/root/data'
fab_utils.create_if_remote_dir_not_exist(self.conn, base_dir)
# 檢查該task 是否已經(jīng)執(zhí)行過. 如果已經(jīng)執(zhí)行過, 在 /root/data/task 目錄下有 done.txt 文件
task_home = base_dir + '/' + self.task
fab_utils.create_if_remote_dir_not_exist(self.conn, task_home)
self.task_home = task_home
task_file = task_home + '/done.txt'
if not fab_utils.is_remote_file_exist(self.conn, task_file):
return
# 如果存在, 又不指定重新運行, 則退出
if not rerun:
title('此程序已經(jīng)在目標機器運行過了. 如果想再次運行, 請設置啟動參數(shù) -r true')
exit(0)
# ...... 其他代碼
# 命令行指定啟動參數(shù)
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser('傳入?yún)?shù):***.py')
parser.add_argument('-r', '--rerun', default=True) # 開發(fā)時用
# parser.add_argument('-r', '--rerun', default=False)
args = parser.parse_args()
rerun = args.rerun
print('---- 參數(shù) rerun: ', rerun)
do_it(args)
判斷jdk存在,不用重復下載
思路: 當?shù)谝淮蜗螺d好jdk包的時候, 我們可以拿到該包的md5, 然后如果服務器上已經(jīng)存在jdk包并且md5一致, 那么就不用再下載. 否則開始下載jdk.
代碼如下:
with self.conn.cd(self.task_home):
# 檢查下載jdk
file_path = self.task_home + '/' + jdk_file
file_md5 = 'ef599e322eee42f6769991dd3e3b1a31'
down_link = 'wget https://repo.huaweicloud.com/java/jdk/8u181-b13/' + jdk_file
fab_utils.check_download(self.conn, file_path, file_md5, down_link)
# 創(chuàng)建jvm目錄
fab_utils.create_if_remote_dir_not_exist(self.conn, jvm_dir)
# 解壓. 華為的這個包解壓后名字不同, 所以手動指定一個目錄
hint('正在解壓 jdk ......')
fab_utils.run(self.conn, 'tar xvf ' + jdk_file + ' -C ' + jvm_dir + ' --strip-components 1 ')
寫入配置文件與重復檢查
有兩個思路:
- 利用sed, 如果發(fā)現(xiàn)已經(jīng)有相應的環(huán)境變量, 將之刪除重新寫入.
- 用 echo 顯示 JAVA_HOME, 如果有值
第一個思路容易錯刪其他內(nèi)容, 所以后來采用了第二個思路. 代碼如下:
run_result = conn.run('echo $JAVA_HOME ', hide=True).stdout.strip()
if run_result == jvm_dir:
hint(system_file + ' 已經(jīng)包含 JAVA_HOME, 不需要重新設置')
else:
# 追加內(nèi)容到系統(tǒng)配置
conn.run("echo '# ===== java 配置' >> " + system_file)
conn.run("echo 'export JAVA_HOME=" + jvm_dir + "' >> " + system_file)
# 將 exporter jar 也加入到 classpath 里
export = 'export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JAVA_HOME/jre/lib/rt.jar:'\
+ exporter_jar_path
conn.run("echo '" + export + "' >> " + system_file)
conn.run("echo 'export PATH=$PATH:$JAVA_HOME/bin' >> " + system_file)
# 使環(huán)境變量生效
conn.run('source ' + system_file)
這里還有個小竅門, linux 那么多配置文件如 /etc/profile , /etc/bashrc , 究竟應該寫到哪個文件? 查了一下資料, /etc/profile 只在用戶第一次登錄的時候起作用, 所以應該寫到 /etc/bashrc 里.
怎么生成新用戶
思路: 生成新用戶很簡單, 需要考慮的是給它設定密碼. 考察了一下linux上生成密碼的方式, 最后決定用 pwgen . 用法:
# 生成10個字符的密碼, 個數(shù)為1
sudo apt install pwgen
pwgen -s 10 1
代碼大概長這樣:
def add_user(self, username):
"""
增加用戶, 并設置密碼.
:param username: 用戶名
:return: 登錄密碼
"""
if self._is_user_exist(username):
title('用戶 ' + username + ' 已經(jīng)存在')
fab_utils.close_connection(self.conn)
return None
# 生成用戶. 該命令只在centos 測試過
fab_utils.run(self.conn, 'adduser ' + username)
# 生成10位密碼
cmd = 'pwgen -s 10 1'
password = fab_utils.run(self.conn, cmd)
# 更改密碼
fab_utils.yum_passwd(self.conn, username, password)
# 退出登錄
fab_utils.close_connection(self.conn)
# 登錄進行測試
self.verify_login(username, self.host, password)
return password
完整代碼
項目地址: https://gitee.com/xiaofeipapa/fabric-tutorial
請看 setup_java.py 文件
總結(jié)
通過這么多方法和整個案例的展示, 你應該掌握了fabric 90%的用法, 剩下的就靠你自己探索了. 希望你用fabric玩得愉快~
記得給個star和贊啊, 少年~