如何缩减docker镜像体积大小
docker 如何消减镜像体积?
docker 体积越小,那么传输和部署就越方便。
实际开发过程中,大部分人都是从dockerhub中拉取现有镜像,然后基于此构建自己的镜像,实际上,这样的操作虽然方便,但是带来的问题也很大。
就是,打包后的 image 体积很大。
如何解决这个问题?推荐三个方法
利用多阶段构建方法
新建文件夹 nodefs
在文件夹下新建test.js
// test.js
const express = require('express')
const app = express()
app.get('/', (req, res) => res.send('Hello World!'))
app.listen(3000, () => {
console.log(`Example app listening on port 3000!`)
})
执行 npm init
,来创建生成 package.json
编辑 package.json,添加如下代码:
{
"name": "hello-world",
"version": "1.0.0",
"main": "test.js",
"dependencies": {
"express": "^4.16.2"
},
"scripts": {
"start": "node test.js"
}
}
创建 Dockerfile 文件并编辑:
FROM node:8
EXPOSE 3000
WORKDIR /app
COPY package.json test.js ./
RUN npm install
CMD ["npm", "start"]
然后利用 docker 命令 docker build -t node-vanilla .
构建一个镜像,构建完成后
再次编辑 Dockerfile :
FROM node:8 as build
WORKDIR /app
COPY package.json test.js ./
RUN npm install
FROM node:8
COPY --from=build /app /
EXPOSE 3000
CMD ["test.js"]
然后构建一个新镜像 docker build -t node-vanillas-m .
,完成后
利用命令 docker images |grep node-
查看镜像,得到结果如下
node-vanilla-m latest 331b81a245b1 2seconds ago 903MB
node-vanilla latest 8ec8897e1157 12seconds ago 904MB
可以看到,分层构建后的镜像体积稍微减小了一点,但是与正体体积相比几乎可以忽略,这是因为我们的代码非常简单,Dockerfile 分层也比较少
利用distroless
换句话说,我们的容器在运行时仅仅依赖 node ,但是我们构建的镜像不仅仅包括 node,还有许许多多的配置文件,二进制文件,以及一些实用程序等,所以我们可以删除不必须的内容,来缩小镜像体积。
Google 在 github 上创建了 distroless 项目来提供只包含运行应用程序的最新容器镜像
重新编辑 Dockerfile 文件:
FROM node:8 as build
WORKDIR /app
COPY package.json test.js ./
RUN npm install
FROM gcr.io/distroless/nodejs
COPY --from=build /app /
EXPOSE 3000
CMD ["test.js"]
然后构建一个新的镜像 docker build -t node-distroless .
运行这个镜像验证一下构建是否可用
docker run -p 3000:3000 -ti --rm --init node-distroless
查看这个镜像
docker images | grep node-distroless
node-distroless latest 7b4db3b7f1e5 2seconds ago 76.7MB
这个镜像的大小简直令人惊讶!
使用 distroless 的时候有一些注意事项,比方,想检查某个正在运行的容器,
dc exec containername bash
这就像是建立了一个SSH会话一样,但是我们的镜像里没有其他的二进制文件,也没有shell,唯一存在的二进制文件就是 node.js,这是个坏消息也是个好消息
坏消息是因为在容器内你只能执行二进制
dc exec containername node
好消息则是因为即便攻击者利用你的应用程序获得对容器的访问权限,也无法像访问shell那样造成太多破坏。
即更少的二进制文件意味着更小的体积和更高的安全性。
distroless 的使用基于你不在生产环境进行调试,而是充分利用日志和监控
但是,加入真的需要在生产环境进行调试工作呢?distroless 就会变得异常痛苦。
小体积的Alpine基础镜像
Alpine Linux 是基于 musl libc 和 busybox ,以安全性为目标的轻量级 Linux 发行版。意思是:size 更小,安全性更高!
所以,我们可以把 Alpine 作为父镜像,来减少镜像体积。
但需要注意的是Alpine Linux 中的 C 库不是我们常用的 glibc,而是 muslc。
glibc更常见,速度也更快;
muslc使用较少的空间,并侧重于安全性。
也就是说,使用基于 Alpine 的镜像有可能产生 C 库不同导致的问题。
编辑Dockerfile
FROM node:8 as build
WORKDIR /app
COPY package.json index.js ./
RUN npm install
FROM node:8-alpine
COPY --from=build /app /
EXPOSE 3000
CMD ["npm", "start"]
构建镜像 docker build -t node-alpine .
查看一下构建后的镜像
docker images | grep node-alpine
node-alpine latest aa1f85f8e724 2seconds ago 69.7MB
运行镜像验证是否可用
docker run -p 3000:3000 -ti --rm --init node-alpine
进入容器
docker exec -ti aa1f85f8e724 sh
虽然不支持 bash,但是 sh 是完全可以的
总结
如果在生产环境中运行容器,并且更注重安全性,那么可能distroless镜像更合适
如果更注重更小的镜像体积,那么可以考虑基于Alpine的镜像
原始基础镜像则非常适合用于测试和开发