Google cloud发布容器基础图像托管服务,使操作和维护更加容易,并支持多种操作系统

容器应用逐渐普及,已经从开发者喜欢的工具,进入企业内部基础架构。不过镜像更新工作,对于运维人员依旧是不简单的工作。

近日,谷歌公有云平台GCP,发布了基础镜像更新托管服务(managed base images),从最初构建应用程序开始就可以使用。目前此服务所支持的Linux发行版,分别有CentOS 7、Debian 9,以及Ubuntu 16.04,使用者可以直接从GCP市场下载。

这一次推出的新服务,Google会提供用户多种操作系统基础镜像文件,后续更新工作也由谷歌进行。开发者除了能时时保持镜像在最新版本,由谷歌负责镜像审核,运维人员也不须从未知来源下载镜像,能提升容器基础架构的安全。

谷歌表示,即便导入基础镜像更新托管服务,容器镜像文件经过扫描后,仍可能发现镜像存在的漏洞。出现这种状况主要有三大原因。第一,扫描出的漏洞可能被归类为低风险漏洞,而项目维护人多半优先处理高风险漏洞,导致当前版本未有可用更新。第二,则可能是项目刚爆出漏洞事件,还未进行可用镜像更新。第三,则可能为开源项目新推出的功能,可能对系统安全带来风险,但项目维护人并不视其为漏洞。

由于容器的生命周期极短,不停进行重新构建、部署,为了确保使用者容器镜像安全,谷歌建议企业用户建立高度一致的CI/CD流程,确保基础架构环境内的容器镜像,有经过审核、认证程序。谷歌表示,实施方法总共有四大方向。第一,导入集中化管理的CI/CD,除了减少使用容器存储库数量,而正式环境的软件上线工作,必须通过中央发送管道进行。第二,则是选用安全存储库作为镜像来源,从开发源头就做好安全工作。

第三,建立镜像文件扫描、分析的使用习惯,可使用第三方或公有云厂商原生服务执行。第四,当镜像要进入正式环境前,运维人员得确保只有经认证的镜像,才能部署至正式环境。

谷歌表示,未考虑使用此托管服务的开发者,也可尝试其他开源方案,如谷歌的开源容器工具Distroless images。此镜像内仅打包用户的应用程序、Runtime相关性,删除标准Linux发行版内的套件管理工具、Shells,缩小镜像的攻击范围。

阿里巴巴开源容器图像加速技术

近日阿里巴巴开源了其云原生容器镜像加速技术,它推出的 overlaybd 镜像格式,相比于传统的分层 tar 包文件格式,实现了基于网络的按需读取,从而使得容器可以快速启动。

该技术方案原本是阿里云内部 DADI 项目的一部分, DADI 是 Data Accelerator for Disaggregated Infrastructure 的缩写,旨在为计算存储分离架构提供各种可能的数据访问加速技术。镜像加速是 DADI 架构在容器及云原生领域的一次突破性尝试,该技术自 2019 年投产以来,已在线上部署了大量机器,累计启动容器次数超过 10 亿,支持了阿里巴巴集团及阿里云的多个业务线,极大提高了应用的发布和扩容效率。2020 年,团队在国际顶级会议发表了论文"DADI: Block-Level Image Service for Agile and Elastic Application Deployment. USENIX ATC'20"[1],并随后启动了开源项目,计划将技术该贡献给社区,通过建立标准并打造生态,吸引更多的开发者投入到容器及云原生性能优化这个领域上来。

背景简介

随着 Kubernetes 和云原生的大爆发,容器在企业内部的大规模应用已经越来越广泛。部署启动快是容器的核心优势之一,这个启动快是指本地镜像实例化的时间非常短,即“热启动”时间短。然而对于“冷启动”,即在本地无镜像的情况下,需要先从 Registry 下载镜像才能创建容器。业务的镜像经过长期维护和更新,无论是镜像层数还是整体大小都会达到一个较大的量级,比如可能达到数百 MB 或者几个 GB。因此生产环境中,容器的冷启动往往耗时数分钟,并且随规模扩大会导致 Registry 因集群内网络拥堵而无法快速地下载镜像。

例如,在之前某年的双十一活动中,阿里内部一个应用因为容量不足触发紧急扩容,但因并发量过大,整体扩容耗时较长,这期间对部分用户的使用体验造成了影响。而到了 2019 年,随着 DADI 的部署上线,新镜像格式的容器在“镜像拉取+容器启动”上耗费的总时间比普通容器缩短了 5 倍,且 p99 长尾时间更是比后者快了 17 倍。

如何处理存储在远端的镜像数据,这是解决容器冷启动慢这个问题的核心点。历史上业界对这一问题做出的尝试有:使用块存储或者NAS保存容器镜像,实现按需读取;使用基于网络的分发技术(如 p2p),将镜像从多个源头下载、或者提前预热到主机上,避免出现网络单点瓶颈。近年来,针对新镜像格式的讨论也逐渐被提上议题,根据 Harter 等人的研究表明,拉取镜像占用了容器启动时间的 76%,而只有 6.4% 的时间用来读取数据。因此,支持 On-demand Read 技术的镜像,已经成为默认的潮流风向。Google 提出的 stargz 格式,其全称是 Seekable tar.gz,顾名思义,可以有选择地从存档中搜寻并提取特定的文件,无需扫描或者解压整个镜像。stargz 旨在提高镜像拉取的性能,其延迟拉取技术(lazy-pull)不会拉取整个镜像文件,实现了按需读取。为了进一步提高运行时效率,stargz 又推出了一个 containerd 的 snapshotter 插件,在存储层面对 I/O 做了进一步优化。

在容器的生命周期中,镜像就绪后需要挂载(mount),而分层镜像挂载的核心技术便是 overlayfs,它以一种堆叠的形式将下层的多个 layer 文件合并,并向上暴露出一个统一的只读文件系统。类比上文提到的块存储和 NAS,一般可以通过快照的形式进行分层堆叠,而跟 stargz 绑定的 CRFS,也可以看做是 overlayfs 的另一种实现。

新镜像格式

DADI 没有直接使用 overlayfs,或者说,它只是借鉴了 overlayfs 和早期联合文件系统(union filesystem)的思想,但提出了一种全新的基于块设备的分层堆叠技术,称之为 overlaybd,它为容器镜像提供了一系列基于块的合并数据视图。overlaybd 的实现十分简单,因此很多之前想做而不能做的事都可以成为现实;而实现一个完全 POSIX 兼容的文件系统接口则充满挑战,并可能存在 bug,这点从各个主流文件系统的发展历史上就可以看出。

除了简单以外,overlaybd 对比 overlayfs 的其他优点有:

  • 避免多层镜像导致的性能下降,如 overlayfs 模式下大文件的更新会触发跨层引用复制,系统必须先将文件复制到可写层;或者创建硬链接速度很慢等问题。
  • 可以方便地采集 block 级别的 I/O 模式,进行录制以及重放,从而预取数据,进一步加速启动。
  • 用户的文件系统和宿主机 OS 可以灵活选择,如支持 Windows NTFS。
  • 可以使用有效的编解码器进行在线解压缩。
  • 可以下沉到云中的分布式存储(如 EBS)中,镜像系统盘可以跟数据盘使用同一套存储方案。
  • overlaybd 具有天然的可写层支持(RW),只读挂载甚至可以成为历史。

overlaybd 原理

为了理解 overlaybd 的原理,首先需要了解容器镜像的分层机制。容器镜像由多个增量 layer 文件组成,在使用时进行叠加,这样在镜像分发时只需要对 layer 文件进行分发。每一层实质上都是与上一层的差异(包括文件的添加,修改或删除)的压缩包。容器引擎可以通过其 storage driver,按照约定的方式将差异叠加起来,然后以 Read-Only 的模式挂载到指定目录,该目录即称为 lower_dir;而以 Read/Write 模式挂载的可写层,挂载目录则一般称为 upper_dir。

请注意,overlaybd 本身没有文件的概念,它只是将镜像抽象为虚拟块设备,并在其上装载常规的文件系统。当用户应用读取数据时,该读取请求首先由常规的文件系统处理,将请求转换为虚拟块设备的一次或多次读取。这些读取请求会被转发到用户态的接收程序,即 overlaybd 的运行时载体,最后转换为对一个或多个 layer 的随机读取。

与传统镜像一样,overlaybd 在内部仍然保留着 layer 分层的结构,但每层的内容都是文件系统变更差异对应的一系列 data block。overlaybd 向上提供了一个合并视图,对 layer 的叠加规则很简单,即对于任意一个 data block,总是使用最后的变更,在 layer 中未发生变更的块均视为全零块;向下又提供了将一系列 data block 导出成一个 layer 文件的功能,该文件高密度非稀疏、且可索引。因此,对块设备某个连续 LBA 范围进行读操作,可能包含了原本属于多层的小块数据段,我们将这些小块数据段称为 segment。从 segment 的属性中找到层号,便能够继续映射到对这层的 layer 文件的读取上来。传统的容器镜像可以将它的 layer 文件保存在 Registry 或者对象存储上,那么 overlaybd 镜像自然也可以。

为了更好的兼容性,overlaybd 在 layer 文件的最外层,包装了一层 tar 文件的头和尾,这样伪装成一个 tar 文件。由于 tar 内部仅一个文件,不影响按需读取。目前无论是 docker、containerd 或者 buildkit,对镜像的下载或上传默认都有 untar 和 tar 的流程,不侵入代码是无法逾越的,所以增加 tar 伪装有利于兼容性和流程的统一,例如在镜像转换、构建、或者全量下载使用时,都无需修改代码,只需提供插件即可。

整体架构

DADI 整体架构如图,以下分别介绍各个组件:

1. containerd snapshotter

containerd 自 1.4 版起,开始初步支持一些启动远程镜像的功能,并且 K8s 已经明确将放弃 Docker 作为运行时的支持。所以 DADI 开源版本选择优先支持 containerd 生态,之后再支持 Docker。

snapshotter 的核心功能是实现抽象的服务接口,用于容器 rootfs 的挂载和卸载等操作。它的设计替代了在 Docker 早期版本称之为 graphdriver 的模块,使得存储驱动更加简化,同时兼容了块设备快照与 overlayfs。

DADI 提供的 overlaybd-snapshotter 一方面能让容器引擎支持新的 overlaybd 格式的镜像,即将虚拟块设备挂载到对应的目录,另一方面也兼容传统 OCI tar 格式镜像,让用户继续以 overlayfs 运行普通容器。

2. iSCSI target

iSCSI 是一种被广泛支持的远程块设备协议,稳定成熟性能高,遇到故障可恢复。overlaybd 模块作为 iSCSI 协议的后端存储,即使程序意外 crash,重新拉起即可恢复。而基于文件系统的镜像加速方案,例如 stargz,则无法恢复。

iSCSI target 是 overlaybd 的运行时载体。在本项目中,我们实现了两种 target 模块:第一种是基于开源项目 tgt,由于其拥有 backing store 机制,可以将代码编译成动态链接库以便运行时加载;第二种是基于 Linux 内核的 LIO SCSI target(又称为 TCMU),整个 target 运行在内核态,可以比较方便地输出虚拟块设备。

3. ZFile

ZFile 是我们提供的一种支持在线解压的数据压缩格式。它将源文件按固定大小的 block size 切分,各数据块进行单独压缩,同时维护一个 jump table,记录了各数据块在 ZFile 中的物理偏移位置。如需从 ZFile 中读数据,只要查找索引找到对应位置,并仅解压缩相关的 data block 即可。

ZFile 支持各种有效的压缩算法,包括 lz4,zstd 等,它解压速度极快,开销低,可以有效节省存储空间和数据传输量。实验数据表明,按需解压远程的 ZFile 数据,性能高于加载非压缩数据,这是因为传输节省的时间,大于解压的额外开销。

overlaybd 支持将 layer 文件导出成 ZFile 格式。

4. cache

正如上文所说,layer 文件保存在 Registry 上,容器对块设备的读 I/O 会映射到对 Registry 的请求上(这里利用到了 Registry 对 HTTP Partial Content 的支持)。但是由于 cache 机制的存在,这种情形不会一直存在。cache 会在容器启动后的一段时间后自动开始下载 layer 文件,并持久化到本地文件系统。如果 cache 命中,则读 I/O 就不会再发给 Registry,而是读本地。

行业领先

3 月 25 日,权威咨询机构 Forrester 发布 2021 年第一季度 FaaS 平台(Function-As-A-Service Platforms)评估报告,阿里云凭借产品能力全球第一的优势脱颖而出,在八个评测维度中拿到最高分,成为比肩亚马逊 AWS 的全球 FaaS 领导者。这也是首次有国内科技公司进入 FaaS 领导者象限。

众所周知,容器是 FaaS 平台的承载基础,而容器启动速度更是决定了整个平台的性能与响应延迟。DADI 助力阿里云函数计算产品,大幅度缩短容器启动时间 50%~80%,带来了全新的 Serverless 使用体验。

总结展望

阿里巴巴开源的 DADI 容器加速项目以及其推出的 overlaybd 镜像格式,有助于应对新时代下容器对快速启动的需求。项目组未来将协同社区一起,加快对接主流工具链,积极参与新镜像格式标准制定,目标是让 overlaybd 成为 OCI 远程镜像格式的标准之一。

欢迎大家参与开源项目,一起贡献力量!

后续工作

1. Artfacts Manifest

OCI Image 的 v1 Manifest 格式描述能力有限,无法满足远程镜像需求。目前 v2 的讨论没有实质进展,推翻 v1 也不现实。但是,可以借助 OCI Artfacts Manifest 使用 Additional Descriptor 来描述原始数据,兼容性上有所保证,用户更容易接受。Artfacts 也是 OCI/CNCF 在推广的项目,DADI 未来计划拥抱 Artfacts 并实现 PoC。

2. 开放对多种文件系统的支持

DADI 本身支持用户根据需要选择合适的文件系统来构建镜像,但是目前尚未开放相应的接口,默认使用了 ext4 文件系统。我们未来将完善相关接口并放开此功能,由用户根据自身需要,决定使用什么文件系统。

3. Buildkit 工具链

目前用户可以通过 buildkit 外挂 snapshotter 来构建镜像,未来将进一步完善,形成完整工具链。

4. 数据领取

在容器启动后对 I/O 模式进行记录,后续启动同一镜像时便可以重放该记录,对数据进行预取,避免临时请求 Registry,这样容器的冷启动时间将继续缩短一半以上。理论上所有无状态或幂等容器都可以进行录制和重放。

作者 |陈博

本文为阿里云原创内容,未经允许不得转载。

你的图像安全吗?

你的镜像安全吗?

与传统的服务器和虚拟机相比,Docker容器为我们工作提供了更安全的环境。容器中可以使我们的应用环境组件实现更小,更轻。每个应用的组件彼此隔离并且大大减少了攻击面。这样即使有人入侵了您的应用,也会最大程度限制被攻击的程度,而且入侵后,利用漏洞传播攻击更难。

不过,我们还是需要最大程度了解Docker技术本身的存在的安全隐患,这样才能实现最大程度保护我们的容器化系统。

其中大部分将类似于我们已经为基于服务器所做的工作,例如监视容器活动,限制每个容器环境的资源消耗,维持良好的应用程序设计实践,修补漏洞并确保凭据不会被入侵您的Docker映像。

但是,我们还是需要采取专门针对Docker部署的安全措施。因此,以下列出了确保容器平台上托管的应用程序安全的三个基本步骤。

让我们从最重要的开始。

1. 以非Root用户运行容器镜像

默认情况下,Docker授予容器中进程的root权限,这意味着它们具有对容器和主机环境的完全管理访问权限。 一般来说,就像我们不会在标准Linux服务器上以root身份运行进程一样,我们大部分容器应用部署时,也不会在容器中以root身份运行。

但是,如果没有适当的注意和关注,开发人员可以轻松地忽略此默认行为并创建不安全的映像,这些映像会错误地授予root用户访问权限。这可能是对黑客送了一份大礼,黑客可以利用此漏洞窃取API密钥,令牌,密码和其他机密数据,或者干扰容器部署的基础主机,并对服务器系统造成恶意破坏。

甚至,您的DevOps团队还可能会受到侵害,从而给Docker环境带来意想不到的后果。例如,他们可能会无意中创建具有管理访问权限的,由Dockerfile命令构建的映像,这些映像在启动容器时会擦除数据或更改主机系统设置。

如何防止容器以root权限运行

如果不确定基础镜像使用什么权限,应该强制使用自定义用户的非root用户或用户组。这样,容器进程只能访问我们预期功能所需要的资源

可以通过以下任意方式操作即可:

l 在Dockerfile中设置非root用户

首先,设置仅具有应用程序所需访问权限的专用用户或用户组。

然后,Dockerfile中添加User,并以此用户或组以构建镜像和启动容器运行时进程命令

FROM centos:7

RUN groupadd -g 1000 basicuser &&

useradd -r -u 1000 -g basicuser basicuser

USER basicuser

l 在Docker run命令中使用-user指定非root用户

Docker run命令中的-user选项将覆盖Dockerfile中指定的任何用户。所以,在以下示例中,您的容器将始终以最低特权运行-所提供的用户标识符1009的权限级别也最低。但是,此方法无法解决映像本身的潜在安全缺陷。因此,最好在Dockerfile中指定一个非root用户,以便您的容器始终安全运行。

$ docker run –user 1009 centos:7

2. 使用自己的私有注册中心

私有注册中心是由我们自己的组织搭建的完全独立的容器映像仓库。您可以搭建在自己的服务器上,也可以托管在第三方云服务上,例如Amazon ECR,Azure容器注册,Google容器注册,Red Hat Quay和JFrog自己的容器注册服务。

私有注册中心可以让您获得更完善的镜像的管理方式,并且通常提供更高级的功能,可以帮助确保库存安全。

例如:

l 复杂的镜像扫描工具,用于识别威胁和未修补的漏洞。

l 严格的治理,例如基于角色的访问控制(RBAC)和合规性监视。

l 数字签名,镜像认证和其他防篡改功能。

l 用于开发,测试和生产多环境的镜像仓库。

相比之下,诸如Docker Hub之类的公共注册表一般仅提供基本服务-您必须信任镜像发布者,而镜像发布者可能未遵循相同的高安全标准。

这样,您最终可能会得到包含恶意或过时代码的镜像,并最终获得对数据泄露敞开大门的容器环境。

3. 保持镜像最小化

镜像越大,受到攻击的可能性越大。对于linux系统,您没有选择余地,但是对于Docker来说,只选择自己需要的组件即可。

选择最小的基础镜像

Docker hub上的某些镜像比其他的镜像更简化。比如在ubuntu仓库中,有些镜像的大小是部分版本的2倍以上。

所以在您获取镜像时,不要单纯地只获取最新版本的镜像,最理想的是获取占用空间最小的镜像,然后自主添加应用所需的软件包和依赖。

Docker Hub显示存储库中每个映像的压缩大小,如下面的Minimal Ubuntu版本所示。

拉取镜像后可以使用docker images命令检查其实际大小。

$ docker images

然后,如下所示查找刚刚下载的镜像的条目。

优化Dockerfile和.dockerignore中的镜像

接下来,您需要创建一个Dockerfile来为容器构建简化镜像。这将由基础镜像层和自己的镜像层组成。添加这些层时,有些制品将不是运行时环境的必需部分。要排除这些,应该在要从中构建映像的根目录中设置一个.dockerignore文件。

多阶段构建

最后,减小镜像大小的另一种方法是使用Docker多阶段构建功能,Docker 17.05及更高版本支持。

基于这个能力,Dockerfile中可以使用多个FROM命令。

对于每个新的FROM语句,我们可以使用多个不同的基础镜像。然后我们可以有选择地将所需的文件复制到下一阶段,多余的各层将被留下。具体示例如下:

FROM golang:1.7.3

WORKDIR /go/src/github.com/alexellis/href-counter/

RUN go get -d -v golang.org/x/net/html

COPY app.go .

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

FROM alpine:latest

RUN apk –no-cache add ca-certificates

WORKDIR /root/

COPY –from=0 /go/src/github.com/alexellis/href-counter/app .

CMD ["./app"]

验证镜像完整性

改善容器安全状况的另一种方法是在将镜像从Docker Hub中拉出之前进行验证。

Docker守护程序默认在不检查其完整性的情况下拉取Docker映像。但是,随着Docker Engine 1.8的发布,该平台引入了一项新功能Docker Content Trust,该功能支持镜像的数字签名和身份验证。

此服务使您可以向发布到远程仓库的镜像添加加密签名。同时,每当您尝试拉取镜像时,它都会自动验证数字签名。这样,您可以确定镜像的所有者的身份是不是与他们声明的一致。

要激活Docker Content Trust,您需要使用Linux export命令设置以下变量。

$ export DOCKER_CONTENT_TRUST=1

这只会在您当前的shell中设置。如果要全面启用Docker Content Trust,则需要在所有用户共享的默认环境变量中进行设置。

尽管Docker Content Trust无法验证映像的质量,但可以通过防止在传输过程中受到破坏或通过对存储库的未授权访问,以此来帮助保持镜像的清洁。