mirror of
https://github.com/zhwei820/learn.lianglianglee.com.git
synced 2025-09-17 08:46:40 +08:00
373 lines
21 KiB
HTML
373 lines
21 KiB
HTML
<!DOCTYPE html>
|
||
<!-- saved from url=(0046)https://kaiiiz.github.io/hexo-theme-book-demo/ -->
|
||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||
<head>
|
||
<head>
|
||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
|
||
<link rel="icon" href="/static/favicon.png">
|
||
<title>17 部署初探:为什么一般不在开发环境下部署代码?.md.html</title>
|
||
<!-- Spectre.css framework -->
|
||
<link rel="stylesheet" href="/static/index.css">
|
||
<!-- theme css & js -->
|
||
<meta name="generator" content="Hexo 4.2.0">
|
||
</head>
|
||
|
||
<body>
|
||
|
||
<div class="book-container">
|
||
<div class="book-sidebar">
|
||
<div class="book-brand">
|
||
<a href="/">
|
||
<img src="/static/favicon.png">
|
||
<span>技术文章摘抄</span>
|
||
</a>
|
||
</div>
|
||
<div class="book-menu uncollapsible">
|
||
<ul class="uncollapsible">
|
||
<li><a href="/" class="current-tab">首页</a></li>
|
||
</ul>
|
||
|
||
<ul class="uncollapsible">
|
||
<li><a href="../">上一级</a></li>
|
||
</ul>
|
||
|
||
<ul class="uncollapsible">
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/前端工程化精讲-完/00 开篇词 建立上帝视角,全面系统掌握前端效率工程化.md.html">00 开篇词 建立上帝视角,全面系统掌握前端效率工程化.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/前端工程化精讲-完/01 项目基石:前端脚手架工具探秘.md.html">01 项目基石:前端脚手架工具探秘.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/前端工程化精讲-完/02 界面调试:热更新技术如何开着飞机修引擎?.md.html">02 界面调试:热更新技术如何开着飞机修引擎?.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/前端工程化精讲-完/03 构建提速:如何正确使用 SourceMap?.md.html">03 构建提速:如何正确使用 SourceMap?.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/前端工程化精讲-完/04 接口调试:Mock 工具如何快速进行接口调试?.md.html">04 接口调试:Mock 工具如何快速进行接口调试?.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/前端工程化精讲-完/05 编码效率:如何提高编写代码的效率?.md.html">05 编码效率:如何提高编写代码的效率?.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/前端工程化精讲-完/06 团队工具:如何利用云开发提升团队开发效率?.md.html">06 团队工具:如何利用云开发提升团队开发效率?.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/前端工程化精讲-完/07 低代码工具:如何用更少的代码实现更灵活的需求.md.html">07 低代码工具:如何用更少的代码实现更灵活的需求.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/前端工程化精讲-完/08 无代码工具:如何做到不写代码就能高效交付?.md.html">08 无代码工具:如何做到不写代码就能高效交付?.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/前端工程化精讲-完/09 构建总览:前端构建工具的演进.md.html">09 构建总览:前端构建工具的演进.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/前端工程化精讲-完/10 流程分解:Webpack 的完整构建流程.md.html">10 流程分解:Webpack 的完整构建流程.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/前端工程化精讲-完/11 编译提效:如何为 Webpack 编译阶段提速?.md.html">11 编译提效:如何为 Webpack 编译阶段提速?.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/前端工程化精讲-完/12 打包提效:如何为 Webpack 打包阶段提速?.md.html">12 打包提效:如何为 Webpack 打包阶段提速?.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/前端工程化精讲-完/13 缓存优化:那些基于缓存的优化方案.md.html">13 缓存优化:那些基于缓存的优化方案.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/前端工程化精讲-完/14 增量构建:Webpack 中的增量构建.md.html">14 增量构建:Webpack 中的增量构建.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/前端工程化精讲-完/15 版本特性:Webpack 5 中的优化细节.md.html">15 版本特性:Webpack 5 中的优化细节.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/前端工程化精讲-完/16 无包构建:盘点那些 No-bundle 的构建方案.md.html">16 无包构建:盘点那些 No-bundle 的构建方案.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
<a class="current-tab" href="/专栏/前端工程化精讲-完/17 部署初探:为什么一般不在开发环境下部署代码?.md.html">17 部署初探:为什么一般不在开发环境下部署代码?.md.html</a>
|
||
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/前端工程化精讲-完/18 工具盘点:掌握那些流行的代码部署工具.md.html">18 工具盘点:掌握那些流行的代码部署工具.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/前端工程化精讲-完/19 安装提效:部署流程中的依赖安装效率优化.md.html">19 安装提效:部署流程中的依赖安装效率优化.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/前端工程化精讲-完/20 流程优化:部署流程中的构建流程策略优化.md.html">20 流程优化:部署流程中的构建流程策略优化.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/前端工程化精讲-完/21 容器方案:从构建到部署,容器化方案的优势有哪些?.md.html">21 容器方案:从构建到部署,容器化方案的优势有哪些?.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/前端工程化精讲-完/22 案例分析:搭建基本的前端高效部署系统.md.html">22 案例分析:搭建基本的前端高效部署系统.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/前端工程化精讲-完/23 结束语 前端效率工程化的未来展望.md.html">23 结束语 前端效率工程化的未来展望.md.html</a>
|
||
|
||
</li>
|
||
</ul>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
<div class="sidebar-toggle" onclick="sidebar_toggle()" onmouseover="add_inner()" onmouseleave="remove_inner()">
|
||
<div class="sidebar-toggle-inner"></div>
|
||
</div>
|
||
|
||
<script>
|
||
function add_inner() {
|
||
let inner = document.querySelector('.sidebar-toggle-inner')
|
||
inner.classList.add('show')
|
||
}
|
||
|
||
function remove_inner() {
|
||
let inner = document.querySelector('.sidebar-toggle-inner')
|
||
inner.classList.remove('show')
|
||
}
|
||
|
||
function sidebar_toggle() {
|
||
let sidebar_toggle = document.querySelector('.sidebar-toggle')
|
||
let sidebar = document.querySelector('.book-sidebar')
|
||
let content = document.querySelector('.off-canvas-content')
|
||
if (sidebar_toggle.classList.contains('extend')) { // show
|
||
sidebar_toggle.classList.remove('extend')
|
||
sidebar.classList.remove('hide')
|
||
content.classList.remove('extend')
|
||
} else { // hide
|
||
sidebar_toggle.classList.add('extend')
|
||
sidebar.classList.add('hide')
|
||
content.classList.add('extend')
|
||
}
|
||
}
|
||
|
||
|
||
function open_sidebar() {
|
||
let sidebar = document.querySelector('.book-sidebar')
|
||
let overlay = document.querySelector('.off-canvas-overlay')
|
||
sidebar.classList.add('show')
|
||
overlay.classList.add('show')
|
||
}
|
||
function hide_canvas() {
|
||
let sidebar = document.querySelector('.book-sidebar')
|
||
let overlay = document.querySelector('.off-canvas-overlay')
|
||
sidebar.classList.remove('show')
|
||
overlay.classList.remove('show')
|
||
}
|
||
|
||
</script>
|
||
|
||
<div class="off-canvas-content">
|
||
<div class="columns">
|
||
<div class="column col-12 col-lg-12">
|
||
<div class="book-navbar">
|
||
<!-- For Responsive Layout -->
|
||
<header class="navbar">
|
||
<section class="navbar-section">
|
||
<a onclick="open_sidebar()">
|
||
<i class="icon icon-menu"></i>
|
||
</a>
|
||
</section>
|
||
</header>
|
||
</div>
|
||
<div class="book-content" style="max-width: 960px; margin: 0 auto;
|
||
overflow-x: auto;
|
||
overflow-y: hidden;">
|
||
<div class="book-post">
|
||
<p id="tip" align="center"></p>
|
||
<div><h1>17 部署初探:为什么一般不在开发环境下部署代码?</h1>
|
||
<p>这节课我们将进入前端效率工程化的第三个模块——部署效率篇。本模块主要讨论两个方面的问题:第一个是在前端项目的构建部署流程里,除了使用构建工具执行构建之外,还有哪些因素会影响整个部署流程的工作效率?第二个是在部署系统中进行项目构建时,又会面临哪些和环境相关的问题和优化方案?</p>
|
||
<p>这节课我们先来讨论为什么要使用部署系统,而不是在本地环境下部署代码?</p>
|
||
<p>在分析这个问题之前,我们先对前端项目的部署流程进行界定。</p>
|
||
<h3>前端项目的一般部署流程</h3>
|
||
<p>在前端项目中,通常可以把在一个全新环境下的代码部署过程分为以下几个环节:</p>
|
||
<ol>
|
||
<li><strong>获取代码</strong>:从代码仓库获取项目代码,并切换到待部署的分支或版本。</li>
|
||
<li><strong>安装依赖</strong>:安装项目构建所需要的依赖包。</li>
|
||
<li><strong>源码构建</strong>:使用构建工具对项目源代码进行构建,生成产物代码。</li>
|
||
<li><strong>产物打包</strong>:将部署所需的代码(通常指的是构建后的产物代码,如果是部署 Node 服务则还需要其他目录与文件)打成压缩包。</li>
|
||
<li><strong>推送代码</strong>:将待部署的文件或压缩包推送至目标服务器的特定目录下,如果是推送压缩包的情况,还需执行解压。</li>
|
||
<li><strong>重启服务</strong>:在部署 Node 服务的情况下,在代码推送后需要进行服务重启。</li>
|
||
</ol>
|
||
<h3>本地部署相比部署系统的优势</h3>
|
||
<p>对于使用部署系统的项目而言,除了重启服务这一步骤在普通静态服务部署中不需要执行外,上述其他环节通常是每次构建都需要经历的。</p>
|
||
<p>而如果使用本地开发环境进行部署,则可以根据情况<strong>对前两个环节进行简化</strong>:</p>
|
||
<ol>
|
||
<li>在获取代码的环节中,本地开发环境已经包含了项目的本地代码,同拉取完整的代码仓库相比,直接获取更新内容并切换分支或版本的处理要更快一些。</li>
|
||
<li>在安装依赖的环节中,本地开发环境通常已包含了构建所需的最新依赖包,即使切换到待部署版本后发现依赖版本有变更,更新依赖包的时间也比在空目录下完整安装依赖包的时间更短。</li>
|
||
</ol>
|
||
<p>此外,本地部署还有另外两点优势是使用部署系统所不具备的:</p>
|
||
<ol>
|
||
<li>增量构建:我们之前分析过增量构建的实现原理。在构建配置与项目依赖不发生变化的情况下,理论上,本地部署可以让构建进程长时间地驻留,以达到增量构建的效果。</li>
|
||
<li>快速调试:本地部署时,构建过程会直接在本地进行,因此有任何构建问题时可以第一时间发现并处理。相比之下,远程的部署系统则需要将一定的时间消耗在链路反馈和本地环境切换上。</li>
|
||
</ol>
|
||
<p>因此,如果单从上面的部署环节来看,本地部署的效率一般优于部署系统,那么为什么在企业中通常不建议这样做呢?</p>
|
||
<h3>本地部署的劣势</h3>
|
||
<p>同远程部署系统相比,不管从安全性还是人员效率上看,本地部署都存在诸多问题:</p>
|
||
<h4>流程安全风险</h4>
|
||
<p><strong>环境一致性</strong></p>
|
||
<p>本地部署的第一个问题在于无法保证环境的一致性:</p>
|
||
<ul>
|
||
<li>同一个项目,不同开发人员的本地环境(操作系统、NodeJS 版本等)都可能存在差异。</li>
|
||
<li>由于 NodeJS <a href="https://docs.npmjs.com/about-semantic-versioning">语义化版本</a>(Semantic Version)在安装时自动升级的问题,不同开发人员的本地 node_modules 中的依赖包版本也可能存在差异。</li>
|
||
<li>开发人员的本地环境和部署代码的目标服务器环境之间也可能存在差异。</li>
|
||
</ul>
|
||
<p>这些差异会导致项目代码的稳定性无法得到保障。例如对于一个 Node 项目而言,在一个 NodeJS 低版本环境下构建的产物,在 Node 高版本环境下就有可能启动异常。</p>
|
||
<p>因此,如果项目都由开发人员各自在本地部署,无疑会降低项目的稳定性,增加部署风险。</p>
|
||
<p>而使用远程统一的部署系统,一方面避免了不同开发人员的本地环境差异性,另一方面,部署系统的工作环境也可以与线上服务环境保持一致,从而降低环境不一致的风险。</p>
|
||
<p><strong>过程一致性</strong>
|
||
同环境一致性的问题相似,本地部署的第二个问题是无法保证部署过程的一致性。所谓过程的一致性,就是尽可能地让每次部署的流程顺序、各环节的处理过程都保持一致,从而打造规范化的部署流程。本地部署依赖人工操作,这就可能因为操作中的疏漏,导致过程一致性无法得到保障。尽管可以通过将部署流程写入脚本等方式减少人工误操作的风险,但是这和通过部署系统将完整处理过程写入代码的方式相比,仍然不够安全可靠。同时,系统可以记录每次部署操作的细节日志,便于当出现问题时快速解决。</p>
|
||
<h4>工作效率问题</h4>
|
||
<p><strong>可回溯性</strong>
|
||
可回溯性的问题可以从日志和产物两方面来看。</p>
|
||
<ul>
|
||
<li><strong>日志</strong>:在部署过程中我们可能遇到各种问题,例如构建失败、单元测试执行失败、推送代码失败、部署后启动服务失败等。遇到这些问题时,需要有相应的日志来帮助定位。尽管在本地部署执行时也会输出日志,但是这些日志是临时的,查阅不便,且本地部署的日志至多只能保留当前一次的处理日志,如果希望对历史部署过程进行查看分析,更不能使用这种方式。</li>
|
||
<li><strong>产物</strong>:通常,部署系统中会留存最近几次部署的构建产物包,以便当部署后的代码存在问题时能够快速回滚发布。而本地部署在项目的开发目录下执行,因此通常只会保留最近一次的构建产物,这就阻碍了上述快速回滚的实现。</li>
|
||
</ul>
|
||
<p>相对的,一个规范化的部署系统,则可以记录和留存每一次部署操作的细节日志,以及保留最近若干次的部署代码包,因此在可回溯性上又胜一筹。</p>
|
||
<p><strong>人员分工</strong>
|
||
工作效率的第二个问题是人员分工问题,这个问题又可以从以下几个侧面来分析:</p>
|
||
<p>首先部署过程需要耗费时间。在本地部署当前项目的某一个分支时,无法同时对该项目进行继续开发,往往只能中断当前的工作,等待部署完成。</p>
|
||
<p>在这个前提下,一个项目中的多名开发人员如果各自在电脑中<strong>进行部署</strong>,无疑增大了上述流程安全的风险系数。但反过来,如果一个项目里只有<strong>个别开发者的本地环境拥有部署权限</strong>,则所有人的部署需求都会堆积到一起,大大增加对有权限的开发者的工作时间的占用。如果不能及时响应处理,也会延误其他人的后续工作。</p>
|
||
<p>此外由于分工角色的不同,在许多情况下,部署流程会主动由测试人员而非开发人员发起。当部署在开发人员的本地环境中进行时,会像上面多人开发集中部署那样彼此影响,也增加了相应的沟通成本。</p>
|
||
<p><strong>CI/CD</strong>
|
||
<a href="https://en.wikipedia.org/wiki/Continuous_integration">持续集成</a>(Continuous Integration,CI)和<a href="https://en.wikipedia.org/wiki/Continuous_delivery">持续交付</a>(Continuous Delivery,CD),是软件生产领域提升迭代效率的一种工作方式:开发人员提交代码后由 CI/CD 系统自动化地执行合并、构建、测试和部署等一系列管道化(Pipeline)的流程,从而尽早发现和反馈代码问题,以小步快跑的方式加速软件的版本迭代过程。</p>
|
||
<p>这个过程通常是各系统(版本管理系统、构建系统、部署系统等)以自动化的方式协同完成的。而本地部署依赖人工操作,所以并不支持这种自动化的处理过程。</p>
|
||
<h3>总结</h3>
|
||
<p>作为部署优化的开篇,这节课我们主要讨论了相比远程部署系统,本地部署的优缺点:尽管本地部署有着流程简化、快速调试等优点,但是相对应的也带来了流程安全风险和人员效率下降等问题。因此一般在规范化的企业技术研发流程中,通常都不使用本地人工操作这样的部署方式。</p>
|
||
<p>本节课的思考题是:回顾一下本地部署同远程部署系统相比有哪些优势呢?关于这两者的差异,还可以从其他维度进行分析吗?如果有的话,你可以在留言区回复,期待你的想法。</p>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<div style="float: left">
|
||
<a href="/专栏/前端工程化精讲-完/16 无包构建:盘点那些 No-bundle 的构建方案.md.html">上一页</a>
|
||
</div>
|
||
<div style="float: right">
|
||
<a href="/专栏/前端工程化精讲-完/18 工具盘点:掌握那些流行的代码部署工具.md.html">下一页</a>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<a class="off-canvas-overlay" onclick="hide_canvas()"></a>
|
||
</div>
|
||
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v652eace1692a40cfa3763df669d7439c1639079717194" integrity="sha512-Gi7xpJR8tSkrpF7aordPZQlW2DLtzUlZcumS8dMQjwDHEnw9I7ZLyiOj/6tZStRBGtGgN6ceN6cMH8z7etPGlw==" data-cf-beacon='{"rayId":"7099776d5bba3cfa","version":"2021.12.0","r":1,"token":"1f5d475227ce4f0089a7cff1ab17c0f5","si":100}' crossorigin="anonymous"></script>
|
||
</body>
|
||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-NPSEEVD756"></script>
|
||
<script>
|
||
window.dataLayer = window.dataLayer || [];
|
||
|
||
function gtag() {
|
||
dataLayer.push(arguments);
|
||
}
|
||
|
||
gtag('js', new Date());
|
||
gtag('config', 'G-NPSEEVD756');
|
||
var path = window.location.pathname
|
||
var cookie = getCookie("lastPath");
|
||
console.log(path)
|
||
if (path.replace("/", "") === "") {
|
||
if (cookie.replace("/", "") !== "") {
|
||
console.log(cookie)
|
||
document.getElementById("tip").innerHTML = "<a href='" + cookie + "'>跳转到上次进度</a>"
|
||
}
|
||
} else {
|
||
setCookie("lastPath", path)
|
||
}
|
||
|
||
function setCookie(cname, cvalue) {
|
||
var d = new Date();
|
||
d.setTime(d.getTime() + (180 * 24 * 60 * 60 * 1000));
|
||
var expires = "expires=" + d.toGMTString();
|
||
document.cookie = cname + "=" + cvalue + "; " + expires + ";path = /";
|
||
}
|
||
|
||
function getCookie(cname) {
|
||
var name = cname + "=";
|
||
var ca = document.cookie.split(';');
|
||
for (var i = 0; i < ca.length; i++) {
|
||
var c = ca[i].trim();
|
||
if (c.indexOf(name) === 0) return c.substring(name.length, c.length);
|
||
}
|
||
return "";
|
||
}
|
||
|
||
</script>
|
||
|
||
</html>
|