惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
B
Blog RSS Feed
GbyAI
GbyAI
Google DeepMind News
Google DeepMind News
B
Blog
博客园 - 司徒正美
Vercel News
Vercel News
A
About on SuperTechFans
Martin Fowler
Martin Fowler
WordPress大学
WordPress大学
Recent Announcements
Recent Announcements
S
SegmentFault 最新的问题
博客园_首页
Apple Machine Learning Research
Apple Machine Learning Research
Stack Overflow Blog
Stack Overflow Blog
L
LINUX DO - 热门话题
Y
Y Combinator Blog
F
Full Disclosure
月光博客
月光博客
C
Cyber Attacks, Cyber Crime and Cyber Security
MongoDB | Blog
MongoDB | Blog
The Cloudflare Blog
The Hacker News
The Hacker News
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
A
Arctic Wolf
Scott Helme
Scott Helme
V
Visual Studio Blog
C
Cybersecurity and Infrastructure Security Agency CISA
T
Tor Project blog
P
Privacy International News Feed
Spread Privacy
Spread Privacy
G
GRAHAM CLULEY
Microsoft Security Blog
Microsoft Security Blog
N
News and Events Feed by Topic
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
PCI Perspectives
PCI Perspectives
小众软件
小众软件
博客园 - 【当耐特】
Cloudbric
Cloudbric
S
Secure Thoughts
L
LINUX DO - 最新话题
Google Online Security Blog
Google Online Security Blog
T
Troy Hunt's Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
W
WeLiveSecurity
V
Vulnerabilities – Threatpost
人人都是产品经理
人人都是产品经理
酷 壳 – CoolShell
酷 壳 – CoolShell
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
量子位

WAYJAM

告别 NPM Token:迁移到 Trusted Publishing 全记录 Sing-box:IPv6 优先出站与 Netflix 强制 IPv6 分流 [All In One] HomeLab 虚拟化 iKuai + Mihomo - IPv6 支持 [All In One] HomeLab 虚拟化 iKuai + Mihomo LiteLLM 多模型 API 中转 MacOS Bootstrp with Nix Fluency Support Full Rss [All In One] PVE 中 OpenWrt LXC 重启问题 Tabby 终端配置以及配置全平台同步 Cloudflare Tunnel + Nginx Proxy Manager:服务穿透 PVE 7 升级 8 笔记 PVE 安装 Android x86 & 安装 Magisk Fly.io 部署 Artalk 评论系统 [All In One] HomeLab 2023 搭建总结 [All In One] 轻量的文件 & 媒体服务器配置 [All In One] R71s 主机散热改造 [All In One] PVE LXC 安装 OpenWrt 软路由以及网络规划 [All In One] 安装 Proxmox VE 7.4 从零开始配置Rime - 2023 Self Hosted Bitwarden + Cloudfare HTTPS Debian NFS Server 以及 MacOS Client AdguardHome 和 Clash 透明代理配合使用 使用 Kind 部署 Prow 手记 BackBlaze B2 + Cloudfare Worker 图床 PicGo Amazon S3 插件 自动给模板生成 Inline-Svg Hugo Theme WeUI 发布! Hugo Theme Fluency 发布! Docker Clash 和 透明代理 有限状态机与分布式熔断器 「家庭娱乐」N1刷 EmuELEC 做游戏盒子 「家庭娱乐」N1刷 Armbian 做家庭服务器 分布式限流器之令牌桶实现 Kustomize 管理 Kubernetes 集群 Mixedpaper 主题发布到 Hugo 官方仓库 About 解决 etcd 与 grpc 不兼容问题 Kubernetes 部署 ElasticSearch7 集群 从零开始配置Rime - 2019 转移域名,更换评论系统,更换图床 搭建 NPM Proxy 之预编译包 搭建 NPM Proxy 美区Apple ID终于绑定PayPal成功 无可奈何的页面增强方案 LogStash的调试 GitLab Shell如何通过SSH工作 Python打开类 荒原 [Theme] Bitcron主题Mixedpaper发布! Ngrok端口转发 故事之五 - 柠檬茶 故事之四 - 我是天才 故事之一 Arch滚挂了怎么办? OpenWrt内网IPv6之二 某次更新后Primusrun不能启动Dota2 OpenWrt的DNS智能解析方案 替换Google CDN库 使用Privoxy将socks代理转化为http代理 ArchLinux之软件篇 再次安装ArchLinux手记 博客再次重生 搞了个Minecraft服务器 【LnV工作室荣誉出品】小苹果MV华农大版 雨梦 醉臥石中笑人間 路由器内网IPV6地址分配 OPENWRT安装软件的两个问题 路由器上进行锐捷认证 改了BIOS的LOGO Windows下搭建我的C/C++的开发环境 Linux下用Mentohust认证校园网 Archlinux+Windows 双系统安装手记(UEFI+GPT) Google-code-prettify代码高亮 《末世迷踪》 Hello Net World!
优化 Docker 构建时间
2020-04-08 · via WAYJAM

问题描述

今天看了一下我的一个 Go 的两个服务的构建时间,觉得不是非常满意。这两个服务分别为 server 端 和 worker 端,在使用了多阶段构建的情况下,每次全量编译出两个镜像需要 2.5min,由于两个服务基本属于通过代码结构可以共用代码的形式,只有 entrypoint 稍有不同,所以镜像的构建步骤只有略有不同。观察 docker 镜像的构建日志发现,即便是相同构建命令的 commit 也并没有 use-cache,于是感觉还是有优化空间的。

├── cmd
│   ├── server
│   │   └── main.go # import from internal
│   └── worker
│       └── main.go # import from internal
├── internal
│   ├── server
│   │   └── server.go # import from pkg
│   └── worker
│       └── worker.go # import from pkg
├── pkg
│   ├── pkg1
│   │   ├── some
│   │   │   └── some.go
│   │   ├── another.go

问题分析

原本构建镜像时是分开两份 Dockerfile ,分别构建两个镜像,但除了编译命令以外,编译环境、工具、依赖、运行环境都是一致的。首先,从 Docker 的构建缓存机制入手,由于镜像是分层的,要缩短构建时间,就要尽量复用层,增加 cache 机制命中次数。并且这两个镜像的构建是先后发生的,缓存层并不会带来其他副作用。

FROM golang:alpine as builder
WORKDIR /workdir
RUN apk --no-cache add git make protobuf-dev
COPY . ./
RUN make install && \ # install tools(eg. protoc-gen-go)
    CGO_ENABLED=0 app=worker make build # go build

FROM alpine:latest as prod
WORKDIR /app
COPY --from=0 /workdir/build/worker ./
ENTRYPOINT ["./worker"]

优化思路:

  • 拆开编译和下载依赖的命令(编译工具和依赖都相同)
  • 合并两个 Dockerfile 成一个,并输出两个镜像

方案一 --build-args

我首先想到的是通过构建参数来传入 Dockerfile 然后使用不同的编译命令达到在相应镜像编译出所需程序的效果。

ARG BUILD_APP=server
FROM golang:alpine as base
WORKDIR /workdir
RUN apk --no-cache add git make protobuf-dev
COPY . ./
RUN make install && \ # install tools
    go mod download # download requirements

FROM base as builder
ARG BUILD_APP
RUN CGO_ENABLED=0 app=${BUILD_APP} make build # go build

FROM alpine:latest as prod
ARG BUILD_APP
WORKDIR /app
ENV BINARY_NAME=${BUILD_APP}
COPY --from=builder /workdir/build/${BINARY_NAME} ./
ENTRYPOINT ./$BINARY_NAME

由于 Docker 没有 global args ,所以需要每个 stage 都指定 args 也可以使用 env 等方式传递参数:https://github.com/moby/moby/issues/37345

然后在 Makefile 或者构建脚本内判断参数,编译对应程序即可。

build:
ifeq ($(app),server)
	go build -v -o build/server ./cmd/server
else ifeq ($(app),worker)
	go build -v -o build/worker ./cmd/worker
else
	@echo "build nothing"
endif

执行构建参数使用:docker build -f Dockerfile -t server:tag . --build-arg BUILD_APP=server

方案二 --target

在查询资料的过程中发现多阶段构建还能使用 --target 参数指定输出阶段,于是 Dockerfile 也可以写成:

FROM golang:alpine as base
# ...

FROM base as builder
RUN CGO_ENABLED=0 app=server make build &&\
		CGO_ENABLED=0 app=worker make build  # 

FROM alpine:latest as prod
WORKDIR /app
RUN something

FROM prod as server
COPY --from=builder /workdir/build/server ./
ENTRYPOINT ./server

FROM prod as worker
COPY --from=builder /workdir/build/worker ./
ENTRYPOINT ./worker

base 阶段与方案(一)一致,编译阶段同时编译两个程序,并分别编写两个最终输出镜像层。

使用:

docker build -f Dockerfile -t server:tag . --target server
docker build -f Dockerfile -t worker:tag . --target worker

Reference