背景
最近有项目需要考虑如何做前端静态代码的私有化部署。问了下 AI ,回答说可以用 Docker 部署实现。恰好前段时间在研究开源项目 Dify 的时候,发现他们也是利用 Docker 实现的私有化部署。此时又想到在收集 AI 开源项目时,很多项目都支持 Docker 部署。因此,决定实践一下如何将前端静态项目利用 Docker 部署到目标服务器。
如果对自动化部署感兴趣的朋友,在阅读完本文后,可以继续阅读本文的升级版:前端静态项目 Docker 部署指南(自动化版)
技术背景
- 本文以一个 UmiJS 官方脚手架的 Demo 项目作为示例项目。
- 本文不会详细介绍 Docker 的原理和基本用法。
- 本文是一个前端静态项目,部署案例可以用于任何 Vue、React 以及其他前端静态项目。但不适用 Nodejs 服务项目。
- 本文会分为两个步骤,先在本地用 Docker 的部署,后在服务器用 Docker 部署。
- 默认你已经知道如何在本地和服务器上安装 Docker 及 Docker Compose 。
目录
- Docker 本地部署前端静态项目
- 前端项目目录结构
- Docker 配置文件
- Docker 操作命令
- 最终效果示例
- Docker 服务器部署前端静态项目
- 前端项目目录结构
- Docker 操作命令
- 最终效果示例
- 拓展
Docker 本地部署前端静态项目
前端项目目录结构
这里我用 Umijs 脚手架,选择了 Ant Design Pro 的模板项目。本地开发模式运行后,效果如下:
在项目的根目录下,创建 docker
文件夹,用于存放 Docker 相关的配置文件(此处可以根据个人习惯选择直接在项目根目录下创建配置文件也可以),并分别在 docker
文件夹下创建 nginx
文件夹,docker-compose.yml
文件,Dockerfile
文件,在 nginx
文件夹下创建 nginx.conf
文件。目录结构如下:
Docker 配置文件
此部分的内容,采用了 AI 的建议,并经过自己调试后而成,所以非常建议大家根据自己的实际情况与 AI 对话,获取更符合实际项目的配置。如果配置文件中的内容有不理解的,可以让 AI 帮你解答。
- 在 Dockfile 文件中写入如下内容(因为前端静态页,无法通过 http 协议访问,需要通过如 Nginx、Apache、Tomcat 服务器才可以正常访问,此处选用 Nginx 服务器)
# 以最新版的 nginx 镜像为基础制作镜像
FROM nginx:latest
# 设置工作目录,设置的是镜像中的目标目录
WORKDIR /usr/share/nginx/html
# 复制静态资源,复制当前项目的内容到镜像中,需要注意此处的路径是根据后面执行 docker 命令的位置来设置的
COPY ../dist/ .
# 复制 nginx 配置文件
COPY ./docker/nginx/nginx.conf /etc/nginx/conf.d/default.conf
# 暴露端口,暴露的是镜像中的 nginx 端口,可以自定义
EXPOSE 80
# 启动 nginx
CMD ["nginx", "-g", "daemon off;"]
- 在 docker-compose.yml 中写入如下内容
# 配置文件的版本为3
version: '3'
# 服务列表
services:
# 服务名称为 DockerDemo
DockerDemo:
# 镜像名称
image: docker-demo
# 容器名称
container_name: docker-demo
# 端口映射,7000 端口为主机端口,80 端口为容器端口
ports:
- "7000:80"
- 在 nginx.conf 中写入如下内容
server {
access_log /var/log/nginx/access-web.log main;
error_log /var/log/nginx/error-web.log notice
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html;
# 用于解决 React SPA 刷新页面时 404 问题
try_files $uri $uri/ /index.html$is_args$args;
}
# 为静态资源(如图片)配置单独的location块
location ~* \.(jpg|jpeg|png|gif|ico|svg|mp4)$ {
root /usr/share/nginx/html; # 指定静态资源的根目录
}
# 配置错误页面
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
# 定义50x.html的路径
location = /50x.html {
root /usr/share/nginx/html;
}
}
Docker 操作命令
- 打包 dist 文件
// 根据实际项目执行对应的命令即可
pnpm build
- 创建镜像
// 进入项目的根目录执行
// -t 后面是镜像名,需要小写
// -f 后面为 docker 构建所依赖的 Dockerfile 文件的路径
// 最后的 . 表示将当前目录作为构建上下文
docker build -t docker-demo -f ./docker/Dockerfile .
- 启动服务
// 还是在项目的根目录执行
// -f 后面为 docker-compose 命令所依赖的配置文件路径
docker-compose -f ./docker/docker-compose.yml up -d
- 其他命令
- 查看启动的 Docker 服务:
docker ps
- 利用 docker-compose 关闭刚才启动的所有服务:
docker-compose -f ./docker/docker-compose.yml stop
- 查看启动的 Docker 服务:
最终效果示例
为了特意与本地服务做区分,所以选择了 7000 端口作为区别,效果如下:
至此,我们已经实现了 Docker 镜像的构建及本地部署,下一步我们将会在服务器上重复此步骤。
Docker 服务器部署前端静态项目
前端项目目录结构
此处我们不着重介绍如何保证打包后的文件存在于服务器上,但也给出一些参考:
- 通过诸如
FinalShell
的文件传输工具,将打包后的文件上传到服务器的指定目录。 - 通过诸如
Jenkins
、阿里云效等方式在打包服务器上对代码进行打包后,通过Shell
脚本将打包后的产物推送至服务器的指定目录。 最终在服务器上的文件示例,需保证dist
和docker
两个目录上传至服务器(此处用FinalShell
演示一下目录结构)。
- 创建一个
docker-opt
的目录(根据个人喜好创建或不创建,此处是为了方便存放 docker 相关的项目) - 将
dist
和docker
目录上传至docker-opt
。 最终目录结构如下图所示(务必保证 dist 和 nginx 中的存在文件):
Docker 操作命令
- 创建镜像
// 进入 docker-opt 中的 docker-demo 目录 (根据具体情况而定)
// -t 后面是镜像名,需要小写
// -f 后面为 docker 构建所依赖的 Dockerfile 文件的路径
// 最后的 . 表示将当前目录作为构建上下文
docker build -t docker-demo -f ./docker/Dockerfile .
- 启动服务
// 还是在项目的根目录执行
// -f 后面为 docker-compose 命令所依赖的配置文件路径
docker-compose -f ./docker/docker-compose.yml up -d
最终效果示例
通过服务器的 IP 访问,效果如下:
至此,完成了前端静态项目的服务器端的 Docker 部署。
拓展
- 如果在服务器上无法通过 IP 正常访问,需要确认服务器的安全组对应的端口是否放开。
- 本文的案例相对来说是最基础的版本,如果是小团队或者个人,其实基本也足够使用。
- 当我们掌握了最基础的部分之后,可以根据实际情况做更多的扩展,比如:
- 现在的镜像是构建到了本地,实际中可以构建到 Docker 的官方镜像库或者国内的其他公共镜像库,甚至自己搭建镜像库。
- 基础的镜像还可以添加很多其他配置,比如外接一些特有的配置文件、环境变量等等。
- 作为前端而言,Docker 可以快速的帮我们完成项目的部署,比如我们不在需要专门在服务器上安装、维护 Nginx 及其配置,尤其是当一台服务器承载了多个项目时,我们完全可以通过多个 Docker 镜像的方式,用端口作为区分的约定,来快速实现我们的部署需求。
- 当你在运行过程中,遇到任何报错,建议优先咨询 AI。
杂谈
- 网上有很多关于 Docker 部署前端项目的文章,但是每个人的需求不一样,因此在我们决定要去解决一个问题的时候,一定要抓住重点,即当前我的目标是什么,否则很容易会被一些其他信息所干扰。比如我可能只是想利用 Docker 把我的静态文件在服务器上跑起来,并且被人访问到。那么重点就是
静态文件
、Docker 部署
、可被访问
。如果不明确重点,很可能就会被前端服务
、Docker 前端构建
等其他内容所干扰 - 善用 AI ,我在查了一圈资料后,发现都不是我想要的答案,最终决定借助 AI 来帮我做一些可以实际操作的解决方案,即使中间会有不少坑,但大体思路是可行的。我的一个体会就是,现在我的编程模式似乎从
面向搜索引擎编程
变为了面向 AI 编程
。同时我在定位问题的时,思考问题的方式也从面向小黄鸭调试
变为了面向 AI Copliot 调试
。 - 本文重点是如何写基础的
Dockerfile
,以及如何用docker-compose.yml
来启动 Docker 服务。即解决“如何将前端静态代码通过 Docker 的方式运行起来”的问题。在真正的商业项目中,还需要结合后端服务的 Docker 部署来一起完善整个项目的Dockerfile
及docker-compose.yml
,当然如果团队发展的更为庞大,也会有更专业的运维同学来维护整个部署相关的工作。 - 本文没有介绍如何将代码自动化推送到服务器,以及如何自动化部署 Docker 的内容,如果掌握了手动的操作方式,自动化只是借助其他工具执行命令的过程而已。
- 在整个实践过程中还有一个解决问题的思路可以分享一下,就是在我编写 Nginx 的配置文件时,因为用的是 AI 返回的模板,在启动镜像的时候,总是失败,查看了原因之后提示说个别配置项不允许在当前位置出现,问 AI 也没有什么好的解决方案。于是我把自己的配置文件注释后,进入了容器内部的服务,查看了具体 Nginx 在运行时引用的配置文件后,发现了问题。这里分享这个点的原因在于,当我们遇到一个陌生的技术栈时,因为不知道全貌,在运行时可以先把我们自定义的内容去掉,然后看看默认的内容是否可以正常运行,这样能够帮我们快速隔离出是我们项目的问题还是其自身的问题,也即是控制变量法。
参考文档
- 阿里通义千问 - VSCode 插件:help.aliyun.com/document_de…
- KimiChat :kimi.moonshot.cn/
- UmiJS:umijs.org/docs/guides…
- 前端静态项目 Docker 部署指南(自动化版):juejin.cn/spost/73617…
浏览知识共享许可协议
本作品采用知识共享署名-相同方式共享 4.0 国际许可协议进行许可。