关注微信公众号《云原生CTO》更多云原生干货等你来探索
专注于 云原生技术
分享
提供优质 云原生开发
视频技术培训
面试技巧
,及技术疑难问题 解答

云原生技术分享不仅仅局限于Go
、Rust
、Python
、Istio
、containerd
、CoreDNS
、Envoy
、etcd
、Fluentd
、Harbor
、Helm
、Jaeger
、Kubernetes
、Open Policy Agent
、Prometheus
、Rook
、TiKV
、TUF
、Vitess
、Argo
、Buildpacks
、CloudEvents
、CNI
、Contour
、Cortex
、CRI-O
、Falco
、Flux
、gRPC
、KubeEdge
、Linkerd
、NATS
、Notary
、OpenTracing
、Operator Framework
、SPIFFE
、SPIRE
和 Thanos
等


放弃Docker,在没有 Dockerfile 的情况下构建镜像
您需要容器来构建镜像。是的,你没听错。
对于那些通过Docker
构建容器的人(嗯,我相信我们中的大多数人)来说,镜像似乎有点原始的性质。我们被教导从Dockerfile
开始,使用该文件构建镜像,然后才从该镜像运行容器。或者,我们可以运行一个容器,从镜像仓库中指定一个镜像,但主要思想仍然存在——首先是镜像,然后才是容器。
但是如果我告诉你实际的工作流程是相反的呢?甚至当您使用Docker
、podman
或buildah
构建您的第一个镜像时,尽管是隐式地,您已经在底层运行容器了!
如何创建容器映像
让我们避免任何毫无根据的情况,仔细看看镜像构建过程。发现这种行为的最简单的方法是使用下面的Dockerfile
构建一个简单的镜像:
FROM debian:latest
RUN sleep 2 && apt-get update
RUN sleep 2 && apt-get install -y uwsgi
RUN sleep 2 && apt-get install -y python3
COPY some_file /
在构建镜像时,尝试在另一个终端上运行docker stats -a
:
> 观看构建镜像的画面地址:https://iximiuz.com/you-need-containers-to-build-an-image/docker-build.mp4
嗯,我们自己并没有启动任何容器,然而,docker
的数据显示有3个容器🙈但是为什么呢?
稍微简化一点,可以将镜像视为内部带有文件系统的存档文件。此外,它们还可能包含一些配置数据,比如在容器启动时执行的默认命令、暴露的端口等,但我们将主要关注文件系统部分。幸运的是,我们已经知道,从技术上讲,运行容器并不需要镜像。与虚拟机不同,容器只是在Linux
主机上隔离和限制进程。它们确实形成了一个隔离的执行环境,包括个性化的根文件系统,但启动容器的最低限度只是一个包含单个可执行文件的文件夹。所以,当我们从一个镜像启动一个容器时,镜像被解包,它的内容以文件系统bundle
的形式提供给容器运行时,例如,一个包含未来根文件系统文件和一些配置的常规目录(你可能已经开始考虑的所有这些层都被一个像overlay fs
的联合挂载驱动抽象掉了)。因此,如果你没有镜像但你需要apline Linux
发行版作为执行环境,您总是可以获取 Alpine
的 rootfs
( 2.6 MB
) 并将其放在磁盘上的常规文件夹中,然后混入您的应用程序文件,将它提供给容器运行时。
然而,为了充分发挥容器的力量,我们需要方便的形象建设设施。从历史上看,Dockerfiles
一直服务于此目的。任何Dockerfile
在一开始都必须有FROM
指令。该指令指定了基本镜像,而Dockerfile
的其余部分描述了基本镜像和派生镜像(即当前镜像)之间的区别。
最基本的容器镜像是所谓的scratch
镜像。它对应一个空文件夹,Dockerfile
中的FROM scratch
指令的意思是noop
。
现在,让我们来看看这幅深受喜爱的apline
镜像:
# https://github.com/alpinelinux/docker-alpine/blob/v3.11/x86_64/Dockerfile
FROM scratch
ADD alpine-minirootfs-3.11.6-x86_64.tar.gz /
CMD ["/bin/sh"]
即要制作 Alpine Linux
发行版镜像,我们只需要将其根文件系统复制到一个空文件夹(临时镜像)即可!好吧,我敢打赌,到目前为止,您所见过的 Dockerfiles
很少有那么简单。很多时候,我们需要利用发行版的设施准备未来的容器和最常见的例子之一的文件系统可能是当我们需要使用预先安装一些外部包yum
,apt
或apk
:
FROM debian:latest
RUN apt-get install -y ca-certificates
但是,apt
如果我们在 Fedora
主机上构建这个镜像,我们怎么能运行呢?容器来救援!每次,Docker
(或 buildah
,或 podman
等)RUN
在 Dockerfile
中遇到一条指令,它实际上都会触发一个新容器!该容器的包由基础镜像加上 Dockerfile
(如果有)的前面指令所做的所有更改构成。当该RUN
步骤的执行完成时,对容器文件系统所做的所有更改(所谓的diff
)成为正在构建的镜像中的一个新层,并且该过程从下一个 Dockerfile
指令开始重复。

使用 Dockerfile 构建镜像
回到本文开头的原始示例,读者可能已经注意到,我们在第二个终端中看到的容器数量RUN
与 Dockerfile
中的指令数量完全对应。对于对容器的内部构造有深入了解的人来说,这听起来很明显。然而,对于我们其他拥有相当动手容器经验的人来说,基于 Dockerfile
(并且非常流行)的工作流程可能会掩盖一些事情。
幸运的是,尽管 Dockerfiles
是描述镜像的事实上的标准,但它并不是唯一存在的方式。因此,当使用 Docker
时,我们可以在commit
任何运行的容器中生成一个新的镜像。自启动以来,容器内运行的任何命令对容器文件系统所做的所有更改都将形成由commit
创建的镜像的最顶层,而基础将取自用于创建所述容器的镜像。虽然,如果我们决定使用这种方法,我们可能会面临一些可重复性问题。
如何在没有 Dockerfile 的情况下构建镜像
有趣的是,一些新颖的镜像构建工具并不认为 Dockerfile
是一种优势,而是一种限制。例如,buildah
推广了另一种命令行镜像构建过程:
https://www.redhat.com/sysadmin/building-buildah
# Start building an image FROM fedora
$ buildah from fedora
> Getting image source signatures
> Copying blob 4c69497db035 done
> Copying config adfbfa4a11 done
> Writing manifest to image destination
> Storing signatures
> fedora-working-container # <-- name of the newly started container!
# Examine running containers
$ buildah ps
> CONTAINER ID BUILDER IMAGE ID IMAGE NAME CONTAINER NAME
> 2aa8fb539d69 * adfbfa4a115a docker.io/library/fedora:latest fedora-working-container
# Same as using ENV instruction in Dockerfile
$ buildah config --env MY_VAR="foobar" fedora-working-container
# Same as RUN in Dockerfile
$ buildah run fedora-working-container -- yum install python3
> ... installing packages
# Finally, make a layer (or an image)
$ buildah commit fedora-working-container
我们可以在交互式命令行镜像构建或将所有这些指令放入shell
脚本之间进行选择,但无论实际选择如何,buildah
的方法都使运行构建器容器的需求变得明显。跳过关于这种构建技术的利弊的咆哮,我只想注意到,如果buildah
的方法早于 Dockerfiles
,那么镜像构建的性质可能会更加明显。
最后,让我们简要介绍一下另外两个突出的工具——Google
的kaniko
和 Uber
的makisu
. 他们试图从稍微不同的角度解决镜像构建问题。这些工具在构建镜像时并没有真正运行容器。相反,他们在遵循镜像构建说明的同时直接修改本地文件系统🤯 也就是说,如果您不小心在笔记本电脑上启动了这样的工具,很可能您的主机操作系统将被擦除并替换为镜像的 rootfs
。所以,当心。显然,这些工具应该在已经存在的容器内完全执行。这解决了一些安全问题,绕过了提升构建器进程权限的需要。尽管如此,虽然技术本身与传统的 Docker
或 buildah
的方法有很大不同,但容器仍然存在。主要区别在于它们已移出构建工具的范围。
结论
结果证明容器镜像的概念非常方便。分层镜像结构与诸如overlay fs
之类的联合挂载相结合,使得镜像的存储和使用非常高效。声明式基于 Dockerfile
的方法支持可重现和可缓存的工件构建。这使得容器镜像的想法变得如此广泛,以至于有时它看起来像是容器世界中不可分割的原型部分。然而,正如我们在文章中看到的,从实现的角度来看,容器独立于镜像。相反,大多数时候我们需要容器来构建镜像,而不是反之亦然。
编写代码,而不是战争!
参考地址[1]
参考资料
参考地址: https://iximiuz.com/en/posts/you-need-containers-to-build-an-image/




