Pod控制器就是帮助我们自动的调度管理Pod,并满足期望的Pod数量。
Pod控制器是用于实现管理pod的中间层,确保pod资源符合预期的状态,pod的资源出现故障时,会尝试进行重启,当根据重启策略无效,则会重新新建pod的资源。
创建为具体的控制器对象之后,每个控制器均通过API Server
提供的接口持续监控相关资源对象的当前状态,并在因故障、更新或其他原因导致系统状态发生变化时,尝试让资源的当前状态想期望状态迁移和逼近。
Pod
控制器资源通过持续性地监控集群中运行着的Pod
资源对象来确保受其管控的资源严格符合用户期望的状态,例如资源副本的数量要精确符合期望等。通常,一个
Pod
控制器资源至少应该包含三个基本的组成部分:
Pod
资源对象,并据此完成受其管控的Pod
资源计数。Pod
资源的对象数量。Pod
资源对象的Pod
模板资源。自主式Pod
对象由调度器调度到目标工作节点后即由相应节点上的kubelet
负责监控其容器的存活状态,容器主进程崩溃后,kubelet
能够自动重启相应的容器,但对出现非主进程崩溃类的容器错误却无从感知,这便依赖于pod
资源对象定义的存活探测,以便kubelet
能够探知到此类故障。
但若pod
被删除或者工作节点自身发生故障(工作节点上都有kubelet
,kubelet
不可用,因此其健康状态便无法保证),则便需要控制器来处理相应的容器重启和配置。
Pod
控制器由master
的kube-controller-manager
组件提供,常见的此类控制器有以下几种
代用户创建指定数量的pod
副本数量,确保pod
副本数量符合预期状态,并且支持滚动式自动扩容和缩容功能
工作在ReplicaSet
之上,用于管理无状态应用,目前来说最好的控制器。支持滚动更新和回滚功能,还提供声明式配置。
用于确保集群中的每一个节点只运行特定的pod
副本,常用于实现系统级后台任务,比如ELK
服务
ReplicaSet
是取代早期版本中的ReplicationController
控制器,其功能基本上与ReplicationController
相同
ReplicaSet
(简称RS)是Pod
控制器类型的一种实现,用于确保由其管控的Pod
对象副本数在任意时刻都能精确满足期望的数量,ReplicaSet
控制器资源启动后会查找集群中匹配器标签选择器的Pod
资源对象,当前活动对象的数量与期望的数量不吻合时,多则删除,少则通过Pod
模板创建以补足。
ReplicaSet
能够实现以下功能:
确保Pod资源对象的数量精确反映期望值:ReplicaSet
需要确保由其控制运行的Pod副本数量精确吻合配置中定义的期望值,否则就会自动补足所缺或终止所余。
**确保Pod健康运行:**探测到由其管控的Pod
对象因其所在的工作节点故障而不可用时,自动请求由调度器于其他工作节点创建缺失的Pod
副本。
**弹性伸缩:**可通过ReplicaSet
控制器动态扩容或者缩容Pod
资源对象的数量,必要时还可以通过HPA
控制器实现Pod
资源规模的自动伸缩。
spec字段一般嵌套使用以下几个属性字段:
字段值 | 类型 | 描述 |
---|---|---|
replicas | Integer | 指定期望的Pod对象副本数量 |
selector | Object | 当前控制器匹配Pod对象副本的标签选择器,支持matchLabels和matchExpressions两种匹配机制 |
template | Object | 用于定义Pod时的Pod资源信息 |
minReadySeconds | Integer | 用于定义Pod启动后多长时间为可用状态,默认为0秒 |
vi nginx-rs.yml
apiVersion: apps/v1 #api版本定义
kind: ReplicaSet #定义资源类型为ReplicaSet
metadata: #元数据定义
name: nginx-rs
namespace: default
spec: #ReplicaSet的规格定义
replicas: 2 #定义副本数量为2个
selector: #标签选择器,定义匹配Pod的标签
matchLabels:
app: nginx
template: #Pod的模板定义
metadata: #Pod的元数据定义
name: nginx-pod #自定义Pod的名称
labels: #定义Pod的标签,需要和上面的标签选择器内匹配规则中定义的标签一致,可以多出其他标签
app: nginx
spec: #Pod的规格定义
containers: #容器定义
- name: nginx #容器名称
image: nginx:1.12 #容器镜像
imagePullPolicy: IfNotPresent #拉取镜像的规则
ports: #暴露端口
- name: http #端口名称
containerPort: 80
kubectl apply -f nginx-rs.yaml
kubectl get rs
通过查看pod可以看出pod命令是规则是前面是replicaset控制器的名称加随机生成的字符串
kubectl get pods -o wide -w
修改上面创建的
replicaset
示例文件,将镜像nginx:1.12
改为1.20
版本
vi nginx-rs.yml
apiVersion: apps/v1 #api版本定义
kind: ReplicaSet #定义资源类型为ReplicaSet
metadata: #元数据定义
name: nginx-rs
namespace: default
spec: #ReplicaSet的规格定义
replicas: 2 #定义副本数量为2个
selector: #标签选择器,定义匹配Pod的标签
matchLabels:
app: nginx
template: #Pod的模板定义
metadata: #Pod的元数据定义
name: nginx-pod #自定义Pod的名称
labels: #定义Pod的标签,需要和上面的标签选择器内匹配规则中定义的标签一致,可以多出其他标签
app: nginx
spec: #Pod的规格定义
containers: #容器定义
- name: nginx #容器名称
image: nginx:1.20 #容器镜像
imagePullPolicy: IfNotPresent #拉取镜像的规则
ports: #暴露端口
- name: http #端口名称
containerPort: 80
kubectl apply -f nginx-rs.yaml
kubectl get pods -o wide -w
我们发现pod没有任何更新变化
kubectl get pods -o custom-columns=Name:metadata.name,Image:spec.containers[0].image
这里并没有更新pod的nginx版本号
这里虽然重载了,但是已有的pod所使用的镜像仍然是1.12版本的,只是新建pod时才会使用1.20版本,这里测试先手动删除已有的pod。
kubectl delete pods -l app=nginx
kubectl get pods -o custom-columns=Name:metadata.name,Image:spec.containers[0].image
现在我们发现pod的版本已经更新正确了
可以直接通过
vim
编辑清单文件修改replicas
字段,也可以通过kubect edit
命令去编辑
kubectl
还提供了一个专用的子命令scale
用于实现应用规模的伸缩,支持从资源清单文件中获取新的目标副本数量,也可以直接在命令行通过“--replicas”
选项进行读取。
命令扩容一般用于短期的临时性扩容,应付完成后要记得缩容到原来水平
可看到当前是两个节点
kubectl get pods -o wide
使用
scale
命令可以对集群进行扩缩容
kubectl scale replicasets nginx-rs --replicas=4
在更新前打开新窗口,监控pod的更新变化
kubectl get pods -o wide -w
在更新前打开新窗口,监控RS的更新变化
kubectl get rs -o wide -w
我们发现扩容后只是在原来的RS集群上面增加了两个节点
配置文件扩容一般用于初始容量变更,长期进行扩容
可看到当前是四个节点
kubectl get pods -o wide
因为没有变更配置文件可以直接应用配置文件
kubectl apply -f nginx-rs.yml
在更新前打开新窗口,监控pod的更新变化
kubectl get pods -o wide -w
在更新前打开新窗口,监控RS的更新变化
kubectl get rs -o wide -w
我们发现扩容后只是在原来的RS集群上面减少了两个节点
使用
Kubectl delete
命令删除ReplicaSet
对象时默认会一并删除其管控的各Pod
对象,有时,考虑到这些Pod
资源未必由其创建,或者即便由其创建也并非自身的组成部分,这时候可以添加“--cascade=false”
选项,取消级联关系。
kubectl get rs -o wide
kubectl get pods -o wide
删除rs可以通过参数
cascade=false
设置不删除pod
kubectl delete replicasets nginx-rs --cascade=false
kubectl get rs -o wide
kubectl get pods -o wide
kubectl delete pods nginx-rs-7rzz6
kubectl delete pods nginx-rs-c5zrs
Deployment为Pod和Replica Set(下一代Replication Controller)提供声明式更新
只需要在 Deployment 中描述想要的目标状态是什么,Deployment controller 就会帮您将 Pod 和ReplicaSet 的实际状态改变到您的目标状态,也可以定义一个全新的 Deployment 来创建 ReplicaSet 或者删除已有的 Deployment 并创建一个新的来替换。
Deployment
控制器资源的主要职责是为了保证Pod
资源的健康运行,其大部分功能均可通过调用ReplicaSet
实现,同时还增添部分特性。
Deployment
对象升级的详细进度和状态。Deployment
对象的每一个操作都予以保存,以供后续可能执行的回滚操作使用。Recreate
,即重建更新机制,全面停止、删除旧有的Pod
后用新版本替代;另一个是RollingUpdate
,即滚动升级机制,逐步替换旧有的Pod
至新的版本。vi nginx-deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 2
minReadySeconds: 2 # 这里需要估一个比较合理的值,从容器启动到应用正常提供服务
strategy: # k8s 默认的 strategy 就是 RollingUpdate, 这里写明出来可以调节细节参数
#type: Recreate
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # 更新时允许最大激增的容器数,默认 replicas 的 1/4 向上取整
maxUnavailable: 0 # 更新时允许最大 unavailable 容器数,默认 replicas 的 1/4 向下取整
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80
总的来说一个Deploymet控制器可以由两部分组成:
kubectl apply -f nginx-deployment.yml
kubectl get pods -o wide
ReplicaSet是一个副本控制器,ReplicaSet可以用selector来控制Pod的数量,而Deployments是一个更高层次的概念,它管理ReplicaSets,并提供对pod的声明性更新以及许多其他的功能。
kubectl get replicaset -o wide
通过查看资源对象可以看出,Deployment会自动创建相关的ReplicaSet控制器资源,并以"[DEPLOYMENT-name]-[POD-TEMPLATE-HASH-VALUE]"格式为其命名,其中的hash值由Deployment自动生成。而Pod名则是以ReplicaSet控制器的名称为前缀,后跟5位随机字符。
ReplicaSet控制器的应用更新需要手动分成多步并以特定的次序进行,过程繁杂且容易出错,而Deployment却只需要由用户指定在Pod模板中要改动的内容,(如镜像文件的版本),余下的步骤便会由其自动完成。Pod副本数量也是一样。
Deployment控制器支持两种更新策略:滚动更新(rolling updata)和 重建更新(recreate),默认情况下为滚动更新
重建更新为:先删除所有的Pod再根据新的模板创建新的Pod,中间会导致服务的不可用,用户要么使用的是新版本,要么就是旧版本
滚动更新是默认的更新策略,它在删除一些旧版本的Pod的同时补充创建一些新的Pod,更新期间服务不会中断。
滚动更新期间,应用升级期间还要确保可用的Pod对象数量不低于某些阈值,确保可以持续处理客户端请求,变动的方式和Pod对象的数量范围将通过maxSurge
和maxunavailable
两个属性协同进行定义
两个参数用法如下:
maxSurge和maxUnavailable的数量不能同时为0,否则Pod对象的复本数量在符合用户期望的数量后无法做出合理变动以进行滚动更新操作。
命令扩容一般用于短期的临时性扩容,应付完成后要记得缩容到原来水平
通过命令查看pod的版本号
kubectl get pods -o custom-columns=Name:metadata.name,Image:spec.containers[0].image
kubectl set image deployment/nginx-deployment nginx=nginx:1.15
在更新前打开新窗口,监控pod的更新变化
kubectl get pods -o wide -w
在更新前打开新窗口,监控RS的更新变化
kubectl get rs -o wide -w
我们发现更新后新建了一个RS,并且保留原来的RS但是节点数为0用来回滚
通过命令查看pod的版本号
kubectl get pods -o custom-columns=Name:metadata.name,Image:spec.containers[0].image
通过命令查看pod的版本号
kubectl get pods -o custom-columns=Name:metadata.name,Image:spec.containers[0].image
vi nginx-deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.20 # 将nginx版本改为1.20
ports:
- containerPort: 80
kubectl apply -f nginx-deployment.yml
在更新前打开新窗口,监控pod的更新变化
kubectl get pods -o wide -w
在更新前打开新窗口,监控RS的更新变化
kubectl get rs -o wide -w
我们发现更新后新建了一个RS,并且保留原来的RS但是节点数为0用来回滚
通过命令查看pod的版本号
kubectl get pods -o custom-columns=Name:metadata.name,Image:spec.containers[0].image
通过
rollout
命令进行回滚操作
通过命令查看pod的版本号
kubectl get pods -o custom-columns=Name:metadata.name,Image:spec.containers[0].image
kubectl rollout undo deployment/nginx-deployment
在更新前打开新窗口,监控pod的更新变化
kubectl get pods -o wide -w
在更新前打开新窗口,监控RS的更新变化
kubectl get rs -o wide -w
我们发现回滚没有创建新的rs而是将使用了原来的rs
通过命令查看pod的版本号
kubectl get pods -o custom-columns=Name:metadata.name,Image:spec.containers[0].image
通过命令查看pod的版本号
kubectl get pods -o custom-columns=Name:metadata.name,Image:spec.containers[0].image
kubectl rollout undo deployment/nginx-deployment
在更新前打开新窗口,监控pod的更新变化
kubectl get pods -o wide -w
在更新前打开新窗口,监控RS的更新变化
kubectl get rs -o wide -w
我们发现回滚没有创建新的rs而是将使用了原来的rs
通过命令查看pod的版本号
kubectl get pods -o custom-columns=Name:metadata.name,Image:spec.containers[0].image
我们发现
rollout
回滚只在最近的两个版本之间来回回滚,不会回滚到在上一个版本。
通过
rollout history
查看版本历史
kubectl rollout history deployment/nginx-deployment
通过指定版本号来查看变更内容,找到需要回滚的版本,这里我会回滚到最早版本
nginx:1.7.9
kubectl rollout history deployment/nginx-deployment --revision=1
我们找到了需要回滚的版本是
1
通过命令查看pod的版本号
kubectl get pods -o custom-columns=Name:metadata.name,Image:spec.containers[0].image
写入我们需要回滚到的指定版本
1
kubectl rollout undo deployment/nginx-deployment --to-revision=1
通过命令查看pod的版本号
kubectl get pods -o custom-columns=Name:metadata.name,Image:spec.containers[0].image
到此我们已经回滚到了指定版本
命令扩容一般用于短期的临时性扩容,应付完成后要记得缩容到原来水平
当前是两个节点
kubectl get pods -o wide
使用
scale
命令可以对集群进行扩缩容,扩充到4个节点
kubectl scale deployment nginx-deployment --replicas=4
在更新前打开新窗口,监控pod的更新变化
kubectl get pods -o wide -w
在更新前打开新窗口,监控RS的更新变化
kubectl get rs -o wide -w
我们发现扩容后只是在原来的RS集群上面增加了两个节点
配置文件扩缩容一般用于初始容量变更,长期进行扩缩容
当前是4个节点
kubectl get pods -o wide
因为我们没有更改配置文件,直接应用配置文件即可
kubectl apply -f nginx-deployment.yml
在更新前打开新窗口,监控pod的更新变化
kubectl get pods -o wide -w
在更新前打开新窗口,监控RS的更新变化
kubectl get rs -o wide -w
我们发现扩容后只是在原来的RS集群上面减少了两个节点
kubectl get deployments -o wide
kubectl get pods -o wide
执行删除命令删除Deployment
kubectl delete deployment nginx-deployment
kubectl get deployments -o wide
kubectl get pods -o wide
Pod
本身具有生命周期,这就带了一系列的问题,
kubelet
会重启这个容器,但是文件会丢失-这个容器会是一个全新的状态;Pod
中运行的时候,很多时候需要数据文件的共享。Docker
支持配置容器使用存储卷将数据持久存储于容器自身文件系统之外的存储空间之中,它们可以是节点文件系统或网络文件系统之上的存储空间。相应的,kubernetes
也支持类似的存储卷功能,不过,其存储卷是与Pod
资源绑定而非容器。
简单来说,存储卷是定义在Pod
资源之上、可被其内部的所有容器挂载的共享目录,它关联至某外部的存储设备之上的存储空间,从而独立于容器自身的文件系统,而数据是否具有持久能力取决于存储卷自身是否支持持久机制。Pod
、容器与存储卷的关系图如下。
Kubernetes
支持非常丰富的存储卷类型,包括本地存储(节点)和网络存储系统中的诸多存储机制,还支持Secret
和ConfigMap
这样的特殊存储资源。
例如,关联节点本地的存储目录与关联GlusterFS
存储系统所需要的配置参数差异巨大,因此指定存储卷类型时也就限定了其关联到的后端存储设备。通过命令# kubectl explain pod.spec
可以查看当前kubernetes
版本支持的存储卷类型。常用类型如下:
SAN:iscsi
NFS:nfs、cfs
emptyDir
存储卷是Pod
对象生命周期中的一个临时目录,类似于Docker
上的“docker 挂载卷”
,在Pod
对象启动时即被创建,而在Pod
对象被移除时会被一并删除(永久删除)。
当pod的存储方案设定为emptydir的时候,pod启动时,就会在pod所在节点的磁盘空间开辟出一块空卷,最开始里面是什么都没有的,pod启动后容器产生的数据会存放到那个空卷中,空卷变成了一个临时卷供pod内的容器读取和写入数据,一旦pod容器消失,节点上开辟出的这个临时卷就会随着pod的销毁而销毁
注意:一个容器崩溃了不会导致数据的丢失,因为容器的崩溃并不移除Pod
。
一般来说
emptydir
的用途都是用来充当临时存储空间,例如一些不需要数据持久化的微服务,我们都可以用emptydir来当做微服务pod的存储方案
这里定义了一个deploy
资源对象(vol-emptydir-deploy
),在其内部定义了两个容器,其中一个容器是辅助容器sidecar
,每隔10秒生成一行信息追加到index.html
文件中;
另一个是nginx
容器,将存储卷挂载到站点家目录。然后访问nginx
的html
页面验证两个容器之间挂载的emptyDir
实现共享。
vi vol-emptydir-deploy.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: vol-emptydir-deploy
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
volumes: #定义存储卷
- name: html #定义存储卷的名称
emptyDir: {} #定义存储卷的类型
containers:
- name: nginx
image: nginx:1.12
ports:
- containerPort: 80
volumeMounts: #在容器中定义挂载存储卷的名和路径
- name: html
mountPath: /usr/share/nginx/html
- name: sidecar
image: alpine
volumeMounts: #在容器中定义挂载存储卷的名和路径
- name: html
mountPath: /html
command: ["/bin/sh", "-c"]
args:
- while true; do
echo $(hostname) $(date) >> /html/index.html;
sleep 10;
done
kubectl apply -f vol-emptydir-deploy.yml
kubectl get pods -o wide
curl 10.244.1.16
我们发现
nginx
可以共享来自sidecar
的数据
登录
sidecar
,并查看目录文件
kubectl exec vol-emptydir-deploy-86cd768757-5mtzx -c sidecar -it -- /bin/sh
在改容器中写入文件
echo "hello world" > hello
再打开一个窗口登录
nginx
,查看hello文件是否存在
kubectl exec vol-emptydir-deploy-86cd768757-5mtzx -c nginx -it -- /bin/sh
删除
hello
文件
rm -rf hello
在
sidecar
中查看文件时已经删除了
登录
sidecar
并在html中写入一段文件
kubectl exec vol-emptydir-deploy-86cd768757-5mtzx -c sidecar -it -- /bin/sh
echo "This is a text to test whether the pod is persistent" > /html/index.html
curl 10.244.1.16
手动删除容器模拟容器销毁,用于是pod是被控制器管理的,删除后会被重建新的pod
kubectl delete pod vol-emptydir-deploy-86cd768757-5mtzx
我们发现销毁pod后新建的pod转移到node2节点了,并且pod名称也改变了
curl 10.244.2.22
这时候在看我们之前写入的文字不见了
hostPath
类型的存储卷是指将工作节点上的某文件系统的目录或文件挂载于Pod
中的一种存储卷,独立于Pod
资源的生命周期,具有持久性,在Pod
删除时,数据不会丢失。
hostPath类型则是映射node文件系统中的文件或者目录到pod里,在使用hostPath类型的存储卷时,也可以设置type字段,支持的类型有文件、目录、File、Socket、CharDevice和BlockDevice。
其实这个功能就相当于docker中的-v 目录映射,只不过在k8s中的时候,pod会漂移,当pod漂移到其他node节点的时候,pod不会跨节点的去读取目录。所以说hostpath只能算一种半持久化的存储方式
这里定义了一个deploy
资源对象(vol-hostpath-deploy
),在其内部定义了一个容器,将nginx的html文件映射到外部,我们外面给html写入文件,访问nginx来验证是否能够访问。
vi vol-hostpath-deploy.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: vol-hostpath-deploy
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
volumes: #定义存储卷
- name: html # 定义存储名称
hostPath: # 定义存储类型
path: /tmp/k8s/data/volumn1 # 宿主机存储路径
type: DirectoryOrCreate # 不存在路径创建路径
containers:
- name: nginx
image: nginx:1.12
ports:
- containerPort: 80
volumeMounts: #在容器中定义挂载存储卷的名和路径
- name: html
mountPath: /usr/share/nginx/html
kubectl apply -f vol-hostpath-deploy.yml
kubectl get pods -o wide
查看pod发现pod在node1节点,登录node1节点写入文件
cd /tmp/k8s/data/volumn1/
echo "This is a text to test whether the pod is persistent" > index.html
curl 10.244.1.17
kubectl exec vol-emptydir-deploy-86cd768757-5mtzx -c nginx -it -- /bin/sh
cd /usr/share/nginx/html
cat index.html
echo "hello hostpath" > hello
登录pod所在的节点的宿主机目录查看文件是否存在
cat hello
发现写入的文件是存在的
手动删除容器模拟容器销毁,用于是pod是被控制器管理的,删除后会被重建新的pod
kubectl delete pod vol-hostpath-deploy-595699bdb4-4m8s4
我们知道hostpath是在宿主机创建文件的,现在我们发现pod漂移到了node2节点,我们尝试访问,并登录容器查看
curl 10.244.2.23
发现文件不存在符合我们的预期
因为上一次删除漂移到了node2,我们再次删除让其漂移到node1
kubectl delete pod vol-hostpath-deploy-595699bdb4-gd8sw
curl 10.244.1.18
可以发现容器被删除后,新建的pod也可以看到我们映射的目录,我们写入的文件是存在的
这有个缺点就是不能够跨容器去读取数据,如果删除后的pod被调度到其他节点的话,原来的数据也就没有了,如果能不受节点的影响,并且挂载的数据不会随生命周期的结束而结束,我们应该怎么解决呢?我们可以采用NFS的方式进行存储
一般而言pod的调度都是通过RC、Deployment等控制器自动完成,但是仍可以通过手动配置的方式进行调度,目的就是让pod的调度符合我们的预期。
Pod.spec.nodeName
用于强制约束将Pod调度到指定的Node节点上,这里说是“调度”,但其实指定了nodeName的Pod会直接跳过Scheduler的调度逻辑,直接写入PodList列表,该匹配规则是强制匹配。
vi pod-node-dispatch.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
nodeName: node1 #指定调度节点为node1
containers:
- name: nginx
image: nginx:1.20
ports:
- containerPort: 80
kubectl apply -f pod-node-selector-dispatch.yml
kubectl get pods -o wide
创建部署后,我们发现节点在node1的节点上
删除pod应用,pod控制器会重建pod
kubectl delete pod nginx-deployment-5dc7769598-n25q5
我们发现节点删除后,重新的应用还是落在了node1节点上,说明我们的节点调度生效的
定向调度是把pod调度到具有特定标签的node节点的一种调度方式,比如把MySQL数据库调度到具有SSD的node节点以优化数据库性能。此时需要首先给指定的node打上标签,并在pod中设置nodeSelector属性以完成pod的指定调度。
给指定的node打上标签
给node2添加上
disk=ssd
的标签
kubectl label nodes node2 disk=ssd
显示node2的所有标签
kubectl label node node2 --list=true
我们发现我们的标签已经添加上去了
vi pod-node-selector-dispatch.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
nodeSelector:
disk: ssd # 节点调度到
containers:
- name: nginx
image: nginx:1.20
ports:
- containerPort: 80
kubectl apply -f pod-node-selector-dispatch.yml
kubectl get pods -o wide
创建部署后,我们发现两个pod都在node2的节点上
删除pod应用,pod控制器会重建pod
kubectl delete pod nginx-deployment-5dc7769598-n25q5
我们发现节点删除后,重新的应用还是落在了node2节点上,说明我们的定向调度生效的
注意:定向调度可以把pod调度到特定的node节点,但随之而来的缺点就是如果集群中不存在响应的node,即使有基本满足条件的node节点,pod也不会被调度
Kubernetes service 定义了这样一种抽象:一个 Pod 的逻辑分组,一种可以访问它们的策略 —— 通常称为微服务,这一组 pod 能够被 Service 访问到,通常是通过 Label selector
Service能够提供负载均衡的能力,但是在使用上有以下限制:
Kubernetes Service 从逻辑上代理了一组 Pod,具体是哪些 Pod 则是由 label 来挑选,Service 有自己 IP,而且这个 IP 是不变的。
当Pod宕机后重新生成时,其IP等状态信息可能会变动,Service会根据Pod的Label对这些状态信息进行监控和变更,保证上游服务不受Pod的变动而影响。
Kubernetes Pods 是有生命周期的,他们可以被创建,而且销毁不会再启动, 如果您使用Deployment来运行您的应用程序,则它可以动态创建和销毁 Pod。
一个Kubernetes的Service
是一种抽象,它定义了一组Pods
的逻辑集合和一个用于访问它们的策略 - 有的时候被称之为微服务,一个Service
的目标Pod
集合通常是由Label Selector
来决定的。
如下图所示,当Nginx Pod
作为客户端访问Tomcat Pod
中的应用时,IP
的变动或应用规模的缩减会导致客户端访问错误。而Pod
规模的扩容又会使得客户端无法有效的使用新增的Pod
对象,从而影响达成规模扩展之目的。为此,Kubernetes
特地设计了Service
资源来解决此类问题。
Service
资源基于标签选择器将一组Pod
定义成一个逻辑组合,并通过自己的IP
地址和端口调度代理请求至组内的Pod
对象之上,如下图所示,它向客户端隐藏了真实的、处理用户请求的Pod
资源,使得客户端的请求看上去就像是由Service
直接处理并响应一样。
Service
对象的IP
地址也称为Cluster IP
,它位于Kubernetes
集群配置指定专用IP
地址的范围之内,是一种虚拟IP
地址,它在Service
对象创建后既保持不变,并且能够被同一集群中的Pod
资源所访问。Service
端口用于接收客户端请求并将其转发至其后端的Pod
中的相应端口之上,因此,这种代理机构也称为“端口代理”(port proxy
)或四层代理,工作于TCP/IP
协议栈的传输层。
Service
资源会通过API Server
持续监视着(watch
)标签选择器匹配到的后端Pod
对象,并实时跟踪各对象的变动,例如,IP
地址变动、对象增加或减少等。Service
并不直接链接至Pod
对象,它们之间还有一个中间层——Endpoints
资源对象,它是一个由IP
地址和端口组成的列表,这些IP
地址和端口则来自由Service
的标签选择器匹配到的Pod
资源。当创建service
对象时,其关联的Endpoints
对象会自动创建。
Service 在 K8s 中有以下四种类型
vi nginx-deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 1 # 只有一个副本
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
volumes: #定义存储卷
- name: html # 定义存储名称
hostPath: # 定义存储类型
path: /tmp/k8s/data/volumn1 # 宿主机存储路径
type: DirectoryOrCreate # 不存在路径创建路径
containers:
- name: nginx
image: nginx:1.20
volumeMounts: #在容器中定义挂载存储卷的名和路径
- name: html
mountPath: /usr/share/nginx/html
ports:
- containerPort: 80
kubectl apply -f nginx-deployment.yml
kubectl get pods -o wide
curl 10.244.2.54
类型为ClusterIP的service,这个service有一个Cluster-IP,其实就一个VIP,具体实现原理依靠kubeproxy组件,通过iptables或是ipvs实现。
注意:这种类型的service 只能在集群内访问
vi cluster-iP-service.yml
apiVersion: v1
kind: Service
metadata:
name: nginx-service
namespace: default
labels:
app: nginx-service
spec:
type: ClusterIP
ports:
- port: 8000 # Service 暴漏端口
targetPort: 80 # 代理的对象的端口
selector:
app: nginx # 绑定标签是nginx的对象
kubectl apply -f cluster-iP-service.yml
kubectl get service -o wide
curl 10.1.206.66:8000
删除pod让Pod的节点漂移再次访问Service节点
kubectl delete pod nginx-deployment-6897679c4b-2hqmk
我们发现虽然pod的IP以及节点变化了但是
Service
访问IP没有任何变化
curl 10.1.206.66:8000
当我们需要集群外业务访问,那么ClusterIP就满足不了了,NodePort当然是其中的一种实现方案
vi node-port-service.yml
apiVersion: v1
kind: Service
metadata:
name: nginx-node-port-service
namespace: default
labels:
app: nginx-service
spec:
type: NodePort
ports:
- port: 8000 # Service 暴漏端口
targetPort: 80 # 代理的对象的端口
selector:
app: nginx # 绑定标签是nginx的对象
kubectl apply -f node-port-service.yml
kubectl get service -o wide
curl 10.1.137.200:8000
删除pod让Pod的节点漂移再次访问Service节点
kubectl delete pod nginx-deployment-6897679c4b-2hqmk
我们发现虽然pod的IP以及节点变化了但是
Service
访问IP没有任何变化
curl 10.1.137.200:8000
NodePort的最主要功能是进行外网访问,我们是不能通过访问
8000
端口访问的,他是内网端口,外网访问需要访问映射出来的端口8000:31337/TCP
这里映射出来的nodeport
是31337
,还可以通过指定nodePort
来指定端口,要求端口必须大于30000
curl 192.168.64.160:31337
通过浏览器访问
k8s 中
port
,nodePort
,targetPort
有什么区别呢
k8s集群内部服务之间访问service的入口,即clusterIP:port是service暴露在clusterIP上的端口
主要作用是集群内其他pod访问本pod的时候,需要的一个port,如nginx的pod访问mysql的pod,那么mysql的pod的service可以如下定义,由此可以这样理解,port是service的port,nginx访问service的33306
apiVersion: v1
kind: Service
metadata:
name: mysql-service
spec:
ports:
- port: 33306
targetPort: 3306
selector:
name: mysql-pod
容器的端口(最终的流量端口)。targetPort是pod上的端口,从port和nodePort上来的流量,经过kube-proxy流入到后端pod的targetPort上,最后进入容器。
看上面的targetport,targetport说过是pod暴露出来的port端口,当nginx的一个请求到达service的33306端口时,service就会将此请求根据selector中的name,将请求转发到mysql-pod这个pod的3306端口上
nodeport就很好理解了,它是集群外的客户访问,集群内的服务时,所访问的port,比如客户访问下面的集群中的nginx,就是这样的方式,ip:30001
外部流量访问k8s集群中service入口的一种方式(另一种方式是LoadBalancer),即nodeIP:nodePort是提供给外部流量访问k8s集群中service的入口
比如外部用户要访问k8s集群中的一个Web应用,那么我们可以配置对应service的type=NodePort,nodePort=30001。其他用户就可以通过浏览器http://node:30001访问到该web服务。而数据库等服务可能不需要被外界访问,只需被内部服务访问即可,那么我们就不必设置service的NodePort。
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
type: NodePort # 有配置NodePort,外部流量可访问k8s中的服务
ports:
- port: 30080 # 服务访问端口
targetPort: 80 # 容器端口
nodePort: 30001 # NodePort
selector: name: nginx-pod
nodeport是集群外流量访问集群内服务的端口类型,比如客户访问nginx,apache,port是集群内的pod互相通信用的端口类型,比如nginx访问mysql,而mysql是不需要让客户访问到的,最后targetport,顾名思义,目标端口,也就是最终端口,也就是pod的端口。