6 Contrainer 特性
6.1 容器生命周期
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
状态之前执行。
6.2 容器生命周期回调/事件/钩子
类似于许多具有生命周期回调组件的编程语言框架,例如 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 postStop >> /stop.txt && sleep 5"]
ports:
- containerPort: 80
6.3 容器重启策略
Pod 的 spec
中包含一个 restartPolicy
字段,其可能取值包括 Always(总是重启)、OnFailure(容器异常退出状态码非 0,重启) 和 Never
。默认值是 Always
。
restartPolicy
适用于 Pod 中的所有容器。restartPolicy
仅针对同一节点上 kubelet
的容器重启动作。当 Pod 中的容器退出时,kubelet
会按指数回退方式计算重启的延迟(10s、20s、40s、...),其最长延迟为 5 分钟。 一旦某容器执行了 10 分钟并且没有出现问题,kubelet
对该容器的重启回退计时器执行重置操作。
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.19
imagePullPolicy: IfNotPresent
restartPolicy: Always # OnFailure Never
6.4 自定义容器启动命令
和 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"] # 用来为启动命令传递参数
#args: ["redis-server","--appendonly yes"] # 单独使用修改启动命令并传递参数
#args: # 另一种语法格式
# - redis-server
# - "--appendonly yes"
imagePullPolicy: IfNotPresent
restartPolicy: Always
6.5 容器探针
probe 是由 kubelet对容器执行的定期诊断。 要执行诊断,kubelet 既可以在容器内执行代码,也可以发出一个网络请求。
定义: 容器探针 就是用来定期对容器进行健康检查的。
探测类型
针对运行中的容器,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 检查。如果端口打开,则诊断被认为是成功的。 如果远程系统(容器)在打开连接后立即将其关闭,这算作是健康的。
探针结果
每次探测都将获得以下三种结果之一:
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,第一次检测发现失败,但是第二次检测发现成功后容器就一直处于健康状态不会重启。 1. 如果 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/
6.6 资源限制
在k8s中对于容器资源限制主要分为以下两类:
内存资源限制: 内存请求(request)和内存限制(limit)分配给一个容器。 我们保障容器拥有它请求数量的内存,但不允许使用超过限制数量的内存。
CPU 资源限制: 为容器设置 CPU request(请求) 和 CPU limit(限制)。 容器使用的 CPU 不能超过所配置的限制。 如果系统有空闲的 CPU 时间,则可以保证给容器分配其所请求数量的 CPU 资源。
请求 request memory cpu :可以使用的基础资源 100M
限制 limit memory cpu :可以使用的最大资源 200M 超过最大资源之后容器会被 kill , OOM 错误
1 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
2 指定内存请求和限制
官网: 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,没有资源限制的容器将被杀掉的可行性更大。
- 运行的容器所在命名空间有默认的内存限制,那么该容器会被自动分配默认限制。
3 指定 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
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 也会为其设置与内存限制值相同的内存请求。
7 Pod 中 init 容器
Init 容器是一种特殊容器,在Pod 内的应用容器启动之前运行。Init 容器可以包括一些应用镜像中不存在的实用工具和安装脚本。
7.1 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 容器对资源请求和限制的处理稍有不同。
7.2 使用 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']
- 查看启动详细
$ kubectl describe pod init-demo
# 部分结果
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 2m16s default-scheduler Successfully assigned default/init-demo to k8s-node2
Normal Pulling 2m16s kubelet Pulling image "busybox:1.28"
Normal Pulled 118s kubelet Successfully pulled image "busybox:1.28" in 17.370617268s (17.370620685s including waiting)
Normal Created 118s kubelet Created container init-myservice
Normal Started 118s kubelet Started container init-myservice
Normal Pulled 112s kubelet Container image "busybox:1.28" already present on machine
Normal Created 112s kubelet Created container init-mydb
Normal Started 112s kubelet Started container init-mydb
Normal Pulled 101s kubelet Container image "busybox:1.28" already present on machine
Normal Created 101s kubelet Created container myapp-container
Normal Started 101s kubelet Started container myapp-container
评论