debian镜像源 wheezy_我是如何优化前端 docker 镜像体积的

c97baf0936853aa55e1d21d1bb5dcfae.png

背景

在最近一次交付产品功能的过程中,发现该产品的前端镜像包竟然有 500M 之大,从本地上传到前场服务器平均需要 15 分钟,效率实在不高。为了提高交付效率和减少 bug 修复周期,对 docker 镜像体积优化十分有必要。

前端工程结构分析

在确定优化策略前,必须对项目工程结构和打包发布流程做下梳理。

1. 代码目录结构

# 客户端代码
  > server             

node server 作为 BFF 层,主要承担接口聚合、代理请求以及登录鉴权的职责,对于非开发环境,node server 还承担着静态文件的管理功能, 而主要核心业务代码则集中在 client 目录下,项目的打包发布流程则通过 .gitlab.yml 文件定义。

2. 打包发布流程

60001eb894fa742ea3b9eed864846740.png
gitlab ci/cd

打包发布流程核心步骤有三个:安装(client 和 server)依赖(dependencies devDependencies)、编译 client 代码并输出到 server/views 目录下、根据 Dockerfile 打包 docker 镜像。

.gitlab.yml package 阶段配置如下:

.npm_build:
  script:
    - npm install
    - export NODE_OPTIONS=$NODE_OPTIONS
    - npm run build

Dockerfile 配置如下:

# 拉取 node 镜像

如何减少 docker 镜像体积?

按照之前的打包部署配置,项目打包出来的镜像体积高达 530M,通过如下手段,最终打出来的镜像包体积为 ???????

1. 剔除无用代码

通过 ci/cd 之后的 client 代码实际上已经通过 webpack 打包成静态文件并输出到 server/views 目录下,用户在正常访问页面时,所需要的静态资源实际都是从 server/views 目录下读取的,因此在非开发环境,client 代码实际已无作用可以剔除。

依据非开发环境无用代码的思路,可以发现不仅 client 代码是无用的,工程根目录下其他一下配置文件如 .npmrc.gitignore.eslintrc 等在非开发环境也是无用的,最后发现非开发环境服务要想正常运行只需要 server、scripts、node_modules、package.json、package-lock.json 这些文件和目录。

docker 镜像中输出最小文件集合有两种办法:通过 .dockerignore 配置禁用文件或通过 COPY 命令指定要输出的文件,针对本工程采用 COPY 命令指定输出文件方法更加简单。

修改 Dockerfile 的配置如下:

# 拉取 node 镜像

通过这种方法,镜像包体积由 530M 变为 512M。

2. 剔除无用依赖

对于非开发环境而言,devDependencies 开发依赖是不需要的,只保留 dependencies 相关依赖即可,可以通过 npm prune --production 命令来剔除 devDependencies

.gitlab.yml package 阶段配置修改如下:

.npm_build

另外,如前所述,client 代码通过 webpack 输出到 server/views 下成为静态资源,client 代码是无用的,client 代码的依赖也同样是无用的,可以考虑将 client 的相关依赖从 dependencies 迁移到 devDependencies

通过这种办法,镜像包体积由 512M 减为 413M。

3. 替换基础镜像源

通过 1,2 两步,镜像体积减少了 22% 左右,但是 413M 的体积仍然让人无法接受,从项目代码层面已经没有更多的手段可以继续优化。

观察 Dockerfile 脚本可以发现,工程镜像继承自 node:12.10.0 镜像,它本身体积非常庞大,而 dockerhub 在提供 node:12.10.0 的同时,提供了体积和功能更精简的 alpine 版本。

修改 Dockerfile 的配置如下:

# 拉取 node alpine 镜像
FROM node:12.10.0-alpine

# 设置工作目录和用户 
WORKDIR /usr/src/app/
USER root

# 拷贝必须的执行文件到工作目录
COPY ./package.json ./package-lock.json ./
COPY ./node_modules ./node_modules
COPY ./scripts ./scripts
COPY ./server ./server
 
# 设置启动命令
CMD npm start

node:12.10.0-alpine 镜像为基础镜像的前端镜像最终体积为 107M,较最开始的镜像体积减少了 80% 左右。

更进一步,node:12.10.0-alpine 镜像相当于在 linux alpine 镜像中安装了 node12.10.0,在这一个过程中会额外安装一些其他依赖(参考 node12 alpine 的配置),这些依赖并不是必须的。

修改 Dockerfile 配置如下:

# 拉取 alpine 镜像
FROM alpine:latest

# 只安装 node
RUN apk add --no-cache nodejs nodejs-npm

# 设置工作目录和用户 
WORKDIR /usr/src/app/
USER root

# 拷贝必须的执行文件到工作目录
COPY ./package.json ./package-lock.json ./
COPY ./node_modules ./node_modules
COPY ./scripts ./scripts
COPY ./server ./server
 
# 设置启动命令
CMD npm start

直接基于 alpine 构建的 node 镜像最终的体积为 98M,比 node:12.10.0-alpine 减少了 10M。

至此,前端镜像体积经过优化从 530M 总共减少了 81.5%。

2e6d687a39dd7bdbfe658f834057c858.png
镜像体积

总结

本文结合具体场景阐述了对 docker 镜像包体积优化的两种思路:

  1. 通过分析代码结构剔除无用的文件,确保 docker 镜像只包含必须的可执行文件。
  2. 在不影响功能的前提下选取体积更小的基础镜像源。值得注意的是:
- linux alpine 版本的镜像底层 c 标准库是基于 musl libc 的,而 linux 其他版本的镜像则基于 glibc,对于某些直接或间接依赖 glibc 的 npm 包而言,直接使用 node:12.10.0-alpine 镜像会出错,如果项目既有依赖 glibc 的 npm 依赖,又有优化镜像体积的需求,可以考虑采用比 alpine 镜像体积更大的 debian 镜像。
- 自定义 node alpine 镜像包的方式在使用 apk add --no-cache nodejs 命令时会自动安装符合 alpine 版本的 node 版本,如本文中的例子最后安装的 nodejs 版本为 12.18.4,如果想安装指定版本 node,可以通过在 alpine 镜像中下载 node 压缩包然后解压到可执行目录的方式。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值