
| 版本 | 发布时间 | 结束支持时间 |
|---|---|---|
| v1.33 | 2025 年 4 月 23 日 | 2026 年 6 月 28 日 |
| v1.32 | 2024 年 12 月 11 日 | 2026 年 2 月 28 日 |
| v1.31 | 2024 年 8 月 13 日 | 2025 年 10 月 28 日 |
| v1.30 | 2024 年 4 月 17 日 | 2025 年 6 月 28 日 |
| v1.29 | 2023 年 12 月 13 日 | 2025 年 2 月 28 日 |
| v1.28 | 2023 年 8 月 15 日 | 2024 年 10 月 28 日 |
| v1.27 | 2023 年 4 月 11 日 | 2024 年 5 月 30 日 |
| v1.26 | 2022 年 12 月 9 日 | 2024 年 2 月 24 日 |
| v1.25 | 2022 年 8 月 23 日 | 2023 年 10 月 27 日 |
| v1.24 | 2022 年 5 月 3 日 | 2023 年 7 月 28 日 |
| v1.23 | 2021 年 12 月 8 日 | 2023 年 4 月 28 日 |
| v1.22 | 2021 年 8 月 4 日 | 2022 年 12 月 28 日 |
| v1.21 | 2021 年 4 月 8 日 | 2022 年 8 月 17 日 |
| v1.20 | 2020 年 12 月 8 日 | 2021 年 12 月 8 日 |
| v1.19 | 2020 年 8 月 3 日 | 2021 年 8 月 3 日 |
| v1.18 | 2020 年 3 月 19 日 | 2021 年 3 月 19 日 |
| v1.17 | 2019 年 11 月 13 日 | 2020 年 11 月 13 日 |
| v1.16 | 2019 年 9 月 18 日 | 2020 年 9 月 18 日 |
| v1.15 | 2019 年 6 月 12 日 | 2020 年 6 月 12 日 |
| v1.14 | 2019 年 3 月 19 日 | 2020 年 3 月 19 日 |
| v1.13 | 2018 年 12 月 5 日 | 2019 年 12 月 5 日 |
Kubernetes 早期版本通常都有大约 9 个月的主流支持期。从 Kubernetes 1.19 开始,官方将每个版本的支持窗口从原来的 9 个月延长至 1 年的主流支持期,之后进入 2 个月的维护模式期,共计约 14 个月的支持周期。
Kubernetes 是一个可移植、可扩展的开源平台,用于管理容器化的工作负载和服务,使声明式配置和自动化更容易。 Kubernetes 拥有一个庞大且快速增长的生态,其服务、支持和工具的使用范围相当广泛。
Kubernetes 这个名字源于希腊语,意为“舵手”或“飞行员”。k8s 这个缩写是因为 k 和 s 之间有八个字符的关系。 Google 在 2014 年开源了 Kubernetes 项目。Kubernetes 建立在 Google 超过 15 年、大规模运行生产工作负载经验的基础上, 结合了社区中最佳想法和实践。
让我们回顾一下为何 Kubernetes 能够裨益四方。

传统部署时代:
早期,各个组织是在物理服务器上运行应用程序。 由于无法限制在物理服务器中运行的应用程序资源使用,因此会导致资源分配问题。 例如,如果在同一台物理服务器上运行多个应用程序, 则可能会出现一个应用程序占用大部分资源的情况,而导致其他应用程序的性能下降。 一种解决方案是将每个应用程序都运行在不同的物理服务器上, 但是当某个应用程式资源利用率不高时,剩余资源无法被分配给其他应用程式, 而且维护许多物理服务器的成本很高。
虚拟化部署时代:
因此,虚拟化技术被引入了。虚拟化技术允许你在单个物理服务器的 CPU 上运行多台虚拟机(VM)。 虚拟化能使应用程序在不同 VM 之间被彼此隔离,且能提供一定程度的安全性, 因为一个应用程序的信息不能被另一应用程序随意访问。
虚拟化技术能够更好地利用物理服务器的资源,并且因为可轻松地添加或更新应用程序, 而因此可以具有更高的可扩缩性,以及降低硬件成本等等的好处。 通过虚拟化,你可以将一组物理资源呈现为可丢弃的虚拟机集群。
每个 VM 是一台完整的计算机,在虚拟化硬件之上运行所有组件,包括其自己的操作系统。
容器部署时代:
容器类似于 VM,但是更宽松的隔离特性,使容器之间可以共享操作系统(OS)。 因此,容器比起 VM 被认为是更轻量级的。且与 VM 类似,每个容器都具有自己的文件系统、CPU、内存、进程空间等。 由于它们与基础架构分离,因此可以跨云和 OS 发行版本进行移植。
容器因具有许多优势而变得流行起来,例如:
Kubernetes 为你提供:
服务发现和负载均衡
Kubernetes 可以使用 DNS 名称或自己的 IP 地址来暴露容器。 如果进入容器的流量很大, Kubernetes 可以负载均衡并分配网络流量,从而使部署稳定。
存储编排
Kubernetes 允许你自动挂载你选择的存储系统,例如本地存储、公共云提供商等。
自动部署和回滚
你可以使用 Kubernetes 描述已部署容器的所需状态, 它可以以受控的速率将实际状态更改为期望状态。 例如,你可以自动化 Kubernetes 来为你的部署创建新容器, 删除现有容器并将它们的所有资源用于新容器。
自动完成装箱计算
你为 Kubernetes 提供许多节点组成的集群,在这个集群上运行容器化的任务。 你告诉 Kubernetes 每个容器需要多少 CPU 和内存 (RAM)。 Kubernetes 可以将这些容器按实际情况调度到你的节点上,以最佳方式利用你的资源。
自我修复
Kubernetes 将重新启动失败的容器、替换容器、杀死不响应用户定义的运行状况检查的容器, 并且在准备好服务之前不将其通告给客户端。
密钥与配置管理
Kubernetes 允许你存储和管理敏感信息,例如密码、OAuth 令牌和 ssh 密钥。 你可以在不重建容器镜像的情况下部署和更新密钥和应用程序配置,也无需在堆栈配置中暴露密钥。
Kubernetes 的架构包括控制平面和数据平面。控制平面负责管理集群的状态和配置信息,包括 kube-apiserver、etcd、kube-scheduler、kube-controller-manager、cloud-controller-manager 等组件。数据平面负责容器的运行和通信,包括 kubelet、kube-proxy、容器网络等组件。
Kubernetes集群主要由Master Node和Worker Node两类节点组成
K8S 是属于主从设备模型(Master-Slave 架构),即有 Master 节点负责核心的调度、管理和运维,Slave 节点则在执行用户的程序。但是在 K8S 中,主节点一般被称为Master Node ,而从节点则被称为Worker Node 或者 Node。
Master节点指的是集群控制节点,管理和控制整个集群,Master组件可以在集群中任何节点上运行。但是为了简单起见,通常在一台VM/机器上启动所有Master组件,并且不会在此VM/机器上运行用户容器。基本上Kubernetes所有的控制命令都是发给它,它来负责具体的执行过程,我们后面所有执行的命令基本都是在Master节点上运行的;
需部署在 master 节点上;是 Kubernetes 控制面的前端。集群服务的统一入口,各组件协调者,以RESTful API提供接口服务,所有对象资源的增删改查和监听操作都交给APIServer处理后再提交给Etcd存储。
kube-scheduler 节点调度器,根据调度算法为新创建的Pod选择一个Node节点,可以任意部署,可以部署在同一个节点上,也可以部署在不同的节点上。Scheduler在调度时会对集群的结构进行分析,当前各个节点的负载,以及应用对高可用、性能等方面的需求。
kube-controller-manager运行管理控制器,负责执行各种控制器,处理集群中常规后台任务,维持副本期望数目,一个资源对应一个控制器,而 ControllerManager 就是负责管理这些控制器的。k8s中所有资源对象的自动化控制中心,维护管理集群的状态,比如故障检测,自动扩展,滚动更新等
Controller Manager作为集群内部的管理控制中心,负责集群内的Node、Pod副本、服务端点(Endpoint)、命名空间(Namespace)、服务账号(ServiceAccount)、资源定额(ResourceQuota)等的管理,当某个Node意外宕机时,Controller Manager会及时发现并执行自动化修复流程,确保集群始终处于预期的工作状态。
Controller Manager 内部包含Replication Controller、 Node Controller、ResourceQuota Controller、 Namespace Controller、 ServiceAccountController、 Token Controller、Service Controller及Endpoint Controller等多个Controller,每种Controller都负责一种具体的控制流程,而ControllerManager正是这些Con位oller的核心管理者。
ReplicationController 用来确保容器应用的副本数始终保持在用户定义的副本数,即如果有容器异常退出,会自动创建新的 Pod 来替代;而如果异常多出来的容器也会自动回收。
在新版本的 Kubernetes 中建议使用 ReplicaSet 来取代 ReplicationController。ReplicaSet 跟 ReplicationController 没有本质的不同,只是名字不一样,并且 ReplicaSet 支持新的基于集合的标签 selector (in notin),Replication Controller对选择器仅支持基于等值的关系(= !=)。
虽然 ReplicaSet 可以独立使用,但一般还是建议使用 Deployment 来自动管理 ReplicaSet,这样就无需担心跟其他机制的不兼容问题(比如 ReplicaSet 不支持rolling-update滚动更新、rollout undo回滚、scale扩缩容,暂停rollout pause和继续rollout resume,但 Deployment 支持)。
运行与基础云提供商交互的控制器,从k8s 1.6之后出现的新功能
也包括了多个控制器:节点(Node)控制器、路由(Route)控制器、服务控制器、数据卷(Volume)控制器
etcd是Kubernetes提供默认的存储系统,保存所有集群数据,使用时需要为etcd数据提供备份计划。分布式键值存储系统,用于保存集群状态数据,比如Pod、Service等对象信息。
群集 DNS是一个DNS服务器,能够为 Kubernetes services提供 DNS记录。 由Kubernetes启动的容器自动将这个DNS服务器包含在他们的DNS searches中。
Cluster-level logging,负责保存容器日志,搜索/查看日志。
真正的工作节点,运行业务应用的容器;节点组件运行在Node,提供Kubernetes运行时环境,以及维护Pod。
kubelet 节点管理工具,Worker Node 的监视器,以及与 Master Node 的通讯,是Master在Node节点上的Agent(代理),管理本机运行容器的生命周期,比如创建容器,Pod挂载数据卷,下载secret,获取容器和节点状态等工作,kubelet 将每个Pod转换成一组容器,可以理解为当在master上执行创建、删除对象时,kubelet负责执行从kube-apiserver下达的指令,master派到node节点代表,管理本机容器。
kube-proxy 网络管理器,运行在每个node节点上,管理维护node上的网络规则,提供网络代理,四层负载均衡
kube-proxy 是集群中每个节点(node)上所运行的网络代理, 实现 Kubernetes 服务(Service) 概念的一部分。kube-proxy 负责为 Service实现了一种VIP(虚拟 IP)。kube-proxy 维护节点上的一些网络规则, 这些网络规则会允许从集群内部或外部的网络会话与 Pod 进行网络通信。如果操作系统提供了可用的数据包过滤层,则 kube-proxy 会通过它来实现网络规则。 否则,kube-proxy 仅做流量转发。 kubeproxy提供以下几种代理模式:
因为deployment包含replicaset包含pod,所以创建deployment的时候会连续创建三种资源
1、准备好一个包含应用程序的Deployment的yml文件,然后通过kubectl客户端工具发送给ApiServer,ApiServer 接收到客户端的请求并将资源内容存储到数据库(etcd)中。 3、Deployment Controller 监控Apiserver状态发生了改变,知道了Apiserver需要创建一个deployment资源对象 4、Deployment Controller 根据Apiserver的状态信息,想要去创建一个ReplicaSet资源对象,此时Apiserver检测到了Deployment Controller 的期望状态的变化,再次记录到数据库etcd中 5、ReplicaSet 监控 Apiserver 状态发生了改变,知道了 Apiserver 需要创建一个 ReplicaSet资源对象 6、ReplicaSet 根据 Apiserver 的状态信息,想要去创建一个 Pod,此时Apiserver检测到了 ReplicaSet 的期望状态的变化,再次记录到数据库etcd中
此时最低级别的pod的创建准备工作也完成,此时实际上pod,replicaset,deployment三种资源都没有实际被创建,刚才所说的创建类似于一种演习
7、Scheduler 监控 Apiserver 状态发生了改变,发现尚未被分配到具体执行节点(node)的Pod 8、Scheduler 根据一组相关规则将pod分配到可以运行它们的节点上,并更新数据库,记录pod分配情况。 8、Kubelet 监控 Apiserver状态发生了改变,此时kubelet知道自己该创建pod了,但是它本身并没有创建容器的功能,所以它通知了有创建容器功能的docker引擎,并把创建的约束信息(如镜像名称,镜像源等)传递给docker引擎 9、docker引擎根据收到的信息开始拉取镜像,创建容器,此时,deployment才算真正创建成功
kuberproxy运行在集群各个主机上,管理网络通信,如服务发现、负载均衡。例如当有数据发送到主机时,将其路由到正确的pod或容器。对于从主机上发出的数据,它可以基于请求地址发现远程服务器,并将数据正确路由,在某些情况下会使用轮训调度算法(Round-robin)将请求发送到集群中的多个实例。
随后我们通过Kubectl提交一个映射到该Pod的Server的创建请求,Controller Manager会通过Label标签查询到相关联的Pod实例,然后生成Service的Endpoints信息;接下来,所有Node上运行的Proxy进程通过API Server查询并监听Service对象及其对应的Endpoints信息,建立一个负载均衡器来实现Service访问到后端Pod的流量转发功能;

1、客户端提交Pod创建请求,可以通过API Server的Restful API,也可以使用kubectl命令行工具。支持的数据类型包括JSON和YAML。 2、API Server处理用户请求,存储Pod数据到etcd。 3、controller-manager监控API Server实时监听 Pod 事件,Pod 控制器知道了需要创建一个pod资源对象,通过API Server将pod的配置信息存储到etcd。 4、Scheduler通过API Server实时监听 Pod 事件,发现新的未被调度的Pod。尝试为Pod分配主机。 5、过滤主机 (调度预选):调度器用一组规则过滤掉不符合要求的主机。比如Pod指定了所需要的资源量,那么可用资源比Pod需要的资源量少的主机会被过滤掉。 6、主机打分(调度优选):对第一步筛选出的符合要求的主机进行打分,在主机打分阶段,调度器会考虑一些整体优化策略,比如把容一个Replication Controller的副本分布到不同的主机上,使用最低负载的主机等。 7、选择主机:选择打分最高的主机,进行binding操作,通过API Server将结果存储到etcd中。 8、kubelet通过API Server实时监听 Pod 事件,发现新Pod 被调度到当前节点,调用 cri 接口让容器运行时启动容器。 9、绑定成功后,scheduler会调用APIServer的API在etcd中创建一个boundpod对象,描述在一个工作节点上绑定运行的所有pod信息。运行在每个工作节点上的kubelet也会定期与etcd同步boundpod信息,一旦发现应该在该工作节点上运行的boundpod对象没有更新,则调用Docker API创建并启动pod内的容器。

namespace 状态为 terminating, 无法删除 Error from server (AlreadyExists): object is being deleted: namespaces "monitoring" already exists
xxxxxxxxxxNAMESPACE=monitoringkubectl get namespace $NAMESPACE -o json > $NAMESPACE.jsonsed -i -e 's/"kubernetes"//' $NAMESPACE.jsonkubectl replace --raw "/api/v1/namespaces/$NAMESPACE/finalize" -f ./$NAMESPACE.json
设置K8S中的容器时区 为Asia/Shanghai
xxxxxxxxxxapiVersion: apps/v1kind: Deploymentmetadata:name: demospec:replicas: 1selector:matchLabels:app: myapptemplate:metadata:labels:app: myappspec:containers:- name: demoimage: xxxximagePullPolicy: Alwaysenv:- name: TZvalue: Asia/Shanghai
实际上,k8s在进行调度时,计算的就是requests的值,不管你limits设置多少,k8s都不关心。所以当这个值没有达到资源瓶颈时,理论上,该节点就会一直有pod调度上去。所以这个时候就会出现调度不均衡的问题。有什么解决办法?
给每一个pod设置requests和limits,如果资源充足,最好将requests和limits设置成一样的,提高Pod的QoS
重平衡,采取人为介入或定时任务方式,根据多维度去对当前pod分布做重平衡
在容器环境下,Java只能获取服务器的配置,无法感知容器内存限制。可以通过-XX:MaxRAMPercentage限制堆大小。
推荐的JVM参数设置。
xxxxxxxxxx-XX:+UseContainerSupport -XX:InitialRAMPercentage=70.0 -XX:MaxRAMPercentage=70.0 -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/logs/xxx.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/logs/xxx.hprof参数说明如下。
| 参数 | 说明 |
|---|---|
-XX:+UseContainerSupport | 使用容器内存。允许JVM从主机读取cgroup限制,例如可用的CPU和RAM,并进行相应的配置。当容器超过内存限制时,会抛出OOM异常,而不是强制关闭容器。 |
-XX:InitialRAMPercentage | 设置JVM使用容器内存的初始百分比。建议与-XX:MaxRAMPercentage保持一致,推荐设置为70.0。 |
-XX:MaxRAMPercentage | 设置JVM使用容器内存的最大百分比。由于存在系统组件开销,建议最大不超过75.0,推荐设置为70.0。 |
-XX:+PrintGCDetails | 输出GC详细信息。 |
-XX:+PrintGCDateStamps | 输出GC时间戳。日期形式,例如2019-12-24T21:53:59.234+0800。 |
-Xloggc:/logs/xxx.log | GC日志文件路径。需保证Log文件所在容器路径已存在,建议您将该容器路径挂载到NAS目录或收集到SLS,以便自动创建目录以及实现日志的持久化存储。 |
-XX:+HeapDumpOnOutOfMemoryError | JVM发生OOM时,自动生成DUMP文件。 |
-XX:HeapDumpPath=/logs/xxx.hprof | DUMP文件路径。需保证DUMP文件所在容器路径已存在,建议您将该容器路径挂载到NAS目录,以便自动创建目录以及实现日志的持久化存储。 |
说明
-XX:+UseContainerSupport 选项,而且是默认启用的。-XX:+PrintGCDetails、-XX:+PrintGCDateStamps、-Xloggc:$LOG_PATH/gc.log参数已废弃,请使用参数-Xlog:gc:$LOG_PATH/gc.log代替。补充说明
-XX:+UseContainerSupport
-XX:+UseContainerSupport是一个Java虚拟机参数,用于启用或禁用Java容器支持。当该参数设置为true时,Java虚拟机将使用容器化相关的附加配置来优化应用程序的性能和资源管理。该参数通常在运行Java应用程序时与容器化平台集成时使用。 在容器环境中,Java应用程序通常需要更好地适应与资源的交互,包括更好地处理内存、CPU和网络资源。启用UseContainerSupport参数后,Java虚拟机将执行以下操作:
总之,启用UseContainerSupport参数可以更好地适应容器化平台,并具有更好的资源管理和性能优化能力,以提高Java应用程序在容器环境中的整体效率。
-XX:InitialRAMPercentage
-XX:MaxRAMPercentage、-XX:MinRAMPercentage
xxxxxxxxxxkubectl describe node | grep Runtimeps -ef | grep kubelet |grep runtimeps aux | grep kubelet |grep runtime
xxxxxxxxxx# serviceSubnet:cat /etc/kubernetes/manifests/kube-controller-manager.yaml | grep cluster-cidr# podSubnet:cat /etc/kubernetes/manifests/kube-apiserver.yaml | grep service-cluster-ip-rangekubectl get -n kube-system pod -l component=kube-apiserver -o yaml | grep service-cluster-ip-rangekubectl get -n kube-system pods kube-controller-manager-master -oyaml | grep cluster-cidrkubectl get -n kube-system pods kube-controller-manager-master -oyaml | grep service-cluster-ip-range
PVC 是命名空间级别的资源,而 PV 是集群级别的资源。
持久卷
xxxxxxxxxxroot@opj-sbx-cdp009:~# kgpv -oyaml rdsl-opj-ldp-common-778bb38fapiVersion: v1kind: PersistentVolumemetadata:name: rdsl-opj-ldp-common-778bb38fspec:accessModes:- ReadWriteOncecapacity:storage: 30GihostPath:path: /opt/roche/home/data/rdsl-opj-ldp/commontype: DirectoryOrCreatepersistentVolumeReclaimPolicy: RetainvolumeMode: Filesystem
accessModes 访问模式包括:
persistentVolumeReclaimPolicy 回收策略有:
一个volume卷处于以下几个阶段之一:
pv中的spec: claimRef 字段用于表示pv与pvc之间的关系。
claimRef 字段将被清除,PV 的状态将变为 Released,等待其他 PVC 的绑定,或者在 ReclaimPolicy 为 Retain 时手动管理。若pv为Retain策略,处于Released状态时,删除claimRef可以重新被BoundclaimRef 让 Kubernetes 跟踪和管理这个关系。xxxxxxxxxxspec:claimRef:apiVersion: v1kind: PersistentVolumeClaimname: rdsl-opj-ldp-pvc-common <- 这是对应 PVC 的名称namespace: xxx <- PVC 所在命名空间。PVC是命名空间级别的资源,pv是集群级别的。resourceVersion: "xxx"uid: xxx
持久卷声明
xxxxxxxxxxroche@opj-sbx-cdp009:~$ kgpvc rdsl-opj-ldp-pvc-common -oyamlapiVersion: v1kind: PersistentVolumeClaimmetadata:name: rdsl-opj-ldp-pvc-commonspec:accessModes:- ReadWriteOnceresources:requests:storage: 30GivolumeMode: FilesystemvolumeName: rdsl-opj-ldp-common-778bb38f <- 这是对应 PV 的名字
前面说了init容器initContainers,这主要是为应用容器做前期准备工作的,一般都会用到shell脚本,这就会用到command,这里写写command的用法。
command就是将命令在创建的容器中执行,有这些命令去完成一些工作,command用法和dockerfile中的cmd差不多, command可以单独写,也可以分成command和参数args,可以参考之前的CMD去理解,例如下面的写法都可以。
xxxxxxxxxxcommand: ['/bin/sh']command: ["rm", "-fr", "/var/lib/dbs/lost+found"]command:- 'sh'- '-c'- 'DATA_SOURCE_NAME="root@(localhost:3306)/" /bin/mysqld_exporter'command: ["sh", "-c", "until nslookup myservice; do echo waiting for myservice; sleep 2; done;"]command: ["sh"]args: ["-c","until nslookup myservice; do echo waiting for myservice; sleep 2; done;"]livenessProbe:exec:command:- /bin/sh- -c- 'wget -O - -q --header "Authorization: Basic `echo -n \"$RABBIT_MANAGEMENT_USER:$RABBIT_MANAGEMENT_PASSWORD\" | base64`" http://localhost:15672/api/healthchecks/node | grep -qF "{\"status\":\"ok\"}"'initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds }}periodSeconds: {{ .Values.livenessProbe.periodSeconds }}
另外args还有一种写法,可以理解成args后面是一个.sh文件,command来直接执行一个脚本文件,可以写相对复杂的脚本。
xxxxxxxxxxcommand: ['sh']args:- "-c"- |set -exif [ ! -d "/opt/ShenTong/odbs/OSRDB" ];thenmkdir /opt/ShenTong/odbs/cp -r /opt/OSRDB /opt/ShenTong/odbs/elseecho "数据库文件已存在"fi
最后贴一个官方写的一个rabbitmq的完整例子
xxxxxxxxxxspec:{{- if .Values.image.pullSecrets }}imagePullSecrets:{{- range .Values.image.pullSecrets }}- name: {{ . }}{{- end }}{{- end }}terminationGracePeriodSeconds: {{ .Values.terminationGracePeriodSeconds }}securityContext:{{ toYaml .Values.securityContext | indent 10 }}serviceAccountName: {{ template "rabbitmq-ha.serviceAccountName" . }}initContainers:- name: bootstrapimage: {{ .Values.registry }}{{ .Values.busyboxImage.repository}}{{ .Values.arch }}:{{ .Values.busyboxImage.tag}}imagePullPolicy: {{ .Values.busyboxImage.pullPolicy }}command: ['sh']args:- "-c"- |set -excp /configmap/* /etc/rabbitmqrm -f /var/lib/rabbitmq/.erlang.cookie{{- if .Values.forceBoot }}if [ -d "${RABBITMQ_MNESIA_DIR}" ]; thentouch "${RABBITMQ_MNESIA_DIR}/force_load"fi{{- end }}env:- name: POD_NAMEvalueFrom:fieldRef:apiVersion: v1fieldPath: metadata.name- name: RABBITMQ_MNESIA_DIRvalue: /var/lib/rabbitmq/mnesia/rabbit@$(POD_NAME).{{ template "rabbitmq-ha.fullname" . }}-discovery.{{ .Release.Namespace }}.svc.{{ .Values.clusterDomain }}resources:{{ toYaml .Values.initContainer.resources | indent 12 }}volumeMounts:- name: configmapmountPath: /configmap- name: configmountPath: /etc/rabbitmq- name: {{ .Values.persistence.name }}mountPath: /var/lib/rabbitmq
和虚拟机/物理机不同,K8s的容器提供标准输出stdout和文件两种方式。在容器中,标准输出将日志直接输出到stdout或stderr,而DockerEngine接管stdout和stderr文件描述符,将日志接收后按照DockerEngine配置的LogDriver规则进行处理;日志打印到文件的方式和虚拟机/物理机基本类似,只是日志可以使用不同的存储方式,例如默认存储、EmptyDir、HostVolume、NFS等。 虽然使用Stdout打印日志是Docker官方推荐的方式,但大家需要注意这个推荐是基于容器只作为简单应用的场景,实际的业务场景中我们还是建议大家尽可能使用文件的方式,主要的原因有以下几点:
因此我们建议线上应用使用文件的方式输出日志,Stdout只在功能单一的应用或一些K8s系统/运维组件中使用。
采集容器日志,Agent有两种部署方式:
两种部署方式的优劣都显而易见:
Tip:正常情况下,优先使用 DaemonSet 的方式采集日志,如果单个Pod日志量特别大,超过一般 Agent 发送吞吐量,可以单独对该 Pod 使用 Sidecar 的方式采集日志。
如果使用容器运行时的是docker,正常情况下我们可以在节点的docker路径中找到容器的stdout的日志,默认为/var/lib/docker/containers/{containerId}/{containerId}-json.log。在Kubernetes 1.14版本之前,kubelet会在/var/log/pods///.log建立一个软链接到stdout文件中。类似如下所示:
root@master0:/var/log/pods# tree
在Kubernetes 1.14版本之后,改成了/var/log/pods/<namespace>_<pod_name>_<pod_id>/<container_name>/<num>.log的形式。
root@master-0:/var/log/pods# tree
所以,对于 Agent 采集标准输出日志来说,也就是采集节点上的这些日志文件。一种简单粗暴的采集方式是,使用DaemonSet部署日志Agent,挂载/var/log/pods目录,Agent的配置文件使用类似/var/log/pod.log去通配日志文件,采集节点上所有的容器标准输出。
但是这样的局限在于:
当然现在的一些日志Agent比如Filebeat/Fluentd都针对性的做了支持,比如可以将namespace/pod等信息注入日志中,但仍然没有解决大部分的问题。所以,这种方式只适合简单的业务场景,后续也难以满足其他更多的日志需求。
如果 Pod 里不仅仅是输出 stdout,还包括日志文件,就需要考虑到挂载日志文件到节点上,同时采用DaemonSet部署的Agent也需要挂载相同的目录,否则采用容器化部署的Agent 无法查看到相应的文件,更无法采集。业务Pod挂载日志路径的方式有以下几种:
(1) emtpyDir
emtpyDir 的生命周期跟随Pod,Pod销毁后其中存储的日志也会消失。
(2) hostPath
生命周期和Pod无关,Pod迁移或者销毁,日志文件还保留在现有磁盘上。
为了解决隔离性,避免多个Pod打印日志到相同的路径和文件中,我们需要使用 subPathExpr 字段从 Downward API 环境变量构造 subPath 目录名。该 VolumeSubpathEnvExpansion 功能从 Kubernetes1.15 开始默认开启,在1.17 GA。
(3) Pv
Pv的访问模式包括:
对于大部分的业务来说,都是Deployment无状态部署,需要挂载同一个Pv共享;对于一些中间件等有状态服务,一般会使用StatefulSet部署,每个Pod会使用独立的Pv。
虽然同样可以在Node上找到使用Pv挂载的对应日志文件,但是Pv根据不同的底层实现,在Node上的路径会有一定的区别。目前市面上大部分日志Agent均对这些挂载方式没有感知,所以你能做的和上面使用stdout的方式类似,也就是简单粗暴的让Agent将路径都挂载,使用通配的方式采集所有的日志,使用上的局限和stdout的方式同样一致。