较轻的Docker图像
#网络开发人员 #go #docker #containers

有时,使用自定义Dockerfile会产生大量图像,是的,它们是一堆会重现孤立环境的层。

今天,我将向您展示一种用简单而酷的码头技巧来节省许多空间的方法。

Dockerfiles

要创建自定义映像时,您需要编写一个Dockerfile,如您所知,文件中的每个句子代表一个新图层,让我们看到一个快速示例。

FROM ubuntu:latest # Pulls latest ubuntu image

RUN apt update # Updates apt repositories
RUN apt install figlet # Installs figlet

CMD ["figlet", "-c", "Hello from dockerized ubuntu!"]
 # Runs!

如果您尝试构建并运行以前的Dockerfile

docker build -t hello .
docker run hello

输出将像

一样

Docker run hello output

好吧,输出很漂亮,但是让我们看看我的hello图像使用了多少空间。

docker image ls

输出为

REPOSITORY   TAG       IMAGE ID       CREATED          SIZE
hello        latest    3d0b79dcdd1f   10 minutes ago   109MB

109MB要使用Ubuntu在我的控制台中打印一个Hello World,这实际上是巨大的,现在,考虑一下现实生活中的应用程序Docker Image将使用多少空间。

单阶段Dockerfile

在此示例中,我们将使用超级简单的go rest api,代码已经完成,您可以找到它here
由于此博客文章的范围是如何减少Docker映像的大小,因此我将解释GO代码,但我保证将来会创建有关该帖子的文章。

repository中,您会找到两个Dockerfiles,让我们看看Dockerfile首先

FROM golang:latest

WORKDIR /server
COPY . .

RUN go mod download
RUN go build -o server .

CMD ["./server"]

由此Dockerfile构建的图像将

  1. 拉动最新的golang Docker Image
  2. 创建一个名为/server的目录并将上下文更改为
  3. 将当前目录中的所有文件复制到我们的容器
  4. 执行go mod download以安装所有项目的依赖项
  5. 执行go build -o server .构建应用程序并将bin重命名为server7
  6. 最后,每次我们从此图像运行一个容器
  7. 时执行server二进制文件

非常简单,对吗?让我们构建图像并查看大小,为此,您可以运行以下命令。

docker build -t GoServer .
docker image ls

输出应该类似于

REPOSITORY   TAG       IMAGE ID       CREATED          SIZE
hello        latest    3d0b79dcdd1f   12 minutes ago   109MB
GoServer     latest    34e3ad5acd86   21 minutes ago   1.02GB

1.02GB用于隔离我的服务器环境,可能会令人震惊,但让我们看一下它的包含。

  • 我所有的项目依赖项
  • 全部依赖
  • 轻巧的Linux发行版
  • 我的应用程序的代码
  • 我的应用程序的最后二进制

因此,它实际上存储了许多我们只使用一次的东西,例如,由于我已经编制了我的项目,也许存储所有依赖关系实际上是不必要的,就像我的应用程序的所有代码一样,以及所有的依赖性。

然后,我们可以摆脱上述列表的某些项目,并像

一样保持它
    我所有的项目依赖项 全部依赖
  • 轻巧的Linux发行版
  • 我的应用程序的代码
  • 我的应用程序的最后二进制

但是,我们如何实现这一目标?

为此,Docker实际上有一个解决方案,是建立Dockerfiles的不同策略,将其分配在STAGES中以减少层的数量。

多阶段Dockerfiles

project’s repository中,您还有另一个dockerfile,称为Dockerfile.multi Dockerfile.multi扩展名.multi实际上不是必需的,我只是用它来区分它与以前的dockerfile,让我们看一下

# Build stage
FROM golang:latest AS builder

WORKDIR /build
COPY . .
RUN go mod download

RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o server .

# Run stage
FROM alpine:latest

WORKDIR /server
COPY --from=builder /build/server .

CMD ["./server"]

它看起来真的很复杂,因为它使用FROM两次,但实际上更小,更易于阅读(使用练习)。

当我们构建图像时,这个Dockerfile将做什么?

  1. 拉动最新的golang docker映像,并给予它builder别名
  2. 创建一个名为/build的目录并将上下文更改为
  3. 将当前目录中的所有文件复制到我们的容器
  4. 执行go mod download以安装所有项目的依赖项
  5. 执行go build -o server .构建应用程序并将bin重命名为服务器,其中一些GO FLAGS使我们的二进制可在不同的环境中可执行。

好吧,我们将在此时休息一下。

  • 我们复制了代码
  • 安装项目依赖项
  • 编译了我们的项目 我们想要摆脱的许多文件都在此第一阶段中使用,名为构建器

好,准备好了吗?让我们看看第二阶段。

  1. 拉最新的alpine Docker Image,轻量级Linux发行版
  2. 创建一个名为/server的目录并将上下文更改为
  3. 复制从上一个builder阶段提取的最终二进制server,我们使用from=键参数
  4. 进行此操作
  5. 最后,执行server二进制

这是一条更长的道路,但值得,让我们看到图像使用与以前相同的两个命令所使用的空间,并进行了几次修改。

docker build -t GoLight -f Dockerfile.multi .
docker image ls

,输出为

REPOSITORY   TAG       IMAGE ID       CREATED          SIZE
hello        latest    3d0b79dcdd1f   12 minutes ago   109MB
GoServer     latest    34e3ad5acd86   21 minutes ago   1.02GB
GoLight      latest    309467e3d24e   6 minutes ago    17.5MB

它有效!现在,我们的服务器映像仅使用17.5MB甚至比hello映像少,但是为什么?

多阶段Dockerfiles允许我们从创建图像的特定点提取文件和数据。

因此,我们可以摆脱许多不必要的东西,并在不牺牲功能的情况下节省很多磁盘空间。

不相信我吗?让我们简要介绍使用每个图像的执行容器的执行。

我们将首先使用较重的图像
Docker Run -P 8080:8080 Goserver:最新

然后,此容器创建Web服务器并开始在:8080上收听,目前我们的API只有一个端点,所以让我们执行GET请求到http://127.0.0.1:8080/songs

curl -X GET http://127.0.0.1:8080/songs

,输出应该是下一个

[
    {
        "id": 1,
        "name": "Alabaster",
        "artist": "Foals",
        "album": "Total Life Forever"
    },
    {
        "id": 2,
        "name": "Bravery",
        "artist": "Human Tetris",
        "album": "River Pt. 1"
    },
    {
        "id": 3,
        "name": "Lately",
        "artist": "Metronomy",
        "album": "Metronomy Forever"
    },
    {
        "id": 4,
        "name": "Paranoid Android",
        "artist": "Radiohead",
        "album": "OK Computer"
    }
]

好!重容器正常工作,并使用我的磁盘的1.02GB,现在让我们使用较轻的图像(请记住要停止当前容器)

docker run -p 8080:8080 GoLight:latest

此命令将创建一个容器,但是这次使用较轻的图像,行为是相同的;因此,我们可以执行相同的GET请求。

curl -X GET http://127.0.0.1:8080/songs

,输出与以前相同,请尝试一下!

结论

Docker是一个很棒的工具,如今在市场上广泛使用,尽管它有一些缺点。

当然,大型图像仍然比虚拟机轻,但是我们可以更好地利用Docker来利用其全部潜力。

多阶段Dockerfiles在编译语言中的工作效果更好,因为您可以使用阶段进行构建和编译,而另一个可以实际执行二进制的阶段,但这不是一个限制。

请继续关注更多Docker帖子!