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

在使用容器化应用时,你遵循这些最佳实践了吗?git

用Kubernetes,你能够自动化的且按需的、以极少的或者是零宕机时间来扩展业务,这优化了IT成本,而且增长了系统的可靠性。数据库

对于Kubernetes中运行的应用来讲,容器是其核心。当你建立Kubernetes工做负载(也就是用于调度、扩展和升级应用的规则)的时候,你启动了一个运行着服务或者Kubernetes工做负载的容器镜像。镜像在测试以及与应用的代码基的其余部分集成之后,它一般被推送到容器仓库。可是,在这个点以前,当你写服务而且将其容器化的时候,有不少须要牢记在心的最佳实践。缓存

一、跟上最新的Kubernets模安全

随着Kubernets新特性的持续发布,Kubernetes的使用模式可能会有所改变。为了确保你的集群遵循了最近确立的Kubernetes使用模式,咱们建议你遵循官方的Kubernetes文档,并周期性的阅读它;除此以外,也要关注在每一个Kubernetes发布中引入的变动,这些变动体如今发布说明(release notes)中。服务器

二、利用基础镜像节省时间网络

建立用于Kubernetes集群的应用容器涉及到构建一个Docker基础镜像;基于这个基础镜像来构建部分或者全部应用容器。使用基础镜像,使得复用镜像配置成为可能,由于不少应用会共享依赖项、库和配置。架构

Docker Hub和Google Container Registry都有数以千计的、即拿即用的基础镜像供下载。经过在你的应用中使用这些预先配置的基础镜像能够节省大量时间。app

如图1所示,它展现了基础镜像和应用的关系。工具

 

图1 Ubuntu基础镜像和应用的关系学习

三、不要信任随意选择的基础镜像;老是使用漏洞扫描

虽然使用预先构建的镜像很是方便,可是要保证安全,也要确认,首先你对其执行了某些漏洞扫描。

一些开发者会从Docker Hub上获取别人建立的基础镜像,由于初看起来,这个基础镜像有他们须要的包。而后,他们把这个随意选择的容器推送到了生产环境。

这样作是错误的。你在使用的代码版本可能会有可被利用的漏洞,可能有缺陷,更糟糕的,它可能被人有目的的绑上了恶意软件。而只是你不知道而已。

为了减轻风险,你可使用静态分析工具(例如Snyk或者Twistlock)并将其集成进持续集成和持续交付流水线来扫描全部容器中的漏洞。

如下是一条通用规则:若是你确实在基础镜像中发现了漏洞,你应该从新构建它,而不是仅仅为其打补丁。容器应该是不可变的。所以,最佳实践是用已包含的补丁来从新构建镜像,而后从新部署这个镜像。

四、优化基础镜像

从最精简、最小可行的基础镜像开始,而后在其之上构建你的包。经过这种方法,你会准确的知道,在你的容器里面有什么东西。

更小的基础镜像也有助于减小开销。你的应用可能只有5MB,可是,若是你直接把现成的镜像(例如Node.js)加在上面并且包含了整个库,那么你最终可能获得了600MB你不须要的额外库。

更小的镜像带来的其余优点包括:

更快的构建更小的存储空间使用更快的拉取镜像可能更小的攻击面

五、在一个容器中只运行一个进程

与保持小的基础镜像相关的是,在一个容器中只运行一个进程。容器与其托管的应用拥有相同的生命周期。这意味着,你的每一个容器都应该只有一个单一的父进程(如图2中好的例子所示)。

图2 容器进程模型示例

 

按照Google Cloud文档(https://cloud.google.com/solutions/best-practices-for-building-containers#package_a_single_app_per_container),把容器当成虚拟机(能够同时运行多个进程)是常见的错误。虽然可能以这种方式来使用容器,可是这没有利用到Kubernetes的自愈特性(self-healing properties)。

请记住,容器和应用这两者应该在同一时刻启动;相应的,当应用中止运行的时候,容器也应该中止运行。若是在一个容器中有多个进程,那么你可能会遇到这样的状况:应用的状态是不同的,这会致使Kubernetes没法判断容器是不是健康的。

六、正确的处理Linux信号

Linux信号用于控制容器内进程的生命周期。为了把应用和容器的生命周期连接在一块儿,你须要确保应用正确的处理了Linux信号。

Linux信号,例如SIGTERM、SIGKILL和SIGINIT,用于在内核中终止进程。可是,容器中的Linux以不一样的方式执行这些常见的信号。默认状况下,它们不能按预期工做,这致使错误和中断写入。

解决这种问题的方法之一是,使用一种特殊化的init系统,例如适用于容器的Linux Tini。Linux Tini这个工具会正确的注册信号处理器(例如PID),这样一来,对于容器化的应用来讲,Linux信号也能够正常工做了,并且可以优雅的关闭孤立进程和僵尸进程以回收内存。

七、利用Docker构建缓存顺序

容器镜像是使用模板或者Dockerfile中的指令以一系列层的方式构建的。层以及层构建的顺序一般被缓存在容器平台上。例如,Docker就有一个构建缓存,它用于层的复用。这种缓存使你构建更快,可是只有在这种状况下你才可以使用到它:之前构建中用到的全部前置层都是存在的。

例如,你有个构建文件,其中有步骤X、步骤Y和步骤Z。你对步骤Z作了变动。在这种状况下,构建文件会复用缓存中的步骤X和步骤Y,由于在你修改的层(步骤Z)以前的那些层(步骤X和步骤Y)都是存在的。这就加速了构建,节省了一些时间。可是,若是你仅仅改变了步骤X,那么缓存将不会包含其后的任何步骤和层。

虽然这很方便并且节省了时间,可是你必须确保全部的层都是最新的以及它们不是从更老的过期的缓存中拉取出来的。

八、使用相似Helm的包管理器

做为Kubernetes非官方的包管理器,Helm是获取和更新运行在集群上的常见工做负载和容器的另外一个可选方案。Helm使用chart来声明依赖项,并提供了滚动升级和回滚的工具。

对于你但愿在Kubernetes集群中提供的通用服务来讲,你能够利用已存在的基础镜像。这些通用服务的例子包括数据库和Web服务器。对于你的内部应用来讲,你能够建立定制化的基础镜像。建立你本身的chart会简化部署,减小开销,减小开发团队的重复工做。

能够参考文章“Managing Helm Releases the GitOps Way”(https://www.weave.works/blog/managing-helm-releases-the-gitops-way)来学习Helm是如何工做的。

九、使用标签(tag)和语义版本

请记住,你不该该使用:latest标签。对大部分开发者来讲,这是显而易见的事情。可是,若是你没有为容器添加一个定制的标签,那么将永远会尝试从仓库中拉取最新的一个。那个最新的容器可能会也可能不会包含有你认为应该有的变动。

在建立定制化的镜像时,使用镜像标签和语义版本化来追踪对Docker容器的变动。当它们运行在Kubernetes里面时,镜像标签用于表达出你但愿运行在Kubernetes集群中的镜像版本是什么。为了最优化的使用Kubernetes,在选择Docker镜像版本化方案时,要同时考虑生产工做负载和开发流程。

十、聪明的管理秘钥

在不少构建Docker镜像的案例中,你须要受权运行在容器中的应用访问敏感数据。那些敏感数据包括API令牌、私钥和数据库链接字符串。

把那些秘钥嵌入到容器中不是一个安全的解决方案,即便在你保持镜像处于私有状态的状况下。把未加密的秘钥做为Docker镜像的一部分推送出去让你暴露在各类额外的安全风险之下,例如网络和镜像仓库的安全等。Docker架构自己并无为容器中存放未加密敏感数据而作优化。

相反,最容易的安全的在容器外存储秘钥的方式是使用Kubernetes秘钥对象(https://kubernetes.io/docs/concepts/configuration/secret/)。

Kubernetes提供了Secrets抽象来让你在Docker镜像外或者pod定义以外存储秘钥。你能够以在容器内挂载卷的方式来使用Kubernetes秘钥,或者以环境变量的方式来使用它。当你更新Kubernetes Secrets中的秘钥值的时候,只要滚动服务的pods便可开始使用新的凭据。也有一些其余的存储秘钥的选项存在,例如Hashicorp Vault和 Bitnami密封秘钥(https://www.weave.works/blog/storing-secure-sealed-secrets-using-gitops)。

原文连接:https://dzone.com/articles/10-tips-for-building-and-managing-containers

相关文章
相关标签/搜索