文章内容:
资源类型介绍、YAML格式介绍、常用字段说明、Pod生命周期、initC、initC特殊说明、探针-1、探针-2、start、stop、相位
资源清单,简单来说就是通过资源清单中的资源项编写剧本yaml文件,实现服务pod化、service、volume等等。
资源清单类型
名称空间级别:
工作负载型资源(workload)
Pod
ReplicaSet 用于通过标签创建pod
Deployment 用于通过ReplicaSet创建pod
StatefulSet 无状态服务管理器
DaemonSet 用于在每个节点都创建一个组件,例如部署监控
Job 任务
CronJob 计划任务
服务发现及负载均衡型资源(ServiceDiscovery LoadBalance)
Service
Ingress
配置与存储型资源
Volume(存储卷)
CSI (容器存储接口,可以扩展各种各样的第三方存储卷)
特殊类型的存储卷
ConfigMap (配置资源类型,部分应用的配置文件管理,达到热提交)
Secret (保存敏感数据)
DownwardAPI (吧外部环境中的信息输出给容器,下载或上传文件的接口)
集群级别资源:
Namespace 命名空间
Node 节点
Role 角色
ClusterRole 集群角色
RoleBinding
ClusterRoleBinding
元数据型资源:
根据指标进行操作
HPA 根据cpu 内存占用情况自动扩展缩减节点
PodTemplate pod模板
LimitRange 资源限制
YAML格式语法
yaml语言是一种可读性高,用来表达数据序列的格式语言。其意思是:仍是一种标记型语言,但为了强调这种语言一数据作为中心,而不是以标记语言为重点。
基本语法:
- 缩进时不允许使用Tab键,只允许使用空格
- 缩进的空格数目不重要,但要相同层级的元素左对齐
- #表示注释
yaml支持的数据结构
- 对象:键值对的集合,又称为映射/哈希/字典
- 数组:一组按次序排列的值,又称为序列/列表
- 纯量:单个的,不可再分的值
对象类型:对象的一组键值对,使用冒号结构表示
name: Steve
age: 18
另一种写法
hash: { name: Steve, age: 18 }
数组类型:一组连词线开头的行,构成一个数组
animal
- Cat
- Dog
数组也可以采用行内表示法
animal: [Cat, Dog]
复合结构:对象和数组可以结合使用,形成复合结构
languages:
- Ruby
- Perl
- Python
websites:
YAML: yaml.org
Ruby: ruby-lang.org
Python: python.org
纯量:纯量是最基本的、不可再分的值。一下数据类型都属于纯量
字符串、布尔值、整数、浮点数、null
时间、日期
数值直接以字面量的形式表示:
number: 12.13
布尔值用true和false表示:
isSet: true
null用 ~ 表示,不写也是null:
parent: ~
时间采用ISO8601格式:
iso8601: 2020-06-30t11:20:40.10-05:00
日期采用复合 iso8601 格式的年、月、日表示
date: 1999-06-30
YAML允许使用两个感叹号,强制转换数据类型:
e: !!str 123 转换为字符类型
f: !!str true 转换为字符类型
字符串
字符串默认不使用引号表示:
str: 这是一行字符串
如果字符串之中包含空格或特殊字符,需要放在引号之中:
str: '这是 一行字符串'
单引号和双引号都可以使用,双引号不会对特殊字符进行转义:
s1: '这是\n字符串‘
s2: "这是\n字符串"
单引号之中如果还有单引号,必须连续使用两个单引号转义:
str: 'labor ''s day'
字符串可以写成多行,从第二行开始,必须有一个单空格缩进。换行符挥别转为空格:
str: 这是一段
多行
字符串
多行字符串可以使用 | 保留换行符,也可以使用 > 折叠换行:
this: |
Foo
Bar
thar: >
Foo
Bar
+ 表示保留文字块末尾的换行,-表示删除字符串末尾的换行:
s1: |
Foo
s2: |+
Foo
s3: |-
Foo
常用字段解释
必须存在的属性表
参数名 | 字段类型 | 说明 |
version | String | 这里指的是K8S API的版本,目前基本上是V1,可以用kubectl api-versions命令查询 |
kind | String | 这里指的是yaml文件定义的资源类型和角色,比如:Pod |
metadata | Object | 元数据对象,固定值就写metadata |
metadata.name | String | 元数据对象的名字,这里由我们编写,比如命名Pod的名字 |
metadata.namespace | String | 元数据对象的命名空间,由我们自身定义 |
Spec | Object | 详细定义对象,固定值就写Spec |
spec.containers[] | list | 这里是Spec对象的容器列表定义,是个列表 |
spec.containers[].name | String | 这里定义容器的名字 |
spec.containers[].image | String | 这里定义要用到的镜像名称 |
主要对象
参数名 | 字段类型 | 说明 |
spec.containers[].name | String | 定义容器的名字 |
spec.containers[].image | String | 定义要用到的镜像名称 |
spec.containers[].imagePullPolicy | String | 定义镜像拉取策略,有Always、Never、ifNotPresent三个值可选 Always:意思是每次都尝试重新拉取镜像 Never:表示仅使用本地镜像 ifNotPresent:如果本地有镜像就使用本地镜像,没有就拉取镜像 默认值:Always 最佳选择是:ifNotPresent |
spec.containers[].command[] | List | 指定容器启动命令,因为是数组可以指定多个,不指定则使用镜像打包时使用的启动命令 |
spec.containers[].args[] | List | 指定容器启动命令参数,因为是数组可以指定多个 |
spec.containers[].workingDir | String | 指定容器的工作目录 |
spec.containers[].env[].name | String | 指定环境变量名称 |
spec.containers[].env[].value | String | 指定环境变量值 |
spec.containers[].resources | Object | 指定资源限制和资源请求的值(这里开始就是设置容器的资源上限) |
spec.containers[].resources.limits | Object | 指定设置容器运行时的资源使用上限 |
spec.containers[].resources.limits.cpu | String | 指定CPU的限制,单位为core,将用于docker run --cpu-shares参数 |
spec.containers[].resources.limits.memory | String | 指定MEM内存的限制,单位为MIB、GiB |
spec.containers[].resources.requests | Object | 指定容器启动和调度是的限制设置 |
spec.containers[].resources.requests.cpu | String | CPU请求,单位为core数,容器启动是初始化可用数量 |
spec.containers[].resources.requests.memory | String | 内存请求,单位为MIB、GiB,容器启动时初始化可用数量 |
额外的参数项
参数名 | 字段类型 | 说明 |
spec.restartPolicy | String | 定义Pod的重启策略,可选值为Always、OnFailure,默认值为Always Always:Pod一旦终止运行,则无论容器是如何终止的,kubelet服务都会将其重启 OnFailure:只要Pod以非0退出码终止时,kubelet才会重启该容器 Never:Pod终止后,kubelete将退出码报告给Master,不会重启该Pod |
spec.nodeSelector | Object | 定义Node的Label过了标签,以key:value格式指定 |
spec.imagePullSecrets | Object | 定义pull镜像时使用secret名称,以name:secretkey格式指定 |
spec.hostNetwork | Boolean | 定义是否使用主机网络模式,默认值为false 设置true表示使用苏主机网络,不实用docker网桥,同时设置了true将无法在同一台宿主机上启动第二个副本 |
除了上面常用的以外还有很多,可以通过命令查看
查看pod的字段
kubectl create pod
FIELDS下面就是创建pod的一些模板字段
继续查看字段的详细描述信息
例如查看apiVersion
kubectl create pod.apiVersion
pod yaml文件示例:
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
namespace: default
labels:
app: myapp
spec:
containers:
- name: app
image: docker.harbor.com/library/myapp:v1
- name: test
image: docker.harbor.com/library/myapp:v1
注意,上面的示例代码创建出来的pod实际上是个故障pod,原因是个pod中两个容器没有问题,问题是两个容器使用了同一个镜像,同一个镜像使用的端口一样,所以会造成端口冲突。
故障排查:
根据上面的文件创建出来的pod通过 kubectl get pod 会发现pod状态不断改变并重启,RESTARTS下的重启数值会增加
查看pod信息
kubectl describe pod myapp-pod 会发现是第二个容器 test 在重启
查看test容器日志报错信息
kubectl log myapp-pod -c test 会发现是因为端口被占用
Pod生命周
initC:容器启动时的初始化工作,不止有一个initC,初始化后将丢弃initC,多个initC则必须是前一个initC完成后才执行下一个
Main C:初始化后运行的主程序
START:启动时执行一个指令
STOP:停止时执行一个指令
readiness:pod启动后假设pod中有个容器运行了一个程序,程序启动即running,而running的pod将可以让外部访问,但是程序 有可能正在启动而无法提供给用户想要的返回结果,而readiness就是判断服务是否真正启动完成
Liveness:pod中容器运行期间如果容器中进程出现假死,僵尸进程z,则Liveness可以通过预设检测Main C 判断该pod是否正常
initC
pod能够具有多个容器,应用运行在容器中,但是他也可能有一个或多个先于应用容器启动的 init 容器
init 容器与普通的容器非常像,除了如下亮点:
- init 容器总是运行到成功完成为止
- 每个 init 容器都必须在下一个init容器启动之前成功完成
如果 pod 的 init 容器失败,kubernetes 会不断的重启该pod,直到 init 容器成功为止,除非 pod 对应的 restartPolicy 为 Never。
举例说明:
有下面一个文件 init-pod.yaml
首先运行一个容器 myapp-container,运行一条命令,等待6分钟
然后initContainers运行init,执行一条命令,解析域名myservice,每两秒循环一次,直到解析成功才退出
前一个 init-myservice init执行完成后执行 init-mydb的init
解析域名mydb,同样解析成功退出
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app: myapp
spec:
containers:
- name: myapp-container
image: docker.harbor.com/library/busybox:v1
command: ['sh', '-c', 'echo The app is running! && sleep 3600']
initContainers:
- name: init-myservice
image: docker.harbor.com/library/busybox:v1
command: ['sh', '-c', 'until nslookup myservice; do echo waiting for myservice; sleep 2; done;']
- name: init-mydb
image: docker.harbor.com/library/busybox:v1
command: ['sh', '-c', 'until nslookup mydb; do echo waiting for mydb; sleep 2; done;']
运行:
kubectl create -f init-pod.yaml
此时因为没有创建 myservice 和 mydb 这两个 service,所以会出现下面的状态
[root@m1 ymal]# kubectl get pod
NAME READY STATUS RESTARTS AGE
myapp-pod 0/1 Init:0/2 0 6s
查看故障原因
可以看到在 init-myservice 处 ready 为False
[root@m1 ymal]# kubectl describe pod myapp-pod
Name: myapp-pod
Namespace: default
Priority: 0
Node: m3-n2/172.26.220.94
Start Time: Wed, 01 Jul 2020 18:03:46 +0800
Labels: app=myapp
Annotations: <none>
Status: Pending
IP: 10.244.2.2
Init Containers:
init-myservice:
Container ID: docker://b2d3ffc1b3fc6309c2ee1ca2f268eb03638d97f8b18dfe26c787ba1045481e5f
Image: docker.harbor.com/library/busybox:v1
Image ID: docker-pullable://docker.harbor.com/library/busybox@sha256:2131f09e4044327fd101ca1fd4043e6f3ad921ae7ee901e9142e6e36b354a907
Port: <none>
Host Port: <none>
Command:
sh
-c
until nslookup myservice; do echo waiting for myservice; sleep 2; done;
State: Running
Started: Wed, 01 Jul 2020 18:03:47 +0800
Ready: False
Restart Count: 0
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from default-token-wprng (ro)
init-mydb:
Container ID:
Image: docker.harbor.com/library/busybox:v1
Image ID:
Port: <none>
Host Port: <none>
Command:
sh
-c
until nslookup mydb; do echo waiting for mydb; sleep 2; done;
State: Waiting
Reason: PodInitializing
Ready: False
Restart Count: 0
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from default-token-wprng (ro)
查看故障故障点
可以看到是因为域名解析失败了
[root@m1 ymal]# kubectl log myapp-pod -c init-myservice
log is DEPRECATED and will be removed in a future version. Use logs instead.
Server: 10.96.0.10
Address: 10.96.0.10:53
** server can't find myservice.default.svc.cluster.local: NXDOMAIN
*** Can't find myservice.svc.cluster.local: No answer
*** Can't find myservice.cluster.local: No answer
*** Can't find myservice.default.svc.cluster.local: No answer
*** Can't find myservice.svc.cluster.local: No answer
*** Can't find myservice.cluster.local: No answer
好的我们创建 myservice.yaml 并启动
kind: Service
apiVersion: v1
metadata:
name: myservice
spec:
ports:
- protocol: TCP
port: 80 # 容器内部端口
targetPort: 9376 # 映射到外部的端口
运行
# kubectl create -f myservice.yaml
稍微等待一会再次查看 pod 会发现变成了下面的状态, Init 变成了 1/2,表示第一个init已经运行成功了
[root@m1 ymal]# kubectl get pod
NAME READY STATUS RESTARTS AGE
myapp-pod 0/1 Init:1/2 0 10m
我们再继续创建下一个 service, mydb.yaml
kind: Service
apiVersion: v1
metadata:
name: mydb
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9377
运行
# kubectl create -f mydb.yaml
等待一会再次查看 pod
成功运行
[root@m1 ymal]# kubectl get pod
NAME READY STATUS RESTARTS AGE
myapp-pod 1/1 Running 0 10m
init C 特殊说明
- pod启动过程中,第一个启动的不是init,而是pause
- 如果init运行失败退出,将导致容器启动失败,它会根据pod的 restartPolicy 指定的策略进行重试。如果pod的 restartPolicy设置为Always,Init 容器失败时会使用 RestartPolicy 策略。
- 在所有init容器没有成功之前,pod不会变成Ready状态,正在初始化的pod处于Pending状态
- 如果pod重启,所有init容器必须重新执行
- 更改init容器的image字段,等价于重启该pod
- Init 容器具有应用容器的所有字段,除了 readinessProbe ,因为 init 容器无法定义不同于完成(completion)的就绪(readiness)之外的其它状态。这回在验证过程中强制执行
- 在pod中的每个app和init容器名称必须唯一;与任何其它容器共享同一个名称,会在验证时抛出错误
探针
探针概念:
探针是由kubelet 对容器执行的定期诊断,要执行诊断,kubelet调用由容器实现的Handler。
有三种类型的处理程序:
- ExecAction:在容器内执行指定的命令,退出码为 0 表示诊断成功
- TCPSocketAction:对指定端口上的容器IP地址进行TCP检查,端口通则表示诊断成功
- HTTPGetAction:对指定容器的ip和端口执行HTTP Get请求,状态吗返回结果大于等于200 小于400,表示诊断成功
每次探测都将获得一下三种结果之一:
- 成功:容器通过诊断
- 失败:容器未通过诊断
- 未知:诊断失败,因此不会采取任何行动
探测方式:
readinessProbe:指示容器是否做好服务请求,如果就绪探测失败,端点控制器将从Pod匹配的所有Service的端点中删除该Pod的IP地址。初始延迟之前的就绪状态默认为Failure。如果容器不提供就绪探针,则默认状态为Success。。简单来说就是如果配置此探针但是诊断失败,Main C网络不会对外开放访问。
livenessProbe:指示容器是否正在运行。如果存活探测失败,则kubelet会杀死容器,并且容器将受到其 重启策略 的影响。如果容器不提供存活探针,则默认状态为Success。
示例:
探针检测-就绪检测
执行 get 检测,因为根本就没有index1.html这个文件,所以这个剧本执行一定失败
initialDelaySeconds: 1 表示启动后一秒执行检测
periodSeconds: 3 重试间隔3秒
[root@m1 yaml]# cat readiness-httpget-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: readiness-httpget-pod
namespace: default
spec:
containers:
- name: readiness-httpget-container
image: docker.harbor.com/library/nginx:v1
imagePullPolicy: IfNotPresent
readinessProbe:
httpGet:
port: 80
path: /index1.html
initialDelaySeconds: 1
periodSeconds: 3
[root@m1 yaml]# kubectl create -f readiness-httpget-pod.yaml
启动后查看pod状态
会发现状态为Running,但是READY状态显示为成功
[root@m1 yaml]# kubectl get pod
NAME READY STATUS RESTARTS AGE
readiness-httpget-pod 0/1 Running 0 5s
查看pod详细信息
[root@m1 yaml]# kubectl describe pod readiness-httpget-pod
会发现最后显示为:
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 3m33s default-scheduler Successfully assigned default/readiness-httpget-pod to m3-n2
Normal Pulling 3m33s kubelet, m3-n2 Pulling image "docker.harbor.com/library/nginx:v1"
Normal Pulled 3m30s kubelet, m3-n2 Successfully pulled image "docker.harbor.com/library/nginx:v1"
Normal Created 3m30s kubelet, m3-n2 Created container readiness-httpget-container
Normal Started 3m29s kubelet, m3-n2 Started container readiness-httpget-container
Warning Unhealthy 2m27s (x21 over 3m27s) kubelet, m3-n2 Readiness probe failed: HTTP probe failed with statuscode: 404
状态码为 404
我们进入容器创建一个 index1.html
[root@m1 yaml]# kubectl exec readiness-httpget-pod -it -- /bin/sh
# cd /usr/share/nginx/html
# echo '123' >> index1.html
# exit
再次查看pod,发现已经就绪
[root@m1 yaml]# kubectl get pod
NAME READY STATUS RESTARTS AGE
readiness-httpget-pod 1/1 Running 0 6m5s
探针检测-存活检测-ExecAction命令探测
检测意思就是,启动后会创建一个文件 /tmp/live ,60秒后删除这个文件
然后存活检测是查看这个文件是否存在,存在即存活,然而60秒后因为删除了这个文件,所以会检测失败
[root@m1 yaml]# cat live-exec.yaml
apiVersion: v1
kind: Pod
metadata:
name: liveness-exec-pod
namespace: default
spec:
containers:
- name: liveness-exec-container
image: docker.harbor.com/library/busybox:v1
imagePullPolicy: IfNotPresent
command: ["/bin/sh", "-c", "touch /tmp/live ; sleep 60; rm -rf /tmp/live; sleep 3600"]
livenessProbe:
exec:
command: ["test", "-e", "/tmp/live"]
initialDelaySeconds: 1
periodSeconds: 3
[root@m1 yaml]# kubectl create -f live-exec.yaml
启动后持续观察这个pod状态
会发现每隔一段时间就会重启一次,因为检测失败就会触发重启策略,重启后文件又重新创建,检测也就能成功,如此循环。
[root@m1 yaml]# kubectl get pod -w
NAME READY STATUS RESTARTS AGE
liveness-exec-pod 1/1 Running 5 8m14s
liveness-exec-pod 1/1 Running 6 9m54s
探针检测-存活检测-HTTPGetAction
检测访问index.html
timeoutSeconds: 10 超时时间10秒
[root@m1 yaml]# cat liveness-httpget-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: liveness-httpget-pod
namespace: default
spec:
containers:
- name: liveness-httpget-container
image: docker.harbor.com/library/nginx:v1
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
livenessProbe:
httpGet:
port: http
path: /index.html
initialDelaySeconds: 1
periodSeconds: 3
timeoutSeconds: 10
[root@m1 yaml]# kubectl create -f liveness-httpget-pod.yaml
删除 index.html文件
[root@m1 yaml]# kubectl exec liveness-httpget-pod -it -- /bin/sh
# cd /usr/share/nginx/html
# ls
50x.html index.html
# rm -rf index.html
# ls
50x.html
# exit
查看pod状态,发生重启,重启后容器会自动建立index.html所以会成功
[root@m1 yaml]# kubectl get pod -w
NAME READY STATUS RESTARTS AGE
liveness-httpget-pod 1/1 Running 0 4m44s
liveness-httpget-pod 1/1 Running 1 4m46s
探针检测-存活检测-TCPSocketAction
初始化后五秒开始检测 80 端口,超时时间 1 秒,检测失败重启三秒
# cat livenessProbe-tcp.yaml
apiVersion: v1
kind: Pod
metadata:
name: probe-tcp
spec:
containers:
- name: nginx
image: docker.huarbor.com/library/nginx:v1
livenessProbe:
initialDelaySeconds: 5
timeoutSeconds: 1
tcpSocket:
port: 80
periodSeconds: 3
# kubectl create -f livenessProbe-tcp.yaml
以上的检测方式可以合并到一个文件中进行多重条件检测。
start、stop
启动、退出动作
定义一个pod启动时想要做什么,和退出时要做什么
apiVersion: v1
kind: Pod
metadata:
name: lifecycle-demo
spec:
containers:
- name: lifecycle-demo-container
image: nginx
lifecycle:
postStart:
exec:
command: ['/bin/sh", "-c", "echo Hello from the postStart handler > /usr/share/message"]
preStop:
exec:
command: ["/usr/sbin/nginx", "-s", "quit"]