用 docker 部署 create-react-app 构建的应用

5,131 阅读5分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第3天,点击查看活动详情

大家好,我是前端西瓜哥。

假设我们有一个用 create-react-app 构建的前端项目,我们希望它能快速在和开发环境不同的服务器上部署。

这时候我们可以使用 docker 容器技术,将项目打包成 image,然后通过 image 来生成容器,然后运行其上的服务。

核心就是使用 Dockerfile 配置文件来自动构建 docker 镜像。

其实我们用的一些基础镜像,也是用 Dockefile 构建的。比如 nodejs 的 16.14.2 版本镜像使用的 Dockerfile 为:https://github.com/nodejs/docker-node/blob/main/16/alpine3.14/Dockerfile

首先我们用 create-react-app 创建一个 react 项目。

npx create-react-app docker-react-project
# 或
yarn create react-app docker-react-project

项目构建好后,在这个项目根目录下运行 yarn build 就会构建网站需要的静态文件,放到打包目录 build 下。

我们需要用一个 web server 提供静态服务器,让用户进行访问,这里我们用 Nginx 来部署。

这个需求该怎么用 Dockerfile 来配置实现呢?

这里我们先直接给出 Dockerfile 的配置实现。

FROM node:16 AS build
# 容器内的目录,通常我们会使用 app 目录
WORKDIR /app
COPY . .
RUN yarn && yarn build

FROM nginx
WORKDIR /usr/share/nginx/html
RUN rm -rf ./*
COPY --from=build /app/build .

这些语句会顺序执行,一步步完成构建过程。

FROM

FROM node:16 AS build

上面这个语句就是构造一个基于 nodejs 的基础镜像,版本为 16。版本可以省略不指定,如此一来的话会应用默认的 latest 对应的镜像。

FROM 就是一个指令,后面则是参数。

指令是大小写不敏感的,就像 SQL 一样。一般我们会使用大写。

FROM 的作用是构建基础镜像。一个 Dockerfile 文件的开头必须使用 FROM(一些参数设置和注释除外)。此外我们也可以使用多个 FROM,即会构建多个基础镜像。

AS 可以给一个镜像添加名字,后续的通过 FROM 构建的新的镜像就可以通过 COPY --from=<name> 来指定拷贝来源,COPY 这个指令我们后面会介绍。

WORKDIR

WORKDIR /app

WORKDIR 用于指定容器所在文件系统的工作目录。通常我们会使用 /app 来放置我们的文件。

WORKDIR 后面的路径除了可以是绝对路径,还可以是相对路径。相对路径也是相对前面的路径计算的,就像我们用 bash 写 cd 命令一样。

后面我们使用 nginx 的镜像后,使用的是:

WORKDIR /usr/share/nginx/html

就是将工作目录设置为 /usr/share/nginx/html,因为这里是 nginx 启动时,80 端口静态服务指向的目录,我们跑到这里来,是为了清空这里的内容,并把我们构建好的文件搬到这里面去。

COPY

COPY . .

COPY 可以将宿主机器的文件拷贝到容器里。

前面的路径为 source,表示源路径,通常为宿主机器当前项目的目录,可以为多个。后面的路径为目标路径,指要拷贝到的容器里文件系统目录位置。

上面这条语句的意思是,将当前目录下的文件,都拷贝到容器中。

有时候我们可能不希望一些文件也被拷贝,我们可以使用 .dockerignore 文件 来指明哪些文件不需要被拷贝。

这里希望放了第三方库的 node_module 和构建目录 build 不想被拷贝,我们需要在项目根目录下创建一个 .dockerignore 文件,并在上面添加一下内容:

node_module
build

后面 nginx 构建后,我们的 COPY 通过 --from=name 的方式,拷贝源不再指向用户的上下文(指宿主机器的工作目录),而是指定了之前 node 镜像所在的文件系统,拷贝了我们在其构建的文件到 nginx 的环境中。

COPY --from=build /app/build .

RUN

RUN yarn && yarn build

RUN 用于运行一些命令,构建新的一层镜像。

RUN 可以执行多次。

下面我们再说我这个 Dockerfile 没用到的指令。

EXPOSE

EXPOSE 用于显式容器运行时暴露的端口。如:

EXPOSE 80

端口可以为多个。

这样我们才可以使用 docker run -p 80:80 <容器> 的方式来做端口映射。

CMD

CMD 为容器运行时,需要执行的命令。如果有多个 CMD,只有最后一个 CMD 才会生效。

如果你有命令要执行,就要将它们合并到一起执行,如

CMD echo "This is a test." | wc -

解读开头写的 Dockerfile

现在我们回到我们写的 Dockerfile 文件,在上面加上注释:

# 使用 node 镜像
FROM node:16 AS build
# 设置容器内的目录,通常我们会使用 app 目录
WORKDIR /app
# 项目文件拷贝到容器 /app 下
COPY . .
# 下载依赖包,并构建打包文件
RUN yarn && yarn build

# 使用 nginx 镜像
FROM nginx
# 跳转到 nginx 的 80 静态服务对应的目录
WORKDIR /usr/share/nginx/html
# 删掉里面的文件
RUN rm -rf ./*
# 将我们在 node 镜像的打包文件拷贝到这里
COPY --from=build /app/build .

这里有一些隐藏点,我说一下:

  • yarn 其实在 node 镜像中就安装了,所以我们可以直接执行 yarn 命令
  • nginx 启动的时候,其实预先给一个目录启动了 80 端口的静态服务,我们将打包的文件覆盖掉就行了。另一种做法是改 nginx.conf 配置文件,比如要升级为 https 的时候就要这样做。
  • nginx 镜像里面已经写了 EXPOSE 80 和 CMD ["nginx", "-g", "daemon off;"],已经启动好服务并暴露了容器端口给外部,所以我们不需要额外写。

构建镜像

下面我们执行

docker build -t react-nginx .

来构建镜像。其中 -t 表示打个tag,其实就是设置镜像名。

这个命令默认会取当前工作目录下的 Dockerfile 来构建镜像,但如果你想指定一个配置文件,可以使用 -f /path/to/Dockerfile 选项。

在正式构建前,docker 会做一些初步的语法校验,如果发现不合法会报错,中止流程。

最后,我们执行

docker run -d -p 80:80 react-nginx

然后打开 localhost,就能看到我们的网站了。

文章首发于我的公众号:前端西瓜哥