您当前的位置: 首页 > 

凌云时刻

暂无认证

  • 0浏览

    0关注

    1437博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

落后产能的实现路径 | 凌云时刻

凌云时刻 发布时间:2020-05-15 18:52:31 ,浏览量:0

凌云时刻 · 技术

导读:当一个骑自行车人的准备提速,是该换轮胎,还是该换引擎?

作者 | 高启

来源 | 凌云时刻(微信号:linuxpk)

背景

最近,跟朋友聊天的时候,见到过一个使用docker的例子,但使用方法相当的野蛮暴力。

大概的流程如下: 

  • 代码上传git 

  • jenkins对代码打jar包

  • jar 包上传docker的宿主机

  • docker run -v 的方式将jar包载入容器

这种发布流程引来了大伙的群嘲——完全是为了docker而docker嘛,这和直接丢几个jar包到物理机上run起来有何区别? 

我没有加入群嘲,因为我也曾经用过这样的一套发布系统,内心里,更多的是理解他们为什么要这么做。

背景

 发布框架

本文会细致到该发布系统的配置项,在深入之前,让我们先来看一下架构图,从全局上对这套发布系统有个大致的了解。

流程说明: 

  • 开发将代码提交到 Dev git中。 

  • 在jenkins里可以配置git hook,发现代码提交时,自动触发构建动作,生成war包。

  • Maven 私服略过不表。

  • 在完成war包的构建之后,jenkins将该war包以日期+当天构建批次为tag,commit到Publish git中。

  • 发布代码存于jenkins本机,jenkins以ssh方式访问测试vm,执行war包的部署脚本,将war包拖入tomcat的webapps目录。(记忆中应该是这个目录吧,时间太长,有点忘了。) 

  • 测试vm上功能验证完毕,再将war包发布于生产vm。

这里只对java代码的发布进行一个流程说明,实际上js/css等静态文件的发布,也可以使用这套流程,只需简化代码构建流程即可。

 详细配置说明

了解了该发布系统的架构后,接下来就是实操;通过看配置,来理解jenkins是如何将工作流给串起来的。

 新建项目

新建项目这块不再详细说明,我直接以之前jenkins镜像里的项目做说明。如下图:

从命名上,大家应该能看出来,第一个项目主要是用以构建war包。第二个项目则是在war包完成构建之后,将war包部署到机器上。

 构建配置

首先看一下构建war包项目的配置项。

源码读取

配置源码的git库。(图中是svn,逻辑一样)

构建war包

这里是构建war包的脚本,里面有两个信息比较重要。分别是war包构建完成之后要commit的git地址以及提交的版本号。

这里面用到了一个cap命令,该命令并非linux系统自带的,其实是Rails的一个自动化部署工具。

版本号的生成规则如下:

也就是任务名+日期+构建批次

进入部署工作流

war包构建完成后,可以直接在jenkins上配置工作流的触发,进入项目部署的任务中。配置如下:

 war包部署

支持传入的参数

先来看一下这个流程支持的功能:

当然这些功能是我们自己定义的,后面会说明在哪里调用、定义。

调用部署脚本

这里我们假设要部署的机器为192.168.180.152(可填入多个),依然是利用cap调用前期写好的脚本,通过ssh方式在192.168.180.152机器上执行这些脚本,完成war包的分发和部署。

后面的一大堆参数实际上都是传给了cap调用的脚本读取。

干活脚本

从上图我们可以看到cap实际上是将CapfileKjt2这个脚本在远端主机上调用并执行,而 支持传入的参数 

小节中提到的功能,也正是该脚本提供的能力。该“面条”脚本信息如下:

#!/usr/bin/ruby
Capistrano::Configuration.instance(true).load do
   namespace :atdp do
    def git_clone(git_server, git_name, git_local_name)
        git_path = "/opt/applications/#{git_local_name}"
            git_clone="if [ ! -d #{git_path} ]; then mkdir -p #{git_path} && git clone git://#{git_server}/#{git_name}.git #{git_path}; fi;"
        return git_clone
        end


        def git_cmd(git_local_name,tag_name)
        app_bin = "/xxx/nginx/sbin"
        git_path = "/opt/applications/#{git_local_name}"
            git_cmd="cd #{git_path}&&git clean -f -d&&git reset --hard&&git fetch&&git reset --merge #{tag_name}; chmod u+x #{app_bin}/*;"
            return git_cmd
        end


    def nginx_deploy(app_action)
        nginx_deploy = ". /etc/profile&&/gensee/nginx/sbin/nginx #{app_action};"
            return nginx_deploy
        end


    def config_backup()
        backup_path = "/opt/backup/nginx/"
        config_path = "/xxx/nginx/conf/"
        config_backup = "if [ ! -d #{backup_path} ];then mkdir -p #{backup_path};  cp #{config_path}*.conf #{backup_path};fi; if [ -f #{backup_path}nginx.conf ];then rm -rf #{backup_path}*.conf; cp #{config_path}*.conf #{backup_path};else cp #{config_path}*.conf #{backup_path} ; fi;"
            return config_backup
        end


    def conf_recovery()
        backup_path = "/opt/backup/nginx/"
        config_path = "/xxx/nginx/conf/"
        conf_recovery = "if [ -f #{config_path}nginx.conf ];then rm -rf #{config_path}*.conf; cp #{backup_path}*.conf #{config_path};else cp #{backup_path}*.conf #{config_path} ; fi;"
            return conf_recovery
        end


    def modify_conf()
        nginx_deploy = ". /etc/profile&&sh /xxx/nginx/sbin/modify_conf.sh;"
            return nginx_deploy
        end


    def add_context(git_name, middleware_ctl)
        git_path = "/opt/applications/#{git_name}"
        if middleware_ctl =~ /nginx/
            app_path = "/xxx/nginx"
            app_bin = "/xxx/nginx/sbin"
        add_context="if [ ! -d /xxx ]; then mkdir -p /xxx; fi;if (ls #{git_path}); then rm -rf #{app_path}*;ln -s  #{git_path}/`ls #{git_path}` #{app_path}; fi; chmod u+x #{app_bin}/*; "
            else
        puts "param error!"
        exit 1
        end
        return add_context
        end


    task :static_git_up_tag, :max_hosts=>1 do
        if !exists?(:git_local_name)
        git_local_name2 = git_name
        else
        git_local_name2 = git_local_name
        end
        if tag_name == "init"
        run git_clone("192.168.101.132",git_name,git_local_name2) + add_context(git_name, middleware_ctl) + modify_conf()
        elsif tag_name =~ /static/
            run config_backup() + git_clone("192.168.101.132",git_name,git_local_name2) + git_cmd(git_name, tag_name) + conf_recovery()
        elsif tag_name == "stop"
        run nginx_deploy("-s stop")
        elsif tag_name == "start"
        run nginx_deploy("")
        elsif tag_name == "re"
        run nginx_deploy("-s reload")
            else
        puts "param error!"
        exit 1
        end
        end
     end
end

脚本中的以下部分,定义了所有支持的功能。

 回滚

如果上线的代码存在严重bug,那么我们需要针对发布的war包进行一个快速的版本回滚。前面我们提到,每次构建war包的时候,都会打上一个版本号。通过该版本号,即可实现问题版本的快速回滚。

详见下图:

 小结

该发布流程实现了从源码的打包到发布的全链路,基本上能够满足小企业的日常发布需求。以今天的眼光来看,这套发布系统是落后的,但在当时,还没有docker/k8s,salt、ansible刚刚起步(印象中是刚起步,记错勿喷)的时期,确实也从流程上减少了很多人肉的步骤,一定程度上提升了运维效率。

思考

对于这套发布系统的一些个人思考: 

  • 没有正规的CMDB,所有的主机信息和配置都写在jenkins中做维护,需要发布的项目多的时候,很难做维护。 

  • 只考虑了发布,未考虑服务发现场景。 

其实,很多系统都是这样的,先入为主:一旦用的习惯,很难去做改动。一是改动成本高,另一方面是看起来好像没有改动的意义:我这套东西用的好好的,够用了,为什么还要再搞一套新的发布系统呢?

所以,在公司的长期技术发展中,如何制定目标?能力应该如何分配?预算如何规划?这些,都是企业决策者应该思考的问题。

END

往期精彩文章回顾

跑赢业务的同时如何实现技术成长?

Java 代码精简之道

那一年,创业 vs 阿里(下):阿里篇

桌面版IDE将迎终结,Github发布代码空间Codespaces

2020 有哪些不容错过的前端技术趋势?

陈绪:被疫情加速的云计算

阿里云存储:安防行业背后的赋能者

长按扫描二维码关注凌云时刻

每日收获前沿技术与科技洞见

关注
打赏
1663816507
查看更多评论
立即登录/注册

微信扫码登录

0.0436s