1.前言
Python 的 Fabric 库在系统管理和自动化任务方面发挥着重要作用,随着其发展,出现了版本的迭代。
2.版本区分
- 语法风格: Fabric 2 采用了更现代、简洁的 Python 语法,相比 1 版本更加清晰易读。
- 任务task: 2 版本对任务的定义和组织方式进行了优化,使其更具逻辑性和结构性。
- 上下文: 在上下文的处理上,2 版本有了更明确和方便的机制。
3.Fabric 1 版本的特点
较为传统的任务声明和执行模式,代码风格可能相对较为复杂。
- 代码示列
from fabric.api import *
env.hosts = ['192.168.168.131']
env.port = 22
env.user = 'root'
env.password = '123456'
def start_test():
# jar目录
localRootPath = "/Users/admin/python_test"
liunxPath = "~/local/system"
run("mkdir -p %s" % liunxPath)
with lcd(localRootPath):
local("ls")
put("*.jar", liunxPath)
run("ls")
- 打开命令行,执行这个任务
fab start_test
- 输出日志
[192.168.168.131] Executing task 'start_test'
[192.168.168.131] run: mkdir -p ~/local/system
[localhost] local: ls
abc.txt ccc.yml member.jar system.jar test test222
[192.168.168.131] put: /Users/admin/python_test/member.jar -> /home/suadmin/local/system/member.jar
[192.168.168.131] put: /Users/admin/python_test/system.jar -> /home/suadmin/local/system/system.jar
[192.168.168.131] run: ls
[192.168.168.131] out: ftmo-service jdk-8u301-linux-x64.tar.gz local logs nacos
本地的指令lcd
,local
,远程的指令使用的是cd
,run
,上传文件用的put
,本地参数默认支持通配符。
4.Fabric 2 版本
使用了注解来定义任务,代码更加明了。明显的区分了本地和远程的操作对象,也就是class区分了。
- 代码示列
import glob
import os
from fabric import task, Connection
dict = {
"dev": {
"host": "192.168.168.131",
"port": 22,
"user": "root",
"connect_kwargs": {"password": "123456"}
}
}
#这里的c 默认是本地操作对象 远程对象需要自己连接 env参数可以通过指令传参进去
@task
def liunxTest(c, env):
print("开始部署本地环境:" + env)
ssh = dict[env]
#建立连接
remoteConn = Connection(ssh["host"], user=ssh["user"], port=ssh["port"], connect_kwargs=ssh["connect_kwargs"])
c.run("ls /Users/admin/python_test")
#2版本的put不支持通配符 需要自己获取文件或者目录传到服务器 匹配方法在下面
localRootPath = "/Users/admin/python_test"
# fileList = searchFile(localRootPath, "*.jar")
fileList = searchFileAll(localRootPath)
print(fileList)
#服务器连接上的家目录 不能用“~”了 可以用相对路径
liunxPath = "local/system"
remoteConn.run("mkdir -p %s" % liunxPath)
with remoteConn.cd(liunxPath):
for file in fileList:
#获取相对目录路径
relPath = os.path.relpath(file, localRootPath)
print("执行文件:", file,relPath)
#如果是目录 则创建 否则才上传
if os.path.isdir(file):
remoteConn.run("mkdir -p %s" % relPath)
else:
remoteConn.put(file, os.path.join(liunxPath,relPath))
remoteConn.run("ls")
# 查指定目录下指定后缀的文件
def searchFile(path, suffix):
files = glob.glob(os.path.join(path, suffix))
files = [filePath for filePath in files if os.path.isfile(filePath)]
return files
def searchFileAll(path):
"""递归搜索目录中的文件和文件夹"""
matches = []
for root, dirnames, filenames in os.walk(path):
for dirname in dirnames:
matches.append(os.path.join(root, dirname))
for filename in filenames:
matches.append(os.path.join(root, filename))
return matches
- 启动任务
# 启动任务 传参 dev
fab2 liunxTest dev
- 日志输出
开始部署本地环境:dev
['/Users/admin/python_test/test', '/Users/admin/python_test/test222', '/Users/admin/python_test/.DS_Store', '/Users/admin/python_test/abc.txt', '/Users/admin/python_test/member.jar', '/Users/admin/python_test/system.jar', '/Users/admin/python_test/ccc.yml', '/Users/admin/python_test/test/test1', '/Users/admin/python_test/test/test.txt', '/Users/admin/python_test/test/test1/test1.txt', '/Users/admin/python_test/test222/1222.txt']
执行文件: /Users/admin/python_test/test test
执行文件: /Users/admin/python_test/test222 test222
执行文件: /Users/admin/python_test/abc.txt abc.txt
执行文件: /Users/admin/python_test/member.jar member.jar
执行文件: /Users/admin/python_test/system.jar system.jar
执行文件: /Users/admin/python_test/ccc.yml ccc.yml
执行文件: /Users/admin/python_test/test/test1 test/test1
执行文件: /Users/admin/python_test/test/.DS_Store test/.DS_Store
执行文件: /Users/admin/python_test/test/test.txt test/test.txt
执行文件: /Users/admin/python_test/test/test1/test1.txt test/test1/test1.txt
执行文件: /Users/admin/python_test/test222/1222.txt test222/1222.txt
abc.txt
ccc.yml
member.jar
system.jar
test
test222
5.使用场景与选择
对于新的项目,如果追求简洁和现代的代码风格,Fabric 2 是更好的选择。
若要维护基于 1 版本的旧项目,则需根据实际情况决定是否迁移到 2 版本。
6.Python3虚拟环境及fabric下载配置
Python3自行下载,虚拟环境配置,Python2忽略这个
# 虚拟环境
# python3 -m venv ./python3_venv
# 激活
# source python3_venv/bin/activate
# 在当前窗口 调用的python和pip会下载到对应的虚拟环境中
# https://pypi.org/ python仓库
# pip install fabric==1.14.0 #1版本
# pip install fabric2 #2版本
# IDEA新增pythonSDK 目录选择 /Users/admin/python3_venv/bin/python3.9
# 虚拟环境下载的包会在/Users/admin/python3_venv/lib/python3.9/site-packages
# 关闭窗口则会退出虚拟环境 在idea中下载以SDK为准
7.总计
无论是 Fabric 1 版本还是 2 版本,都有其适用的场景和价值。理解它们之间的区别,能帮助我们在不同的项目中做出合适的选择,充分发挥 Fabric 在自动化和系统管理方面的强大功能。随着技术的不断进步,我们可以根据需求灵活运用,以实现更高效的开发和管理。
希望这篇文章能为你深入了解 Python Fabric 的不同版本提供有益的参考。
8.自己用的,备忘一下
- 配置文件
dev_path.conf
[member]
localPath=/Users/admin/apple/java-work/test/test-service
dirName=member
sshPath=~/test-service
- python代码 基于1版本的(因为先入为主),支持vue代码发布
#fab start agent 走这个
if env.get('env') == 'agent':
env.host_string = 'ubuntu@xx.xx.xx.xx'
env.key_filename = "/Users/admin/.ssh/id_rsa"
else:
# 其他走这个
env.hosts = ['192.168.168.131']
env.port = 22
env.user = 'root'
env.password = '123456'
class Docker:
def __init__(self, configPath):
self.configPath = configPath
self.con = ConfigParser.SafeConfigParser()
self.con.read(self.configPath)
def packet(self):
localPath = self.con.get(self.serviceName, "localPath")
print('#########################################')
print("Beginning Pack", localPath)
if self.isPacket == 1:
with lcd(localPath):
local("ls")
if self.con.has_option(self.serviceName, "web"):
# web打包 npm run build:prod
a = local("npm run build:prod", capture=True)
else:
# mvn clean install
a = local("mvn clean install -Dmaven.test.skip=true", capture=True)
if "BUILD FAILURE" in a:
print(a)
print("BUILD Failed")
return
print(u"BUILD SUCCESS")
return "success"
def commitFileToServer(self):
'''
上传本地文件到服务器
'''
localPath = self.con.get(self.serviceName, "localPath")
sshPath = self.con.get(self.serviceName, "sshPath")
dirName = self.con.get(self.serviceName, "dirName")
liunxPath = sshPath + "/" + dirName
run("mkdir -p %s" % liunxPath)
if self.con.has_option(self.serviceName, "web"):
# 取配置路径下的dist目录
with lcd(localPath + "/dist"):
local("ls")
with cd(liunxPath):
run("rm -fr ./*")
put("*", liunxPath)
try:
with lcd(localPath + "/deploy/" + self.active):
local("ls")
with cd(sshPath):
run("rm -fr *.yaml")
put("*", sshPath)
except:
print("无deploy配置,跳过")
print(u"web资源上传完成!")
else:
# 服务端
with cd(liunxPath):
with lcd(localPath + "/" + dirName + "/target"):
local("ls")
run("pwd")
run("mkdir -p logs")
# .jar结尾的复制到服务器
run("rm -fr *.sh *.jar *.yaml ./lib Dockerfile")
put("*.jar", liunxPath)
try:
put("./lib", liunxPath)
except:
print("无lib目录,跳过")
# 项目下的deploy下复制docker相关的
with lcd(localPath + "/" + "deploy" + "/" + self.active):
put("Dockerfile", liunxPath)
# sed -i -e 's/${app_name}/advert/g' -e 's/${active}/dev/g' Dockerfile
# 改为docker-compose传参
run("sed -i -e 's/${app_name}/%s/g' -e 's/${active}/%s/g' Dockerfile" % (dirName, self.active))
put(dirName + "/*", liunxPath)
put("build.sh", liunxPath)
run("dos2unix build.sh")
run("chmod 777 *.yaml")
run("chmod 777 *.sh")
print(u"jar包上传完成!")
# 停止 && 启动 容器
def sshCmd(self):
sshPath = self.con.get(self.serviceName, "sshPath")
dirName = self.con.get(self.serviceName, "dirName")
if self.con.has_option(self.serviceName, "web"):
with cd(sshPath):
run("sudo docker-compose stop")
run("sudo docker-compose up -d")
time.sleep(2)
run("sudo docker-compose logs -f")
else:
with cd(sshPath + "/" + dirName):
run("./build.sh start %s" % self.serviceName)
time.sleep(2)
run("sudo docker-compose up -d")
time.sleep(2)
run("sudo docker-compose logs -f --tail 100")
def selectPath(self):
arr = {}
redaFile = open(self.configPath, "r")
inputSteam = redaFile.readlines()
index = 1
for i in range(inputSteam.__len__()):
lineStr = inputSteam[i]
if lineStr.startswith("["):
line = re.sub(r'\[|\]\n', "", lineStr)
arr[str(index)] = line
index += 1
redaFile.close()
print(u"请选择发布的项目:")
print(arr)
path = raw_input()
print(u"是否需要打包:")
isPacket = input("1.yes 2.no:")
active = {"1": "dev", "2": "test", "3": "prod"}
print(u"环境选择:")
print(active)
a = input()
if arr[path] != None:
self.serviceName = arr[path]
self.isPacket = isPacket
self.active = active[str(a)]
return
else:
print(u"输入选项不存在,请重新选择")
self.createPath()
def start():
path = "./path.conf"
if env.get('env') != None:
path = "./%s_path.conf" % (env.get('env').split("_")[0])
print(u"读取配置 %s" % path)
docker = Docker(path)
print(u"选择项目\n")
docker.selectPath()
print(u"打包")
docker.packet()
print(u"文件传输")
docker.commitFileToServer()
print(u"执行liunx命令")
docker.sshCmd()
并不是每个人通用,谨慎copy
以上就是本文的全部内容了!
上一篇:随手记录第十二话 – JDK8-21版本的新增特性记录(Lambda,var,switch,instanceof,record,virtual虚拟线程等)
下一篇:随手记录第十四话 – 在 Spring Boot 3.2.3 中使用 springdoc-openapi-starter-webmvc-ui
烹羊宰牛且为乐,会须一饮三百杯