微容器——小的,轻便的Docker容器



  • efe66190df3208cb66e877ec32ffb277.png

    Docker 使你能把你的应用和应用的依赖打包到一个良好的自包含镜像中。然后你可以用那个镜像来在容器中运行你的应用。问题是你通常打包了比你需要的更多的东西,座椅最重你得到了一个巨大的镜像和巨大的容器。大多数开始使用Docker的人会使用Docker的官方仓库作为他们的语言的选择,但是不幸的是,如果你使用它们,你会得到一个巨大的镜像,而奔来你可以得到一个小镜像的。你并不需要和这些镜像一起的许多复杂的东西。如果你使用官方的Node镜像构建一个你的应用的Node的镜像,它至少有643MB大,因为这是官方Node镜像的大小。

    我构建了一个简单的Hello World Node 应用,在官方Node镜像上构建,它的大小是644MB。
    ad461135c9ca6667aa2911888e087a2d.jpg

    这很巨大!我的应用加上依赖也不超过1MB,Node.js的运行时大概20MB,什么占据了其他的620MB?我们必须能做的更好。

    什么是微容器?

    一个微容器仅包含OS库和运行应用所需要的语言依赖以及应用本身。不需要更多的。

    与其从所有都包含的开始,不如从仅包含最小的开始,然后在需要的时候添加依赖。

    以上面的Node应用为例,使用一个小的基础镜像,然后安装核心的部分,即Node.js和它的依赖,它只有29MB,小了22倍!

    232d19bc7dd5c4a374869b11c20ec284.jpg

    dff40d912190464e8afdfdfa36aa0c52.png

    如果你想,现在试试运行这两个镜像,docker run –rm -p 8080:8080 treeder/tiny-node:fat 然后 docker run –rm -p 8080:8080 treeder/tiny-node:latest。完全一样的应用,大小大不相同。

    为什么微容器很棒?

    有许多使用微容器的好处:

    • 大小 —— 微容器很小。像上面展示的,没有改变任何代码,镜像大小减少了22倍。
    • 快速/简单的发布 —— 因为镜像非常小,可以更快的从 Docker registry(例如 Docker Hub)下载镜像,因而可以更快的发布到不同的机器。
    • 提高安全性 —— 更少的代码和程序在容器中意味着更小的攻击面。基础OS就更安全(详见下文)。

    这些好处和Unikernels的好处类似,没有任何缺点。

    如何构建微容器

    所有Docker镜像的基础镜像是scratch镜像。它本质上什么都没有。这听起来无用,但是你事实上可以用它创建你的应用的最小可能镜像,如果你把你的应用编译为一个没有依赖的静态二进制文件像Go或C那样。例如,我的treeder/static-go 镜像 包含了一个Go的web应用,整个镜像包括我的应用是5MB。

    78c2c651f65fce6c5c3f87adb417c1df.jpg

    这是关于如何得到尽可能小的镜像:scratch镜像+你的应用的二进制文件。

    并不是所有人都是用Go(不幸地)所以你可能有更多的依赖并且你需要比scratch镜像要多一些的东西。关于Alpine Linux,我不会枯燥的告诉你关于它的细节,但是他们的广告语说出了全部:“Alpine Linux是一个面向安全的,轻量的Linux发行版,基于musl libc和busybox。” 你可以在这里得到详细说明,但是我们这篇文章最关心的是“轻量”的部分。基础的Alpine镜像只有5MB。

    006351c817afb99e95ea729472fcafd9.jpg

    现在我们有一个非常好的OS最为一个基础镜像,它有一个很好的包管理系统来添加我们的依赖。对于我们的简单的Node应用,我们只需要Node自己,所以我们可以只添加Node包。我们的Dockerfile看起来像这样:

    FROM alpine
    RUN apk update && apk upgrade
    RUN apk add nodejs
    

    简单而整洁。现在我们只在镜像中有Node和Node的依赖。
    现在为了添加我们的代码到镜像中,只需要在我们的Dockerfile中添加几行:

    FROM alpine
    RUN apk update && apk upgrade
    RUN apk add nodejs
    WORKDIR /app
    ADD . /app
    ENTRYPOINT [ "node", "server.js" ]
    

    你可以获得示例代码并且在这里查看完整的构建指导。

    同样的规则适用于所有其他的语言。

    适用于所有语言的基础镜像

    幸运的是,我们已经构建了一个适用所有主要语言的基础镜像,你可以在这里找到它们:https://github.com/iron-io/dockers。

    它们有一些优化来使它们尽可能的小,并且我们会定期更新,使得它们比起你自己来做是一个更加好的选择。适用Iron.io的基础镜像,上面Node应用的Dockerfile变成这样:

    FROM iron/node
    WORKDIR /app
    ADD . /app
    ENTRYPOINT [ "node", "server.js" ]
    

    另外,对于每一个其他语言,我们构建了两个版本的这种镜像,一个用于构建,另一个用于运行。用于构建的镜像有所有的构建工具在其中,所以可能比运行时要更大。

    例如,为了构建Node的依赖,你要适用iron/node:dev像这样:

    docker run --rm -v "$PWD":/app -w /app iron/node:dev npm install
    

    然后在你的Dockerfile中使用iron/node然后运行它:

    docker run --rm -it -p 8080:8080 -v "$PWD":/app -w /app iron/node node server.js
    

    同样适用于其他语言,但是你需要使用它们自己的build/vendor/run的指令。

    如果你想要一个语言的不同版本,你可以改变tag,例如你可以使用iron/node:4.1或者iron/node:0.12。你可以在Docker Hub上找到每个语言的所有的版本tag。例如Node的tag在这里:https://hub.docker.com/r/iron/node/tags/。你可以在iron-io/dockers的项目里找到所有其他Docker Hub tags的链接。

    如何为所有的语言构建和打包

    这可能没那么幸运了,但是我们有一些例子来为大多数主流语言使用上面的基础镜像:https://github.com/iron-io/dockerworker。

    如果你看看那个项目中的每个语言的README,它会指引你如何构建你的依赖,测试你的代码,构建一个小的Docker镜像和测试镜像。

    没有回头

    读完本文之后,你应该可以为你的应用创建只包含运行你的的应用需要的东西的Docker镜像。一个容器本质是一个镜像的实例,所以一旦你开始用你的镜像来运行容器,你就进入了微容器的世界。没有(理由)回头。

    原文链接: http://dockone.io/article/1035


登录后回复
 

与 青云QingCloud 社区 的连接断开,我们正在尝试重连,请耐心等待