4.最小调度单元 Pod
docker调度的是容器,在k8s集群中,最小的调度单元是Pod(豆荚)
为什么引入Pod
- 与容器引擎解耦
Docker、Rkt。平台设计与引擎的具体的实现解耦
- 多容器共享网络|存储|进程 空间, 支持的业务场景更加灵活
docker run -d -p 3306:3306 --name mysql -v /opt/mysql:/var/lib/mysql -e MYSQL_DATABASE=myblog -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7 --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
docker run -d -p 8002:8002 --name myblog -e MYSQL_HOST=172.16.200.22 -e MYSQL_USER=root -e MYSQL_PASSWD=123456 myblog:v1
使用yaml格式定义Pod
myblog/one-pod/pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: myblog
namespace: myblog
labels:
component: myblog
spec:
containers:
- name: myblog
image: registry.cn-hangzhou.aliyuncs.com/fxkjnj-study/myblog:v1
env:
- name: MYSQL_HOST # 指定root用户的用户名
value: "127.0.0.1"
- name: MYSQL_PASSWD
value: "123456"
ports:
- containerPort: 8002
- name: mysql
image: registry.cn-hangzhou.aliyuncs.com/fxkjnj-study/mysql:5.7-utf8
ports:
- containerPort: 3306
env:
- name: MYSQL_ROOT_PASSWORD
value: "123456"
- name: MYSQL_DATABASE
value: "myblog"
使用json格式定义Pod
myblog/one-pod/pod.json
{
"apiVersion": "v1",
"kind": "Pod",
"metadata": {
"name": "myblog",
"namespace": "myblog",
"labels": {
"component": "myblog"
}
},
"spec": {
"containers": [
{
"name": "myblog",
"image": "registry.cn-hangzhou.aliyuncs.com/fxkjnj-study/myblog:v1",
"env": [
{
"name": "MYSQL_HOST",
"value": "127.0.0.1"
},
{
"name": "MYSQL_PASSWD",
"value": "123456"
}
],
"ports": [
{
"containerPort": 8002
}
]
},
{
"name": "mysql",
...
}
]
}
}
apiVersion | 含义 |
---|---|
alpha | 进入K8s功能的早期候选版本,可能包含Bug,最终不一定进入K8s |
beta | 已经过测试的版本,最终会进入K8s,但功能、对象定义可能会发生变更。 |
stable | 可安全使用的稳定版本 |
v1 | stable 版本之后的首个版本,包含了更多的核心对象 |
apps/v1 | 使用最广泛的版本,像Deployment、ReplicaSets都已进入该版本 |
资源类型与apiVersion对照表
[root@k8s-master01 ~]# kubectl get apiservices
NAME SERVICE AVAILABLE AGE
v1. Local True 161d
v1.admissionregistration.k8s.io Local True 161d
v1.apiextensions.k8s.io Local True 161d
v1.apps Local True 161d
v1.authentication.k8s.io Local True 161d
v1.authorization.k8s.io Local True 161d
v1.autoscaling Local True 161d
v1.batch Local True 161d
v1.certificates.k8s.io Local True 161d
v1.coordination.k8s.io Local True 161d
v1.crd.projectcalico.org Local True 161d
v1.discovery.k8s.io Local True 161d
v1.enterprise.gloo.solo.io Local True 155d
v1.events.k8s.io Local True 161d
v1.gateway.solo.io Local True 155d
v1.gloo.solo.io Local True 155d
v1.kuboard.cn Local True 30d
v1.networking.k8s.io Local True 161d
v1.node.k8s.io Local True 161d
v1.policy Local True 161d
v1.rbac.authorization.k8s.io Local True 161d
v1.scheduling.k8s.io Local True 161d
v1.storage.k8s.io Local True 161d
v1alpha1.ratelimit.solo.io Local True 155d
v1beta1.batch Local True 161d
v1beta1.discovery.k8s.io Local True 161d
v1beta1.events.k8s.io Local True 161d
v1beta1.flowcontrol.apiserver.k8s.io Local True 161d
v1beta1.metrics.k8s.io kube-system/metrics-server True 29d
v1beta1.node.k8s.io Local True 161d
v1beta1.policy Local True 161d
v1beta1.storage.k8s.io Local True 161d
v1beta2.flowcontrol.apiserver.k8s.io Local True 161d
v2.autoscaling Local True 161d
v2beta1.autoscaling Local True 161d
v2beta2.autoscaling Local True 161d
Kind | apiVersion |
---|---|
ClusterRoleBinding | rbac.authorization.k8s.io/v1 |
ClusterRole | rbac.authorization.k8s.io/v1 |
ConfigMap | v1 |
CronJob | batch/v1beta1 |
DaemonSet | extensions/v1beta1 |
Node | v1 |
Namespace | v1 |
Secret | v1 |
PersistentVolume | v1 |
PersistentVolumeClaim | v1 |
Pod | v1 |
Deployment | v1、apps/v1、apps/v1beta1、apps/v1beta2 |
Service | v1 |
Ingress | extensions/v1beta1 |
ReplicaSet | apps/v1、apps/v1beta2 |
Job | batch/v1 |
StatefulSet | apps/v1、apps/v1beta1、apps/v1beta2 |
快速获得资源和版本信息
$ kubectl explain pod
$ kubectl explain Pod.apiVersion
创建和访问Pod
使用上面的pod.yaml 创建
## 创建namespace, namespace是逻辑上的资源池
$ kubectl create namespace myblog
## 使用指定文件创建Pod
$ kubectl create -f pod.yaml
## 查看pod,可以简写po
## 所有的操作都需要指定namespace,如果是在default命名空间下,则可以省略
$ kubectl -n myblog get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE
myblog 2/2 Running 0 3m 10.244.1.146 k8s-node1
## 使用Pod Ip访问服务,3306和8002
$ curl 10.244.1.146:8002/blog/index/
## 进入容器,执行初始化, 不必到对应的主机执行docker exec
$ kubectl -n myblog exec -ti myblog -c myblog bash
/ # env
/ # python3 manage.py migrate
$ kubectl -n myblog exec -ti myblog -c mysql bash
/ # mysql -p123456
## 再次访问服务,3306和8002
$ curl 10.244.1.146:8002/blog/index/
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<h3>我的博客列表:</h3>
</br>
</br>
<a href=" /blog/article/edit/0 ">写博客</a>
</body>
Pause(Infra)容器
$ docker ps -a |grep myblog ## 发现有三个容器
## 其中包含mysql和myblog程序以及Pause(Infra)容器
## 为了实现Pod内部的容器可以通过localhost通信,每个Pod都会启动Pause(Infra)容器容器,然后Pod内部的其他容器的网络空间会共享该Pause(Infra)容器的网络空间(Docker网络的container模式),Pause(Infra)容器只需要hang住网络空间,不需要额外的功能,因此资源消耗极低。
## 登录master节点,查看pod内部的容器ip均相同,为pod ip
$ kubectl -n myblog exec -ti myblog -c myblog bash
/ # ifconfig
$ kubectl -n myblog exec -ti myblog -c mysql bash
/ # ifconfig
pod容器命名: k8s_<container_name>_<pod_name>_<namespace>_<pod-UUID>
查看Pod详细信息
## 查看pod调度节点及pod_ip
$ kubectl -n myblog get pods -o wide
## 查看完整的yaml
$ kubectl -n myblog get po myblog -o yaml
## 查看pod的明细信息及事件
$ kubectl -n myblog describe pod myblog
## 查看pod中容器的日志
#进入Pod内的容器
$ kubectl -n <namespace> exec <pod_name> -c <container_name> -ti /bin/sh
#查看Pod内容器日志,显示标准或者错误输出日志
$ kubectl -n <namespace> logs -f <pod_name> -c <container_name>
在线更新Pod配置
$ kubectl apply -f demo-pod.yaml
删除Pod服务
#根据文件删除
$ kubectl delete -f demo-pod.yaml
#根据pod_name删除
$ kubectl -n <namespace> delete pod <pod_name>
Pod环境变量
变量值几种定义方式:
- 自定义变量值
- 变量值从Pod属性获取
- 变量值从Secret获取
- 变量值从ConfigMap获取
自定义变量值
apiVersion: v1
kind: Pod
metadata:
name: myblog
namespace: myblog
labels:
component: myblog
spec:
containers:
- name: myblog
image: registry.cn-hangzhou.aliyuncs.com/fxkjnj-study/myblog:v1
env:
- name: MYSQL_HOST # 指定root用户的用户名
value: "127.0.0.1"
- name: MYSQL_PASSWD
value: "123456"
ports:
- containerPort: 8002
变量值从Pod属性获取
apiVersion: v1
kind: Pod
metadata:
name: dapi-envars-fieldref
spec:
containers:
- name: test-container
image: busybox
command: [ "sh", "-c"]
args:
- while true; do
echo -en '\n';
printenv MY_NODE_NAME MY_POD_NAME MY_POD_NAMESPACE;
printenv MY_POD_IP MY_POD_SERVICE_ACCOUNT;
sleep 10;
done;
env:
- name: MY_NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
- name: MY_POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: MY_POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: MY_POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
- name: MY_POD_SERVICE_ACCOUNT
valueFrom:
fieldRef:
fieldPath: spec.serviceAccountName
restartPolicy: Never
变量值从Secret获取
Secret 是K8S资源的一种,请看这个链接XXXXXXX
一般用于 用户名密码的凭证
$ cat secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: myblog
namespace: myblog
type: Opaque
data:
MYSQL_USER: cm9vdA== #注意加-n参数, echo -n root | base64
MYSQL_PASSWD: MTIzNDU2 #注意加-n参数, echo -n 123456 | base64
$ kubect apply -f secret.yaml
$ cat mysql-with-config.yaml
apiVersion: v1
kind: Pod
metadata:
name: mysql
namespace: myblog
labels:
component: mysql
spec:
hostNetwork: true # 声明pod的网络模式为host模式,效果同docker run --net=host
volumes:
- name: mysql-data
hostPath:
path: /opt/mysql/data
nodeSelector: # 使用节点选择器将Pod调度到指定label的节点
component: mysql
containers:
- name: mysql
image: registry.cn-hangzhou.aliyuncs.com/fxkjnj-study/mysql:5.7-utf8
ports:
- containerPort: 3306
env:
- name: MYSQL_USER
valueFrom:
secretKeyRef:
name: myblog
key: MYSQL_USER
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: myblog
key: MYSQL_PASSWD
- name: MYSQL_DATABASE
value: "myblog"
volumeMounts:
- name: mysql-data
mountPath: /var/lib/mysql
变量值从ConfigMap获取
ConfigMap 是K8S资源的一种,请看这个链接XXXXXXX
一般用于配置文件的保存
$ cat configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: myblog
namespace: myblog
data:
MYSQL_HOST: "172.16.200.21"
MYSQL_PORT: "3306"
$ kubectl apply -f configmap.yaml
$ cat myblog-with-config.yaml
apiVersion: v1
kind: Pod
metadata:
name: myblog
namespace: myblog
labels:
component: myblog
spec:
containers:
- name: myblog
image: registry.cn-hangzhou.aliyuncs.com/fxkjnj-study/myblog:v1
imagePullPolicy: IfNotPresent
env:
- name: MYSQL_HOST
valueFrom:
configMapKeyRef:
name: myblog
key: MYSQL_HOST
- name: MYSQL_PORT
valueFrom:
configMapKeyRef:
name: myblog
key: MYSQL_PORT
ports:
- containerPort: 8002
Pod数据持久化
若删除了Pod,由于mysql的数据都在容器内部,会造成数据丢失,因此需要数据进行持久化。
- 这里使用hostPath挂载,需要使用 nodeSelector 给节点打上标签
其他持久化方式,使用PV+PVC连接分布式存储解决方案 等,[这里只简要说下hostPath的持久化方式,K8S的存储请看这个链接XXXXXXX]
- ceph
- glusterfs
- nfs
myblog/one-pod/pod-with-volume.yaml
apiVersion: v1
kind: Pod
metadata:
name: myblog
namespace: myblog
labels:
component: myblog
spec:
volumes:
- name: mysql-data
hostPath:
path: /opt/mysql/data
nodeSelector: # 使用节点选择器将Pod调度到指定label的节点
component: mysql
containers:
- name: myblog
image: registry.cn-hangzhou.aliyuncs.com/fxkjnj-study/myblog:v1
env:
- name: MYSQL_HOST # 指定root用户的用户名
value: "127.0.0.1"
- name: MYSQL_PASSWD
value: "123456"
ports:
- containerPort: 8002
- name: mysql
image: registry.cn-hangzhou.aliyuncs.com/fxkjnj-study/mysql:5.7-utf8
ports:
- containerPort: 3306
volumeMounts:
- name: mysql-data
mountPath: /var/lib/mysql
env:
- name: MYSQL_ROOT_PASSWORD
value: "123456"
- name: MYSQL_DATABASE
value: "myblog"
保存文件为pod-with-volume.yaml
,执行创建
## 若存在旧的同名服务,先删除掉,后创建
$ kubectl -n myblog delete pod myblog
## 创建
$ kubectl create -f pod-with-volume.yaml
## 此时pod状态Pending
$ kubectl -n myblog get pod
NAME READY STATUS RESTARTS AGE
myblog 0/2 Pending 0 32s
## 查看原因,提示调度失败,因为节点不满足node selector
$ kubectl -n myblog describe pod myblog
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 12s (x2 over 12s) default-scheduler 0/3 nodes are available: 3 node(s) didn't match node selector.
## 为节点打标签
$ kubectl label node k8s-node1 component=mysql
## 再次查看,已经运行成功
$ kubectl -n myblog get po
NAME READY STATUS RESTARTS AGE IP NODE
myblog 2/2 Running 0 3m54s 10.244.1.150 k8s-node1
## 到k8s-node1节点上,查看/opt/mysql/data
$ ll /opt/mysql/data/
total 188484
-rw-r----- 1 polkitd input 56 Mar 29 09:20 auto.cnf
-rw------- 1 polkitd input 1676 Mar 29 09:20 ca-key.pem
-rw-r--r-- 1 polkitd input 1112 Mar 29 09:20 ca.pem
drwxr-x--- 2 polkitd input 8192 Mar 29 09:20 sys
...
## 执行migrate,创建数据库表,然后删掉pod,再次创建后验证数据是否存在
$ kubectl -n myblog exec -ti myblog python3 manage.py migrate
## 访问服务,正常
$ curl 10.244.1.150:8002/blog/index/
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<h3>我的博客列表:</h3>
</br>
</br>
<a href=" /blog/article/edit/0 ">写博客</a>
</body>
## 删除pod
$ kubectl delete -f pod-with-volume.yaml
## 再次创建Pod,观察mysql数据有没有持久化成功
$ kubectl create -f pod-with-volume.yaml
## 查看pod ip并访问服务
$ kubectl -n myblog get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE
myblog 2/2 Running 0 7s 10.244.1.151 k8s-slave1
## 服务访问,依旧正常
$ curl 10.244.1.151:8002/blog/index/
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<h3>我的博客列表:</h3>
</br>
</br>
<a href=" /blog/article/edit/0 ">写博客</a>
</body>
其他持久化方式,使用PV+PVC连接分布式存储解决方案 等,[这里只简要说下hostPath的持久化方式,K8S的存储请看这个链接XXXXXXX]
- ceph
- glusterfs
- nfs
健康检查+重启策略(应用自修复)
检测容器服务是否健康的手段,若不健康,会根据设置的重启策略(restartPolicy)进行操作,两种检测机制可以分别单独设置,若不设置,默认认为Pod是健康的。
健康检查有以下两种机制:
-
livenessProbe(存活检查,存活性探测):如果检查失败,将杀死容器,根据Pod的重启策略(restartPolicy)来操作。
-
readinessProbe(就绪检查,可用性探测):如果检查失败,Kubernetes会把Pod从service endpoints中剔除。
livenessProbe探针
存活性探测:用于判断容器是否存活,即Pod是否为running状态,如果LivenessProbe探针探测到容器不健康,则kubelet将kill掉容器,并根据容器的重启策略是否重启,如果一个容器不包含LivenessProbe探针,则Kubelet认为容器的LivenessProbe探针的返回值永远成功。
...
containers:
- name: myblog
image: registry.cn-hangzhou.aliyuncs.com/fxkjnj-study/myblog:v1
livenessProbe:
httpGet:
path: /blog/index/
port: 8002
scheme: HTTP
initialDelaySeconds: 10 # 容器启动后第一次执行探测是需要等待多少秒
periodSeconds: 10 # 执行探测的频率
timeoutSeconds: 2 # 探测超时时间
...
ReadinessProbe探针
可用性探测:用于判断容器是否正常提供服务,即容器的Ready是否为True,是否可以接收请求,如果ReadinessProbe探测失败,则容器的Ready将为False, Endpoint Controller 控制器将此Pod的Endpoint从对应的service的Endpoint列表中移除,不再将任何请求调度此Pod上,直到下次探测成功。(剔除此pod不参与接收请求不会将流量转发给此Pod)
...
containers:
- name: myblog
image: registry.cn-hangzhou.aliyuncs.com/fxkjnj-study/myblog:v1
readinessProbe:
httpGet:
path: /blog/index/
port: 8002
scheme: HTTP
initialDelaySeconds: 10
timeoutSeconds: 2
periodSeconds: 10
...
三种检查方法
- exec:通过执行命令来检查服务是否正常,返回值为0则表示容器健康
- httpGet方式:通过发送http请求检查服务是否正常,返回200-399状态码则表明容器健康
- tcpSocket:通过容器的IP和Port执行TCP检查,如果能够建立TCP连接,则表明容器健康
案例1
使用livenessProbe,定义一个HTTP的健康检测方式
K8S将在Pod开始启动5s(initialDelaySeconds)后利用HTTP访问8002端口的/blog/index/,如果超过2s或者返回码不在200~399内,则健康检查失败
apiVersion: v1
kind: Pod
metadata:
name: myblog
namespace: myblog
labels:
component: myblog
spec:
volumes:
- name: mysql-data
hostPath:
path: /opt/mysql/data
nodeSelector: # 使用节点选择器将Pod调度到指定label的节点
component: mysql
containers:
- name: myblog
image: registry.cn-hangzhou.aliyuncs.com/fxkjnj-study/myblog:v1
livenessProbe:
httpGet:
path: /blog/index/
port: 8002
scheme: HTTP
initialDelaySeconds: 5 # 容器启动后第一次执行探测是需要等待多少秒
periodSeconds: 5 # 执行探测的频率
timeoutSeconds: 2 # 探测超时时间
env:
- name: MYSQL_HOST # 指定root用户的用户名
value: "127.0.0.1"
- name: MYSQL_PASSWD
value: "123456"
ports:
- containerPort: 8002
- name: mysql
image: registry.cn-hangzhou.aliyuncs.com/fxkjnj-study/mysql:5.7-utf8
ports:
- containerPort: 3306
volumeMounts:
- name: mysql-data
mountPath: /var/lib/mysql
env:
- name: MYSQL_ROOT_PASSWORD
value: "123456"
- name: MYSQL_DATABASE
value: "myblog"
案例2
使用livenessProbe,定义一个tcpSocket 的健康检测方式
apiVersion: v1
kind: Pod
metadata:
name: myblog
namespace: myblog
labels:
component: myblog
spec:
volumes:
- name: mysql-data
hostPath:
path: /opt/mysql/data
nodeSelector: # 使用节点选择器将Pod调度到指定label的节点
component: mysql
containers:
- name: myblog
image: registry.cn-hangzhou.aliyuncs.com/fxkjnj-study/myblog:v1
livenessProbe:
tcpSocket:
port: 8002
initialDelaySeconds: 5 # 容器启动后第一次执行探测是需要等待多少秒
periodSeconds: 5 # 执行探测的频率
timeoutSeconds: 2 # 探测超时时间
env:
- name: MYSQL_HOST # 指定root用户的用户名
value: "127.0.0.1"
- name: MYSQL_PASSWD
value: "123456"
ports:
- containerPort: 8002
- name: mysql
image: registry.cn-hangzhou.aliyuncs.com/fxkjnj-study/mysql:5.7-utf8
ports:
- containerPort: 3306
volumeMounts:
- name: mysql-data
mountPath: /var/lib/mysql
env:
- name: MYSQL_ROOT_PASSWORD
value: "123456"
- name: MYSQL_DATABASE
value: "myblog"
案例2
使用livenessProbe,定义一个exec 的健康检测方式
apiVersion: v1
kind: Pod
metadata:
labels:
test: liveness
name: liveness-exec
spec:
containers:
- name: liveness
image: busybox
args:
- /bin/sh
- -c
- touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
livenessProbe:
exec:
command:
- cat
- /tmp/healthy
initialDelaySeconds: 5
periodSeconds: 5
- initialDelaySeconds:容器启动后第一次执行探测时需要等待多少秒。
- periodSeconds:执行探测的频率。默认是10秒,最小1秒。
- timeoutSeconds:探测超时时间。默认1秒,最小1秒。
- successThreshold:探测失败后,最少连续探测成功多少次才被认定为成功。默认是1。
- failureThreshold:探测成功后,最少连续探测失败多少次才被认定为失败。默认是3,最小值是1。
重启策略(restartPolicy)
Pod的重启策略(RestartPolicy)应用于Pod内的所有容器,并且仅在Pod所处的Node上由kubelet进行判断和重启操作。当某个容器异常退出或者健康检查失败时,kubelet将根据RestartPolicy的设置来进行相应的操作。 Pod的重启策略如下:
- Always:当容器终止退出后,总是重启容器,默认策略。
- OnFailure:当容器异常退出(退出状态码非0)时,才重启容器。
- Never:当容器终止退出,从不重启容器。
演示重启策略:
apiVersion: v1
kind: Pod
metadata:
name: test-restart-policy
spec:
restartPolicy: OnFailure
containers:
- name: busybox
image: busybox
args:
- /bin/sh
- -c
- sleep 10 && exit 0
- 使用默认的重启策略,即 restartPolicy: Always ,无论容器是否是正常退出,都会自动重启容器
- 使用OnFailure的策略时
- 如果把exit 1,去掉,即让容器的进程正常退出的话,则不会重启
- 只有非正常退出状态才会重启
- 使用Never时,退出了就不再重启
可以看出,若容器正常退出,Pod的状态会是Completed,非正常退出,状态为CrashLoopBackOff
镜像拉取策略
spec:
containers:
- name: myblog
image: registry.cn-hangzhou.aliyuncs.com/fxkjnj-study/myblog:v1
imagePullPolicy: IfNotPresent
设置镜像的拉取策略,默认为IfNotPresent
- Always,总是拉取镜像,即使本地有镜像也从仓库拉取
- IfNotPresent ,本地有则使用本地镜像,本地没有则去仓库拉取
- Never,只使用本地镜像,本地没有则报错
Pod资源限制
为了保证充分利用集群资源,且确保重要容器在运行周期内能够分配到足够的资源稳定运行,因此平台需要具备Pod的资源限制的能力。 对于一个pod来说,资源最基础的2个的指标就是:CPU
和内存
。Kubernetes提供了个采用requests
和limits
两种类型参数对资源进行预分配和使用限制。
容器使用的最小资源需求,作为容器调度时资源分配的依据:【最小】
- resources.requests.cpu
- resources.requests.memory
容器资源限制:【最大】
- resources.limits.cpu
- resources.limits.memory
myblog/one-pod/pod-with-resourcelimits.yaml
apiVersion: v1
kind: Pod
metadata:
name: myblog
namespace: myblog
labels:
component: myblog
spec:
volumes:
- name: mysql-data
hostPath:
path: /opt/mysql/data
nodeSelector: # 使用节点选择器将Pod调度到指定label的节点
component: mysql
containers:
- name: myblog
image: registry.cn-hangzhou.aliyuncs.com/fxkjnj-study/myblog:v1
env:
- name: MYSQL_HOST # 指定root用户的用户名
value: "127.0.0.1"
- name: MYSQL_PASSWD
value: "123456"
ports:
- containerPort: 8002
resources:
requests:
memory: 100Mi
cpu: 50m
limits:
memory: 500Mi
cpu: 100m
- name: mysql
image: registry.cn-hangzhou.aliyuncs.com/fxkjnj-study/mysql:5.7-utf8
ports:
- containerPort: 3306
resources:
requests:
memory: 100Mi
cpu: 50m
limits:
memory: 500Mi
cpu: 100m
volumeMounts:
- name: mysql-data
mountPath: /var/lib/mysql
env:
- name: MYSQL_ROOT_PASSWORD
value: "123456"
- name: MYSQL_DATABASE
value: "myblog"
requests:
- 容器使用的最小资源需求,作用于schedule阶段,作为容器调度时资源分配的判断依赖
- 只有当前节点上可分配的资源量 >= request 时才允许将容器调度到该节点
- request参数不限制容器的最大可使用资源
- requests.cpu被转成docker的--cpu-shares参数,与cgroup cpu.shares功能相同 (无论宿主机有多少个cpu或者内核,--cpu-shares选项都会按照比例分配cpu资源)
- requests.memory没有对应的docker参数,仅作为k8s调度依据
limits:
- 容器能使用资源的最大值
- 设置为0表示对使用的资源不做限制, 可无限的使用
- 当pod 内存超过limit时,会被oom
- 当cpu超过limit时,不会被kill,但是会限制不超过limit值
- limits.cpu会被转换成docker的–cpu-quota参数。与cgroup cpu.cfs_quota_us功能相同
- limits.memory会被转换成docker的–memory参数。用来限制容器使用的最大内存
对于 CPU,我们知道计算机里 CPU 的资源是按“时间片”
的方式来进行分配的,系统里的每一个操作都需要 CPU 的处理,所以,哪个任务要是申请的 CPU 时间片越多,那么它得到的 CPU 资源就越多。
然后还需要了解下 CGroup 里面对于 CPU 资源的单位换算:
1 CPU = 1000 millicpu(1 Core = 1000m)
这里的 m
就是毫、毫核的意思,Kubernetes 集群中的每一个节点可以通过操作系统的命令来确认本节点的 CPU 内核数量,然后将这个数量乘以1000,得到的就是节点总 CPU 总毫数。比如一个节点有四核,那么该节点的 CPU 总毫量为 4000m。
docker run
命令和 CPU 限制相关的所有选项如下:
选项 | 描述 |
---|---|
--cpuset-cpus="" |
允许使用的 CPU 集,值可以为 0-3,0,1 |
-c ,--cpu-shares=0 |
CPU 共享权值(相对权重) |
cpu-period=0 |
限制 CPU CFS 的周期,范围从 100ms~1s,即[1000, 1000000] |
--cpu-quota=0 |
限制 CPU CFS 配额,必须不小于1ms,即 >= 1000,绝对限制 |
docker run -it --cpu-period=50000 --cpu-quota=25000 ubuntu:16.04 /bin/bash
将 CFS 调度的周期设为 50000,将容器在每个周期内的 CPU 配额设置为 25000,表示该容器每 50ms 可以得到 50% 的 CPU 运行时间。
注意:若内存使用超出限制,会引发系统的OOM机制,因CPU是可压缩资源,不会引发Pod退出或重建
pod状态与生命周期
Pod的状态如下表所示:
状态值 | 描述 |
---|---|
Pending | API Server已经创建该Pod,等待调度器调度 |
ContainerCreating | 拉取镜像启动容器中 |
Running | Pod内容器均已创建,且至少有一个容器处于运行状态、正在启动状态或正在重启状态 |
Succeeded|Completed | Pod内所有容器均已成功执行退出,且不再重启 |
Failed|Error | Pod内所有容器均已退出,但至少有一个容器退出为失败状态 |
CrashLoopBackOff | Pod内有容器启动失败,比如配置文件丢失导致主进程启动失败 |
Unknown | 由于某种原因无法获取该Pod的状态,可能由于网络通信不畅导致 |
生命周期示意图:
启动和关闭示意:
初始化容器:
- 验证业务应用依赖的组件是否均已启动
- 修改目录的权限
- 调整系统参数
应用场景:
- 环境检查:例如确保应用容器依赖的服务启动后再启动应用容器
- 初始化配置:例如给应用容器准备配置文件,下载代码等
案例:
本例中你将创建一个包含一个应用容器和一个 Init 容器的 Pod。Init 容器在应用容器启动前运行完成。
下面是 Pod 的配置文件:
cat > init-pod.yaml << EOF
apiVersion: v1
kind: Pod
metadata:
name: init-demo
spec:
hostNetwork: true # 声明pod的网络模式为host模式,效果同docker run --net=host
initContainers:
- name: download
image: busybox
command:
- wget
- "-O"
- "/opt/index.html"
- http://fxkjnj.com
volumeMounts:
- name: workdir
mountPath: "/opt"
containers:
- name: nginx
image: nginx:1.16.0
ports:
- containerPort: 80
volumeMounts:
- name: workdir
mountPath: /usr/share/nginx/html
volumes:
- name: workdir
emptyDir: {}
EOF
#创建pod测试
[root@k8s-master init-pod]# kubectl apply -f init-pod.yaml
pod/init-demo created
#可以看到在初始化容器
[root@k8s-master init-pod]# kubectl get pods
NAME READY STATUS RESTARTS AGE
init-demo 0/1 PodInitializing 0 12s
#查看到pod ip
[root@k8s-master init-pod]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
init-demo 1/1 Running 0 59s 172.16.200.22 k8s-node2 <none> <none>
浏览器访问: http://172.16.200.22
验证Pod生命周期:
cat > demo-pod-start.yaml << EOF
apiVersion: v1
kind: Pod
metadata:
name: demo-start-stop
namespace: myblog
labels:
component: demo-start-stop
spec:
initContainers:
- name: init
image: busybox
command: ['sh', '-c', 'echo $(date +%s): INIT >> /loap/timing']
volumeMounts:
- mountPath: /loap
name: timing
containers:
- name: main
image: busybox
command: ['sh', '-c', 'echo $(date +%s): START >> /loap/timing;
sleep 10; echo $(date +%s): END >> /loap/timing;']
volumeMounts:
- mountPath: /loap
name: timing
livenessProbe:
exec:
command: ['sh', '-c', 'echo $(date +%s): LIVENESS >> /loap/timing']
readinessProbe:
exec:
command: ['sh', '-c', 'echo $(date +%s): READINESS >> /loap/timing']
lifecycle:
postStart:
exec:
command: ['sh', '-c', 'echo $(date +%s): POST-START >> /loap/timing']
preStop:
exec:
command: ['sh', '-c', 'echo $(date +%s): PRE-STOP >> /loap/timing']
volumes:
- name: timing
hostPath:
path: /tmp/loap
EOF
创建pod测试:
$ kubectl create -f demo-pod-start.yaml
## 查看demo状态
$ kubectl -n myblog get pod -o wide -w
## 查看调度节点的/tmp/loap/timing
$ cat /tmp/loap/timing
1585424708: INIT
1585424746: START
1585424746: POST-START
1585424754: READINESS
1585424756: LIVENESS
1585424756: END
须主动杀掉 Pod 才会触发
pre-stop hook
,如果是 Pod 自己 Down 掉,则不会执行pre-stop hook