Fabric is a great tool for automated operations and deployment projects developed in Python, allowing automated interactions with remote servers over SSH, such as transferring local files to the server and executing shell commands on the server.

Here is an example of automating a Django project

?
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
# -*- coding: utf-8 -*-
The file name should be saved as fabfile.py
from
__future__
import
unicode_literals
from
fabric.api
import
*
User name and host name:
env.user
=
'root'
# If not set, Fabric will prompt you to log in when required
env.password
=
'youpassword'
# If there are multiple hosts, fabrics are automatically deployed one by one
env.hosts
=
[
'www.example.com'
]
TAR_FILE_NAME
=
'deploy.tar.gz'
def
pack():

"" "

Define a pack task and tar it

:return:

"" "

tar_files
=
[
'*.py'
.
'static/*'
.
'templates/*'
.
'vue_app/'
.
'*/*.py'
.
'requirements.txt'
]

exclude_files
=
[
'fabfile.py'
.
'deploy/*'
.
'*.tar.gz'
.
'.DS_Store'
.
'*/.DS_Store'
.

'*/.*.py'
.
'__pycache__/*'
]

exclude_files
=
[
'--exclude=\'%s\''
%
t
for
t
in
exclude_files]

local(
'rm -f %s'
%
TAR_FILE_NAME)

local(
'tar -czvf %s %s %s'
%
(TAR_FILE_NAME,
' '
.join(exclude_files),
' '
.join(tar_files)))

print
(
'Create a package file in the current directory: %s'
%
TAR_FILE_NAME)
def
deploy():

"" "

Define a deployment task

:return:

"" "

# Pack first

pack()

Temporary files on the remote server

remote_tmp_tar
=
'/tmp/%s'
%
TAR_FILE_NAME

run(
'rm -f %s'
%
remote_tmp_tar)

# upload tar file to remote server, local_path, remote_path

put(TAR_FILE_NAME, remote_tmp_tar)

# decompression

remote_dist_base_dir
=
'/home/python/django_app'

If it doesn't exist, create a folder

run(
'mkdir -p %s'
%
remote_dist_dir)

The CD command switches the working directory of the remote host to the specified directory

with cd(remote_dist_dir):

print
(
'Unzip file to directory: %s'
%
remote_dist_dir)

run(
'tar -xzvf %s'
%
remote_tmp_tar)

print
(
'Install dependency packages in requirements.txt'
)

# I'm using python3 to develop

run(
'pip3 install -r requirements.txt'
)

remote_settings_file
=
'%s/django_app/settings.py'
%
remote_dist_dir

settings_file
=
'deploy/settings.py'
%
name

print
(
'Upload settings.py file %s'
%
settings_file)

put(settings_file, remote_settings_file)

nginx_file
=
'deploy/django_app.conf'

remote_nginx_file
=
'/etc/nginx/conf.d/django_app.conf'

print
(
'Upload nginx configuration file %s'
%
nginx_file)

put(nginx_file, remote_nginx_file)

Upload the Supervisor configuration file in the deploy subdirectory of the current directory to the server

supervisor_file
=
'deploy/django_app.ini'

remote_supervisor_file
=
'/etc/supervisord.d/django_app.ini'

print
(
'Upload supervisor configuration file %s'
%
supervisor_file)

put(supervisor_file, remote_supervisor_file)

Reload the nginx configuration file

run(
'nginx -s reload'
)

run(
'nginx -t'
)

Delete the local package file

local(
'rm -f %s'
%
TAR_FILE_NAME)

Load the latest configuration file, stop the old process and start all processes with the new configuration

run(
'supervisorctl reload'
)

If you run restart all, start or stop fabric, an error message is displayed and the operation is aborted

The supervisor has been restarted

# run('supervisorctl restart all')

Executing the Pack Task

fab pack

Executing a Deploy Task

fab deploy

One more example of an automated deployment of code using Fabric

?
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
#coding=utf-8
from
fabric.api
import
local, abort, settings, env, cd, run
from
fabric.colors
import
*
from
fabric.contrib.console
import
confirm
env.hosts
=
[
"[email protected]. * * * *"
]
env.password
=
"X x x x x"
def
get_git_status():

git_status_result
=
local(
"git status"
, capture
=
True
)

if
"No files to submit, clean workspace."
not
in
git_status_result:

print
red(
"**** current branch still has files not submitted"
)

print
git_status_result

abort(
"**** has been terminated"
)
def
local_unit_test():

with settings(warn_only
=
True
) :

test_result
=
local(
"python manage.py test"
)

if
test_result.failed:

print
test_result

if
not
confirm(red(
"**** unit test failed. Do you want to continue?"
)) :

abort(
"**** has been terminated"
)
def
server_unit_test():

with settings(warn_only
=
True
) :

test_result
=
run(
"python manage.py test"
)

if
test_result.failed:

print
test_result

if
not
confirm(red(
"**** unit test failed. Do you want to continue?"
)) :

abort(
"**** has been terminated"
)
def
upload_code():

local(
"git push origin dev"
)

print
green(
"**** code uploaded successfully"
)
def
deploy_at_server():

print
green(
"**** SSH to the server to do the following"
)

with cd(
"/ var/WWW/x x x x x x"
) :

#print run("pwd")

print
green(
"**** will download the code at remote repository"
)

run(
"git checkout dev"
)

get_git_status()

run(
"git pull origin dev"
)

print
green(
"**** will run unit tests on the server"
)

server_unit_test()

run(
"service apache2 restart"
, pty
=
False
)

print
green(
"**** Apache2 restarted successfully"
)

print
green(
"******** code deployed successfully ********"
)
def
deploy():

get_git_status()

local(
"git checkout dev"
, capture
=
False
)

print
green(
"**** switch to dev branch"
)

get_git_status()

print
green(
"**** will start running unit tests"
)

local_unit_test()

print
green(
"**** unit tests complete, start uploading code"
)

upload_code()

deploy_at_server()

Fabric can consolidate commands for automated deployment or multi-machine operations into a script, reducing manual operations. Above is today the first time contact this thing after writing, really very practical. Just run Fab Deploy.

The main logic is to run unit tests on the local dev branch, commit to the server, SSH into the server, pull down, run unit tests, and restart Apache2. The first time you write it, it might be a little bit easier, but it’s going to keep improving.