内容简介:【编者的话】本文介绍了除了Docker以外构建容器镜像的各种开源方案和它们各自的优缺点。在这篇文章里,我将会介绍几种无需用到 Docker 本身即可构建容器的方法。我将会使用另请参阅
【编者的话】本文介绍了除了 Docker 以外构建容器镜像的各种开源方案和它们各自的优缺点。
在这篇文章里,我将会介绍几种无需用到 Docker 本身即可构建容器的方法。我将会使用 OpenFaaS 作为案例研究,它使用了 OCI 格式的容器镜像作为它的工作负载。简单来说,我们可以把 OpenFaaS 看作是一个面向 Kubernetes 的 CaaS 平台,我们可以在上面运行微服务,而且它为我们带来了免费的 FaaS 以及由事件驱动的工具。
另请参阅 OpenFaaS.com
文章里的第一个方案将会展示如何使用 Docker 命令行里提供的内置的 buildkit 选项,随后会介绍一下独立运行的(只支持Linux) buildkit ,其次就是 Google 的容器构建工具, Kaniko 。
注意:这篇文章涵盖的是能够基于一份 Dockerfile 即可构建产出一个镜像的相关工具,因此任何比如限制用户只能使用 Java 或者 Go 的情况不在本文讨论范围。
然后我将会做一个总结,让你知道如何得到一些建议、反馈,以及自己关于容器 工具 周边的一些想法和需求的用户故事。
那么,Docker 有什么问题呢?
其实也没啥好说的,Docker 在 armhf
、 arm64
还有 x86_64
上都运行地很好。Docker 命令行的主要功能已经不再是构建/装载/运行了,还包括了拖延了数年之久的沉重负担,现在它把 Docker Swarm 还有 Docker EE 的一些功能都捆绑到了一些。
有些人做过一些努力,试图将 "docker" 剥离出来,回归其原本的组件部分,我们都爱上了那个最初的UX:
- Docker - docker 本身现在是使用 containerd 来运行容器,而且已经支持通过启用 buildkit 来实施更高效地,缓存式的构建任务。
- Podman 和 buildah 的结合 - 这是 RedHat 和 IBM 他们在做的尝试,使用他们自己的 OSS 工具链来生成 OCI 镜像。Podman 标榜的是无守护进程和去root,但是始终需要挂载 overlay 文件系统以及使用 Unix 套接字。
- pouch - Pouch 来自阿里巴巴,它被称为 “一个高效的企业级容器引擎”。它同 Docker 一样使用的是 containerd,而且同时支持 runc 带来的容器级别的隔离,以及像 runv 这样 “轻量级虚拟机”。此外,它还把更多的 精力放在了镜像分发以及强隔离方面 。
- 独立的 buildkit - buildkit 是由 Docker 公司的 Tõnis Tiigi 发起的,它是一款全新的兼顾了缓存和并发能力的容器构建工具。buildkit 目前只支持作为守护进程运行,但是你将会从人们那里听到完全相反的说辞。它们会 fork 该守护进程然后在一次构建结束后干掉它。
- img - img 是一款由 Jess Frazelle 编写的工具,经常在这些指导手册里被引用,而且它是 buildkit 的一次重新包装。也就是说,和上述提到的其他方案相比,我并没有看到它有啥特别吸引人的地方。该项目 一直活跃到2018年底,此后仅收到了一些补丁 。img 声称它是无守护进程的,但是它用到了 buildkit,因此这里面可能有一些黑科技。我听说
img
提供了比 buildkit 本身的命令行buildctr
更棒的UX,但是也应该注意的是,img 只发布了x86_64
下的版本,而没有针对 armhf / arm64 的二进制文件。
img
的一个替代方案可能会是 k3c
,它也引入了一个运行时组件,并且计划加入对 ARM 架构的支持。
- k3c - 这是一个 Rancher 最近的实验项目,它借助 containerd 和 buildkit 重新还原了最初的 Docker 版本所具备的原始而又经典的,香草一样精巧的用户体验。
以上所有方案里,我认为我最喜欢的是 k3c,但是它还非常稚嫩,而且因为把所有东西都打包到了一个二进制文件里,这很可能造成和其他软件存在冲突,目前它运行它自己内嵌的 containerd 和 buildkit 执行文件。
注意:如果你是 RedHat 的客户,并且购买了支持服务的话,那么你确实应该物尽其用,使用他们一整套的工具链。我查看了一些示例,并且看到了一个用到了我那篇“经典的”多阶段构建的博客文章。你可以比较一下这两个例子,看看自己更喜欢 buildah 还是 Dockerfile 。
那么,由于我们在这里关注的是“构建”部分,并且想要了解的是那些相对稳定的方案,接下来我将会看看下面这些选项:
- docker 内置的 buildkit;
- 单独运行的 buildkit;
- 以及 kaniko。
OpenFaaS 命令行可以输出一个标准的任何构建工具都可以使用的“构建上下文”,因此我们可以方便地验证如上所有或者其他更多方案。
构建一个测试应用
让我们从一个 Golang 的 HTTP 中间件开始吧,这是一个函数和一个微服务之间的交错部分,而它展示了 OpenFaas 的通用性。
faas-cli template store pull golang-middleware faas-cli new --lang golang-middleware \ build-test --prefix=alexellis2
--lang build-test --prefix
我们将可以得到如下结果:
./ ├── build-test │ └── handler.go └── build-test.yml 1 directory, 2 files
handler 看上去像下面这样,而且改起来也方便。可以通过 vendor 或者 Go modules 来添加额外的依赖项。
package function import ( "fmt" "io/ioutil" "net/http" ) func Handle(w http.ResponseWriter, r *http.Request) { var input []byte if r.Body != nil { defer r.Body.Close() body, _ := ioutil.ReadAll(r.Body) input = body } w.WriteHeader(http.StatusOK) w.Write([]byte(fmt.Sprintf("Hello world, input was: %s", string(input))))
}
以正常方式构建
正常情况下,我们会使用如下方式来构建这个应用:
faas-cli build -f build-test.yml
./template/golang-middleware/Dockerfile
里面也提供了模板文件以及 Dockerfile 的本地缓存。
这个模板在这里将会拉取三个镜像:
FROM openfaas/of-watchdog:0.7.3 as watchdog FROM golang:1.13-alpine3.11 as build FROM alpine:3.11
使用传统的构建工具的话,每个镜像将会被逐个顺序拉取。
等待片刻就大功告成了,如今在我们的本地库里已经有了该镜像。
我们也可以通过 faas-cli push -f build-test.yml
的方式将它推送上传到一个镜像仓库。
使用 Docker 和 BuildKit 构建
要做的改动再简单不过了,而且我们也可以得到一个更快的构建。
DOCKER_BUILDKIT=1 faas-cli build -f build-test.yml
我们将可以看到,使用这个方案的情况下,Docker守护进程会自动地将它的构建工具切换到buildkit。
BuildKit 有很多优点:
- 更复杂的缓存机制;
- 可以的话,请先执行后面的指令 - 比如,在"sdk"层的构建完成前下载"runtime"镜像;
- 在第二次构建时能够更快
借助 buildkit,所有的基础镜像都可以立即拉取到我们的本地库中,因为FROM(下载)命令不是顺序执行的。
FROM openfaas/of-watchdog:0.7.3 as watchdog FROM golang:1.13-alpine3.11 as build FROM alpine:3.11
此选项甚至在 Mac 上也可以使用,因为 buildkit 是被虚拟机里运行的 Docker 守护进程代理的。
使用独立运行的 BuildKit 构建
要使用在独立运行模式下的 BuildKit 构建镜像的话,我们需要在一台 Linux 宿主机上单独运行 buildkit ,因此这里不能使用 Mac。
faas-cli build
通常会调用执行或者 fork docker
,该命令只是包了一层而已。因此,要绕过此行为的话,我们应当写出一个构建上下文,这可以通过执行如下命令实现:
faas-cli build -f build-test.yml --shrinkwrap [0] > Building build-test. Clearing temporary build folder: ./build/build-test/ Preparing ./build-test/ ./build/build-test//function Building: alexellis2/build-test:latest with golang-middleware template. Please wait.. build-test shrink-wrapped to ./build/build-test/ [0] < Building build-test done in 0.00s. [0] Worker done. Total build time: 0.00
如今可以在 ./build/build-test/
目录下找到我们需要的上下文,其中包含了我们的函数代码,以及带有 entrypoint 和 Dockerfile 的模板文件。
./build/build-test/ ├── Dockerfile ├── function │ └── handler.go ├── go.mod ├── main.go └── template.yml 1 directory, 5 files
现在我们需要运行 buildkit,我们可以基于源码构建,或者获取上游的二进制文件。
curl -sSLf https://github.com/moby/buildkit/releases/download/v0.6.3/buildkit-v0.6.3.linux-amd64.tar.gz | sudo tar -xz -C /usr/local/bin/ --strip-components=1
如果你查看 releases 页面的话,你还将会找到适用于 armhf 和 arm64 的 buildkit,对于多体系结构的情况这一点棒极了。
在一个新的窗口里运行 buildkit 守护进程:
sudo buildkitd WARN[0000] using host network as the default INFO[0000] found worker "l1ltft74h0ek1718gitwghjxy", labels=map[org.mobyproject.buildkit.worker.executor:oci org.mobyproject.buildkit.worker.hostname:nuc org.mobyproject.buildkit.worker.snapshotter:overlayfs], platforms=[linux/amd64 linux/386] WARN[0000] skipping containerd worker, as "/run/containerd/containerd.sock" does not exist INFO[0000] found 1 workers, default="l1ltft74h0ek1718gitwghjxy" WARN[0000] currently, only the default worker can be used. INFO[0000] running server on /run/buildkit/buildkitd.sock
现在让我们发起一次构建,把收缩包装(shrinkwrap)了的位置作为构建上下文传进去。我们需要的命令即是 buildctl
,buildctl 是守护进程的客户端程序,它将会配置如何构建镜像,以及完成后的操作,比如导出tar,忽略构建或者推送到镜像仓库。
buildctl build --help NAME: buildctl build - build USAGE: To build and push an image using Dockerfile: $ buildctl build --frontend dockerfile.v0 --opt target=foo --opt build-arg:foo=bar --local context=. --local dockerfile=. --output type=image,name=docker.io/username/image,push=true OPTIONS: --output value, -o value Define exports for build result, e.g. --output type=image,name=docker.io/username/image,push=true --progress value Set type of progress (auto, plain, tty). Use plain to show container output (default: "auto") --trace value Path to trace file. Defaults to no tracing. --local value Allow build access to the local directory --frontend value Define frontend used for build --opt value Define custom options for frontend, e.g. --opt target=foo --opt build-arg:foo=bar --no-cache Disable cache for all the vertices --export-cache value Export build cache, e.g. --export-cache type=registry,ref=example.com/foo/bar, or --export-cache type=local,dest=path/to/dir --import-cache value Import build cache, e.g. --import-cache type=registry,ref=example.com/foo/bar, or --import-cache type=local,src=path/to/dir --secret value Secret value exposed to the build. Format id=secretname,src=filepath --allow value Allow extra privileged entitlement, e.g. network.host, security.insecure --ssh value Allow forwarding SSH agent to the builder. Format default|<id>[=<socket>|<key>[,<key>]]
如下命令和 Docker 命令用 DOCKER_BUILDKIT
覆盖后执行的结果是等价的:
sudo -E buildctl build --frontend dockerfile.v0 \ --local context=./build/build-test/ \ --local dockerfile=./build/build-test/ \ --output type=image,name=docker.io/alexellis2/build-test:latest,push=true
在执行此命令前,你需要运行 docker login
,或者创建一个 $HOME/.docker/config.json 文件,里面带上一组有效的未加密的安全凭证。
你将可以看到一个漂亮地描述当前构建进度的ASCII动画。
使用 img
和 buildkit 来构建
由于我从未使用过 img
,也没有真正意义上听闻过有哪个团队经常使用,而对于更常见的选项我想我会试一试。
我的第一印象是,多体系结构不是它优先考虑的问题,而且鉴于该项目的年代,它也不太可能上岸。它没有提供适用于armhf或者ARM64架构下的二进制文件。
对于 x86_64
来说,目前最新版本是2019年5月7日发布的 v0.5.7
,该版本使用Go1.11构建,而当前版本是Go1.13。
sudo curl -fSL "https://github.com/genuinetools/img/releases/download/v0.5.7/img-linux-amd64" -o "/usr/local/bin/img" \ && sudo chmod a+x "/usr/local/bin/img"
提供的构建选项看起来像是buildctl的一个子集:
img build --help Usage: img build [OPTIONS] PATH Build an image from a Dockerfile. Flags: -b, --backend backend for snapshots ([auto native overlayfs]) (default: auto) --build-arg Set build-time variables (default: []) -d, --debug enable debug logging (default: false) -f, --file Name of the Dockerfile (Default is 'PATH/Dockerfile') (default: <none>) --label Set metadata for an image (default: []) --no-cache Do not use cache when building the image (default: false) --no-console Use non-console progress UI (default: false) --platform Set platforms for which the image should be built (default: []) -s, --state directory to hold the global state (default: /home/alex/.local/share/img) -t, --tag Name and optionally a tag in the 'name:tag' format (default: []) --target Set the target build stage to build (default: <none>)
以下是我们需要进行构建时执行的命令:
sudo img build -f ./build/build-test/Dockerfile -t alexellis2/build-test:latest ./build/build-test/
目前由于某种原因, img
实际上无法成功构建。可能是由于某些优化原因在尝试以非root身份执行时导致的。
fatal error: unexpected signal during runtime execution [signal SIGSEGV: segmentation violation code=0x1 addr=0xe5 pc=0x7f84d067c420] runtime stack: runtime.throw(0xfa127f, 0x2a) /home/travis/.gimme/versions/go1.11.10.linux.amd64/src/runtime/panic.go:608 +0x72 runtime.sigpanic() /home/travis/.gimme/versions/go1.11.10.linux.amd64/src/runtime/signal_unix.go:374 +0x2f2 goroutine 529 [syscall]: runtime.cgocall(0xc9d980, 0xc00072d7d8, 0x29) /home/travis/.gimme/versions/go1.11.10.linux.amd64/src/runtime/cgocall.go:128 +0x5e fp=0xc00072d7a0 sp=0xc00072d768 pc=0x4039ee os/user._Cfunc_mygetgrgid_r(0x2a, 0xc000232260, 0x7f84a40008c0, 0x400, 0xc0004ba198, 0xc000000000)
Github上似乎有 三个类似的issue 还处于未关闭状态。
使用 Kaniko 构建
Kaniko 是 Google 的容器构建工具,它的目标是沙盒容器构建。你可以把它当成一次性容器使用,也可以用作独立的二进制文件。
在这篇博客文章里 ,我们体验了一下构建过程:
docker run -v $PWD/build/build-test:/workspace \ -v ~/.docker/config.json:/kaniko/config.json \ --env DOCKER_CONFIG=/kaniko \ gcr.io/kaniko-project/executor:latest \ -d alexellis2/build-test:latest
- -d 标志指定了在构建成功后应当将镜像推送到的位置。
- -v 标志会把当前目录监听挂载(bind-mount)到 Kaniko 容器里,它还会添加config.json 文件用于推送镜像到一个远端镜像仓库。
Kaniko 在缓存方面提供了一些支持,但是由于 Kaniko 是采用一次性执行方式运行的,而不是像 Buildkit 那样的守护进程,因此我们可能需要手动管理和保存。
对上述方案做一下总结
- Docker - 传统的构建工具
安装Docker可能会稍显繁重,而且会添加一些超出我们系统需求的功能。这个构建工具是最老的,也是最慢的,但是它可以完成任务。要注意的是docker安装的网桥,它可能会和使用相同私有IP段的其他私有网络冲突。
- Docker - 和buildkit一起工作
这是在尽量不分裂或者变化最少的情况下的最快选项了。只需要简单地在命令前面加上前缀 DOCKER_BUILDKIT=1 即可启用。
- 独立运行的 buildkit
这个方案非常适用于集群内的构建,或者说是不需要用到 Docker 的系统(比如一个CI盒或者执行器)。它确实需要一台 Linux 宿主机,而且目前没有什么在 MacOS 使用它的优良经验,也许是通过跑一台额外的虚拟机或者宿主机然后通过 TCP 访问来实现?
我还想在这里附上 [Akihiro Suda]( https://twitter.com/@AkihiroSuda /) 的一次演示,他是来自日本NTT的 buildkit 维护人员。这个演讲已经大概是2年前的事情了,但是它为我们提供了另外一个宏观角度的概述,在2018年 比较下一代容器镜像构建工具里的面貌 。
对于 faasd 用户 来说这是一个最佳选择了,这些用户仅需要依赖 containerd 和 CNI,而不是 Docker 或者 Kubernetes。
- Kaniko
我们使用 Kaniko 时始终是需要安装Docker的,但是它其实提供了其他的选项。
结语
你可以继续在 OpenFaaS 里使用常规的容器构建工具,又或者是执行 faas-cli build --shrinkwrap
然后把构建上下文传给你偏好的工具。
下面是一些构建OpenFaaS容器用到的工具的例子:
在 OpenFaaS 云 ,我们使用 buildkit 守护进程搭配在本文里标明的 shrinkwrap 方案打造了一个完整的无需干涉的CICD体验。对于所有其他用户的话,我将会建议他们使用 Docker,或者带有 buildkit 的 Docker。对于 faasd 的用户,建议使用带守护进程模式的buildkit。
在这篇文章里,确实少了作为重要部分之一的工作流,即部署这块的内容。任何OCI容器 只要符合serverless工作负载的定义 ,就都可以部署到Kubernetes上面的OpenFaaS控制平面。如果想要了解构建,推送和部署这块的完整经验的话,请参阅 OpenFaaS 的研讨会 。
原文链接: building-containers-without-docker (译者:吴佳兴)
以上所述就是小编给大家介绍的《无需 Docker 也能构建容器》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Hacker's Delight
Henry S. Warren Jr. / Addison-Wesley / 2002-7-27 / USD 59.99
A collection useful programming advice the author has collected over the years; small algorithms that make the programmer's task easier. * At long last, proven short-cuts to mastering difficult aspec......一起来看看 《Hacker's Delight》 这本书的介绍吧!