浅谈 Linux 容器和镜像签名
容器 Docker LXD RKT
![](/data/attachment/album/201608/31/113136ayvzbkkzov1ovor3.jpg)


从根本上说，几乎所有的主要软件，即使是开源软件，都是在基于镜像的容器技术出现之前设计的。这意味着把软件放到容器中相当于是一次平台移植。这也意味着一些程序可以很容易就迁移，[而另一些就更困难](http://rhelblog.redhat.com/2016/04/21/architecting-containers-part-4-workload-characteristics-and-candidates-for-containerization/)。


我大约在三年半前开展基于镜像的容器相关工作。到目前为止，我已经容器化了大量应用。我了解到什么是现实情况，什么是迷信。今天，我想简要介绍一下 Linux 容器是如何设计的，以及谈谈镜像签名。


### Linux 容器是如何设计的


对于基于镜像的 Linux 容器，让大多数人感到困惑的是，它把操作系统分割成两个部分：[内核空间与用户空间](http://rhelblog.redhat.com/2015/07/29/architecting-containers-part-1-user-space-vs-kernel-space/)。在传统操作系统中，内核运行在硬件上，你无法直接与其交互。用户空间才是你真正能交互的，这包括所有你可以通过文件浏览器或者运行`ls`命令能看到的文件、类库、程序。当你使用`ifconfig`命令调整 IP 地址时，你实际上正在借助用户空间的程序来使内核根据 TCP 协议栈改变。这点经常让没有研究过 [Linux/Unix 基础](http://rhelblog.redhat.com/tag/architecting-containers/)的人大吃一惊。


过去，用户空间中的类库支持了与内核交互的程序（比如 ifconfig、sysctl、tuned-adm）以及如网络服务器和数据库之类的面向用户的程序。这些所有的东西都堆积在一个单一的文件系统结构中。用户可以在 /sbin 或者 /lib 文件夹中找到所有操作系统本身支持的程序和类库，或者可以在 /usr/sbin 或 /usr/lib 文件夹中找到所有面向用户的程序或类库（参阅[文件系统层次结构标准](/article-6132-1.html)）。这个模型的问题在于操作系统程序和业务支持程序没有绝对的隔离。/usr/bin 中的程序可能依赖 /lib 中的类库。如果一个应用所有者需要改变一些东西，就很有可能破坏操作系统。相反地，如果负责安全更新的团队需要改变一个类库，就（常常）有可能破坏面向业务的应用。这真是一团糟。


借助基于镜像的容器，比如 Docker、LXD、RKT，应用程序所有者可以打包和调整所有放在 /sbin、/lib、/usr/bin 和 /usr/lib 中的依赖部分，而不用担心破坏底层操作系统。本质上讲，容器技术再次干净地将操作系统隔离为两部分：内核空间与用户空间。现在开发人员和运维人员可以分别独立地更新各自的东西。


然而还是有些令人困扰的地方。通常，每个应用所有者（或开发者）并不想负责更新这些应用依赖：像 openssl、glibc，或很底层的基础组件，比如，XML 解析器、JVM，再或者处理与性能相关的设置。过去，这些问题都委托给运维团队来处理。由于我们在容器中打包了很多依赖，对于很多组织来讲，对容器内的所有东西负责仍是个严峻的问题。


### 迁移现有应用到 Linux 容器


把应用放到容器中算得上是平台移植，我准备突出介绍究竟是什么让移植某些应用到容器当中这么困难。


（通过容器，）开发者现在对 /sbin 、/lib、 /usr/bin、 /usr/lib 中的内容有完全的控制权。但是，他们面临的挑战是，他们仍需要将数据和配置放到 /etc 或者 /var/lib 文件夹中。对于基于镜像的容器来说，这是一个糟糕的想法。我们真正需要的是代码、配置以及数据的隔离。我们希望开发者把代码放在容器当中，而数据和配置通过不同的环境（比如，开发、测试或生产环境）来获得。


这意味着我们（或者说平台）在实例化容器时，需要挂载 /etc 或 /var/lib 中的一些文件或文件夹。这会允许我们到处移动容器并仍能从环境中获得数据和配置。听起来很酷吧？这里有个问题，我们需要能够干净地隔离配置和数据。很多现代开源软件比如 Apache、MySQL、MongoDB、Nginx 默认就这么做了。[但很多自产的、历史遗留的、或专有程序并未默认这么设计](http://rhelblog.redhat.com/2016/04/21/architecting-containers-part-4-workload-characteristics-and-candidates-for-containerization/)。对于很多组织来讲，这是主要的痛点。对于开发者来讲的最佳实践是，开始架构新的应用，移植遗留代码，以完成配置和数据的完全隔离。


### 镜像签名简介


信任机制是容器的重要议题。容器镜像签名允许用户添加数字指纹到镜像中。这个指纹随后可被加密算法测试验证。这使得容器镜像的用户可以验证其来源并信任。


容器社区经常使用“容器镜像”这个词组，但这个命名方法会让人相当困惑。Docker、LXD 和 RKT 推行获取远程文件来当作容器运行这样的概念。这些技术各自通过不同的方式处理容器镜像。LXD 用单独的一层来获取单独一个容器，而 Docker 和 RKT 使用基于开放容器镜像（OCI）格式，可由多层组成。糟糕的是，会出现不同团队和组织对容器镜像中的不同层负责的情况。容器镜像概念下隐含的是容器镜像格式的概念。拥有标准的镜像格式比如 OCI 会让容器生态系统围绕着镜像扫描、签名，和在不同云服务提供商间转移而繁荣发展。


现在谈到签名了。


容器存在一个问题，我们把一堆代码、二进制文件和类库放入其中。一旦我们打包了代码，我们就要把它和必要的文件服务器（注册服务器）共享。代码只要被共享，它基本上就是不具名的，缺少某种密文签名。更糟糕的是，容器镜像经常由不同人或团队控制的各个镜像层组成。每个团队都需要能够检查上一个团队的工作，增加他们自己的工作内容，并在上面添加他们自己的批准印记。然后他们需要继续把工作交给下个团队。


（由很多镜像组成的）容器镜像的最终用户需要检查监管链。他们需要验证每个往其中添加文件的团队的可信度。对于最终用户而言，对容器镜像中的每一层都有信心是极其重要的。


*作者 Scott McCarty 于 8 月 24 日在 ContainerCon 会议上作了题为 [Containers for Grownups: Migrating Traditional & Existing Applications](https://lcccna2016.sched.org/event/7JUc/containers-for-grownups-migrating-traditional-existing-applications-scott-mccarty-red-hat) 的报告，更多内容请参阅报告[幻灯片](http://schd.ws/hosted_files/lcccna2016/91/Containers%20for%20Grownups_%20Migrating%20Traditional%20%26%20Existing%20Applications.pdf)。*




---


via: <https://opensource.com/bus/16/8/introduction-linux-containers-and-image-signing>


作者：[Scott McCarty](https://opensource.com/users/fatherlinux) 译者：[Tanete](https://github.com/Tanete) 校对：[wxy](https://github.com/wxy)


本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


OK
Fundamentally, all major software, even open source, was designed before image-based containers. This means that putting software inside of containers is fundamentally a platform migration. This also means that some programs are easy to migrate into containers, while [others are more difficult](http://rhelblog.redhat.com/2016/04/21/architecting-containers-part-4-workload-characteristics-and-candidates-for-containerization/).

I started working with image-based containers nearly 3.5 years ago. In this time I have containerized a ton of applications. I have learned what's real, and what is superstition. Today, I'd like to give a brief introduction to how Linux containers are designed and talk briefly about image signing.

## How Linux containers are designed

What most people find confusing about the image based Linux containers, is that it's really about breaking an operating system into two parts: [the kernel and the user space](http://rhelblog.redhat.com/2015/07/29/architecting-containers-part-1-user-space-vs-kernel-space/). In a traditional operating system, the kernel runs on the hardware and you never interact with it directly. The user space is what you actually interact with and this includes all the files, libraries, and programs that you see when you look at a file browser or run the `ls`

command. When you use the `ifconfig`

command to change an IP address, you are actually leveraging a user space program to make kernel changes to the TCP stack. This often blows people's minds if they haven't studied [Linux/Unix fundamentals](http://rhelblog.redhat.com/tag/architecting-containers/).

Historically, the libraries in the user space supported programs that interacted with the kernel (`ifconfig`

, `sysctl`

, `tuned-adm`

) and user-facing programs such as web servers or databases. Everything was dumped together in a single filesystem hierarchy. Users could inspect the */sbin* or* /lib* directories and see all of the applications and libraries that support the operating system itself, or inspect the */usr/sbin* or */usr/lib* directory to see all of the user-facing programs and libraries (Check out the [Filesystem Hierarchy Standard](https://en.wikipedia.org/wiki/Filesystem_Hierarchy_Standard)). The problem with this model was that there was never complete isolation between operating system programs and business supporting applications. Programs in */usr/bin* might rely on libraries which live in */lib*. If an application owner needed to change something, it could break the operating system. Conversely, if the team in charge of doing security updates needed to change a library, it could (and often did) break business facing applications. It was a mess.

With image-based containers such as Docker, LXD, and RKT, an application owner can package and modify all of the dependencies in */sbin, /lib, /usr/bin*, and */usr/lib* without worrying about breaking the underlying operating system. Essentially, using image-based containers cleanly isolates the operating system into two parts, again, the kernel and the user space. Now dev and ops can update things independently of each other, kinda...

There is some serous confusion though. Often, each application owner (or developer) doesn't want to be responsible for updating application dependencies such as `openssl`

, `glibc`

, or hardening underlying components, such as XML parsers, or JVMs, or dealing with performance settings. Historically, these problems were delegated to the operations team. Since we are packing a lot of dependencies in the container, the delegation of responsibility for all of the pieces in the container is still a real problem for many organizations.

## Migrating existing applications to Linux containers

Putting software inside of containers is basically a platform migration. I'd like to highlight what makes this difficult to migrate some applications into containers.

Developers now have complete control over what's in */sbin, /lib, /usr/bin*, and */usr/lib*. But, one of the challenges they have is, they still need to put data and configuration in folders such as */etc* or */var/lib*. With image-based containers, this is a bad idea. We really want good separation of code, configuration, and data. We want the developers to provide the code in the container, but we want the data and configuration to come from the environment, e.g. development, testing, or production.

This means we need to mount some files from */etc* or directories from */var/lib* when we (or better, the platform) instantiate a container. This will allow us to move the containers around and still get its configuration and data from the environment. Cool, right? Well, there is a problem, that means we have to be able to isolate configuration and data cleanly. Many modern open source programs like Apache, MySQL, MongoDB, or Nginx do this by default, [but many home-grown, legacy, or proprietary programs are not designed to do this by default](http://rhelblog.redhat.com/2016/04/21/architecting-containers-part-4-workload-characteristics-and-candidates-for-containerization/). This is a major pain point for many organizations. A best practice for developers would be to start architecting new applications and migrating legacy code so that configuration and data are cleanly isolated.

## Introduction to image signing

Trust is a major issue with containers. Container image signing allows a user to add a *digital fingerprint* to an image. This fingerprint can later be cryptographically tested to verify trust. This allows the user of a container image to verify the source and trust the container image.

The container community uses the words “container image" quite a lot, but this nomenclature can be quite confusing. Docker, LXD, and RKT operate on the concept of pulling remote files and running them as a container. Each of these technologies treats containers images in different ways. LXD pulls a single container image with a single layer, while Docker and RKT use Open Container Image (OCI)-based images which can be made up of multiple layers. Worse, different teams or even organization may be responsible for different layers of a container image. Implicit in the concept of a container image is the concept of a Container Image Format. Having a standard image format such as OCI will allow an ecosystem to flourish around container scanning, signing, and movement between cloud providers.

Now on to signing.

One of the problems with containers is we package a bunch of code, binaries, and libraries into a container image. Once we package the code, we share it with essentially fancy file servers which we call Registry Servers. Once the code is shared, it is basically anonymous without some form of cryptographic signing. Worse yet, container images are often made up of image layers which are controlled by different people or teams of people. Each team needs to have the ability to check the last team's work, add their work, and then put their stamp of approval on it. They then need to send it on to the next team.

The final user of the container image (really made up of multiple images) really needs to check the chain of custody. They need to verify trust with every team that added files to the container image. It is critical for end users to have confidence about every single layer of the container image.

*Scott McCarty will give a talk called Containers for Grownups: Migrating Traditional & Existing Applications at ContainerCon on August 24. Talk attendees will gain a new understanding of how containers work, and be able to leverage their current architectural knowledge to the world of containers. He will teach attendees which applications are easy to put in containers and why, and he'll explain which types of programs are more difficult and why. He will provide tons of examples and help attendees gain confidence in building and migrating their own applications into containers. *

## Comments are closed.