概览
K8S官网文档:https://kubernetes.io/zh/docs/home/
K8S 是Kubernetes的全称,源于希腊语,意为“舵手”或“飞行员”,官方称其是:用于自动部署、扩展和管理“容器化(containerized)应用程序”的开源系统。翻译成大白话就是:“K8S 是负责自动化运维管理多个跨机器 Docker 程序的集群”。
K8S核心特性
- 服务发现与负载均衡:无需修改你的应用程序即可使用陌生的服务发现机制。
- 存储编排:自动挂载所选存储系统,包括本地存储。
- Secret和配置管理:部署更新Secrets和应用程序的配置时不必重新构建容器镜像,且不必将软件堆栈配置中的秘密信息暴露出来。
- 批量执行:除了服务之外,Kubernetes还可以管理你的批处理和CI工作负载,在期望时替换掉失效的容器。
- 水平扩缩:使用一个简单的命令、一个UI或基于CPU使用情况自动对应用程序进行扩缩。
- 自动化上线和回滚:Kubernetes会分步骤地将针对应用或其配置的更改上线,同时监视应用程序运行状况以确保你不会同时终止所有实例。
- 自动装箱:根据资源需求和其他约束自动放置容器,同时避免影响可用性。
- 自我修复:重新启动失败的容器,在节点死亡时替换并重新调度容器,杀死不响应用户定义的健康检查的容器。
K8S 快速实战
kubectl命令使用
kubectl是apiserver的客户端工具,工作在命令行下,能够连接apiserver实现各种增删改查等操作
kubectl官方使用文档:https://kubernetes.io/zh/docs/reference/kubectl/overview/
Namespace
K8s 中,命名空间(Namespace) 提供一种机制,将同一集群中的资源划分为相互隔离的组。同一命名空间内的资源名称要唯一,命名空间是用来隔离资源的,不隔离网络。
Kubernetes 启动时会创建四个初始命名空间:
- default
Kubernetes 包含这个命名空间,以便于你无需创建新的命名空间即可开始使用新集群。 - kube-node-lease
该命名空间包含用于与各个节点关联的 Lease(租约)对象。 节点租约允许 kubelet 发送心跳, 由此控制面能够检测到节点故障。 - kube-public
所有的客户端(包括未经身份验证的客户端)都可以读取该命名空间。 该命名空间主要预留为集群使用,以便某些资源需要在整个集群中可见可读。 该命名空间的公共属性只是一种约定而非要求。 - kube-system
该命名空间用于 Kubernetes 系统创建的对象。
# 查看namespace、
kubectl get namespace
#查看kube-system下的pod
kubectl get pods -n kube-system
#查看所有namespace下的pod
kubectl get pods -A
创建Namesapce示例
- 命令行方式
可以使用下面的命令创建Namespace:
kubectl create namespace yuyang
- yaml方式
新建一个名为 my-namespace.yaml 的 YAML 文件,并写入下列内容:
apiVersion: v1
kind: Namespace
metadata:
name: yuyang
然后运行:
kubectl apply -f my-namespace.yaml
删除namesapce
kubectl delete namespace yuyang
kubectl delete -f my-namespace.yaml
Pod
Pod 是可以在 Kubernetes 中创建和管理的、最小的可部署的计算单元。Pod(就像在鲸鱼荚或者豌豆荚中)是一组(一个或多个)容器; 这些容器共享存储、网络、以及怎样运行这些容器的声明。
创建Pod示例:运行一个NGINX容器
- 命令行方式
# 创建pod
kubectl run mynginx --image=nginx:1.14.2
#在指定命名空间创建pod
kubectl run mynginx --image=nginx:1.14.2 -n <namespace>
kubectl run mynginx --image=nginx:1.14.2 -n yuyang #例子
# 获取pod的信息,-owide 表示更详细的显示信息 -n 命名空间 查询对应namespace下的pod
kubectl get pod
kubectl get pod -owide
kubectl get pod -owide -n <namespace-name>
#查看pod的详情
kubectl describe pod <pod-name>
kubectl describe pod <pod-name> -n <namespace-name>
# 查看Pod的运行日志
kubectl logs <pod-name>
# 删除pod
kubectl delete pod <pod-name>
kubectl delete pod <pod-name> -n <namespace>
kubectl describe pod
- yaml方式
#vim nginx-pod.yaml
apiVersion: v1 #版本
kind: Pod #类型
metadata: #以上是默认格式
labels:
run: mynginx #标签别名
name: mynginx #pod名字
spec:
containers:
- name: nginx
image: nginx:1.14.2 #无版本号则拉取最新的
ports:
- containerPort: 80
然后运行:
kubectl apply -f nginx-pod.yaml
删除pod
kubectl delete -f nginx-pod.yaml
一个pod运行多个容器
apiVersion: v1
kind: Pod
metadata:
labels:
run: myapp
name: myapp
spec:
containers:
- image: nginx:1.14.2
name: nginx
- image: tomcat:9.0.55
Deployment
Deployment负责创建和更新应用程序的实例,使Pod拥有多副本,自愈,扩缩容等能力。创建Deployment后,Kubernetes Master 将应用程序实例调度到集群中的各个节点上。如果托管实例的节点关闭或被删除,Deployment控制器会将该实例替换为群集中另一个节点上的实例。这提供了一种自我修复机制来解决机器故障维护问题。
创建一个tomcat应用程序
使用kubectl create deployment 命令创建一个应用部署deployment 与 pod
#my-tomcat表示pod的名称 --image表示镜像的地址
kubectl create deployment my-tomcat --image=tomcat:9.0.55
#查看一下deployment的信息
kubectl get deployment
#删除deployment
kubectl delete deployment my-tomcat
#查看Pod打印的日志
kubectl logs my-tomcat-6d6b57c8c8-n5gm4
#使用 exec 可以在Pod的容器中执行命令
kubectl exec -it my-tomcat-6d6b57c8c8-n5gm4 - env #使用 env 命令查看环境变量
kubectl exec -it my-tomcat-6d6b57c8c8-n5gm4 - ls / # 查看容器的根目录下面内容
kubectl exec -it my-tomcat-6d6b57c8c8-n5gm4 - bash #进入Pod容器内部并执行bash命令,如果想退出容器可以使用exit命令
kubectl exec -it my-tomcat-6d6b57c8c8-n5gm4 - sh
kubectl exec -it my-tomcat-6d6b57c8c8-n5gm4 /bin/bash
kubectl create my-tomcat --image=tomcat:9.0.55 #创建pod
kubectl create deployment my-tomcat --image=tomcat:9.0.55 #创建deployment
自愈
#查看pod信息,-w意思是一直等待观察pod信息的变动
kubectl get pod -w
开另外一个命令窗口执行如下命令,同时观察之前命令窗口的变化情况
kubectl delete pod my-tomcat-67975b6b59-4np5j
可以看到之前的tomcat的pod被销毁了,但是又重新启动一个新的tomcat pod ,这是k8s的服务自愈功能,不需要运维人员干预
多副本
- 命令行的方式
# 创建3个副本
kubectl create deployment my-tomcat --image=tomcat:9.0.55 --replicas=3
- yaml方式
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: my-tomcat
name: my-tomcat
spec:
replicas: 3
selector:
matchLabels:
app: my-tomcat
template:
metadata:
labels:
app: my-tomcat
spec:
containers:
- image: tomcat:9.0.55
name: tomcat
扩缩容
# 扩容到5个pod
kubectl scale --replicas=5 deployment my-tomcat
# 缩到3个pod
kubectl scale --replicas=3 deployment my-tomcat
扩容到5个pod
缩容到3个pod
滚动升级与回滚
对my-tomcat这个deployment进行滚动升级和回滚,将tomcat版本由tomcat:9.0.55升级到tomcat:10.1.11,再回滚到tomcat:9.0.55
滚动升级:
kubectl set image deployment my-tomcat tomcat=tomcat:10.1.11 --record
可以执行 kubectl get pod -w 观察pod的变动情况,可以看到有的pod在销毁,有的pod在创建
查看pod信息
kubectl get pod
查看某个pod的详细信息,发现pod里的镜像版本已经升级了
kubectl describe pod my-tomcat-85c5c8f685-lnkfm
版本回滚:
查看历史版本
kubectl rollout history deploy my-tomcat
回滚到上一个版本
kubectl rollout undo deployment my-tomcat #--to-revision 参数可以指定回退的版本
#回滚(回到指定版本)
kubectl rollout undo deployment/my-dep --to-revision=2
访问tomcat pod
集群内访问(在集群里任一worker节点都可以访问)
curl 172.20.125.82:8080
集群外部访问
当在集群之外访问是发现无法访问,那么集群之外的客户端如何才能访问呢?这就需要service服务了
Service
Service是一个抽象层,它定义了一组Pod的逻辑集,并为这些Pod支持外部流量暴露、负载均衡和服务发现。
尽管每个Pod 都有一个唯一的IP地址,但是如果没有Service,这些IP不会暴露在群集外部。Service允许您的应用程序接收流量。Service也可以用在ServiceSpec标记type的方式暴露,type类型如下:
- ClusterIP(默认):在集群的内部IP上公开Service。这种类型使得Service只能从集群内访问。
- NodePort:使用NAT在集群中每个选定Node的相同端口上公开Service。使用 : 从集群外部访问Service。是ClusterIP的超集。
- LoadBalancer:在当前云中创建一个外部负载均衡器(如果支持的话),并为Service分配一个固定的外部IP。是NodePort的超集。
- ExternalName:通过返回带有该名称的CNAME记录,使用任意名称(由spec中的externalName指定)公开Service。不使用代理。
创建Service示例
命令行的方式
kubectl expose deployment my-tomcat --name=tomcat --port=8080 --type=NodePort
#查看service信息,port信息里冒号后面的端口号就是对集群外暴露的访问接口
# NodePort范围在 30000-32767 之间
kubectl get svc -o wide
kubectl delete svc <service-name> -n <namespace>
集群外部访问
使用集群节点的ip加上暴露的端口就可以访问
tomcat版本太高返回404的解决办法:进入tomcat容器,把 webapps 目录删除,再把 webapps.dist 重命名为 webapps 即可。
- yaml的方式
# vim mytomcat-service.yaml
apiVersion: v1
kind: Service
metadata:
labels:
app: my-tomcat
name: my-tomcat
spec:
ports:
- port: 8080 # service的虚拟ip对应的端口,在集群内网机器可以访问用service的虚拟ip加该端口号访问服务
nodePort: 30001 # service在宿主机上映射的外网访问端口,端口范围必须在30000-32767之间
protocol: TCP
targetPort: 8080 # pod暴露的端口,一般与pod内部容器暴露的端口一致
selector:
app: my-tomcat
type: NodePort
执行如下命令创建service:
kubectl apply -f mytomcat-service.yaml
集群外部访问
存储
Volume
Volume指的是存储卷,包含可被Pod中容器访问的数据目录。容器中的文件在磁盘上是临时存放的,当容器崩溃时文件会丢失,同时无法在多个Pod中共享文件,通过使用存储卷可以解决这两个问题。
Kubernetes支持很多类型的卷。Pod可以同时使用任意数目的卷类型。临时卷类型的生命周期与Pod相同,但持久卷可以比 Pod 的存活期长。当Pod不再存在时,Kubernetes也会销毁临时卷;不过 Kubernetes不会销毁持久卷。对于给定Pod 中任何类型的卷,在容器重启期间数据都不会丢失。
卷的核心是一个目录,其中可能存有数据,Pod中的容器可以访问该目录中的数据。所采用的不同卷的类型将决定该目录如何形成的、使用何种介质保存数据以及目录中存放的内容。常用的卷类型有configMap、emptyDir、local、nfs、secret等。
-
ConfigMap:可以将配置文件以键值对的形式保存到ConfigMap 中,并且可以在Pod 中以文件或环境变量的形式使用。ConfigMap可以用来存储不敏感的配置信息,如应用程序的配置文件。
-
EmptyDir:是一个空目录,可以在Pod中用来存储临时数据,当Pod被删除时,该目录也会被删除。
-
Local:将本地文件系统的目录或文件映射到Pod 中的一个Volume中,可以用来在Pod中共享文件或数据。
-
NFS:将网络上的一个或多个NFS共享目录挂载到Pod中的Volume中,可以用来在多个Pod之间共享数据。
-
Secret:将敏感信息以密文的形式保存到Secret中,并且可以在Pod中以文件或环境变量的形式使用。Secret可以用来存储敏感信息,如用户名密码、证书等。
使用方式
使用卷时,在.spec.volumes 字段中设置为Pod提供的卷,并在.spec.containers[*].volumeMounts 字段中声明卷在容器中的挂载位置。容器中的进程看到的文件系统视图是由它们的容器镜像的初始内容以及挂载在容器中的卷(如果定义了的话)所组成的。其中根文件系统同容器镜像的内容相吻合。任何在该文件系统下的写入操作,如果被允许的话,都会影响接下来容器中进程访问文件系统时所看到的内容。
apiVersion: v1
kind: Pod
metadata:
name: configmap-pod
spec:
containers:
- name: test
image: busybox:1.28
volumeMounts:
..........
volumes:
............
搭建nfs文件系统
nfs(network filesystem ):网络文件存储系统
安装nfs-server
# 在每个机器。
yum install -y nfs-utils
# 在master 执行以下命令
echo "/nfs/data/ *(insecure,rw,sync,no_root_squash)" > /etc/exports
# 执行以下命令,启动 nfs 服务;创建共享目录
mkdir -p /nfs/data
# 在master执行
systemctl enable rpcbind
systemctl enable nfs-server
systemctl start rpcbind
systemctl start nfs-server
# 使配置生效
exportfs -r
#检查配置是否生效
exportfs
配置nfs-client
# nfs-server节点的ip
showmount -e 192.168.11.101
mkdir -p /nfs/data
mount -t nfs 192.168.11.101:/nfs/data /nfs/data #192.168.11.101 是主节点是ip地址
nfs方式数据挂载
相对于emptyDir和hostPath,nfs这种 Volume类型的最大特点就是不依赖Kuberees Volume的底层基础设施由独立的存储系统管理,与Kubernetes集群是分离的。数据被持久化后,即使整个Kubernetes崩溃也不会受损。
vim nginx-pv.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx-pv-demo #
name: nginx-pv-demo
spec:
replicas: 2 #副本数
selector:
matchLabels:
app: nginx-pv-demo #与metadata.labels.app和name的一致
template:
metadata:
labels:
app: nginx-pv-demo
spec:
containers:
- image: nginx #镜像
name: nginx
volumeMounts:
- name: html #文件名
mountPath: /usr/share/nginx/html #文件挂载所在目录
volumes:
- name: html
nfs:
server: 192.168.11.101 #其他节点的ip
path: /nfs/data/nginx-pv #mountPath对应的目录是/nfs/data/nginx-pv
PV & PVC
Volume 提供了非常好的数据持久化方案,不过在可管理性上还有不足。前面 nfs 例子来说,要使用 Volume, Pod 必须事先知道以下信息:
- 当前的 Volume 类型并明确 Volume 已经创建好。
- 必须知道 Volume 的具体地址信息。
但是 Pod 通常是由应用的开发人员维护,而 Volume 则通常是由存储系统的管理员维护。开发人员要获得上面的信息,要么询问管理员,要么自己就是管理员。这样就带来一个管理上的问题:应用开发人员和系统管理员的职责耦合在一起了。如果系统规模较小或者对于开发环境,这样的情况还可以接受,当集群规模变大,特别是对于生产环境,考虑到效率和安全性,这就成了必须要解决的问题。
Kubernetes给出的解决方案是Persistent Volume和PersistentVolume Claim
。
PersistentVolume(PV)是外部存储系统中的一块存储空间,由管理员创建和维护,将应用需要持久化的数据保存到指定位置
。与Volume一样,PV具有持久性,生命周期独立于Pod。
Persistent Volume Claim (PVC)是对PV的申请(Claim),申明需要使用的持久卷规格
。PVC通常由普通用户创建和维护。需要为Pod分配存储资源时,用户可以创建一个PVC,指明存储资源的容量大小和访问模式(比如只读)等信息,Kubernetes会查找并提供满足条件的PV。有了PersistentVolumeClaim,用户只需要告诉Kubernetes需要什么样的存储资源,而不必关心真正的空间从哪里分配、如何访问等底层细节信息。这些Storage Provider的底层信息交给管理员来处理,只有管理员才应该关心创建PersistentVolume的细节信息。
基本使用
创建pv
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-pv
spec:
capacity:
storage: 1Gi #指定容量大小
accessModes: # 访问模式
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
storageClassName: nfs
nfs:
path: /{nfs-server目录名称}
server: {nfs-server IP 地址}
- accessModes: 支持的访问模式有3种:
- ReadWriteOnce 表示 PV 能以 readwrite 模式 mount 到单个节点
- 这个PV只能被某个节点以读写方式挂载,意味着这个PV只能被一个Pod挂载到某个节点上,并且这个Pod可以对这个PV进行读写操作。如果尝试在其他节点上挂载这个PV,就会失败。
- ReadOnlyMany 表示 PV 能以 read-only 模式 mount 到多个节点,
- 这个PV能被多个节点以只读方式挂载,意味着这个PV可以被多个Pod挂载到多个节点上。
- ReadWriteMany 表示 PV 能以 read-write 模式 mount 到多个节点。
- 这个PV能被多个节点以读写方式挂载,意味着这个PV可以被多个Pod挂载到多个节点上。
- ReadWriteOnce 表示 PV 能以 readwrite 模式 mount 到单个节点
- persistentVolumeReclaimPolicy: 指定当 PV 的回收策略支持的策略有3种:
- Retain:在 PVC 被删除后,保留 PV 和其数据,手动清理 PV 中的数据。
- Delete:在 PVC 被删除后,自动删除 PV 和其数据。
- Recycle:在 PVC 被删除后,通过删除 PV 中的数据来准备 PV 以供重新使用。
值得注意的是,persistentVolumeReclaimPolicy只适用于一些类型的 PV,如 NFS、HostPath、iSCSI 等。对于一些云平台提供的存储,如 AWS EBS 和 Azure Disk,由于底层提供商会自动处理 PV 的回收问题,因此该属性不适用。
- storageClassName: 指定 PV 的class 为 nfs。相当于为 PV 设置了一个分类,PVC可以指定 class 申请相应 class 的 PV。
创建pvc
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nfs-pvc
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
storageClassName: nfs # 通过名字进行选择
#selector: 通过标签形式
# matchLabels:
# pv-name: nfs-pv
静态供应示例
创建PV池
#nfs主节点
mkdir -p /nfs/data/01
mkdir -p /nfs/data/02
mkdir -p /nfs/data/03
创建PV
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv01-10m
spec:
capacity:
storage: 10M
accessModes:
- ReadWriteMany
storageClassName: nfs
nfs:
path: /nfs/data/01
server: 192.168.11.101
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv02-1gi
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteMany
storageClassName: nfs
nfs:
path: /nfs/data/02
server: 192.168.11.101
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv03-3gi
spec:
capacity:
storage: 3Gi
accessModes:
- ReadWriteMany
storageClassName: nfs
nfs:
path: /nfs/data/03
server: 192.168.11.101
创建PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nginx-pvc
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 200Mi
storageClassName: nfs
创建Pod绑定PVC
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx-deploy-pvc
name: nginx-deploy-pvc
spec:
replicas: 2
selector:
matchLabels:
app: nginx-deploy-pvc
template:
metadata:
labels:
app: nginx-deploy-pvc
spec:
containers:
- image: nginx
name: nginx
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
volumes:
- name: html
persistentVolumeClaim:
claimName: nginx-pvc
动态供应
在前面的例子中,提前创建了PV,然后通过PVC申请PV并在Pod中使用,这种方式叫作静态供应(Static Provision)与之对应的是动态供应(Dynamical Provision),即如果没有满足PVC条件的PV,会动态创建PV。相比静态供应,动态供应有明显的优势:不需要提前创建PV,减少了管理员的工作量,效率高。动态供应是通过 StorageClass 实现的,StorageClass定义了如何创建PV,但需要注意的是每个StorageClass 都有一个制备器(Provisioner),用来决定使用哪个卷插件制备PV,该字段必须指定才能实现动态创建。
配置动态供应的默认存储类
## 创建了一个存储类
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs-storage
annotations:
storageclass.kubernetes.io/is-default-class: "true"
provisioner: k8s-sigs.io/nfs-subdir-external-provisioner
parameters:
archiveOnDelete: "true" ## 删除pv的时候,pv的内容是否要备份
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nfs-client-provisioner
labels:
app: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: default
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: nfs-client-provisioner
template:
metadata:
labels:
app: nfs-client-provisioner
spec:
serviceAccountName: nfs-client-provisioner
containers:
- name: nfs-client-provisioner
image: registry.cn-hangzhou.aliyuncs.com/lfy_k8s_images/nfs-subdir-external-provisioner:v4.0.2
# resources:
# limits:
# cpu: 10m
# requests:
# cpu: 10m
volumeMounts:
- name: nfs-client-root
mountPath: /persistentvolumes
env:
- name: PROVISIONER_NAME
value: k8s-sigs.io/nfs-subdir-external-provisioner
- name: NFS_SERVER
value: 192.168.11.101 ## 指定自己nfs服务器地址
- name: NFS_PATH
value: /nfs/data ## nfs服务器共享的目录
volumes:
- name: nfs-client-root
nfs:
server: 192.168.11.101
path: /nfs/data
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: nfs-client-provisioner-runner
rules:
- apiGroups: [""]
resources: ["nodes"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["persistentvolumes"]
verbs: ["get", "list", "watch", "create", "delete"]
- apiGroups: [""]
resources: ["persistentvolumeclaims"]
verbs: ["get", "list", "watch", "update"]
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["events"]
verbs: ["create", "update", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: run-nfs-client-provisioner
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: default
roleRef:
kind: ClusterRole
name: nfs-client-provisioner-runner
apiGroup: rbac.authorization.k8s.io
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: leader-locking-nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: default
rules:
- apiGroups: [""]
resources: ["endpoints"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: leader-locking-nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: default
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: default
roleRef:
kind: Role
name: leader-locking-nfs-client-provisioner
apiGroup: rbac.authorization.k8s.io
配置
ConfigMap
在Kubernetes 中,ConfigMap是一种用于存储非敏感信息的Kubernetes 对象。它用于存储配置数据,如键值对、整个配置文件或JSON数据等。ConfigMap通常用于容器镜像中的配置文件、命令行参数和环境变量等。
ConfigMap可以通过三种方式进行配置数据的注入:
- 环境变量注入:将配置数据注入到Pod中的容器环境变量中。
- 配置文件注入:将配置数据注入到Pod中的容器文件系统中,容器可以读取这些文件。
- 命令行参数注入:将配置数据注入到容器的命令行参数中。
优点
- 避免了硬编码,将配置数据与应用代码分离。
- 便于维护和更新,可以单独修改ConfigMap而不需要重新构建镜像。
- 可以通过多种方式注入配置数据,更加灵活。
- 可以通过Kubernetes的自动化机制对ConfigMap进行版本控制和回滚。
- ConfigMap可以被多个Pod共享,减少了配置数据的重复存储。
定义ConfigMap
- 基本操作
#查看configmap
$ kubectl get configmap/ cm
#查看详细
$ kubectl describe configmap/cm my-config
#删除cm
$ kubectl delete cm my-config
- 命令行创建:
- 可以使用kubectl create configmap命令来创建configmap,具体命令如下:
kubectl create configmap my-config --from-literal=key1=value1 --from-literal=key2=value2
- 通过配置文件创建:推荐
- 可以通过创建YAML文件的方式来定义configmap的内容。例如,创建一个名为my-config的configmap,内容如下:
-
- 然后使用kubectl apply -f命令来创建configmap。
apiVersion: v1
kind: ConfigMap
metadata:
name: my-config
data:
key1: value1
key2: value2
---
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
application.yml: |
name: yuyang
- 通过文件创建:
echo-n admin >./username
echo -n 123456 > ./password
kubectl create configmap myconfigmap --from-file=./username --from-file=./password
- 通过文件夹创建:
- 可以将多个配置文件放在同一个文件夹下,然后使用kubectl create configmap命令来创建configmap,例如:
- 这将创建一个名为my-config的configmap,其中包含config-files/文件夹下所有的文件内容作为键值对。
kubectl create configmap my-config --from-file=config-files/
- 通过环境变量创建:
- 可以将环境变量的值转换为configmap。例如,使用以下命令将当前环境变量的值转换为configmap:
kubectl create configmap my-config --from-env-file=<env>
使用示例
# docker安装redis
docker run -v /data/redis/redis.conf:/etc/redis/redis.conf \
-v /data/redis/data:/data \
-d --name myredis \
-p 6379:6379 \
redis:latest redis-server /etc/redis/redis.conf
创建ConfigMap
- 通过文件的方式创建
#创建redis.conf
daemonize yes
requirepass root
# 创建配置,redis保存到k8s的etcd
kubectl create cm redis-conf --from-file=redis.conf
#查看资源清单
kubectl get cm redis-conf -oyaml
- 通过yaml的方式创建
apiVersion: v1
kind: ConfigMap
metadata:
name: redis-conf
data:
redis.conf: |
maxmemory-policy allkeys-lru
requirepass root
创建Pod
apiVersion: v1
kind: Pod
metadata:
name: redis
spec:
containers:
- name: redis
image: redis
command:
- redis-server
- "/redis-master/redis.conf" #指的是redis容器内部的位置
ports:
- containerPort: 6379
volumeMounts:
- mountPath: /data
name: data
- mountPath: /redis-master
name: config
volumes:
- name: data
emptyDir: {}
- name: config
configMap:
name: redis-conf
items:
- key: redis.conf
path: redis.conf
测试
kubectl exec -it redis -- redis-cli
127.0.0.1:6379> config get maxmemory-policy
Secret
Secret 对象类型用来保存敏感信息,例如密码、OAuth 令牌和 SSH 密钥。 将这些信息放在 secret 中比放在 Pod 的定义或者 容器镜像 中来说更加安全和灵活。
在 Kubernetes 中,Secrets 通常被用于以下场景:
- 作为卷挂载到 Pod 中,用于存储证书、密钥等敏感文件
- 在 Pod 中使用环境变量,用于存储用户名和密码等敏感信息
- 用于存储 Docker 镜像仓库的登录信息
- 用于存储外部服务的 API 密钥
定义 Secret
- 使用命令行创建:
- 可以使用 kubectl create secret 命令来创建 secret,例如:
kubectl create secret generic my-secret --from-literal=username=admin --from-literal=password=admin123
- 使用 YAML 文件定义:
- 可以创建一个 YAML 文件来定义 Secret 对象,例如:
apiVersion: v1
kind: Secret
metadata:
name: my-secret
type: Opaque
data:
username: YWRtaW4= # base64 编码后的用户名 admin
password: MWYyZDFlMmU2N2Rm # base64 编码后的密码 1f2d1e2e67df
注意: 这个 YAML 文件定义了一个名为 my-secret 的 Secret 对象,其中包含了两个 base64 编码后的 key-value 对:username 和 password。
- 使用文件创建:
echo -n admin >./username
echo -n 123456 > ./password
kubectl create secret generic mysecret --from-file=./username --from-file=./password
- 通过环境变量创建:
- 可以将环境变量的值转换为secret。例如,使用以下命令将当前环境变量的值转换为secret:
kubectl create secret generic my-config --from-env-file=<env>
使用示例:从私有docker仓库拉取镜像
docker pull registry.cn-hangzhou.aliyuncs.com/fox666/tulingmall-product:0.0.5
无法从私有镜像仓库拉取镜像,抛出如下错误:
解决方案:使用 docker 的用户信息来生成 secret:
##命令格式
kubectl create secret docker-registry myregistrykey \
--docker-server=<你的镜像仓库服务器> \
--docker-username=<你的用户名> \
--docker-password=<你的密码> \
--docker-email=<你的邮箱地址>
kubectl create secret docker-registry myregistrykey --docker-server=registry.cn-hangzhou.aliyuncs.com --docker-username=fox666 --docker-password=xxx
在创建 Pod 的时候,通过imagePullSecrets来引用刚创建的myregistrykey
apiVersion: v1
kind: Pod
metadata:
name: mall-product
spec:
containers:
- name: tulingmall-product
image: registry.cn-hangzhou.aliyuncs.com/fox666/tulingmall-product:0.0.5
imagePullSecrets:
- name: myregistrykey
Ingress
Ingress 是一种 Kubernetes 资源类型,它允许在 Kubernetes 集群中暴露 HTTP 和 HTTPS 服务。通过 Ingress,您可以将流量路由到不同的服务和端点,而无需使用不同的负载均衡器。Ingress 通常使用 Ingress Controller 实现,它是一个运行在 Kubernetes 集群中的负载均衡器,它根据Ingress 规则配置路由规则并将流量转发到相应的服务。
这是一个将所有流量都发送到同一 Service 的简单 Ingress 示例:
Ingress 和 Service 区别
Ingress 和 Service都是 Kubernetes 中用于将流量路由到应用程序的机制,但它们在路由层面上有所不同:
- Service 是 Kubernetes 中抽象的应用程序服务,它公开了一个单一的IP地址和端口,可以用于在 Kubernetes 集群内部的 Pod 之间进行流量路由。
- Ingress 是一个 Kubernetes 资源对象,它提供了对集群外部流量路由的规则。Ingress 通过一个公共IP地址和端口将流量路由到一个或多个Service。
安装Ingress
1)下载ingress配置文件
wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.47.0/deploy/static/provider/baremetal/deploy.yaml
[root@k8s-master k8s]# grep image: deploy.yaml
image: k8s.gcr.io/ingress-nginx/controller:v0.46.0@sha256:52f0058bed0a17ab0fb35628ba97e8d52b5d32299fbc03cc0f6c7b9ff036b61a
image: docker.io/jettech/kube-webhook-certgen:v1.5.1
image: docker.io/jettech/kube-webhook-certgen:v1.5.1
#修改镜像
vi deploy.yaml
#1、将image k8s.gcr.io/ingress-nginx/controller:v0.46.0@sha256:52f0058bed0a17ab0fb35628ba97e8d52b5d32299fbc03cc0f6c7b9ff036b61a的值改为如下值:
registry.cn-hangzhou.aliyuncs.com/lfy_k8s_images/ingress-nginx-controller:v0.46.0
2)安装ingress,执行如下命令
kubectl apply -f ingress-controller.yaml
- 查看是否安装成功
kubectl get pod,svc -n ingress-nginx -owide
使用Ingress
官网地址: https://kubernetes.github.io/ingress-nginx/
配置ingress访问规则(就是类似配置nginx的代理转发配置),让ingress将域名tomcat.tuling.com转发给后端的my-tomcat服务,新建一个文件ingress-tomcat.yaml,内容如下:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web-ingress
spec:
rules:
- host: tomcat.tuling.com #转发域名
http:
paths:
- pathType: Prefix
path: /
backend:
service:
name: my-tomcat
port:
number: 8080 #service的端口
执行如下命令生效规则:
kubectl apply -f ingress-tomcat.yaml
查看生效的ingress规则:
kubectl get ing
在访问机器配置host, win10客户机在目录: C:\Windows\System32\drivers\etc,在host里增加如下host(ingress部署的机器ip对应访问的域名)
192.168.65.82 tomcat.yuyang.com
配置完后直接在客户机浏览器访问http://tomcat.yuyang.com:30940能正常访问tomcat.
Service&Ingress总结
Service是K8S服务的核心,屏蔽了服务细节,统一对外暴露服务接口,真正做到了“微服务”。举个例子,我们的一个服务A,部署了3个备份,也就是3个Pod;对于用户来说,只需要关注一个Service的入口就可以,而不需要操心究竟应该请求哪一个Pod。优势非常明显: 一方面外部用户不需要感知因为Pod . 上服务的意外崩溃、K8S 重新拉起Pod而造成的IP变更,外部用户也不需要感知因升级、变更服务带来的Pod替换而造成的IP变化,另- -方面,Service 还可以做流量负载均衡。
但是,Service 主要负责K8S集群内部的网络拓扑。集群外部需要用Ingress。
Ingress是整个K8S集群的接入层,复杂集群内外通讯。
Ingress和Service的网络拓扑关系图如下:
K8S核心原理
K8S的网络模型
K8S的网络中主要存在4种类型的通信:
- 同一Pod内的容器间通信
- 各个Pod彼此间的通信
- Pod和Service间的通信
- 集群外部流量和Service之间的通信
K8S为Pod和Service资源对象分别使用了各自的专有网络,Pod网络由K8S的网络插件配置实现,而Service网络则由K8S集群进行指定。如下图:
K8S使用的网络插件需要为每个Pod配置至少一个特定的地址,即Pod IP。Pod IP地址实际存在于某个网卡(可以是虚拟机设备)上。
而Service的地址却是- -个虚拟IP地址,没有任何网络接口配置在此地址上,它由Kube-proxy借助iptables规则或ipvs规则重定向到本地端口,再将其调度到后端的Pod对象。Service的IP地址 是集群提供服务的接口,也称为Cluster IP。
Pod网络和IP由K8S的网络插件负责配置和管理,具体使用的网络地址可以在管理配置网络插件时进行指定,如10.244.0.0/16网络。 而Cluster网络和IP是由K8S集群负责配置和管理,如10.96.0.0/12网络。
从上图进行总结起来,一个K8S集群包含是三个网络。
- 节点网络:各主机(Master、 Node、 ETCD等)自身所属的网络,地址配置在主机的网络接口,用于各主机之间的通信,又称为节点网络。
- Pod网络:专用于Pod资源对象的网络,它是一个虚拟网络,用于为各Pod对象设定IP地址等网络参数,其地址配置在Pod中容器的网络接口上。Pod网络需要借助kubenet插件或CNI插件实现。
- Service网络:专用于Service资源对象的网络,它也是一一个虚拟网络,用于为K8S集群之中的Service配置IP地址,但是该地址不会配置在任何主机或容器的网络接口,上,而是通过Node.上的kube-proxy配置为iptables或ipvs规则, 从而将发往该地址的所有流量调度到后端的各Pod对象之.上。
K8S的工作流程
用K8S部署Nginx的过程中,K8S内部各组件是如何协同工作的:
在master节点执行一条命令要master部署-个nginx应用(kubectl create deployment nginx – image=nginx)
- 这条命令首先发到master节点的网关api server, 这是matser的唯一 入口
- api server将命令请求交给controller mannager进行控制
- controller mannager进行应用部署解析
- controller mannager会生成一次部署信息,并通过api server将信息存入etcd存储中
- scheduler调度 器通过api server从etcd存储中,拿到要部署的应用,开始调度看哪个节点有资源适合部署
- scheduler把计 算出来的调度信息通过api server再放到etcd中
- 每一个node节点的监控组件kubelet, 随时和master保持联系 (给api-server发送请求不断获取最新数据),拿到master节 点存储在etcd中的部署信息
- 假设node2的kubelet拿到部署信息,显示他自己节点要部署某某应用
- kubelet就自 己run一一个应用在当前机器上,并随时给master汇 报当前应用的状态信息
- node和master也是通过master的api-server组件联系的
- 每一个机器上的kube proxy能知道集群的所有网络,只要node访问别人或者别人访问node, node上的kube-proxy网络代理自动计算进行流量转发
K8S架构原理六连问
K8S是- -个基于容器技术的分布式集群管理系统。既然是个分布式系统,那势必有多个Node节点(物理主机或虚拟机),它们共同组成一个分布式集群,并且这些节点中会有一-个Master节点,由它来统一管理Node节点。
如图所示:
问题一:主节点和工作节点是如何通信的呢?
首先,Master 节点启动时,会运行一一个kube-apiserver进程,它提供了集群管理的API接口,是集群内各个功能模块之间数据交互和通信的中心枢纽,并且它页提供了完备的集群安全机制。
在Node节点上,使用K8S中的kubelet组件,在每个Node节点上都会运行一-个kubelet进程,它负责向Master汇报自身节,点的运行情况,如Node节点的注册、终止、定时上报健康状况等,以及接收Master发出的命令,创建相应Pod。
在K8S中,Pod是最基本的操作单元,它与docker的容器有略微的不同,因为Pod可能包含- -个或多个容器(可以是docker容器),这些内部的容器是共享网络资源的,即可以通过localhost进行相互访问。
关于Pod内是如何做到网络共享的,每个Pod启动,内部都会启动一个pause容器(google的一 个镜像),它使用默认的网络模式,而其他容器的网络都设置给它,以此来完成网络的共享问题。
如图所示:
问题二: Master 是如何将Pod调度到指定的Node .上的?
该工作由kube-scheduler来完成,整个调度过程通过执行-些列复杂的算法最终为每个Pod 计算出一一个最佳的目标Node,该过程由kube-scheduler进程自动完成。常见的有轮询调度(RR) 。当然也有可能,我们需要将Pod调度到一个指定的Node.上, 我们可以通过节点的标签(Label) 和Pod的nodeSelector属性的相互匹配,来达到指定的效果。
如图所示: .
问题三:各节点、Pod 的信息都是统- -维护在哪里的,由谁来维护?
从上面的Pod调度的角度看,我们得有-个存储中心,用来存储各节点资源使用情况、健康状态、以及各Pod的基本信息等,这样Pod的调度来能正常进行。
在K8S中,采用etcd组件作为一一个高可用强一致性的存储仓库, 该组件可以内置在K8S中,也可以外部搭建供K8S使用。
集群上的所有配置信息都存储在了etcd, 为了考虑各个组件的相对独立,以及整体的维护性,对于这些存储数据的增、删、改、查,统一由kube-apiserver来进行调用,apiserver 也提供了REST的支持,不仅对各个内部组件提供服务外,还对集群外部用户暴露服务。
外部用户可以通过REST接口,或者kubectl命令行工具进行集群管理,其内在都是与apiserver 进行通信。
如图所示:
问题四:外部用户如何访问集群内运行的Pod ?
前面讲了外部用户如何管理K8S,而我们更关心的是内部运行的Pod如何对外访问。使用过Docker的同学应该知道,如果使用bridge模式,在容器创建时,都会分配一个虚拟IP,该IP外部是没法访问到的,我们需要做一层端口映射, 将容器内端口与宿主机端口进行映射绑定,这样外部通过访问宿主机的指定端口,就可以访问到内部容器端口了。
那么,K8S的外部访问是否也是这样实现的?答案是否定的,K8S中情况要复杂-些。因为上面讲的Docker是单机模式下的,而且一个容器对外就暴露一一个服务。 在分布式集群下,一个服务往往由多个Application 提供,用来分担访问压力,而且这些Application可能会分布在多个节点上,这样又涉及到了跨主机的通信。
这里,K8S引入了Service的概念,将多个相同的Pod包装成一个完整的service对外提供服务,至于获取到这些相同的Pod,每个Pod启动时都会设置labels属性,在Service中我们通过选择器Selector,选择具有相同Name标签属性的Pod,作为整体服务,并将服务信息通过Apiserver存入etcd中,该工作由Service Controller 来完成。同时,每个节点上会启动一个kube-proxy进程,由它来负责服务地址到Pod地址的代理以及负载均衡等工作。
如图所示:
问题五: Pod如何动态扩容和缩放?
既然知道了服务是由Pod组成的,那么服务的扩容也就意味着Pod的扩容。通俗点讲,就是在需要时将Pod复制多份,在不需要后,将Pod缩减至指定份数。K8S中通过Replication Controller来进行管理,为每个Pod设置一个期望的副本数,当实际副本数与期望不符时,就动态的进行数量调整,以达到期望值。期望数值可以由我们手动更新,或自动扩容代理来完成。
如图所示:
问题六:各个组件之间是如何相互协作的?
最后,讲一下kube-controller-manager这个进程的作用。我们知道了ectd 是作为集群数据的存储中心,apiserver 是管理数据中心,作为其他进程与数据中心通信的桥梁。而Service Controller、Replication Controller 这些统- -交由 kube-controller-manager来管理,kube-controller-manager 作为一一个守护进程,每个Controller 都是-个控制循环,通过apiserver监视集群的共享状态,并尝试将实际状态与期望不符的进行改变。关于Controller, manager 中还包含了Node节点控制器( Node Controller)、资源配额管控制器( ResourceQuota Controller)、命名空间控制器( Namespace Controller)等。
如图所示:
Pod
Pod标签管理
标签(Labels) 是附加到 Kubernetes 对象(比如 Pod)上的键值对。 标签旨在用于指定对用户有意义且相关的对象的标识属性。标签可以在创建时附加到对象,随后可以随时添加和修改。每个对象都可以定义一组键(key)/值(value)标签,但是每个键(key)对于给定对象必须是唯一的。
标签作用: 就是用来给 k8s 中对象起别名, 有了别名可以过滤和筛选
语法
标签由键值对组成,其有效标签值:
- 必须为 63 个字符或更少(可以为空)
- 除非标签值为空,必须以字母数字字符([a-z0-9A-Z])开头和结尾
- 包含破折号(-)、下划线(_)、点(.)和字母或数字
示例
apiVersion: v1
kind: Pod
metadata:
name: myapp
labels:
name: myapp #创建时添加
spec:
containers:
- name: nginx
image: nginx:1.21
imagePullPolicy: IfNotPresent
- name: redis
image: redis:5.0.10
imagePullPolicy: IfNotPresent
restartPolicy: Always
标签基本操作
# 查看标签
$ kubectl get pods --show-labels
# kubectl label pod pod名称 标签键值对
$ kubectl label pod myapp env=prod
# 覆盖标签 --overwrite
$ kubectl label --overwrite pod myapp env=test
# 删除标签 -号代表删除标签
$ kubectl label pod myapp env-
# 根据标签筛选 env=test/env > = <
$ kubectl get po -l env=test
$ kubectl get po -l env
$ kubectl get po -l '!env' # 不包含的 pod
$ kubectl get po -l 'env in (test,prod)' #选择含有指定值的 pod
$ kubectl get po -l 'env notin (test,prod)' #选择含有指定值的 pod
Pod 的生命周期
官网: https://kubernetes.io/zh-cn/docs/concepts/workloads/pods/pod-lifecycle/
Pod 遵循预定义的生命周期,起始于 Pending 阶段, 如果至少其中有一个主要容器正常启动,则进入 Running,之后取决于 Pod 中是否有容器以失败状态结束而进入 Succeeded 或者 Failed 阶段。与此同时Pod 在其生命周期中只会被调度一次。 一旦 Pod 被调度(分派)到某个节点,Pod 会一直在该节点运行,直到 Pod 停止或者被终止。
生命周期
和一个个独立的应用容器一样,Pod 也被认为是相对临时性(而不是长期存在)的实体。 Pod 会被创建、赋予一个唯一的 ID(UID), 并被调度到节点,并在终止(根据重启策略)或删除之前一直运行在该节点。如果一个节点死掉了,调度到该节点的 Pod 也被计划在给定超时期限结束后删除。
Pod 自身不具有自愈能力。如果 Pod 被调度到某节点而该节点之后失效, Pod 会被删除;类似地,Pod 无法在因节点资源耗尽或者节点维护而被驱逐期间继续存活。 Kubernetes 使用一种高级抽象来管理这些相对而言可随时丢弃的 Pod 实例, 称作控制器。
任何给定的 Pod (由 UID 定义)从不会被“重新调度(rescheduled)”到不同的节点; 相反,这一 Pod 可以被一个新的、几乎完全相同的 Pod 替换掉。 如果需要,新 Pod 的名字可以不变,但是其 UID 会不同。
如果某物声称其生命期与某 Pod 相同,例如存储卷, 这就意味着该对象在此 Pod (UID 亦相同)存在期间也一直存在。 如果 Pod 因为任何原因被删除,甚至完全相同的替代 Pod 被创建时, 这个相关的对象(例如这里的卷)也会被删除并重建。
pod 阶段
Pod 阶段的数量和含义是严格定义的。 除了本文档中列举的内容外,不应该再假定 Pod 有其他的 phase 值。
取值 | 描述 |
---|---|
Pending(悬决) | Pod 已被 Kubernetes 系统接受,但有一个或者多个容器尚未创建亦未运行。此阶段包括等待 Pod 被调度的时间和通过网络下载镜像的时间。 |
Running(运行中) | Pod 已经绑定到了某个节点,Pod 中所有的容器都已被创建。至少有一个容器仍在运行,或者正处于启动或重启状态。 |
Succeeded(成功) | Pod 中的所有容器都已成功终止,并且不会再重启。 |
Failed(失败) | Pod 中的所有容器都已终止,并且至少有一个容器是因为失败终止。也就是说,容器以非 0 状态退出或者被系统终止。 |
Unknown(未知) | 因为某些原因无法取得 Pod 的状态。这种情况通常是因为与 Pod 所在主机通信失败。 |
说明:
- 当一个 Pod 被删除时,执行一些 kubectl 命令会展示这个 Pod 的状态为 Terminating(终止)。 这个 Terminating 状态并不是 Pod 阶段之一。 Pod 被赋予一个可以体面终止的期限,默认为 30 秒。 你可以使用 --force 参数来强制终止 Pod。
- 如果某节点死掉或者与集群中其他节点失联,Kubernetes 会实施一种策略,将失去的节点上运行的所有 Pod 的 phase 设置为 Failed。
Container生命周期
容器生命周期
Kubernetes 会跟踪 Pod 中每个容器的状态,就像它跟踪 Pod 总体上的阶段一样。 你可以使用容器生命周期回调来在容器生命周期中的特定时间点触发事件。
一旦调度器将 Pod 分派给某个节点,kubelet 就通过容器运行时开始为 Pod 创建容器。容器的状态有三种:Waiting
(等待)、Running
(运行中)和 Terminated
(已终止)。
要检查 Pod 中容器的状态,你可以使用 kubectl describe pod <pod 名称>
。 其输出中包含 Pod 中每个容器的状态。
每种状态都有特定的含义:
- Waiting (等待)
如果容器并不处在Running
或Terminated
状态之一,它就处在Waiting
状态。 处于 Waiting 状态的容器仍在运行它完成启动所需要的操作:例如, 从某个容器镜像仓库拉取容器镜像,或者向容器应用 Secret 数据等等。 当你使用kubectl
来查询包含 Waiting 状态的容器的 Pod 时,你也会看到一个 Reason 字段,其中给出了容器处于等待状态的原因。 - Running(运行中)
Running 状态表明容器正在执行状态并且没有问题发生。 如果配置了postStart
回调,那么该回调已经执行且已完成。 如果你使用kubectl
来查询包含Running
状态的容器的 Pod 时, 你也会看到关于容器进入Running
状态的信息。 - Terminated(已终止)
处于Terminated
状态的容器已经开始执行并且或者正常结束或者因为某些原因失败。 如果你使用kubectl
来查询包含Terminated
状态的容器的 Pod 时, 你会看到容器进入此状态的原因、退出代码以及容器执行期间的起止时间。
如果容器配置了preStop
回调,则该回调会在容器进入Terminated
状态之前执行。
容器生命周期回调/事件/钩子
类似于许多具有生命周期回调组件的编程语言框架,例如 Angular、Vue、Kubernetes 为容器提供了生命周期回调。 回调使容器能够了解其管理生命周期中的事件,并在执行相应的生命周期回调时运行在处理程序中实现的代码。
有两个回调暴露给容器:
- PostStart 这个回调在容器被创建之后立即被执行。 但是,不能保证回调会在容器入口点(ENTRYPOINT)之前执行。 没有参数传递给处理程序。
- PreStop 在容器因 API 请求或者管理事件(诸如存活态探针、启动探针失败、资源抢占、资源竞争等) 而被终止之前,此回调会被调用。 如果容器已经处于已终止或者已完成状态,则对 preStop 回调的调用将失败。 在用来停止容器的 TERM 信号被发出之前,回调必须执行结束。 Pod 的终止宽限周期在 PreStop 回调被执行之前即开始计数, 所以无论回调函数的执行结果如何,容器最终都会在 Pod 的终止宽限期内被终止。 没有参数会被传递给处理程序。
使用容器生命周期回调
# nginx-pod.yml
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx:1.19
lifecycle:
postStart: #容器创建过程中执行
exec:
command: ["/bin/sh","-c","echo postStart >> /start.txt"]
preStop: #容器终止之前执行
exec:
command: ["/bin/sh","-c","echo preStop >> /stop.txt && sleep 15"]
ports:
- containerPort: 80
容器资源限制
在k8s中对于容器资源限制主要分为以下两类:
- 内存资源限制: 内存请求(request)和内存限制(limit)分配给一个容器。 我们保障容器拥有它请求数量的内存,但不允许使用超过限制数量的内存。
- 官网参考地址: https://kubernetes.io/zh-cn/docs/tasks/configure-pod-container/assign-memory-resource/
- CPU 资源限制: 为容器设置 CPU request(请求) 和 CPU limit(限制)。 容器使用的CPU 不能超过所配置的限制。 如果系统有空闲的 CPU 时间,则可以保证给容器分配其所请求数量的 CPU 资源。
- 官网参考地址: https://kubernetes.io/zh-cn/docs/tasks/configure-pod-container/assign-cpu-resource/
metrics-server
官网地址: https://github.com/kubernetes-sigs/metrics-server
Kubernetes Metrics Server (Kubernetes指标服务器),它是一个可扩展的、高效的容器资源度量源。Metrics Server 用于监控每个 Node 和 Pod 的负载(用于Kubernetes内置自动扩缩管道)。Metrics Server 从Kubelets 收集资源指标,并通过 Metrics API 在Kubernetes apiserver中公开,供 Horizontal Pod Autoscaler 和 Vertical Pod Autoscaler 使用。Metrics API 也可以通过 kubectl top 访问,使其更容易调试自动扩缩管道。
- 查看 metrics-server(或者其他资源指标 API metrics.k8s.io 服务提供者)是否正在运行, 请键入以下命令:
kubectl get apiservices
- 如果资源指标 API 可用,则会输出将包含一个对 metrics.k8s.io 的引用。
NAME
v1beta1.metrics.k8s.io
- 安装 metrics-server
# components.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
labels:
k8s-app: metrics-server
name: metrics-server
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
k8s-app: metrics-server
rbac.authorization.k8s.io/aggregate-to-admin: "true"
rbac.authorization.k8s.io/aggregate-to-edit: "true"
rbac.authorization.k8s.io/aggregate-to-view: "true"
name: system:aggregated-metrics-reader
rules:
- apiGroups:
- metrics.k8s.io
resources:
- pods
- nodes
verbs:
- get
- list
- watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
k8s-app: metrics-server
name: system:metrics-server
rules:
- apiGroups:
- ""
resources:
- nodes/metrics
verbs:
- get
- apiGroups:
- ""
resources:
- pods
- nodes
verbs:
- get
- list
- watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
labels:
k8s-app: metrics-server
name: metrics-server-auth-reader
namespace: kube-system
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: extension-apiserver-authentication-reader
subjects:
- kind: ServiceAccount
name: metrics-server
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
labels:
k8s-app: metrics-server
name: metrics-server:system:auth-delegator
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:auth-delegator
subjects:
- kind: ServiceAccount
name: metrics-server
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
labels:
k8s-app: metrics-server
name: system:metrics-server
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:metrics-server
subjects:
- kind: ServiceAccount
name: metrics-server
namespace: kube-system
---
apiVersion: v1
kind: Service
metadata:
labels:
k8s-app: metrics-server
name: metrics-server
namespace: kube-system
spec:
ports:
- name: https
port: 443
protocol: TCP
targetPort: https
selector:
k8s-app: metrics-server
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
k8s-app: metrics-server
name: metrics-server
namespace: kube-system
spec:
selector:
matchLabels:
k8s-app: metrics-server
strategy:
rollingUpdate:
maxUnavailable: 0
template:
metadata:
labels:
k8s-app: metrics-server
spec:
containers:
- args:
- --cert-dir=/tmp
- --secure-port=4443
- --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
- --kubelet-use-node-status-port
- --metric-resolution=15s
- --kubelet-insecure-tls #修改去掉证书验证
image: dyrnq/metrics-server:v0.6.2 #修改官方无法下载
imagePullPolicy: IfNotPresent
livenessProbe:
failureThreshold: 3
httpGet:
path: /livez
port: https
scheme: HTTPS
periodSeconds: 10
name: metrics-server
ports:
- containerPort: 4443
name: https
protocol: TCP
readinessProbe:
failureThreshold: 3
httpGet:
path: /readyz
port: https
scheme: HTTPS
initialDelaySeconds: 20
periodSeconds: 10
resources:
requests:
cpu: 100m
memory: 200Mi
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1000
volumeMounts:
- mountPath: /tmp
name: tmp-dir
hostNetwork: true #必须指定这个才行
nodeSelector:
kubernetes.io/os: linux
priorityClassName: system-cluster-critical
serviceAccountName: metrics-server
volumes:
- emptyDir: {}
name: tmp-dir
---
apiVersion: apiregistration.k8s.io/v1
kind: APIService
metadata:
labels:
k8s-app: metrics-server
name: v1beta1.metrics.k8s.io
spec:
group: metrics.k8s.io
groupPriorityMinimum: 100
insecureSkipTLSVerify: true
service:
name: metrics-server
namespace: kube-system
version: v1beta1
versionPriority: 100
$ kubectl appply -f components.yaml
指定内存请求和限制
官网: https://kubernetes.io/zh-cn/docs/tasks/configure-pod-container/assign-memory-resource/
为容器指定内存请求,请在容器资源清单中包含 resources:requests 字段。 同理,要指定内存限制,请包含 resources:limits。
# nginx-memory-demo.yaml
#内存资源的基本单位是字节(byte)。你可以使用这些后缀之一,将内存表示为 纯整数或定点整数:E、P、T、G、M、K、Ei、Pi、Ti、Gi、Mi、Ki。
#例如,下面是一些近似相同的值:128974848, 129e6, 129M, 123Mi
apiVersion: v1
kind: Pod
metadata:
name: nginx-memory-demo
spec:
containers:
- name: nginx-memory-demo
image: nginx:1.19
resources:
requests:
memory: "100Mi"
limits:
memory: "200Mi"
- 查看容器内存使用情况
$ kubectl get pod nginx-memory-demo --output=yaml
- 查看容器正在使用内存情况
$ kubectl top pod nginx-memory-demo
- 内存请求和限制的目的通过为集群中运行的容器配置内存请求和限制,你可以有效利用集群节点上可用的内存资源。 通过将 Pod 的内存请求保持在较低水平,你可以更好地安排 Pod 调度。 通过让内存限制大于内存请求,你可以完成两件事:
- Pod 可以进行一些突发活动,从而更好的利用可用内存。
- Pod 在突发活动期间,可使用的内存被限制为合理的数量。
- 没有指定内存限制如果你没有为一个容器指定内存限制,则自动遵循以下情况之一:
- 容器可无限制地使用内存。容器可以使用其所在节点所有的可用内存, 进而可能导致该节点调用 OOM Killer。 此外,如果发生 OOM Kill,没有资源限制的容器将被杀掉的可行性更大。
- 运行的容器所在命名空间有默认的内存限制,那么该容器会被自动分配默认限制。
指定 CPU 请求和限制
官网: https://kubernetes.io/zh-cn/docs/tasks/configure-pod-container/assign-cpu-resource/
为容器指定 CPU 请求,请在容器资源清单中包含 resources: requests 字段。 要指定 CPU 限制,请包含 resources:limits。
# nginx-cpu-demo.yaml
#CPU 资源以 CPU 单位度量。小数值是可以使用的。一个请求 0.5 CPU 的容器保证会获得请求 1 个 CPU 的容器的 CPU 的一半。
#你可以使用后缀 m 表示毫。例如 100m CPU、100 milliCPU 和 0.1 CPU 都相同。 CPU 请求只能使用绝对数量,而不是相对数量。0.1 在单核、双核或 48 核计算机上的 CPU 数量值是一样的。
apiVersion: v1
kind: Pod
metadata:
name: nginx-cpu-demo
spec:
containers:
- name: nginx-cpu-demo
image: nginx:1.19
resources:
limits:
cpu: "1"
requests:
cpu: "0.5"
- 显示 pod 详细信息
$ kubectl get pod nginx-cpu-demo --output=yaml
- 显示 pod 运行指标
$ kubectl top pod nginx-cpu-demo
测试
#窗口1
watch -n 1 -d kubectl top pod nginx-cpu-demo
#窗口2
kubectl exec -it nginx-cpu-demo -- bash
#进入容器执行命令
while true ;do echo "yuyang"; done;
- CPU 请求和限制的初衷通过配置你的集群中运行的容器的 CPU 请求和限制,你可以有效利用集群上可用的 CPU 资源。 通过将 Pod CPU 请求保持在较低水平,可以使 Pod 更有机会被调度。 通过使 CPU 限制大于 CPU 请求,你可以完成两件事:
- Pod 可能会有突发性的活动,它可以利用碰巧可用的 CPU 资源。
- Pod 在突发负载期间可以使用的 CPU 资源数量仍被限制为合理的数量。
- 如果不指定 CPU 限制如果你没有为容器指定 CPU 限制,则会发生以下情况之一:
- 容器在可以使用的 CPU 资源上没有上限。因而可以使用所在节点上所有的可用 CPU 资源。
- 容器在具有默认 CPU 限制的名字空间中运行,系统会自动为容器设置默认限制。
- 如果你设置了 CPU 限制但未设置 CPU 请求
如果你为容器指定了 CPU 限制值但未为其设置 CPU 请求,Kubernetes 会自动为其 设置与 CPU 限制相同的 CPU 请求值。类似的,如果容器设置了内存限制值但未设置 内存请求值,Kubernetes 也会为其设置与内存限制值相同的内存请求。
Container重启策略
Pod 的 spec 中包含一个 restartPolicy 字段,其可能取值包括 Always(总是重启)、OnFailure(容器异常退出状态码非 0,重启) 和 Never。默认值是 Always。
restartPolicy 适用于 Pod 中的所有容器。restartPolicy 仅针对同一节点上 kubelet 的容器重启动作。当 Pod 中的容器退出时,kubelet 会按指数回退方式计算重启的延迟(10s、20s、40s、…),其最长延迟为 5 分钟。 一旦某容器执行了 10 分钟并且没有出现问题,kubelet 对该容器的重启回退计时器执行重置操作。
https://kubernetes.io/zh-cn/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.19
imagePullPolicy: IfNotPresent
restartPolicy: Always # OnFailure Never
自定义容器启动命令
和 Docker 容器一样,k8s中容器也可以通过command、args 用来修改容器启动默认执行命令以及传递相关参数。但一般推荐使用 command 修改启动命令,使用 args 为启动命令传递参数。
apiVersion: v1
kind: Pod
metadata:
name: redis
labels:
app: redis
spec:
containers:
- name: redis
image: redis:5.0.10
command: ["redis-server"] #用来指定启动命令
args: ["--appendonly yes"] # 用来为启动命令传递参数 配置aof持久化
#args: ["redis-server","--appendonly yes"] # 单独使用修改启动命令并传递参数
#args: # 另一种语法格式
# - redis-server
# - "--appendonly yes"
imagePullPolicy: IfNotPresent
restartPolicy: Always
测试效果
容器探针
probe 是由 kubelet对容器执行的定期诊断。 要执行诊断,kubelet 既可以在容器内执行代码,也可以发出一个网络请求。
容器探针就是用来定期对容器进行健康检查的。
https://kubernetes.io/zh-cn/docs/concepts/workloads/pods/pod-lifecycle/#container-probes
探测类型
针对运行中的容器,kubelet 可以选择是否执行以下三种探针,以及如何针对探测结果作出反应:
- livenessProbe 指示容器是否正在运行。如果存活态探测失败,则 kubelet 会杀死容器, 并且容器将根据其重启策略决定未来。如果容器不提供存活探针, 则默认状态为 Success。
- readinessProbe指示容器是否准备好为请求提供服务。如果就绪态探测失败, 端点控制器将从与 Pod 匹配的所有服务的端点列表中删除该 Pod 的 IP 地址。 初始延迟之前的就绪态的状态值默认为 Failure。 如果容器不提供就绪态探针,则默认状态为 Success。
- startupProbe 1.7+指示容器中的应用是否已经启动。如果提供了启动探针,则所有其他探针都会被 禁用,直到此探针成功为止。如果启动探测失败,kubelet 将杀死容器, 而容器依其重启策略进行重启。 如果容器没有提供启动探测,则默认状态为 Success。
探针机制
使用探针来检查容器有四种不同的方法。 每个探针都必须准确定义为这四种机制中的一种:
- exec在容器内执行指定命令。如果命令退出时返回码为 0 则认为诊断成功。
- grpc使用 gRPC 执行一个远程过程调用。 目标应该实现 gRPC健康检查。 如果响应的状态是 “SERVING”,则认为诊断成功。 gRPC 探针是一个 Alpha 特性,只有在你启用了 “GRPCContainerProbe” 特性门控时才能使用。
- httpGet对容器的 IP 地址上指定端口和路径执行 HTTP GET 请求。如果响应的状态码大于等于 200 且小于 400,则诊断被认为是成功的。
- tcpSocket对容器的 IP 地址上的指定端口执行 TCP 检查。如果端口打开,则诊断被认为是成功的。 如果远程系统(容器)在打开连接后立即将其关闭,这算作是健康的。
注意: 和其他机制不同,exec 探针的实现涉及每次执行时创建/复制多个进程。 因此,在集群中具有较高 pod 密度、较低的 initialDelaySeconds 和 periodSeconds 时长的时候, 配置任何使用 exec 机制的探针可能会增加节点的 CPU 负载。 这种场景下,请考虑使用其他探针机制以避免额外的开销。
探针结果
每次探测都将获得以下三种结果之一:
- Success(成功)容器通过了诊断。
- Failure(失败)容器未通过诊断。
- Unknown(未知)诊断失败,因此不会采取任何行动。
探针参数
initialDelaySeconds: 5 #初始化时间5s
periodSeconds: 4 #检测间隔时间4s
timeoutSeconds: 1 #默认检测超时时间为1s
failureThreshold: 3 #默认失败次数为3次,达到3次后重启pod
successThreshold: 1 #默认成功次数为1次,1次监测成功代表成功
使用探针
exec
apiVersion: v1
kind: Pod
metadata:
name: liveness-exec
labels:
exec: exec
spec:
containers:
- name: nginx
image: nginx:1.19
ports:
- containerPort: 80
args:
- /bin/sh
- -c
- sleep 7;nginx -g "daemon off;" #这一步会和初始化同时开始运行,也就是在初始化5s后和7秒之间,会检测出一次失败,7秒后启动后检测正常,所以pod不会重启
imagePullPolicy: IfNotPresent
livenessProbe:
exec: #这里使用 exec 执行 shell 命令检测容器状态
command:
- ls
- /var/run/nginx.pid #查看是否有pid文件
initialDelaySeconds: 5 #初始化时间5s
periodSeconds: 4 #检测间隔时间4s
timeoutSeconds: 1 #默认检测超时时间为1s
failureThreshold: 3 #默认失败次数为3次,达到3次后重启pod
successThreshold: 1 #默认成功次数为1次,1 次代表成功
说明:
1.如果 sleep 7s,第一次检测发现失败,但是第二次检测发现成功后容器就一直处于健康状态不会重启。
2.如果 sleep 30s,第一次检测失败,超过 3 次检测同样失败,k8s 就回杀死容器进行重启,反复循环这个过程。
tcpSocket
apiVersion: v1
kind: Pod
metadata:
name: liveness-tcpsocket
labels:
tcpsocket: tcpsocket
spec:
containers:
- name: nginx
image: nginx:1.19
ports:
- containerPort: 80
args:
- /bin/sh
- -c
- sleep 7;nginx -g "daemon off;" #这一步会和初始化同时开始运行,也就是在初始化5s后和7秒之间,会检测出一次失败,7秒后启动后检测正常,所以pod不会重启
imagePullPolicy: IfNotPresent
livenessProbe:
tcpSocket:
port: 80
initialDelaySeconds: 5 #初始化时间5s
periodSeconds: 4 #检测间隔时间4s
timeoutSeconds: 1 #默认检测超时时间为1s
failureThreshold: 3 #默认失败次数为3次,达到3次后重启pod
successThreshold: 1 #默认成功次数为1次,1 次代表成功
httpGet
# probe-liveness-httget.yml
apiVersion: v1
kind: Pod
metadata:
name: liveness-httpget
labels:
httpget: httpget
spec:
containers:
- name: nginx
image: nginx:1.19
ports:
- containerPort: 80
args:
- /bin/sh
- -c
- sleep 7;nginx -g "daemon off;" #这一步会和初始化同时开始运行,也就是在初始化5s后和7秒之间,会检测出一次失败,7秒后启动后检测正常,所以pod不会重启
imagePullPolicy: IfNotPresent
livenessProbe:
httpGet: #httpget
port: 80 #访问的端口
path: /index.html #访问的路径
initialDelaySeconds: 5 #初始化时间5s
periodSeconds: 4 #检测间隔时间4s
timeoutSeconds: 1 #默认检测超时时间为1s
failureThreshold: 3 #默认失败次数为3次,达到3次后重启pod
successThreshold: 1 #默认成功次数为1次,1 次代表成功
GRPC 探针
官方参考地址: https://kubernetes.io/zh-cn/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/
Pod的init 容器
init 容器是一种特殊容器,在Pod 内的应用容器启动之前运行。init 容器可以包括一些应用镜像中不存在的实用工具和安装脚本。
init 容器特点
init 容器与普通的容器非常像,除了如下几点:
- 它们总是运行到完成。如果 Pod 的 Init 容器失败,kubelet 会不断地重启该 Init 容器直到该容器成功为止。 然而,如果 Pod 对应的 restartPolicy 值为 “Never”,并且 Pod 的 Init 容器失败, 则 Kubernetes 会将整个 Pod 状态设置为失败。
- 每个都必须在下一个启动之前成功完成。
- 同时 Init 容器不支持 lifecycle、livenessProbe、readinessProbe 和 startupProbe, 因为它们必须在 Pod 就绪之前运行完成。
- 如果为一个 Pod 指定了多个 Init 容器,这些容器会按顺序逐个运行。 每个 Init 容器必须运行成功,下一个才能够运行。当所有的 Init 容器运行完成时, Kubernetes 才会为 Pod 初始化应用容器并像平常一样运行。
- Init 容器支持应用容器的全部字段和特性,包括资源限制、数据卷和安全设置。 然而,Init 容器对资源请求和限制的处理稍有不同。
使用 init 容器
官网地址: https://kubernetes.io/zh-cn/docs/concepts/workloads/pods/init-containers/
在 Pod 的规约中与用来描述应用容器的 containers 数组平行的位置指定 Init 容器。
apiVersion: v1
kind: Pod
metadata:
name: init-demo
spec:
containers:
- name: myapp-container
image: busybox:1.28
command: ['sh', '-c', 'echo The app is running! && sleep 3600']
initContainers:
- name: init-myservice
image: busybox:1.28
command: ['sh', '-c', 'echo init-myservice is running! && sleep 5']
- name: init-mydb
image: busybox:1.28
command: ['sh', '-c', 'echo init-mydb is running! && sleep 10']
查看启动详细
节点亲和性分配 Pod
官方地址: http://kubernetes.p2hp.com/docs/concepts/scheduling-eviction/assign-pod-node.html
你可以约束一个 Pod 以便 限制 其只能在特定的节点上运行, 或优先在特定的节点上运行。 有几种方法可以实现这点,推荐的方法都是用 标签选择算符来进行选择。 通常这样的约束不是必须的,因为调度器将自动进行合理的放置(比如,将 Pod 分散到节点上, 而不是将 Pod 放置在可用资源不足的节点上等等)。但在某些情况下,你可能需要进一步控制 Pod 被部署到哪个节点。例如,确保 Pod 最终落在连接了 SSD 的机器上, 或者将来自两个不同的服务且有大量通信的 Pods 被放置在同一个可用区。
你可以使用下列方法中的任何一种来选择 Kubernetes 对特定 Pod 的调度:
- 与节点标签匹配的 nodeSelector 推荐
- 亲和性与反亲和性 推荐
- nodeName
- Pod 拓扑分布约束 推荐
使用节点亲和性可以把 Kubernetes Pod 分配到特定节点。
给节点添加标签
- 列出集群中的节点及其标签:
$ kubectl get nodes --show-labels
#输出类似于此:
NAME STATUS ROLES AGE VERSION LABELS
k8s-node1 Ready control-plane 10d v1.26.0 beta.kubernetes.io/arch=arm64,beta.kubernetes.io/os=linux...
k8s-node2 Ready <none> 10d v1.26.0 beta.kubernetes.io/arch=arm64,beta.kubernetes.io/os=linux...
k8s-node3 Ready <none> 10d v1.26.0 beta.kubernetes.io/arch=arm64,beta.kubernetes.io/os=linux...
- 选择一个节点,给它添加一个标签:
kubectl label nodes k8s-node1(节点名称) disktype=ssd
- 验证你所选节点具有 disktype=ssd 标签:
$ kubectl get nodes --show-labels
#输出类似于此:
NAME STATUS ROLES AGE VERSION LABELS
k8s-node1 Ready control-plane 10d v1.26.0 beta.kubernetes.io/arch=arm64,beta.kubernetes.io/os=linux,disktype=ssd...
k8s-node2 Ready <none> 10d v1.26.0 beta.kubernetes.io/arch=arm64,beta.kubernetes.io/os=linux...
k8s-node3 Ready <none> 10d v1.26.0 beta.kubernetes.io/arch=arm64,beta.kubernetes.io/os=linux...
根据选择节点标签指派 pod 到指定节点[nodeSelector]
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
env: nginx
spec:
containers:
- name: nginx
image: nginx:1.19
imagePullPolicy: IfNotPresent
nodeSelector:
disktype: ssd # 选择节点为标签为 ssd 的节点
根据节点名称指派 pod 到指定节点[nodeName]
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
nodeName: worker1 # 调度 Pod 到特定的节点
containers:
- name: nginx
image: nginx:1.19
imagePullPolicy: IfNotPresent
根据 亲和性和反亲和性 指派 pod 到指定节点
官网地址: https://kubernetes.p2hp.com/docs/concepts/scheduling-eviction/assign-pod-node.html#affinity-and-anti-affinity
说明
nodeSelector 提供了一种最简单的方法来将 Pod 约束到具有特定标签的节点上。 亲和性和反亲和性扩展了你可以定义的约束类型。使用亲和性与反亲和性的一些好处有:
- 亲和性、反亲和性语言的表达能力更强。nodeSelector 只能选择拥有所有指定标签的节点。 亲和性、反亲和性为你提供对选择逻辑的更强控制能力。
- 可以标明某规则是“软需求”或者“偏好”,这样调度器在无法找到匹配节点时仍然调度该 Pod。
- 可以使用节点上(或其他拓扑域中)运行的其他 Pod 的标签来实施调度约束, 而不是只能使用节点本身的标签。这个能力让你能够定义规则允许哪些 Pod 可以被放置在一起。
亲和性功能由两种类型的亲和性组成:
- 节点亲和性功能类似于 nodeSelector 字段,但它的表达能力更强,并且允许你指定软规则。
- Pod 间亲和性/反亲和性允许你根据其他 Pod 的标签来约束 Pod。
节点亲和性概念上类似于 nodeSelector, 它使你可以根据节点上的标签来约束 Pod 可以调度到哪些节点上。 节点亲和性有两种:
requiredDuringSchedulingIgnoredDuringExecution
: 调度器只有在规则被满足的时候才能执行调度。此功能类似于 nodeSelector, 但其语法表达能力更强。preferredDuringSchedulingIgnoredDuringExecution
: 调度器会尝试寻找满足对应规则的节点。如果找不到匹配的节点,调度器仍然会调度该 Pod。
注意:在上述类型中,IgnoredDuringExecution 意味着如果节点标签在 Kubernetes 调度 Pod 后发生了变更,Pod 仍将继续运行。
apiVersion: v1
kind: Pod
metadata:
name: with-node-affinity
spec:
affinity:
nodeAffinity:
#节点必须包含一个键名为 ssd 的标签, 并且该标签的取值必须为 fast 或 superfast。
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: ssd
operator: In
values:
- fast
- superfast
containers:
- name: nginx
image: nginx:1.19
注意: 你可以使用 In、NotIn、Exists、DoesNotExist、Gt 和 Lt 之一作为操作符。NotIn 和 DoesNotExist 可用来实现节点反亲和性行为。
节点亲和性权重
可以为 preferredDuringSchedulingIgnoredDuringExecution 亲和性类型的每个实例设置 weight 字段,其取值范围是 1 到 100。
apiVersion: v1
kind: Pod
metadata:
name: with-node-affinity
spec:
affinity:
nodeAffinity:
#节点最好具有一个键名为 app 且取值为 demo 的标签。
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1 #取值范围是 1 到 100
preference:
matchExpressions:
- key: ssd
operator: In
values:
- fast
- weight: 50
preference:
matchExpressions:
- key: app
operator: In
values:
- demo
containers:
- name: nginx
image: nginx:1.19
pod 间亲和性和反亲和性及权重
与节点亲和性类似,Pod 的亲和性与反亲和性也有两种类型:
requiredDuringSchedulingIgnoredDuringExecution
preferredDuringSchedulingIgnoredDuringExecution
例如,你可以使用requiredDuringSchedulingIgnoredDuringExecution
亲和性来告诉调度器, 将两个服务的 Pod 放到同一个云提供商可用区内,因为它们彼此之间通信非常频繁。 类似地,可以使用preferredDuringSchedulingIgnoredDuringExecution
反亲和性来将同一服务的多个 Pod 分布到多个云提供商可用区中。
要使用 Pod 间亲和性,可以使用 Pod 规约中的 spec.affinity.podAffinity
字段。
对于 Pod 间反亲和性,可以使用 Pod 规约中的 spec.affinity.podAntiAffinity
字段。
apiVersion: v1
kind: Pod
metadata:
name: redis
labels:
app: redis
spec:
containers:
- name: redis
image: redis:5.0.10
imagePullPolicy: IfNotPresent
restartPolicy: Always
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
#更确切的说,调度器必须将 Pod 调度到具有 bj 标签的节点上,并且集群中至少有一个位于该可用区的节点上运行着带有 app=nginx 标签的 Pod。
- topologyKey: bj
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- nginx
- pod 间亲和性权重
apiVersion: v1
kind: Pod
metadata:
name: redis
labels:
app: redis
spec:
containers:
- name: redis
image: redis:5.0.10
imagePullPolicy: IfNotPresent
restartPolicy: Always
affinity:
podAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- podAffinityTerm:
topologyKey: bj
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- nginx
weight: 1
- podAffinityTerm:
topologyKey: bj
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- web
weight: 30
污点和容忍度
参考: http://kubernetes.p2hp.com/docs/concepts/scheduling-eviction/taint-and-toleration.html
Pod 拓扑分布约束
参考: http://kubernetes.p2hp.com/docs/concepts/scheduling-eviction/topology-spread-constraints/