如何使用docker + nginx来部署前端项目

2,025 阅读6分钟

本文不教如何安装docker,默认已安装好docker。这是我的docker版本,在我本地安装的。

image.png

docker 基本操作

docker的命令远远不止这些,本文只讲需要用到的。

docker pull 拉取nginx镜像

如果你之前用nginx部署过前端项目的话,用docker部署你就会发现更简单。

docker pull nginx

等待pull完成,pull成功之后,可以用docker images查看一下

image.png

可以看到nginx已经成功下载了。

docker run 启动nginx镜像

docker run -d -p 80:80 --name nginx nginx
  • -d: 让容器后台运行(detached mode)
  • -p 80:80: 前面80是宿主机的端口号,后面80是容器的端口号,意思是将宿主机的80端口映射到容器的80端口。当访问宿主机的80端口时,请求会被映射到容器内的Nginx服务的80端口,从而能够访问到Nginx服务。
  • --name nginx: 设置容器的名称,方便后续对该容器进行管理操作,例如停止、启动或删除容器时,可以通过这个名称来指定操作的容器对象,名字随便取,叫什么都可以。
  • nginx: 镜像名称

image.png 启动之后,终端会输出对应的容器id,说明已经启动成功。也可以用docker ps查看一下

docker ps 查看运行中的容器

image.png 可以看到,有一条nginx的容器正在运行,这个容器的id84开头,正是我们刚刚执行docker run返回的那个容器id

打开浏览器,输入localhost,可以看到下面的默认页面,恭喜你,已经成功用docker启动nginx了。

http端口号默认80,所以可以省略端口号,如果你映射的是其他端口号,记得带上端口号

image.png

如果你是在本地电脑上安装的docker,搭配docker官方提供的Docker Desktop桌面端,使用起来更方便。上面所有的命令,在Docker Desktop里面都有对应的可视化操作。

docker stop

docker stop <容器名/容器ID>

这里要注意是容器名称,不是镜像名称,就是前面我们执行docker run命令后面带的参数--name nginxnginx

执行docker stop nginx,可以看到返回了容器的名称。然后docker ps再查看一下运行的容器,发现并没有nginx容器里。

image.png

再刷新一下浏览器看看

image.png

部署

创建项目

npm create vite@latest

image.png

依次执行

cd docker-fe-demo
npm install
run run dev

在App.tsx里面随便写一点代码,保存一下。

function App() {
  return <>hello sens</>;
}
export default App;

页面正常渲染了

image.png

编写Dockerfile--多阶段构建

在项目根目录新建一个Dockerfile和docker.nginx.conf文件。

Dockerfile是固定名字,类似于webpack.config.js,docker.nginx.conf自己随便取,但是要保证.conf结尾

我的做法是把nginx的conf配置文件暴露出来,放到前端,方便修改,最后copy进去,丢到nginx对应配置文件的地方。当然,你也可以不放到前端,写死在nginx里面也行。

# build 阶段
FROM node:18-alpine as build-stage

WORKDIR /app
COPY package*.json ./
RUN npm config set registry https://registry.npmmirror.com/
RUN npm install
COPY . .
RUN npm run build

# production 阶段
FROM nginx:stable as production-stage

COPY --from=build-stage /app/dist /usr/share/nginx/html
COPY --from=build-stage /app/docker.nginx.conf /etc/nginx/conf.d/default.conf

EXPOSE 80

CMD ["nginx", "-g", "daemon off;"]

解释一下这份配置文件,把镜像分成了两个阶段(FROM了两次),build阶段和production阶段。最终镜像产物就是最后一个FROM的镜像,这里最后FROM了nginx,相当于生成了一份nginx的镜像。

build阶段

  • WORKDIR /app: 设置容器内的工作目录为/app,后续的操作(如复制文件、运行命令等)都将在这个目录下进行。
  • COPY package*.json ./: 将主机上与package相关的json文件(通常是package.jsonpackage-lock.json)复制到容器内的/app目录下。这一步是为了先安装项目依赖
  • RUN npm config set registry https://registry.npmmirror.com/: 设置npm淘宝源
  • RUN npm install: 执行npm install
  • COPY . .:将(主机)当前目录下的所有文件复制到容器内的/app目录下。
  • RUN npm run build:在容器内运行npm run build命令,构建项目(通常是将源代码编译、打包等操作)。

build阶段完成之后,我们可以得到打包之后的dist文件了,后面只要把这份文件,丢给nginx即可。

production阶段

  • COPY --from=build-stage /app/dist /usr/share/nginx/html: 将build-stage阶段构建的/app/dist复制到/usr/share/nginx/html目录(为什么是/usr/share/nginx/html目录呢?后面说)
  • COPY --from=build-stage /app/docker.nginx.conf /etc/nginx/conf.d/default.conf: 同理,将build-stage阶段构建的/app/docker.nginx.conf 复制到/etc/nginx/conf.d/default.conf
  • EXPOSE 80:容器暴露80端口。
  • CMD ["nginx", "-g", "daemon off;"]:启动nginx服务器,并关闭守护进程模式,以便容器可以持续运行并提供服务。

上面多了两个很陌生的文件目录地址,/usr/share/nginx/html/etc/nginx/conf.d/default.conf。了解nginx的朋友应该知道这是什么意思。 一开始我们已经pull nginx镜像,并且将nginx运行起来,现在让我们进入到nginx容器里面。

docker exec -it nginx /bin/bash

image.png 这里也可以看到,是我们之前84开头的nginx容器。 然后执行:

cat /etc/nginx/nginx.conf

打印一下nginx的配置信息。

image.png 可以看到,有这样的一项配置,include /etc/nginx/conf.d/*.conf;它的意思是/etc/nginx/conf.d/目录下所有以.conf为后缀的配置文件都会引入到主配置文件中使其生效。 那让我们去/etc/nginx/conf.d/目录看看。

cd /etc/nginx/conf.d/

ls

cat default.conf

image.png 圈中的意思是指定root(根)目录是/usr/share/nginx/html,当收到一个请求的时候,就会去指定的root目录里面找对应的index文件。

再进入到/usr/share/nginx/html里面看看:

cd /usr/share/nginx/html

ls

cat index.html

image.png

这不正是我们开头运行nginx镜像,访问localhost显示出来的页面内容吗?

image.png

所以,我们只要把打包之后的dist文件丢到/usr/share/nginx/html里面就行。

你也可以修改conf配置文件,指定root的值是任何目录,这里我选择的是不指定,而是把我的dist文件copy丢进去。

编写nginx配置文件

docker.nginx.conf

server {
    listen       80;
    listen  [::]:80;
    server_name  localhost;
    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
        try_files $uri $uri/ /index.html;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

特别注意的是 try_files $uri $uri/ /index.html; 这是实现前端路由的关键。我们都知道,history路由,页面刷新的时候,是会请求服务器地址的。这句话的意思是,请求过来,会尝试在root目录下面寻找当前的uri,如果没有找到就找uri/,如果还没有找到,就返回index.html。只有返回了index.html,才能走到前端路由逻辑。

docker build 构建镜像

 docker build -t docker-fe-demo .
  • -t:指定构建出来的镜像的名称和标签。我这里没有指定,tag默认就是latest,你也可以-t docker-fe-demo:sens,这个镜像的标签就是sens
  • .:Docker将会在当前目录下寻找Dockerfile文件并执行构建,就是前面设置的WORKDIR

docker run 启动镜像

docker run -d -p 80:80 --name docker-fe-demo docker-fe-demo

注意端口不要被其他程序占用,再次访问localhost,可以看到,成功部署了。

image.png

总结

大致流程如下:

image.png 如果你是在云服务器上,比如阿里云、腾讯云之类,要注意打开安全组和防火墙的端口号,不然无法访问。