在现代软件开发中,容器化技术已经成为一个不可或缺的部分。Docker 作为最流行的容器化平台之一,为我们提供了强大的工具来打包、发布和运行应用程序。然而,随着应用程序的增长,Docker 镜像的大小也往往会变得庞大,这不仅增加了构建时间,也可能影响到容器的启动速度。今天,我们将探讨如何通过 Dockerfile 的多阶段构建功能来解决这个问题。

实战案例:多阶段构建

什么是多阶段构建?
多阶段构建允许我们在 Dockerfile 中定义多个构建阶段,每个阶段都可以使用不同的基础镜像,并且可以有选择地将文件从一个阶段复制到另一个阶段。这种方法让我们能够分离出编译/构建环境与最终运行环境,从而大幅减少生成的 Docker 镜像的体积。
利用多阶段构建,可以大大减小镜像的大小,但要注意相关依赖文件都要复制到新构建中才能正常运行容器。多阶段构建比较适用于静态编译的程序,比如:Golang。
另外在Dockerfile文件中删除文件,也不会让镜像变小,也可以利用多阶段构建缩小镜像。
多阶段构建

[root@ubuntu2404 golang]#vim hello.go
package main
import "fmt"
func main() {
           fmt.Println("hello, world")
   }       

#第一次构建
[root@ubuntu2404 golang]#cat build.sh 
#!/bin/bash
docker build -t go-hello:$1 .
[root@ubuntu2404 golang]#cat Dockerfile 
FROM golang:alpine3.22
COPY hello.go /opt
WORKDIR /opt
RUN go build hello.go
CMD "./hello"

[root@ubuntu2404 golang]#bash build.sh v1.0
[root@ubuntu2404 golang]#docker run --name hello go-hello:v1.0 
hello, world

#第二次优化
[root@ubuntu2404 golang]#cp Dockerfile Dockerfile-v1.0
[root@ubuntu2404 golang]#vim Dockerfile
FROM golang:alpine3.22 as builder
WORKDIR /opt
COPY hello.go .
RUN go build hello.go

FROM alpine:latest
WORKDIR /opt
COPY --from=builder /opt/hello /opt/hello
CMD ["./hello"]
[root@ubuntu2404 golang]#bash build.sh v2.0
DEPRECATED: The legacy builder is deprecated and will be removed in a future release.
            Install the buildx component to build images with BuildKit:
            https://docs.docker.com/go/buildx/

Sending build context to Docker daemon   5.12kB
Step 1/7 : FROM golang:alpine3.22 as builder
 ---> 88aa171b8c32
Step 2/7 : COPY hello.go /opt
 ---> Using cache
 ---> 4061c57f99a9
Step 3/7 : WORKDIR /opt
 ---> Using cache
 ---> 0c70cd292561
Step 4/7 : RUN go build hello.go
 ---> Using cache
 ---> 896bfd2a09b5
Step 5/7 : FROM alpine:3.3
 ---> a6fc1dbfa81a
Step 6/7 : COPY --from=builder /opt/hello /opt/hello
 ---> cd347a44c5bb
Step 7/7 : CMD ["/hello"]
 ---> Running in 1df61f494aee
 ---> Removed intermediate container 1df61f494aee
 ---> 44574120c5d0
Successfully built 44574120c5d0
Successfully tagged go-hello:v2.0
[root@ubuntu2404 golang]#docker run --name hello go-hello:v2.0 
hello, world
[root@ubuntu2404 golang]#docker images go-hello
REPOSITORY   TAG       IMAGE ID       CREATED          SIZE
go-hello     v2.0      44574120c5d0   37 minutes ago   7.03MB
go-hello     v1.0      416d9a7cc413   57 minutes ago   296MB

#第三次优化
[root@ubuntu2404 golang]#cat Dockerfile
FROM golang:alpine3.22 as builder
WORKDIR /opt
COPY hello.go .
RUN go build hello.go

FROM scratch
WORKDIR /opt
COPY --from=builder /opt/hello /opt/hello
#COPY --from=0 /opt/hello /hello
CMD ["./hello"]

[root@ubuntu2404 golang]#bash build.sh v3.0
DEPRECATED: The legacy builder is deprecated and will be removed in a future release.
            Install the buildx component to build images with BuildKit:
            https://docs.docker.com/go/buildx/

Sending build context to Docker daemon   5.12kB
Step 1/7 : FROM golang:alpine3.22 as builder
 ---> 88aa171b8c32
Step 2/7 : WORKDIR /opt
 ---> Using cache
 ---> 97b788140589
Step 3/7 : COPY hello.go .
 ---> Using cache
 ---> 0789546cb5ee
Step 4/7 : RUN go build hello.go
 ---> Using cache
 ---> e6d6aa6e1c54
Step 5/7 : FROM scratch
 ---> 
Step 6/7 : COPY --from=builder /opt/hello /opt/hello
 ---> f7ef2e1009b0
Step 7/7 : CMD ["/hello"]
 ---> Running in 81d37bcc8c98
 ---> Removed intermediate container 81d37bcc8c98
 ---> 1f3765e7cafb
Successfully built 1f3765e7cafb
Successfully tagged go-hello:v3.0
[root@ubuntu2404 golang]#docker run --name hello1 go-hello:v4.0 
hello, world

#比较三次构建的结果
[root@ubuntu2404 golang]#docker images go-hello
REPOSITORY   TAG       IMAGE ID       CREATED             SIZE
go-hello     v3.0      46aec64556f9   18 seconds ago      2.2MB
go-hello     v2.0      44574120c5d0   59 minutes ago      7.03MB
go-hello     v1.0      416d9a7cc413   About an hour ago   296MB

结论
通过采用 Dockerfile 的多阶段构建方法,我们可以有效地减小 Docker 镜像的尺寸,加快构建和部署的速度。此外,它还帮助我们保持了良好的开发和生产环境之间的分离,使得维护更加容易。

希望这篇指南能为您提供一些关于如何利用 Docker 多阶段构建来优化您的项目部署的见解。尝试一下吧,看看它是如何改变你的工作流程的!

更多推荐