当前位置:首页 » 服务存储 » 容器化存储实践
扩展阅读
webinf下怎么引入js 2023-08-31 21:54:13
堡垒机怎么打开web 2023-08-31 21:54:11

容器化存储实践

发布时间: 2023-02-14 13:25:16

1. 深挖Kubernetes存储为何如此难及其解决方案

以Kubernetes为代表的容器编排工具在应用开发部署领域起正发挥着颠覆性的变革作用。随着微服务架构的发展,从开发人员的角度来看,应用逻辑架构与基础设施架构之间开始解耦,这意味着开发者能够将精力更多集中在软件构建以及价值交付身上。

当管理Docker镜像的时候,Kubernetes也让实际应用变的十分便捷灵活。在利用Kubernetes进行容器架构的应用部署时,管理员们将在无需修改底层代码的前提下将其部署在任何位置——包括公有云、混合云乃至私有云。

虽然Kubernetes在扩展性、便携性与管理性等方面的表现都相当给力,但截至目前,它仍然不支持存储状态。与之对应的是,如今的大多数应用都是有状态的——换言之,要求在一定程度上配合外部存储资源。

Kubernetes架构本身非常灵活的,能够根据开发者的需求、规范以及实际负载情况,对容器进行随意创建与撤销。此外,Pod和容器还具有自我修复与复制能力。因此从本质上讲,它们的生命周期普遍非常短暂。

但是,现有持久存储解决方法无法支持动态的应用场景,而持久化存储也无法满足动态创建与撤销的需求。

当我们需要将有状态应用部署到其它基础架构平台,或者另一家内部或混合云供应商的环境中时,可移植性低下无疑将成为我们面临的巨大挑战。更具体地讲,持久化存储解决方案往往会锁定于特定云服务供应商,而无法灵活完成转移。

另外,云原生应用中的存储机制也相当复杂、难于理解。Kubernetes中的不少存储术语极易混淆,其中包含着复杂的含义与微妙的变化。再有,在原生Kubernetes、开源框架以及托管与付费服务之间还存在着诸多选项,这极大增加了开发人员在做出决定之前的考量与试验成本。

以下是CNCF列出的云原生存储可选方案:

我们首先从最简单的场景出发,即在Kubernetes当中部署一套数据库。具体流程包括:选择一套符合需求的数据库,让它在本地磁盘上运行,然后将其作为新的工作负载部署到集群当中。但是,由于数据库中存在的一些固有属性,这种方式往往无法带来符合预期的效果。

容器本身是基于无状态原则进行构建的,凭借这一天然属性,我们才能如此轻松地启动或撤销容器环境。由于不存在需要保存及迁移的数据,集群也就不需要同磁盘读写这类密集型操作绑定在一起了。

但对于数据库,其状态必须随时保存。如果以容器方式部署在集群当中的数据库不需要进行迁移,或者不需要频繁开关,那么其基本属性就相当于一种物理存储设备。在理想情况下,使用数据的容器应该与该数据库处于同一Pod当中。

当然,这并不是说将数据库部署在容器中的作法不可取。在某些应用场景下,这样的设计完全能够满足需求。举例来说,在测试环境或者处理非生产级数据时,由于总体数据量很小,将数据库纳入集群完全没有问题。但在实际生产中,开发人员往往需要仰仗于外部存储机制。

Kubernetes到底是如何与存储资源彼此通信的?其利用的是控制层接口。这些接口负责将Kubernetes与外部存储相对接。接入Kubernetes的外部存储解决方案被称为“卷插件(Volume Plugins)”。正是有了卷插件的存在,存储资源才得以抽象化并实现可移植性。

以前,卷插件一般由核心Kubernetes代码库进行构建、链接、编译以及装载。这样就极大的限制了开发人员的发挥空间,同时也带来了额外的维护开销。因此,项目维护人员们决定在Kubernete的代码库上增加一些新的存储功能。

随着CSI以及Flexvolume的引入,卷插件如今可以在集群中直接部署,而完全无需更改代码库。

原生Kubernetes与存储

持久卷是由管理员负责配置的存储单元,它们独立于任何单一Pod之外,因此不受Pod生命周期的影响。

存储资源有两种使用方式:静态存储与动态存储。

实际上,静态定义的持久卷并不能适应Kubernetes的可移植特性,因为存储资源具有对环境的依赖性——例如AWS EBS或者GCE Persistent Disk。另外,手动绑定还需要根据不同供应商的存储方案修改YAML文件。

在资源分配方面,静态配置实际上也违背了Kubernetes的设计原则。后者的CPU与内存并非事先被分配好绑定在Pod或者容器上,而是以被动态形式进行分配。

通过简单的说明,相信大家已经了解了原生Kubernetes对外部存储资源的使用方式。当然,这里仅仅做出概括,实际使用场景中还有更多其它因素需要考量。

CSI——容器存储接口

下面来看容器存储接口(简称CSI)。CSI是由CNCF存储工作组创建的统一标准,旨在定义一个标准的容器存储接口,从而使存储驱动程序能够在任意容器架构下正常起效。

CSI规范目前已经在Kubernetes中得到普及,大量驱动插件被预先部署在Kubernetes集群内供开发人员使用。如此一来,我们就可以利用Kubernetes上的CSI卷来访问与CSI兼容的开放存储卷。

CSI的引入,意味着存储资源能够作为Kubernetes集群上的另一种工作负载实现容器化以及部署。

相关开源项目

目前,围绕云原生技术涌现出大量工具与项目。但作为生产场景中的一大突出问题,我们往往很难在云原生架构中选择最合适的开源项目。换言之,解决方案选项太多,反而令存储需求变得更难解决。

我们再看一次CNCF列出的云原生存储的可选方案:

下面我会分享一下当下流行的存储方案Ceph与Rook,还有Rancher开源的容器化分布式存储Longhorn。

Ceph

Ceph是一种动态托管、横向扩展的分布式存储集群。Ceph面向存储资源提供一种逻辑抽象机制,其设计理念包括无单点故障、自管理以及软件定义等特性。Ceph可以面向同一套存储集群分别提供块存储、对象存储以及文件存储的对应接口。

Ceph架构相当复杂的,其中使用到大量的底层技术,例如RADOS、librados、RADOSGW、RDB、CRUSH算法,外加monitor、OSD以及MDS等功能性组件。这里我们先不谈它的底层架构,关键在于Ceph属于一种分布式存储集群,这使得扩展更便利、能够在不牺牲性能的前提下消除单点故障,且提供涵盖对象存储、块存储以及文件存储的统一存储体系。

Ceph架构图

Rook

另一个有趣且颇具人气的项目是Rook,这是一项旨在将Kubernetes与Ceph融合起来的技术方案。从本质上讲,它将计算节点和存储节点放进了同一个集群当中。

Rook是一种云原生编排器,并对Kubernetes做出扩展。Rook允许用户将Ceph放置在容器内,同时提供卷管理逻辑以立足Kubernetes之上实现Ceph的可靠运行。Rook还使本应由集群管理员操作的多种任务完成了自动化实现,其中包括部署、引导、配置、扩展以及负载均衡等等。

Rook自身不具备持久状态,也不需要单独管理。这,才是真正与Kubernetes设计原则相符的存储资源管理方案。

Rook凭借着将Ceph与Kubernetes协同起来的强大能力而颇受欢迎,在GitHub上获得近4000颗星,1600多万次的下载,并吸引到100多名贡献者,现已进入CNCF孵化阶段。

Longhorn

Longhorn项目是Rancher Labs推出的开源的基于云和容器部署的分布式块存储新方式。Longhorn遵循微服务的原则,利用容器将小型独立组件构建为分布式块存储,并使用容器编排来协调这些组件,形成弹性分布式系统。

如今,基于云和容器的部署规模日益扩大,分布式块存储系统也正变得越来越复杂,单个存储控制器上的volume数量在不断增加。2000年代初,存储控制器上的volume数量只有几十个,但现代云环境却需要数万到数百万的分布式块存储卷。存储控制器变成了高度复杂的分布式系统。

Longhorn充分利用了近年来关于 如何编排大量的容器和虚拟机的核心技术 。例如,Longhorn并没有构建一个可以扩展到100,000个volume的高度复杂的控制器,而是出于让存储控制器简单轻便的考虑,创建了100,000个单独的控制器。然后,我们可以利用像Kubernetes这样的最先进的编排系统来调度这些独立的控制器,共享一组磁盘中的资源,协同工作,形成一个弹性的分布式块存储系统。

Longhorn基于微服务的设计还有很多其他优势。因为每个volume都有自己的控制器,在升级每个volume的控制器和replica容器时,是不会导致IO操作明显的中断的。Longhorn可以创建一个长期运行的工作来编排所有live volume的升级,同时确保不会中断系统正在进行的操作。为确保升级不会导致意外的问题,Longhorn可以选择升级一小部分volume,并在升级过程中出现问题时回滚到旧版本。这些做法在现代微服务应用中已得到广泛应用,但在存储系统中并不常见。希望Longhorn可以 助力于微服务在存储领域的更多应用。

结 语

对于实际应用层面出现的任何问题,最重要的自然是判断需求、设计系统或者选择适当的工具。同样的道理也适用于云原生环境。虽然具体问题非常复杂,但也必然会出现大量工具方案尝试解决。随着云原生世界的持续发展,我们可以肯定,新的解决方案将不断涌现。未来,一切都会更加美好!

2. 持久化存储之 PV、PVC、StorageClass

容器化一个应用比较麻烦的地方,就是对于有状态的服务的管理,最常见的状态就是 存储状态 。

创建的PVC只有和对应的PV绑定才可以使用
绑定条件:

成功绑定之后,Pod 就是声明PVC绑定的持久化存储了,使用方法如下:

Pod 只需要在 volumes 字段里声明要使用的 PVC 的name,等Pod创建后,Kubelet会将 PVC 绑定的 PV, 例如上面的 NFS 类型的 volume 挂载到容器内目录。

PVC 和 PV 的设计,其实跟“面向对象”的思想完全一致

当每次创建 PVC 声明使用存储时,都需要去手动的创建 PV,来满足 PVC 的使用。

可以用一种机制来根据用户声明的存储使用量(PVC)来动态的创建对应的持久化存储卷(PV)。k8s 用 StorageClass 来实现动态创建 持久化存储。

存储控制器 Volume Controller,是用来专门处理持久化存储的控制器,其一个子控制循环 PersistentVolumeController 负责实现 PV 和 PVC 的绑定。
PersistentVolumeController 会 watch kube-apiserver 的 PVC 对象。如果发现有 PVC对象创建,则会查看所有可用的 PV, 如果有则绑定,若没有,则会使用 StorageClass 的配置和 PVC 的描述创建 PV 进行绑定。

所谓将一个 PV 与 PVC 进行“绑定”,其实就是将这个PV对象的名字,填在了 PVC 对象的 spec.volumeName 字段上

3. Docker+ Kubernetes已成为云计算的主流(二十六)

最近正在抽时间编写k8s的相关教程,很是费时,等相关内容初步完成后,再和大家分享。对于k8s,还是上云更为简单、稳定并且节省成本,因此我们需要对主流云服务的容器服务进行了解,以便更好地应用于生产。

主流云服务容器服务介绍

Docker+ Kubernetes已成为云计算的主流

亚马逊AWS

Amazon Web Services (AWS) 是亚马逊公司旗下云计算服务平台,为全世界范围内的客户提供云解决方案。AWS面向用户提供包括弹性计算、存储、数据库、应用程序在内的一整套云计算服务,帮助企业降低IT投入成本和维护成本。

那么如何在AWS上运行Docker呢?AWS 同时为 Docker 开源解决方案和商业解决方案提供支持,并且可通过多种方式在 AWS 上运行容器:

微软Azure

Microsoft Azure 是一个开放而灵活的企业级云计算平台。通过 IaaS + PaaS 帮助用户加快发展步伐,提高工作效率并节省运营成本。

Azure是一种灵活和支持互操作的平台,它可以被用来创建云中运行的应用或者通过基于云的特性来加强现有应用。它开放式的架构给开发者提供了Web应用、互联设备的应用、个人电脑、服务器、或者提供最优在线复杂解决方案的选择。

在容器这块,Azure同样的提供了众多解决方案:

下面我们侧重介绍下以下服务:

阿里云

阿里云(www.aliyun.com)创立于2009年,是全球领先的云计算及人工智能 科技 公司,为200多个国家和地区的企业、开发者和政府机构提供服务。2017年1月阿里云成为奥运会全球指定云服务商。2017年8月阿里巴巴财报数据显示,阿里云付费云计算用户超过100万。阿里云致力于以在线公共服务的方式,提供安全、可靠的计算和数据处理能力,让计算和人工智能成为普惠 科技 。阿里云在全球18个地域开放了49个可用区,为全球数十亿用户提供可靠的计算支持。此外,阿里云为全球客户部署200多个飞天数据中心,通过底层统一的飞天操作系统,为客户提供全球独有的混合云体验。

飞天(Apsara)是由阿里云自主研发、服务全球的超大规模通用计算操作系统。 它可以将遍布全球的百万级服务器连成一台超级计算机,以在线公共服务的方式为 社会 提供计算能力。 从PC互联网到移动互联网到万物互联网,互联网成为世界新的基础设施。飞天希望解决人类计算的规模、效率和安全问题。飞天的革命性在于将云计算的三个方向整合起来:提供足够强大的计算能力,提供通用的计算能力,提供普惠的计算能力。飞天诞生于2009年2月,目前为全球200多个国家和地区的创新创业企业、政府、机构等提供服务。

同样,阿里云对容器也提供了友好的支持:

容器服务提供高性能可伸缩的容器应用管理服务,支持用Docker和Kubernetes进行容器化应用的生命周期管理,提供多种应用发布方式和持续交付能力并支持微服务架构。容器服务简化了容器管理集群的搭建工作,整合了阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器运行环境。

容器服务 Kubernetes 版(简称 ACK)提供高性能可伸缩的容器应用管理能力,支持企业级 Kubernetes 容器化应用的全生命周期管理。容器服务 Kubernetes 版简化集群的搭建和扩容等工作,整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳的 Kubernetes 容器化应用运行环境。

阿里云弹性容器实例(Elastic Container Instance)是 Serverless 和容器化的弹性计算服务。用户无需管理底层 ECS 服务器,只需要提供打包好的镜像,即可运行容器,并仅为容器实际运行消耗的资源付费。

容器镜像服务(Container Registry)提供安全的镜像托管能力,稳定的国内外镜像构建服务,便捷的镜像授权功能,方便用户进行镜像全生命周期管理。容器镜像服务简化了Registry的搭建运维工作,支持多地域的镜像托管,并联合容器服务等云产品,为用户打造云上使用Docker的一体化体验。

腾讯云

腾讯云为腾讯倾力打造的云计算品牌,以卓越 科技 能力助力各行各业数字化转型,为全球客户提供领先的云计算、大数据、人工智能服务,以及定制化行业解决方案。其基于QQ、微信、腾讯 游戏 等海量业务的技术锤炼,从基础架构到精细化运营,从平台实力到生态能力建设,腾讯云将之整合并面向市场,使之能够为企业和创业者提供集云计算、云数据、云运营于一体的云端服务体验。

在容器这块,腾讯云提供了如下解决方案:

腾讯云容器服务(Tencent Kubernetes Engine ,TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务。腾讯云容器服务完全兼容原生 kubernetes API ,扩展了腾讯云的 CBS、CLB 等 kubernetes 插件,为容器化的应用提供高效部署、资源调度、服务发现和动态伸缩等一系列完整功能,解决用户开发、测试及运维过程的环境一致性问题,提高了大规模容器集群管理的便捷性,帮助用户降低成本,提高效率。容器服务提供免费使用,涉及的其他云产品另外单独计费。

容器实例服务(Container Instance Service , CIS)可以帮用户在云上快捷、灵活的部署容器,让用户专注于构建程序和使用容器而非管理设备上。无需预购 CVM(云服务器),就可以在几秒内启动一批容器来执行任务。同时,开发者也可以通过 kubernetes API 把已有kubernetes 集群的 pod 调度到 CIS 上以处理突增业务。CIS 根据实际使用的资源计费,可以帮用户节约计算成本。使用 CIS 可以极大降低用户部署容器的门槛,降低用户执行 batch 型任务或处理业务突增的成本。

从上面主流的云服务中我们可以看到,没有哪家云厂商不支持Docker,同样的,也没有哪家云厂商不支持Kubernetes!也就是说,Docker+ Kubernetes已经成为云计算的主流!

什么是Kubernetes(k8s)

Kubernetes(简称k8s)诞生于谷歌,是一个开源的,用于管理云平台中多个主机上的容器化的应用,k8s的目标是让部署容器化的应用简单并且高效,其提供了应用部署、规划、更新、维护的机制。

k8s主要有以下特点:

支持公有云,私有云,混合云,多重云(multi-cloud) 。可以将容器化的工作负载从本地开发计算机无缝移动到生产环境。在本地基础结构以及公共云和混合云中,在不同环境中协调容器,保持一致性。

支持模块化,插件化,可挂载,可组合。并且k8s的扩展和插件在社区开发者和各大公司的支持下高速增长,用户可以充分利用这些社区产品/服务以添加各种功能。

支持自动部署,自动重启,自动复制,自动伸缩/扩展,并且可以定义复杂的容器化应用程序并将其部署在服务器群集甚至多个群集上——因为k8s会根据所需状态优化资源。通过内置的自动缩放器,k8s可轻松地水平缩放应用程序,同时自动监视和维护容器的正常运行。

Kubernetes正在塑造应用程序开发和管理的未来

k8s构建于 Google 数十年经验,一大半来源于 Google 生产环境规模的经验。结合了社区最佳的想法和实践,而且还在不断地高速迭代和更新之中。

她衔着金钥匙出生,一诞生就广受欢迎,更是在2017,其打败了所有的竞争对手,赢得了云计算的战争——主流的云厂商基本上都纷纷放弃了自己造“轮子”的举动,终止了各自的容器编排工具,加盟了k8s阵营,其中包括Red Hat、微软、IBM、阿里、腾讯、华为和甲骨文等。

k8s像风暴一样席卷了应用开发领域,并且已成为云原生应用程序(架构、组件、部署和管理方式)的事实标准,大量的开发者和企业正在使用k8s创建由微服务和无服务器功能组成的现代架构。

Docker+ Kubernetes已成为云计算的主流

容器是现代软件交付的未来,而Kubernetes是编排容器的最佳方案(事实上的标准)。

Docker 和Kubernetes相辅相成,联手打下了云计算的“万里江山”。Docker 为打包和分发容器化应用程序提供了一个开放的标准,而 Kubernetes 则协调和管理通过 Docker 创建的分布式容器化应用程序。换句话说,Kubernetes 提供了部署和运行通过Docker生成的应用程序所需的基础结构。

在主流的云服务,基于Docker+k8s的新型PaaS平台具有敏捷部署、弹性伸缩、灵活调度、故障自动恢复等优势,充分满足业务扩展中的资源支持,因此在短短两年之内,便从Docker Swarm、Cloud Foundry Diego、Kontena、Apache Mesos、Amazon ECS…等大量对手中脱颖而出,拿下了皇冠。

k8s和Docker的胜利意味着这是有史以来第一次,无论使用哪一种云平台,研发人员都可以拥有完全相同的计算环境。

4. Kubernetes存储

在 Docker的设计 实现中, 容器中的数据是临时性的,当容器销毁或重新启动时存储在容器内部的数据将会全部丢失 ,但实际上很多容器化应用是需要持久化保存的数据,这就需要使用Docker数据卷挂载宿主机上的文件或者目录到容器中以保证数据的持久化存储。在 Kubernetes中Pod重建如同Docker销毁一样,数据就会丢失 ,Kubernetes也通过挂载数据卷方式为Pod数据提供持久化能力,这些数据卷以Pod为最小单位进行存储,通过共享存储或分布式存储在各个Pod之间实现共享。

Kubernetes是由Master节点及Node节点组成的,在Master节点中通过etcd存储了Kubernetes集群的节点信息、Pod信息、容器信息、配置信息。Node节点主要对外提供容器服务,着重描述Node节点与存储相关的内容。

Kubernetes以Pod为单位对外提供容器服务,真正的服务是通过Service进行访问的。 Kubernetes中的服务按类型分成三种:无状态服务(stateless)、普通有状态服务、有状态集群服务 。

无状态服务:不需要持久化存储的,即使Pod重建也不会受影响,只要确保服务的可靠性便可,Kubernetes通过ReplicationSet来保证某个服务的实例数量。

普通有状态服务:这类服务需要保留服务的状态,通常通过Kubernetes提供的Volume及Persistent Volume、Persistent Volume Claim来保存状态。

有状态的集群服务:这类服务除了保存服务状态的同时还需要提供集群管理的功能,集群管理过程中也涉及临时数据的保存、集群内数据共享等。

Kubernetes中涉及存储的主要使用场景 :

1) 容器集群相关配置信息及运行时信息保存,这类信息存储在etcd中。

2) 服务的基本配置文件及证书文件。

3) 服务的状态存储、数据存储信息。

4) 集群内不同服务交换共享的数据信息。

1) 临时文件形式 :同一个Pod内不同容器间通过共享内存方式访问,会创建一个空目录,交换完信息后会删除这个空目录。

2) HostPath方式 :同一个Node内不同的Pod间进行信息共享使用HostPath方式(比如时区timezone)。如果Pod配置了EmptyDir数据卷,则它在Pod的生命周期内都会存在。当Pod被分配到Node上的时候,会在Node上创建EmptyDir数据卷,并挂载到Pod的容器中。

3) PV及PVC: Kubernetes的持久化存储机制的核心是PV(Persistent Volume)、PVC (Persistent Volume Claim)。PV是Volume插件,关联到真正的后端存储系统,PVC是从PV中申请资源,而不需要关心存储的提供方。PVC和PV的关系就如同Pod和Node一样,Pod是消费Node提供的资源,PVC是消费PV提供的存储资源。PVC和PV通过匹配完成绑定关系,PVC可以被Pod里的容器挂载。

4) 网络方式 :不同Node节点之间的数据共享通过网络方式使用,通常采用分布式存储方式。开源的分布式文件系统比较流行的选择有GlusterFS和Ceph,还有一些其他的分布式文件系统(NFS)选择。

5) 自定义插件方式 :Kubernetes提供了丰富的Volume的插件来进行存储管理。如果存储管理接口不够用,用户可以通过CSI或Flex Volume进行扩展。

存储管理组件(存储组件)主要是接收北向API收到的Rest请求,维护持久卷的生命周期管理。如创建、删除卷, 存储组件负责与后端存储软件交互完成实际的创建、删除卷等操作;并负责调用Kubernetes原生接口创建对应的PVC和PV 。

存储后端系统提供数据文件的实际持久化能力,不仅需要实现数据文件的读写、副本复制等存储操作,通常还需具备多节点高可用的能力。

当需要为自己的微服务应用挂载持久卷时,只需要通过存储组件创建持久卷,存储组件会在Kubernetes业务集群创建PVC/PV,并到后端存储系统(如GlusterFS)上创建真正的物理Volume,同时维护好PVC/PV/Volume之间的一一映射对应关系。这样,用户部署容器时就可以选择挂载相应的持久卷,部署后相应的卷就可以挂载到对应的容器。应用挂载持久卷后,进行读写时就类似于本地目录的读写操作。

在Pod进行重建或者迁移到其他节点时,Pod可以自动挂回原来对应的持久卷,继续使用原先的数据。多个Pod可以共享一个持久卷,从而达到容器间文件共享的目的。

摘抄自陆平的《基于Kubernetes的容器云平台实战》一书的第11章Kubernetes存储

5. 超值一篇分享,Docker:从入门到实战过程全记录

作者 | 天元浪子

来源 | CSDN博客

想要真正理解Docker,就不得不从虚拟化技术的发展历程说起。普遍认为虚拟化技术经历了物理机时代、虚拟机时代,目前已经进入到了容器化时代。可以说,Docker是虚拟化技术不断发展的必然结果。

那么,什么是容器呢?容器和虚拟机有什么不同?Docker和容器又是什么关系呢?搞明白这几个问题,Docker的概念就清晰了。

1.1 虚拟机和容器

借助于VMWare等软件,可以在一台计算机上创建多个虚拟机,每个虚拟机都拥有独立的操作系统,可以各自独立的运行程序。这种分身术虽然隔离度高(操作系统级),使用方便(类似物理机),但占用存储资源多(GB级)、启动速度慢(分钟级)的缺点也是显而易见的。

相较于虚拟机,容器(Container)是一种轻量型的虚拟化技术,它虚拟的是最简运行环境(类似于沙盒)而非操作系统,启动速度快(秒级)、占用存储资源少(KB级或MB级),容器间隔离度为进程级。在一台计算机上可以运行上千个容器,这是容器技术对虚拟机的碾压式优势。

1.2 容器、镜像和Docker

Docker是一个开源的应用容器引擎,可以创建容器以及基于容器运行的程序。Docker可以让开发者打包他们的应用和依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化。

听起来很简单,但是在Docker和容器之间,还隐藏着一个镜像的概念,令初学者颇感困惑。本质上,Docker镜像是一个特殊的文件系统,它提供容器运行时所需的程序、库、资源、配置等文件。Docker镜像类似于一个py文件,它需要Docker的运行时(类似于Python解释器)运行。镜像被运行时,即创建了一个镜像的实例,一个实例就是一个容器。

1.3 Docker 和 k8s

作为容器引擎,Docker为容器化的应用程序提供了开放的标准,使得开发者可以用管理应用程序的方式来管理基础架构,实现快速交付、测试和部署代码。随着容器的大量使用,又产生了如何协调、调度和管理容器的问题,Docker的容器编排应运而生。

k8s是Google开源的一个容器编排引擎,它支持自动化部署、大规模可伸缩、应用容器化管理,是一个开源的,用于管理云平台中多个主机上的容器化的应用,k8s的目标是让部署容器化的应用简单并且高效,k8s提供了应用部署、规划、更新、维护的一种机制。

Docker和k8sr都是以containerd(容器化标准)作为运行时,因此使用Docker创建的镜像完全可以在k8s中无障碍的使用。


2.1 在ubuntu中安装

在linux系统中安装Docker非常简单,官方为我们提供了一键安装脚本。这个方法也适用于Debian或CentOS等发行版。

安装过程如果出现超时,不要灰心,多试几次,总会成功的。安装完成后,Docker只能被root用户使用,可以使用下面的命令取消权限限制:

然后,重启docker服务:

最后,关闭当前的命令行,重新打开新的命令行就可以了。

顺便提一下,如果在CentOS下安装,可能会出现一堆类似于下面的错误:

这是由于docker和Podman冲突造成的,需要先卸载Podman:

2.2 在Win10中安装

Docker的运行,依赖linux的环境,官方提供了Docker Desktop for Windows,但是它需要安装Hyper-V,Hyper-V是微软开发的虚拟机,类似于 VMWare 或 VirtualBox,仅适用于 Windows 10。这个虚拟机一旦启用,QEMU、VirtualBox 或 VMWare Workstation 15 及以下版本将无法使用!如果你必须在电脑上使用其他虚拟机(例如开发 Android 应用必须使用的模拟器),请不要使用 Hyper-V!

我的电脑是win10家庭版,不能直接安装hyper-v,需要将下面的命令保存到cmd文件中:

然后在cmd文件上点击右键,选择使用管理员运行。执行完毕后会重启,在重启的过程中进行安装。

2.3 Hello world

docker服务启动的情况下,运行下面的命令:

此命令的含义是:

第一次运行时,因为本地没有ubuntu:20.04镜像,docker会自动从镜像服务器下载。下载过程可能需要多试几次,只要成功一次,以后执行就不再需要下载了。

docker官方还提供了一个hello-world镜像,可以直接运行:

此命令省略了镜像版本和运行参数,docker使用latest作为版本,即最新版本。

从hello world的例子中,也可以体验到,docker实例的运行是非常快的。


docker官方的镜像库比较慢,在进行镜像操作之前,需要将镜像源设置为国内的站点。

新建文件/etc/docker/daemon.json,输入如下内容:

然后重启docker的服务:

3.1 列出本地所有镜像

执行命令 docker images 可以查看

当前我本地只有刚才安装的两个镜像。

3.2 从镜像库中查找镜像

执行命令 docker search 镜像名称可以从docker镜像库中查找镜像。

最好选择官方(OFFICIAL)的镜像,这样的镜像最稳定一些。

3.3 下载新的镜像

执行命令docker pull 镜像名称:版本号即可下载新的镜像。

镜像下载后,就可以使用镜像来创建容器了。


4.1 启动容器

执行命令docker run即可启动容器,也就是创建某个镜像的实例。docker run命令非常复杂,可以先执行一个docker run --help来查看帮助:

比如我们要执行python的shell,需要添加-it参数,即:docker run -it python:3.8

4.2 将宿主机的文件挂载到容器

docker容器与宿主机是隔离的,要想让容器内的程序能访问宿主机上的文件,需要通过-v参数将宿主机的文件挂载到容器中。

比如我们在宿主机上有一个hello.py,可以打印hello,想要在python容器中执行,就需要进行挂载。-v后还需要接两个参数,分别是宿主机的目录和容器内的目录,两者使用:分隔,路径必须都是绝对路径。

我的hello.py保存在主目录的/docker_test目录中,将这个目录挂载到容器的/docker_test目录,然后在容器内执行python /docker_test/hello.py:

4.3 容器的端口映射

我们修改一下hello.py,创建一个socket服务端,并监听5000端口,当有客户端连接时,打印客户端的地址,先客户端发送hello,然后关闭连接:

在容器内执行:

接下来,尝试用telnet命令连接,结果却是失败的。原因是,127.0.0.1是宿主机的ip地址,5000是容器的端口,这与我们的习惯稍微有些不同。事实上,docker的容器是非常轻量的,它并没有自己的网络,要想访问容器的端口,需要进行端口映射,将容器的某端口映射到宿主机的端口,客户端连接时,只要与宿主机的端口进行连接就可以了。

需要注意的是,上面的代码创建的服务器,无论如何也不可能被客户端连接,因为代码中绑定了127.0.0.1的ip,在容器中运行时,需要绑定所有ip,即0.0.0.0。

然后,再使用-p参数,-p还需要三个参数,即宿主机的ip地址、宿主机的端口、容器的端口,三者之间使用:分隔。一般的,可以将宿主机的ip地址省略,只写宿主机的端口:容器的端口即可。

这样,就将容器的5000端口映射到了宿主机的5001端口,使用:

即可与容器中的服务器进行连接。

4.4 容器管理

上面的服务运行之后,可以使用docker ps命令,查看运行中的容器:

显示的内容有下面几列:

要想结束容器,可以使用docker kill 容器ID命令。


一般而言,当我们的程序开发完成后,会连同程序文件与运行环境一起制作成一个新的镜像。

要制作镜像,需要编写Dockerfile。DockeFile由多个命令组成,常用的命令有:

注意,Docker镜像中有一个层的概念,每执行一个RUN命令,就会创建一个层,层过多会导致镜像文件体积增大。尽量在RUN命令中使用&&连接多条shell命令,减少RUN命令的个数,可以有效减小镜像文件的体积。

5.1 自制显示文本文件内容镜像

编写cat.py,接收一个文件名,由python读取文件并显示文件的内容:

这个例子比较简单,缩写Dockerfile如下:

这个Dockerfile的含义是:

需要说明的是,ENTRYPOINT有两种写法:

这里采用第二种写法,是因为我们要在外部给容器传递参数。执行命令编译Docker镜像:

这个命令中,-t的含义是目标,即生成的镜像名为hello,版本号为1.0,别忘了最后那个.,这叫到上下文路径,是指 docker 在构建镜像,有时候想要使用到本机的文件(比如复制),docker build 命令得知这个路径后,会将路径下的所有内容打包。

这样,我们的第一个镜像就制作完成了,使用下面的命令执行它:

即可看到~/docker_test/cat/files/test.txt的内容。

5.2 自制web服务器镜像

我们使用tornado开发一个网站,而python的官方镜像是没有tornado库的,这就需要在制作镜像时进行安装。

测试的ws.py如下:

编写Dockerfile文件如下:

在此我们验证一下CMD与ENTRYPOINT的区别。在Dockerfile所在有目录下执行如下命令:

执行完成后,再使用docker images使用就可以看到生成的镜像了,然后使用下面的命令运行:

在浏览器中输入宿主机的ip和8000端口,就可以看到页面了。

在这个例子中,我使用的运行命令是CMD,如果在docker run中指定的其他的命令,此命令就不会被执行,如:

此时,容器中被执行的是python命令,而不是我们的服务。在更多情况下,我们希望在docker run命令中为我们的服务传参,而不是覆盖执行命令,那么,我们应该使用ENTRYPOINT而不是CMD:

上面这种写法,是不支持传递参数的,ENTRYPOINT和CMD还支持另一种写法:

使用这种写法,docker run命令中的参数才可以传递给hello.py:

这个命令中,--port=9000被作为参数传递到hello.py中,因此容器内的端口就成了9000。

在生产环境中运行时,不会使用-it选项,而是使用-d选项,让容器在后台运行:

这种方式下,即使当前的控制台被关闭,该容器也不会停止。

5.3 自制apscheler服务镜像

接下来,制作一个使用apscheler编写的服务镜像,代码如下:

Dockerfile也是信手拈来:

生成镜像:

应该可以运行了,文件复制需要两个目录,在运行时,可以使用两次-v来挂载不同的目录:


前面用到的官方python镜像大小足足882MB,在这个基础上,再安装用到的第三方库,添加项目需要的图片等资源,大小很容易就超过1个G,这么大的镜像,网络传给客户非常的不方便,因此,减小镜像的体积是非常必要的工作。


docker hub上有个一python:3.8-alpine镜像,大小只有44.5MB。之所以小,是因为alpine是一个采用了busybox架构的操作系统,一般用于嵌入式应用。我尝试使用这个镜像,发现安装一般的库还好,但如果想安装numpy等就会困难重重,甚至网上都找不到解决方案。

还是很回到基本的路线上来,主流的操作系统镜像,ubuntu的大小为72.9MB,centos的大小为209MB——这也算是我更喜欢使用ubuntu的一个重要原因吧!使用ubuntu作为基础镜像,安装python后的大小为139MB,再安装pip后的大小一下子上升到了407MB,要是再安装点其他东西,很容易就赶上或超过python官方镜像的大小了。

看来,寻常路线是很难压缩镜像文件体积了。幸好,还有一条曲线救国的路可走,这就是多阶段构建法。

多阶段构建的思想其实很简单,先构建一个大而全的镜像,然后只把镜像中有用的部分拿出来,放在一个新的镜像里。在我们的场景下,pip只在构建镜像的过程中需要,而对运行我们的程序却一点用处也没有。我们只需要安装pip,再用pip安装第三方库,然后将第三方库从这个镜像中复制到一个只有python,没有pip的镜像中,这样,pip占用的268MB空间就可以被节省出来了。

1、在ubuntu镜像的基础上安装python:

然后运行:

这样,就生成了python:3.8-ubuntu镜像。

2、在python:3.8-ubuntu的基础上安装pip:

然后运行:

这样,就生成了python:3.8-ubuntu-pip镜像。

3、多阶段构建目标镜像:

这个dockerfile需要解释一下了,因为它有两个FROM命令。

第一个是以python:3.8-ubuntu-pip镜像为基础,安装numpy,当然,在实际应用中,把所有用到的第三方库出写在这里。

第二个FROM是以FROM python:3.8-ubuntu镜像为基础,将第三方库统统复制过来,COPY命令后的–from=0的意思是从第0阶段进行复制。实际应用中再从上下文中复制程序代码,添加需要的ENTRYPOINT等。

最后,再运行:

这然,用于我们项目的镜像就做好了。比使用官方python镜像构建的版本,小了大约750MB。


到此,我们的镜像已经制作好了,可是,镜像文件在哪,如何在生产环境下运行呢?

刚才使用docker images命令时,已经看到了生成的镜像:

我们可以使用docker save命令将镜像保存到指定的文件中,保存的文件是一个.tar格式的压缩文件:

将hello.tar复制到生产环境的机器上,然后执行导入命令:

就可以使用了。

6. 金融行业中容器技术被泛接受和使用,容器安全将要面临哪些问题

容器有多安全?
很多人认为,容器比虚拟机安全性更低,因为如果容器主机内核存在漏洞,那么它可以提供一种进入共享它的容器的方法。管理程序也是如此,但由于管理程序提供远远少于Linux内核(通常实现文件系统,网络,应用程序进程控制等)的功能,因此它的攻击面更小。
但是在过去的几年里,为了增强容器的安全性开发了大量的软件。
例如,Docker(和其它容器系统)现在包括一个签名的基础架构,允许管理员签署容器镜像,以防止不可信的容器被部署。
然而,可信任的签名容器不一定可以安全运行,因为在签名后容器中的一些软件可能会被发现漏洞。因此,Docker和其它容器提供容器安全扫描方案,可以就容器镜像是否有任何可被利用的漏洞而通知管理员。
更专业的容器安全软件也被开发出来了。比如Twistlock,它提供的软件可以配置容器的预期行为和“白名单”进程,网络活动(如源和目标IP地址和端口),甚至是某些存储实践,以便可以标记任何恶意的或意外的行为。
另一家专业的容器安全公司Polyverse采用了不同的方法。它利用了这样一个事实,容器可以在几分之一秒内启动,以便每隔几秒在已知的良好状态中重新启动容器化应用程序,将黑客必须利用在容器中运行的应用程序的时间最小化。
哪一个Linux发行版适合用作容器主机?
如果Linux发行版的预期用途只是充当容器主机来运行容器,那么它们大多数都是功能上臃肿的。因此,很多Linux发行版本被设计为专门用于运行容器。
一些例子包括:
·Container Linux(以前的CoreOS Linux)—为容器而构建的第一个轻量级容器操作系统之一。
·RancherOS –由容器构建的简化的Linux发行版,专门用于运行容器。
·Photon OS - 最小的Linux容器主机,被优化在VMware平台上运行。
·Project Atomic Host - Red Hat的轻量级容器操作系统拥有基于CentOS和Fedora的版本,Red Hat Enterprise Linux中还有一个下游企业版本。
·Ubuntu Core - 最小的Ubuntu版本,Ubuntu Core被设计为用于物联网设备和大规模云端容器部署的主机操作系统
如果是Windows环境会怎么样?
除了在任何运行3.10(或更高版本)的Linux内核的Linux发行版上运行,Docker还可以在Windows上运行。
这是因为在2016年,微软在Windows Server 2016和Windows 10中引入了运行Windows容器的能力。这些是为Windows设计的Docker容器,并且它们可以在任何Docker客户端或微软的PowerShell中进行管理。
(微软还引入了Hyper-V容器,这些容器是运行在Hyper-V虚拟机中的Windows容器,用于增加隔离度。)
Windows容器可以部署在Windows Server 2016的标准安装中,精简的Server Core安装或Nano Server安装选项,专门用于在容器或虚拟机中运行应用程序。
除了Linux和Windows之外,Docker还在流行的云平台上运行,包括亚马逊的EC2,谷歌的 Compute Engine,微软的Azure和Rackspace。
容器最终会取代全面的服务器虚拟化吗?
由于一些重要的原因,这在可预见的未来不太可能。
首先,仍然有广泛的意见认为虚拟机比容器提供了更高的安全性,因为它们提供了增强的隔离级别。
其次,可用于编排大量容器的管理工具还不如管理虚拟化基础架构的软件(如VMware的 vCenter或微软的System Center)全面。对这类软件进行了大量投资的公司在没有充分理由的情况下不太可能放弃他们的虚拟化基础架构。
也许更重要的是,虚拟化和容器也开始被视为互补技术而不是敌对技术。这是因为容器可以在轻量级虚拟机中运行,以增加隔离度,进而提高安全性,并且因为硬件虚拟化可以更轻松地管理支持容器所需的硬件基础架构(网络、服务器和存储)。
VMware鼓励投资虚拟机管理基础架构的客户在其轻量级虚拟机上的Photon OS容器Linux发行版上运行容器,而这些轻量级的虚拟机可以在vCenter进行管理。这是VMware的“VM中的容器”策略。
但是,VMware还引入了所谓的vSphere集成容器(vSphere Integrated Containers ,VIC)。这些容器可以被直接部署到独立的ESXi主机,也可以像虚拟机一样被部署到vCenter Server。这是VMware的“容器作为虚拟机”策略。
这两种方法都有其优点,但重要的是,能够在虚拟化基础架构中使用容器而不是替换虚拟机,这往往是很有用的。