Fabric是一个Python库, 也是一个命令行工具, 通过 SSH 来做应用程序的部署和系统管理任务
它可以执行本地的远程的系统命令, 上传下载文件, 以及其他能用Python编程完成的任务
其实它是一个工具框架, 启动时会默认执行一个 python 文件 fabfile.py
安装
先安装好 python3.x, 再安装 fabric3
1
2pip install fabric3
快速上手
简单写个小例子
1
2$vi fabfile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18from fabric.api import * env.hosts = ['10.224.64.106'] env.user = "root" env.password = "pass" def freedisk(param='-h'): cmd = 'df ' + param run(cmd) def listfile(folder='~'): cmd = 'ls -l ' + folder run(cmd) def pullcodes(folder='/workspace/cpp/snippets'): with cd(folder): run("git pull origin master")
1
2
3# 察看远程服务器上的磁盘剩余空间 $ fab listfile:folder=/home/walter
-
为安全起见, 不用在文件中存放密码, 在命令行提示输入
$ fab -u root -I -H 10.224.64.106 freedisk
更好的做法是把本机私钥预先拷贝到目标服务器上, 这样就不用输入密码了
1
2
3
4
5
6
7
8
9
101. 在本机上生成公钥 ~/.ssh/id_rsa.pub ssh-keygen -t rsa 2. 拷贝此公钥到目标服务器 10.224.64.106 上 scp id_rs.pub root@10.224.64.106:/root 3. 目标服务器 10.224.64.106 上 cat id_rsa.pub >> ~/.ssh/authorized_keys chmod 700 ~/.ssh/authorized_keys
常用方法
- run (fabric.operations.run)
- sudo (fabric.operations.sudo)
- local (fabric.operations.local)
- get (fabric.operations.get)
- put (fabric.operations.put)
- prompt (fabric.operations.prompt)
- reboot (fabric.operations.reboot)
常用函数
- cd (fabric.context_managers.cd)
- lcd (fabric.context_managers.lcd)
- path (fabric.context_managers.path)
- settings (fabric.context_managers.settings)
- prefix (fabric.context_managers.prefix)
高阶用法
设置角色role来指定远程的服务器范围
或者直接用字典由输入参数指定, 例如:
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# usage: # fab localpull:rtc # fab checkfiles:hf2 from fabric.api import * from fabric.context_managers import * from fabric.contrib.console import confirm env.user = 'root' env.roledefs = { 'qa': ['root@10.224.57.202:22'], 'dev': ['root@10.224.64.106:22'] } env.passwords = { 'root@10.224.57.202:22': 'pass', 'root@10.224.64.106:22': 'pass', 'root@10.224.64.107:22': 'pass' } @roles('dev') @task def localpull(app='web'): if app == 'web': code_dir = '/workspace/walter/hfweb' with lcd(code_dir): local("git pull origin master") elif app == 'rtc': code_dir = '/workspace/walter/hfrtc' with lcd(code_dir): local("git pull origin master") local("git branch -l") test_servers = {'hf1':['root@10.224.64.46:22'], 'hf2':['root@10.224.64.106:22'], 'hf3':['root@10.224.64.107:22']} @task def listfiles(): run("ls -l") @task def checkfiles(target_env='hf2'): execute("listfiles", hosts=test_servers[target_env])
示例
例1:批量上传下载文件
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
48from fabric.api import * from fabric.context_managers import * from fabric.contrib.console import confirm env.user='root' env.hosts=['10.224.64.106'] env.passwords = { 'root@10.224.64.106:22': 'password' } local_dir='/workspace/cpp/codelab' remote_dir = '/opt/cpp/codelab' file_list = [ 'src/FileUtils.cpp', 'src/FileUtils.h', 'src/Makefile.am', 'src/StringUtils.cpp' ] @task def hostinfo(): run('uname -s') @task def upload(): #upload file task with cd(remote_dir) : for filename in file_list: local_file = local_dir + "/" + filename remote_file = remote_dir + "/" + filename #print local_file, " to ", remote_file with settings(warn_only=True): #when upload error,continue result = put(local_file, remote_file) if result.failed and not confirm("put file failed,Continue[Y/N]?"): abort("Aborting file put task!") @task def download(): #upload file task with cd(remote_dir) : for filename in file_list: local_file = local_dir + "/" + filename remote_file = remote_dir + "/" + filename #print local_file, " to ", remote_file with settings(warn_only=True): #when upload error,continue result = get(remote_file,local_file) if result.failed and not confirm("put file failed,Continue[Y/N]?"): abort("Aborting file put task!")
例2: 创建 gitbook 目录, 以使用 markdown 来写作
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
79from fabric.api import * #import SimpleHTTPServer import http.server import socketserver import os from utils import chapters BOOK_CONTENT_FOLDER = './book/content' BOOK_OUTPUT_FOLDER = './book/output' @task def build_book(is_open_index=False): local( "rm -rf %s" % BOOK_OUTPUT_FOLDER) local( "mkdir -p %s" % BOOK_OUTPUT_FOLDER) cmd = "gitbook build %s %s --log=debug --debug" % (BOOK_CONTENT_FOLDER, BOOK_OUTPUT_FOLDER) local(cmd) if is_open_index: local("open %s/index.html" % BOOK_OUTPUT_FOLDER) @task def build_pdf(): cmd = "gitbook pdf ./book/content/" local(cmd) @task def build_epub(): cmd = "gitbook epub ./book/content/" local(cmd) local("ebook-convert book.epub book.docx") @task def init_book(): create_templates() @task def serve_book(port=8000): web_dir = os.path.join(os.path.dirname(__file__), BOOK_OUTPUT_FOLDER) os.chdir(web_dir) Handler = http.server.SimpleHTTPRequestHandler httpd = socketserver.TCPServer(("", port), Handler) print("serving at port", port) httpd.serve_forever() def md2rst(mdfile): rstfile = mdfile[:-3]; cmd = "pandoc --from=markdown --to=rst --output=%s.md %s" % (rstfile, mdfile) print(cmd) local(cmd) def rst2md(rstfile): mdfile = rstfile[:-3]; cmd = "pandoc --from=rst --to=markdown --output=%s.md %s" % (mdfile, rstfile) print(cmd) local(cmd) def create_templates(): create_chapter(chapters.folder1, chapters.files1) create_chapter(chapters.folder2, chapters.files2) create_chapter(chapters.folder3, chapters.files3) create_chapter(chapters.folder4, chapters.files4) create_chapter(chapters.folder5, chapters.files5) def create_chapter(folder, files): print("--- create chapter in %s ---" % folder) cmd = "mkdir -p %s/%s" % (BOOK_CONTENT_FOLDER, folder) local(cmd) for file in files: cmd = "touch %s/%s/%s" % (BOOK_CONTENT_FOLDER, folder, file) local(cmd)
例3. 用 Fabric 玩转 docker 基本命令
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
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265from fabric.api import * from fabric.context_managers import * from fabric.contrib.console import confirm import os, subprocess local_path = os.path.dirname(os.path.abspath(__file__)) local_dir = os.getcwd() backend_service_ports={ "tomcat": "8080", "kanban": "8080", "cassandra": "9042", "elasticsearch": "9200 9300", "influxdb": "8086", "postgres": "5432", "rabbitmq": "4369 5671 5672 15671 15672 25672", "redis": "6379", "riak": "8087 8098", "kafka-zookeeper": "2181 9092" } need_print_cmd=True only_display_cmd=False docker_image_prefix="walterfan-" docker_container_prefix="msa-" restart_policy="--restart always" jenkins_volume_mapping = "/o/jenkins:/var/jenkins_home" jenkins_container_name="jenkins" jenkins_image_name="walterfan-jenkins" def run_cmd(cmd): if(need_print_cmd): print(cmd) if not only_display_cmd: local(cmd) @task def jenkins_build(): docker_build("jenkins") @task def jenkins_run(listen_port="1980"): cmd = "docker run %s -v %s -p %s:8080 -p 50000:50000 --name=%s -d %s" % (restart_policy, jenkins_volume_mapping, listen_port, jenkins_container_name, jenkins_image_name) run_cmd(cmd) @task def jenkins_start(): cmd = "docker start %s" % jenkins_container_name run_cmd(cmd) @task def jenkins_stop(): cmd = "docker stop %s" % jenkins_container_name local(cmd) #cmd = "docker cp jenkins-container:/var/log/jenkins/jenkins.log jenkins.log" #local(cmd) @task def jenkins_remove(): docker_remove(jenkins_container_name) @task def jenkins_commit(message): cmd = "docker commit -m "%s" %s walterfan/jenkins:1.0" % (message, jenkins_container_name) @task def jenkins_check(): cmd = "docker exec %s ps -ef | grep java" % jenkins_container_name print(cmd) local(cmd) cmd = "docker exec %s cat /var/jenkins_home/secrets/initialAdminPassword" % jenkins_container_name print(cmd) local(cmd) #-----------------------------grafana influx --------------------------# @task def graflux_build(): cmd = "docker build --tag %s docker/%s" % ("graflux", "graflux") run_cmd(cmd) @task def graflux_start(): grafana_port = 3000 influx_api_port = 8086 influx_web_port = 8083 cmd = "docker run --name local-graflux -d -p %d:3000 -p %d:8086 -p %d:8083 graflux" % (grafana_port, influx_api_port, influx_web_port) print(cmd) local(cmd) @task def influx(): """ execute the influx command in graflux docker """ cmd = "docker exec -it local-graflux influx" run_cmd(cmd) @task def graflux_bash(): """ execute the /bin/bash in graflux docker """ cmd = "docker exec -it local-graflux /bin/bash" run_cmd(cmd) @task def graflux_stop(): #cmd = "docker stop local-graflux" docker_remove("local-graflux") @task def redis_cli(): cmd = "docker exec -it local-redis redis-cli" local(cmd) @task def redis_bash(): cmd = "docker exec -it local-redis /bin/bash" local(cmd) @task def cassandra_cql(cql=''): cmd = "docker exec -it local-cassandra /usr/bin/cqlsh " if cql: cmd = cmd + " -e '%s'" % cql local(cmd) @task def mysql_cli(usr='root'): cmd = "docker exec -it local-mysql /usr/bin/mysql -u %s -p" % usr local(cmd) @task def mysql_bash(): cmd = "docker exec -it local-mysql /bin/bash" local(cmd) #---------------------------- freeswitch -------------------------------# #bettervoice/freeswitch-container 1.6.16 @task def freeswitch_start(): cmd = "sudo docker run --name freeswitch -p 5060:5060/tcp -p 5060:5060/udp -p 5080:5080/tcp -p 5080:5080/udp -p 8021:8021/tcp -p 7443:7443/tcp -p 60535-65535:60535-65535/udp -v %s/etc/freeswitch:/usr/local/freeswitch/conf bettervoice/freeswitch-container:1.6.16" % local_path print(cmd) local(cmd) @task def freeswitch_stop(): docker_remove(freeswitch) #-----------------------------------------------------------# @task def start_services(): cmd = "docker-compose up -d" run_cmd(cmd) @task def stop_services(): cmd = "docker-compose down -v" run_cmd(cmd) #----------------------------- general command ---------------- @task def link_war(war_package, war_name): cmd = "docker exec tomcat ln -s %s/%s /usr/local/tomcat/webapps/%s" % (local_path, war_package, war_name) local(cmd) @task def deploy_war(war_package, war_name): cmd = "docker cp %s/%s tomat:/usr/local/tomcat/webapps/%s" % (local_path, war_package, war_name) local(cmd) @task def undeploy_war(war_name): cmd = "docker exec tomcat rm -rf /usr/local/tomcat/webapps/%s" % (war_name) local(cmd) cmd = "docker exec tomcat rm -f /usr/local/tomcat/webapps/%s.war" % (war_name) local(cmd) #----------------------------- general commands --- def get_container_id(container_name): str_filter = "-aqf name=%s" % container_name; arr_cmd = ["docker", "ps", str_filter] container_id = subprocess.check_output(arr_cmd).strip() return container_id def get_port_args(service_name="kanban", increment=0): str_port = "" ports = backend_service_ports[service_name] if ports: arr_port = ports.split("\s") for port in arr_port: str_port = str_port + "-p %s:%d" %(port, int(port) + int(increment)) return str_port @task def docker_rename(old_name, new_name): cmd = "docker tag %s %s" % (old_name, new_name) run_cmd(cmd) @task def docker_build(service_name="local-tomcat"): docker_image_name = docker_image_prefix + service_name cmd = "docker build --tag %s docker/%s" % (docker_image_name, service_name) run_cmd(cmd) @task def docker_run(service_name="local-tomcat", volume_args="-v /workspace:/workspace"): port_args = get_port_args(service_name) docker_container_name = docker_container_prefix + service_name docker_image_name = docker_image_prefix + service_name cmd = "docker run %s %s %s -d --name %s %s" % (restart_policy, volume_args, port_args, docker_container_name, docker_image_name) run_cmd(cmd) @task def docker_stop(container_name="local-tomcat"): cmd = "docker stop %s" % (container_name) run_cmd(cmd) @task def docker_list(): cmd = "docker ps" run_cmd(cmd) @task def docker_exec(container_name="local-tomcat", instruction="/bin/bash"): instruction = "/bin/bash" cmd = "docker exec -it %s %s" % (container_name, instruction) run_cmd(cmd) @task def docker_remove(container_name="kanban"): cmd1 = "docker kill %s|| true" % container_name run_cmd(cmd1) cmd2 = "docker rm -v %s || true" % container_name run_cmd(cmd2) @task def docker_commit(container_id, image_name, message=""): cmd = "docker commit -m "%s" %s %s" % (message, container_id, image_name) run_cmd(cmd) @task def docker_install(): #cmd ="brew remove docker && brew upgrade" cmd = "brew cask install docker && open /Applications/Docker.app" run_cmd(cmd) @task def help(): print("examples:tfab docker_run:cassandra,"-v /opt:/workspace" ")
FAQ
问题1: 切换环境
写一个字典对象,在参数里传入环境类型, 再组成所需的环境变量
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
27environments = { "integration": { "ApiServiceUrl" :"https://checklist-int.example.com/checklist/api/v1", "environment":"integration" }, "production":{ "ApiServiceUrl" :"https://checklist.example.com/checklist/api/v1", "environment":"production" }, "lab":{ "ApiServiceUrl" :"https://checklist-lab.example.com/checklist/api/v1", "environment":"lab" }, "local":{ "ApiServiceUrl" :"https://localhost:2008/checklist/api/v1", "environment":"lab" } } def get_env_vars(env_type): defined_vars = " " for key, value in environments[env_type].iteritems(): defined_vars = defined_vars + " -D%s=%s" %(key, value) return defined_vars;
问题2: 如何读取配置文件
- 以 json file 为例
1
2
3
4
5
6
7
8
9
10
11
12
13
14class ProvisionConfig: def __init__(self, json_file): self.read_config(json_file) self.base_path = self.config_data['basePath'] self.username = self.config_data['username'] self.locale = self.config_data['locale'] def read_config(self, json_file): json_data=open(json_file) self.config_data = json.load(json_data)
问题3: 如何获取命令行的输出结果
比如想获得 docker 容器的id, 可以用如下命令
1
2docker ps -aqf name=jenkins
用 subprocess.check_output 方法就可以获取输出, 以下面这个函数为例
1
2
3
4
5
6def get_container_id(container_name): str_filter = "-aqf name=%s" % container_name; arr_cmd = ["docker", "ps", str_filter] container_id = subprocess.check_output(arr_cmd).strip() return container_id
问题4: fab put error: paramiko.ssh_exception.SSHException: Channel closed
解决方法:
编辑 /etc/ssh/sshd_config:
<pre>
vi /etc/ssh/sshd_config
</pre>加上一行
Subsystem sftp internal-sftp
<pre>
Port 22
Protocol 2
LogLevel INFO
X11Forwarding no
MaxAuthTries 4
IgnoreRhosts yes
HostbasedAuthentication no
PermitRootLogin yes
PermitEmptyPasswords no
PermitUserEnvironment no
Ciphers aes128-ctr,aes192-ctr,aes256-ctr
ClientAliveInterval 600
Banner /etc/issue
Subsystem sftp internal-sftp
</pre>保存并重启 SSH server:
1
2service sshd restart
参考链接
- Fabric site
- Fabric Tutorial
- Fabric options
最后
以上就是搞怪高跟鞋最近收集整理的关于程序员瑞士军刀之 Fabric的全部内容,更多相关程序员瑞士军刀之内容请搜索靠谱客的其他文章。
发表评论 取消回复