python
主页 > 脚本 > python >

使用python编写一个自动化部署工具

2024-12-11 | 佚名 | 点击:

效果

起因

现在springboot项目的自动化部署已经非常普遍,有用Jenkins的,有用git钩子函数的,有用docker的...等等。这段时间在玩python,想着用python实现自动化部署,即能锻炼下编码能力,又方便运维。于是开始着手写了一个exe程序,可直接在任何windows电脑上运行(不具备python环境的windows电脑也可以运行)。有兴趣的小伙伴可以跟着代码一起练一练噢,写的详细一点,对python新手也很友好。

实现步骤

开发准备

步骤

1. 导入依赖

新建一个py文件,可以把它命名为 deployment.py(名字随意哈,什么名儿都可以),然后把下面的库导入语句copy到此py文件中

1

2

3

4

5

6

7

8

import os #用于-提取文件名 

import re #用于-正则表达式 

import time #用于-线程休眠 

import paramiko #用于-远程执行linux命令 

from alive_progress import alive_bar #用于-进度条工具类 

from cryptography.fernet import Fernet #用于-加解密代码 

import base64 #用于-加解密代码 

import hashlib #用于-加解密代码

在导入依赖的时候,可能有些依赖咱们的电脑上之前没下载过,不要紧,只需要在pycharm中按 alt+enter就可以自动导入了,PyCharm跟Idea的快捷键一模一样,可以按Idea的习惯使用。而且在python中还不用配置maven或pom文件,非常方便。

2. 输入校验

部署毕竟是件严谨的事情,我们增加个部署密钥校验,我的这个部署密钥承担了以下的功能

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

import os #用于-提取文件名 

import re #用于-正则表达式 

import time #用于-线程休眠 

import paramiko #用于-远程执行linux命令 

from alive_progress import alive_bar #用于-进度条工具类 

from cryptography.fernet import Fernet #用于-加解密代码 

import base64 #用于-加解密代码 

import hashlib #用于-加解密代码 

 

#检查密钥格式

def check_deploy_sign(deploy_site): 

    #确保密钥只能是以下4个之一才能继续往下操作,否则无限循环输入 或 退出程序

    if deploy_site != 'pro-main' and deploy_site != 'pro-manage' and deploy_site != 'test-main' and deploy_site != 'test-manage':

        #校验失败,一直校验

        new_deploy_site = input("错误:请填写部署密钥:") 

        check_deploy_sign(new_deploy_site) 

     #校验成功,退出

     return deploy_site 

   

   

try: 

    deploy_sign = input("提示:请填写部署密钥:") 

    deploy_sign = check_deploy_sign(deploy_sign) 

 

    # 部署环境  pro代表生成环境,test代表测试环境

    deploy_server = deploy_sign.split('-')[0] 

    # 部署模块或项目 manage代表manage模块,main代表main模块,

    deploy_site = deploy_sign.split('-')[1] 

    # 打包时的包名,三目运算符

    package_name = 'production' if deploy_server == 'pro' else 'staging' 

 

except Exception as e: 

    print(f"异常: {str(e)}") 

上面的代码中 增加了全局的异常处理,类似Java的try catch,也定义了一些基本的变量。密钥是一串由短线连接的字符串,短线前的代码用以区分环境,短线后的代码用以区分模块或项目。另外上面代码中的package_name是打包时的包名(即profiles.profile.id),一般配置在springboot项目pom文件中的编辑模块,类似下面这样:

3. 连接linux服务器

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

import os #用于-提取文件名 

import re #用于-正则表达式 

import time #用于-线程休眠 

import paramiko #用于-远程执行linux命令 

from alive_progress import alive_bar #用于-进度条工具类 

from cryptography.fernet import Fernet #用于-加解密代码 

import base64 #用于-加解密代码 

import hashlib #用于-加解密代码 

 

#检查密钥格式

def check_deploy_sign(deploy_site): 

    #确保密钥只能是以下4个之一才能继续往下操作,否则无限循环输入 或 退出程序

    if deploy_site != 'pro-main' and deploy_site != 'pro-manage' and deploy_site != 'test-main' and deploy_site != 'test-manage':

        #校验失败,一直校验

        new_deploy_site = input("错误:请填写部署密钥:") 

        check_deploy_sign(new_deploy_site) 

     #校验成功,退出

     return deploy_site 

      

# 连接服务器 

def connect_service(deploy_server):

    server_password = '' 

    server_host = '' 

    sign = hashlib.sha256(deploy_server.encode()).digest() 

    sign = base64.urlsafe_b64encode(sign) 

    if deploy_server == 'pro': 

        server_password = decrypt_str(sign, service_password_pro) 

        server_host = decrypt_str(sign, service_host_pro) 

    elif deploy_server == 'test': 

        server_password = decrypt_str(sign, service_password_test) 

        server_host = decrypt_str(sign, service_host_test) 

    else: 

        raise Exception('失败:部署服务器标识有误') 

    # 连接远程服务器 

    ssh = paramiko.SSHClient() 

    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 

    ssh.connect(server_host, username='root', password=server_password) 

    return ssh 

   

# 解密密码 

def decrypt_str(key, encrypted_password): 

    f = Fernet(key) 

    decrypted_password = f.decrypt(encrypted_password).decode() 

    return decrypted_password

 

try: 

    # 服务器环境信息的加密字符串,包含各服务器的 ip和密码 

    service_password_pro = 'asdatrgsd==' 

    service_password_test = 'sgherfhdf==' 

    service_host_pro = 'jfhgfvdcfdtr==' 

    service_host_test = 'jutyrbfvret=='

     

     

    deploy_sign = input("提示:请填写部署密钥:") 

    deploy_sign = check_deploy_sign(deploy_sign) 

 

    # 部署环境  pro代表生成环境,test代表测试环境

    deploy_server = deploy_sign.split('-')[0] 

    # 部署模块或项目 manage代表manage模块,main代表main模块,

    deploy_site = deploy_sign.split('-')[1] 

    # 打包时的包名,三目运算符

    package_name = 'production' if deploy_server == 'pro' else 'staging' 

    #进度条

    with alive_bar(7, force_tty=True, title="进度") as bar: 

        # 连接服务器 

        ssh = connect_service(deploy_server) 

        bar(0.1) 

        print("完成-服务器连接成功") 

        time.sleep(0.5)

except Exception as e: 

    print(f"异常: {str(e)}") 

在连接服务器之前,我们加个进度条显示,方便查看部署到哪一步了,要点讲解:

4. 部署工具主逻辑

代码要点讲解: 下面的代码是工程的全部代码,主要包含了以下逻辑

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

import os #用于-提取文件名

import re #用于-正则表达式

import time #用于-线程休眠

import paramiko #用于-远程执行linux命令

from alive_progress import alive_bar #用于-进度条工具类

from cryptography.fernet import Fernet #用于-加解密代码

import base64 #用于-加解密代码

import hashlib #用于-加解密代码

 

def check_deploy_sign(deploy_site):

    if deploy_site != 'pro-main' and deploy_site != 'pro-manage' and deploy_site != 'test-main' and deploy_site != 'test-manage':

        new_deploy_site = input("错误:请填写部署密钥:")

        check_deploy_sign(new_deploy_site)

    return deploy_site

 

 

# 解密密码

def decrypt_str(key, encrypted_password):

    f = Fernet(key)

    decrypted_password = f.decrypt(encrypted_password).decode()

    return decrypted_password

 

# 执行远程命令

def execute_command(ssh, command):

    stdin, stdout, stderr = ssh.exec_command(command)

    stdout.channel.recv_exit_status()  # 等待命令执行完毕

    output = stdout.read().decode('utf-8')

    time.sleep(0.5)

    return output

 

# 执行远程命令

def execute_command_shell(shell, command, endword):

    shell.send(command + '\n')

    output = ''

    while True:

        while shell.recv_ready():

            recv = shell.recv(1024).decode('utf-8', errors='ignore')

            output += recv

        if endword == '# ':

            if output.endswith('$ ') or output.endswith('# '):

                break

        elif endword in output:

            break

    time.sleep(0.5)

    return output

 

# 连接服务器

def connect_service(deploy_server): 

    server_password = ''

    server_host = ''

    sign = hashlib.sha256(deploy_server.encode()).digest()

    sign = base64.urlsafe_b64encode(sign)

    if deploy_server == 'pro':

        server_password = decrypt_str(sign, service_password_pro)

        server_host = decrypt_str(sign, service_host_pro)

    elif deploy_server == 'test':

        server_password = decrypt_str(sign, service_password_test)

        server_host = decrypt_str(sign, service_host_test)

    else:

        raise Exception('失败:部署服务器标识有误')

    # 连接远程服务器

    ssh = paramiko.SSHClient()

    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

    ssh.connect(server_host, username='root', password=server_password)

    return ssh

 

# 查询进程

def query_process(ssh, process_name): 

    process_id = ''

    command = f"ps -ef | grep {process_name}-system-master. | grep -v grep"

    process_output = execute_command(ssh, command)

    if process_output:

        # 提取进程ID并杀死进程

        process_id = process_output.split("    ")[1]

    return process_id

 

# 杀掉进程

def kill_process(ssh, process_id): 

    command = f"kill -9 {process_id}"

    output = execute_command(ssh, command)

    return output

 

# 寻找编译好的jar包

def find_jarname(output):

    match = re.search(r"Building jar: .+?/(.+?.jar)", output)

    if match:

        jar_filepath = match.group(1)

        jar_filename = os.path.basename(jar_filepath)

        return jar_filename

    else:

        raise Exception('失败:jar未找到')

 

 

try:

    service_password_pro = 'asdatrgsd=='

    service_password_test = 'sgherfhdf=='

    service_host_pro = 'jfhgfvdcfdtr=='

    service_host_test = 'jutyrbfvret=='

 

 

    deploy_sign = input("提示:请填写部署密钥:")

    deploy_sign = check_deploy_sign(deploy_sign)

 

    # 部署环境

    deploy_server = deploy_sign.split('-')[0]

    # 部署模块

    deploy_site = deploy_sign.split('-')[1]

    # 部署环境对应服务正式的名字

    package_name = 'production' if deploy_server == 'pro' else 'staging'

 

    with alive_bar(7, force_tty=True, title="进度") as bar:

        # 连接服务器

        ssh = connect_service(deploy_server)

        bar(0.1)

        print("完成-服务器连接成功")

        time.sleep(0.5)

 

        # 拉取代码

        shell = ssh.invoke_shell()

        execute_command_shell(shell, 'cd /root/build/x-system','#')

        execute_command_shell(shell, 'git pull','#')

        bar(0.2)

        print("完成-git代码拉取成功")

 

        # 编译代码

        execute_command_shell(shell, 'cd /root/build/x-system/modules', '#')

        execute_command_shell(shell, 'mvn clean install', 'BUILD SUCCESS')

        bar(0.4)

        print("完成-公共模块编译成功")

 

        # 打包代码

        execute_command_shell(shell, 'cd /root/build/x-system/webapps/' + deploy_site + '-system ', '#')

        output=execute_command_shell(shell, 'mvn clean package -P ' + package_name, 'BUILD SUCCESS')

 

        bar(0.6)

        print("完成-" + deploy_site + "模块打包成功")

 

        # 查询进程,如果查不到 就不执行kill命令

        pid = query_process(ssh, deploy_site)

        if pid != '':

            kill_process(ssh, pid)

            print("完成-旧程序进程已被杀掉,等待启动")

        else:

            print("完成-旧程序PID未找到,直接启动")

        bar(0.7)

 

 

        # 启动jar

        jar_name = find_jarname(output)

        execute_command_shell(shell, 'cd /root/build/x-system/webapps/' + deploy_site + '-system/target', '#')

        execute_command_shell(shell, 'nohup java -jar ' + jar_name + '>log.out  2>&1 & ', '#')

        bar(0.8)

        print("完成-程序正在启动中...")

 

 

        # 查看日志确认服务启动成功

        log_path = '/var/log/x-system/' + deploy_site + '-system' if deploy_server == 'pro' else '/var/log/x-system/' + deploy_site + '-system-staging'

        execute_command_shell(shell, 'cd '+log_path, '#')

        execute_command_shell(shell, 'tail -200f '+deploy_site+'-system-info.log', 'TomcatWebServer:206 - Tomcat started on port(s)')

        bar(1)

        print("完成-程序启动成功")

except Exception as e:

    print(f"异常: {str(e)}")

 

finally:

    time.sleep(10)

    # 关闭连接

    shell.close()

    ssh.close()

代码用try catch finally包裹,如果过程中出现任何异常,都输出错误原因 一些提示:

5.打包

打包命令:

1

pyinstaller --onefile --icon 太空人.ico --add-data ".\grapheme_break_property.json;grapheme\data"  --name 远程部署 deployment.py

打包命令中的几个参数解释一下:

原文链接:
相关文章
最新更新