写点什么

不用 Docker 也能构建容器的 4 种方法

2020 年 12 月 14 日

不用Docker也能构建容器的4种方法

本文将介绍几种不用 Docker 就能构建容器的方法。我会以 OpenFaaS 作为参考案例,它的工作负载使用了 OCI 格式的容器镜像。OpenFaaS 是 Kubernetes 的一个 CaaS 平台,可以运行微服务和添加 FaaS 及事件驱动工具。


第一个示例将展示如何使用 Docker CLI 内置的 buildkit 选项,然后是单独使用buildkit,最后是谷歌的容器构建器Kaniko


本文涉及的工具都是基于 Dockerfile 文件来构建镜像的,因此,任何限制用户只能使用 Java (jib)或 Go (ko)的工具都不在讨论范围之内。


Docker 有什么问题?


Docker 在 armhf、arm64 和x86_64平台上运行良好。Docker CLI 不仅用于构建/发布/运行镜像,多年来它还背负了太多的东西,现在还与 Docker Swarm 和 Docker EE 特性捆绑在一起。


Docker 之外的选择


有一些项目试图让“docker”回到它原本的组件身份,也就是我们最初都喜爱的用户体验:


  • Docker——Docker现在使用containerd来运行容器,并且支持使用buildkit进行高效的缓存式构建。

  • Podman和buildah组合——由RedHat/IBM使用他们自己的OSS工具链来生成OCI镜像。Podman是无守护进程和无根的,但最后仍然需要挂载文件系统以及使用UNIX套接字。

  • pouch——来自阿里巴巴,被标榜为“高效的企业级容器引擎”。它像Docker一样,使用了containerd,并支持容器级别的隔离(runc)和“轻量级虚拟机”(如runV)。

  • 独立版本的buildkit——buildkit是由Docker公司的Tonis Tiigi创建的,一个全新的具有缓存和并发支持的容器构建器。buildkit目前仅作为守护进程运行,但你可能会听到有人说不是这样的。实际上,它会派生守护进程,然后在构建后将其终止。

  • img——img由Jess Frazelle开发,对buildkit进行了封装。与其他工具相比,它并没有更大的吸引力。在2018年下半年之前,这个项目一直很活跃,但之后只发布了几个补丁。img声称自己是无守护进程的,但它使用的是buildkit,所以这里有值得商榷的地方。我听说img提供了比buildkit的CLI buildctr更好的用户体验,但需要注意的是,img只针对x86_64平台发布了二进制文件,不支持armhf/arm64。

  • k3c——使用containerd和buildkit重建初始Docker原始、经典、朴素、轻量级的体验。


在所有的选项中,我最喜欢 k3c,但它使用起来比较繁琐,它把所有东西都捆绑在一个二进制文件中,这很可能会与其他软件发生冲突。它运行的是自己的嵌入式 containerd 和 buildkit 二进制文件。


由于我们关注的是“构建”部分以及相对稳定的选项,所以我们将着重看一下:


  • Docker的buildkit;

  • 单独的buildkit;

  • kaniko。


由于 OpenFaaS CLI 可以输出任意构建器都可以使用的标准“构建上下文”,所以上述的所有东西都可以实现。

构建一个测试应用程序


让我们从一个 Golang HTTP 中间件开始,并借此来展示 OpenFaaS 的通用性。


faas-cli template store pull golang-middleware
faas-cli new --lang golang-middleware \ build-test --prefix=alexellis2
复制代码


  • --lang指定构建模板;


  • build-test是函数的名字;

  • --prefix是Docker Hub用户名,用于推送我们的OCI镜像。


我们将获得以下这些内容:


./├── build-test│   └── handler.go└── build-test.yml
1 directory, 2 files
复制代码


处理程序修改起来很容易,还可以通过 vendoring 或 Go 模块来添加其他依赖项。


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 watchdogFROM golang:1.13-alpine3.11 as buildFROM alpine:3.12
复制代码


如果使用传统的构建器,将按顺序拉取每个镜像。


等一会儿构建就完成了,现在本地库中就有了构建的镜像。


我们还可以使用 faas-cli push -f build-test.yml将镜像推到注册表中。



使用 buildkit 和 Docker 构建


这是最简单的做法,构建起来也很快。


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 watchdogFROM golang:1.13-alpine3.11 as buildFROM alpine:3.11
复制代码


这个在 Mac 上也可以使用,因为 buildkit 是由运行在 VM 中 Docker 守护进程负责代理的。



使用单独的 buildkit 构建


要单独使用 buildkit 进行镜像构建,我们需要在 Linux 主机上单独运行 buildkit,因此不能使用 Mac。


faas-cli build通常会运行或分叉出 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//functionBuilding: 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/文件夹中找到,其中包含了函数代码和模板及其入口点和 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
复制代码


如果你仔细看一下发布页,你会发现 buildkit 也支持 armhf 和 arm64。


在新窗口中运行 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 
复制代码


现在我们开始构建,并将配置文件的位置作为构建上下文传给它。我们需要 buildctl 命令,buildctl 是守护进程的一个客户端,它将指定如何构建镜像以及在构建完成后应该做什么,比如导成 tar、忽略构建或将其推送到注册表。


buildctl build --helpNAME:   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 命令等价的效果:


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 文件。



使用 img 和 buildkit 构建


由于我从未使用过 img,也没有听说有团队在大规模使用它,所以我想要尝试一下。


首先它不支持多平台架构,armhf 和 ARM64 平台没有对应的二进制文件,而且项目年龄不算短了,所以不太可能会提供多平台支持。


x86_64平台的最新版本是 2019 年 5 月 7 号的 v0.5.7,使用 Go 1.11 构建:


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 --helpUsage: 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 +0x72runtime.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=0x4039eeos/user._Cfunc_mygetgrgid_r(0x2a, 0xc000232260, 0x7f84a40008c0, 0x400, 0xc0004ba198, 0xc000000000)
复制代码


似乎已经存在三个类似的问题


使用 Kaniko 构建


Kaniko 是谷歌的容器构建器,旨在为容器构建提供沙箱。你可以将其作为一次性容器,也可以将其作为独立的二进制文件。


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将当前目录挂载到Kaniko容器中,还添加了config.json配置文件,指定将镜像推送到哪个远程注册表。



Kaniko 提供了缓存支持,但需要手动管理和保存,因为 Kaniko 是在一次性模式下运行的,不像 Buildkit 那样是守护进程。


以上各种工具的总结


  • Docker——传统的构建器


安装 Docker 是个“大工程”,可能会给你的系统带来比预想的要多得多的东西。Docker 构建器是最古老的,也是最慢的。要注意在安装 Docker 时附带安装的网桥,它可能会与使用相同私有 IP 段的其他私有网络发生冲突。


  • Docker——与buildkit一起


这是最快的工具选择,改动最少,只需要加个DOCKER_BUILDKIT=1就可以启用。


  • 单独的buildkit


这个选项非常适合集群内构建,或者不需要 Docker 的系统(如 CI 或执行器)。它需要 Linux 主机,在 MacOS 上的使用体验太差,或许可以运行一个额外的 VM 或主机,然后通过 TCP 来访问?


  • Kaniko


使用 Kaniko 仍然需要安装 Docker,但不管怎样,它毕竟提供了另一种选择。


全文总结


你可以在 OpenFaaS 中使用普通的容器构建器,也可以使用 faas-cli build --shrinkwrap,并将构建上下文传给首选工具。


下面是使用相应工具构建 OpenFaaS 容器的示例:



在 OpenFaaS 云上,我们使用本文介绍的上下文传递方法和 buildkit 守护进程提供了完全不需要人工干预的 CI/CD 构建体验。对于其他用户,我建议使用 Docker,或者带有 buildkit 的 Docker。


你可以使用 GitHub 或 GitLab 集成构建自托管的 OpenFaaS 云环境。


对于 faasd 用户,你的主机上只安装了 containerd,而没有安装 docker,所以最好的选择是下载 buildkit。


原文链接:


https://blog.alexellis.io/building-containers-without-docker/

2020 年 12 月 14 日 13:592457
用户头像

发布了 80 篇内容, 共 11.1 次阅读, 收获喜欢 120 次。

关注

评论

发布
暂无评论
发现更多内容

Windows安装MySQL5.7教程

Simon

MySQL windows 安装 七日更

Polkadot系列(三)——如何实现共享安全性

QTech

区块链 polkadot 跨链

盘点 2020 | 鲜衣怒马少年时,不负韶华行且知!

程序员的时光

程序员 成长 编程之路 计算机 盘点2020

OLAP计算引擎怎么选?

数据社

OLAP 七日更

dForce挖矿APP系统开发|dForce挖矿软件开发

开發I852946OIIO

系统开发

差点跳起来了!全靠这份“Java核心知识笔记”我成功拿到美团offer

比伯

Java 程序员 架构 计算机 编写

【经验分享】遵循10步法,应用系统发布效率大不同!

嘉为蓝鲸

敏捷 运维自动化 部署 发布流程 应用发布

数字货币交易所系统开发功能方案

系统开发咨询:I76-883I-5I52 邓森

一场由fork引发的超时,让我们重新探讨了Redis的抖动问题

华为云开发者社区

redis fork 时延抖动

发布会直播技术及业务实践

vivo互联网技术

分布式 服务器 直播技术

养猫了!

小林coding

生活

快递员出售用户信息被判刑:如何防止快递行业信息泄露

石头IT视角

模糊匹配、相似度查询怎么破?看PG亿级检索毫秒响应

PostgreSQLChina

数据库 postgresql 开源

向我看齐!京东智联云成 2020 TOP100 Summit“技术标兵”

京东智联云开发者

DevOps 云原生 数字化

合成游戏app系统开发软件技术

系统开发咨询:I76-883I-5I52 邓森

四币连发交易所系统开发技术

系统开发咨询:I76-883I-5I52 邓森

突破程序员基本功的16课

田维常

程序员

堪称完美!阿里架构师用60个实战案例讲明白了Spring Boot

Java架构追梦

Java 架构 面试 微服务 springboot

我敢说这是全网最详细的基础讲解,附源码实例,没人学不明白

小Q

Java 学习 架构 面试 基础

为什么现代系统需要一个新的编程模型?

华为云开发者社区

编程 模型 语言

执法办案信息化建设,情报研判管控分析平台搭建解决方案

t13823115967

智慧公安

Gridea+GitHub搭建个人博客

Simon

GitHub Pages 博客 七日更

智慧警务大数据可视化平台搭建,警情分析研判系统

135深圳3055源中瑞8032

智慧公安重点人员管控系统开发,预警研判系统搭建

135深圳3055源中瑞8032

区块链食品溯源--为食品安全保驾护航

135深圳3055源中瑞8032

Spring 源码学习 09:refresh 大概流程

程序员小航

spring 源码 源码阅读

智慧社区综合信息服务平台搭建,智能社区建设解决方案

t13823115967

智慧社区系统开发

AWS云上安全最佳实践

雪雷

安全 AWS 云安全

提升awk技能的两个教程【译】

程序员架构进阶

Linux Shell awk

惊艳!阿里自爆用480页讲清楚了44种微服务架构设计模式

996小迁

程序员 面试 微服务 设计模式 架构设计

做音视频最好用的几款跨平台框架

anyRTC开发者

flutter uni-app ios android WebRTC

InfoQ 极客传媒开发者生态共创计划线上发布会

InfoQ 极客传媒开发者生态共创计划线上发布会

不用Docker也能构建容器的4种方法-InfoQ