Vue 3 项目的开发体验已经相当成熟,但当构建产物需要落到多台服务器上时,手工 SSH 上去拷贝文件、改配置的方式就会暴露出各种问题:环境不一致、配置漂移、发布步骤遗漏。Chef 作为老牌的配置管理工具,恰好可以解决这类基础设施层面的重复劳动。本文将围绕 Vue 3 项目的静态资源部署场景,详细讲解如何用 Chef 把整套部署流程固化成可版本管理的代码。

一、Chef 的核心概念与部署模型
在动手写部署脚本之前,需要先理解 Chef 的几个基本组成部分。Chef 采用的是"基础设施即代码"的理念,把服务器的状态描述成一份份代码文件,由 Chef Client 在目标节点上执行并收敛到期望状态。理解这套模型,才能明白为什么用 Chef 管理部署比写一堆 Shell 脚本更可靠。
Chef 的核心概念包括 Cookbook、Recipe、Node 和 Chef Server。Cookbook 是最大的组织单位,可以理解为一个可复用的部署包;Recipe 是 Cookbook 中的具体执行单元,描述"这个节点上应该是什么状态";Node 是被管理的一台服务器;Chef Server 则负责集中分发 Cookbook 和节点信息。对于小型团队,也可以使用 chef-solo 或 chef-zero 的本地模式,跳过 Server 直接运行。
对于 Vue 3 这种纯前端项目来说,部署的本质其实很简单:安装 Nginx、准备目录、把 dist 目录的内容同步过去、写入 Nginx 配置。这四步都可以用 Recipe 描述出来。与 Shell 脚本最大的区别在于,Recipe 是幂等的——重复执行不会产生副作用,比如目录已存在就不会重建,软件包已安装就会跳过,这对反复发布的场景非常友好。
二、编写部署 Vue 3 项目的 Cookbook
接下来创建一个专门用于部署 Vue 3 项目的 Cookbook。使用 chef generate cookbook vue3_deploy 命令生成目录结构后,在 recipes 目录下编写默认的部署逻辑。下面的 Recipe 完整展示了安装 Nginx、创建目录、同步构建产物的全过程。
# vue3_deploy/recipes/default.rb # 1. 安装并启动 Nginx package 'nginx' do action :install end service 'nginx' do action [:enable, :start] end # 2. 创建部署目录 directory '/var/www/vue3-app' do owner 'www-data' group 'www-data' mode '0755' recursive true end # 3. 同步 Vue 3 构建产物 remote_directory '/var/www/vue3-app' do source 'dist' owner 'www-data' group 'www-data' mode '0755' files_owner 'www-data' files_group 'www-data' purge false action :create end
这里的 remote_directory 资源会从 Cookbook 的 files/default/dist 目录同步文件到目标路径。也就是说,CI 流水线只需要把 npm run build 产出的 dist 内容拷贝到 Cookbook 的 files 目录,再执行 berks install && berks upload,Chef 就会自动把文件分发到所有节点。注意 purge false 这个选项,它保留了目标目录中不在源目录里的文件,为后续配合 CDN 缓存策略留下空间。
接着还需要管理 Nginx 配置。推荐用 template 资源生成站点配置,这样可以根据不同环境变量渲染出不同内容,比如测试环境和生产环境的域名、缓存策略都不一样。
# nginx 站点配置模板:templates/default/vue3-app.conf.erb
server {
listen 80;
server_name <%= @server_name %>;
root /var/www/vue3-app;
index index.html;
# Vue Router 的 history 模式需要把所有路由回退到 index.html
location / {
try_files $uri $uri/ /index.html;
}
# 构建产物带 hash,可以设置长缓存
location ~* \.(js|css|png|jpg|woff2)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
}
# Recipe 中渲染模板
template '/etc/nginx/sites-available/vue3-app.conf' do
source 'vue3-app.conf.erb'
variables(
server_name: node['vue3_deploy']['server_name']
)
notifies :reload, 'service[nginx]', :immediately
end
link '/etc/nginx/sites-enabled/vue3-app.conf' do
to '/etc/nginx/sites-available/vue3-app.conf'
notifies :reload, 'service[nginx]', :immediately
end
模板中的 try_files 指令是部署 Vue 3 项目时最容易踩的坑。如果项目使用了 Vue Router 的 history 模式,刷新非根路径时 Nginx 会直接返回 404,必须把请求回退到 index.html 由前端路由接管。另外由于 Vite 构建的静态资源文件名自带内容 hash,可以放心设置 30 天的长缓存,而 index.html 本身不缓存,保证发版后用户能立即拿到新版本入口。
三、回滚策略与多环境管理实践
自动化部署如果不能方便地回滚,就不算完整的方案。基于 Chef 的部署可以采用"版本目录加软链接"的经典模式:每次发布把构建产物放到带版本号的目录中,再切换软链接指向。
# 按时间戳生成版本目录,发布后切换软链接
timestamp = Time.now.strftime('%Y%m%d%H%M%S')
release_dir = "/var/www/vue3-app/releases/#{timestamp}"
directory release_dir do
recursive true
end
remote_directory release_dir do
source 'dist'
purge true
end
# 切换软链接到新版本
link '/var/www/vue3-app/current' do
to release_dir
end
这种结构下,Nginx 的 root 指向 /var/www/vue3-app/current,每次发布只是改一个软链接,切换是原子操作。一旦新版本出现问题,只需要把软链接指回上一个版本目录,几秒钟就能完成回滚,不需要重新构建。同时保留最近若干个版本目录,超出数量后由定时任务清理,兼顾磁盘空间和回滚窗口。
多环境管理方面,建议利用 Chef 的 Environment 机制,为 dev、staging、production 分别定义属性覆盖。例如 staging 环境关闭长缓存方便调试,production 环境开启 gzip 压缩,这些差异都写在各自 Environment 的 JSON 属性里,Recipe 代码保持完全一致。这样做的好处是部署逻辑只有一份,环境差异全部收敛到配置文件中,避免了"测试环境改了脚本忘了同步生产"这类低级事故。
最后补充一点与 CI/CD 的衔接经验。典型流水线是:CI 中执行 npm ci && npm run build,将 dist 打包进 Cookbook,通过 berks upload 上传到 Chef Server,再触发目标节点的 chef-client 执行。整个过程中服务器上没有任何手工操作,所有变更都来自版本库中的代码审查记录,出问题时可以直接追溯是哪一次提交引入的。这种把基础设施状态完全代码化的方式,才是 Chef 带给前端工程化部署的真正价值。