vue项目实现CDN动静分离及自动化部署
前世今生
1410是刚毕业那会做一个毕业照展示网站项目。
早期使用的是Vue.js作为前端项目,Java作为后端项目。
中间将Python写的服务端替换了Java后端。
最后将Golang写的服务端替换了Python后端。
近期又做了一些前端方面的改造:
- 使用authelia作为Nginx层的SSO网关,设置为登录可访问(不能实现全站CDN的原因是需要NGINX层)
- 照片使用腾讯云CDN访问,并且为加快访问速度还使用了腾讯云的万象图片处理
- 抛弃后端接口,直接使用json存储照片列表
- 实现前端项目动静分离,引入CDN加快访问速度
- 使用coding的持续集成实现自动化部署
以后再也不用操心改完代码还要做一堆的操作让代码上线了,网站访问速度和安全性都得到了提升。
升级打怪过程
这篇文章就主要介绍一下将vue项目build后实现动静分离和自动化部署过程,以及遇到的一些坑点。
以下是1410前端项目的项目结构:
| 1 | |-- public //存放不可变静态资源 | 
改造vue项目实现动静分离
如我上述的结构,改造vue项目非常简单,主要分为三步:
- 在根目录中创建 - .env.deploy文件,然后写入- 1 
 2- NODE_ENV=production 
 DEPLOY=online
- 修改 - vue.config.js,将一下内容写入:- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11- // 根据自定义的变量来进行内容设置,将这块放在整个js文件的最上面即可 
 let BASE_URL = '/'
 switch(process.env.DEPLOY) {
 case 'online':
 BASE_URL = 'https://cdn.songbo.fun/'
 break
 default:
 BASE_URL = '/'
 }
 然后在module.exports中增加一行,注意','
 publicPath: BASE_URL
- 在 - package.json中的scripts增加一行- "deploy": "vue-cli-service build --mode deploy"此处注意**,**
完成已上三步后就改造完成了,如果需要打包正常的项目使用npm run build 如果需要动静分离,使用npm run deploy 即可。
最终效果如下,在dist/index.html,对应的css地址就换成了如下的内容:
| 1 | <link href=http://cdn.songbo.fun/static/css/app.887c93b2.css rel=preload as=style> | 
** 注意 这一步也没有遇到什么坑点,很快就完成了项目的动静分离。
实现自动化部署
上一步已经实现了打包的过程,初步具备了前端站点动静分离的条件,最终生成的dist目录结构如下:
| 1 | |-- index.html // 需要copy到docker镜像中 | 
自动化部署总体流程比较简单,分为如下几步:
- 选择一个devops服务提供商,我目前主要用两个:coding的持续集成以及github的action,配置起来都比较简单。
- 选定Docker镜像并且准备好对应的配置,我这里选用的是nginx的docker镜像,准备了config/nginx.conf文件和Dockerfile文件,对应的文件存放位置可以在上文中看到,文件具体内容如下:
 nginx.conf:Dockerfile: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# nginx.conf 
 user nginx;
 worker_processes 1;
 error_log /var/log/nginx/error.log warn;
 pid /var/run/nginx.pid;
 events {
 worker_connections 1024;
 }
 http {
 map $http_x_forwarded_for $clientRealIp {
 "" $remote_addr;
 ~^(?P<firstAddr>[0-9\.]+),?.*$ $firstAddr;
 }
 include /etc/nginx/mime.types;
 default_type application/octet-stream;
 log_format main '$remote_addr - $remote_user [$time_local] "$request" '
 '$status $body_bytes_sent "$http_referer" '
 '"$http_user_agent" "$http_x_forwarded_for"';
 access_log /var/log/nginx/access.log main;
 sendfile on;
 tcp_nopush on;
 gzip on;
 client_max_body_size 100m;
 client_body_buffer_size 10m;
 proxy_connect_timeout 600s;
 proxy_send_timeout 600s;
 proxy_read_timeout 600s;
 send_timeout 600s;
 proxy_request_buffering off;
 proxy_buffering off;
 server {
 listen 80;
 server_name localhost;
 charset utf-8;
 root /usr/share/nginx/html;
 location / {
 try_files $uri $uri/ /index.html;
 }
 location ~* \.(?:jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ {
 expires 1y;
 access_log off;
 add_header Cache-Control "public";
 }
 location ~* \.(?:css|js)$ {
 try_files $uri =404;
 expires 1y;
 access_log off;
 add_header Cache-Control "public";
 }
 location ~ ^.+\..+$ {
 try_files $uri =404;
 }
 error_page 500 502 503 504 /50x.html;
 location = /50x.html {
 root /usr/share/nginx/html;
 }
 }
 }1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11FROM nginx:1.17.9-alpine 
 ENV TZ=Asia/Shanghai
 WORKDIR /usr/share/nginx/html
 COPY ./config/nginx.conf /etc/nginx/nginx.conf
 COPY ./dist /usr/share/nginx/html
 EXPOSE 80
- 将对应的文件打包成镜像,然后部署到宿主机上,我使用的是coding的持续集成,对应的jenkinsfile如下: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
 90pipeline { 
 agent any
 stages {
 stage('检出') {
 steps {
 checkout([$class: 'GitSCM', branches: [[name: env.GIT_BUILD_REF]],
 userRemoteConfigs: [[url: env.GIT_REPO_URL, credentialsId: env.CREDENTIALS_ID]]])
 }
 }
 stage('构建') {
 steps {
 echo '构建中...'
 sh 'npm install'
 sh 'npm run deploy'
 sh 'docker build -t ${registry}/${projectName}:${GIT_COMMIT} .'
 sh "docker login --username='${username}' --password='${password}' '${registry}'"
 sh 'docker push ${registry}/${projectName}:${GIT_COMMIT}'
 echo '构建完成.'
 }
 }
 stage("部署到远端服务") {
 steps {
 script {
 def remoteConfig = [:]
 remoteConfig.name = "my-remote-server"
 remoteConfig.host = "10.0.0.1"
 remoteConfig.port = 22
 remoteConfig.allowAnyHosts = true
 withCredentials([
 sshUserPrivateKey(
 credentialsId: "${remote_cred}",
 keyFileVariable: 'id_rsa'
 ),
 ]) {
 // SSH 登陆用户名
 remoteConfig.user = "root"
 // SSH 私钥文件地址
 remoteConfig.identityFile = id_rsa
 // 请确保远端环境中有 Docker 环境
 sshCommand(
 remote: remoteConfig,
 command: "docker login -u ${username} -p ${password} ${registry}",
 sudo: true,
 )
 sshCommand(
 remote: remoteConfig,
 command: "docker rm -f ${containerName} | true",
 sudo: true,
 )
 // DOCKER_IMAGE_VERSION 中涉及到 GIT_LOCAL_BRANCH / GIT_TAG / GIT_COMMIT 的环境变量的使用
 // 需要在本地完成拼接后,再传入到远端服务器中使用
 DOCKER_IMAGE_URL = sh(
 script: "echo ${registry}/${projectName}:${GIT_COMMIT}",
 returnStdout: true
 )
 sshCommand(
 remote: remoteConfig,
 command: "docker run -d -p 2111:80 --name ${containerName} ${DOCKER_IMAGE_URL}",
 sudo: true,
 )
 echo "部署成功,请到 https://1410.xxxx.com 预览效果"
 }
 }
 }
 }
 stage('上传CDN') {
 steps {
 echo '构建中...'
 sh 'node index.js'
 echo '上传完成'
 }
 }
 }
 environment {
 registry = ''
 username = ''
 password = ''
 projectName = '1410'
 containerName = '1410'
 remote_cred = ''
 }
 }
- 为防止docker镜像启动不成功,导致原有的网站不可访问,所以将对应的dist中的文件上传到CDN中作为最后一步,这样可以保证服务启动后,访问的是最新的站点,将dist文件上传到cos上我使用的是我前期写的一个开源项目alidaodao-cos-uploader,具体的步骤可以访问项目后进行配置。
到此整个自动化部署的过程也结束了,可以看看对应的运行步骤截图:
遇到的问题以及解决方案
coding持续集成部署到宿主机时始终报错:invalid privatekey: [B@51d78dde
最终查到该问题是由于我使用的连接到宿主机的SSH密钥的版本过新导致的,我使用的密钥如下:
| 1 | -----BEGIN OPENSSH PRIVATE KEY----- | 
但是coding使用的Jenkins只支持如下密钥:
| 1 | -----BEGIN RSA PRIVATE KEY----- | 
问题找到,解决就简单了,将对应的OPENSSH的密钥改成RSA的密钥即可:ssh-keygen -p -m PEM -f 你的密钥地址,然后再转化的密钥存入对应凭据中就解决了。
总结
对于服务端开发者,自动化早已深入人心,它能帮你节省大量的时间和精力,如何用好自动化技术也是我们的挑战,欢迎与我一起交流。
参考文献:
