运维自动化平台

1、项目背景

经过几年的发展,公司最早是人工发布工程代码,之后使用了jenkins大部分工作只要新建一次脚本、nginx配置后就能比较自动的完成,但是回退依然使用人工处理的方式。自从公司业务发展起来以后,每个月2次的活动,都要上下主机,根据工程扩容。其中主要工作有新建ecs主机,初始化主机环境,添加dns解析,添加监控,添加jenkins发布脚本配置,配置项目发布模板等,这些给运维工作带了许多重复而且非常容易出错的过程效率也非常低下,同时活动结束后还要完成下线删除nginx配置,删除jenkins发布脚本配置,删除dns解析,删除ecs等,一次活动扩容、缩容往往要耗费2个运维1天的时间,而且频繁的线上文本配置变革已经不止一次的出现人为事故,刚开始的时候为了降低工作疲劳度,甚至提前2天开始扩容这样下来对公司业务基础设施的成本也有不小的开销。在这个时候准备思考使用可视化,平台化的运维去解决这样的问题,并且为后期公司技术人员的扩容做好运维支撑。(感谢公司前端大神路飞的给力支持,用react快速的搞出了一套界面)

2、平台功能

因为不是专业的开发和产品,整个平台的需求和后端设计都是由运维自己完成,大方向上有如下几个需求:新建ecs主机;新建工程;根据工程关联ecs主机形成发布调用;可以实时查看发布日志;批量和串行的发布支持;发布单的概念以便日后的发布权限审批。但是做着做着就发现细节的东西越来越多,比如说和前端的对接方式,代码变更日志等,处理这些小细节实际上耗费了不少时间。整个平台使用python django web框架开发完成。

3、流程图

ecs与工程:

工程构建与发布:

下发salt发布命令流程:

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

class project_type(models.Model):#给前端用的工程的类型名称

    ProjectType = models.CharField(max_length=30, default=None, null=True)

class project_info(models.Model):#工程信息

    ProjectName = models.CharField(max_length=50,default=None,null=False,unique=True)#工程名

    PackageName = models.CharField(max_length=50,default=None,null=True)#java工程打包出来的包名字

    ProjectDir = models.TextField(max_length=300,default=None,null=True)#工程的运行路径

    UpsName = models.CharField(max_length=50,default=None,null=True)#nginx中upstream的名字

    Repertory = models.CharField(max_length=10,default=None,null=True)#仓库类型,git还是svn

    DeployAddress = models.TextField(max_length=300,default=None,null=True)#仓库地址

    Branch = models.CharField(max_length=100,default=None,null=True)#分支

    ProjectType = models.CharField(max_length=30,default=None,null=True)#工程类型

    CreationTime = models.IntegerField(default=None,null=True)#创建时间

    Port = models.CharField(max_length=10,default=None,null=True)#tcp端口

    MavenArgs = models.CharField(max_length=100,default=None,null=True)#java打包参数

    LastBuildStatus = models.CharField(max_length=30,default=None,null=True)#最近一次的构建状态

    LastBuildTime = models.IntegerField(default=None, null=True)#最近一次的构建时间

    

class server_project_r(models.Model):#工程和ecs主机的关系表,表示ecs属于某个工程

    ProjectName = models.CharField(max_length=50, default=None, null=False, db_index=True)#工程名

    InstanceId = models.CharField(max_length=50, default=None, null=False, db_index=True)#ecs的id号,唯一

    CreationTime = models.IntegerField(default=None, null=True)#创建时间

class server_info(models.Model):#ecs主机信息表

    InstanceId = models.CharField(max_length=50,default=None,null=False,unique=True)#ecs的id号

    InstanceName = models.CharField(max_length=50,default=None,null=True,db_index=True)#ecs的名字

    ZoneId = models.CharField(max_length=50,default=None,null=True)#ecs所属的区域,比如说杭州B

    PrivateIp = models.CharField(max_length=50,default=None,null=True)#vpc网络ip

    Cpu = models.CharField(max_length=50,default=None,null=True)#CPU个数

    Memory = models.CharField(max_length=50,default=None,null=True)#内存大小

    OsType = models.CharField(max_length=50,default=None,null=True)#系统类型,linux还是windows

    PayType = models.CharField(max_length=50,default=None,null=True)#支付类型,预付费,后付费

    Status = models.CharField(max_length=30,default=None,null=True)#状态,running,stoped,

    CreationTime = models.IntegerField(default=None,null=True)#创建时间

class build_history(models.Model):#工程构建历史表

    BuildId = models.CharField(max_length=50,default=None,null=False,unique=True)#构建id,唯一

    ProjectName = models.CharField(max_length=50, default=None, null=False, db_index=True)#工程名字

    Status = models.CharField(max_length=30,default=None,null=True)#构建状态

    BuildLog = models.TextField(max_length=1000,default=None,null=True)#构建日志的路径

    CreationTime = models.IntegerField(default=None, null=True)#创建日期

    Note = models.TextField(max_length=1000,default=None,null=True)#备注

class deploy_build_r(models.Model):#工程构建发布关系表

    BuildId = models.CharField(max_length=50, default=None, null=False, db_index=True)#构建id,唯一

    DeployId = models.CharField(max_length=50, default=None, null=False, db_index=True)#发布id,唯一

    ProjectName = models.CharField(max_length=50, default=None, null=False, db_index=True)#工程名字

    CreationTime = models.IntegerField(default=None, null=True)#创建日期

    Status = models.CharField(max_length=30,default=None,null=True)#发布状态

class deploy_server(models.Model):#工程发布ecs表

    DeployId = models.CharField(max_length=50,default=None,null=False,db_index=True)#发布id

    InstanceId = models.CharField(max_length=50, default=None, null=False, db_index=True)#ecs的id

    PrivateIp = models.CharField(max_length=50, default=None, null=True)#vpc私网ip

    ProjectName = models.CharField(max_length=50, default=None, null=False, db_index=True)#ecs的id

    CreationTime = models.IntegerField(default=None, null=True)#创建日期

    Note = models.TextField(max_length=1000, default=None, null=True)#备注

    Status = models.CharField(max_length=30, default=None, null=True)#发布状态

class ecs_type(models.Model):#ecs类型表给前端用的

    TypeName = models.CharField(max_length=30,default=None,null=False)

    TypeId = models.CharField(max_length=50,default=None,null=False)

class vswitch(models.Model):#vswitch阿里云网络的id表

    VswitchName =  models.CharField(max_length=30,default=None,null=False)

    VswitchId = models.CharField(max_length=50, default=None, null=False)

5、核心业务代码实现

其中有用到的python库如下:svn,gitpython,json,django,salt,aliyunsdkcore,aliyunsdkecs,shutil,threadpool,time,datetime,subprocess,uuid,base64,jinja2,sqlalchemy等

下载git或者svn仓库代码:


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

def getcode(Repertory=None, DeployAddress=None,buildProjectDir=None,Branch=None,BuildLog=None,ProjectName=None):

    p1 = project_info.objects.filter(ProjectName=ProjectName).get()

    LastBuildTime = p1.LastBuildTime

    if Repertory == "git":

        try:

            Repo.clone_from(DeployAddress, buildProjectDir, branch=Branch)

        except Exception, e:

            print e

            return False

        if LastBuildTime or LastBuildTime != "null":

            with open(BuildLog, "a") as codelog:

                codelog.write("代码仓库日志如下,如果第一次构建或者无变更记录则为空:\n\n")

            = Git(buildProjectDir)

            log = g.log("--since=" + time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(LastBuildTime)))

            with open(BuildLog, "a") as codelog:

                codelog.write(log.encode("utf8"))

        return True

    elif Repertory == "svn":

        try:

            svncmd = svn.remote.RemoteClient(DeployAddress, username="xxxxx", password="xxxxx")

        except Exception, e:

            print e

            return False

        svncmd.checkout(buildProjectDir)

        localsvn = svn.local.LocalClient(buildProjectDir, username="xxxxx", password="xxxxx")

        if LastBuildTime or LastBuildTime != "null":

            with open(BuildLog, "a") as codelog:

                codelog.write("代码仓库日志如下,如果第一次构建或者无变更记录则为空:\n\n")

            for in localsvn.log_default(timestamp_from_dt=datetime.datetime.utcfromtimestamp(LastBuildTime),

                                          timestamp_to_dt=datetime.datetime.now()):

                with open(BuildLog, "a") as codelog:

                    codelog.write(e.author.encode("utf8"+ " " + e.msg.encode("utf8"+ " " + e.date.strftime(

                        "%Y-%m-%d %H:%M:%S"+ "\n")

            return True

maven打包实现:


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

def mavenProject(ProjectName=None,BuildId=None, BuildLog=None, Repertory=None,DeployAddress=None,

                 Branch=None, ProjectType=None, MavenArgs=None, PackageName=None):

    basedir = "/data/src/"

    buildProjectDir = basedir + ProjectName

    cmd = "cd " + buildProjectDir + ";/opt/apache-maven/bin/mvn -B -f pom.xml -s /opt/apache-maven/conf/settings.xml -gs /opt/apache-maven/conf/settings.xml " + MavenArgs

    print cmd

    if os.path.exists(buildProjectDir):

        shutil.rmtree(buildProjectDir)

        os.makedirs(buildProjectDir)

    else:

        os.makedirs(buildProjectDir)

    #下载仓库代码

    coderesult = getcode(Repertory=Repertory, DeployAddress=DeployAddress, buildProjectDir=buildProjectDir,

                         Branch=Branch, BuildLog=BuildLog, ProjectName=ProjectName)

    if coderesult is False:

        mavenWriteDb(ProjectName=ProjectName, BuildId=BuildId, Result="Failed")

    #构建工程,并且实时得到输出写入文件中。

    with open(BuildLog, "a") as loggin:

        loggin.write("\n打包日志日志如下:\n\n")

    buildcommand = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

    while buildcommand.poll() is None:

        line = buildcommand.stdout.readline()

        if line:

            with open(BuildLog,"a") as loggin:

                loggin.write(line)

        else:

            time.sleep(2)

            continue

    if buildcommand.returncode == 0:

        backupMaven(ProjectType=ProjectType, PackageName=PackageName, buildProjectDir=buildProjectDir, BuildId=BuildId)

        mavenWriteDb(ProjectName=ProjectName, BuildId=BuildId, Result="Success")

    else:

        mavenWriteDb(ProjectName=ProjectName, BuildId=BuildId, Result="Failed")

saltstack调用方式:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

def SaltDepoly(PrivateIp=None,DeployId=None,ProjectName=None,ProjectType=None):

    p1 = deploy_server.objects.get(DeployId=DeployId, PrivateIp=PrivateIp)

    p1.Status = "InProcessing"

    p1.save()

    config_file_path = "/etc/salt/master"

    projectargs = list()

    projectargs.append(ProjectName)

    projectargs.append(DeployId)

    try:

        print projectargs

        client = salt.client.LocalClient(config_file_path)

        if ProjectType == "tomcat":

            result = client.cmd(tgt=PrivateIp,tgt_type="ipcidr", fun=‘projectdeploy.deploy‘,arg=projectargs,

                                timeout=600)

        elif ProjectType == "dubbo":

            result = client.cmd(tgt=PrivateIp, tgt_type="ipcidr", fun=‘dubbodeploy.deploy‘, arg=projectargs,

                                timeout=600)

        print result

        return True

    except Exception, e:

        print e

        return False

发布项目控制实现部分:


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

if len(IpList) > 3:#并行发布

    print "主机大于3台,开始第一轮发布"

    pool = threadpool.ThreadPool(len(IpList))

    args_list = list()

    for in range(0len(IpList)/2):

        args_tup = ([IpList[i], DeployId, ProjectName, ProjectType],None)

        args_list.append(args_tup)

    request = threadpool.makeRequests(SaltDepoly, args_list)

    [pool.putRequest(req) for req in request]

    pool.wait()

    #检测这个DeployId中是否有fail的主机,如果有报错并且退出

    if GodeployCheck(DeployId=DeployId) != True:

        GoDeployStatus(DeployId=DeployId, Action="close")

        print "发布过程中有部分主机失败,退出发布主进程"

        return

    elif GodeployCheck(DeployId=DeployId) == True:

        GoDeployStatus(DeployId=DeployId, Action="close")

        print "开始第二轮发布"

    pool = threadpool.ThreadPool(len(IpList))

    args_list = list()

    for in range(len(IpList)/2len(IpList)):

        args_tup = ([IpList[i], DeployId, ProjectName, ProjectType], None)

        args_list.append(args_tup)

    request = threadpool.makeRequests(SaltDepoly, args_list)

    [pool.putRequest(req) for req in request]

    pool.wait()

    #检测这个DeployId中是否有fail的主机,如果有报错并且退出

    if GodeployCheck(DeployId=DeployId) != True:

        GoDeployStatus(DeployId=DeployId, Action="close")

        print "发布过程中有部分主机失败,退出发布主进程"

        return

    elif GodeployCheck(DeployId=DeployId) == True:

        GoDeployStatus(DeployId=DeployId, Action="close")

        print "全部发布成功"

else:#串行发布

    for in range(0len(IpList)):

        ip = IpList[i]

        print ip

        SaltDepoly(ip, DeployId, ProjectName, ProjectType)

        if GodeployCheck(DeployId=DeployId) != True:

            GoDeployStatus(DeployId=DeployId, Action="close")

            print "发布过程中有部分主机失败,退出发布主进程"

            return

        elif GodeployCheck(DeployId=DeployId) == True:

            GoDeployStatus(DeployId=DeployId, Action="close")

            print "一台发布成功"

jwt检测,装饰器实现:


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

def CheckToken(func):

    @wraps(func)

    def Check(*keys, **kw):

        if keys[0].COOKIES.has_key("token"is True:

            secret_key = "xxxxxx"

            tokenstring = keys[0].COOKIES.get("token","")

            try:

                userid = jwt.decode(tokenstring, key=secret_key)["userInfo"]["id"]

            except Exception,e:

                print e

                print "Token不正确,禁止访问"

                result = json.dumps({"isSuccess"0"message""接口不支持单独调用"})

                return HttpResponse(result, content_type="application/json")

            return func(*keys, **kw)

        else:

            print "没有token,禁止访问"

            result = json.dumps({"isSuccess":0,"message":""})

            return HttpResponse(result, content_type="application/json")

    return Check

    

#装饰器在django里的应用    

@CheckToken

def webEcsList (request):#ecs展示接口

    if request.method == "GET":

        PageSize = request.GET.get("PageSize"None)

        PageNumber = request.GET.get("PageNumber"None)

        aliyun = request.GET.get("aliyun",None)

        result = getDescribeInstances(PageNumber=PageNumber, PageSize=PageSize,aliyun=aliyun)

        return HttpResponse(result,content_type="application/json")

6、salt扩展模块

用过salt的人应该知道,salt在执行起来比ansible要快,而且配置方法比chef要舒服很多。但是有个比较致命的缺点就是sdk调用的时候只要内部的python代码执行不出错,salt就无法明确的告诉你本次调用是否真的达到你想要结果了,所以我采取了一个思路用salt模块去检测每台ecs的发布结果然后落库。

其实整个平台的核心不止是http的调用和显示,salt这边也非常非常的重要和复杂。其中要先写最基础的发布bash脚本,给salt调用,然后判断project类型,是否tomcat项目要用jinja2渲染配置配置。最终判断结果落库,下面是一些核心的代码。其中落库使用了sqlalchemy这个非常著名的python orm。

渲染配置:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

def rendering_argv(self):

    if not os.path.exists(self.ProjectDir):

        os.system("cp -rp /data/tomcat_template %s" % (self.ProjectDir))

        port = int(self.Port)

        http_port=port

        shutdown_port=port+1

        ajp_port=port+2

        redirect_port=port+3

        jmx_port=port+4

        templateloader=jinja2.FileSystemLoader("/data/tomcat_template/conf")

        env=jinja2.Environment(loader=templateloader)

        template= env.get_template("server_template.xml")

        server_xml = template.render(http_port=http_port, shutdown_port=shutdown_port, ajp_port=ajp_port,

                                     redirect_port=redirect_port)

        with open(self.ProjectDir+"/conf/server.xml","w") as f:

            f.write(server_xml)

        templateloader = jinja2.FileSystemLoader("/data/tomcat_template/bin")

        env = jinja2.Environment(loader=templateloader)

        template = env.get_template("catalina_template.sh")

        catalina_sh = template.render(jmx_port=jmx_port)

        with open(self.ProjectDir+"/bin/catalina.sh","w") as f:

            f.write(catalina_sh)

    else:

        return

启动、停止业务程序:


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

def stopApp(self,ProjectDir=None):

    stoppath = self.ProjectDir + "/bin/stop.sh"

    cpid = os.system("sh "+stoppath)

    if cpid != 0:

        exit(1)

    os.system("cd " + self.ProjectDir +";rm -rf webapps/*")

def startApp(self,ProjectDir=None):

    self.getPackage()

    startpath = self.ProjectDir + "/bin/start.sh"

    cpid = os.system("sh "+startpath+" "+self.myaddr)

    if cpid != 0:

        print "发布失败"

        result = self.db.sessiondb.query(deploy_server).filter(and_(deploy_server.DeployId ==self.DeployId,

                                                 deploy_server.PrivateIp == self.myaddr))

        result.update({deploy_server.Status: "Failed"})

        result.update({deploy_server.CreationTime: int(time.mktime(datetime.datetime.now().timetuple()))})

        self.db.sessiondb.commit()

        exit(1)

    else:

        result = self.db.sessiondb.query(deploy_server).filter(and_(deploy_server.DeployId ==self.DeployId,

                                                 deploy_server.PrivateIp == self.myaddr))

        result.update({deploy_server.Status: "Success"})

        result.update({deploy_server.CreationTime: int(time.mktime(datetime.datetime.now().timetuple()))})

        self.db.sessiondb.commit()

        print "发布成功"

7、结束语

这套平台上线后,日常耗费半天甚至一天时间的扩容和锁容。基本上15分钟就能搞定。当然其中还是有很多不足和需要改进的地方。后续还会慢慢优化。

时间: 2024-10-30 20:32:51

运维自动化平台的相关文章

实战:基于Python构建运维自动化平台

导语: 今天与大家一起探讨如何基于Python构建一个可扩展的运维自动化平台,也希望能与大家一起交流,共同成长. 此次分享将通过介绍OMServer.OManager具备的功能.架构设计.模块定制.安全审计.C/S结构的实现等几个方面的内容来展开. 为什么选择Python? 默认安装且跨平台 可读性好且开发效率高 丰富的第三方库(开发框架.各类API.科学计算.GUI等) 社区活跃&众多开发者. Python在腾讯的现状,根据去年内部提交组件语言统计,除去2.3.4前端技术,Python在高级编

如何基于Python构建一个可扩展的运维自动化平台

嘉宾简介 刘天斯 从事互联网运维工作已13年,目前就职于腾讯-互动娱乐部,负责游戏大数据的运营,曾就职于天涯社区,担任首席架构师/系统管理员. 热衷开源技术的研究,包括系统架构.运维开发.负载均衡.缓存技术.数据库.NOSQL.分布式存储.消息中间件.大数据及云计算.Mesos.Docker.DevOps等领域.擅长大规模集群的运维工作,尤其在自动化运维方面有着非常丰富的经验.同时热衷于互联网前沿技术的研究,活跃在国内社区.业界技术大会,充当一名开源技术的传播与分享者. 导言 受 Reboot

运维自动化平台思路

⑴客户端初始化: info collect  信息收集  CMDB 配置管理工具 ⑵服务端 ①资产管理simplecmdb ②配置管理(软件安装.配置同步) puppet.saltstack.ansible ③代码发布(自动化部署) jenkins.svn.python xml RPC ④监控报警  nagios(关乎状态) graphite(关乎性能和趋势) ⑤日志搜集  kibana logstash  elasticasearch 运维自动化平台思路

[运维] 第三篇:漫谈数据中心运维自动化

运维自动化是从2010年以后起来的一个运维需求,10年之前,运维项目主要集中在监控和ITIL流程上,当时也有BMC Control-M等产品在推,但是客户接受程度和影响力不如监控和流程.10年之后,运维自动化提上日程,建行开始招运维自动化的标,IBM.BMC.HP都纷纷参与,测了三轮,最后HP opsware中标,只能说一句厉害!工商银行也在自己组织服务商做自己特色的运维自动化平台,做了3.4年,基本成型,服务商也做出了自己的运维自动化产品,正式推向市场.当时运维自动化的主要功能是五项:自动化巡

【有感而发】从中华武术谈运维工程师以及运维自动化

从中华武术谈运维工程师以及运维自动化 任何事物都没有完美一说,但是我们可以死磕自己,追求极致... 无论我们现在是搬砖呢,砌墙呢,还是在逗自己混日子,我们需要关注的是自己的方向在哪里,而不是过于在意自己当前的所站的位置,人生不能受限于自己的意识. 平时和小伙伴们聊人生谈理想的时候,我会经常和别人讲我所认为的专业化运维工程师和运维工作的方向,有认可的也有不认可的,认可的多在努力让自己的工作越来越轻松,自己的价值越来越能得到体现,不认可者多属于一天都很忙,而且认为运维就是帮开发人员打打杂,做大量重复

浅谈运维自动化的那些事儿

前言 运维管理兜兜转转十几余载,大家的运维管理再也不是小米加×××.人工费力拉线扛服务器的传统时代,如你所知,这些年大家张口闭口谈的都是运维自动化如何如何.一千个读者就有一千个哈姆雷特,一千个运维就有一千种运维自动化想法或构建思路,小生不才,今日斗胆来聊聊我眼中"运维自动化"的那些事儿!如有不妥,还请大家给出相应的意见...... 运维自动化到底干个啥? 据度娘之意,IT运维自动化是将日常IT运维中大量的重复性工作,小到简单的日常检查.配置变更和软件安装,大到整个变更流程的组织调度等,

结合Ansible在AWS云计算平台上实现运维自动化

刚刚看了金山梁晓聪的"在AWS上的运维自动化实践分享",发现技术都是相通的,大家都是用最好的技术.我们的业务平台主要也是AWS云计算平台,尝试了许多自动化运维/配置工具,最后还是选终了Ansible. 下一步在公司运维自动化DevOps要做的工作:增大Ansible在系统中的应用比重,真正跟AWS结合起来.选择 Ansible 主要因为丰富的相关支持,包括很多现有的组件和模块和开源的 Ansible 部署和脚本.笔者也尝试了市面上所有自动化运维和自动化配置工具,发现Ansible是对A

运维自动化工具Cobbler之——安装实践

运维自动化工具--Cobbler实践 第1章 About Cobbler 1.1 Cobbler Introduction Cobbler是一个Linux服务器安装的服务,可以通过网络启动(PXE)的方式来快速安装.重装物理服务器和虚拟机,同时还可以管理DHCP,DNS等. Cobbler可以使用命令行方式管理,也提供了基于Web的界面管理工具(cobbler-web),还提供了API接口,可以方便二次开发使用.Cobbler是较早前的kickstart的升级版,优点是比较容易配置,还自带web

18页PPT带你深度解读运维自动化【转】

来自地址:[http://www.opsers.org/tech/18-pages-ppt-show-you-depth-interpretation-operations-automation.html] 说实话,一个运维团队的运维能力如何,其实看一个自动化管理系统便知! ********文章较长,索引目录如下******* 一.概述 二.运维自动化的三重境界 三.运维自动化的多维解读 ******第一.基于应用变更场景的维度划分 ******第二.基于系统层次的维度划分 ******第三.基