前言
pod 可以说是 k8s 的基础单元. 我觉得可以类比云环境的ecs/ec2这一类的基本计算单元.而 pod 上运行的容器, 可以类比为ecs/ec2上的app程序.
你总能在k8s的各类资源中找到云环境对应的资源影子. 如果你用过GCP,你会更有这种感觉.
https://kubernetes.io/docs/concepts/workloads/pods/
Pod与容器
一个pod可以拥有多个容器。
pod 内包含多个容器,所以多个容器共享以下资源。
-
PID命名空间: pod内的进程能互相看到PID
-
网络命名空间: pod中的多个容器共享一个ip (唯一)
-
IPC命名空间: pod中的多个容器之间可以互相通信
-
UTS命名空间: pod中的多个容器共享一个主机名 (唯一)
-
存储卷: pod多个容器可以共同访问pod定义的存储卷
另外,Pod可以包含一个init的特殊容器,它始终首先运行。
若pod只有一个容器,那么pod就是一个包装器
若pod有多个容器,则一般主容器提供服务;边车/挂斗/附属容器提供额外功能,例如刷新主容器的文件,收集日志。
ℹ️上述的功能的实现基于pod内的容器共享网络命名空间和存储空间.
如果你玩过星际争霸,那么应该知道一个人族建筑物,总是会有一个附属建筑物,它很小,但提供了主建筑物所需的科技。
因此,除非你两个容器必须放在一起,否则你应该用多个单容器pod.
Pod与负载控制器
生产环境中,pod 一般不单独使用,因为单独使用意味着没有高可用,且难以管理。k8s建议 pod 要始终和负载控制器一起使用。负载控制器可以批量创建pod。
k8s将负载控制器主要分为三种:
- Deployment 无状态 https://kubernetes.io/docs/concepts/workloads/controllers/deployment/
- StatefulSet 有状态 https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/
- DaemonSet 守护态 https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/
还有CronJob、Jobs任务类型的.
负载控制器需要依托于镜像模板创建 pod 和依托于缩放规则控制 pod 数量。
镜像模板即 pod 模板(pod template).
负载控制器 - Pod模板
一个构建nginx的例子
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
如何寻找最合适的kind所属的apiVersion
在这里可能有人不知道如何选择apiVersion。你可以通过kubectl api-versions来找到kind所属的apiGroup,然后再通过
kubectl get --raw “/apis” 的输出找 preferredVersion。
➜ kubectl api-resources | grep deployment
deployments deploy apps/v1 true Deployment
➜ kubectl get --raw "/apis"|jq '.groups[]|select(.name=="apps")'
{
"name": "apps",
"versions": [
{
"groupVersion": "apps/v1",
"version": "v1"
}
],
"preferredVersion": {
"groupVersion": "apps/v1",
"version": "v1"
}
}
如上命令所示,在我的k8s版本中kind: Deployment的最合适apiVersion是apps/v1
pod 存储
这是一个大问题. 如果你想真正的使用k8s的pod资源, 那么需要先看这一部分的内容.
简单来说, 存储资源主要分网络和本地两大类.
-
本地 用于临时或者特殊环境. 就如同云服务中的【存储类节点】里的那种本地盘, 它不可靠. 因为pod本身默认是不强制绑定某个节点的,因此如果你pod异常了,那么它有可能重建的时候漂移到其它节点.此时你如果用本地盘,那么数据将丢失。
-
网络 可以对接的有云服务厂家的存储资源,也可以对接自建的nfs这一类网络存储。
细致的说明, 参考官方文档https://kubernetes.io/docs/concepts/storage/
pod 网络
鉴于前面提到的pod类同于ecs/ec2. 因此. pod中的容器就如同ecs/ec2里的app一样,都有相同的ip, 端口范围, 主机名.
k8s的网络基于各种插件.每一种插件的实现详情见官网. https://kubernetes.io/docs/concepts/cluster-administration/networking/#how-to-implement-the-kubernetes-networking-model
如果你是本地搭建, 那么常用的插件是Flannel. 如果你是在云服务上搭建,那么建议使用云服务已有的k8s服务.
如果你必须在云服务上自己搭建,那么aws/azure/gcp都有对应的网络插件.它可以让你在k8s中结合使用云服务的网络组件.
当然你依然可以用 flannel 网络插件。
静态pod
特点:
- 永远运行在固定节点
- 由所在节点的kubelet管理,但只负责保活,即pod崩溃重生
- kubelet会让apiserver创建一个镜像pod,便于可以通过kubectl查询到静态pod
配置:
- 存放在 /etc/kubernetes/manifests 当采用kubeadm安装的时候,一般位于此目录。具体需要去看kubelet配置。
- 配置本身可以按照标准pod方式来创建
检测:
- kubelet会定期检测配置目录加载配置创建/重建pod
当你通过kubeadm创建的时候,那么k8s的几个重要组件均会以静态pod的方式在master节点上创建,你可以在/etc/kubernetes/manifests/这里找到他们的配置
➜ ll /etc/kubernetes/manifests/
total 16
-rw------- 1 root root 1848 Aug 25 16:28 etcd.yaml
-rw------- 1 root root 2709 Aug 25 16:28 kube-apiserver.yaml
-rw------- 1 root root 2564 Aug 25 16:33 kube-controller-manager.yaml
-rw------- 1 root root 1120 Aug 25 16:33 kube-scheduler.yaml
容器生命周期
- waiting,一般对应Pod的pending阶段
- running,容器运行OK
- terminated,容器退出
Pod-生命周期
pod生命周期包含5个状态:
- pending,调度时间和拉取容器镜像时间,容器均处于waiting状态
- running,pod里所有容器都已经创建,且至少一个容器处于启动、重启、running状态
- failed,pod里的容器全退出,有部分容器以非0状态进入terminated状态
- succeeded,pod里的容器全部以0状态进入terminated状态,并且不会再重启
- unknown,pod所在节点和主节点之间失联,不过这种状态会因k8s的策略转为failed状态
pod是通过uid来鉴别,而不是pod名,pod被替换时名称可以不变。
重启策略
https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#lifecycle
pod重启策略的间隔时间是10s,成指数上涨,但不超过5分钟。一旦重启成功且运行10分钟,则重置为10s。
状况
通过kubectl describe pod/<pod_name>
查看Conditions
字段条件
Conditions:
Type Status
Initialized True
Ready False
ContainersReady False
PodScheduled True
Ready为True,即表示【应该】被加入到svc的端点列表中。
但极端情况下有可能因为其它服务没准备好,导致及时Pod的Ready为True,svc也无法正常的转发流量到Pod。
这种行为,可能在滚动更新的时候,会导致丢数据。
针对上述问题,kubernetes允许在上面4个默认状态的基础上自定义就绪状态类型。
https://kubernetes.io/zh/docs/concepts/workloads/pods/pod-lifecycle/#pod-readiness-gate
终止
https://kubernetes.io/zh/docs/concepts/workloads/pods/pod-lifecycle/#pod-termination
-
Apiserver拿到删除请求、Apiserver更新pod状态,转为
Terminating
。默认Pod有30 秒优雅终止时间。 -
kubelet推送preStop事件到容器中执行,如果设置了preStop的话
-
kubelet通过
container runtime
发送TERM信号(kill-14)给每个容器中pid为1的进程号,并同时将pod从svc端点中剥离。- 如果容器在30秒内没有停止成功,则kubelet会发送SIGKILL信号(kill -9)给容器,强行杀掉。
-
容器关闭状态转为Terminated,Apiserver将Pod删除。
- 2和3是并行的,并且执行时间取决于Pod终止宽限期
terminationGracePeriodSeconds
- kubectl 添加
--grace-period=0 --force
可以立即删除Pod
ℹ️失败的pod状态会根据kube-controller-manager
参数 terminated-pod-gc-threshold
阈值设置保存一定数量。默认这个值是12500
,不清楚为何设置这么高。https://github.com/kubernetes/kubernetes/pull/79047这是一个被驳回的修正,它提议设置为500
。
容器Terminated
通过下方命令可以查找Terminated的原因。
kubectl get pod -o go-template='{{range.status.containerStatuses}}{{"Container Name: "}}{{.name}}{{"\r\nLastState: "}}{{.lastState}}{{end}}' <pod_name:simmemleak>
simmemleak
Container Name: simmemleak
LastState: map[terminated:map[exitCode:137 reason:OOM Killed startedAt:2015-07-07T20:58:43Z finishedAt:2015-07-07T20:58:43Z containerID:docker://0e4095bba1feccdfe7ef9fb6ebffe972b4b14285d5acdec6f0d3ae8a22fad8b2]]