基于zabbix用Python写一个运维流量气象图-阿里云开发者社区

开发者社区> 科技小能手> 正文

基于zabbix用Python写一个运维流量气象图

简介:
+关注继续查看

  前言:同事问我,你写运维平台最先写哪一部分?好吧,还真把我问倒了,因为这是在问最应该放在放在第一位的东西~作为一个工作不足两年,运维不足一年的新手来说,还真不敢妄下评论,其实按照我的思路,觉得最重要的部分肯定是故障处理,报警,但是这一块怎么写?怎么说?肯定不能重复造轮子了,不过我最想写的是报表系统,思路是有的,但是一直耽搁了,详情参考http://youerning.blog.51cto.com/10513771/1708925

  好吧,在回到那个问题,应该先写哪个部分。我没回答,反问他了。

  他说,应该是运维气象图,这张图上有各个节点的位置,并且标注出流量情况,如果我们在服务器发生故障的时候发现其中一个节点流量过高或者过低,或者一些其他指标,我们在一定程度上可以快速的地位故障的位置。注:cacti,zabbix似乎是有这个插件的,不过不是那么好看,或者有一定局限性,我也没调查过,反正不想用。

  然后,我被上了一课, 那么为毛类似这样的应用或者框架之类的(除了上面说的两个插件,本人暂时没有Google或百度到,如果你知道抨击一下我呗,当然,最好是Python开发的,我好自定义一下),他说,收费的软件有~~~


  首先瞧瞧我花了几天鼓捣出来的Beta版本吧:

wKioL1bcIAqwbO3OAACOSx9nVX4089.png

现在,正题~~~

如果你不会python就收藏着以后看吧,当下就看看思路吧。

如果你会python就在涉猎一下JS吧,比如AngularJS,D3JS什么的,不过可以

如果你什么都不会,希望能激发你的兴趣。

如果你是大神,还执意要看就忽略我代码中的一些写的不优雅,不好看的地方吧T_T

题外话:话说,有什么觉得比较实用的功能是需要收费的,或者一些想法需要实现的可以Q我,我们把它实现了(仅限于大概一周以内能写完的,特别有意思的来说)

授人以鱼不如授人以渔嘛,主要两部分,一部分思路,一部分代码讲解


(一)

思路

Q:数据来源,通过写客户端?

A:当然不,nagios,cacti,zabbix什么的不是有一大堆么,为毛还要自己写,而且还不一定写的比别人好,美其名曰不愿重复造轮子~~~根据自己情况选择吧,这里就选的通过zabbix的API作为数据来源。

Q:用什么web框架?

A:用Flask,很喜欢一句从网上看来的评论django的话,上它的人很多,喜欢它的很少,再者,我实在不想去配置什么配置文件,以及帮我创建一大堆文件(当然也可以不需要),再再者,我的功能不需要太多,再再再者,flask的官方文档写得太棒了~~~


然后瞧瞧我们写什么,完成什么~


代码实现:

     web框架flask

功能页面:

        页面一:展示页面(bootstrap提供样式效果,AngularJS实时数据查询并刷新,为毛不用jQuery?因为jQuery我不会T_T)

        页面二:数据查询接口

代码文件:

        一:用于查询数据的py模块 x 1

        二:用于提供web界面的py文件 x 1

        三:html模板文件 x 3

        四:js文件 x 2


注:其实个人不建议用模板渲染直接把数据渲染到展示页面上去,虽然这样不用写js了,但是写到后面,你就难过了,所以前台后台分离吧,这里也是前台后台分离,前台bootstrap加AngularJS~~~


(二)

代码

zabbix数据获取

获取zabbix数据可参考:http://youerning.blog.51cto.com/10513771/1740152第三部分~~

所以直接放代码吧~~

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
#coding=utf-8
import json
import requests
from pprint import pprint
from os import path
###zabbix api 访问地址
zabbix_pre = "http://10.10.102.88/zabbix/"
zabbix_url = zabbix_pre + "/api_jsonrpc.php"
###用户名密码
user = ""
passwd = ""
###这里只查询进出口流量,所以只有下面两个关键字,后面可能会查询一些其他的~~~
net_in = "net.if.in[eth0]"
net_out = "net.if.out[eth0]"
###构造post请求提交的数据
auth_data = json.dumps(
{
    "jsonrpc""2.0",
    "method""user.login",
    "params": {
    "user""%s" %user,
    "password""%s" %passwd
},
"id"0
})
###http头部信息,zabbix要求的
headers = {
    'content-type''application/json',
}
###构造一个返回查询hostid的json数据,函数是一等公民~~~
def host_data(auth):
    data = json.dumps(
    {
        "jsonrpc":"2.0",
        "method":"host.get",
        "params":{
            "output":["hostid","host"],
            "search":{"host":""}
        },
        "auth":"%s" %auth,
        "id":1,
    })
    return data
###如上,查询hostid
def host_data_search(auth,search):
    data = json.dumps(
    {
        "jsonrpc":"2.0",
        "method":"host.get",
        "params":{
            "output":["hostid","name"],
            "search":{"host":search}
        },
        "auth":"%s" %auth,
        "id":1,
    })
###如上,查询itemid
def item_data_filter1(auth,hostid,filters):
    data = json.dumps(
        {
           "jsonrpc":"2.0",
           "method":"item.get",
           "params":{
               "output":["itemid"],
               "hostids":"%s" %hostid,
                "search":{
                    "key_":filters
                }
           },
           "auth":"%s" %auth,
           "id":1,
        })
    return data
###如上,查询item的所有信息,hostname,itemid一大堆
def item_data_filter2(auth,hostid,filters):
    data = json.dumps(
    {
    "jsonrpc""2.0",
    "method""item.get",
    "params": {
        "output":"extend",
        "hostids":"%s" %hostid,
        "filter": {
            "name": filters
        },
        "sortfield""name"
    },
    "auth":"%s" %auth,
    "id"1
    })
    return data
###如上,获取最新监控值
def history_data(auth,itemid,limit,his=0):
    data = json.dumps(
        {
           "jsonrpc":"2.0",
           "method":"history.get",
           "params":{
               "output":"extend",
               "history":his,
               "sortfield""clock",
               "sortorder""DESC",
               "itemids":"%s" %itemid,
               "limit":limit
           },
           "auth":"%s" %auth,
           "id":1,
        })
    return data
###构造获取zabbix验证id,为了反复操作,当然封装成函数
def getauth(zabbix_url,auth_data,headers):
    auth_ret  = requests.post(zabbix_url, data=auth_data, headers=headers)
    auth_id = auth_ret.json()["result"]
    return auth_id
###将所有结果保存成本地之间,结果包括,主机名(这里指zabbix上的命名),hostid,出入口的itemid
def savefile():
    host_ret = requests.post(zabbix_url, data=host_data(auth_id), headers=headers)
    host_ret = host_ret.json()["result"]
     
###这里请根据实际情况设定,比如包括nginx集群,mysql集群,tomcat集群,如下
    json_all = {}
    json_all["nginx_cluster"= {}
    json_all["tomcat_cluster"= {}
    json_all["mysql_cluster"= {}
    for host in host_ret:
        hostid = host["hostid"]
        hostname = host["host"]
        item_ret = requests.post(zabbix_url, data=item_data_filter1(auth_id,hostid,"net.if"), \
                    headers=headers)
        item_ret = item_ret.json()["result"]
        #pprint(item_ret)
        item_in = item_ret[0]["itemid"]
        item_out = item_ret[1]["itemid"]
        """"这里如上,根据实际情况设定
        if "nginx" in hostname:
            json_all["nginx_cluster"][hostname] = [hostid,item_in,item_out]
        elif "tomcat" in hostname:
            json_all["tomcat_cluster"][hostname] = [hostid,item_in,item_out]
        elif "mysql" in hostname:
            json_all["mysql_cluster"][hostname] = [hostid,item_in,item_out]
        else:
            pass"""
    #pprint(json_all)
    fp = open("clusters.json","w")
    fp.write(json.dumps(json_all))
    fp.close()
###然后通过itemid获取最新的监控值
def gethist(auth_id,itemid,limit,outtype=3):
    while not path.isfile("clusters.json"):
        savefile()    
    history_ret = requests.post(zabbix_url, data=history_data(auth_id,itemid,limit,outtype),\
                         headers=headers)
    #print history_ret.json()
    if len(history_ret.json()["result"]) == 0:
        return 0
    else:
        history_ret = history_ret.json()["result"][0]
        #pprint(history_ret["value"])
        return history_ret["value"]
###然后通过集群名获取整个集群的总和监控值
def gethist_cluster(auth_id,cluster_name,opt):
    clsname = cluster_name
    opt = opt
    auth_id = getauth(zabbix_url,auth_data,headers)
    while not path.isfile("clusters.json"):
        savefile() 
    cluster_file = json.load(open("clusters.json","r"))
    net_list = {"in":1,"out":2}
    if clsname in cluster_file.keys() and opt in net_list.keys():
        sum = 0 
        cls = cluster_file[clsname]
        inf = net_list[opt]
        for host in cls:
            itemid = cls[host][inf]
            his_ret = int(gethist(auth_id,itemid,1,3))
            sum = sum + his_ret
        #print float(sum)/float(1024)
        return float(sum)/float(1024)
auth_id = getauth(zabbix_url,auth_data,headers)
#gethist(auth_id,25919,1,3)
print gethist_cluster(auth_id,"mysql_cluster","in")


然后将上面的代码保存为getsource.py文件用作模块导入,之所以不将所有py代码不写在一起也是为了更好看,更容易反复使用。

注:如果主机多的话会很慢吧~~~因为我没有写并发

然后是flask部分的web代码

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
#coding: utf-8
from flask import Flask,jsonify,render_template
###flask的插件,用的restful作为提供api
from flask.ext import restful
###从上面的那个py文件导入我们需要的函数
from getsource import gethist,getauth,gethist_cluster,savefile
import json
from os import path
app = Flask(__name__)
api = restful.Api(app)
###zabix url
zabbix_pre = "http://10.10.102.88/zabbix/"
zabbix_url = zabbix_pre + "/api_jsonrpc.php"
###username and passwd
user = ""
passwd = ""
###auth data
auth_data = json.dumps(
{       
    "jsonrpc""2.0",
    "method""user.login",
    "params": {
    "user""%s" %user,
    "password""%s" %passwd
},   
"id"0
})   
###headers 
headers = {
    'content-type''application/json',
}    
###根据实际情况设定,这里给每个集群加了个id,用于排序,实际集群参考上面代码,这里也以nginx_cluser,tomcat_cluster,mysql_cluster为例
cluster_id = {
            "nginx_cluster":1,
            "tomcat_cluster":2,
            "mysql_cluster":7
    }
class MyApi(restful.Resource):
    def get(self,name,opt):
        cls = name
        opt = opt
        auth_id = getauth(zabbix_url,auth_data,headers)
        while not path.isfile("clusters.json"):
            savefile()
        cluster_file = json.load(open("clusters.json","r"))
        net_list = {"in":1,"out":2}
        if cls in cluster_file.keys() and opt in net_list.keys():
            sum = 0
            cls = cluster_file[cls]
            inf = net_list[opt]
            for host in cls:
                itemid = cls[host][inf]
                his_ret = int(gethist(auth_id,itemid,1,3))
                sum = sum + his_ret
            return float(sum)/float(1024)
        elif cls == "cluster_all" and opt == "traffic" :
            keys = cluster_file.keys()
            cls_ret = {}
            cls_lis = []
            for key in keys:
                dic = {}   
                dic["name"= key
                dic["id"= cluster_id[key]
                dic["in"= gethist_cluster(auth_id,key,"in")
                dic["out"= gethist_cluster(auth_id,key,"out")
                cls_lis.append(dic)
            cls_ret["ret"= cls_lis
            return jsonify(cls_ret)
        elif cls == "cluster_all" and opt == "list":
            return jsonify(cluster_file)
        elif cls in cluster_id.keys() and opt == "list":
            ret = cluster_file[cls]
            return jsonify(ret)
        else:
            return "None"
api.add_resource(MyApi,"/api/<string:name>/<string:opt>")
@app.route("/")
def hello():
    return "Hello world 你好"
@app.route("/weathermap")
@app.route("/weathermap/<string:name>")
def weathermap(name=None):
    name = name
    if name == "all":
        return render_template("weathermap_all.html")
    elif name == "list":
        return render_template("weathermap_list.html")
    elif name == "plot":
        return render_template("weathermap_plot.html")
    else:
        return render_template("weathermap_all.html")
app.debug = True
app.run(host="0.0.0.0")

再是html模板文件

layout.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <title>Weather Map</title>
    <!-- Bootstrap -->
    <link href="/static/css/bootstrap.min.css" rel="stylesheet">
    <link rel="shortcut icon" href="static/img/flask.ico">
    <script src="/static/js/angular.min.js"></script>
    <script src="/static/js/d3.v3.min.js"></script>
  </head>
  <body>
  <nav class="navbar navbar-default navbar-static-top">
    <span class="label label-primary text-center">Beta</span>
    <h1>运维流量表</h1>
  </nav>
  {% block body %}{% endblock %}
  </body>
</html>

weathermap_all.hml

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
{% extends "layout.html" %}
{% block body %}
  <h3>集群流量一览表</h3>
    <div ng-app="myApp" ng-controller="myCtrl" class="col-md-10 col-md-offset-1">
        <table id="myTable" class="table table-hover">
          <thead>
            <tr>
              <th>ID</th>
              <th>集群名</th>
              <th>出口流量(KB/s)</th>
              <th>入口流量(KB/s)</th>
            </tr>
          </thead>
          <tbody>
            <tr ng-repeat="x in names | orderBy:'id'">
              <td ng-bind="x.id"></td>
              <td ng-bind="x.name"></td>
              <td ng-bind="x.out"></td>
              <td ng-bind="x.in"></td>
            </tr>
          </tbody>
        </table>
    </div>
   </div>
  </div>
    <script src="/static/js/jquery-1.11.3.min.js"></script>
    <script src="/static/js/bootstrap.min.js"></script>
    <script src="/static/js/netdata.js"></script>
    <script src="/static/js/cls_svg.js"></script>
{% endblock %}
</html>

weathermap_list.html

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
{% extends "layout.html" %}
{% block body %}
  <h3>集群流量一览表</h3>
    <div ng-app="myApp" ng-controller="myCtrl" class="col-md-10 col-md-offset-1">
        <table id="myTable" class="table table-hover">
          <thead>
            <tr>
              <th>ID</th>
              <th>集群名</th>
              <th>出口流量(KB/s)</th>
              <th>入口流量(KB/s)</th>
            </tr>
          </thead>
          <tbody>
            <tr ng-repeat="x in names | orderBy:'id'">
              <td ng-bind="x.id"></td>
              <td ng-bind="x.name"></td>
              <td ng-bind="x.out"></td>
              <td ng-bind="x.in"></td>
            </tr>
          </tbody>
        </table>
    </div>
   </div>
  </div>
    <script src="/static/js/jquery-1.11.3.min.js"></script>
    <script src="/static/js/bootstrap.min.js"></script>
    <script src="/static/js/netdata.js"></script>
{% endblock %}
</html>

weathermap_plot.html

1
2
3
4
5
6
7
{% block body %}
<h3>集群拓扑图</h3>
    <script src="/static/js/jquery-1.11.3.min.js"></script>
    <script src="/static/js/bootstrap.min.js"></script>
    <script src="/static/js/cls_svg.js"></script>
{% endblock %}
</html>

最后js文件

netdata.js

1
2
3
4
5
var app = angular.module('myApp', []);
app.controller('myCtrl'function($scope, $http) {
    $http.get("/api/cluster_all/traffic")
    .success(function(response) {$scope.names = response.ret;});
});

cls_svg.js

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
###假如是nginx_cluster,tomcat_cluster,mysql_cluster
var nodes = [ { name: "nginx_cluster" }, { name: "tomcat_cluster" },
              { name: "mysql_cluster" },{ name: "Internet" }];
 var edges = [ { source : 0 , target: 1 } , { source : 0 , target: 2 } ,
               { source : 1 , target: 3 } , { source : 3 , target: 0}];
###上面是0到1,0到2,1到3,3到0的连线,数字分别对应上面的nodes里的name顺序
var width = 500;
var height = 500;
var svg = d3.select("body")
            .append("svg")
            .attr("width",width)
            .attr("height",height);
var force = d3.layout.force()
        .nodes(nodes)       //指定节点数组
        .links(edges)       //指定连线数组
        .size([width,height])   //指定范围
        .linkDistance(200)  //指定连线长度
        .charge([-400]);    //相互之间的作用力
force.start();  //开始作用
console.log(nodes);
console.log(edges);
//添加连线      
var svg_edges = svg.selectAll("line")
                    .data(edges)
                    .enter()
                    .append("line")
                    .style("stroke","#ccc")
                    .style("stroke-width",1);
var color = d3.scale.category20();
//添加节点          
var svg_nodes = svg.selectAll("circle")
                    .data(nodes)
                    .enter()
                    .append("circle")
                    .attr("r",20)
                    .style("fill",function(d,i){
                        return color(i);
                    })
                    .call(force.drag);  //使得节点能够拖动
//添加描述节点的文字
var svg_texts = svg.selectAll("text")
                    .data(nodes)
                    .enter()
                    .append("text")
                    .style("fill""black")
                    .attr("dx", 20)
                    .attr("dy", 8)
                    .text(function(d){
                        return d.name;
                    });
force.on("tick"function(){    //对于每一个时间间隔
     //更新连线坐标
     svg_edges.attr("x1",function(d){ return d.source.x; })
            .attr("y1",function(d){ return d.source.y; })
            .attr("x2",function(d){ return d.target.x; })
            .attr("y2",function(d){ return d.target.y; });
     //更新节点坐标
     svg_nodes.attr("cx",function(d){ return d.x; })
            .attr("cy",function(d){ return d.y; });
     //更新文字坐标
     svg_texts.attr("x"function(d){ return d.x; })
        .attr("y"function(d){ return d.y; });
});

注:我会告诉你我直接去copy来的么,d3js还在钻研ing,所以仅作demo用

然后是目录结构

├── clusters.json

├── getsource.py

├── myapp.py

├── static

│   ├── css

│   │   └── bootstrap.min.css

│   └── js

│       ├── angular.min.js

│       ├── bootstrap.min.js

│       ├── cls_svg.js

│       ├── d3.v3.min.js

│       ├── jquery-1.11.3.min.js

│       └── netdata.js

└── templates

    ├── layout.html

    ├── weathermap_all.html

    ├── weathermap_list.html

    └── weathermap_plot.html


如果都完成了,整个项目是下面这样的

注:bootstrap,jquery,angularjs这些css,js文件百度Google下载吧


API:

查询jjjr2集群列表                                                 http://IP:5000/api/cluster_all/list

查询jjjr2集群流量进出口情况                                http://IP:5000/api/cluster_all/traffic

查询单个集群列表(如tomcat集群)                        http://IP:5000/api/tomcat_cluster/list

查询单个集群流量进出口情况                               http://IP:5000/api/tomcat_cluster/in    其中in,out分别代表入口,出口流量

查询单个主机流量进出口情况(暂不能提供)

比如

wKiom1bcI8DSG4q6AAEjzxhYrnw474.png

访问:http//IP:5000/weathermap/list,仅访问列表

访问:http//IP:5000/weathermap/plot,仅访问拓扑图

访问:http//IP:5000/weathermap或者http//IP:5000/weathermap/all 查看列表与拓扑图在同一个页面

比如

wKiom1bcI9DDAe3xAAGQTMl9HE4742.png

wKioL1bcJFDTPJ97AADJxt01lS8053.png

存在问题:

1:mysql集群中包括了poms-mysql(后面更新)

2:拓扑图并不理想

3:页面不能实时刷新,通过在html页面加入<meta http-equiv="refresh" content="20">可页面自动刷新,可是不优雅,所以并未添加

4:并未设置阈值已区别各个集群健康情况

5:并没有考虑中间件之类,总而言之,现在很粗糙~~


  总结:我们将获取zabbix数据的代码部分抽离出来做成一个模块,这样就能分工明确,也为了让代码显得不是那么庞大很难看,然后web方面主要提供功能,API以及数据展示,通过API我们可以将数据反复利用,并且有很好的兼容性,web的展示当然不能少了bootstrap,一个多漂亮的样式库,不用自己设置css,然后数据操作通过AngularJS,前台通过AngularJS去调用自身提供的API以获取数据,然后填充,最后拓扑图用强大的D3JS,这是做前端的同事推荐的~~



  后记:我一直想写个项目,不是很大的项目,因为这样很快的就能写完,太大的项目要写太久太久~基于的工作阅历还不够感受到的痛点并不多,所以想到的点子并不多,并且也找到了很好的工具了,如果谁来俩点子,我帮你写出来呗~当然了,希望盈利的就算了,我想写的项目都是能直接放到github上~~大家一起爽的那种,哈哈。

  在一定意义上也是为了练手。

  话说,我的从无到有写一个运维APP系列如果这个月前,写不完,也就也写不完了~~~因为拖太长了,热情要没了~

 


本文转自 youerning 51CTO博客,原文链接:http://blog.51cto.com/youerning/1748157



版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
学python 对以后运维的工作很重要吗
**(604657***)  10:10:22 学python  对以后运维的工作 很重要吗 LAMP(812711277)  10:11:07 如虎添翼 加薪的筹码 **(604657***)  10:11:40 感谢 LAMP(812711...
735 0
运维调试笔记:python中pip下载速度慢问题解决
在python开发时,我们经常使用pip来安装相应的python库,但是国外的源下载速度实在太慢,而且经常出现下载后安装出错问题。
1544 0
阿里云服务器端口号设置
阿里云服务器初级使用者可能面临的问题之一. 使用tomcat或者其他服务器软件设置端口号后,比如 一些不是默认的, mysql的 3306, mssql的1433,有时候打不开网页, 原因是没有在ecs安全组去设置这个端口号. 解决: 点击ecs下网络和安全下的安全组 在弹出的安全组中,如果没有就新建安全组,然后点击配置规则 最后如上图点击添加...或快速创建.   have fun!  将编程看作是一门艺术,而不单单是个技术。
4415 0
《Python自动化运维:技术与最佳实践》一导读
由于Python具有脚本语言的特点,学习资源多,社区非常活跃,且在Linux平台默认已安装等优势。Python已经是当今运维领域最流行程的开发语言之一
2839 0
运维调试记录:Ubuntu16.04安装MySQL和Python库MySQLdb步骤
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhaobryant/article/details/45501241 一、安装M...
863 0
阿里云大学「学习路线」,一站式从入门到高手——Python、Java、前端、运维、数据库、云原生……
阿里云大学有哪些学习路线? 已上线(免费学习+自测考试): Python学习路线:https://edu.aliyun.com/roadmap/pythonJava学习路线:https://edu.aliyun.
11625 0
python linux 系统管理与自动化运维
pypdf2 图片:pillow 邮件:smtplib yagmail:简单发送邮件 image.png image.png 接收邮件:imapclient image.png settools打包 python-nmap image.png image.png ipy。
1073 0
14426
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
文娱运维技术
立即下载
《SaaS模式云原生数据仓库应用场景实践》
立即下载
《看见新力量:二》电子书
立即下载