Containerd 1.5 震撼发布 | 支持镜像加密的容器引擎

​Containerd 1.5 于 2021 年 5 月 4 日发布。这是 Containerd 的第 6 个主要版本,包括许多稳定性改进, 以及代码组织结构变化,使后续开发更方便。该版本默认启用支持 OCI crypt 解密,并引入了对 zstd、NRI 和 FreeBSD 实验性支持还包括将 CRI 插件纳入主容器库和切换到 Go modules。Containerd 相对于 Docker 更轻量,也更先进。

🔗 详细设计稿参考:

https://static.sched.com/hosted_files/kccnceu2021/d3/containerd-KubeConEU2021.pdf

— 01 重要功能 —

   支持加密镜像

可以把镜像加密后上传到镜像仓库, Containerd 只需要配置解码的 key 就可以运行这加密的镜像, 对于一些对于安全性有需求的镜像可以使用这种方式。

自从 Containerd 1.3 加入该功能后,已支持容器使用加密镜像 「OCI crypt」但是一直并没有启用该功能。

该特性将在 1.5 中已经默认启用,使用方法请参见文档 「依赖的二进制文件包含在 cri-containerd-cni-1.5.0-linux-amd64.tar.gz」

注意:当前 Docker 是不支持 OCI crypt 的,因为 Docker 目前还没有使用 Containerd 管理镜像。

   1.1 全局启用

在 /etc/containerd/config.toml 中增加如下配置并手动重启 Containerd 服务。

 1version = 2
 2
 3[plugins."io.containerd.grpc.v1.cri".image_decryption]
 4  key_model = "node"
 5
 6[stream_processors]
 7  [stream_processors."io.containerd.ocicrypt.decoder.v1.tar.gzip"]
 8    accepts = ["application/vnd.oci.image.layer.v1.tar+gzip+encrypted"]
 9    returns = "application/vnd.oci.image.layer.v1.tar+gzip"
10    path = "ctd-decoder"
11    args = ["--decryption-keys-path", "/etc/containerd/ocicrypt/keys"]
12    env= ["OCICRYPT_KEYPROVIDER_CONFIG=/etc/containerd/ocicrypt/ocicrypt_keyprovider.conf"]
13  [stream_processors."io.containerd.ocicrypt.decoder.v1.tar"]
14    accepts = ["application/vnd.oci.image.layer.v1.tar+encrypted"]
15    returns = "application/vnd.oci.image.layer.v1.tar"
16    path = "ctd-decoder"
17    args = ["--decryption-keys-path", "/etc/containerd/ocicrypt/keys"]
18    env= ["OCICRYPT_KEYPROVIDER_CONFIG=/etc/containerd/ocicrypt/ocicrypt_keyprovider.conf"]

在本例中,容器镜像解码被设置为使用 Node 上的密钥,配置参数 –decryption-keys-path 以指定解密密钥在 Node 中的路径。

$OCICRYPT_KEYPROVIDER_CONFIG 环境变量用于 OCI crypt keyprovider protocol。

   1.2 单独启用

可以使用如下所示的配置文件启动一个新的 Containerd。为了避免对当前安装的 Containerd 的干扰,我们使用 /tmp 作为目录。

创建 /tmp/etc/containerd/config.toml 配置, 并启动 Containerd。

 1disable_plugins = ["cri"]
 2root = "/tmp/var/lib/containerd"
 3state = "/tmp/run/containerd"
 4[grpc]
 5  address = "/tmp/run/containerd/containerd.sock"
 6  uid = 0
 7  gid = 0
 8[stream_processors]
 9    [stream_processors."io.containerd.ocicrypt.decoder.v1.tar.gzip"]
10        accepts = ["application/vnd.oci.image.layer.v1.tar+gzip+encrypted"]
11        returns = "application/vnd.oci.image.layer.v1.tar+gzip"
12        path = "/usr/local/bin/ctd-decoder"
13    [stream_processors."io.containerd.ocicrypt.decoder.v1.tar"]
14        accepts = ["application/vnd.oci.image.layer.v1.tar+encrypted"]
15        returns = "application/vnd.oci.image.layer.v1.tar"
16        path = "/usr/local/bin/ctd-decoder"
17
18# sudo containerd -c config.toml

使用 openssl 命令行工具创建 RSA 密钥对,并对镜像进行加密:

 1# openssl genrsa -out mykey.pem
 2Generating RSA private key, 2048 bit long modulus (2 primes)
 3...............................................+++++
 4............................+++++
 5e is 65537 (0x010001)
 6# openssl rsa -in mykey.pem -pubout -out mypubkey.pem
 7writing RSA key
 8# sudo chmod 0666 /run/containerd/containerd.sock
 9# CTR="/usr/local/bin/ctr-enc -a /run/containerd/containerd.sock"
10# $CTR images pull --all-platforms docker.io/library/bash:latest
11[...]
12# $CTR images layerinfo --platform linux/amd64 docker.io/library/bash:latest
13   #                                                                    DIGEST      PLATFORM      SIZE   ENCRYPTION   RECIPIENTS
14   0   sha256:9d48c3bd43c520dc2784e868a780e976b207cbf493eaff8c6596eb871cbd9609   linux/amd64   2789669                         
15   1   sha256:7dd01fd971d4ec7058c5636a505327b24e5fc8bd7f62816a9d518472bd9b15c0   linux/amd64   3174665                         
16   2   sha256:691cfbca522787898c8b37f063dd20e5524e7d103e1a3b298bd2e2b8da54faf5   linux/amd64       340                         
17# $CTR images encrypt --recipient jwe:mypubkey.pem --platform linux/amd64 docker.io/library/bash:latest bash.enc:latest
18Encrypting docker.io/library/bash:latest to bash.enc:latest
19$ $CTR images layerinfo --platform linux/amd64 bash.enc:latest
20   #                                                                    DIGEST      PLATFORM      SIZE   ENCRYPTION   RECIPIENTS
21   0   sha256:360be141b01f69b25427a9085b36ba8ad7d7a335449013fa6b32c1ecb894ab5b   linux/amd64   2789669          jwe        [jwe]
22   1   sha256:ac601e66cdd275ee0e10afead03a2722e153a60982122d2d369880ea54fe82f8   linux/amd64   3174665          jwe        [jwe]
23   2   sha256:41e47064fd00424e328915ad2f7f716bd86ea2d0d8315edaf33ecaa6a2464530   linux/amd64       340          jwe        [jwe]

启动一个支持加密镜像的最新版本的本地镜像仓库,这样我们就可以将加密的镜像推送进去。

1# docker pull registry:latest
2# docker run -d -p 5000:5000 --restart=always --name registry registry

将加密镜像推入本地镜像仓库,使用 ctr-enc 拉取镜像,然后运行镜像。

1# $CTR images tag bash.enc:latest localhost:5000/bash.enc:latest
2# $CTR images push localhost:5000/bash.enc:latest
3# $CTR images rm localhost:5000/bash.enc:latest bash.enc:latest
4# $CTR images pull localhost:5000/bash.enc:latest
5# sudo $CTR run --rm localhost:5000/bash.enc:latest test echo 'Hello World!'
6ctr: you are not authorized to use this image: missing private key needed for decryption
7# sudo $CTR run --rm --key mykey.pem localhost:5000/bash.enc:latest test echo 'Hello World!'
8Hello World!

   节点资源接口

节点资源接口 「NRI」 用于 CPU 调度约束和内存配额的标准接口。

其类似容器网络接口 「CNI」 的基本接口、概念和插件设计是一种处理容器网络堆栈的多种实现的优雅方式。此概念可用于其他接口,以定制容器的运行时环境。节点资源接口(NRI)是一个新的接口,其实带有结构化API和容器插件设计,用对节点上的资源进行管理。与 CNI 相同 NRI 插件将在容器创建完毕, 但是还没有真正启动之前的初始化时被调用。

可以看示例代码 「https://github.com/containerd/nri#sample-plugin」 了解 NRI 的使用。

   2.1 如何配置自己的 NRI 插件呢

配置分为两个部分。一个是特定于插件调用的可执行文件,而第二个是指定要运行哪些插件并为插件提供额外配置。

插件可执行文件路径可以通过用户配置,但默认为 /opt/nri/bin。二进制文件以其类型命名为二进制名称,与 CNI 插件命名方案相同。

配置的默认路径是 /etc/nri/resource.d/*.conf 。

 1/etc/nri/resource.d/*.conf
 2{
 3  "version": "0.1",
 4  "plugins": [
 5    {
 6      "type": "konfine",
 7      "conf": {
 8        "systemReserved": [0, 1]
 9      }
10    },
11    {
12      "type": "clearcfs"
13    }
14  ]
15}

   2.2 插件的工作原理

插件的输入输出是通过 STDIN/STDOUT 其格式是 JSON。

  • 输入示例
 1Input
 2{
 3  "version": "0.1",
 4  "state": "create",
 5  "id": "redis",
 6  "pid": 1234,
 7  "spec": {
 8    "resources": {},
 9    "cgroupsPath": "default/redis",
10    "namespaces": {
11      "pid": "/proc/44/ns/pid",
12      "mount": "/proc/44/ns/mnt",
13      "net": "/proc/44/ns/net"
14    },
15    "annotations": {
16      "qos.class": "ls"
17    }
18  }
19}
  • 输出示例
1Output
2{
3  "version": "0.1",
4  "state": "create",
5  "id": "redis",
6  "pid": 1234,
7  "cgroupsPath": "qos-ls/default/redis"
8}

   更好的压缩算法支持

现在除了 gzip 之外,还支持 zstd 作为镜像压缩算法。zstd 是 Facebook 在 2016 年开源的新无损压缩算法,优点是压缩率和压缩/解压缩性能都很突出。

zstd 的设计目的是达到与 deflate 算法 「开发于1991年,用于gzip」 相当的压缩比, 并且更快,尤其是解压的时候。

zstd 在其最大压缩级别的压缩比接近 lzma、lzham 和 ppmx,并且比 lza 或 bzip2 性能更好。它的解压速度比任何当前可用的算法都快,并且压缩比更好。

zstd 的基准测试结果在速度方面,在最快模式下,它的速度通常是 stdlib deflate/gzip 2倍。

🔗 图片来源:

Smaller and faster data compression with Zstandard

   支持 FreeBSD

现在支持在 FreeBSD 运行 Containerd,容器运行时 OCI 是使用 FreeBSD jails 「Samuel Karp 的 runj」。文件系统目前仅支持 ZFS, 后续计划支持 unionfs。

🔗 图片来源:

https://medium.com/nttlabs/containerd15-fe0a9845a572

 

— 02 其它改变 —
  • CRI 插件合并入主库,以简化代码贡献流程
  • 使用 Go modules 管理依赖版本
  • 支持 JSON 格式的日志输出

 

Leave a Reply

Your email address will not be published. Required fields are marked *