1/15
这一章演示了如何将一个 NodeJS Web 应用打包成镜像,并使用该镜像运行容器,最后可以在浏览器访问这个 Web 应用。
新建文件夹,并在文件夹内创建两个文件:
package.json
{
"name": "docker-simpleweb",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node index.js"
},
"keywords": [],
"author": "wuxianmimi",
"license": "ISC",
"dependencies": {
"express": "^4.18.2"
}
}
index.js
const express = require('express');
const app = express();
app.get('/', (req, res) => {
res.send('你好鸭');
});
app.listen(8080, () => {
console.log("已监听8080端口");
})
-
基础镜像问题
创建 Dockerfile 文件,并增加3条指令:
# 三步曲1:规定基础镜像
FROM alpine
# 三步曲2:运行命令来安装必要的依赖、程序
RUN npm install
# 三步曲3:规定容器启动参数
CMD ["npm", "start"]
然后运行 docker build . 命令,但是我们在第二步安装依赖时会报错:
wuxianmimi docker-simpleweb % docker build .
[+] Building 2.3s (5/5) FINISHED
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 229B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/alpine:latest 2.1s
=> CACHED [1/2] FROM docker.io/library/alpine@sha256:8914eb54f968791faf6a8638949e480fef81e697984fba772b3976835194c6d4 0.0s
=> ERROR [2/2] RUN npm install 0.2s
------
> [2/2] RUN npm install:
#5 0.172 /bin/sh: npm: not found
------
executor failed running [/bin/sh -c npm install]: exit code: 127
报错说没有找到 npm 这个命令,原因就是 alpine 这个镜像中,并没有 NodeJS 环境,当然也不能运行 npm 命令。
我们当然可以在使用 apt 自己安装 NodeJS 环境,但是 Docker 官方提供了 Node 基础镜像,镜像中已经安装了 Node 开发需要的工具,所以我们直接使用即可。
Docker Hub 上 Node 镜像地址:https://hub.docker.com/_/node
在 Supported tags and respective Dockerfile links 一栏下发现有很多支持的镜像标签,这里咪咪建议使用14-alpine 这个标签,原因是 NodeJs 16版本改动较大,可能与一些框架的老版本存在兼容性问题。
视频中讲师使用的 NodeJs 大版本是 8,喜欢原汁原味的同学可以使用这个版本。不过这个版本咪咪没有测试过,不一定能走通后续的流程。
# 三步曲1:规定基础镜像
# alpine ===> node:14-alpine
FROM node:14-alpine
# 三步曲2:运行命令来安装必要的依赖、程序
RUN npm install
# 三步曲3:规定容器启动参数
CMD ["npm", "start"]
运行 docker build .
wuxianmimi docker-simpleweb % docker build .
[+] Building 18.2s (6/6) FINISHED
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 237B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/node:14-alpine 3.5s
=> [1/2] FROM docker.io/library/node:14-alpine@sha256:80e825b1f5ab859498a2f0f98f8197131a562906e5f8c95977057502e68ca05a 13.0s
=> => resolve docker.io/library/node:14-alpine@sha256:80e825b1f5ab859498a2f0f98f8197131a562906e5f8c95977057502e68ca05a 0.0s
=> => sha256:f19b50748cdfe11c52ec028226d21a0cbc5a6b860e311b41a6299c2c43d1bfae 37.78MB / 37.78MB 12.1s
=> => sha256:7bfe7e7dc195d0082ab6b8c2bb09a8a405c51369b5c25c015327227f1390a312 2.43MB / 2.43MB 1.5s
=> => sha256:cb8b674ae1bc8e69210b956081a7c092daabfd2a4834f660ef3e0cadaab5db44 449B / 449B 1.9s
=> => sha256:80e825b1f5ab859498a2f0f98f8197131a562906e5f8c95977057502e68ca05a 1.43kB / 1.43kB 0.0s
=> => sha256:98f1a5d744f2343703913c3981098ac92fed74a6edec9133291d9f0ad4a03fe8 1.16kB / 1.16kB 0.0s
=> => sha256:8673fd44cb467741701c5006a1edca4f315bc1ac8af2964fe0a9586c7bbe195f 6.45kB / 6.45kB 0.0s
=> => extracting sha256:f19b50748cdfe11c52ec028226d21a0cbc5a6b860e311b41a6299c2c43d1bfae 0.7s
=> => extracting sha256:7bfe7e7dc195d0082ab6b8c2bb09a8a405c51369b5c25c015327227f1390a312 0.1s
=> => extracting sha256:cb8b674ae1bc8e69210b956081a7c092daabfd2a4834f660ef3e0cadaab5db44 0.0s
=> [2/2] RUN npm install 1.5s
=> exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:5a8723a245fdf92ef58101dd66e664c3365af9015b84adb30fc93a58dd25b07a 0.0s
构建成功,我们运行一下
wuxianmimi docker-simpleweb % docker run 5a8723a245fd
npm ERR! code ENOENT
npm ERR! syscall open
npm ERR! path /package.json
npm ERR! errno -2
npm ERR! enoent ENOENT: no such file or directory, open '/package.json'
npm ERR! enoent This is related to npm not being able to find a file.
npm ERR! enoent
npm ERR! A complete log of this run can be found in:
npm ERR! /root/.npm/_logs/2023-01-09T02_53_07_209Z-debug.log
报错了,说是没有 package.json 这个文件,什么情况?(可能由于 Docker Engine 版本差异,视频中讲师在构建阶段就会报着个错误)
-
COPY 指令
缺少 package.json 这个文件,是因为目前容器内的文件系统快照,是 node:14-alpine 这个镜像的拷贝,所以里面当然不会有我们的代码。
所以我们需要在构建镜像的时候,将我们的代码拷贝到容器内,这需要用到 COPY 指令:
COPY [--chown=<user>:<group>] <src>... <dest>
COPY [--chown=<user>:<group>] ["<src>",... "<dest>"]
COPY 指令把 <src> 的文件和文件夹,拷贝到容器内的文件系统 <dest> 中。
在我们 Dockerfile 中添加 COPY 指令:
# 三步曲1:规定基础镜像
FROM node:14-alpine
# 三步曲2:运行命令来安装必要的依赖、程序
COPY ./ ./
RUN npm install
# 三步曲3:规定容器启动参数
CMD ["npm", "start"]
这里咪咪建议在项目根目录创建一个 .npmrc 文件,在文件内添加一行:
registry=https://registry.npmmirror.com
这是 npm 镜像地址,对大陆用户比较友好,否则在容器内安装 npm 依赖的时间很长很长。。。(后面所有 NodeJS 项目都一样)
重新构建运行容器:
wuxianmimi docker-simpleweb % docker run 6c8d589ccc7e
> docker-simpleweb@1.0.0 start /
> node index.js
已监听8080端口
服务成功运行了,我们打开本地浏览器,在地址栏输入 localhost:8080 并回车:
无法访问应用
咦,网站无法访问,什么情况?
-
端口映射
当我们在本地浏览器前往 http://localhost:8080 时,请求的是本地 8080 端口,而不是容器内的 8080 端口。而我们本地并没有程序监听这个端口,所以也不会有响应。
默认情况下,容器内的端口是不会**接收**到外部的请求的,我们需要在运行容器时,指定端口映射的规则。
在 docker run 命令添加 -p [本地端口]:[容器端口] 选项,增加端口映射:
wuxianmimi docker-simpleweb % docker run -p 8080:8080 b964820c57bd7
> docker-simpleweb@1.0.0 start /
> node index.js
已监听8080端口
重新在浏览器访问 localhost:8080,成功返回:
获得返回数据
-
工作目录
我们目前的项目文件,都是通过 COPY 指令拷贝到容器内的根目录的,这样的做法其实不太好。原因是根目录还有很多其他的文件,可能会与我们的项目有冲突。
/ # ls
Dockerfile home mnt package.json sbin usr
bin index.js node_modules proc srv var
dev lib opt root sys
etc media package-lock.json run tmp
解决办法就是在 Dockerfile 中使用 WORKDIR 指令,这个指令设置一个文件夹,成为之后 RUN、CMD、COPY 这些指令的起始目录。
更新我们的 Dockerfile:
# 三步曲1:规定基础镜像
FROM node:14-alpine
# 增加工作目录
WORKDIR /usr/app
# 三步曲2:运行命令来安装必要的依赖、程序
COPY ./ ./
RUN npm install
# 三步曲3:规定容器启动参数
CMD ["npm", "start"]
重新构建镜像并运行,现在在容器中的项目文件结构:
/usr/app # ls
Dockerfile index.js node_modules package-lock.json package.json
/usr/app # cd /
/ # ls
bin dev etc home lib media mnt opt proc root run sbin srv sys tmp usr var
/ #
-
优化构建
目前构建镜像的流程已经走通了,但是仍有一个细节可以优化。
虽然 docker build 每一步指令都有缓存机制,只要上一步指令不更改,当前指令就会使用缓存,节省时间。但是如果我们现在更改了 index.js 的内容,我们肯定需要重新构建镜像,由于 RUN npm install 这步之前的指令肯定会变动(因为我们改了代码),所以每次更改代码后构建都需要重新安装依赖,比较浪费时间。
于是我们可以分两步 COPY 文件,先将 package.json 拷贝进容器,执行安装依赖的指令,再将其余文件拷贝进容器。这样的话,只要 package.json 不变,安装依赖这一步就可以用缓存的镜像。
# 三步曲1:规定基础镜像
FROM node:14-alpine
# 增加工作目录
WORKDIR /usr/app
# 三步曲2:运行命令来安装必要的依赖、程序
COPY ./package.json ./
COPY ./.npmrc ./
RUN npm install
COPY ./ ./
# 三步曲3:规定容器启动参数
CMD ["npm", "start"]

浙公网安备 33010602011771号