0

Docker镜像多阶段构建

 2 years ago
source link: https://chenjiehua.me/linux/docker-multistage-build.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Docker镜像多阶段构建 

当我们采用容器化来部署项目时,对于大部分的程序(除开脚本语言)我们一般需要将源码编译成二进制的可执行文件,再将可执行文件打包成docker镜像。如果在一个Docker镜像中完成整个流程,那构建出来的镜像文件必然很大(包含了编译环境);如果把编译过程和最终的可执行文件拆分为两个镜像,又会增加维护成本。那么有什么方法来解决这个问题呢?

在构建Docker镜像时,一个最大的挑战是如何减小目标镜像的体积。

在Dockerfile中,每一行指令(ADD、COPY、RUN)会给镜像增加一个新的层,所以一般情况下我们需要在当前层将下一层不再使用的内容清除掉,而这需要利用shell的各种小trick和一些特殊的写法。

在开头提到的问题中,先来看看两个镜像的做法,其中一个大镜像包含完整的编译环境,另一个小镜像只包含最终的可执行文件(用于生产环境运行)。因此,我们需要维护两个Dockerfile。

hello.go

package main

import (
        "fmt"
)

func main(){
        fmt.Println("hello world")
}

Dockerfile.build

FROM golang:1.17
WORKDIR /go/src/app
COPY hello.go .
RUN go build -o hello hello.go

Dockerfile

FROM alpine:latest
WORKDIR /app
COPY hello .
CMD ["./hello"]

build.sh

#!/bin/bash

echo Building hello-build

docker build -t myhello:build . -f Dockerfile.build
docker container create --name extract myhello:build
docker container cp extract:/go/src/app/hello .
docker container rm -f extract

echo Building hello-go
docker build --no-cache -t myhello:latest .

最终执行:

$./build.sh
$docker run --rm myhello
hello world

可以看到,即使是编译一个简单的程序,我们需要维护的Dockerfile和脚本也是如此复杂。

多阶段构建

利用Docker的多阶段构建,我们可以在一个Dockerfile中多次使用 FROM 语句,每一个FROM意味着使用不同的基础镜像并且开始一个新的构建,而且我们还可以直接在多个构建之间复制需要的文件。这样,我们就只需要维护一个Dockerfile。

FROM golang:1.17 AS builder
WORKDIR /go/src/app
COPY hello.go .
RUN go build -o hello hello.go


FROM alpine:latest
WORKDIR /app
COPY --from=builder /go/src/app/hello .
CMD ["./hello"]

构建镜像并运行:

$docker build -t myhello:latest .
$docker run --rm myhello
hello world

这样子整个流程就简单多了,我们不再创建中间镜像也不再需要将可执行文件复制到本地环境中。

在指定 --from 的时候,我们还可以使用外部镜像,比如:

COPY --from=nginx:latest /etc/nginx/nginx.conf /nginx.conf

构建指定阶段

如果我们的Dockerfile中包含多个构建,我们可以指定只构建某一部分,比如:

$docker build --target builder -t myhello:build .

这种方式在某些场景下会非常有用:

  • 调试某个特定的构建;
  • 使用 debug 来构建带有调试符号信息的程序和工具,而不影响production
  • 使用 testing 来构建带有测试数据的环境,而 production 始终使用真实数据;

参考:https://docs.docker.com/develop/develop-images/multistage-build/


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK