很长时间没写博客了,主要原因出在上家的公司,自己承受着三个半月没工资的痛苦。
突然想起普希金的《假如生活欺骗了你》:
假如生活欺骗了你
不要悲伤 不要心急
忧郁的日子里须要镇静
相信吧 快乐的日子将会来临
心儿永远向往着未来
现在却常是忧郁
一切都是瞬息
一切都将会过去
而那过去了的
就会成为亲切的回忆
对于一个横跨2000多公里、漂泊他乡的人来说,在一线城市打拼,没收入意味着什么?
上海房租让你喘不过气
生活开销怎么办?
想去玩,算了,还是老老实实家里呆着吧
没激情,没动力
没安全感、归属感、怕
回家吧,压力就没那么大了
梦想还是要有的,万一见鬼了呢?但残酷的事实是梦想和现实往往不能共存
。
如果共存的话,中国人这么多,怎么没出来第二个马云、第二个马化腾?
话转回来,谈谈技术吧。
1.为什么需要实现自动打包发布上线
这个功能?
简单理解,减少重复性工作,方便项目管理
。
一个产品或项目的上线要经历开发、测试、部署
三个环节。
一个公司,可能产品或项目就有上百个,那么每个产品或项目都需要重复走这三个环节吗?答案是必须的,但如果能减少里面重复性的工作
岂不是能更好的管理
?
如何高效的进行迭代开发,与同类公司、同类产品作竞争,自动化部署
就显得尤为重要。
2.实现自动化部署基本流程
上图简要说明了如何实现自动化部署的流程。 先看下实现后的效果:
神奇吧?通过一个立即构建
按钮,就实现了页面内容的更改,将图片和文字都替换了。
那么点击按钮后究竟做了哪些事情呢?
简要概括实现了以下步骤:
将gitlab上的代码下载到了本地电脑上,且进行了代码压缩,压缩完成后发布压缩包到自己的阿里服务器上,阿里服务器成功接收到压缩包后,解压放到了服务器指定目录,最后通过nginx反向代理实现了自动化部署。
3.详细讲解每部分内容
该章节会讲解的很细,细化到前端工程师从开始编码,到最后运维工程师发布上线的完整流程,每一步做了具体哪些操作,从而实现自动化部署的。 具体步骤如下:
1.Vue+GitLab:(研发阶段)
- 用vue编码实现业务迭代
- 使用webpack进行前端构建
- 将最新的代码和构建后的dist目录共同组成的项目推送到远程仓库gitlab
2.GitLab与Jenkins:(构建阶段)
- 获取gitlab仓库上最新的完整项目下载到本地电脑
- 将下载好的完整项目中构建好的dist目录进行压缩
3.Jenkins与阿里服务器:(构建阶段)
- 压缩成功后将自动连接阿里服务器进行上传
- 上传阿里服务器成功后开始解压gz包到指定好的nginx目录下
4.Nginx:(发布阶段,可提前做好)
- 通过nginx将域名反向代理到服务器指定目录
3.1 Vue+GitLab
3.1.1 使用vue进行前端开发,通过webpack进行构建。
该部分内容属于前端技术范畴,之前讲解过前端构建的文章《前端工程化+webpack》,在这里就不详细说明了。
vue和webpack方面,需要特别注意两个地方:
1.vue history模式
对于vue开发的单页面应用(SPA)时,在切换不同页面的时候,可以发现html永远只有一个,而vue-router默认hash
模式。
vue hash模式:
格式:http://localhost:8081/#/parent/id
说明:利用url的hash来模拟一个完整的url,当url改变时页面不会重新加载。
vue history模式:
格式:http://localhost:8081/parent/id
说明:利用HTML5的history.pushStateAPI来完成url的跳转而不需要重新加载页面。
用hash模式的缺点是#这种形式真的很丑,完全不像正常的url地址。 既然如此,当然选择使用history模式啦。
铺垫了那么久,想使用好history模式,还需要后台配置支持
。
因为我们的应用是个单页面应用,就一个html文件,如果后台没有正确的配置,当用户在浏览器直接访问http://xxx.com/parent/id时就会返回404,找不到这个页面。 想解决这个问题很简单,只需要在后台配置如果url匹配不到任何静态资源,就跳转到默认的index.html。 vue的router代码中设置默认跳转的页面是componentNotFound组件,404页面:
import Vue from 'vue'
import Router from 'vue-router'
import App from '@/App'
const home = () => import('@/pages/home')
const componentNotFound = () => import('@/pages/404')
Vue.use(Router)
export default new Router({
mode: 'history',
routes: [
{
path: '/',
component: App,
children: [
{
path: '',
name: 'home',
component: home
}
]
},
{
//404 页面
path: '*',
component: componentNotFound
}
]
})
nginx具体配置如下:
location / {
try_files $uri $uri/ /index.html;
}
最终效果:
2.webpack可配置接口代理 开发一个页面的时候,可能会遇到接口是从不同服务器提供的。如果想实现跨域访问,那么必须配置代理。 以vuejs-templates模板为例,Github地址:https://github.com/vuejs-templates/webpack。
本地想配置代理,具体实现如下: 1.找到项目里config的index.js,配置接口域名:
...
module.exports = {
dev:{
assetsSubDirectory: 'static',
assetsPublicPath: '/',
proxyTable: {
'/api/wechat': {
target:"http://zm01bs.zmredu.com", // 接口域名
changeOrigin:true // 是否跨域
}
}
}
...
2.通过axios和qs创建一个http实例,且与代理/api/wechat后缀保持一致
import axios from 'axios'
import qs from 'qs'
const weiService = axios.create({
timeout: 5000,
headers: {
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
'Api-Version': '1.3.0'
},
transformRequest: [function (data) {
data = qs.stringify(data)
return data
}]
})
})
export default {
getSecondShareKey(data) {
return weiService({
method: 'post',
url: '/api/wechat/get-signature',
data:data
})
}
}
3.在需要的页面使用该方法即可
...
api.getSecondShareKey()
...
本地代理接口的原理: 将含有localhost:8081/api/wechat/xxx匹配的接口后缀自动代理到http://zm01bs.zmredu.com/api/wechat/xxx。
上面只说到本地的接口代理,真正的项目部署上线也需要配置接口代理,当然又要用到代理神器nginx。 服务器想配置代理,具体实现如下:
location ~ ^/api/wechat {
proxy_pass http://zm01bs.zmredu.com;
}
想了解更多关于代理的知识,强烈推荐姐妹篇《谁说前端不需要懂 Nginx》 和 《谁说前端需要懂 Nginx》。
3.1.2 开发完成后,将webpack构建后的dist源代码推送到远程仓库GitLab。
这部分没什么特别需要讲的,但是一定要会安装Git,且熟练使用Git命令
。
下面总结了常用的Git命令,仅供参考:
分支类:
查看:
1.本地分支:
git branch
2.本地和远程分支:
git branch -a
-------------------
创建、切换、绑定:
1.创建分支:
git branch dev
2.切换分支:
git checkout develp
3.绑定分支:
git branch --set-upstream-to=origin/develop develop
4.创建切换分支:
git checkout -b dev
5.创建切换绑定分支:
git checkout -b develop origin/develop
-------------------
删除、合并:
1.删除分支:
git branch -d dev
2.合并某分支到当前分支(删除某分支):
git merge -d dev
3.合并某分支到当前分支(不删除某分支)
git merge --no-ff -m "commit info" dev
分支有怎么多操作,好复杂的感觉,那为什么需要分支呢?
因为创建、合并和删除分支非常快,所以Git鼓励你使用分支完成某个任务,合并后再删掉分支,这和直接在master分支上工作效果是一样的,但过程会更安全。
-------------------
流程操作类:
查看:
1.仓库当前状态:
git status
2.仓库修改状态:
git diff
3.查看分支合并图:
git log --graph
4.查看大致内容:
git log --oneline
5.查看详细内容:
git log --pretty=raw
-------------------
推送操作流程:
1.将所有修改添加到暂存区:
git add .
2.提交到存储库:
git commit -m "commit info"
3.推送到远程仓库:
git push
-------------------
其他操作:
1.更新远程仓库所有分支到本地合并:
git pull
2.回退到上个/上上个/上上上个版本:
git reset --hard HEAD / HEAD^ / HEAD~2
想系统的了解学习Git相关的更多内容,强烈推荐廖雪峰老师的《Git教程》。
至此,研发阶段所做的工作已经完毕。最后一句话总结这个阶段究竟做了什么事:
根据业务实现页面,通过webpack打包生成dist上线目录和源码通过git命令上传到远程仓库。
附上项目效果图: 本地打包提交上传gitlab:
gitlab远程仓库接收成功:
3.2 GitLab与Jenkins
第一次听到jenkins
这个词是从我媳妇儿那听到的~惊不惊喜、意不意外?哈哈,因为她是个测试工程师,也懂点自动化测试。于是乎jenkins给我第一印象就是它是做自动化测试用的
。
真正接触jenkins后,发现jenkins的强大之处不仅如此,还有更多更强大的功能,值得我去探索。
大致说下通过jenkins做了哪些上线前的准备工作:
- 创建了两个任务,一个任务是正式环境用的,一个任务是测试环境用的
- 关联了gitlab仓库,正式用的master分支,测试用的develop分支
- 下载gitlab仓库后,执行Shell脚本,实现压缩dist上线目录功能
3.2.1 安装Jenkins
jenkins官网是https://jenkins.io/,
1.安装jenkins
最简单的方式是使用war包安装,具体操作可根据官网的指示操作,
安装教程地址:https://jenkins.io/doc/pipeline/tour/getting-started/
安装时需注意一个地方,因为jenkins是基于Java开发的
,所以需要提前安装Java运行环境,且JDK版本不能低于1.8
。
3.2.2 运行Jenkins
将下载下来的war包放到任意目录,
执行java -jar jenkins.war --httpPort=12000
执行第一次的时候可能会很慢,因为需要安装jenkins。
安装成功后的会在当前用户根目录下生成一个.jenkins目录,如:/Users/username/.jenkins
,里面就是jenkins的运行程序。
Mac电脑可通过Command+Shift+.
实现对隐藏目录的显隐,安装成功后如下:
.jenkins目录下,通过名字能大概清楚其每个目录下的意义:
jobs 放创建的任务的
logs 放出错日志的
plugins 放jenkins插件的
users 放jenkins用户的
workspace 工作间,放从gitlab远程仓库下载的项目的
最终运行成功的控制台:
3.2.3 解锁jenkins
用浏览器打开http://localhost:12000/
运行,提示您输入管理员密码。按提示路径打开密码文件,输入密码即可。
3.2.4 安装常用插件,创建用户
这个就不截图了,一直默认点击下去就行,安装常用插件可能需要些时间,安装成功后,点击继续,进入创建用户页面。输入用户名、密码,点击完成会进入到jenkins的主页。
这里值得注意的地方是一定要有Git plugin
,因为可以通过该插件实现gitlab和jenkins的互通
。
3.2.5 创建任务、安装插件支持ssh协议
打击右边开始创建一个新任务
,输入一个任务名称,选择构建一个自由风格的软件项目,点击确定。
接着选择系统管理
找到管理插件
,进行插件的安装:
这里介绍jenkins第二大插件神器Publish Over SSH
,因为可以通过该插件实现jenkins和阿里服务器的互通
。
接着选择可选插件
,搜索Publish Over SSH,进行下载安装,最后重启jenkins。安装成功后,可在已安装
插件中找到:
3.2.6 配置任务
配置任务有两个功能点:
- 实现gitlab与jenkins的关联
- 执行压缩打包操作
找到新建好的任务名称,点击后面的小三角,选择配置
:
1.gitlab与jenkins的关联,找到源码管理
,选择Git,输入自己gitlab的地址到Repository URL
后面,默认选中的是master分支,点击应用配置成功。
2.执行压缩打包操作,目的是为上线到阿里服务器做准备
。将gitlab仓库下面的dist目录打包压缩成gz包。找到构建
,点击增加构建步骤
,选择执行shell
:
输入tar zcvf dist.tar.gz dist
命令,该命令的意思是将在workspace目录下的当前任务下的dist目录打包,生成dist.tar.gz包
,点击保存配置成功。
至此,gitlab与jenkins构建阶段所做的工作已经完毕,可以试着点击立即构建
按钮,发现在workspace目录下多了从gitlab仓库下载下来的代码,且仓库下面的dist目录,被打包成了dist.tar.gz。
最后一句话总结这个阶段究竟做了什么事:
jenkins关联gitlab仓库,压缩打包仓库下面的dist目录
3.3.Jenkins与阿里服务器
终于到最关键的一步,jenkins和阿里服务器进行关联。 大致说下做了哪些工作:
- 本地通过ssh协议登录到阿里服务器(免密码)
- 本地也能通过sftp协议登录到阿里服务器(免密码)
- 配置jenkins与阿里服务器关联,设置将gz包上传到阿里服务器的指定目录
- 上传gz包到阿里服务器后,执行shell脚本,解压gz包且把解压后的目录放到配置好的nginx目录下
- 配置正式环境和测试环境的nginx
jenkins与阿里服务器想实现关联,需执行以下两个命令成功,且不需要输密码:
ssh ww@101.132.111.255 -p 33333
sftp -P 33333 ww@101.132.111.255
3.3.1 通过ssh实现免密码登录
先通过cd ~/.ssh
找到.ssh
目录,然后open .
打开该目录,如果你本地拥有公钥和密钥的话,就不需要创建,直接使用即可。
1.如果没有,先执行创建:
ssh-keygen -t rsa -P ''
2.如果已经存在,则执行:
ssh-copy-id -i id_rsa.pub ww@101.132.111.255 -p 33333
ssh实现免密码登录是通过公钥认证
实现的。
公钥认证,是使用一对加密字符串,一个称为公钥(public key), 任何人都可以看到其内容,用于加密;另一个称为密钥(private key),只有拥有者才能看到,用于解密。
在使用jenkins自动构建并远程登录服务器进行发布应用的时候,需要使用ssh公钥认证来解决登录服务器的问题。
上面两个命令完成了哪些事情可自主查询相关文章,再这就不一一说明了,因为一时半会真说不清楚。
3.ssh登录阿里服务器,在.ssh
目录下会发现多了一个authorized_keys
文本,执行一下命令:
sudo chmod 644 authorized_keys //提高该文件权限
查看authorized_keys
文本,你会发现里面的内容,和你本地电脑上id_rsa.pub
公钥上的内容完全一致。
至此,你应该可以通过ssh协议实现免密码登录了。
3.3.2 通过sftp实现免密码登录(坑)
这块内容如果运气好,可能不会遇到,如果能执行sftp -P 33333 ww@101.132.111.255
成功,这节就可以忽略了。
我在配置jenkins的时候,明明能够通过ssh实现免密码登录,但是在配置SSH的时候,一直报以下错:
Failed to connect SFTP channel. Message [java.io.IOException: Pipe closed
解决方案如下:
1.ssh登录到阿里服务器,执行命令,全局搜索含有sftp-server的文件
,结果如下:
执行搜索
locate sftp-server
结果
/usr/lib/sftp-server
/usr/lib/openssh/sftp-server
/usr/share/doc/openssh-sftp-server
/usr/share/man/man8/sftp-server.8.gz
/var/cache/apt/archives/openssh-sftp-server_1%3a6.6p1-2ubuntu2.10_amd64.deb
/var/cache/apt/archives/openssh-sftp-server_1%3a7.2p2-4ubuntu2.4_amd64.deb
/var/lib/dpkg/info/openssh-sftp-server.list
/var/lib/dpkg/info/openssh-sftp-server.md5sums
2.执行命令查看sshd_config
文本,结果如下:
执行查看
cat -n /etc/ssh/sshd_config
结果
....
Subsystem sftp /usr/libexec/openssh/sftp-server
....
最后发现全局搜索的结果里面竟然没有任何目录与sshd_config
文本里面的目录相对应,难怪sftp命令执行不了。
3.将sshd_config
文本里面的内容替换:
# Subsystem sftp /usr/libexec/openssh/sftp-server
Subsystem sftp /usr/lib/openssh/sftp-server
4.最后重启ssh,更新ssh配置:
sudo service ssh restart
至此,你应该可以通过sftp协议实现免密码登录了。
3.3.3 Jenkins与阿里服务器关联
1.配置全局系统设置SSH
打开jenkins控制台,选择系统管理
找到系统设置
,点击进入:
进入后,找到Publish over SSH
模块,配置如下:
这一步是最最关键的,如果你按照我之前的说法能够执行那两个命令的话,相信你在配置SSH这一步时肯定不会出错。
配置内容讲解:
Jenkins SSH Key
Passphrase 生成ssh公钥和密钥时设置的密码,没有设置就不填
Path to key 本地电脑密钥的绝对目录
Key 密钥的Key,上面已经填了,不填
SSH Servers
Name 给这个服务器取个名字,随便写
Hostname 远程主机IP
Username 登录阿里的用户名
Remote Directory 链接后指定阿里远程的根目录
Use password authentication, or use a different key 已通过ssh实现免密码登录,不勾选
Jump host 跳板机的IP 没有就不填
Port 通过ssh登录的端口号,默认22
Proxy type
... 没代理 就不填
点击右下角的Test Configuration
按钮,如果出来Success
,那么恭喜你,你的jenkins与你的阿里服务器已经成功打通了。
2.SSH与任务关联
打开jenkins控制台,找到之前创建的任务名称,点击后面的小三角,选择配置
。
找到构建后操作
,点击增加构建后操作步骤
,选择Send build artifacts over SSH
。
配置内容讲解:
SSH Server
Name 之前在全局取的服务器名字,直接选择就行
Transfer Set
Source files 上传到服务器的压缩包类型,我上传的是gz包,所以填**/*.tar.gz
Remove prefix 移除目录(只能指定Source files中的目录)
Remote directory 远程目录(会继承全局系统的根目录)
Exec command 上传后服务器需要执行的命令
重点解释下Remote directory
和Exec command
两个输入框的内容。
- Remote directory
填入
'zhangmen/master/'yyyyMMdd_HH
的意义: 需特别注意master
和yyyyMMdd_HH
。 a.将打包后的dist.tar.gz包放到阿里远程目录的/home/ww/zhangmen/master/yyyyMMdd_HH
中 b.yyyyMMdd_HH是动态值,通过获取上传时服务器时间年月日时,生成的目录 c.一定要点击高级
,然后勾选上Remote directory is a date format
,表示远程目录是需要格式化时间的。
可能有人会问了,为什么配置个远程目录要这么麻烦,还要用到动态获取日期来生成目录,还有就是master有什么好注意的?
我来说说这里面的缘由:
在配置服务器目录的时候,一定要想到扩展性和维护性。
首先公司里面可能会有许多产品和项目,那么肯定需要进行整理和分类。
一个任务只能对应一个项目,一个项目可能有正式环境和测试环境两个。
那么服务器下面的目录结构大致应该是这样的:
...
项目一
正式部署
测试部署
项目二
正式部署
测试部署
项目三
正式部署
测试部署
...
这样的结构看上去还OK,但是每个项目可能会进行多次迭代,如果有问题,想进行版本回退该怎么办?
于是乎目录结构应该是这样的:
...
项目一/
正式/
正式部署
正式目录/包
正式目录/包
测试/
测试部署
测试目录/包
测试目录/包
项目二/
正式/
正式部署
正式目录/包
正式目录/包
测试/
测试部署
测试目录/包
测试目录/包
...
以我的目录结构为例:
正式环境和测试环境:
zhangmen/ 项目名
master/ 正式
dist 正式部署
20180703_00/dist.tar.gz 上传的gz压缩包目录
20180703_16/dist.tar.gz
20180706_19/dist.tar.gz
develop/ 测试
dist 测试部署
20180702_19/dist.tar.gz 上传的gz压缩包目录
20180703_00/dist.tar.gz
20180706_15/dist.tar.gz
这样做的话,如果正式部署的时候线上出了问题,比如20180706_19发布的包有问题,想回退的话,直接解压之前20180703_16的包,替换dist即可。
- Exec command
该输入框内可以输入一条命令,比如实现解压包之类的操作等。
但是我这边上传的gz压缩包目录是动态的(按服务器年月日_时的形式),所以想解压gz压缩包的时候也需要
先获取当前服务器的年月日时
,找到该目录,才能实现解压。 命令:sh /home/ww/crontab/zxvf_git.sh
表示构建成功后,执行阿里服务器下/home/ww/crontab/zxvf_git.sh里面的程序
。
在zxvf_git.sh
文件里面,保存的内容如下:
dateformat="`date +%Y%m%d_%H`"
tar zxvf "/home/ww/zhangmen/master/$dateformat/dist.tar.gz" -C "/home/ww/zhangmen/master"
zxvf_git.sh里面的程序实现了解压刚刚上传成功的dist.tar.gz包到master目录
这个功能。
至此,jenkins构建内容就全部结束了,试试立即构建
看下效果吧,最后一句话总结这个阶段究竟做了什么事:
jenkins配置全局SSH,通过SSH配置相应的任务,任务命令需与阿里服务器配合
3.4 Nginx
终于到最后一步了,回忆一下上面jenkins的配置过程,确实不容易。构建那么久,必须要看到效果才算完美,因此需要用到nginx来代理显示。nginx是一个高性能的HTTP代理服务器,基本上所有公司都会用到它。nginx主要作用:接口转发、反向代理
。
上面提到过两个重要目录:/home/ww/zhangmen/master/dist
和/home/ww/zhangmen/develop/dist
。
一个是正式环境需要访问的页面目录,另一个是测试环境需要访问的页面目录。
那如何才能实现访问http://xcx.xjshen.cn
和http://test.xjshen.cn
来对应访问这两个目录呢?
准备工作:登录阿里控制台,在域名下的域名解析
中添加xcx
和test
的解析记录:
1.记录添加成功后,登上阿里服务器,找到conf.d
目录:
cd /etc/nginx/conf.d
2.conf.d
目录下新增两个配置文件:
创建xcx-xjshen-cn-80.conf
文件,添加如下内容:
server {
listen 80;
server_name xcx.xjshen.cn;
location ~ ^/api/wechat {
proxy_pass http://zm01bs.zmredu.com;
}
location ~^/api/share {
proxy_pass http://appapi.zmlearn.com;
}
location ~ ^/parentsApi[/\w*]*$ {
proxy_pass http://app-parent-h5.zhangmen.com;
}
location / {
root /home/ww/zhangmen/master/dist;
index index.html;
try_files $uri $uri/ /index.html;
}
}
创建test-xjshen-cn-80.conf
文件,添加如下内容:
server {
listen 80;
server_name test.xjshen.cn;
location ~ ^/api/wechat {
proxy_pass http://zm01bs.zmredu.com;
}
location ~^/api/share {
proxy_pass http://appapi-test.zmlearn.com;
}
location ~ ^/parentsApi[/\w*]*$ {
proxy_pass http://app-parent-h5-test.zhangmen.com;
}
location / {
root /home/ww/zhangmen/develop/dist;
index index.html;
try_files $uri $uri/ /index.html;
}
}
具体里面配置是啥意思,可自主查询相关文章,再这就不一一说明了。
3.测试下nginx配置是否正确,重启nginx
sudo nginx -t
sudo service nginx reload
如果有遇到nginx 403
这个问题,可参考以下操作:
1.执行命令:ps aux|grep nginx
查看nginx进程的运行状态
2.输出如下:
www-data 17111 0.0 0.0 118184 3124 ? S 23:07 0:00 nginx: worker process
www-data 17112 0.0 0.0 118184 3124 ? S 23:07 0:00 nginx: worker process
ww 17165 0.0 0.0 14224 960 pts/0 S+ 23:14 0:00 grep --color=auto nginx
发现行程拥有者竟然不一致www-data
和ww
。
3.编辑nginx/nginx.conf
配置文件,保存:
sudo vim /etc/nginx/nginx.conf
找到 user www-data;
将其替换成 user ww;
4.重启nginx,再次执行ps aux|grep nginx
命令查看:
sudo nginx reload
ps aux|grep nginx
输出如下:
ww 17194 0.0 0.1 117756 5152 ? S 23:17 0:00 nginx: worker process
ww 17195 0.0 0.0 117756 3116 ? S 23:17 0:00 nginx: worker process
ww 17213 0.0 0.0 14224 1088 pts/0 S+ 23:20 0:00 grep --color=auto nginx
全部换成同一个行程拥有者,403问题解决。
至此,nginx配置阶段所做的工作已经完毕。最后一句话总结这个阶段究竟做了什么事:
将正式目录成功绑定到正式域名,将测试目录成功绑定到测试域名
上面详细讲解了配置正式部署任务
的所有流程,可以自行再新建一个测试部署任务
,从gitlab与jenkins那部分内容开始,重新创建任务,配置任务即可...
最后你通过本地jenkins,能成功实现自动打包发布上线啦!一个按钮就搞定所有!