Hero Image
K8s Deployments

蓝绿部署 蓝绿部署是一种比较常见的部署方式,经常会用于新旧版本不兼容的情况。 先将新版本(绿版)的服务运行起来。 将所有的流量切到新版本的服务上 删除旧版本(蓝版)的服务 创建新版本的ReplicaSet,将Service指向新的Pods,删除旧版本的ReplicaSet 滚动升级 蓝绿部署可以有效保证业务的正确性,但是也带来了一定的风险,例如稳定性。 假设新部署的应用是有问题的,一旦切换之后就会导致业务的崩溃,造成损失。于是就有了稍微友好的升级方式,滚动升级。 先关闭一个旧版本的实例 开启一个新版本的实例用于替换旧版本 替换成功时候循环1和2,直到所有的实例升级完成。 在整个过程中,如果中途发现异常可以及时停手,及时止损。而且Kubernetes也在客户端中支持了这个特性。kubectl rolling-update。 升级前后RC的Selector都被改变了 操作都是在客户端执行的? 金丝雀发布 金丝雀发布是滚动发布的一种特例,在滚动发布中,是不会等待的,除非中间出错了。但是有些时候,我们并不想要全都升级,可能只是处于POC的一些原因,我们只希望部分实例是新的,大部分是旧的,而这种情形,我们就称之为金丝雀发布。 升级少部分实例 查看效果,如果好,全部升级 如果不好,则不升级 声明式升级 前面介绍的这些升级发布方式在K8s上很多时候是半手工方式执行的,而Kubernetes作为一款DevOPS友好的系统,已经内置了对于部署方式的一种资源抽象,这个资源就是:Deployment。 Deployment –> ReplicaSet –> Pods Deployment 存在的意义为:在升级应用程序时,需要引入额外的ReplicaSet,并协调新旧两个RS,使他们再根据彼此不断修改,而不会造成干扰。Deployment将这些运维过程都代码化,内置为自己的逻辑,从而让升级变得简单。 首先我们使用Deployment创建3个实例 1# deploy.yaml 2--- 3apiVersion: apps/v1 4kind: Deployment 5metadata: 6 name: first-deployment 7 labels: 8 app: simple-pod-deployment 9spec: 10 replicas: 3 11 selector: 12 matchLabels: 13 app: simple-pod-deployment 14 template: 15 metadata: 16 name: simple-pod-deployment 17 labels: 18 app: simple-pod-deployment 19 spec: 20 containers: 21 - name: simple-pod-de 22 image: lukelau/rest-docker:0.

Hero Image
K8s Job

Replicas 和deployment这两类资源都是用于控制workload的。这两种类型的资源一般都是持续运行的,同时还有一些辅助方式帮助workload出现异常时恢复,以及根据情况进行动态伸缩的特性。 Job 根据Job的定义创建出对应的Pod,然后关注Pod的状态, 直到满足定义。例如Pod执行成功了或者执行失败了,并且达到了重试次数。 1--- 2apiVersion: batch/v1 3kind: Job 4metadata: 5 name: pi 6spec: 7 template: 8 spec: 9 containers: 10 - name: pi 11 image: perl 12 command: ["perl", "-Mbignum=bpi","-wle", "print bpi(2000)"] 13 restartPolicy: Never 14 backoffLimit: 4 Job正常执行结束后结果如上图。这是一个只执行一次的Job。它的操作方式就是创建一个Pod,然后运行一遍,然后就退出。如果想执行多次,则只需要增加一个参数 1completions: 2 执行2次时创建了两个Pod,然后保证这两个Pod都执行成功。 我们在使用Deployment等Workload的时候,一般会指定restartPolicy,默认都是RestartOnFail。在Job中不能这么指定,因为这个逻辑应该由Job来控制, 而不是让Pod来控制。 CronJob 定时任务, CronJob就是在Job的基础上加上了周期定义的API 1--- 2apiVersion: batch/v1 3kind: CronJob 4metadata: 5 name: batch-job-pi 6spec: 7 schedule: "0,15,30,45 * * * *" 8 jobTemplate: 9 spec: 10 template: 11 metadata: 12 labels: 13 app: pi-job 14 spec: 15 containers: 16 - name: pi 17 image: perl 18 command: ["perl", "-Mbignum=bpi","-wle", "print bpi(2000)"] 19 restartPolicy: Never

Hero Image
K8s Overview

Node 节点主要负责容器的管理,用于运行容器和保证容器的状态。默认情况下Master节点不承担Node节点的功能,但是可以通过特殊的配置让Master节点也可作为Node节点。 Etcd用于存储Kubernetes的元数据,但是不要求一定要以容器的形式运行。 1kubeadm init --pod-network-cidr=10.244.0.0/16 --apiserver-advertise-address=192.168.0.22 --pod-network-cidr pod 的ippool, --apiserver-advertise-address 为暴露的k8sAPI调用IP. 节点(Node) 一个Node是一个运行K8s的主机,作为K8s worker, 通常称之为Minion。每个节点都运行如下K8s关键组件: kubelet:主节点代理 kube-proxy: service使用其将链接路由到Pod docker/rocket: k8s 使用容器技术创建容器 容器组(Pod) 一个Pod对应若干容器组成的一个容器组,同一个Pod内的容器共享一个存储卷(volume),同一个Pod中的容器共享一个网络Namespace,可以使用localhost互相通信。 Pod是短暂的,不是持续性实体。一个Pod是一个特定的应用打包集合,包含一个或者多个容器。和运行的容器类似,一个Pod只有很短的运行周期。Pod被调度到一个Node运行,直到容器的生命周期结束或者其被删除。 容器组生命周期(Pod Status) 包含所有容器状态集合,包括容器组状态类型,容器组生命周期,事件,重启策略,以及replication controllers. 标签(labels) 标签是用来连接一组对象的,比如容器组Pod。lable可以用来组织和选择子对象。 一个Label是attach到Pod的一对键值对,用来传递用户定义的属性。 Replication Controllers 主要负责指定数量的Pod在同一时间一起运行。Replication controller 确保任意时间都有指定数量的Pod副本在运行。如果为某个Pod创建了Replication Controller并且指定为3副本,它会创建3个Pod,并持续监控他们。如果某个Pod不响应,那么Replication controller 会替换它,保持Pod总数为3. 当创建Replication Controller时,需要指定两个东西。 Pod模板: 用来创建Pod副本的模板 Label:Replication Controller 需要监控的Pod的Label 现在已经创建了Pod的一些副本,那么在这些副本上如何负载均衡呢,我们需要的是service Service: 如果Pod是短暂的,那么重启时IP地址可能会变,怎么才能从前端容器正确可靠的指向后台容器呢。 Service是定义一系列Pod以及访问这些Pod的策略的一层抽象。Service通过Label找到Pod组。因为service是抽象的,所在在图表里通常看不到他们的存在。 现在假定有两个后台Pod,并且定义后台service名称为“backend-service”,label选择器为(tier=backend,app=myapp)。backend-service的Service会完成如下两件重要的事情: 会为Service创建一个本地集群的DNS入口,因此前端只需要DNS查找主机名为“backend-service”就能够解析出前端应用程序可用的IP地址 现在前端已经得到了后台服务的IP地址,但是它应该访问2个后台Pod中的哪一个呢。Service在这两个后台Pod之间提供透明的负载均衡,会将请求发给其中的任意一个。通过每个Node上运行的代理 kube-proxy完成。 Kubernetes Master 集群拥有一个K8s Master,K8s Master 提供集群的独特视角,并拥有一系列组件,如Kubernetes API server. API server 提供可以用来和集群交互的REST 端点。Master 节点包含用来创建和复制Pod的Replication Controller. 参考资料:

Hero Image
K8s StafulSet

前面介绍的几种workload都有一个共性,那就是创建出来的Pod都是一致的。所谓的一致就是说,假设我们使用的是ReplicaSet,创建了3个Pod,那么这3个Pod创了名字一定不一样之外,其他属性可能都是一样的,包括运行时的参数和模式以及数据存储。 如果是Web Service,数据保存到后端的DB中,上述逻辑是没有问题的。 如果使用ReplicaSet来部署一个DB的多实例, 就可肯能存在问题了。 数据持久化,一般使用PVC,当使用PVC 和PV的时候 ​ ReplicaSet -> Pods(3 个) -> 持久卷声明 -> 持久卷 3个Pod的数据都写到同一个Pv中,这样肯定是不行的??? StatefulSet 为了解决Pod的状态性的问题,K8s引入了StatefulSet的概念 Pod有单独的存储和固定的网络标识 需要配备一个headless Service,用于DNS 可以通过DNS快速发现其他的Pod 可以直接通过Pod DNS通信 每个Pod都可以通过DNS访问到,这种特性在其他workload中是不能实现的。通过StatefulSet可以让Pod持有状态,即使因为故障Pod重建了,那么对应的Pod的名字和数据都会保留,和重建之前没有什么区别。 使用StatefulSet 必须建立一个HeadlessService,然后绑定这个headlessservice到StatefulSet。 以MongoDB为例,创建一个StatefulSet,因为还没有介绍到PVC和PV的内容,所以MongoDB将使用本地存储卷。 创建Headless Service 创建 StatefulSet 通过DNS访问Pod 在StatefulSet的Pod中可以通过DNS直接访问其他Pod。 1mongo --host mongo-1.mongo 可以通过<pod-name>.<service-name>的形式访问Pod。这其实和StatefulSet的设计是有关系的,在类似的Deployment中Pod的名字是不固定的,而在StatefulSet中,Pod的名字是固定的。 和ReplicaSet对比 因为有状态的Pod彼此不同,通常希望操作的是其中的特定的一个,所以StatefulSet通常要求你创建一个用来记录每个Pod网络标记的HeadlessService。通过这个Service,每个Pod都拥有独立的DNS记录,而这在ReplicaSet中是不行的?(如果为ReplicaSet创建一个Headless Service会发生啥?) 因为StatefulSet缩容任何时候只会操作一个Pod实例,所以有状态应用的缩容不会很迅速。 StatefulSet在有实例不健康的情况下,是不允许缩容的。 持久存储 一个StatefulSet可以拥有一个或者多个卷声明模板,这些声明会在创建Pod前创建出来,绑定到一个Pod的实例上。 扩容StatefulSet会创建两个API对象,一个Pod和一个卷声明;但是缩容StatefulSet却会删除一个Pod对象,而会留下PVC,因为一旦删除PVC则意味着PV会被回收。 StatefulSet at-most-one Kubernetes 必须保证两个拥有相同标记和绑定相同持久卷声明的有状态的Pod实例不会同时运行。一个StatefulSet必须保证有状态的实例的 at-most-one 语义。也就是说StatefulSet必须保证一个Pod不再运行后,才会去创建它的替换Pod。