前端高級進階:前端部署的發展歷程

  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 上新建了一個倉庫 每日一題,天天一道面試題,歡迎交流。

更多大廠招聘,面試面經,技能要求,請關注公衆號【互聯網大廠招聘】
相關文章
相關標籤/搜索