docker base
什么是 docker
Docker是一种容器技术,它可以在操作系统上创建多个相互隔离的容器。容器内可以独立安装软件、运行服务。

- 但是,这个容器和宿主机还是有关联的,比如可以把宿主机的端口映射到容器内的端口、宿主机某个目录挂载到容器内的目录。

比如映射了
3444端口,那容器内3000端口的服务,就可以在宿主机的3444端口访问了。比如挂载了
/aaa到容器的/bbb/ccc,那容器内读写/bbb/ccc目录的时候,改的就是宿主机的/aaa目录,反过来,改宿主机/aaa目录,容器内的/bbb/ccc也会改,这俩同一个。这分别叫做端口映射、数据卷
volume挂载。这个容器是通过镜像起来的,通过
docker run image-name
docker run -p 3444:3000 -v /aaa:/bbb/ccc --name xxx-container xxx-image通过
xxx-image镜像跑起来一个叫做xxx-container的容器。-p指定端口映射,映射宿主机的3444到容器的3000端口。-v指定数据卷挂载,挂载宿主机的/aaa到容器的/bbb/ccc目录。这个镜像是通过
Dockerfile经过build产生的。
流程示意图:

镜像和容器怎么使用?
一般在项目里维护 Dockerfile ,然后执行 docker build 构建出镜像、push 到镜像仓库,部署的时候 pull 下来用 docker run 跑起来。
安装 docker
- 上官网下载桌面端安装包,安装即可。官网地址:https://www.docker.com/

- 安装完成后,会在桌面上出现一个
Docker图标,双击打开,会出现一个控制台。 - 在控制台里,可以看到镜像、容器、网络、卷等信息。

来个简单的例子
- 用 nest 新建一个项目,然后在项目根目录下新建一个
dockerfile文件。
FROM node:18
WORKDIR /app
COPY package.json .
COPY *.lock .
RUN npm config set registry https://registry.npmmirror.com/
RUN npm install
COPY . .
RUN npm run build
EXPOSE 3000
CMD [ "node", "./dist/main.js" ]FROM
node:18是继承node:18基础镜像。WORKDIR /app是指定当前目录为/appCOPY复制宿主机的package.json和lock文件到容器的当前目录,也就是/app下RUN是执行命令,这里执行了npm install。然后再复制其余的文件到容器内。
EXPOSE指定容器需要暴露的端口是3000CMD指定容器跑起来时执行的命令是node ./dist/main.js
构建镜像命令
- 然后在项目根目录下执行
docker build构建镜像。
docker build -t dockerfile-test:first .-t是指定名字和标签,这里镜像名为dockerfile-test标签为first最后面的
.指的是构建路径为当前根目录,默认会在当前目录下找dockerfile文件,如果不是当前目录,可以指定dockerfile文件的路径。构建完成后,可以在
docker desktop里看到刚刚构建的镜像。

- 现在这个镜像有点大,有
1.5G。
运行容器命令
- 先把刚刚构建的镜像跑起来看看。

docker run -d -p 2333:3000 --name jingjing dockerfile-test:first-d是后台运行。-p指定端口映射,映射宿主机的2333端口到容器的3000端口。--name指定容器名最后面的
dockerfile-test:first是指定镜像名和标签。运行完成后,可以在
docker desktop里看到刚刚运行的容器。

- 然后在浏览器里访问
localhost:2333就可以看到hello world了。

这就是 Dockerfile 构建成镜像,然后通过容器跑起来的流程。
现在这个镜像有点大,有
1.5G,怎么减小镜像体积呢?
技巧一:使用 alpine 镜像,而不是默认的 linux 镜像【减小体积】
docker容器内跑的是linux系统,各种镜像的dockerfile都会继承linux镜像作为基础镜像。比如我们刚刚创建的那个镜像,点开详情可以看到它的镜像继承关系:

可以看到最终还是继承了
debian的Linux镜像,这是一个linux发行版。但其实这个
linux镜像可以换成更小的版本,也就是 alpine。它裁剪了很多不必要的
linux功能,使得镜像体积大幅减小了。修改一下上述
dockerfile内容, 使用alpine的镜像:
FROM node:18 改成 FROM node:18-alpine3.14node:18-alpine3.14是使用18版本的node镜像,它底层使用alpine 3.14的基础镜像。重新
build一下镜像【换个名字】
docker build -t dockerfile-test:second .- 可以打开
docker desktop看到second这个镜像会比first这个镜像体积减少大约900M

- 可以点开
second这个镜像的继承关系,可以看到它最终继承的是alpine的linux镜像。

- 虽然
alpine镜像体积小,但是它的linux功能比较少,所以在使用的时候,可能会遇到一些问题。 - 但是一般来说,如果不是特别复杂的项目,使用
alpine镜像是没有问题的。 - 这里可以重新运行一下
second这个镜像,看看效果。
docker run -d -p 2334:3000 --name jingjing dockerfile-test:second
- 然后在浏览器里访问
localhost:2334就可以看到hello world了。
技巧二:docker 的缓存机制【加快构建速度】
FROM node:18-alpine3.14
WORKDIR /app
COPY package.json .
COPY *.lock .
RUN npm config set registry https://registry.npmmirror.com/
RUN npm install
COPY . .
RUN npm run build
EXPOSE 3000
CMD [ "node", "./dist/main.js" ]观察这个
dockerfile,发现里面有两次copy文件的操作。为什么先复制
package.json进去,安装依赖之后再复制其他文件,直接全部复制进去不就行了?不是的,这两种写法的效果不同。
docker是分层存储的,dockerfile里的每一行指令是一层,会做缓存。每次
docker build的时候,只会从变化的层开始重新构建,没变的层会直接复用。也就说现在这种写法,如果
package.json没变,那么就不会执行npm install,直接复用之前的。那如果一开始就把所有文件复制进去呢?
那不管
package.json变没变,任何一个文件变了,都会重新npm install,这样没法充分利用缓存,性能不好。我们来实际试试看就知道了,现在重新跑
docker build,不管跑多少次,速度都很快,因为文件没变,直接用了镜像缓存。
docker build -t dockerfile-test:second .
- 现在我们改下
README.md,然后重新跑一下docker build,看看效果。

现在花了
9.5s,其实是没有重新npm install的。然后改下
package.json,然后重新跑一下docker build,看看效果。

- 现在花了
80s,时间明显多了很多,过程中你可以看到在npm install那层停留了很长时间。
技巧三:使用多阶段构建【减小体积】
还有一个问题就是源码和很多构建的依赖是不需要的,但是现在都保存在了镜像里。
实际上我们只需要构建出来的
./dist目录下的文件还有运行时的依赖。这时可以采用多阶段构建:
多阶段构建
多阶段构建是一种用于减小 Docker 镜像大小的技术。 它通过将构建过程分成多个阶段来实现,每个阶段都可以使用不同的基础镜像和构建步骤, 最终只将必要的文件和依赖包复制到最终镜像中,从而减小镜像的体积。
- 我们来看看如何使用多阶段构建,先把
dockerfile改成下面这样:
FROM node:18-alpine3.14 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 stage
FROM node:18-alpine3.14 as production-stage
COPY --from=build-stage /app/dist /app
COPY --from=build-stage /app/package.json /app/package.json
WORKDIR /app
RUN npm config set registry https://registry.npmmirror.com/
RUN npm install --production
EXPOSE 3000
CMD [ "node", "/app/main.js" ]FROM后面添加一个as来指定当前构建阶段的名字。通过
COPY --from=xxx可以从上个阶段复制文件过来。然后
npm install的时候添加--production,这样只会安装dependencies的依赖。docker build之后,只会留下最后一个阶段的镜像。也就是说,最终构建出来的镜像里是没有源码的,有的只是
dist的文件和运行时依赖。这样镜像就会小很多。现在重新跑一下
docker build,看看效果。
docker build -t dockerfile-test:third -f jing.Dockerfile .- 标签为
third,-f是指定Dockerfile的名字,这里采用了jing.Dockerfile。

- 从图上能看到,镜像体积比没有用多阶段构建的时候小了
250 M。
russ