什么是Docker
Docker不是虚拟机。 在很多的网络教案中喜欢将Docker与虚拟机进行类比,这种类比用于理解Docker的优势有着不错的作用,因为Docker与虚拟机有着相同的优势。但是从技术而言,虚拟机技术则是对硬件层的虚拟化,而Docker是一种进程隔离技术。简单的说,我们可以在宿主机(Host Machine)使用 ps aux
看到使用Docker启动的进程。这意味着Docker中的程序实际上是跑在宿主操作系统中。这是我不赞成将Docker与虚拟机进行类比的原因。
要去理解Docker,最避不开的是Namespace 与Cgroups。
Namespace 是Linux中对进程之间进行隔离保护的技术。通过Namespace技术,保证了不同Docker Container中看到的内容不一样,这也是Docker的基础。
cgroups(Control Groups)最初叫Process Container,由Google工程师(Paul Menage和Rohit Seth)于2006年提出,后来因为Container有多重含义容易引起误解,就在2007年更名为Control Groups,并被整合进Linux内核。顾名思义就是把进程放到一个组里面统一加以控制 。本质上,Cgroups 是一种对进程资源控制(比如可访问内存,CPU时间使用)的技术手段。
这里我们可以更加清楚的明白,Docker只是一个基于操作系统提供的虚拟化能力的一个上层应用,其核心功能都是由内核实现。这也是与虚拟机最大的不同点。
什么是Kubernetes
Kubernetes 是一个由Google 主导开发的开源容器编排平台,通过数年的发展已经成为了容器编排的事实标准。各大云平台也都提供了完善的Kubernetes功能,我们可以在Kubernetes的源码中看到目前适配的云平台。
在Kubernetes的世界里,我们不在关心具体的服务器细节,我们看到的是一个一个CPU,一条一条内存,一块一块的磁盘摆在眼前,服务器已经被完全的抽象为计算资源。应用服务器的无状态化带来的最大好处是扩容的便利性,我们可以借助云平台,在数分钟内完成百倍的吞吐量扩展。另一方面,对于应用开发者,所有的一切都在kubectl
这一个工具中。
Kubernetes 基础服务
0. Master
Master 管理着整个集群的状态与访问。
1. 节点
节点(Node)代表着一台物理实例。所有集群中的容器都运行在节点中。一旦某一个节点发生故障,则运行在该节点的容器会自动的迁移到其他节点(注意,迁移的过程是销毁重建。事实上为了状态的一致性,在Kubernetes中没有容器的停止与重启的概念)。
在集群运行中,我们可以添加/减少节点。在节点新增之后,
2. 网络
在集群(Cluster)内部,Kubernetes使用虚拟网络层进行网络通讯。这使得每一个容器都有一个自己独立的ip。我们可以从任何一个容器通过这个ip访问到另一个容器内部。
对于Web应用,我们经常需要部署多个实例用于负载均衡,在Kubernetes,可以通过Service服务非常快速的创建一个内部负载均衡(ELB)。对于每一个Service,仍然会分配一个ip,使得我们可以从一个容器去访问另外一组容器而无需任何额外的程序(例如Haproxy,Nginx)支持。
在虚拟网络之上,Kubernetes还提供了针对虚拟网络的DNS系统,我们只需要按照特定的规则就可以访问不同的Service,连ip都不需要知晓。
虚拟网络完全的避开了对于物理网络细节(例如Ip 地址,端口号)的依赖,使得我们可以通过配置得到一个完全一致的集群环境。
除了使用虚拟IP进行访问之外,Kubernetes还提供了基于DNS的访问办法。
3. 存储
我们永远不应该在容器中存储任何需要持久化存储的数据(Mysql,Redis),因为容器会崩溃重建,故障迁移等原因造成自动重新部署,这些动作都会造成数据的丢失。如果在容器中需要使用持久化存储,我们需要使用 Persistent Volume
服务。该服务通过将外部的持久化存储系统挂载到容器中进行使用。对于一块磁盘,可以挂载一个允许写操作的卷到一个容器,以及挂载多个只读权限给多个容器。
4. 配置
不建议将容器中应用程序的配置文件在构建镜像(Docker Image)的时候打包进去,这会造成配置文件的变更需要重新构建。在Kubernetes中,提供了ConfigMap
服务进行配置文件的管理。
我们通过会通过ConfigMap来构建应用程序的配置管理,然后将其挂载到容器中使用。这可以非常容器的让我们的应用程序跑在不同的环境中。
5. 调度
当我们向集群新增加一个(些)容器的时候,集群会自动将容器部署到合适的节点。集群会根据不同节点(Node)的状态(节点状态,节点资源状态)来进行规划,并且自动部署。在新增容器的时候,我们还可以指定对应的Node Label将不同的服务部署到不同的物理实例中,以实现服务的物理隔离。
Kubernetes 核心概念
1. Pod
Pod 是一个或多个Docker容器的组合,它们之间具有共享的存储与网络。Pod也是Kubernetes的最小单位,Pod中的容器会保证永远运行于同一个节点。
比如对于PHP-FPM应用而言,需要PHP-FPM进程与对应的Web Server(Nginx,Apache)。基于Docker最小部署原则,我们会将PHP-FPM与Web Server分为两个镜像。我们可以在一个Pod中同时部署PHP-FPM镜像与Web Server进程,并且它们之间仍然可以通过fastcgi的方式进行通讯。而Kubernetes又保证了这些容器具备相同的生命周期。我们将整体理解为一个应用程序即可。
2. Development
部署(Development)是一个针对如何管理Pod的工具。通过Development我们可以快速地创建多个Pod副本,并且支持滚动热更新,从而实现了应用程序的热更新。我们可以简单的将部署理解为应用的多服务器管理技术手段,在物理机时代,有ansible这样的工具来进行批量部署。
3. Service
我们已经使用部署将我们的应用部署在了多台节点中,尽管Kubernetes已经提供了虚拟IP来让我们进行不同容器之间的通讯,但是这个IP是不稳定的,因为我们的节点会进行故障迁移,宕机,从而可能更新IP。那么我们如何访问这些服务? 答案就是Service。
Service将一些Pod关联在一起,并且设定一些访问策略(比如端口映射),并且允许设定一个固定的ip。当我们尝试去访问这些服务的时候,我们只需要访问Service的ip与端口(映射之后的Service端口)就可以访问这些服务,并且Service本身可以进行负载均衡与健康检查。
这意味着Service是一个内部负载均衡器(ELB)。
4. Ingress
Ingress本质上和Service一样,也是一种流量代理的概念,但是与Service不一样的是:
a. Service是4层代理,Ingress是7层代理(Http)。
b. Service是服务的集群内部访问方法,Ingress则是通过一组规则来控制从外部系统(internet)到多个服务的访问方法。
我们可以设定不同的域名,不同的Http url path 路由到不同的后端服务(Backend Service)。 因此,Ingress代表着流量入口和负载均衡(LB)的作用。
5. StatefulSet
从概念上,StatefulSet与Development是非常的相似,但是不同点是:Development是对无状态Pod的管理(比如Http Server),而StatefulSet则是对有状态的Pod进行管理,比如(数据库 MySQL),同时会遵循固定的顺序进行启动或销毁容器。
通常地,StatefulSet服务都会搭配持久化存储服务一起工作,我们会将数据库的数据目录指向持久化存储中,这保证了容器在销毁、重建之后数据仍然不会丢失。
6. DaemonSet
Daemon Set是一种独特的部署方式,它保证了容器会运行在所有指定的节点中,并且保证在节点生命周期内一直存活。这通常适用于一些节点监控程序或采集程序。比如fluentd
或 logstash
7. Job
Job是一种一次性工作程序的部署方式。它会在所有指定的Pod运行完之后结束生命周期。比如我们可以使用Job来运行数据库迁移程序。
8. CronJob
从名称可知,CronJob是计划任务的方式来运行程序,并且使用Cron语法来描述间隔周期,但是CronJob会有一些需要注意的地方:
a. CronJob是分布式的,它不会保证运行在某一个确定的节点中,除非在YAML中指定。
b. CronJob可能因为调度的问题,造成运行多次,这需要我们保证CronJob程序的幂等性。
Kubernetes YAML 格式分析
Development YAML Sample
apiVersion: apps/v1 #当前服务的API版本,对于Beta中的服务,需要在部署Kubenetes集群中进行开启允许使用beta服务。
kind: Deployment #不同的服务类型使用Kind进行标记,比如Service,Ingress
metadata:
name: nginx-deployment #name是一个非常有用的属性,它会在DNS系统中会使用
labels:
app: nginx #当前服务的标签设定
spec:
replicas: 3 #Pod的副本数量
selector:
matchLabels:
app: nginx
template: #Pod的模板
metadata:
labels:
app: nginx
spec:
imagePullSecrets: #如果镜像使用的私有镜像仓库,则需要指定docker仓库的key才可以pull docker镜像
- name: DOCKER_PULL_KEY_NAME
containers:
- name: nginx #容器名称
env: #附加到容器中的环境变了
- name: ENV_NAME
value: ENV_VALUE
image: nginx:1.7.9 #镜像
ports:
- containerPort: 80 #容器内部的需要导出的端口
总结
Kubernetes 凭借着Google多年的大规模服务器管理经验,将后端架构完整的抽象出了一些更高级别的概念,在这些概念中我们完全脱离了服务器本身的限制,不在操心具体的服务器配置。我们可以将更多的精力关注自己的应用。