赞
踩
Kubernetes系列文章 | 主要内容 |
---|---|
菜鸟学Kubernetes(K8s)系列——(一)关于Pod和Namespace | 通过本文你将学习到: (1)什么是Pod,为什么需要它、如何创建Pod、Pod的健康检查机制(三种探针) (2)什么是标签、标签选择器 (3)什么是Namespace、他能做什么、如何创建它等等 |
菜鸟学Kubernetes(K8s)系列——(三)关于Service、Ingress | 通过本文你将学习到: (1)什么是Service,如何创建它、它的服务发现能力、对外暴露方式 (2)什么是NodePort类型的Service,它的使用方式、工作原理 (3)什么是Ingress,如何创建它、为什么需要Ingress (4)什么的Ingress-Nginx、他和Nginx是什么关系、Ingress的各种功能 (5)什么是headless服务等等 |
菜鸟学Kubernetes(K8s)系列——(四)关于Volume卷(PV、PVC、StorageClass等) | 通过本文你将学习到: (1)什么是Volume卷、它的几种类型(emptyDir、hostPath、NFS)、这几种类型是使用方式 (2)什么是PV-PVC、为什么要用他们、他们是怎么协作的、如何使用PV-PVC (3)动态配置持久卷是什么,它是怎么工作的、如何实现动态的分配PV等等 |
菜鸟学Kubernetes(K8s)系列——(五)关于ConfigMap和Secret | 通过本文你将学习到: (1)什么是ConfigMap,如何创建它、它能用来做什么事情、在实战中怎么使用ConfigMap (2)什么是Secret,如何创建它,怎么使用它等等 |
菜鸟学Kubernetes(K8s)系列——(七)关于Kubernetes底层工作原理 | 通过本文你将学习到: (1)Kubernetes的核心组件:Etcd、Api-Server、Scheduler、Controller-Manager、Kubelet、Kube-proxy的工作方式,工作原理。 (2)Kubernetes集群中核心组件的协作方式、运行原理。 |
菜鸟学Kubernetes(K8s)系列——(八)关于Kubernetes的认证机制(RBAC) | 通过本文你将学习到: (1)Api-Server的认证授权流程(2)什么是ServiceAccount(3)什么是Role、ClusterRole、RoleBinding、ClusterRoleBinding,如何使用他们,他们之间是如何关联协作的。等等 |
菜鸟学Kubernetes(K8s)系列——(番外)实现Deployment的动态扩缩容能力(HPA) | 通过本文你将学会实现Deployment的动态扩缩容能力(HPA) |
菜鸟学Kubernetes(K8s)系列——(番外)安装Ingress-Nginx(工作原理) | 通过本文你将学会安装Ingress-Nginx |
之前有个ReplicationController也可以实现扩缩容,但目前他已经过时了。现在通过Deployment+ReplicaSet来实现这一功能,而且功能更加强大,因为Deployment具有更加便捷的滚动更新能力、ReplicaSet可以实现更加复杂的标签选择器等特性。
Deployment + ReplicaSet > ReplicationController
实际上在开发中我们并不会直接手动的去创建Pod,而是创建一个Deployment这样的工作负载(还有其他的工作负载类型,比如StatefulSet、DaemonSet等,后面会一一提到),由他们来创建并管理实际的Pod。我们可能会想,为什么要用Deployment来管理Pod呢,直接创建Pod不香吗?其实真的不香,通过Deployment管理的Pod具有自愈能力、动态扩缩容能力、滚动升级能力。Deployment才是真香!(至于为什么香,感兴趣的可以自己去看看已经过时的ReplicationController的滚动升级的过程。)
我们可以直接通过一个实验就能明白什么是自愈功能。
首先部署一个Deployment(这时就会创建一个或多个pod,可以通过命令kubectl get pod -owide
查看这个pod部署在哪个节点上)
然后在master上监听这些pod(命令:watch -n 1 kubectl get pod
)
接着在部署pod的节点上去删掉这个Pod中部署的容器(docker rm -f 容器id
),模拟Pod内容器异常。
这时我们在查看Pod(kubectl get pod),会发现有一个Pod处于容器创建的状态,因为我们刚刚删除了这个Pod中的容器,所以他现在在重新创建这个Pod中的容器。
这就是Department的自愈能力,只要由Deployment管理的Pod内的容器出现了异常,那么他会重启这个容器。
而如果是Pod被删除了,那么他会重新拉起一个Pod。如果一个被单独创建的Pod被删除了是不会有人再重新拉起这个Pod的。
需要注意的是:自愈机制不能保证这个Pod还在当前机器上被重启!
通过下面这个图我们再来看看由Deployment管理的Pod的自愈能力:
我们知道Pod中运行的是我们的应用程序,那如果我们对应用程序进行了修改,要怎么做到零停机的更新呢?Deployment可以实现这一操作。
这里我们首先创建一个Deployment,部署一组Pod。
apiVersion: apps/v1 kind: Deployment metadata: name: dep-v2 ### 遵循域名编写规范 namespace: default labels: app: test-01 ### 期望状态 spec: replicas: 5 ### 期望副本你数量 selector: ### 选择器,指定Deployment要控制的所有的Pod的共同标签(即帮助Deployment筛选他要控制哪些Pod) matchLabels: pod-name: ppp ### 和模板template里面的Pod的标签必须一样 ### 编写Pod template: metadata: ### Pod的metadata labels: pod-name: ppp spec: containers: - name: nginx-01 image: nginx
部署:kubectl apply -f dep-v2.yaml --record (–record选项会记录历史版本号,后面会有用)
创建一个Deployment会产生三个资源:Deployment资源、ReplicaSet资源(RS只提供了副本数量的控制功能)、Pod资源。
他们之间的关系是:Deployment控制RS、RS创建和管理Pod
这里可能有一个疑问,为什么非得由Deployment来控制ReplicaSet呢?其实他是为了方便后面的滚动升级的过程。
我们在创建一个Deployment后可以发现,由他产生的5个Pod名称中均包含一串额外的数字,这是什么?
这个数字实际上对应Deployment和ReplicaSet中的Pod模板的哈希值。Pod是由ReplicaSet管理的,所以我们再看看ReplicaSet的样子
我们发现ReplicaSet的名称中也包含了其Pod模板的哈希值。后面会说到**同一个Deployment会创建多个ReplicaSet,用来对应和管理多组不同版本的Pod模板。**像这样使用Pod模板的哈希值,就可以让Deployment始终对给定版本的Pod模板创建相同的ReplicaSet。
滚动更新(RollingUpdate),这是默认的升级策略
该策略会逐渐地删除旧的Pod,与此同时创建新的Pod,使应用程序在整个升级过程中都处于可用状态,并确保其处理请求的能力没有因为升级而有所影响。
重新创建(Recreate)
该策略在会一次性删除所有旧版本的Pod,之后才开始创建新的Pod。如果你的应用程序不支持多个版本同时对外提供服务,需要在启动新版本之前完全停用旧版本,那么需要使用这种策略。但是使用这种策略的话,会导致应用程序出现短暂的不可用。
有一点我们需要了解:仅当Deployment中定义的Pod模板(即
.spec.template
)发生改变时,那么Deployment会使用更新后的Pod模板来创建新的实例,同时会删除旧的Pod。
- 如果仅仅是修改Deployment的副本数等是不会触发Deployment的升级动作的。
- 每次滚动升级后产生的新的Pod是会由一个新的RS进行控制,所以一个Deployment可能会对应多个RS。而旧的RS仍然会被保留,这个旧的RS会在下面回滚的时候被用到!
比如我使用V1版本的镜像部署了第一版本的Deployment,然后再对其进行升级,改为V2版本的镜像。但是这时V2版本的镜像存在一个BUG,这个BUG还不是那么好改。为了不让用户感知到升级导致的内部服务器错误,尽快修复问题。我们可以采用回滚,让他先回滚到V1版本,正常为用户提供服务,然后我们再下来慢慢修改V2版本,修改完后重新升级Deployment。
kubectl rollout undo deployment dep-v2
kubectl rollout history deployment dep-v2
注意,我们前面在部署Deployment时在命令后面加了个 --record 参数,这样会在版本历史中的CHANGE-CAUSE栏记录部署信息,加上这个 --record参数主要是为了方便用户辨别每次版本做了哪些修改。
通过在undo命令中指定一个特定的版本号,便可以回滚到特定的版本。
例如,想要回滚到第一个版本: kubectl rollout undo deployment dep-v2 --to-revision=1
创建新的RS,准备就绪后,替换旧的RS,最后旧的RS会被保留!
回滚升级之所以这么快能完成,主要是因为Deployment始终保持着升级的版本历史记录。这个历史版本号实际上是保存在ReplicaSet中的(每个RS都用特定的版本号来保存Deployment不同版本的完整信息),滚动升级成功后,老版本的ReplicaSet也不会被删掉,这也使得回滚操作可以回滚到任何一个历史版本,而不仅仅是上一个版本。
我们可以通过指定Deployment的
revisionHistoryLimit
属性来限制一个Deployment所能保存的ReplicaSet(历史版本)数量。
spec.strategy
:指定新Pod替换旧Pod的策略
spec.strategy.type
:指定替换策略,有两个属性值
- Recreate:重新创建。将之前的Pod直接先杀死再重新创建Pod(这种方式不推荐)
- RollingUpdate:滚动更新。如果选择的这个属性,则可以通过
spec.strategy.rollingUpdate
指定滚动更新策略
spec.strategy.rollingUpdate
:指定滚动更新速率下面这两个属性用于控制Deployment的滚动升级的速率。他们可以决定在Deployment滚动升级期间一起可以替换多少个Pod。
spec.strategy.rollingUpdate.maxSurge
:指定除Deployment期望的副本数外,最多允许创建超出的Pod的数量,可以指定百分比,也可以指定数字spec.strategy.rollingUpdate.maxUnavailable
:指定在滚动升级期间,最多可以允许有多少个Pod不可用(反过来说就是保证集群中至少有多少Pod是可以处理请求的)示例:
apiVersion: apps/v1 kind: Deployment metadata: name: mydeploy namespace: default labels: dep: test spec: strategy: type: RollingUpdate ### 滚动更新 rollingUpdate: ### 下面这个设置的含义是:在滚动更新时,新创建的Pod数量最多为副本数量的20%个,杀死的Pod数量最多不能超过2个 maxUnavailable: 2 maxSurge: 20% replicas: 10 selector: matchLabels: pod-name: zaq ### 和模板template里面的pod的标签必须一样 template: metadata: labels: pod-name: zaq spec: containers: - name: nginx-test image: nginx
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
我们前面也提到了Deployment不直接管理Pod,而是间接的通过ReplicaSet来管理Pod。那么ReplicaSet是怎么知道哪些Pod是需要他来进行管理的呢?
答:他通过标签选择器来选择具有特定标签的Pod
由于创建一个Deployment会默认创建一个RS,所以这里直接创建一个Deployment,在这个Deployment中我们可以指定标签选择器。
apiVersion: apps/v1 kind: Deployment metadata: name: test namespace: default labels: test: zaq spec: selector: matchExpressions: ### 注意这里是匹配表达式而不是匹配标签 - key: test ### 该选择器要求该Pod包含名为 test 的标签 operator: In values: - zaq ### 标签的值必须是zaq replicas: 2 template: metadata: labels: test: zaq spec: containers: - name: nginx image: nginx
我们发现,新的标签选择器(matchExpressions)可以采用额外的表达式(这是ReplicationController所不能实现的)。
In:上面key的值必须与下面其中一个指定的values匹配。
NotIn:上面key的值与下面任何指定的values不匹配。
Exists:Pod必须包含一个指定名称的标签(这里指的是必须要有指定名称的key,values值是什么不重要),使用此运算符时,不应指定values字段。
matchExpressions:
- key: test
operator: Exists
DoesNotExist:Pod不得包含指定名称的标签。values属性不得指定。
如果指定了多个表达式,则所有这些表达式都必须为true才能使选择器与Pod匹配。如果同时指定matchLabels和matchExpressions,则所有标签都必须匹配,并且所有表达式必须计算为true以使该Pod与选择器匹配。
蓝绿部署:即系统存在两个版本,一个绿版本(正常),一个蓝版本(等待验证的版本)。如果蓝版本经过反复的测试、修改、验证,确定达到上线标准之后,则可以将流量请求切换到蓝版本,绿版本不接受请求,但是还依然存在。如果蓝版本在生产环境出现了问题,则可以立刻将请求转为绿版本。当确定蓝版本可以稳定正常运行时,就可以将原来的绿版本进行销毁,释放资源,然后蓝版本变为绿版本。(注意,蓝绿版本的转换只是我们人为的这么定义的,而并不是说需要配置什么东西才能实现他们角色的转换。)
金丝雀部署:
金丝雀对瓦斯这种气体十分敏感。空气中哪怕有极其微量的瓦斯,金丝雀也会停止歌唱;而当瓦斯含量超过一定限度时,虽然鲁钝的人类毫无察觉,金丝雀却早已毒发身亡。当时在采矿设备相对简陋的条件下,工人们每次下井都会带上一只金丝雀作为“瓦斯检测指标”,以便在危险状况下紧急撤离。
金丝雀部署的意义在于,同时存在两个版本V1和V2,V1版本和V2版本都能接收到流量请求,但是V2刚开始只能接收到少量的请求,所以这时候V2版本也不需要部署到多台机器上。当到达V2版本的请求都能成功处理了,他不存在任何BUG了,那逐步开始增加V2版本的部署,移除V1版本的部署,让更多的流量请求来到V2版本。直到彻底消除V1版本。
蓝绿部署和金丝雀部署的区别:
蓝绿部署的方式两个版本同时存在,但是流量只会发送到一个版本上,而金丝雀部署的方式,两个版本同时存在,流量请求也会发送到这两个版本上。
注意:金丝雀部署和滚动发布是不一样的。
滚动发布的缺点?
他和金丝雀部署的相同点是,滚动发布也同时存在两个版本,都能接收流量。但是他没法控制流量
滚动发布短时间就直接结束,不能直接控制新老版本的存活时间。
这里涉及到了Service的概念,可以学完Service再来看这里。
准备一个Service
apiVersion: v1
kind: Service
metadata:
name: canary-test
namespace: default
spec:
selector:
app: canary-nginx
type: NodePort
ports:
- name: canary-test
port: 80
targetPort: 80 ### 指Pod的访问端口
protocol: TCP
nodePort: 8888 ### 机器上开的端口,客户端访问的就是这个端口
准备版本v1的Deployment
apiVersion: apps/v1 kind: Deployment metadata: name: canary-dep-v1 namespace: default labels: app: canary-dep-v1 spec: selector: matchLabels: app: canary-nginx version: v1 replicas: 3 template: metadata: labels: app: canary-nginx version: v1 spec: containers: - name: nginx image: registry.cn-hangzhou.aliyuncs.com/lfy_k8s_images/nginx-test:env-msg ### 这个版本的nginx访问页会输出 1111111
准备版本v2的Deployment
apiVersion: apps/v1 kind: Deployment metadata: name: canary-dep-v2 namespace: default labels: app: canary-dep-v2 spec: selector: matchLabels: app: canary-nginx version: v2 replicas: 1 # 先让v2版本只有一个副本 template: metadata: labels: app: canary-nginx version: v2 spec: containers: - name: nginx image: nginx ### v2版本使用默认的nginx,主要是为了打印出默认页面,和v1版本的做区分罢了
kubectl apply -f 上面的资源后,接下来访问:主机ip + 8888,我们可以发现3/4的请求会分配到v1版本。
这时候我们再调大v2版本的副本数。
我们发现v2版本基本上稳定了,没什么问题,这时候就可以删除v1版本:kubectl delete -f canary-dev-v1.yaml
这时候v2版本就 上线成功了!
这些复杂的步骤到时候会放在DevOps流水线中自动完成的
学习此处需要先了解Volume和Service的概念,所以之后回过来再看这里。
Deployment用于在Kubernetes集群中部署特定数量的Pod,而且他还不能保证把Pod部署到特定节点上。如果我们现在需要在集群中的所有节点上都仅部署一个Pod,怎么办?
DaemonSet控制器的作用就是确保所有的(或者一部分)节点都运行了一个指定的Pod副本。
上面提到一部分,是通过pod模板中的nodeSelector属性指定的。
DaemonSet将Pod部署到集群中的所有节点上,除非指定这些Pod只在部分节点上运行。这是通过Pod模板中的nodeSelector属性指定的。
创建一个简单的DaemonSet
apiVersion: apps/v1 kind: DaemonSet metadata: name: ssd-monitor spec: selector: matchLabels: app: ssd-monitor template: metadata: labels: app: ssd-monitor spec: nodeSelector: ### 表示只会在有disk:ssd的节点上部署该Pod disk: ssd containers: - name: main image: luksa/ssd-monitor
kubectl apply -f test-daemonset.yaml
上面DaemonSet描述的意思是:该DaemonSet只会在标有disk:ssd标签的节点上部署一个Pod。
如果我们这时候再给Node2节点打上disk=app标签,那么这时就会在Node2上启动一个Pod。
如果我们这时候将Node3节点上的disk=app标签移除,那么这时Node3节点上的Pod就会被删除。
前面,通过Deployment、StatefulSet、DaemonSet创建的Pod都是一个持续运行的Pod,如果我们遇到一个只想运行完工作后就终止的情况怎么办?
Kubernetes中的 Job 对象将创建一个或多个 Pod,并确保指定数量的 Pod 可以成功执行到进程正常结束:
apiVersion: batch/v1
kind: Job
metadata:
name: pi
spec:
completions: 4 ### 期望Pod成功运行的次数
template:
spec:
containers:
- name: pi
image: perl ### 注意:用于执行job的镜像都必须是非阻塞的,也就是容器启动后执行完会自己退出(如果用了nginx镜像,那么他会在执行第一次时就一直阻塞着)
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
restartPolicy: Never #Job情况下,不支持Always
backoffLimit: 4 #任务4次都没成功就认为Job是失败的
activeDeadlineSeconds: 10
这里需要特别说明的是:在一个Pod的定义中,可以指定在容器中运行的进程结束时,K8s会做什么,这是通过Pod配置的属性restartPolicy完成的,默认是Always。Job的Pod是不能使用这个默认策略的,因为他们并不是要无限期地运行。所以,需要明确指定Job的Pod的restartPolicy为OnFailure或Never。
CronJob 按照预定的时间计划(schedule)创建 Job
一个 CronJob 在时间计划中的每次执行时刻,都创建 大约 一个 Job 对象。这里用到了 大约 ,是因为在少数情况下会创建两个 Job 对象,或者不创建 Job 对象。尽管 K8S 尽最大的可能性避免这种情况的出现,但是并不能完全杜绝此现象的发生。因此,Job 程序必须是幂等的。
当以下两个条件都满足时,Job 将至少运行一次:
startingDeadlineSeconds
被设置为一个较大的值,或者不设置该值(默认值将被采纳)concurrencyPolicy
被设置为 Allow
apiVersion: batch/v1beta1 kind: CronJob metadata: name: hello spec: schedule: "*/1 * * * *" #分、时、日、月、周。他是基于 master 所在时区来进行计算的。 jobTemplate: spec: template: spec: containers: - name: hello image: busybox args: - /bin/sh - -c - date; echo Hello from the Kubernetes cluster restartPolicy: OnFailure
未完,待续>>>
参考:Kubernetes in Action
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。