0%

pod详解

pod是k8s集群中的最小调度单元,一个pod中可以有多个容器。k8s引入pod,而不直接对容器进行调度的原因有如下两个:

  • 一个是为了将容器的实现和k8s平台自身引擎的实现进行解耦,从而做到可以支持多种类型的容器(docker、rkt)

  • 另外一个是可以让多个容器共享网络、存储、进程空间,减少资源消耗

使用yaml定义一个pod

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
26
27
28
29
30
apiVersion: v1
kind: pod
metadata:
name: #pod name
namespace: #命名空间,如果无该字段,则使用默认命名空间
labels: #pod标签,可选
key1: value1
key2: value2
spec:
volumes: # 挂载目录,有多种挂载方式
containers:
- name: #容器的名称
image: #容器使用的镜像
ports: # 容器访问
- containerPort:
env: # 容器环境变量
- name:
value:
- name:
value:

- name:
image:
ports:
- containerPort:
env:
- name:
value:
- name:
value:

定义好一个pod描述之后,就可以使用kubectl create -f xxx.yaml来创建一个pod

pod的常用操作

  • 查看pod被调度的节点已经pod ip

    1
    kubectl -n <namespace> get pod -o wide
  • 查看pod的配置

    1
    kubectl -n <namespace> get pod <pod name> -o <yaml|json>
  • 查看pod的信息及事件

    1
    kubectl -n <namespace> describe pod <pod name>
  • 进入pod内的容器

    1
    kubectl -n <namespace> exec <pod name> -c <container name> -it /bin/bash
  • 查看pod内容器日志,显示标准或者错误输出日志

    1
    kubectl -n <namespace> logs -f <pod name> -c <container name>
  • 更新pod

    1
    kubectl apply -f <pod yaml file> # 更新pod是使用更新yaml文件的形式
  • 删除pod

    1
    2
    kubectl delete -f <pod yaml file> # 根据配置文件删除
    kubectl -n <namespace> delete pod <pod name> # 根据pod name删除

Pod健康检查

pod的健康检查由kubelet来进行,pod健康检查有两种机制:

  • LivenessProbe:存活性探测,用于判断容器是否存活,即pod是否为running状态。如果LivenessProbe探针探测到容器不健康,则kubelet将kill掉容器,并根据容器的重启策略是否重启(如果不配置,默认会进行重启),如果一个容器不包含LivenessProbe探针,则kubelet认为容器的LivenessProbe探针的返回值永远成功,即任务容器是健康的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    ...
    containers:
    - name:
    image:
    livenessProbe:
    httpGet: #有三种类型:exec(执行脚本,脚本返回0表示健康)、
    #httpGet(返回200-399状态码表示健康)、
    #tcpSocket(如果能够建立TCP连接,则表示健康)
    path:
    port:
    scheme:
    initialDelaySeconds: # 容器启动后多少秒后第一次执行探测
    periodSeconds: # 多少秒执行一次探,默认10s,最小1s,
    timeoutSeconds: # 探测超时时间默认1s,最小1
    successThreshold: # 连续探测成功多少次才被认为是成功,默认为为1
    failureThreshold: # 连续探测失败多少次才被任务是失败,默认为3
  • ReadinessProbe:可用性探测,用于判断容器是否正常提供服务,即容器的Ready是否为True,是否可以接收请求。如果ReadinessProbe探测失败,则容器的ready将为False,Endpoint Controller控制器会将此Pod的Endpoint从对应的service的Endpoint列表中移除,不再将任何请求调度到此Pod上,直到下次探测成功。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    ...
    containers:
    - name:
    image:
    readinessProbe:
    httpGet:
    path:
    port:
    scheme:
    initialDelaySeconds: # 容器启动后多少秒后第一次执行探测
    periodSeconds: # 多少秒执行一次探测
    timeoutSeconds: # 探测超时时间
    ...
对于同一个容器,两种健康检查可以同时设置。

重启策略

Pod的重启策略应用于Pod内的所有容器,并且仅在Pod所处的Node上由Kubelet进行判断和重启操作。当某个容器异常退出或者健康检查失败时,kubelet将根据RestartPolicy的设置来进行相应的操作。Pod的重启策略包括Always、OnFailure和Never,默认值为Always。 * Always:当容器进程退出后,由kubelet自动重启该容器 * OnFailure:当容器进程终止运行且退出码不为0时,由kubelet自动重启该容器 * Never:不论容器运行状态如何,kubelet都不会重启该容器
1
2
3
4
...
spec:
restartPolicy: OnFailure
containers:
# 镜像拉取策略 有如下几种镜像拉取策略: * Always:总是拉取镜像,即使本地有镜像也从镜像仓库拉取 * IfNotPresent:本地有则使用本地镜像,本地没有则去仓库拉取,默认的镜像拉取策略 * Never:只使用本地镜像,本地没有则报错
1
2
3
4
5
6
...
spec:
containers:
- name: myblog
image:
imagePullPolicy: IfNotPresent
# Pod资源限制 为了保证充分利用集群资源,且确保重要容器在运行周期内能够分配到足够的资源稳定运行,因此平台需要具备Pod的资源限制的能力。对于一个Pod来说,资源最基础的2个指标就是CPU和内存。资源限制有两种类型: * requests:容器使用的最小资源要求,作用于schedule阶段,作为容器调度时分配的判断依赖,只有当节点上可分配的资源量>=request时,才允许将容器调度到该节点。requests参数不限制容器的最大可用资源。requests.cpu被转成docker的--cpu-shares参数,与cgroup.cpu.shared功能相同。requests.memory没有对应的docker参数,仅作为k8s调度的依据 * limits:容器使用的最大资源限制,不设置或者设置为0表示对使用的资源不做限制。当pod内存超过limit时,会被oom,当cpu超过limit时,不会被kill,但会被限制不超过limit值(因为cpu的时间片是动态调度)。limits.cpu会被转换成docker的-cpu-quota参数,与cgroup.cpu.cfs\_quota_us功能相同。limits.memory会被转换成docker的-memory参数,用来限制容器使用的最大内存。
1
2
3
4
5
6
7
8
9
10
11
12
...
spec:
containers:
- name:
image:
resources:
requests:
memory: 100Mi
cpu: 500m # 等同于0.5
limits:
memory: 500Mi
cpu: 2
# configMap和secret configMap和Secret是k8s提供的两种资源类型,它们可以用来实现业务配置的统一管理。一般来说会使用configMap存储一些不包含敏感信息的基本配置,secret用于管理敏感信息配置,例如密码、密钥等。 使用yaml定义一个configMap资源:
1
2
3
4
5
6
7
8
apiVersion: v1
kind: ConfigMap
metadata:
name:
namespace:
data:
key1: value1
key2: value2
secret有三种类型: * Service Accout:用来访问k8s API,由k8s 自动创建,并且会自动挂载到pod的/run/secrets/kubernetes.io/serveraccount目录中; * Opaque:base64编码的Secret,用来存储密码,密钥等 * kubernetes.io/dockerconfigjson:用来存储私有docker registry的认证信息
1
2
3
4
5
6
7
8
9
apiVersion: v1
kind: Secret
metadata:
name:
namespace:
type: Opaque
data:
key1: base64_encoded_value1
key2: base64_encoded_value2
configMap和secret的使用:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
spec:
containers:
- name:
image:
env:
- name: env_name_1
valueFrom:
secretKeyRef:
name: <secret resouce name>
key: <secret resouce key>
- name: env_name_2
valueFrom:
configMapKeyRef:
name: <config map resource name>
key: <config map resource key>
# pod生命周期 一个pod可能处于如下的几种状态 | 状态值 | 描述 | | ---------------------- | ---------------------------------------- | | Pending | API Server已经创建该Pod,等待调度器调度 | | ContainerCreating | 拉取镜像启动容器中 | | Runing | Pod内容器均已创建,且至少有一个容器处于运行状态、正在启动状态或者正在重启状态 | | Succeeded \| Completed | 容器内所有容器均已成功执行退出,且不再重启 | | Failed \| Error | Pod内所有容器均已退出,但至少有一个容器退出为失败状态 | | CrashLoopBackOff | Pod内容器启动失败,比如配置文件丢失导致进程启动失败 | | Unknown | 由于某种原因无法获取该Pod的状态,可能由于网络通信不畅导致 | pod支持两种hook,post-start hook 和pre-stop hook
1
2
3
4
5
6
7
8
9
10
11
12
...
spec:
containers:
- name:
image:
lifecycle:
postStart:
exec:
command:
preStop:
exec:
command:
pre-stop hook只有在手动kill掉pod才会触发,如果是Pod自己down掉,则不会触发pre-stop hook # Pod控制器(workload工作负载) workload是用于实现管理pod的中间层,确保pod资源符合预期的状态,pod的资源出现故障时,会尝试进行重启,当根据重启策略无效时,则会重新新建pod资源。workload有以下几种类型: * ReplicaSet:pod副本数量,确保pod副本数量符合预期状态,并且支持滚动式自动阔缩容 * Deployment:工作在ReplicaSet之上,用于管理无状态应用,目前来说最好的控制器,支持滚动更新和回滚功能 * DaemonSet:用于确保集群中的每一个节点只运行特定的pod服务,通常用于实现系统级后台服务 * Job:只要完成就立即退出,不需要重启或重建 * Cronjob:周期性任务控制,不需要持续后台运行 * StatefulSet:管理有状态应用 ## Deployment 使用yaml定义一个deployment
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
apiVersion: apps/v1
kind: Deployment
metadata:
name:
namespace:
spec:
replicas: 2 # 指定pod副本数
selector: # pod选择器,有对应label的pod都会归于这个deployment管理,不是pod的node selector
matchLabels:
podLabelKey1: podLabelValue
template: # pod 模版,同定义pod的yaml文件
metadata:
labels:
podLabelKey1: podLabelValue #这里定义的lable的key/value要和上面的select字段定义的相对应
spec:
nodeSelector:
nodeLabelKey: nodeLabelValue
containers:
- name:
image:
env:
deployment阔缩容命令:
1
kubectl -n <namespace> scale deployment <deployment name> --replicas=<n>
deployment服务更新(镜像更新) * 通过yaml配置文件的方式(建议)
1
kubectl -n <namespace> apply -f <xxx.yaml>
* 通过命令行直接更新
1
kubectl -n <namespace> set image deployment <deployment name> <container name>=<image>
deploy服务回滚 回滚只能通过命令行进行回滚,不能通过配置文件进行回滚 * 查看可回滚的历史记录
1
kubectl -n <namespace> rollout history deployment <deployment name>
* 回滚
1
kubectl -n <namespace> rollout undo deployment <deployment name> --to-revision=<revsion number>
### deployment更新(回滚)策略配置 deployment的更新(回滚)是滚动更新(回滚)的,即一次只进行一部分pod更新(回滚),直到所有的pod都完成更新(回滚),deployment的更新(回滚)策略有以下几种可配置项: * maxSurge:最大激增数,指更新过程中,最多可以比replicas预先设定值多出的pod数量,可以为固定值或百分比,默认为25%,计算时向上取整 * maxUnavailable:指更新过程中,最多有几个pod处于无法服务状态,可以为固定值或百分比,默认为25%,计算时向下取整 deployment更新的底层实现其实是新建了一个ReplicaSet,这个ReplicaSet使用的最新版本的镜像,然后通过调整新ReplicaSet和老ReplicaSet的副本数来实现滚动升级。同时老的ReplicaSet不会立即删除,目的是为了方便回滚。
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
apiVersion: apps/v1
kind: Deployment
metadata:
name:
namespace:
spec:
replicas: 2
selector:
matchLabels:
podLabelKey1: podLabelValue
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
template:
metadata:
labels:
podLabelKey1: podLabelValue
spec:
nodeSelector:
nodeLabelKey: nodeLabelValue
containers:
- name:
image:
env:

Service负载均衡之Cluster IP

通过deployment,我们已经可以创建一组Pod来提供具有高可用性的服务,虽然每个Pod都会分配一个单独的Pod IP, 然而却存在如下两个问题:

  • Pod IP仅仅是集群内部的虚拟IP,在集群内部可以访问,外部却无法访问

  • Pod IP会随着Pod的销毁而消失,当ReplicaSet对Pod进行动态伸缩容时,Pod IP可能随时会发生变化,这样对于我们访问这个服务带来了难度

Services是一组pod服务的抽象,相当于一组pod的load balancer,负责将请求分发到对应的pod。service会有一个IP,一般称为cluster ip,service对象通过selector进行标签选择,找到到对应的pod

使用yaml定义一个service

1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: v1
kind: Service
metadata:
name:
namespace:
spec:
ports:
- port: 80 # service 端口
protocol: TCP
targetPort: 8002 # pod 端口
selector:
podLabelName: podLabelValue
type: ClusterIP

创建好service后,在集群内部就可以直接使用service name + pod对服务进行访问,因为集群内部的dns会记录相关解析规则

Service负载均衡之NodePort

Cluster IP也是一个虚拟地址,其目的是为了方便集群内部服务直接的通信,只能在k8s集群内部进行访问,如果需要集群外部访问集群内部服务,实现方式之一为使用NodePort方式。NodePort会默认在30000—32767之间,不指定会随机使用其中的一个。

1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: v1
kind: Service
metadata:
name:
namespace:
spec:
ports:
- port: 80
protocol: TCP
targetPort: 8002
selector:
podLabelName: podLabelValue
type: NodePort

kube-proxy

kube-proxy运行在每个节点上,监听API Server中服务对象的变化,再通过创建流量路由规则来实现网络的转发。kube-proxy支持三种模式:

  • User space:让kube-proxy 在用户空间监听一个端口,所有的service都转发到这个端口,然后kube-proxy在内部应用层对其进行转发,所有报文都走一遍用户态,性能不高,在k8s v1.2版本后废弃

  • Iptables:当前默认模式,完全由iptables来实现,通过各个节点上的iptable规则来实现service的负载均衡,但是随着service数量增大,iptables模式由于线性查找匹配、全量更新等特点,其性能会显著下降

  • IPVS:与iptables同样基于Netfilter,但是采用的hash表,因此当service数量达到一定规模时,hash查表的速度优势就会显现出来,从而提高service的服务性能。k8s 1.8版本开始引入,1.11版本开始稳定,需要开启宿主机的ipvs模块

Ingress

对于k8s的service,无论是Cluster-IP还是NodePort的形式,都是四层的负载,集群内的服务如果实现7层的负载均衡,这就需要借助与Ingress。Ingress控制器的实现方式有很多,例如nginx、contour、haproxy,trafik,istio。

ingress-nginx是7层的负载均衡器,根据用户编写的ingress规则(创建的ingress的yaml文件),动态的去更改nginx服务的配置文件。

1
2
3
4
5
6
7
8
9
10
11
12
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name:
spec:
rules:
- host: # 域名host
http:
- paths: /
backend:
serviceName: <service name>
servicePort: <service port>

ingress实现逻辑

  • ingress controller通过api server,监听集群中ingress规则变化

  • 然后读取ingress规则(规则就是写明了哪个域名对应哪个service),按照自定义的规则,生成一段nginx配置

  • 再写到nginx-ingress-contoller的pod里,nginx-ingress-controller的pod里运行着Nginx服务