到目前为止,假设我们的应用已经从DevOps平台上构建好了,准备部署,那么,我们就需要一个平台,利用它的能力,把应用或微服务部署到合适的主机上运行并监控起来。那么这个平台需要的最基本的功能有:
- 资源管理 : 管理所有分散的主机,形成一个应用可用的机器集群
- 调度编排 : 把应用/容器/服务部署到合适的资源节点并进行监控
把所有资源管理起来就形成了“云”,调度编排目前的最佳实践是以容器镜像为标准的,所以,对容器进行调度的云也叫容器云。我们需要的就是这样一个云平台,进行微服务的调度。
目前业界的主流是 Google的Kubernetes(K8S),提供构建“容器调度编排的云平台”,作为开源软件捐献给了CNCF;大部分第三方云服务提供商提供Kubernetes服务,这意味Kubernetes让应用程序在云平台之间可移植或替换,那么,这篇文章就主要以Kubernetes为主,进行实践和总结。
名词解释
- 云 由很多机器组成,通过网络提供计算,存储,软件等服务
- 容器云 可以对容器进行调度编排的云
- 公有云 第三方云服务商提供
- 私有云 自建的或使用第三方托管
- 混合云 私有云和公有云,一起搭建起来的云
- IAAS: 云的基础设施层,提供计算,存储,网络等服务
- PAAS: 云的平台层,提供公共的分布式软件服务
- SASS: 云的软件服务层,提供个性化软件,产品服务
- 云原生:微服务部署运行在云平台上,并使用它的各种功能
K8S
kubernetes架构
master节点做为集群大脑, node节点用于承载部署的容器应用
Master
典型的master-slave架构, master是cluster的大脑,运行着apiserver、controller-manager、scheduler它的主要职责是对资源管理、调度,还有弹性伸缩、安全认证等。
组件 | 功能描述 |
---|---|
etcd | 保存集群的状态 |
apiserver | 资源操作的唯一入口,并提供认证、授权、访问控制、API注册和发现等机制; |
controller manager | 维护集群的状态,比如故障检测、自动扩展、滚动更新等; |
scheduler | 资源的调度,按照预定的调度策略将Pod调度到相应的机器上 |
Node
Node是kubernetes集群的工作节点,可以是物理机,虚拟机或云主机。
组件 | 功能描述 |
---|---|
kubelet | 维护容器的生命周期,同时也负责Volume(CSI)和网络(CNI)的管理 |
Container runtime | 镜像管理以及Pod和容器的真正运行(CRI) |
kube-proxy | 为Service提供cluster内部的服务发现和负载均衡 |
其它一些组件:
组件 | 功能描述 |
---|---|
kube-dns | 域名解析服务 |
Ingress Controller | 服务提供外网入口 |
Heapster | 资源监控 |
Dashboard | 控制台 |
Federation | 跨可用区的集群 |
资源管理
资源对象
kubernetes 中的 Object,将它们简单的分类为以下几种资源对象:
类别 | 名称 |
---|---|
配置对象 | Node、Namespace、Service、Secret、ConfigMap、Ingress、Label、ThirdPartyResource、 ServiceAccount |
资源对象 | Pod、ReplicaSet、ReplicationController、Deployment、StatefulSet、DaemonSet、Job、CronJob、HorizontalPodAutoscaling |
存储对象 | Volume、Persistent Volume |
策略对象 | SecurityContext、ResourceQuota、LimitRange |
集群资源管理
组件 | 功能描述 |
---|---|
Node | 集群的工作节点 |
Namespace | namespace创建多个“虚拟集群”,这些namespace之间可以完全隔离,也可以通过某种方式共享服务 |
Label | 以key/value键值对的形式附加到各种对象上,建立资源之间的关联 |
Annotation | 将资源对象关联到任意的非标识性元数据,存放对象更多的配置信息 |
Pod
Pod是kubernetes中可以创建的最小部署单元,它由一个或者多个容器组成,它们共享容器存储、网络和容器运行配置项。Pod中的容器总是被同时调度,有共同的运行环境。
Pod 的生命周期
类别 | 描述 |
---|---|
挂起(Pending) | Pod 已被 Kubernetes 系统接受,但有一个或者多个容器镜像尚未创建。等待时间包括调度 Pod 的时间和通过网络下载镜像的时间,这可能需要花点时间。 |
运行中(Running) | 该 Pod 已经绑定到了一个节点上,Pod 中所有的容器都已被创建。至少有一个容器正在运行,或者正处于启动或重启状态。 |
成功(Succeeded) | Pod 中的所有容器都被成功终止,并且不会再重启。 |
失败(Failed) | Pod 中的所有容器都已终止了,并且至少有一个容器是因为失败终止。也就是说,容器以非0状态退出或者被系统终止。 |
未知(Unknown) | 因为某些原因无法取得 Pod 的状态,通常是因为与 Pod 所在主机通信失败。 |
控制器
Kubernetes中内建了很多controller(控制器),用来调度和控制资源对对象。
类别 | 名称 |
---|---|
Deployment | 部署,为 Pod 和 ReplicaSet 提供声明式更新 |
StatefulSet | 作为 Controller 为 Pod 提供唯一的标识。它可以保证部署和 scale 的顺序。解决有状态服务的问题 |
DaemonSet | 确保全部Node 上运行一个 Pod 的副本。当有 Node 加入集群时,也会为他们新增一个 Pod |
ReplicaSet | 确保容器应用的副本数始终保持在用户定义的副本数 |
Job | 批处理任务,即仅执行一次的任务,它保证批处理任务的一个或多个Pod成功结束 |
CronJob | 管理基于时间的 Job |
Horizontal Pod Autoscaling | Pod水平自动缩放 |
Deployment
Deployment为Pod和Replica Set(下一代Replication Controller)提供声明式更新。典型的应用场景包括:
定义Deployment来创建Pod和ReplicaSet
创建:
kubectl create -f https://kubernetes.io/docs/user-guide/nginx-deployment.yaml –record滚动升级和回滚应用
滚动升级:
kubectl rolling-update my-rcName-v1 -f my-rcName-v2-rc.yaml –update-period=10s
回滚:
kubectl rollout undo deployment/nginx-deployment扩容和缩容
扩容:
kubectl scale deployment nginx-deployment –replicas 10
自动伸缩:
kubectl autoscale deployment nginx-deployment –min=10 –max=15 –cpu-percent=80暂停和继续Deployment
Kubernetes YML配置中,apiVersion,kind和metadata是必须配置项,其中编写 Deployment Spec的属性如下:
类别 | 名称 |
---|---|
Pod Template | .spec.template 是 pod template. 它跟 Pod有一模一样的schema,除了它是嵌套的并且不需要apiVersion 和 kind字段 |
Replicas | 指定期望的pod数量,默认是1 |
Selector | 用来指定 label selector ,圈定Deployment管理的pod范围 |
Strategy | 指定新的Pod替换旧的Pod的策略:”Recreate”或者是 “RollingUpdate”。”RollingUpdate”是默认值。 |
Recreate Deployment | 创建出新的Pod之前会先杀掉所有已存在的Pod |
Rolling Update Deployment | 滚动更新,可以指定maxUnavailable 和 maxSurge 来控制 rolling update 进程 |
Max Unavailable | 用来指定在升级过程中不可用Pod的最大数量 |
Max Surge | 指定可以超过期望的Pod数量的最大个数 |
Progress Deadline Seconds | 指定在系统报告Deployment的failed前等待的秒数 |
Min Ready Seconds | 指定没有任何容器crash的Pod并被认为是可用状态的最小秒数 |
Rollback To | 配置Deployment回退的配置。设置该参数将触发回退操作,每次回退完成后,该值就会被清除。 |
Revision | 指定回退到的revision。默认是0,意味着回退到上一个revision。 |
Revision History Limit | 指定可以保留的旧的ReplicaSet数量 |
Paused | 指定暂停和恢复Deployment,对paused deployment中的PodTemplateSpec的修改都不会触发新的rollout,默认是非paused。 |
StatefulSet
StatefulSet 作为 Controller 为 Pod 提供唯一的标识。它可以保证部署和 scale 的顺序。
StatefulSet 适用于有以下某个或多个需求的应用:
- 稳定,唯一的网络标志。
- 稳定,持久化存储。
- 有序,优雅地部署和 scale。
- 有序,优雅地删除和终止。
- 有序,自动的滚动升级。
比如有状态的数据库,各种中间件,zookeeper,等应用。
ReplicationController和ReplicaSet
ReplicationController用来确保容器应用的副本数始终保持在用户定义的副本数,即如果有容器异常退出,会自动创建新的Pod来替代。
Horizontal Pod Autoscaling
自动化弹性伸缩,根据Pod的CPU利用率,内存和用户自定义的metric扩缩容。自定义指标的HPA,例如QPS,并且,可以对接Prometheus。
负载均衡
- Service
应用的负载均衡,为Service 分配一个虚拟ip,并使用Label Selector与一组 Pod 绑定, 实现通过 Service 负载均衡访问到Pod
发布服务 — 服务类型
Kubernetes ServiceTypes 允许指定一个需要的类型的 Service,默认是 ClusterIP 类型。Type 的取值以及行为如下:
- ClusterIP:通过集群的内部IP 暴露服务,选择该值,服务只能够在集群内部可以访问,这也是默认的 ServiceType。
- NodePort:通过每个 Node 上的 IP 和静态端口(NodePort)暴露服务。NodePort 服务会路由到 ClusterIP 服务,这个 ClusterIP 服务会自动创建。通过请求
: ,可以从集群的外部访问一个 NodePort 服务。 - LoadBalancer:使用云提供商的负载均衡器,可以向外部暴露服务。外部的负载均衡器可以路由到 NodePort 服务和 ClusterIP 服务。
ExternalName:通过返回 CNAME 和它的值,可以将服务映射到 externalName 字段的内容(例如, foo.bar.example.com)。
Ingress
Ingress,是一种HTTP方式的负载均衡 路由转发机制,并暴露集群内服务的工具。由实现Ingress 的Ingress Controller, HTTP代理服务器组成。kubernetes当前支持并维护GCE和nginx两种controller。Ingress Controller实时监控Kubernetes API,实时更新HTTP代理服务器的转发规则。
其它方案还有,F5,Kong ,Traefik 等实现的Ingress Controller。
负载均衡 : VIP 和 Service 代理
VIP
在 Kubernetes 集群中,每个 Node 运行一个 kube-proxy 进程。kube-proxy 负责为 Service 实现了一种 VIP(虚拟 IP)的形式, 来捕获到达该 Service 的虚拟 IP和端口的请求 。
转发
然后将请求重定向到 Service 绑定的一组Pod backend 上面。Kubernetes v1.2 起,默认使用 iptables 代理,转发Server请求到backend。
而实现 service 这一功能的关键,就是 kube-proxy。
kube-proxy
运行在每个节点上,监听 API Server 中服务对象的变化,通过管理 更新iptables ,实现random转发。
kube-proxy 有两种实现 service 的方案:userspace 和 iptables
- userspace: service 转发到 kube-proxy iptables,所以效率不高
- iptables: iptables安装在 service,实现监听和转发,是目前默认的方式,效率很高
服务发现
Kubernetes 支持2种基本的服务发现模式 : 环境变量 和 DNS
- 环境变量
当 Pod 运行在 Node 上,kubelet 会为每个活跃的 Service 添加一组环境变量。 - DNS
DNS 服务器监视着创建新 Service 的 Kubernetes API,从而为每一个 Service 创建一组 DNS 记录。
其它
Endpoints 与 Service 同名, 维护了 Service 与 Pod 的映射关系。
调度与编排
调度
Kubernetes中有一个叫做kube-scheduler的组件,监听kube-apiserver中的调度命令,通过资源类型Deployment、DaemonSet、StatefulSet等定义的一些默认调度策略,和标签,将Pod调度到节点上。
- 在特定节点上运行所有pod,指定节点名称
- 节点标签进行调度:指定节点标签, 亲和性
- 容器标签进行调度:将容器调度到具有特定标签的容器的主机
- 基于资源的调度:指定工作负载pod所需的内存和CPU资源
- 仅给主机调度特定服务:主机上指定容器标签,添加节点taints(如主机标签)并使用容差”
- 全局服务: DaemonSet,调度到所有节点上
编排
kubernetes 通过使用YAML文件,来编排描述要创建的资源
一些简化编排的工具:
- 模拟命令执行
kubectl中很多命令支持 –dry-run 和 -o yaml 参数,可以方便地模拟命令执行,并输出yaml格式 - 导出资源描述
kubectl get–export -o yaml 命令会以Yaml格式导出系统中已有资源 - Kompose转换工具
将docker-compose.yml编排文件 转换为Kubernetes YAML编排文件。 - Helm
Helm把Kubernetes资源(比如deployments、services或 ingress等) 打包到一个chart中,而chart被保存到chart仓库。通过chart仓库可用来存储和分享chart。Helm使发布可配置,支持发布应用配置的版本管理,简化了Kubernetes部署应用的版本控制、打包、发布、删除、更新等操作。
身份与权限认证
Service Account
为Pod中的进程提供身份信息RBAC
基于角色的访问控制Network Policy
Pod 之间是如何被允许互相通信,以及如何与其它网络 Endpoint 进行通信。
存储
Secret
Secret解决了密码、token、密钥等敏感数据的配置问题,而不需要把这些敏感数据暴露到镜像或者Pod Spec中。Secret可以以Volume或者环境变量的方式使用。
Secret有三种类型:
- Service Account :用来访问Kubernetes API,由Kubernetes自动创建,并且会自动挂载到Pod的/run/secrets/kubernetes.io/serviceaccount目录中;
- Opaque :base64编码格式的Secret,用来存储密码、密钥等;
- kubernetes.io/dockerconfigjson :用来存储私有docker registry的认证信息。
ConfigMap
解决应用程序会从配置文件、命令行参数或环境变量中读取配置信息,这些配置信息与docker image解耦。ConfigMaps可以被用来:
- 设置环境变量的值
- 在容器里设置命令行参数
- 在数据卷里面创建config文件
Volume
Pod下挂的目录,Pod每次重启可以共享该目录的数据。卷的生命比 Pod 中的所有容器都长,当这个容器重启时数据仍然得以保存。当然,当 Pod 不再存在时,卷也将不复存在。
Persistent Volume
持久化卷
PersistentVolume(PV)
是由管理员设置的存储,它是群集的一部分。就像节点是集群中的资源一样,PV 也是集群中的资源。 PV 是 Volume 之类的卷插件,但具有独立于使用 PV 的 Pod 的生命周期。此 API 对象包含存储实现的细节,即 NFS、iSCSI 或特定于云供应商的存储系统。PersistentVolumeClaim(PVC)
是用户存储的请求。它与 Pod 相似。Pod 消耗节点资源,PVC 消耗 PV 资源。StorageClass
StorageClass 为管理员提供了描述存储 “class(类)” 的方法。 不同的 class 可能会映射到不同的服务质量等级或备份策略,或由群集管理员确定的任意策略。管理员可以为没有申请绑定到特定 class 的 PVC 指定一个默认的 StorageClass。本地持久化存储
本地持久化卷允许用户通过标准 PVC 接口以简单便携的方式访问本地存储。
网络
Kubernetes本身并不提供网络功能,只是把网络接口开放出来,通过插件的形式实现。
Kubernetes管理的是集群,Kubernetes中的网络要解决的核心问题就是每台主机的IP地址网段划分,以及单个容器的IP地址分配。概括为:
- 保证每个Pod拥有一个集群内唯一的IP地址
- 保证不同节点的IP地址划分不会重复
- 保证跨节点的Pod可以互相通信
- 保证不同节点的Pod可以与跨节点的主机互相通信
- 为了解决该问题,出现了一系列开源的Kubernetes中的网络插件与方案,如:
flannel,calico,contiv,weave net,kube-router,cilium,canal
只要实现Kubernetes官方的设计的CNI - Container Network Interface(容器网络接口)就可以自己写一个网络插件,最常用的时flannel和calico插件。
flannel
Kubernetes集群内部存在三类IP,分别是:
- Node IP:宿主机的IP地址
- Pod IP:使用网络插件创建的IP(如flannel),使跨主机的Pod可以互通
- Cluster IP:Service 虚拟IP,通过iptables规则访问服务
在安装node节点的时候,节点上的进程是按照flannel -> docker -> kubelet -> kube-proxy的顺序启动的。
Flannel是作为一个二进制文件的方式部署在每个node上,主要实现两个功能:
- 为每个node分配subnet,容器将自动从该子网中获取IP地址
- 当有node加入到网络中时,为每个node增加路由配置
calico
Calico 创建和管理一个扁平的三层网络(不需要overlay),每个容器会分配一个可路由的ip。由于通信时不需要解包和封包,网络性能损耗小,易于排查,且易于水平扩展。
以上更详细请参考:
https://jimmysong.io/kubernetes-handbook/
http://kubernetes.kansea.com/docs/
云平台
上面介绍了用Kubernetes构建基于容器调度的云平台,在这个平台上我们可以添加和管理集群的机器,安装基础的分布式软件,最后部署我们的应用程序-微服务。
云的通用基础设施
因为它的抽象与通用性,已经成为云的通用基础设施,通用的云平台。分布式应用的管理平台
利用它的编排调度与容器管理,可以轻松快捷的创建和管理分布式应用服务。微服务的运行时平台
提供了负载均衡,服务发现等微服务必须的组件,也随着Service Mesh - istio , FAAS等技术的慢慢成熟,将提供一个让微服务更加简单,快捷的落地方案与最佳实践,也将是一个成熟的微服务运行时平台。