docker优化:多阶段构建镜像

背景

为什么多阶段构建

  • Docker镜像是分层的,Dockerfile中的每个指令都会创建一个新的镜像层,镜像层可以被复用和缓存。当Dockerfile的指令修改了,复制的文件变化了,或者构建镜像时指定的变量不同了,对应的镜像层缓存就会失效,某一层的镜像缓存失效之后,它之后的镜像层缓存都会失效。

  • 因此我们还可以将RUN指令合并,但是需要记住的是,我们只能将变化频率一致的指令合并。

  • 我们应该把变化最少的部分放在Dockerfile的前面,这样可以充分利用镜像缓存。

  • 通过最小化镜像层的数量,我们可以得到更小的镜像。

实践

因为最近都在看nestjs,所以直接以nestjs来构建镜像

1
2
yarn global add @nestjs/cli
nest new nestjs-admin

稍等一会你会得到这样一个目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
./nestjs-admin
├── .gitignore
├── .prettierrc
├── README.md
├── nest-cli.json
├── package.json
├── src
│ ├── app.controller.spec.ts
│ ├── app.controller.ts
│ ├── app.module.ts
│ ├── app.service.ts
│ └── main.ts
├── test
│ ├── app.e2e-spec.ts
│ └── jest-e2e.json
├── tsconfig.build.json
├── tsconfig.json
├── tslint.json
└── yarn.lock

我们在根目录下创建一个 .dockerignore 文件,内容如下

.dockerignore

1
2
3
4
5
node_modules  
.git
.idea
.vscode
/coverage

然后我们在根目录继续创建一个 Dockerfile 文件,内容如下

Dockerfile

为了避免镜像中打包冗余的文件,我们使用多阶段构建镜像

大幅减小镜像体积的最简单和最快的方法是选择一个小得多的基本镜像。Alpine是一个很小的Linux发行版,可以完成这项工作。只要选择Node.js的Alpine版本,就会有很大的改进。

1
2
3
4
5
6
7
8
9
10
11
FROM node:12-alpine AS dependencies  
WORKDIR /usr/src/app
COPY package.json yarn.lock ./
RUN yarn install --production

FROM node:12-alpine
WORKDIR /usr/src/app
COPY package.json dist ./
COPY --from=dependencies /usr/src/app/node_modules ./node_modules
EXPOSE 3000
CMD [ "node", "dist/main" ]

package增加命令

1
"build:docker": "yarn build && docker build -t bulolo/$npm_package_name:latest . && docker push bulolo/$npm_package_name:latest",

运行docker打包镜像
yarn build:docker