共计 10344 个字符,预计需要花费 26 分钟才能阅读完成。
作者:京东物流 杨建民
一、微服务架构起源
单体架构:能够了解为次要业务逻辑模块(咱们编写的代码模块,不包含独立的中间件)运行在一个过程中的利用,最典型的是运行在一个 Tomcat 容器中,位于一个过程里。单体架构益处是技术门槛低、编程工作量少、开发简略快捷、调试不便、环境容易搭建、容易公布部署及降级,开发运维等总体老本很低、见效快。其毛病也显著:
(1)单体利用零碎比拟收缩与臃肿,耦合度高,导致进行可继续开发和运维很艰难。
(2)单体利用难以承载迅速增长的用户申请和需要。
基于 Spring Framework 的单体利用架构图
分布式架构核心思想是把一个繁多过程的零碎拆分为性能上相互协作又能独立部署在多个服务器上的一组过程,这样一来,零碎能够依据理论业务须要,通过以下两种形式实现某些独立组件的扩容,晋升吞吐量。
- 程度扩大:通过减少服务器数量进行扩容
- 垂直扩大:给零碎中的某些非凡业务调配更好的机器,提供更多资源,从而晋升这些业务的零碎负载和吞吐
分布式架构是将一个宏大的单体利用拆分成多个独立运行的过程,这些过程能通过某种形式实现近程调用,因而,分布式架构要解决的第一个核心技术问题就是独立过程之间的近程通信。该问题的最早答案就是 RPC 技术(Remote Procedure Call),一种典型的微服务架构平台的构造示意图如下:
大家比拟熟知的微服务架构框架有 Dubbo 与 Spring Cloud,之后比拟胜利的微服务架构根本都和容器技术挂钩了,其中最胜利的、影响最大的当属 Kubernetes 平台了,与之类似的还有 Docker 公司推出的 Docker Swarm(在 2017 年底,Docker Swarm 也反对 Kubernetes 了)。
对于微服务架构的劣势因为文章篇幅无限,不再开展,但任何技术都存在两面性,微服务架构具备肯定的复杂性,如开发者必须把握某种 RPC 技术,并且必须通过写代码来解决 RPC 速度过慢或者调用失败等简单问题。为了解决微服务带来的编程复杂性问题,一种新的架构设计思维呈现了,这就是 Service Mesh,Service Mesh 定义是:一个用于解决服务于服务之间通信(调用)的简单的基础架构设施。从实质上看,Service Mesh 是一组网络代理程序组成的服务网络,这些代理会与用户程序部署在一起,充当服务代理,这种代理起初在 Google 的 Istio 产品架构中称为“Sidecar”,其实就是采纳了代理模式的思维去解决代码入侵及反复编码的问题,。下图给出了 Service Mesh 最简略的架构图。Servie Mesh 同样不是本次的配角,感兴趣的小伙伴可自行学习。
二、初识 k8s
官网原文是:K8s is an abbreviation derived by replacing the 8 letters“ubernete”with 8.
k8s 全称 kubernetes,名字来源于希腊语,意思为“舵手”或“领航员”,它是第一代容器技术的微服务架构(第二代是 Servie Mesh)。
Kubernetes 最后源于谷歌外部的 Borg,提供了面向利用的容器集群部署和管理系统。Kubernetes 的指标旨在打消编排物理 / 虚构计算,网络和存储基础设施的累赘,并使应用程序运营商和开发人员齐全将重点放在以容器为核心的原语上进行自助经营。Kubernetes 也提供稳固、兼容的根底(平台),用于构建定制化的 workflows 和更高级的自动化工作。
Kubernetes 具备欠缺的集群治理能力,包含多层次的平安防护和准入机制、多租户利用撑持能力、通明的服务注册和服务发现机制、内建负载均衡器、故障发现和自我修复能力、服务滚动降级和在线扩容、可扩大的资源主动调度机制、多粒度的资源配额治理能力。
Kubernetes 还提供欠缺的管理工具,涵盖开发、部署测试、运维监控等各个环节。
2.1 k8s 架构与组件
Kubernetes 次要由以下几个外围组件组成:
- etcd 保留了整个集群的状态;
- apiserver 提供了资源操作的惟一入口,并提供认证、受权、访问控制、API 注册和发现等机制;
- controller manager 负责保护集群的状态,比方故障检测、主动扩大、滚动更新等;
- scheduler 负责资源的调度,依照预约的调度策略将 Pod 调度到相应的机器上;
- kubelet 负责保护容器的生命周期,同时也负责 Volume(CVI)和网络(CNI)的治理;
- Container runtime 负责镜像治理以及 Pod 和容器的真正运行(CRI);
- kube-proxy 负责为 Service 提供 cluster 外部的服务发现和负载平衡;
2.2 k8s 设计理念
API 设计准则
API 对象是 k8s 集群中的治理操作单元。k8s 集群零碎每反对一项新性能,引入一项新技术,肯定会新引入对应的 API 对象,反对对该性能的治理操作。例如正本集 Replica Set 对应的 API 对象是 RS。
k8s 采纳申明式操作,由用户定义 yaml,k8s 的 API 负责创立。每个对象都有 3 大类属性:元数据 metadata、标准 spec 和状态 status。元数据是用来标识 API 对象的,每个对象都至多有 3 个元数据:namespace,name 和 uid;除此以外还有各种各样的标签 labels 用来标识和匹配不同的对象,例如用户能够用标签 env 来标识辨别不同的服务部署环境,别离用 env=dev、env=testing、env=production 来标识开发、测试、生产的不同服务。标准形容了用户冀望 k8s 集群中的分布式系统达到的现实状态(Desired State),例如用户能够通过复制控制器 Replication Controller 设置冀望的 Pod 正本数为 3;status 形容了零碎理论以后达到的状态(Status),例如零碎以后理论的 Pod 正本数为 2;那么复制控制器以后的程序逻辑就是主动启动新的 Pod,争取达到正本数为 3。
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80
- apiVersion – 创建对象的 Kubernetes API 版本
- kind – 要创立什么样的对象?
- metadata- 具备惟一标示对象的数据,包含 name(字符串)、UID 和 Namespace(可选项)
应用上述.yaml 文件创建 Deployment,是通过在 kubectl 中应用 kubectl create 命令来实现。将该.yaml 文件作为参数传递。如下例子:
$ kubectl create -f docs/user-guide/nginx-deployment.yaml --record
k8s 常见对象:Pod、复制控制器(Replication Controller,RC)、正本集(Replica Set,RS)、部署(Deployment)、服务(Service)、工作(Job)、存储卷(Volume)、长久存储卷和长久存储卷申明(Persistent Volume,PV、Persistent Volume Claim,PVC)、节点(Node)、ConfigMap、Endpoint 等。
管制机制设计准则
- 每个模块都能够在必要时优雅地降级服务管制逻辑应该只依赖于以后状态。这是为了保障分布式系统的稳固牢靠,对于经常出现部分谬误的分布式系统,如果管制逻辑只依赖以后状态,那么就非常容易将一个临时呈现故障的零碎复原到失常状态,因为你只有将该零碎重置到某个稳固状态,就能够自信的晓得零碎的所有管制逻辑会开始依照失常形式运行。
- 假如任何谬误的可能,并做容错解决。在一个分布式系统中呈现部分和长期谬误是大概率事件。谬误可能来自于物理系统故障,内部系统故障也可能来自于零碎本身的代码谬误,依附本人实现的代码不会出错来保证系统稳固其实也是难以实现的,因而要设计对任何可能谬误的容错解决。
- 尽量避免简单状态机,管制逻辑不要依赖无奈监控的外部状态。因为分布式系统各个子系统都是不能严格通过程序外部放弃同步的,所以如果两个子系统的管制逻辑如果相互有影响,那么子系统就肯定要能相互拜访到影响管制逻辑的状态,否则,就等同于零碎里存在不确定的管制逻辑。
- 假如任何操作都可能被任何操作对象回绝,甚至被谬误解析。因为分布式系统的复杂性以及各子系统的绝对独立性,不同子系统常常来自不同的开发团队,所以不能奢望任何操作被另一个子系统以正确的形式解决,要保障呈现谬误的时候,操作级别的谬误不会影响到零碎稳定性。
- 每个模块都能够在出错后主动复原。因为分布式系统中无奈保证系统各个模块是始终连贯的,因而每个模块要有自我修复的能力,保障不会因为连贯不到其余模块而自我解体。
- 每个模块都能够在必要时优雅地降级服务。所谓优雅地降级服务,是对系统鲁棒性的要求,即要求在设计实现模块时划分分明基本功能和高级性能,保障基本功能不会依赖高级性能,这样同时就保障了不会因为高级性能呈现故障而导致整个模块解体。依据这种理念实现的零碎,也更容易疾速地减少新的高级性能,认为不用放心引入高级性能影响原有的基本功能。
三、资源管理
容器云平台如何对租户可用资源进行精密治理,对平台的可用性、可维护性和易用性起着至关重要的作用,是容器云平台可能为用户提供丰盛的微服务治理的基石。在云计算畛域,资源可被分为计算资源、网络资源和存储资源三大类,也可被别离称作计算云、网络云和存储云。
3.1、计算资源管理
Namespace
在 k8s 集群中,提供计算资源的实体叫做 Node,Node 既能够是物理机服务器,也能够是虚拟机服务器,每个 Node 提供了 CPU、内存、磁盘、网络等资源。每个 Node(节点)具备运行 pod 的一些必要服务,并由 Master 组件进行治理,Node 节点上的服务包含 Docker、kubelet 和 kube-proxy。
通过引入 Namespace,k8s 将集群近一步划分为多个虚构分组进行治理,Namespace 所实现“分区”是逻辑上的,并不与理论资源绑定,它用于多租户场景实现资源分区和资源最大化利用。
大多数 Kubernetes 资源(例如 pod、services、replication controllers 或其余)都在某些 Namespace 中,但 Namespace 资源自身并不在 Namespace 中。而低级别资源(如 Node 和 persistentVolumes)不在任何 Namespace 中。Events 是一个例外:它们可能有也可能没有 Namespace,具体取决于 Events 的对象。
Pod
Pod 是 Kubernetes 创立或部署的最小 / 最简略的根本单位,一个 Pod 代表集群上正在运行的一个过程。
一个 Pod 封装一个利用容器(也能够有多个容器),存储资源、一个独立的网络 IP 以及管理控制容器运行形式的策略选项。Pod 代表部署的一个单位:Kubernetes 中单个利用的实例,它可能由单个容器或多个容器共享组成的资源。
每个 Pod 都是运行利用的单个实例,如果须要程度扩大利用(例如,运行多个实例),则应该应用多个 Pods,每个实例一个 Pod。在 Kubernetes 中,这样通常称为 Replication。Replication 的 Pod 通常由 Controller 创立和治理。Controller 能够创立和治理多个 Pod,提供正本治理、滚动降级和集群级别的自愈能力。例如,如果一个 Node 故障,Controller 就能主动将该节点上的 Pod 调度到其余衰弱的 Node 上。
Container
docker 自身比拟重,2015 年 OCI(Open ContainerInitiative)诞生,它定义了镜像规范、运行时规范和散发规范,因为 k8s 自身不具备创立容器的能力,是通过 kubelet 组件调用容器运行时 API 接口和命令来创立容器,Kubernete 与 容器运行时的关系是历史性的,也很简单。然而随着 Kubernete 弃用 Docker,目前支流的运行时次要是 containerd 和 CRI-O
“one-container-per-Pod”模式是 Kubernetes 最常见的用法,一个 Pod 也能够有多个容器。
三个级别的计算资源管理
在 k8s 中,能够从 Namespace、Pod 和 Container 三个级别区治理资源的配置和限度。例如:
- 容器级别能够通过 Resource Request、Resource Limits 配置项
apiVersion: v1
kind: Pod
metadata:
name: memory-demo-3
spec:
containers:
- name: memory-demo-3-ctr
image: vish/stress
resources:
limits:
memory: "1000Gi"
requests:
memory: "1000Gi"
args:
- -mem-total
- 150Mi
- -mem-alloc-size
- 10Mi
- -mem-alloc-sleep
- 1s
- Pod 级别能够通过创立 LimitRange 对象实现设置,这样能够对 Pod 所含容器进行对立配置
apiVersion: v1
kind: LimitRange
metadata:
name: mylimits
spec:
limits:
- max:
cpu: "4"
memory: 2Gi
min:
cpu: 200m
memory: 6Mi
maxLimitRequestRatio:
cpu: 3
memory: 2
type: Pod
- default:
cpu: 300m
memory: 200Mi
defaultRequest:
cpu: 200m
memory: 100Mi
max:
cpu: "2"
memory: 1Gi
min:
cpu: 100m
memory: 3Mi
maxLimitRequestRatio:
cpu: 5
memory: 4
type: Container
- Namespace 级别能够通过对 ReSourceQuota 资源对象的配置,提供一个总体的资源使用量限度,这个限度能够是对所有 Poid 应用的计算资源总量下限,也能够是对所有 Pod 某种类型对象的总数量下限(包含能够创立的 Pod、RC、Service、Secret、ConfigMap 及 PVC 等对象的数量)
apiVersion: v1
kind: ResourceQuota
metadata:
name: pod-demo
spec:
hard:
request.cpu: "4"
request.memory: 8GB
limit.memory:16GB
pods: "2"
3.2 网络资源管理
k8s 的 ip 模型
node Ip:node 节点的 ip,为物理 ip.
pod Ip:pod 的 ip,即 docker 容器的 ip,为虚构 ip。
cluster Ip:service 的 ip,为虚构 ip。提供一个集群外部的虚构 IP 以供 Pod 拜访。实现原理是通过 Linux 防火墙规定,属于 NAT 技术。当拜访 ClusterIP 时,申请将被转发到后端的实例上,如果后端实例有多个,就顺便实现了负载平衡,默认是轮训形式。
跨主机容器网络计划
在 k8s 体系中,k8s 的网络模型设计的一个根本准则:每个 pos 都领有一个独立的 IP 地址,而且假设所有的 Pod 都在一个能够间接联通的、扁平的网络空间中,不论它们是否运行在同一个 Node(宿主机)中,都能够间接通过对方的 IP 进行拜访。但 k8s 自身并不提供跨主机的容器网络解决方案。私有云环境(例如 AWS、Azure、GCE)通常都提供了容器网络计划,然而在公有云环境下,依然须要容器云平台位不同的租户提供各种容器网络计划。
目前,为容器设置 Overlay 网络是最支流的跨主机容器网络计划。Overlay 网络是指在不扭转原有网络配置的前提下,通过某种额定的网络协议,将原 IP 报文封装起来造成一个逻辑上的网络。在 k8s 平台上,倡议通过 CNI 插件的形式部署容器网络。
CNI(Container Network Interface)是 CNCF 基金会下的一个我的项目,由一组用于配置容器的网络接口的标准和库组成,它定义的是容器运行环境与网络插件之间的接口标准,仅关怀容器创立时的网络配置和容器被销毁是网络资源的开释,并且一个容器能够绑定多个 CNI 网络插件退出网络中,如下图。
目前比拟流程的 CNI 插件实现形式有 Flannel、Calico、macvlan、Open vSwitch、间接路由。
Ingress
在 k8s 集群内,利用默认以 Service 的模式提供服务,有 kube-proxy 实现 Service 到容器的负载均衡器的性能,如下图定义一个 mysql service:
kind: Service
apiVersion: v1
metadata:
name: mysql-master
spec:
selector:
app: mysql-master
ports:
port: 3306
targetPort: 3306
此时,集群外是无法访问这个 Service 的。对于须要 k8s 集群外的客户端提供服务的 Service,能够通过 Ingress 将服务裸露进来,并且如果该集群(网络)领有实在域名,则还能将 Service 间接与域名进行对接。
k8s 将一个 Ingress 资源对象的定义和一个具体的 Ingress Controller 相结合来实现 7 层负载均衡器。Ingress Controller 在转发客户申请到后端服务时,将跳过 kube-proxy 提供的 4 层负载均衡器的性能,间接转发到 Service 的后端 Pod(Endpoints),以进步网络转发效率。
如上图展现了一个典型的 HTTP 层路由的 Ingress 例子,其中:
- 对 http://mywebsite.com/api 的拜访将被路由到后端名为“api”的 Service;
- 对 http://mywebsite.com/web 的拜访将被路由到后端名为“web”的 Service;
- 对 http://mywebsite.com/doc 的拜访将被路由到后端名为“doc”的 Service。
如下是一个典型的 Ingress 策略定义,Ingress Controller 将对指标地址 http://mywebsite.com/demo 的拜访申请转发到集群外部服务的 webapp(webapp:8080/demo)
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: mywebsite-ingress
spec:
rules:
-host:mywebsite.com
http:
paths:
- path: /demo
backend:
serviceName: webapp
servicePort: 8080
罕用的 Ingress Controller 有:Nginx、HAProxy、Traefik、apisix 等。
3.3 存储资源
k8s 反对的 Volume 类型
长期目录(emptyDir)
应用 emptyDir,当 Pod 调配到 Node 上时,将会创立 emptyDir,并且只有 Node 上的 Pod 始终运行,Volume 就会始终存。当 Pod(不论任何起因)从 Node 上被删除时,emptyDir 也同时会删除,存储的数据也将永恒删除。注:删除容器不影响 emptyDir。
配置类
- ConfigMap:将保留在 ConfigMap 资源对象中的配置文件信息挂载到容器的某个目录下
- Secret:将保留在 Secret 资源对象中的明码密钥等信息挂载到容器内的某个文件中
- DownwardApI:将 downward API 的数据以环境变量或文件的模式注入容器中
- gitRepo:将某 Git 代码库挂载到容器内的某个目录下
本地存储类
- hostPath:将宿主机的目录或文件挂载到容器内进行应用
- local:从 v1.9 版本引入,将本地存储以 PV 模式提供给容器应用,并可能给实现存储空间的治理
共享存储类
- PV(Persistne Volume):将共享存储定义为一种“长久存储卷”,能够被多个容器共享应用
- PVC(Persistne Volume Claim):用户对存储资源的一次“申请”,PVC 申请的对象是 PV,一旦申请胜利,利用就可能想应用本地目录一样应用共享存储卷。下图是一个 PV 对象 ymal 定义:
apiVersion: v1
kind: PersistentVolume
metadata:
name: example-pv
annotations:
"volume.alpha.kubernetes.io/node-affinity": '{"requiredDuringSchedulingIgnoredDuringExecution": {"nodeSelectorTerms": [
{ "matchExpressions": [
{ "key": "kubernetes.io/hostname",
"operator": "In",
"values": ["example-node"]
}
]}
]}
}'
spec:
capacity:
storage: 100Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Delete
storageClassName: local-storage
local:
path: /mnt/disks/ssd1
PV 与 PVC
PV 和 PVC 互相关系生命周期如上图所示,k8s 的共享存储供给模式包含动态模式(Static)和动静模式(Dynamic),资源供给的后果就是创立好的 PV。运维人员手动创立 PV 就是动态,而动静模式的要害就是 StorageClass,它的作用就是创立 PV 模板。
创立 StorageClass 外面须要定义 PV 属性比方存储类型、大小等;另外创立这种 PV 须要用到存储插件。最终成果是,用户提交 PVC,外面指定存储类型,如果合乎咱们定义的 StorageClass,则会为其主动创立 PV 并进行绑定。
下图通过 ymal 创立一个 StorageClass 对象
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: standard
provisioner: kubernetes.io/aws-ebs // 存储分配器
parameters:
type: gp2
reclaimPolicy: Retain // 回收策略
mountOptions:
- debug
StorageClass 和 PV、PVC 之间的运作关系如下图所示:
CSI
CSI(Container Storage Interface)与 k8s 的关系与 CNI 有点相似,CSI 旨在容器和共享存储之间倡议一套规范的存储拜访接口。在它诞生前,经验了“in-tree”形式、FlexVolume 模式。
CSI 标准用语将存储供应商代码与 k8s 代码齐全解耦,存储插件的代码由存储供应商自行保护。
和 kube-apiserver 间接进行交互的是 K8S 官网间接提供的一些 sidecar 容器,这些 sidecar 间接部署即可。这些 sidecar 容器(次要是上图的三个次要部件)监听本人对应的 CRD,触发对应的操作,通过 UDS 接口间接调用 CSI driver 的接口(例如 CreateVolume()、NodePublishVolme()等)来实现对卷的操作。
要开发 CSI Drivers 一般来说实现以下几个服务:
- CSI Identity service
容许调用者 (Kubernetes 组件和 CSI sidecar 容器) 辨认驱动程序及其反对的可选性能。
- CSI Node service
NodePublishVolume, NodeUnpublishVolume 和 NodeGetCapabilities 是必须的。
所需的办法使调用者可能使卷在指定的门路上可用,并发现驱动程序反对哪些可选性能。
- CSI Controller Service
实现 CreateVolume、DeleteVolume 接口
3.4 多集群资源管理计划 - 集群联邦(Federation)
Federation 是 Kubernetes 的子项目,其设计指标是对多个 Kubernetess 集群进行对立治理,将用户的利用部署到不同地区的数据中心。Federation 引入了一个位于 Kubernetes 集群只上的管制立体,屏蔽了后端各 k8s 子集群,向客户提供了对立的治理入口,如下图:
Federation 管制立体“封装”了多个 k8s 集群的 Master 角色,提供了对立的 Master,包含 Federation API Server、Federation Controller Manafer,用户能够向操作单个集群一样操作 Federation,还对立了全副 k8s 集群的 DNS、ConfigMap,并将数据保留在集中的 etcd 数据库中。
写给读者
对于 k8s 初学者来说,对 k8s 的第一印象应该是概念多,名词多。《kubernetes 权威指南:企业级容器云实战》这本书从企业实际角度动手,讲述了技术的演进,并在很多场景提供了不同技术实现的比照,联合 k8s 中文社区,这本书能够作为学习 k8s 的入门书籍。本文实际上是此书的一篇读书笔记,文章从计算资源、网络资源、存储资源三个方向开展,介绍了 k8s 里一些常见的概念和对象。因为篇幅问题,很多也很重要的概念并没有在文中具体介绍,读者可依据本身状况开展补充学习。对于 k8s 外围组件及工作原理将在后续陆续推出。