猫头鹰的深夜翻译:持久化容器存储

前言

临时性存储是容器的一个很大的买点。“根据一个镜像启动容器,随意变更,然后停止变更重启一个容器。你看,一个全新的文件系统又诞生了。”

在docker的语境下:

# docker run -it centos
[root@d42876f95c6a /]# echo "Hello world" > /hello-file
[root@d42876f95c6a /]# exit
exit
# docker run -it centos
[root@a0a93816fcfe /]# cat /hello-file
cat: /hello-file: No such file or directory

当我们围绕容器构建应用程序时,这个临时性存储非常有用。它便于水平扩展:我们只是从同一个镜像创建多个容器实例,每个实例都有自己独立的文件系统。它也易于升级:我们只是创建了一个新版本的映像,我们不必担心从现有容器实例中保留任何内容。它可以轻松地从单个系统移动到群集,或从内部部署移动到云:我们只需要确保集群或云可以访问registry中的镜像。而且它易于恢复:无论我们的程序崩溃对文件系统造成了什么损坏,我们只需要从镜像重新启动一个容器实例,之后就像从未发生过故障一样。

因此,我们希望容器引擎依然提供临时存储。但是从教程示例转换到实际应用程序时,我们确实会遇到问题。真实的应用必修在某个地方存储数据。通常,我们将状态保存到某个数据存储中(SQL或是NOSQL)。这也引来了同样的问题。数据存储也是位于容器中吗?理想情况下,答案是肯定的,这样我们可以利用和应用层相同的滚动升级,冗余和故障转移机制。但是,要在容器中运行我们的数据存储,我们再也不能满足于临时存储。容器实例需要能够访问持久存储。

如果使用docker管理持久性存储,有两种主流方案:我们可以在宿主机文件系统上指定一个目录,或者是由Docker管理存储:

# docker volume create data
data
# docker run -it -v data:/data centos
[root@5238393087ae /]# echo "Hello world" > /data/hello-file
[root@5238393087ae /]# exit
exit
# docker run -it -v data:/data centos
[root@e62608823cd0 /]# cat /data/hello-file
Hello world

Docker并不会保留第一个容器的根目录,但是它会保留“data”卷。而该卷会被再次挂载到第二个容器上。所以该卷是持久存储。

在单节点系统上这样的方法是ok的。但是在一个容器集群环境下如Kubernetes或是Docker Swarm,情况会变得复杂。如果我们的数据存储容器可能在上百个节点中的任意一个上启动,而且可能从一个节点随时迁移到另一个节点,我们无法依赖于单一的文件系统来存储数据,我们需要一个能够感知到容器的分部署存储的方案,从而无缝集成。

容器持久化的需求

在深入容器持久化的方案之前,我们应该先了解一下这个方案应该满足什么特性,从而更好的理解各种容器持久化方案的设计思路。

冗余

将应用移动到容器中并且将容器部署到一个编排环境的原因在于我们可以有更多的物理节点,从而可以支持部分节点当掉。同理,我们也希望持久化存储能够容忍磁盘和节点的崩溃并且继续支持应用运行。在持久化的场景下,冗余的需求更加重要了,因为我们无法忍受任何数据的丢失。

分布式

冗余的持久化驱动我们使用某种分布式策略,至少在磁盘层面上。但是我们还希望通过分布式存储来提高性能。当我们将容器水平扩展到成百上千个节点上是,我们不希望这些节点竞争位于同一个磁盘上的数据。所以当我们将服务部署到各个区域的环境上来减少用户延时时,我们还希望将存储也同时分布式部署。

动态的

容器架构持续变更。新版本不断的被构建,更新,应用被添加或是移除。测试用例被创建并启动,然后被删除。在这个架构下,需要能够动态的配置和释放存储。事实上,配置存储应当和我们声明容器实例,服务和网络连通性一样通过声明来实现。

灵活性

容器技术在飞速发展,我们需要能够引入新的存储策略,并且将应用移植到新的存储架构上。我们的存储策略需要能够支持任何底层架构,从开发人员用于测试的单节点到一个开放的云环境。

透明性

我们需要为各种类型的应用提供村塾,而且我们需要持续更新存储方案。这意味着我们不应该将应用强关联与一个存储方案。因此,存储需要看上去像是原生的,也就是对上层用户来说仿佛是一个文件系统,或者是某种现有的,易于理解的API。

云原生存储

另一种说法是我们希望容器存储解决方案是“Cloud Native”(云原生的)。云原生计算组织(CNCF)定义了云原生系统的三个属性。这些属性也适用于存储:

  • 容器打包: 我们的物理或虚拟存储位于容器之外,但是我们希望它仅对特定容器课件(这样的话,容器就不会共享存储,除非特殊需求)。除此以外,我们可能希望容器化存储管理软件本身,从而利用容器化来管理和升级存储管理软件。
  • 动态管理:对于有状态容器的持久部署,我们需要在无需管理员认为干预的情况下,分配存储给新的容器,并清理失效的存储。
  • 面向微服务:当我们定义一个容器的时候,他应当明确的制定对存储的依赖。除此以外,存储管理软件本身应当基于微服务部署,从而更好的实现扩容和异地部署。

提供容器存储

为了满足容器持久化存储的需求,Kubernetes和Docker Swarm提供了一组声明式资源来声明并绑定持久化存储至容器。这些持久化存储的功能构建与一些存储架构之上。我们首先来看一下这两种环境下是如何支持容器来声明对持久化存储的以来的。

Kubernetes

在Kubernetes中,容器存活于Pods中。每个pod包含一个或多个容器,它们共享网络栈和持久存储。持久化存储的定义位于pod定义的volumn字段下。该卷可以被挂在到pod的任意一个容器下。比如,一下有一个Kubernetes的Pod定义,它使用了一个emptyDir卷在容器间共享信息。emptyDir卷初始为空,即使pod被迁移到另一个节点上仍将保存下来(这意味着容器的崩溃不会使其消失,但是node崩溃会将其删除)

apiVersion: v1
kind: Pod
metadata:
  name: hello-storage
spec:
  restartPolicy: Never
  volumes:
  - name: shared-data
    emptyDir: {}
  containers:
  - name: nginx-container
    image: nginx
    volumeMounts:
    - name: shared-data
      mountPath: /usr/share/nginx/html
  - name: debian-container
    image: debian
    volumeMounts:
    - name: shared-data
      mountPath: /pod-data
    command: ["/bin/sh"]
    args: ["-c", "echo Hello from the debian container > /pod-data/index.html"]

如果你将以上内容保存到名为two-containers.yaml并使用kubectl create -f two-containers.yaml将其部署到kubernetes上,我们可以使用pod的IP地址来访问NGINX服务器,并获取新建的index.html文件。

这个例子说明了Kubernetes是如何支持在pod中使用volumn字段声明一个存储依赖的。但是,这不是真正的持久化存储。如果我们的Kubernetes容器使用AWS EC2,yaml文件如下:

apiVersion: v1
kind: Pod
metadata:
  name: webserver
spec:
  containers:
  - image: nginx
    name: nginx
    volumeMounts:
    - mountPath: /usr/share/nginx/html
      name: web-files
  volumes:
  - name: web-files
    awsElasticBlockStore:
      volumeID: <volume-id>
      fsType: ext4

在这个例子中,我们可以重复创建和销毁pod,同一个持久存储会被提供给新的pod,无论容器位于哪个节点上。但是,这个例子还是无法提供动态存储,因为我们在创建pod之前必须先创建好EBS卷。为了从Kubernetes获得动态存储的支持,我们需要另外两个重要的概念。第一个是storageClass,Kubernetes允许我们创建一个storageClass资源来收集一个持久化存储供应者的信息。然后将其和persistentVolumeClaim,一个允许我们从storageClass动态请求持久化存储的资源结合起来。Kubernetes会帮我们向选择的storageClass发起请求。这里还是以AWS EBS为例:

kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: file-store
provisioner: kubernetes.io/aws-ebs
parameters:
  type: io1
  zones: us-east-1d, us-east-1c
  iopsPerGB: "10"
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: web-static-files
spec:
  resources:
    requests:
      storage: 8Gi
  storageClassName: file-store
---
apiVersion: v1
kind: Pod
metadata:
  name: webserver
spec:
  containers:
  - image: nginx
    name: nginx
    volumeMounts:
    - mountPath: /usr/share/nginx/html
      name: web-files
  volumes:
  - name: web-files
    persistentVolumeClaim:
      claimName: web-static-files

如你所见,我们还在使用volume关键字来制定pod所需要的持久化存储,但是我们使用了额外的PersistentVolumeClaim声明来请求Kubernenetes替我们发起请求。总体上,集群管理员会为每一个集群部署一个StorageClass代表可用的底层存储。然后应用开发者会在第一次需要持久存储时指定PersistentVolumeClaim。之后根据应用程序升级的需要部署和更换pod,不会丢失持久存储中的数据。

Docker Swarm

Docker Swarm利用我们在单节点Docker卷上看到的核心卷管理功能, 从而支持能够为任何节点上的容器提供存储:

version: "3"
services:
  webserver:
    image: nginx
    volumes:
      - web-files:/usr/share/nginx/html
volumes:
  web-files:
    driver: storageos
    driver-opts:
      size: 20
      storageos.feature.replicas: 2

当我们使用docker栈部署时,Docker Swarm会创建web-files卷,仿佛它并不存在。这个卷会被保留,及时我们删除了docker栈。

总的来说,我们可以看到Kubernetes和Docker都满足了云原生存储的要求。他们允许容器声明依赖的存储,并且动态的管理存储从而使其在应用需要时可见。无论容器在集群的哪个机器上运行,他们都能够提供持久存储。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

Docker – 清理磁盘占用

最近,我们开始对一些应用进行 docker 化,不得不说,我已经爱上 Docker 了!这是一个非常棒的工程,在 AWS EC2 上,它让我们的生活变得更加轻松...

49990
来自专栏点滴积累

Docker网络——单host网络

前言 前面总结了Docker基础以及Docker存储相关知识,今天来总结一下Docker单主机网络的相关知识。毋庸置疑,网络绝对是任何系统的核心,他在Docke...

38950
来自专栏云计算教程系列

如何在CentOS 7上使用Docker安装Prometheus

Prometheus是一个开源监控系统和时间序列数据库。它涉及监控的许多方面,例如度量标准的生成和收集,在仪表板上绘制结果数据以及针对异常情况发出警报。为实现这...

1.2K00
来自专栏A周立SpringCloud

Docker系列教程15-Docker容器网络

本文是篇翻译。原文:https://docs.docker.com/engine/userguide/networking/ 本节概述了Docker默认的网络行...

42670
来自专栏编程坑太多

『中级篇』Docker Compose到底是什么(38)

PS:上节通过image 和container的方式创建wordpress,一般麻烦吧还不算特别麻烦,但是相比今天的docker-compose.yml来说,还...

12130
来自专栏CRPER折腾记

Docker折腾记: (1)构建yapi容器,从构建发布到可用

Docker/Linux/Node基础, 比如Linux和docker的常用命令,shell的编写等等

40920
来自专栏Kirito的技术分享

Docker Network—Bridge 模式

又开一个新坑,Docker 系列打算记录一下个人学习 Docker,使用 Docker 应用于项目实践中的一些感悟,可能不会像之前的文章成一个体系,一方面自己对...

62960
来自专栏Laoqi's Linux运维专列

KVM部署篇

80940
来自专栏企鹅号快讯

十分钟带你理解Kubernetes核心概念

本文将会简单介绍Kubernetes的核心概念。因为这些定义可以在Kubernetes的文档中找到,所以文章也会避免用大段的枯燥的文字介绍。相反,我们会使用一些...

30070
来自专栏云计算教程系列

现代化Kubernetes的应用程序

现代无状态应用程序的构建和设计可在Docker等软件容器中运行,并由Kubernetes等容器集群管理。它们使用Cloud Native和Twelve Fact...

16400

扫码关注云+社区

领取腾讯云代金券