一起养成写作习惯!这是我参与「掘金日新计划 · 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,就能看到我们的网站了。
文章首发于我的公众号:前端西瓜哥