概念
k8s是通过定义一个Pod的资源,然后在Pod里面运行容器,容器需要指定一个镜像,这样就可以用来运行具体的服务。一个Pod封装一个容器(也可以封装多个容器),Pod里的容器共享存储、网络等。也就是说,应该把整个pod看作虚拟机,然后每个容器相当于运行在虚拟机的进程。Pod是需要调度到k8s集群的工作节点来运行的,具体调度到哪个节点,是根据scheduler调度器实现的。
分为自主式的pod和控制器控制的pod,一般采用控制器来管理pod。
#一个pod的创建流程
1.客户端提交创建Pod的请求,可以通过调用API Server的Rest API接口,也可以通过kubectl命令行工具。如kubectl apply -f filename.yaml(资源清单文件)
2.apiserver接收到pod创建请求后,会将yaml中的属性信息(metadata)写入etcd。
3.apiserver触发watch机制准备创建pod,信息转发给调度器scheduler,调度器使用调度算法选择node,调度器将node信息给apiserver,apiserver将绑定的node信息写入etcd
4.apiserver又通过watch机制,调用kubelet,指定pod信息,调用Docker API创建并启动pod内的容器。
5.创建完成之后反馈给kubelet, kubelet又将pod的状态信息给apiserver,apiserver又将pod的状态信息写入etcd。
# scheduler调度器
调度器用一组规则过滤掉不符合要求的主机。比如Pod指定了所需要的资源量,那么可用资源比Pod需要的资源量少的主机会被过滤掉。
scheduler 查看 k8s api ,类似于通知机制。
首先判断:pod.spec.Node == null?
若为null,表示这个Pod请求是新来的,需要创建;因此先进行调度计算,找到最“闲”的node。
然后将信息在etcd数据库中更新分配结果:pod.spec.Node = nodeA (设置一个具体的节点)
资源清单文件
vim pod-xxxx.yaml
# 查看资源信息,根据提示写资源文件
kubectl explain Pod.spec.containers.xxxx
# 更新资源清单
kubectl apply -f pod-xxx.yaml
# 查看Pod资源(带有详细信息)
kubectl get pods -n <namespace> -o wide
# 查看有app=tomcat标签的Pod资源
kubectl get pods -n test -l app=tomcat
# 查看有app=tomcat和env=dev标签的Pod资源
kubectl get pods -n test -l app=tomcat,env=dev
apiVersion: v1
kind: Pod
metadata:
name: tomcat-test
namespace: test
labels:
app: tomcat
version: v1.0
env: dev
spec:
containers:
- name: tomcat-test
image: tomcat:8.5-jre8-alpine
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
# 资源书写详细解释
apiVersion: v1 # api版本(默认是v1)
kind: Pod # 资源类型,Pod类型
metadata:
name: tomcat-test # 资源名称(Pod的名字)
namespace: test # 资源所在的命名空间
labels: # 资源标签(自己定义区分使用)
app: tomcat
version: v1.0
env: dev
spec:
containers: # 容器列表
- name: tomcat-test # 容器的名称
image: tomcat:8.5-jre8-alpine # 使用的镜像
imagePullPolicy: IfNotPresent # 镜像拉取策略
ports: # 端口列表
- containerPort: 8080 # 容器端口
# kubectl explain Pod
apiVersion: 字符串值,定义了对象,代表了一个版本。
kind: 字符串类型的值,代表了要创建的资源。服务器可以从客户端提交的请求推断出这个资源。
metadata: 对象,定义元数据属性信息的
spec:对象,制定了定义Pod的规格,里面包含容器的信息
status:对象,表示状态,这个不可以修改,定义pod的时候也不需要定义这个字段
# kubectl explain Pod.metadata
annotations<map[string]string>:
map类型表示对应的值是key-value键值对,<string,string>表示 key和value都是String类型的。
用Annotation来记录的信息包括:
build信息、release信息、Docker镜像信息等,例如时间戳、release id号、镜像hash值、docker registry 地址等;
日志库、监控库、分析库等资源库的地址信息;
程序调试工具信息,例如工具名称、版本号等;
团队的联系信息,例如电话号码、负责人名称、网址等。
clusterName: 字符串,对象所属群集的名称。这是用来区分不同集群中具有相同名称和命名空间的资源。此字段现在未设置在任何位置,apiserver将忽略它,如果设置了就使用设置的值
labels<map[string]string>:map对象,创建的资源具有的标签,方便后续资源筛选识别
name<string>:创建的资源的名字
namespace<string>:创建的资源所属的名称空间。在同一个namesace下的资源名字是唯一的,默认的名称空间是default。
# kubectl explain pod.spec
activeDeadlineSeconds<integer>:表示Pod可以运行的最长时间,达到设置的值后,Pod会自动停止。
affinity<Object>:定义亲和性的
containers<[]Object> -required-:containers是对象列表,用来定义容器的,是必须字段。对象列表表示下面有很多对象,对象列表下面的内容用 - 连接,至少有一个容器对象。
# kubectl explain pod.spec.containers
image<string>:image是用来指定容器需要的镜像的
imagePullPolicy <string>:
镜像拉取策略,pod启动需要镜像,可以根据这个字段设置镜像拉取策略,支持如下三种:
Always:不管本地是否存在镜像,都要重新拉取镜像
Never: 从不拉取镜像
IfNotPresent:如果本地存在,使用本地的镜像,本地不存在,从官方拉取镜像
name<string> -required-:name是必须字段,用来指定容器名字的
ports<[]Object>:port是端口,属于对象列表
# kubectl explain pod.spec.containers.ports
containerPort <integer> -required-: containerPort是必须字段, pod中的容器需要暴露的端口。
hostIP <string>: 将容器中的服务暴露到宿主机的端口上时,可以指定绑定的宿主机 IP。
hostPort <integer>:容器中的服务在宿主机上映射的端口
###补充###
# 查看pod日志
kubectl logs pod-first
# 查看pod里指定容器的日志
kubectl logs pod-first -c tomcat-first
# 进入到刚才创建的pod,刚才创建的pod名字是web
kubectl exec -it pod-first -- /bin/bash
# 假如pod里有多个容器,进入到pod里的指定容器,按如下命令:
kubectl exec -it pod-first -c tomcat-first -- /bin/bash
# 通过命令行创建Pod(不推荐)
kubectl run tomcat --image=tomcat-8.5-jre8 --image-pull-policy='IfNotPresent' --port=8080
命名空间
又可称为虚拟集群,控制k8s集群级别的资源。适用于跨团队或者项目的场景。
## 使用命令 ##
# 系统内置命名空间 kube-system、default
# 创建一个命名空间
kubectl create namespace <ns-name>
# 查看命名空间&查看制定ns详细信息
kubectl get ns
kubectl describe ns <ns-name>
# 切换命名空间
kubectl config set-context --current --namespace=kube-system
# 查看指定命名空间的pod
kubectl get pods -n <ns-name>
#查看哪些资源属于命名空间级别的
kubectl api-resources --namespaced=true
可以对命名空间的硬件资源进行使用限制
vim namespace-quota.yaml
##每个容器必须设置内存请求(memory request),内存限额(memory limit),cpu请求(cpu request)和cpu限额(cpu limit)。
#所有容器的内存请求总额不得超过2GiB。
#所有容器的内存限额总额不得超过4 GiB。
#所有容器的CPU请求总额不得超过2 CPU。
#所有容器的CPU限额总额不得超过4CPU。
apiVersion: v1
kind: ResourceQuota
metadata:
name: mem-cpu-quota
namespace: test
spec:
hard:
requests.cpu: "2"
requests.memory: 2Gi
limits.cpu: "4"
limits.memory: 4Gi
# 创建pod时候必须设置资源限额,否则创建失败
vim pod-test.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-test
namespace: test
labels:
app: tomcat-pod-test
spec:
containers:
- name: tomcat-test
ports:
- containerPort: 8080
image: tomcat-8.5-jre8
imagePullPolicy: IfNotPresent
标签
其实就一对 key/value ,被关联到资源对象上,可以用来划分和标识特定的对象,方便后面对资源进行筛选使用等。
# 对已经存在的pod打标签 & 查看
kubectl label pods pod-first release=v1
kubectl get pods pod-first --show-labels
#列出默认名称空间下标签key是release的pod,不显示标签
kubectl get pods -l release
#列出默认名称空间下标签key是release、值是v1的pod,不显示标签
kubectl get pods -l release=v1
#列出默认名称空间下标签key是release的所有pod,并打印对应的标签值
kubectl get pods -L release
#查看所有名称空间下的所有pod的标签
kubectl get pods --all-namespace --show-labels
kubectl get pods -l release=v1 -L release
节点选择器
可以使Pod调度到指定名称/特点的node上
nodeName
# 定义一个pod,将其调度到node1上
vim pod-test.yaml
kubectl apply -f pod.test.yaml
kubectl get pods -o wide
apiVersion: v1
kind: Pod
metadata:
name: demo-pod
namespace: default
labels:
app: myapp
env: dev
spec:
nodeName: node1
containers:
- name: tomcat-pod-java
ports:
- containerPort: 8080
image: tomcat:8.5-jre8-alpine
imagePullPolicy: IfNotPresent
- name: busybox
image: busybox:latest
command:
- "/bin/sh"
- "-c"
- "sleep 3600"
nodeSelector
# 给node打标签
kubectl label nodes node2 disk=ceph
# 定义一个pod,将其调度到有disk=ceph标签的节点上
vim pod-test.yaml
kubectl apply -f pod-test.yaml
kubectl get pods -o wide
apiVersion: v1
kind: Pod
metadata:
name: demo-pod-1
namespace: default
labels:
app: myapp
env: dev
spec:
nodeSelector:
disk: ceph
containers:
- name: tomcat-pod-java
ports:
- containerPort: 8080
image: tomcat:8.5-jre8-alpine
imagePullPolicy: IfNotPresent
节点亲和度和反亲和度
节点亲和性
pod与node的关系,是将pod调度到node时候的干预策略
# kubectl explain pods.spec.affinity
nodeAffinity <Object> 节点亲和度
podAffinity <Object> pod亲和度
podAntiAffinity <Object> pod反亲和度
# kubectl explain pods.spec.affinity.nodeAffinity
表示有节点尽量满足这个位置定义的亲和性,这不是一个必须的条件,软亲和性
preferredDuringSchedulingIgnoredDuringExecution <[]Object>
表示必须有节点满足这个位置定义的亲和性,这是个硬性条件,硬亲和性
requiredDuringSchedulingIgnoredDuringExecution <Object>
# kubectl explain pods.spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms
matchExpressions <[]Object> 匹配表达式的
matchFields <[]Object> 匹配字段的
# kubectl explain pods.spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms.matchFields
key <string> -required-
values <[]string>
# kubectl explain pods.spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms.matchExpressions
key <string> -required- 检查label
operator <string> -required- 做等值选则还是不等值选则
values <[]string> 给定值
# 给node1打标签
kubectl label nodes node1 zone=foo
# 创建一个pod,检查当前节点中有任意一个节点拥有zone标签的值是foo或者bar,就可以把pod调度到这个 (硬亲和性)
# 硬亲和性:必须要有这个标签的节点,如果没有,则pod状态是pending
# 软亲和性:指定这个标签的节点可有可无,如果没有,则pod则调度到任意一个节点
vim pod-test.yaml
kubectl apply -f pod.test.yaml
kubectl get pods -o wide | grep pod-node
apiVersion: v1
kind: Pod
metadata:
name: pod-node-affinity-demo
namespace: default
labels:
app: myapp
tier: frontend
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: zone
operator: In
values:
- foo
- bar
# 测试软亲和性
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecutio:
nodeSelectorTerms:
- matchExpressions:
- key: zone1
operator: In
values:
- foo1
- bar1
pod亲和性和反亲和性
亲和性:pod和pod之间的相近关系,pod倾向于调度到与自己“相似”或者“亲戚”的pod所在节点。相似手段可以使标签、IP等。
# 与上述一样,同样有软硬亲和度
# kubectl explain pods.spec.affinity.podAffinity.requiredDuringSchedulingIgnoredDuringExecution
labelSelector <Object>
namespaces <[]string>
topologyKey <string> -required-
# topologyKey:
位置拓扑的键,这个是必须字段
怎么判断是不是同一个位置:
rack=rack1 使用rack的键是同一个位置
row=row1 使用row的键是同一个位置
# labelSelector:
我们要判断pod跟别的pod亲和,跟哪个pod亲和,需要靠labelSelector,通过labelSelector选则一组能作为亲和对象的pod资源
# namespace:
labelSelector需要选则一组资源,那么这组资源是在哪个名称空间中呢,通过namespace指定,如果不指定namespaces,那么就是当前创建pod的名称空间
# kubectl explain pods.spec.affinity.podAffinity.requiredDuringSchedulingIgnoredDuringExecution.labelSelector
matchExpressions <[]Object>
matchLabels <map[string]string>
# 定义两个pod,第一个作为基准,第二个跟着它走
# 表示创建的pod必须与拥有app=myapp标签的pod在一个节点上
vim pod-test-affinity.yaml
kubectl apply -f pod-test.yaml
kubectl get pods -o wide
apiVersion: v1
kind: Pod
metadata:
name: pod-first
labels:
app: myapp
tier: frontend
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
---
apiVersion: v1
kind: Pod
metadata:
name: pod-second
labels:
app: backend
tier: db
spec:
containers:
- name: busybox
image: busybox:latest
imagePullPolicy: IfNotPresent
command: ["sh","-c","sleep 3600"]
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- {key: app, operator: In, values: ["myapp"]}
topologyKey: kubernetes.io/hostname
# 如何查看topologyKey
kubectl get nodes --show-labels
# 测试反亲和性
# 第二个pod调度到与第一个pod不一样的节点上
affinity:
podAntAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- {key: app, operator: In, values: ["myapp"]}
topologyKey: kubernetes.io/hostname
# 测试换一个topologyKey 【??】
kubectl label nodes node2 zone=foo
kubectl label nodes node1 zone=foo --overwrite
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- {key: app3 ,operator: In, values: ["myapp3"]}
topologyKey: zone