书籍名称:[Python3 自动化软件发布系统-Django2 实战]
当生成软件包,并放入软件仓库以后,就可以通过 SaltStack 来进行部署了。
Salt Master 及 Minion 的安装
在我们的实验环境中,Salt Master 的 IP地址为 192.168.200.30,具体的安装教程此处略过,可以参考网上的教程安装
通过 Salt Master 远程执行脚本命令
SaltStack 的远程命令执行分为两个:cmd.run
和 cmd.script
。这两个命令的执行机制还是有一些区别的。cmd.run 是将命令直接在 salt minion 上执行,适合日常运维的小操作。cmd.script 则是将指定的脚本拷贝到 Salt Minion 之后,再在 salt minion 上执行,适合比较复杂的自动化运维操作。而 salt.script 指定的脚本需要放在 salt://
协议的目录下,或是 http://
,ftp://
等协议的目录下。这两个命令,在我们的自动化部署系统中都需要。依据实现软件推荐的不同方案,还有可能使用到 cp.get_file,cp.get_dir,cp.get_url 命令,这三个远程推送文件和目录的命令,会将我们需要的文件推送到指定的 Minion 上去。
cmd.run
在 Salt Master 上执行命令:
1 | salt '192.168.200.17' cmd.run 'ifconfig' |
输出如下:
1 | 192.168.200.17: |
可以看出,ifconfig 命令实在指定的 Salt Minion (192.168.200.17) 上执行后,返回给 Salt Minion 显示的。
cmd.script
先建立一个简单的 test_script.sh 脚本文件,内容如下:
1
2
3
4
5
ifconfig
ls /
mkdir -p /tmp/test/dirSalt Master 上有一个默认的
salt://
协议的文件服务器,路径指向为/srv/salt/
目录。现在我们先建立一个/srv/salt/script/
目录,将test_script.sh
文件拷贝到此目录下,然后运行如下命令:1
salt '192.168.200.17' cmd.script salt://scripts/test_script.sh
输出如下:
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
27192.168.200.17:
----------
pid:
10782
retcode:
0
stderr:
stdout:
ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.200.17 netmask 255.255.255.0 broadcast 192.168.200.255
inet6 fe80::250:56ff:fe21:abb1 prefixlen 64 scopeid 0x20<link>
ether 00:50:56:21:ab:b1 txqueuelen 1000 (Ethernet)
RX packets 114913 bytes 67232095 (64.1 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 40657 bytes 3531742 (3.3 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
...
bin
...
sbin
srv
sys
tmp
usr
var
可以看到,这三个命令,一样在 Minion 得到了执行,并得到了输出。这种方式的执行,可以让我们更方便的执行自动化程序高的脚本。
通过 HTTP 协议执行 cmd.script
执行脚本,不但可以像上一步一样通过 salt:// 协议,也可以通过 http:// 协议执行,这样做的好处是可以将执行脚本与 Salt 解耦,只依赖于 Salt 的命令。
将上一步的 test_script.sh 文件拷贝到 Nginx 服务器上的 /data/wwwroot/packages.59izt.com/packages/scripts/ 目录下,然后执行如下命令:
1 | salt '192.168.200.15' cmd.script http://192.168.200.17/packages/scripts/test_script.sh |
可以看打到,输出和上一步是一样的,这里不再展示了。
cp.get_file 和 cp.get_dir
除了远程执行脚本命令,有时候也有将 Salt Master 上的文件分发到 Minion 上的需求,这时 cp.get_file 和 cp.get_dir 就可以派上用场了。这两个命令使用的都是 salt:// 协议。
为了测试,先在 /srv/salt/ 下建立 files 目录,然后在 files 目录建立一个 test_file.txt 文件。然后运行如下命令:
1
salt '192.168.200.15' cp.get_file salt://files/test_file.txt /tmp
输出如下:
1
2192.168.200.15:
/tmp/test_file.txt然后进入 Salt Minion(192.168.200.15) 的 /tmp 目录下,可以看到 test_file.txt 文件已经存在
接下来测试 cp.get_dir 功能,运行如下命令:
1
salt '192.168.200.15' cp.get_dir salt://files /tmp
输出如下:
1
2192.168.200.15:
- /tmp/files/test_file.txt
可以看到,这个 files 目录及文件,已经分发到目标 Minion 的 /tmp 目录下去了。
Salt-API 配置
下面,通过新的安装配置,来启用 Salt-API 方式的远程命令调用。
Salt-API 的安装
运行如下命令安装:
1 | yum install -y salt-api |
Salt-API 的运行,依赖于 Python-Cherrypy 模块,所以这个模块也会一并安装。
生成自签名证书
为了安全,Salt-API 是以 https 的方式运行,所以应生成证书,随后配置在相关文件中。
进入
/etc/pki/tls/certs
目录,运行如下命令,一路回车,即生成一个自签名证书。(期间,需要生成一个pass phrase
加密短语,自行保存):1
make testcert
如果正常,输出如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25umask 77 ; \
/usr/bin/openssl genrsa -aes128 2048 > /etc/pki/tls/private/localhost.key
Generating RSA private key, 2048 bit long modulus
..............................................................................................+++
...............+++
e is 65537 (0x10001)
Enter pass phrase:
Verifying - Enter pass phrase:
umask 77 ; \
/usr/bin/openssl req -utf8 -new -key /etc/pki/tls/private/localhost.key -x509 -days 365 -out /etc/pki/tls/certs/localhost.crt
Enter pass phrase for /etc/pki/tls/private/localhost.key:
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:
State or Province Name (full name) []:
Locality Name (eg, city) [Default City]:
Organization Name (eg, company) [Default Company Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (eg, your name or your server's hostname) []:
Email Address []:这时,即在
/etc/pki/tls/certs/
目录下生成了一个包含 RSA 公钥的localhost.crt
证书,同时在/etc/pki/tls/private/
目录下生成了对应的 RSA 算法的localhost.key
私钥。接着,进入
/etc/pki/tls/private/
目录,运行如下命令,在相同目录下,会生成localhost.key
对应的无密码私钥 —localhost_nopass.key
,这一过程,需要上一步输入的pass phrase
。1
openssl rsa -in localhost.key -out localhost_nopass.key
记住
/etc/pki/tls/certs/localhost.crt
和/etc/pki/tls/private/localhost_nopass.key
文件的位置,因为在 Salt-API 配置时,需要这两个文件作为 ssl 的配置。
创建 Salt-API 用户及密码
接下来需要创建一个用户,用于 Salt-API 的认证,以及进行 SaltStack 平台的命令调用前的认证。对于这一步和上一步的配置,两者之间是有区别的,上一步,是为了 Salt-API 服务能启动运行而进行的证书配置,不涉及用户;这一步,是为了让我们以后的自动化软件发布系统能远程调用 Salt-API 服务而建立的认证用户。切记!
输入以下命令,建立一个 Salt-API 用户:
1
useradd -M -s /sbin/nologin salt-api-client
输入以下命令,设置 Salt-API-Client 用户的密码(请记住这个密码,在以后自动发布系统会用到)
1
passwd salt-api-client
配置 Salt-API
在生成了证书及用户之后,就可以进行 Salt-API 服务的配置了。
Salt-API 的配置包括两个文件,
- 第一个是 api.conf 文件,用来配置证书文件,启动 Salt-API 服务;
- 第二个是 eauth.conf文件,用来配置 Salt-API 的远程认证用户的权限。
这两个文件位于 /etc/salt/master.d/ 这个目录中,如果这个目录不存在,请提前创建好。
/etc/salt/master.d/api.conf 内容如下:
1
2
3
4rest_cherrypy:
port: 8899
ssl_crt: /etc/pki/tls/certs/localhost.crt
ssl_key: /etc/pki/tls/private/localhost_nopass.key可以看到,ssl_crt 和 ssl_key 就是前面几步生成的那两个文件;
/etc/salt/master.d/eauth.conf 内容如下:
1
2
3
4
5
6
7external_auth:
pam:
salt-api-client:
- .*
- '@wheel'
- '@runner'
- '@jobs'可以看到,我们为 Salt-API-Client 设置了很大的权限,在生产需要时,可以调整权限。
经过上面的配置后,重启 Salt-API 和 Salt-Master,让配置生效:
1
2systemctl restart salt-api
systemctl restart salt-master
注意这个先后顺序,调整 Salt-API 的配置后,必须重启 Master 才会生效。
Salt-API 测试
在上面配置完成并重启服务后,先手工测试一下 Salt-API 的使用方法,了解一下流程,然后再对它进行封装。
首先,获取认证的 Token,命令如下:(这里我们使用了已有的 Salt-API-Client 的用户密码)
1
2
3
4curl -sSk https://192.168.200.30:8899/login \
> -H 'Accept: application/x-yaml' \
> -d username=salt-api-client \
> -d password='xxxxxxxx' -d eauth=pam输出如下:
1
2
3
4
5
6
7
8
9
10
11return:
- eauth: pam
expire: 1605232163.439036
perms:
- .*
- '@wheel'
- '@runner'
- '@jobs'
start: 1605188963.439035
token: 1169cd75b898ac0854e5069661fd2b5afa418840
user: salt-api-client接下来的测试,只要带上 Token,就可以远程执行 SaltStack 的相关命令了,如下:
1
2
3
4
5
6
7curl -k https://192.168.200.30:8899/ \
> -H "Accept: application/x-yaml" \
> -H "X-Auth-Token: 1169cd75b898ac0854e5069661fd2b5afa418840" \
> -d client='local' \
> -d tgt='192.168.200.15' \
> -d fun='cmd.run' \
> -d arg='ifconfig'输出如下:
1
2
3
4
5
6
7
8
9
10
11
12
13return:
- 192.168.200.15: "ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500\n inet
192.168.200.15 netmask 255.255.255.0 broadcast 192.168.200.255\n inet6
fe80::250:56ff:fe2f:9b32 prefixlen 64 scopeid 0x20<link>\n ether 00:50:56:2f:9b:32
\ txqueuelen 1000 (Ethernet)\n RX packets 83917 bytes 13417735 (12.7
MiB)\n RX errors 0 dropped 0 overruns 0 frame 0\n TX packets
100808 bytes 165652585 (157.9 MiB)\n TX errors 0 dropped 0 overruns 0
\ carrier 0 collisions 0\n\nlo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536\n inet
127.0.0.1 netmask 255.0.0.0\n inet6 ::1 prefixlen 128 scopeid 0x10<host>\n
\ loop txqueuelen 1000 (Local Loopback)\n RX packets 15231 bytes
1340760 (1.2 MiB)\n RX errors 0 dropped 0 overruns 0 frame 0\n TX
packets 15231 bytes 1340760 (1.2 MiB)\n TX errors 0 dropped 0 overruns
0 carrier 0 collisions 0"
可以看到,通过 Salt-API 执行 cmd.run 命令,返回时正确的。其他模块的执行与此类似,不再重复演示;
Salt-API 命令封装
封装的 py 文件如下(salt.py),如果现在看不太懂,没有关系,先要有主观的代码流程印象,等到后面的章节具体实现软件自动发布时,再来细讲每个封装 API 的用法。
1 | #!/usr/bin/env python |
在上面的代码中,先将 SaltStack 的操作封装成了一个类,初始化时,需要提供 Salt Master 的主机 IP,端口,认证用户和密码信息;然后再内部,实现了常用的 cmd_run
,cmd_script
,cp_file
,cp_dir
。最后提供了一个测试的 demo()
函数
Salt-API 封装后测试
在 Python 环境,执行上面的文件,demo() 的输出如下所示
1 | % python salt.py |
由于是 HTTP 返回,所以格式是以 \n 作为换行输出的,格式不是很直观,但还是可以看出,所有命令都已执行完成;
远程拉取软件,起停服务的脚本
经过前面的讲解,对软件手工发布基本的工作流,再次梳理一下:
- 先在 GitHub 上建立项目目录;
- 将本地编写的代码上传到 GitLab;
- 在 Jenkins 拉取 GitLab 里的醒目代码进行编译,生成软件包,推送到软件 Nginx 仓库;
- 在 Salt Master 上执行 SaltStack 命令,将脚本推送到指定的 Salt Minion 服务器运行;
- 在 Salt Minion 的服务器执行参数脚本时,拉取 Nginx 上的软件包,并实现服务启停。
有了以上工作流,而且前面 4 步已经完成,现在用一个极简的 shell 脚本来实现第 5 步。脚本内容如下:
1 |
|
在上面的脚本中,通过传递给脚本参数,主要实现了软件包的拉取,部署,服务的启停。
我们将这个脚本保存为 ZEP-BACKEND-JAVA.sh 文件,将其放到 Salt Minion 服务器上,然后执行如下几个命令,验证脚本本身的逻辑是正确的。
1 | # 获取软件包 |
测试
将上面的脚本上传至 Nginx 服务器的 /data/wwwroot/default/scripts/ 目录下,然后就能以 http://192.168.200.17/scripts/ZEP-BACKEND-JAVA.sh
的方式访问这个文件。
依据前面获取的知识,通过 Salt-API 来操作脚本的测试步骤如下:
Salt-API 命令封装后的 demo() 函数改写如下:
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
28def demo():
sapi = SaltStack(host="192.168.200.30",
port="8899",
username="salt-api-client",
password="shkzc123!@#",
secure=True)
# 为了语义明晰,使用列表
attach_arg_list = [None] * 9
attach_arg_list[0] = "ZEP-BACKEND-JAVA"
attach_arg_list[1] = "test"
attach_arg_list[2] = "2020-1113-1010-39"
attach_arg_list[3] = "javademo-1.0.0-SNAPSHOT.jar"
attach_arg_list[4] = "18080"
attach_arg_list[5] = sys.argv[1] if len(sys.argv) == 2 else "status"
attach_arg_list[6] = "tot"
attach_arg_list[7] = "http://192.168.200.17/packages"
attach_arg_list[8] = "javademo-1.0.0.tar.gz"
# cmd_script 后面附加参数为字符串,所以需要进行转换
attach_arg = ' '.join(attach_arg_list)
result = sapi.cmd_script(tgt='192.168.200.15', arg=["http://192.168.200.17/scripts/ZEP-BACKEND-JAVA.sh", attach_arg])
print(result['return'][0]['192.168.200.15']['stdout'])
if __name__ == '__main__':
demo在接下来的测试中,需要分别测试 stop, prepare, deploy, start, status 几个动作,看看是否可以通过 Salt-API 收到和本地执行一样的效果。
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# 拉取软件包
% python salt.py prepare
/var/ops/ZEP-BACKEND-JAVA/2020-1113-1010-39/javademo-1.0.0.tar.gz found.
# 部署应用
% python salt.py deploy
javademo-1.0.0-SNAPSHOT.jar
config/
config/application-prd.properties
config/application-test.properties
ZEP-BACKEND-JAVA deploy success.
# 停止应用
% python salt.py stop
Stopping ZEP-BACKEND-JAVA: ZEP-BACKEND-JAVA is not running!!
# 启动应用
% python salt.py start
Starting ZEP-BACKEND-JAVA: start success in 3 seconds with (pid: 1575)
# 查看应用状态
% python salt.py status
Project: ZEP-BACKEND-JAVA (pid 1575) is success running...
# 停止应用
% python salt.py stop
Stopping ZEP-BACKEND-JAVA: Stop success..