跳至主要内容

实操如何缩小docker镜像大小,从1.3GB一路干到10MB

 

在 Docker 中打包应用时,缩小镜像体积是一个重要的优化目标。下面使用我的一个开源项目“分布式ID生成工具”作为调试优化的参考示例,项目地址:https://github.com/luler/hello_id

优化过程


步骤1 使用更小的基础镜像

选择一个轻量级的基础镜像,比如 alpinealpine 是一个非常小的 Linux 发行版,只有几 MB,非常适合构建小型镜像。

镜像体积大小梯度参考:
alpine (5MB) < debian slim (50MB) < ubuntu (70MB) < centos (200MB)

参考项目构建初始Dockerfile如下:

# 基于 Golang 官方镜像构建
FROM golang:1.21.0
# 设置工作目录
WORKDIR /app
# 将本地应用代码复制到容器内的工作目录
COPY . .
# 设置代理、安装依赖、构建二进制文件
RUN go env -w GOPROXY=https://goproxy.cn,direct
RUN go mod download
RUN go build -o main .
# 运行
CMD ["./main"]

优化一个点,修改FROM配置项,使用alpine作为作为基础镜像,配置如下:

# 基于 Golang 官方镜像构建
FROM golang:1.21.0-alpine3.18
# 设置工作目录
WORKDIR /app
# 将本地应用代码复制到容器内的工作目录
COPY . .
# 设置代理、安装依赖、构建二进制文件
RUN go env -w GOPROXY=https://goproxy.cn,direct
RUN go mod download
RUN go build -o main .
# 运行
CMD ["./main"]

打包后的结果对比截图:

  • 可以明显看到,镜像大小从1.3GB,下降到了709.3MB,大小明显缩减了45%


步骤2 合理选择构建阶段

使用多阶段构建来分离编译和运行环境,避免将开发工具和不必要的文件打包到最终镜像中。可以在一个阶段中进行构建和编译,在另一个阶段中只将最终的应用和所需的依赖复制到最终镜像中。

进一步修改上面异步优化的结果,配置如下:

# 基于 Golang 官方镜像构建
FROM golang:1.21.0-alpine3.18 AS builder
# 设置工作目录
WORKDIR /app
# 将本地应用代码复制到容器内的工作目录
COPY . .
# 设置代理、安装依赖、构建二进制文件
RUN go env -w GOPROXY=https://goproxy.cn,direct
RUN go mod download
RUN go build -o main .
# 运行阶段
FROM golang:1.21.0-alpine3.18
WORKDIR /app
#复制必要文件到镜像里面
COPY . .
COPY --from=builder /app/main .
CMD ["./main"]

打包结果截图如下:

  • 镜像大小进一步缩小到257.8MB,比上一步缩小了63%,比未优化版本缩小了惊人的80%

运行阶段还可以继续优化,因为运行节点不一定需要golang:1.21.0-alpine3.18附带的编译环境,所以可以选择更加精简的基础镜像来作为运行环境的基础镜像,从而也可以进一步减少编译后的镜像大小。
进一步的优化后,得到下面的构建配置,只修改了运行阶段的FROM,修改golang:1.21.0-alpine3.18为alpine:3.18:

# 基于 Golang 官方镜像构建
FROM golang:1.21.0-alpine3.18 AS builder
# 设置工作目录
WORKDIR /app
# 将本地应用代码复制到容器内的工作目录
COPY . .
# 设置代理、安装依赖、构建二进制文件
RUN go env -w GOPROXY=https://goproxy.cn,direct
RUN go mod download
RUN go build -o main .
# 运行阶段
FROM alpine:3.18
WORKDIR /app
#复制必要文件到镜像里面
COPY . .
COPY --from=builder /app/main .
CMD ["./main"]

构建结果截图如下:

  • 根据不同需要,区分构建和运行阶段的基础镜像,可以带来非常惊人的优化效果。如上可见,打包构建出来的镜像居然只有43.8MB,比初始镜像缩减了96%,酷!


步骤3 使用 .dockerignore 文件

就像 .gitignore 一样,.dockerignore 可以排除不必要的文件和目录不被添加到 Docker 镜像中。例如,你可以忽略日志文件、测试文件和构建时产生的临时文件。

.git
*.log
bin
runtime
web/*
!web/dist/
!web/dist/**

继承上一步优化结果,新增.dockerignore后,构建结果截图如下:

  • 可见,虽然针对这个项目,优化效果并不大,但是同样是起作用的,特别针对某些代码包很大,额外文件很多的项目,效果肯定是不容小觑的

步骤4 构建参数优化

根据代码语言的特性,构建过程可以对构建参数进行优化,弃用一些调试信息的控制参数,较少外部库依赖,也可以实现缩小镜像大小的目的。
配置内容如下:

# 基于 Golang 官方镜像构建
FROM golang:1.21.0-alpine3.18 AS builder

# 设置工作目录
WORKDIR /app

# 将本地应用代码复制到容器内的工作目录
COPY . .

#安装CA证书(需要请求第三方https接口)、设置代理、安装依赖、构建二进制文件
#-ldflags="-s -w":-s:省略符号表,-w:省略 DWARF 调试信息, 可进一步缩小编译后的二进制文件体积
#CGO_ENABLED=0: 强制禁用CGO,二进制文件将包含所有依赖的代码,不依赖外部动态库,允许使用 scratch 空镜像
RUN apk add --no-cache ca-certificates && go env -w GOPROXY=https://goproxy.cn,direct && go mod download && CGO_ENABLED=0 go build -ldflags="-s -w" -o /app/main .

# 运行阶段
FROM scratch
WORKDIR /app
#复制必要文件到镜像里面
COPY . .
#复制CA证书
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
#复制主程序
COPY --from=builder /app/main .

#设置容器暴露端口
EXPOSE 3000

CMD ["./main"]

构建结果截图如下:

  • 如图,镜像大小又得到了进一步的缩小,仅30MB,相对于原始版本,减小98%了,已经是天壤之别了!


步骤5 使用upx进一步压缩可执行程序文件

如果启动程序是一个单文件,可以使用npx无损压缩这个文件,一般能缩小50%左右的体积大小。但是,需要注意的是,首次运行压缩文件时,因为要自动解压,所以会增加短暂解压延迟,对于项目启动、重启时间敏感的项目酌情使用!

如下是目前最优构建配置版本

# 基于 Golang 官方镜像构建
FROM golang:1.21.0-alpine3.18 AS builder

# 设置工作目录
WORKDIR /app

# 将本地应用代码复制到容器内的工作目录
COPY . .

#安装CA证书(需要请求第三方https接口)、设置代理、安装依赖、构建二进制文件
#-ldflags="-s -w":-s:省略符号表,-w:省略 DWARF 调试信息, 可进一步缩小编译后的二进制文件体积
#CGO_ENABLED=0: 强制禁用CGO,二进制文件将包含所有依赖的代码,不依赖外部动态库,允许使用 scratch 空镜像
#使用upx压缩可执行程序,能够减少程序包50%左右的体积,但会增加启动速度,需要权衡
RUN apk add --no-cache ca-certificates upx && \
    go env -w GOPROXY=https://goproxy.cn,direct && \
    go mod download && \
    CGO_ENABLED=0 go build -ldflags="-s -w" -o /app/main . && \
    upx --best --lzma /app/main

# 运行阶段
FROM scratch
WORKDIR /app
#复制必要文件到镜像里面
COPY . .
#复制CA证书
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
#复制主程序
COPY --from=builder /app/main .

#设置容器暴露端口
EXPOSE 3000

CMD ["./main"]

构建结果截图如下:

  • 至此,把1.3GB干到了10MB,缩小了99%,功德圆满。


总结

  • 缩小docker镜像的方式有很多,如使用较小的基础镜像、多阶段构建、使用.dockerignore排除不必要的文件、构建参数调优、npx压缩程序、合并多个RUN、安装软件包后删除缓存和临时文件等等
  • 对于不同的项目和代码结构,每种优化方式的效果差异很大,要根据项目代码、环境的特点做调试,选择最佳的优化方案













评论

此博客中的热门博文

电脑里的AI帮手:Open Interpreter智能助手食用指南

一、简介 interpreter是一个使用python开发的命令行工具,可以让你在终端中使用类似AI对话的方式,只需简单输入指令要求,即可自动编写程序、执行代码,实现各种自动化操作 interpreter有自动检测输出结果、自动错误修复功能,保证指令执行的可靠运行 具有权限控制与执行确认机制,确保敏感命令不会立即执行(默认开启,但可以关闭) 支持接入各种模型,操作简单,一行命令即可唤起 二、安装 确保本地安装有python、pip等环境 一键安装 pip install open -interpreter 安装完成,查看命令是否可用,运行interpreter -h输出如下: $ interpreter -h 用法: interpreter [选项] Open Interpreter(开放解释器) 选项: -h, --help 显示帮助信息并退出 -p PROFILE, --profile PROFILE 配置文件名。运行` --profiles`可打开配置目录 -ci CUSTOM_INSTRUCTIONS, --custom_instructions CUSTOM_INSTRUCTIONS 语言模型的自定义指令。会追加到系统消息中 -sm SYSTEM_MESSAGE, --system_message SYSTEM_MESSAGE (不建议修改)语言模型的基础提示词 -y, --auto_run 自动运行生成的代码 -nhl, --no_highlight_active_line 关闭代码块中当前行的语法高亮 -v, --verbose 打印详细日志 -m MODEL, --model MODEL 使用的语言模型 -t TEMPERATURE, --temperature TEMPERATURE 语言模型的可选温度参数 -lsv, --llm_supports_v...

跨浏览器书签同步方案:WebDAV + Floccus插件实操指南

  一、简介 Floccus 是一个允许用户在不同浏览器和设备之间私密同步书签的扩展,开源地址: https://github.com/floccusaddon/floccus WebDAV是一种基于HTTP的协议,支持远程文件管理,支持basic授权,部署简单,可以docker私有化部署,也可以选择支持webdav的云盘服务,如坚果云,本文选用私有部署的方式 Floccus支持很多书签数据私有化存储方式,包括webdav,配置接入简单 二、安装 1.安装webdav服务 我这里选择webdav这种方式,只要拥有webdav功能的服务就可以,比如: 坚果云 : https://www.jianguoyun.com/ ,提供webdav服务,路径:账户信息=>安全选项=》添加应用,就可以使用坚果云相关webdav配置来设置floccus了 alist :开源项目alist也提供webdav服务,参考官方说明: https://alist-doc.nn.ci/docs/webdav/。因为alist支持挂载各种云盘 ,所以这种方式可以同步到更多云盘。alist私有部署可参考文章: https://blog.luler.top/d/21 其他支持webdav的服务,如下可以使用docker一键部署wendav : a. 提前安装好docker、docker-compose环境(这里不做讲解) b. 新建docker-compose.yml文件,配置内容如下: 复制 version: '3' services: webdav: image: bytemark/webdav restart: always ports: - "8080:80" environment: AUTH_TYPE: Basic USERNAME: admin #这里basic认证账号 PASSWORD: admin123 #这里basic认证密码 volumes: - ./dav:/var/lib/dav #数据持久化 c. 运行启动 复制 docker-compose up -d d. 访问: http:/...

认识python全栈框架reflex:快速打造工具类网站、模型调用web应用

  一、简介 纯Python编写的,高性能、可自定义的 Web 应用开发框架 网页开发内置组件生态完整,灵活使用、快速接入、快速部署 支持路由页面,可以开发复杂系统、企业级系统,这方面优于gradio、streamlit等全栈框架 开源地址: https://github.com/reflex-dev/reflex ,官网地址: https://reflex.dev 二、基础安装使用 提前准备好Python 3.10+软件环境(不做赘述) 使用pip安装reflex 复制 pip install reflex 初始化项目代码 复制 mkdir reflex_test cd reflex_test reflex init 直接运行 复制 reflex run 运行过程 访问默认页面: http://localhost:3000/ 修改默认代码,页面可以自动重载更新页面 可以参考官方组件库、模板库,直接复制相关代码黏贴即可直接使用 参考官方组件使用与说明: https://reflex.dev/docs/library/ 三、快速接入大模型文生图简单示例 参考代码: https://github.com/luler/reflex_ai_fast 实现功能:旨在通过便捷的操作页面,迅速接入和体验大模型生图功能 安装配置使用: 拉取代码 复制 git clone https://github.com/luler/reflex_ai_fast cd reflex_ai_fast 新增编辑.env,输入文生图大模型配置,内容如下:(注意:需要支持openai兼容的文生图接口https://platform.openai.com/docs/guides/text-generation,如果不支持,需要自行调整代码,也很简单) 复制 OPENAI_BASE_URL =https://xxx/v1 OPENAI_API_KEY =sk-xxx 打包并导出前端代码 复制 API_URL =http:// 127.0 . 0.1 : 8080 reflex export --frontend- on ly docker-compose一键运行(提前部署好docker、docker-compose环境) 复制 docker-compose up -d 访问页面: http...