58

构建和管理容器的 10 个技巧

 5 years ago
source link: http://dockone.io/article/8823?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

【编者的话】近年来,容器以及 Kubernetes 成为开发者以及企业用户重点关注的技术趋势,本文总结了构建和管理容器的十个重要技巧来优化 IT 成本并提高效率。

容器是 Kubernetes 中应用程序的核心载体。当创建 Kubernetes 工作负载,例如创建用于调度、扩容或者升级应用程序的规则时,首先需要创建一个容器镜像,然后通过该镜像来运行服务或 Kubernetes 工作负载。在完成对镜像的测试并与应用程序其余代码整合后,用户通常会将镜像推送到容器注册中心。但在推送之前,仍然有很多实战技巧可以帮助构建和管理容器。

通过 Kubernetes,用户可以自动扩展业务,而整个过程很少出现零宕机,从而优化 IT 成本并提高系统可靠性。

使用最新 Kubernetes 模式

随着 Kubernetes 不断推出新功能,其应用模式也在逐步改变。为了确保 Kubernetes 集群遵循当前最新 Kubernetes 的应用模式,用户需要定期查阅 Kubernetes 官方文档以及每一个版本的发布说明。

复用基础镜像以节省时间

在 Kubernetes 集群中创建应用容器时,用户需要构建一个 Docker 基础镜像,然后在此镜像基础上构建部分或全部应用容器。有很多应用共享依赖项、库和配置,因此可以在基础镜像完成对共享部分进行配置,从而实现复用。

Docker Hub 和 Google Container 注册中心有数千个可供下载的基础镜像,这些镜像已经预先完成应用配置,随时可以投入使用,这可以节省大量时间。

zuUnUbb.jpg!web

不要轻易相信任何镜像

尽管使用预先构建的镜像很方便,但要格外小心并确保对其运行特定漏洞扫描。

一些开发人员会从 Docker Hub 中获取一个其他用户创建的基础镜像,然后将这个容器推送到生产环境,而这一切只是因为乍一看这个镜像包含了所需要的包。

这里有很多错误:镜像中的代码版本可能不正确;这些代码可能有漏洞;或者更糟糕的情形是该项目可能已经被故意绑定了恶意软件。

为了缓解上述问题,用户可以通过 Snyder 或 Twistlock 来运行静态分析,然后将其整合到 CI/CD(持续集成和持续交付)管道中,进而扫描所有容器漏洞。

一般来说,一旦在基础镜像中发现漏洞,用户就应该重新构建整个镜像,而不是仅仅修复漏洞。容器应该是不变的,因此,需要引入补丁重新构建和部署镜像。

优化基础镜像

从最精简、最可行的基础镜像开始,然后在此基础上构建软件包。通过这种方式,可以准确掌握容器中的全部内容。

较小的基础镜像也可以减少开销。例如,应用可能只有 5MB 大小,如果要添加一个现成的 Node.js 镜像,然后再安装所有的库,整个镜像很可能会变成 600MB 大小,但实际上并不需要这些额外的库。

因此,尽量保持最精简的镜像可以使:

  • 构建更快
  • 存储空间更小
  • 镜像拉取更快
  • 潜在攻击面更小

确保容器只运行一个进程

同保持基础镜像最小化类似的是,确保每个容器只有一个进程。容器的生命周期与它托管的应用程序相同,这意味着每个容器应该只包含一个父进程。

f2ErQfz.jpg!web

按照 Google Cloud 的说法,把容器当作虚拟机并同时运行多个进程是一个常见的错误。虽然容器可以实现这种方式,但这样就无法使用 Kubernetes 的自我修复属性。

通常,容器和应用应该同时启动;同样,当应用停止时,容器也应该停止。如果在一个容器中有多个进程,可能会出现应用程序状态混杂的情形,这将导致 Kubernetes 无法确定一个容器是否健康。

正确处理 Linux 信号

容器通过 Linux 信号来控制其内部进程的生命周期。为了将应用的生命周期与容器联系起来,需要确保应用能够正确处理 Linux 信号。

Linux 内核使用了诸如 SIGTERM、SIGKILL 和 SIGINIT 等信号来终止进程。但是,容器内的 Linux 会使用不同的方式来执行这些常见信号,如果执行结果同信号默认结果不符,将会导致错误和中断发生。

创建专门的 init 系统有助于解决此问题,比如专门针对容器的Linux Tini系统。这个工具正确注册了信号处理程序(比如 PID),容器化应用可以正确执行 Linux 信号,从而正常关闭孤立进程和僵尸进程,完成内存回收。

充分利用 Docker 的缓存构建机制

容器镜像由一系列镜像层组成,这些镜像层通过模板或 Dockerfile 中的指令生成。这些层以及构建顺序通常被容器平台缓存。例如,Docker 就有一个可以被不同层复用的构建缓存。这个缓存可以使构建更快,但是要确保当前层的所有父节点都保存了构建缓存,并且这些缓存没有被改变过。简单来讲,需要把不变的层放在前面,而把频繁改变的层放在后面。

例如,假设有一个包含步骤 X、Y 和 Z 的构建文件,对步骤 Z 进行了更改,构建文件可以在缓存中重用步骤 X 和 Y,因为这些层在更改 Z 之前就已经存在,这样可以加速构建过程。但是,如果改变了步骤 X,缓存中的层就不能再被复用。

虽然这是一种方便的行为,可以节省时间,但是必须确保所有镜像层都是最新的,而不是从旧的、过时的缓存构建而成。

使用类似 Helm 的包管理器

Helm 作为 Kubernetes 的非官方软件包管理器,可以帮助安装和更新集群中运行的共同负载和容器。Helm 可以使用 Chart 声明自定义应用程序依赖项,并提供滚动升级和回滚工具。

用户可以通过现有基础镜像为 Kubernetes 集群提供通用服务,如数据库或 Web 服务;也可以为内部应用程序创建自定义基础镜像,创建自定义的 Charts 可以简化部署,减少开发团队的工作负担和重复性工作。

使用标签和语义化版本号

作为基本原则,用户不应该使用 :latest 标记。对大多数开发人员来说,这是显而易见的。如果不为容器添加自定义标签,它将尝试从镜像仓库中拉取最新版本,而最新的容器可能并没有包括需要的更改。

在创建自定义镜像时,使用镜像标签和语义化版本号来追踪对 Docker 容器的更改。当它们在 Kubernetes 集群中运行时,Kubernetes 通过镜像标签确定应该运行哪个版本。在选择 Docker 镜像版本机制时,应该同时考虑生产负载和开发流程两种情况,这样才能在 Kubernetes 中获得更好的效果。

安全

在很多情况下,当构建 Docker 镜像时,需要让容器内的应用程序访问敏感数据,例如 API 令牌、私钥和数据库连接字符串等。

将这些秘密信息嵌入到容器中并不是一个安全的解决方案,即使只是保存到一个私有容器镜像中。将未加密的隐私数据作为 Docker 镜像的一部分进行处理会面临无数额外的安全风险,包括网络和镜像注册表的安全性,而 Docker 架构本身也决定了无法对容器中未加密的敏感数据进行优化。

相反,用户可以通过使用 Kubernetes Secrets 对象将隐私信息存储在容器外面,这样更简单、安全。

Kubernetes 提供了一个 Secrets 抽象,允许在 Docker 镜像或 Pod 定义之外存储隐私数据。用户可以通过挂载卷或环境变量的方式把这些信息加载到容器中。更新时,只需更换相关服务的 Pod 并使用新的证书即可。用户也可以通过 Hashicorp Vault 以及Bitnami Sealed Secrets来保存隐私数据。

原文链接: 构建和管理容器的 10 个技巧 (作者:勤奋的码农)


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK