前端高级进阶:前端部署的发展历程

  1. 前端高级进阶:javascript 代码是如何被压缩
  2. 前端高级进阶:如何更好地优化打包资源
  3. 前端高级进阶:网站的缓存控制策略最佳实践及注意事项
  4. 前端高级进阶:在生产环境中使你的 npm i 速度提高 50%
  5. 前端高级进阶:使用 docker 高效部署你的前端应用
  6. 前端高级进阶:CICD 下的前端多特性分支环境的部署
  7. 前端高级进阶:前端部署的发展历程

更多文章: 前端工程化系列

我在 github 上新建了一个仓库 每日一题,天天一道面试题,欢迎交流。javascript


前端一提及刀耕火种,那确定紧随着前端工程化这一话题。随着 react/vue/angulares6+webpackbabeltypescript 以及 node 的发展,前端已经在逐渐替代过去 scriptcdn 开发的方式了,掀起了工程化这一大浪潮。得益于工程化的发展与开源社区的良好生态,前端应用的可用性与效率获得了很大提升。css

前端之前是刀耕火种,那前端应用部署在之前也是刀耕火种。那前端应用部署的发展得益于什么,随前端工程化带来的副产品?html

这只是一部分,而更重要的缘由是 devops 的崛起。前端

为了更清晰地理解前端部署的发展史,了解部署时运维和前端(或者更普遍地说,业务开发人员)的职责划分,当每次前端部署发生改变时,能够思考两个问题vue

  1. 缓存,前端应用中http 的 response header 由谁来配?得益于工程化发展,能够对打包后获得带有 hash 值的文件能够作永久缓存
  2. 跨域,/api 的代理配置由谁来配?在开发环境前端能够开个小服务,启用 webpack-dev-server 配置跨域,那生产环境呢

这两个问题都是前端面试时的高频问题,但话语权是否掌握在前端手里java

时间来到 React 刚刚发展起来的这一年,这时已经使用 React 开发应用,使用 webpack 来打包。可是前端部署,还是刀耕火种node

本篇文章要求你有必定的 Docker,DevOps 以及前端工程化的知识储备。若是没有的话,本系列文章以及 我的服务器运维指南 中的 Docker 部分会对你有所帮助。react

刀耕火种

一台跳板机webpack

一台生产环境服务器nginx

一份部署脚本

前端调着他的 webpack,开心地给运维发了部署邮件并附了一份部署脚本,想着第一次不用套后端的模板,第一次前端能够独立部署。想着本身基础盘进一步扩大,前端不由开心地笑了

运维照着着前端发过来的部署邮件,一遍又一遍地拉着代码,改着配置,写着 try_files, 配着 proxy_pass

这时候,前端静态文件由 nginx 托管,nginx 配置文件大体长这个样子

server {
  listen 80;
  server_name shanyue.tech;

  location / {
    # 避免非root路径404
    try_files $uri $uri/ /index.html;
  }

  # 解决跨域
  location /api {
    proxy_pass http://api.shanyue.tech;
  }

  # 为带 hash 值的文件配置永久缓存
  location ~* \.(?:css|js)$ {
      try_files $uri =404;
      expires 1y;
      add_header Cache-Control "public";
  }

  location ~ ^.+\..+$ {
      try_files $uri =404;
  }
}
复制代码

不过...常常有时候跑不起来

运维抱怨着前端的部署脚本没有标好 node 版本,前端嚷嚷着测试环境没问题

这个时候运维须要费不少心力放在部署上,甚至测试环境的部署上,前端也要费不少心力放在运维如何部署上。这个时候因为怕影响线上环境,上线每每选择在深夜,前端和运维身心俱疲

不过向来如此

鲁迅说,向来如此,那便对么。

这个时候,不管跨域的配置仍是缓存的配置,都是运维来管理,运维不懂前端。但配置方式倒是前端在提供,而前端并不熟悉 nginx

使用 docker 构建镜像

docker 的引进,很大程度地解决了部署脚本跑不了这个大BUG。dockerfile 即部署脚本,部署脚本即 dockerfile。这也很大程度缓解了前端与运维的摩擦,毕竟前端愈来愈靠谱了,至少部署脚本没有问题了 (笑

这时候,前端再也不提供静态资源,而是提供服务,一个 http 服务

前端写的 dockerfile 大体长这个样子

FROM node:alpine

# 表明生产环境
ENV PROJECT_ENV production
# 许多 package 会根据此环境变量,作出不一样的行为
# 另外,在 webpack 中打包也会根据此环境变量作出优化,可是 create-react-app 在打包时会写死该环境变量
ENV NODE_ENV production
WORKDIR /code ADD . /code RUN npm install && npm run build && npm install -g http-server EXPOSE 80

CMD http-server ./public -p 80 复制代码

单单有 dockerfile 也跑不起来,另外前端也开始维护一个 docker-compose.yaml,交给运维执行命令 docker-compose up -d 启动前端应用。前端第一次写 dockerfiledocker-compose.yaml,在部署流程中扮演的角色愈来愈重要。想着本身基础盘进一步扩大,前端又不由开心地笑了

version: "3"
services:
 shici:
 build: .
 expose:
 - 80
复制代码

运维的 nginx 配置文件大体长这个样子

server {
  listen 80;
  server_name shanyue.tech;

  location / {
    proxy_pass http://static.shanyue.tech;
  }

  location /api {
    proxy_pass http://api.shanyue.tech;
  }
}
复制代码

运维除了配置 nginx 以外,还要执行一个命令: docker-compose up -d

这时候再思考文章最前面两个问题

  1. 缓存,因为从静态文件转换为服务,缓存开始交由前端控制 (可是镜像中的 http-server 不太适合作这件事情)
  2. 跨域,跨域仍由运维在 nginx 中配置

前端能够作他应该作的事情中的一部分了,这是一件使人开心的事情

固然,前端对于 dockerfile 的改进也是一个慢慢演进的过程,那这个时候镜像有什么问题呢?

  1. 构建镜像体积过大
  2. 构建镜像时间过长

使用多阶段构建优化镜像

这中间其实经历了很多坎坷,其中过程如何,详见个人另外一篇文章: 如何使用 docker 部署前端应用

其中主要的优化也是在上述所提到的两个方面

  1. 构建镜像体积由 1G+ 变为 10M+
  2. 构建镜像时间由 5min+ 变为 1min (视项目复杂程度,大部分时间在构建时间与上传静态资源时间)
FROM node:alpine as builder

ENV PROJECT_ENV production
ENV NODE_ENV production

WORKDIR /code 
ADD package.json /code RUN npm install --production 
ADD . /code 
# npm run uploadCdn 是把静态资源上传至 oss 上的脚本文件,未来会使用 cdn 对 oss 加速
RUN npm run build && npm run uploadCdn 
# 选择更小体积的基础镜像
FROM nginx:alpine
COPY --from=builder code/public/index.html code/public/favicon.ico /usr/share/nginx/html/ COPY --from=builder code/public/static /usr/share/nginx/html/static 复制代码

那它怎么作的

  1. ADD package.json /code, 再 npm install --production 以后 Add 全部文件。充分利用镜像缓存,减小构建时间
  2. 多阶段构建,大大减少镜像体积

另外还能够有一些小优化,如

  • npm cache 的基础镜像或者 npm 私有仓库,减小 npm install 时间,减少构建时间
  • npm install --production 只装必要的包

前端看着本身优化的 dockerfile,想着前几天还被运维吵,说什么磁盘一半的空间都被前端的镜像给占了,想着本身节省了前端镜像几个数量级的体积,为公司好像省了很多服务器的开销,想着本身的基础盘进一步扩大,又不由开心的笑了

这时候再思考文章最前面两个问题

  1. 缓存,缓存由前端控制,缓存在oss上设置,将会使用 cdn 对 oss 加速。此时缓存由前端写脚本控制
  2. 跨域,跨域仍由运维在 nginx 中配置

CI/CD 与 gitlab

此时前端成就感爆棚,运维呢?运维还在一遍一遍地上线,重复着一遍又一遍的三个动做用来部署

  1. 拉代码
  2. docker-compose up -d
  3. 重启 nginx

运维以为不再能这么下去了,因而他引进了 CI: 与现有代码仓库 gitlab 配套的 gitlab ci

  • CIContinuous Integration,持续集成
  • CDContinuous Delivery,持续交付

重要的不是 CI/CD 是什么,重要的是如今运维不用跟着业务上线走了,不须要一直盯着前端部署了。这些都是 CI/CD 的事情了,它被用来作自动化部署。上述提到的三件事交给了 CI/CD

.gitlab-ci.ymlgitlab 的 CI 配置文件,它大概长这个样子

deploy:
 stage: deploy
 only:
 - master
 script:
 - docker-compose up --build -d
 tags:
 - shell
复制代码

CI/CD 不只仅更解放了业务项目的部署,也在交付以前大大增强了业务代码的质量,它能够用来 linttestpackage 安全检查,甚至多特性多环境部署,我将会在我之后的文章写这部分事情

个人一个服务器渲染项目 shfshanyue/shici 之前在个人服务器中就是以 docker/docker-compose/gitlab-ci 的方式部署,有兴趣的能够看看它的配置文件

若是你有我的服务器的话,也建议你作一个本身感兴趣的前端应用和配套的后端接口服务,而且配套 CI/CD 把它部署在本身的本身服务器上

而你若是但愿结合 githubCI/CD,那能够试一试 github + github action

另外,也能够试试 drone.ci,如何部署能够参考我之前的文章: github 上持续集成方案 drone 的简介及部署

使用 kubernetes 部署

随着业务愈来愈大,镜像愈来愈多,docker-compose 已经不太能应付,kubernetes 应时而出。这时服务器也从1台变成了多台,多台服务器就会有分布式问题

一门新技术的出现,在解决之前问题的同时也会引进复杂性。

k8s 部署的好处很明显: 健康检查,滚动升级,弹性扩容,快速回滚,资源限制,完善的监控等等

那如今遇到的新问题是什么?

构建镜像的服务器,提供容器服务的服务器,作持续集成的服务器是一台!

须要一个私有的镜像仓库,这是运维的事情,harbor 很快就被运维搭建好了,可是对于前端部署来讲,复杂性又提升了

先来看看之前的流程:

  1. 前端配置 dockerfiledocker-compose
  2. 生产环境服务器的 CI runner 拉代码(能够看作之前的运维),docker-compose up -d 启动服务。而后再重启 nginx,作反向代理,对外提供服务

之前的流程有一个问题: 构建镜像的服务器,提供容器服务的服务器,作持续集成的服务器是一台!,因此须要一个私有的镜像仓库,一个可以访问 k8s 集群的持续集成服务器

流程改进以后结合 k8s 的流程以下

  1. 前端配置 dockerfile,构建镜像,推到镜像仓库
  2. 运维为前端应用配置 k8s 的资源配置文件,kubectl apply -f 时会从新拉取镜像,部署资源

运维问前端,需不须要再扩大下你的基础盘,写一写前端的 k8s 资源配置文件,而且列了几篇文章

前端看了看后端十几个 k8s 配置文件以后,摇摇头说算了算了

这个时候,gitlab-ci.yaml 差很少长这个样子,配置文件的权限由运维一人管理

deploy:
 stage: deploy
 only:
 - master
 script:
 - docker build -t harbor.shanyue.tech/fe/shanyue
 - docker push harbor.shanyue.tech/fe/shanyue
 - kubectl apply -f https://k8s-config.default.svc.cluster.local/shanyue.yaml
 tags:
 - shell
复制代码

这时候再思考文章最前面两个问题

  1. 缓存,缓存由前端控制
  2. 跨域,跨域仍由运维控制,在后端 k8s 资源的配置文件中控制 Ingress

使用 helm 部署

这时前端与运维已不太往来,除了偶尔新起项目须要运维帮个忙之外

但好景不长,忽然有一天,前端发现本身连个环境变量都无法传!因而常常找运维修改配置文件,运维也不胜其烦

因而有了 helm,若是用一句话解释它,那它就是一个带有模板功能的 k8s 资源配置文件。做为前端,你只须要填参数。更多详细的内容能够参考我之前的文章 使用 helm 部署 k8s 资源

假如咱们使用 bitnami/nginx 做为 helm chart,前端可能写的配置文件长这个样子

image:
 registry: harbor.shanyue.tech
 repository: fe/shanyue
 tag: 8a9ac0

ingress:
 enabled: true
 hosts:
 - name: shanyue.tech
 path: /

 tls:
 - hosts:
 - shanyue.tech
 secretName: shanyue-tls

    # livenessProbe:
    # httpGet:
    # path: /
    # port: http
    # initialDelaySeconds: 30
    # timeoutSeconds: 5
    # failureThreshold: 6
    #
    # readinessProbe:
    # httpGet:
    # path: /
    # port: http
    # initialDelaySeconds: 5
    # timeoutSeconds: 3
    # periodSeconds: 5
复制代码

这时候再思考文章最前面两个问题

  1. 缓存,缓存由前端控制
  2. 跨域,跨域由后端控制,配置在后端 Chart 的配置文件 values.yaml

到了这时前端和运维的职责所在呢?

前端须要作的事情有:

  1. 写前端构建的 dockerfile,这只是一次性的工做,并且有了参考
  2. 使用 helm 部署时指定参数

那运维要作的事情呢

  1. 提供一个供全部前端项目使用的 helm chart,甚至不用提供,若是运维比较懒那就就使用 bitnami/nginx 吧。也是一次性工做
  2. 提供一个基于 helm 的工具,禁止业务过多的权限,甚至不用提供,若是运维比较懒那就直接使用 helm

这时前端能够关注于本身的业务,运维能够关注于本身的云原生,职责划分从未这般清楚

统一前端部署平台

后来运维以为前端应用的本质是一堆静态文件,较为单一,容易统一化,来避免各个前端镜像质量的良莠不齐。因而运维准备了一个统一的 node 基础镜像,作了一个前端统一部署平台,而这个平台能够作什么呢

  1. CI/CD: 当你 push 代码到仓库的特定分支会自动部署
  2. http headers: 你能够定制资源的 http header,从而能够作缓存优化
  3. http redirect/rewrite: 若是一个 nginx,这样能够配置 /api,解决跨域问题
  4. hostname: 你能够设置域名
  5. CDN: 把你的静态资源推到 CDN
  6. https: 为你准备证书
  7. Prerender: 结合 SPA,作预渲染

前端不再须要构建镜像,上传 CDN 了,他只须要写一份配置文件就能够了,大体长这个样子

build:
 command: npm run build
 dist: /dist

hosts:
- name: shanyue.tech
 path: /

headers:
- location: /*
 values:
 - cache-control: max-age=7200
- location: assets/*
 values:
 - cache-control: max-age=31536000

redirects:
- from : /api
 to: https://api.shanyue.tech
 status: 200
复制代码

此时,前端只须要写一份配置文件,就能够配置缓存,配置 proxy,作应该属于前端作的一切,而运维也不再须要操心前端部署的事情了

前端看着本身刚刚写好的配置文件,怅然若失的样子...

不过通常只有大厂会有这么完善的前端部署平台,若是你对它有兴趣,你能够尝试下 netlify,能够参考个人文章: 使用 netlify 部署你的前端应用

服务端渲染与后端部署

大部分前端应用本质上是静态资源,剩下的少部分就是服务端渲染了,服务端渲染的本质上是一个后端服务,它的部署能够视为后端部署

后端部署的状况更为复杂,好比

  1. 配置服务,后端须要访问敏感数据,但又不能把敏感数据放在代码仓库。你能够在 environment variablesconsul 或者 k8s configmap 中维护
  2. 上下链路服务,你须要依赖数据库,上游服务
  3. 访问控制,限制 IP,黑白名单
  4. RateLimit
  5. 等等

我将在之后的文章分享如何在 k8s 中部署一个后端

小结

随着 devops 的发展,前端部署愈来愈简单,可控性也愈来愈高,建议全部人都稍微学习一下 devops 的东西。

道阻且长,行则将至。

与我交流

扫码添加个人机器人微信,将会自动(自动拉人程序正在研发中)把你拉入前端高级进阶学习群

推荐一个关于大厂招聘的公众号【互联网大厂招聘】,做者将在公众号里持续推送各个大厂的招聘职位及要求,并与大厂面试官以及招聘负责人直通,感兴趣的能够直接与负责人交流。

另外,做者也将持续推送优质的大厂面试经验,各类大厂独家面试题以及优秀文章分享,不限于前端,后端,运维和系统设计。

我在 github 上新建了一个仓库 每日一题,天天一道面试题,欢迎交流。

更多大厂招聘,面试面经,技能要求,请关注公众号【互联网大厂招聘】
相关文章
相关标签/搜索