Kubernetes(六)
一、Kubernetes Container、Pod、Namespace内存及CPU限制
1.1 Container
https://kubernetes.io/zh-cn/docs/tasks/configure-pod-container/assign-cpu-resource/
https://kubernetes.io/zh-cn/docs/tasks/configure-pod-container/assign-memory-resource/
- CPU 限制:指定容器可以使用的最大 CPU 资源量。
- CPU 请求:指定容器正常运行所需的最小 CPU 资源量。
- 内存限制:指定容器可以使用的最大内存量。
- 内存请求:指定容器正常运行所需的最小内存量。
要为容器指定请求,请在容器资源清单中包含 resources: requests
字段。 要指定限制,请包含 resources:limits
。
示例:
apiVersion: apps/v1 kind: Deployment metadata: name: limit-test-deployment namespace: web spec: replicas: 1 selector: matchLabels: app: limit-test-pod template: metadata: labels: app: limit-test-pod spec: containers: - name: limit-test-container image: lorel/docker-stress-ng resources: limits: # 限制,最多使用资源 cpu: "2" # 2颗CPU memory: "512Mi" # 512M内存 requests: # 请求,最少需要资源 memory: "512M" # 512M内存 cpu: "2" # 2颗CPU args: ["--vm", "3", "--vm-bytes", "256M"]
建议将限制与请求资源值设置相同。
1.1.1 CPU
1.1.1.1 CPU单位
CPU 资源以 CPU 单位度量。Kubernetes 中的一个 CPU 等同于:
- 1 个 AWS vCPU
- 1 个 GCP核心
- 1 个 Azure vCore
- 裸机上具有超线程能力的英特尔处理器上的 1 个超线程
小数值是可以使用的。一个请求 0.5 CPU 的容器保证会获得请求 1 个 CPU 的容器的 CPU 的一半。 你可以使用后缀 m
表示毫。例如 100m
CPU、100 milliCPU 和 0.1 CPU 都相同。 精度不能超过 1m。
CPU 请求只能使用绝对数量,而不是相对数量。0.1 在单核、双核或 48 核计算机上的 CPU 数量值是一样的。
1.1.1.2 如果不指定 CPU 限制
如果没有为容器指定 CPU 限制,则会发生以下情况之一:
- 容器在可以使用的 CPU 资源上没有上限。因而可以使用所在节点上所有的可用 CPU 资源。
- 容器在具有默认 CPU 限制的名字空间中运行,系统会自动为容器设置默认限制。 集群管理员可以使用 LimitRange 指定 CPU 限制的默认值。
1.1.1.3 如果设置了 CPU 限制但未设置 CPU 请求
如果为容器指定了 CPU 限制值但未为其设置 CPU 请求,Kubernetes 会自动为其 设置与 CPU 限制相同的 CPU 请求值。类似的,如果容器设置了内存限制值但未设置 内存请求值,Kubernetes 也会为其设置与内存限制值相同的内存请求。
1.1.1.4 CPU 请求和限制的初衷
通过配置集群中运行的容器的 CPU 请求和限制,可以有效利用集群上可用的 CPU 资源。 通过将 Pod CPU 请求保持在较低水平,可以使 Pod 更有机会被调度。 通过使 CPU 限制大于 CPU 请求,可以完成两件事:
- Pod 可能会有突发性的活动,它可以利用碰巧可用的 CPU 资源。
- Pod 在突发负载期间可以使用的 CPU 资源数量仍被限制为合理的数量。
1.1.2 内存
1.1.2.1 内存单位
内存资源的基本单位是字节(byte)。可以使用以下后缀之一,将内存表示为纯整数或定点整数:E、P、T、G、M、K、Ei、Pi、Ti、Gi、Mi、Ki。
1.1.2.2 如果没有指定内存限制
如果没有为一个容器指定内存限制,则自动遵循以下情况之一:
- 容器可无限制地使用内存。容器可以使用其所在节点所有的可用内存, 进而可能导致该节点调用 OOM Killer。 此外,如果发生 OOM Kill,没有资源限制的容器将被杀掉的可行性更大。
- 运行的容器所在命名空间有默认的内存限制,那么该容器会被自动分配默认限制。 集群管理员可用使用 LimitRange 来指定默认的内存限制。
1.1.2.3 内存请求和限制的目的
通过为集群中运行的容器配置内存请求和限制,可以有效利用集群节点上可用的内存资源。 通过将 Pod 的内存请求保持在较低水平,可以更好地安排 Pod 调度。 通过让内存限制大于内存请求,可以完成两件事:
- Pod 可以进行一些突发活动,从而更好的利用可用内存。
- Pod 在突发活动期间,可使用的内存被限制为合理的数量。
1.2 Pod
https://kubernetes.io/zh-cn/docs/concepts/policy/limit-range/
LimitRange 是限制命名空间内可为每个适用的对象类别 (例如 Pod 或 PersistentVolumeClaim) 指定的资源分配量(限制和请求)的策略对象。
一个 LimitRange(限制范围) 对象提供的限制能够做到:
- 在一个命名空间中实施对每个 Pod 或 Container 最小和最大的资源使用量的限制。
- 在一个命名空间中实施对每个 PersistentVolumeClaim 能申请的最小和最大的存储空间大小的限制。
- 在一个命名空间中实施对一种资源的申请值和限制值的比值的控制。
- 设置一个命名空间中对计算资源的默认申请/限制值,并且自动的在运行时注入到多个 Container 中。
当某命名空间中有一个 LimitRange 对象时,将在该命名空间中实施 LimitRange 限制。
示例:
apiVersion: v1 kind: LimitRange metadata: name: limitrange-web namespace: web spec: limits: - type: Container #限制的资源类型Container max: cpu: "2" #限制单个容器的最大CPU memory: "2Gi" #限制单个容器的最大内存 min: cpu: "500m" #限制单个容器的最小CPU memory: "512Mi" #限制单个容器的最小内存 default: cpu: "500m" #默认单个容器的CPU限制 memory: "512Mi" #默认单个容器的内存限制 defaultRequest: cpu: "500m" #默认单个容器的CPU创建请求 memory: "512Mi" #默认单个容器的内存创建请求 maxLimitRequestRatio: cpu: 2 #限制CPU limit/request比值最大为2 memory: 2 #限制内存limit/request比值最大为1.5 - type: Pod #限制的资源类型Pod max: cpu: "4" #限制单个Pod的最大CPU memory: "4Gi" #限制单个Pod最大内存 - type: PersistentVolumeClaim #限制的资源类型PVC max: storage: 50Gi #限制PVC最大的requests.storage min: storage: 30Gi #限制PVC最小的requests.storage
1.3 Namespace
ResourceQuota 在 web 命名空间中设置如下要求:
- 在该命名空间中的每个 Pod 的所有容器都必须要有内存请求和限制,以及 CPU 请求和限制。
- 在该命名空间中所有 Pod 的内存请求总和不能超过 8 GiB
- 在该命名空间中所有 Pod 的内存限制总和不能超过 8 GiB
- 在该命名空间中所有 Pod 的 CPU 请求总和不能超过 8 cpu
- 在该命名空间中所有 Pod 的 CPU 限制总和不能超过 8 cpu
- 在该命名空间中所有 Pod 的 nvidia GPU 请求总和不能超过 4
- 在该命名空间中所有 Pod数量不能超过 6
- 在该命名空间中所有 service 数量不能超过 6
示例:
apiVersion: v1 kind: ResourceQuota metadata: name: quota-web namespace: web spec: hard: requests.cpu: "8" limits.cpu: "8" requests.memory: 8Gi limits.memory: 8Gi requests.nvidia.com/gpu: 4 pods: "6" services: "6"
二、nodeSelector、nodeName、node亲和与反亲和
https://kubernetes.io/zh-cn/docs/concepts/scheduling-eviction/assign-pod-node/
2.1 nodeSelector
https://kubernetes.io/zh-cn/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector
nodeSelector
是节点选择约束的最简单推荐形式。可以将 nodeSelector
字段添加到 Pod 的规约中设置希望目标节点所具有的节点标签。 Kubernetes 只会将 Pod 调度到拥有指定的每个标签的节点上。
示例
kind: Deployment apiVersion: apps/v1 metadata: labels: app: web-tomcat-app1-deployment-label name: web-tomcat-app1-deployment namespace: magedu spec: replicas: 4 selector: matchLabels: app: web-tomcat-app1-selector template: metadata: labels: app: web-tomcat-app1-selector spec: containers: - name: web-tomcat-app1-container image: tomcat:7.0.94-alpine imagePullPolicy: Always ports: - containerPort: 8080 protocol: TCP name: http env: - name: "password" value: "123456" - name: "age" value: "18" resources: limits: cpu: 1 memory: "512Mi" requests: cpu: 500m memory: "512Mi" nodeSelector: # node标签选择 project: web # label字段project=web disktype: ssd # label字段disktype=ssd
该pod只调度到同时具有project=web、disktype=ssd标签的node上。
添加标签说明
# 添加标签 kubectl label node 10.0.0.41 project="web" # 查看标签 [root@k8s-deploy Affinit-case]#kubectl get node 10.0.0.41 --show-labels NAME STATUS ROLES AGE VERSION LABELS 10.0.0.41 Ready node 22d v1.24.8 beta.kubernetes.io/arch=amd64...kubernetes.io/role=node,project=web # 删除标签 kubectl label node 10.0.0.41 project-
2.2 nodeName
https://kubernetes.io/zh-cn/docs/concepts/scheduling-eviction/assign-pod-node/#nodename
nodeName
是 Pod 规约中的一个字段。如果 nodeName
字段不为空,调度器会忽略该 Pod, 而指定节点上的 kubelet 会尝试将 Pod 放到该节点上。
示例:
kind: Deployment apiVersion: apps/v1 metadata: labels: app: web-tomcat-app2-deployment-label name: web-tomcat-app2-deployment namespace: web spec: replicas: 1 selector: matchLabels: app: web-tomcat-app2-selector template: metadata: labels: app: web-tomcat-app2-selector spec: nodeName: 10.0.0.41 # nodeName containers: - name: web-tomcat-app2-container image: tomcat:7.0.94-alpine imagePullPolicy: IfNotPresent ports: - containerPort: 8080 protocol: TCP name: http env: - name: "password" value: "123456" - name: "age" value: "18" resources: limits: cpu: 1 memory: "512Mi" requests: cpu: 500m memory: "512Mi"
该pod只调度到10.0.0.41 node上。
2.3 node亲和
nodeSelector
提供了一种最简单的方法来将 Pod 约束到具有特定标签的节点上。亲和性和反亲和性扩展了可以定义的约束类型。使用亲和性与反亲和性的一些好处有:
- 亲和性、反亲和性语言的表达能力更强。
nodeSelector
只能选择拥有所有指定标签的节点。 亲和性、反亲和性为你提供对选择逻辑的更强控制能力。 - 可以标明某规则是“软需求”或者“偏好”,这样调度器在无法找到匹配节点时仍然调度该 Pod。
- 可以使用节点上(或其他拓扑域中)运行的其他 Pod 的标签来实施调度约束, 而不是只能使用节点本身的标签。这个能力让你能够定义规则允许哪些 Pod 可以被放置在一起。
节点亲和性概念上类似于 nodeSelector
, 它使你可以根据节点上的标签来约束 Pod 可以调度到哪些节点上。 节点亲和性有两种:
-
requiredDuringSchedulingIgnoredDuringExecution
: 调度器只有在规则被满足的时候才能执行调度。此功能类似于nodeSelector
, 但其语法表达能力更强。 -
preferredDuringSchedulingIgnoredDuringExecution
: 调度器会尝试寻找满足对应规则的节点。如果找不到匹配的节点,调度器仍然会调度该 Pod。
requiredDuringSchedulingIgnoredDuringExecution
硬亲和
示例
kind: Deployment apiVersion: apps/v1 metadata: labels: app: web-tomcat-app2-deployment-label name: web-tomcat-app2-deployment namespace: web spec: replicas: 5 selector: matchLabels: app: web-tomcat-app2-selector template: metadata: labels: app: web-tomcat-app2-selector spec: containers: - name: web-tomcat-app2-container image: tomcat:7.0.94-alpine imagePullPolicy: IfNotPresent ports: - containerPort: 8080 protocol: TCP name: http affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: #匹配条件1,同一个key的多个value只有有一个匹配成功就认为当前key匹配成功 - key: disktype operator: In values: - ssd - hddx - key: project #匹配条件2,当前key也要匹配成功一个value,即条件1和条件2必须同时每个key匹配成功一个value operator: In values: - web - matchExpressions: #匹配条件3,有一个key但是有多个values、则只要匹配成功一个value就可以调度 - key: project operator: In values: - mmm #即使这俩条件都匹配不上也可以调度,即多个matchExpressions只要有任意一个能匹配任何一个value就可以调用。 - nnn
preferredDuringSchedulingIgnoredDuringExecution
软亲和
示例:
kind: Deployment apiVersion: apps/v1 metadata: labels: app: web-tomcat-app2-deployment-label name: web-tomcat-app2-deployment namespace: web spec: replicas: 1 selector: matchLabels: app: web-tomcat-app2-selector template: metadata: labels: app: web-tomcat-app2-selector spec: containers: - name: web-tomcat-app2-container image: tomcat:7.0.94-alpine imagePullPolicy: IfNotPresent ports: - containerPort: 8080 protocol: TCP name: http affinity: nodeAffinity: #可同时设置硬亲和和软亲和 requiredDuringSchedulingIgnoredDuringExecution: #硬亲和 nodeSelectorTerms: - matchExpressions: #硬匹配条件1 - key: "kubernetes.io/role" operator: NotIn values: - "master" #硬性匹配key的值kubernetes.io/role不包含master的节点,即绝对不会调度到master节点(node反亲和) preferredDuringSchedulingIgnoredDuringExecution: #软亲和 - weight: 80 #软亲和条件1,weight值越大优先级越高,越优先匹配调度 preference: matchExpressions: - key: project operator: In values: - web - weight: 60 #软亲和条件2,在条件1不满足时匹配条件2 preference: matchExpressions: - key: disktype operator: In values: - hdd
说明:
weight
字段,其取值范围是 1 到 100。 当调度器找到能够满足 Pod 的其他调度请求的节点时,调度器会遍历节点满足的所有的偏好性规则, 并将对应表达式的 weight
值加和。
最终的加和值会添加到该节点的其他优先级函数的评分之上。 在调度器为 Pod 作出调度决定时,总分最高的节点的优先级也最高
2.4 node反亲和
示例:
kind: Deployment apiVersion: apps/v1 metadata: labels: app: web-tomcat-app2-deployment-label name: web-tomcat-app2-deployment namespace: web spec: replicas: 1 selector: matchLabels: app: web-tomcat-app2-selector template: metadata: labels: app: web-tomcat-app2-selector spec: containers: - name: web-tomcat-app2-container image: tomcat:7.0.94-alpine imagePullPolicy: IfNotPresent ports: - containerPort: 8080 protocol: TCP name: http affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: #匹配条件1 - key: disktype operator: NotIn #调度的目的节点没有key为disktype且值为hdd的标签 values: - hdd #绝对不会调度到含有label的key为disktype且值为hdd的hdd的节点,即会调度到没有key为disktype且值为hdd的hdd的节点
2.5 affinity和nodeSelector对比
IgnoreDuringExecution
表示如果在pod运行期间Node的标签发生变化,导致亲和性策略不能满足,也会继续运行当前pod
Affinity与anti-affinity的目的也是控制pod的调度结果,但相对于nodeSelector,Affinity(亲和)与anti-affinity(反亲和)的功能更加强大。
- 亲和与反亲和对目的标签的选择匹配不仅仅支持and,还支持In,NotIn,Exists,DoesNotExist,Gt,Lt。
-
In
标签的值存在匹配列表中(匹配成功就调度到目的node,实现node亲和)
-
NotIn
标签的值不存在指定的匹配列表中(不会调度到目的node,实现反亲和)
-
Gt
标签的值大于某个值(字符串)
-
Lt
标签的值小于某个值(字符串)
-
Exists
指定的标签存在
-
可以设置软匹配和硬匹配,在软匹配下,如果调度器无法匹配节点,仍然将pod调度到其他不符合条件的节点。
-
还可以对pod定义亲和策略,比如允许哪些pod可以或者不可以被调度至同一个node。
三、pod亲和与反亲和、污点与容忍、驱逐
3.1 pod亲和与反亲和
- pod亲和性与反亲和性可以基于已经存在node节点上运行的pod标签来约束新创建的pod可以调度到的目的节点,注意不是基于node上的标签,而是使用的已经运行在node上的pod标签匹配。
- 其规则的格式为如果node节点A已经运行了一个或多个满足调度新创建的pod B的规则,那么新的pod B在亲和的条件下会调度到A节点上,而在反亲和性的情况下则不会调度到A节点上。
- 其中规则表示一个具有可选的关联命名空间列表的LabelSelector,之所以pod亲和与反亲和可以通过LabelSelector选择namespace,是因为pod是命名空间限定的,而node不属于任何namespace,所以node的亲和与反亲和不需要namespace,因此作用于pod标签的标签选择算符必须指定选择算符应用在哪个命名空间。
- 从概念上讲,node节点是一个拓扑域(具有拓扑结构的域),比如k8s集群中的单台node节点、一个机架、云供应商可用区、云供应商地理区域等,可以使用topologyKey来定义亲和或者反亲和的颗粒度是node级别还是可用区级别,以便kubernetes调度系统用来识别并选择正确的目的拓扑域。
注意:
- Pod 间亲和性和反亲和性都需要相当的计算量,因此会在大规模集群中显著降低调度速度。 不建议在包含数百个节点的集群中使用这类设置。
- Pod 反亲和性需要节点上存在一致性的标签。换言之, 集群中每个节点都必须拥有与
topologyKey
匹配的标签。 如果某些或者所有节点上不存在所指定的topologyKey
标签,调度行为可能与预期的不同。
3.1.1 Pod 间亲和性与反亲和性的类型
与节点亲和性类似,Pod 的亲和性与反亲和性也有两种类型:
-
requiredDuringSchedulingIgnoredDuringExecution
-
preferredDuringSchedulingIgnoredDuringExecution
- pod亲和性与反亲和性的合法操作符(operator)有In、NotIn、Exists、DoesNotExist
- 在pod亲和性配置中,在
requiredDuringSchedulingIgnoredDuringExecution
和preferredDuringSchedulingIgnoredDuringExecution
中,topologyKey
不允许为空(Empty topologyKey is not allowed
)。 - 在pod反亲和性配置中,
requiredDuringSchedulingIgnoredDuringExecution
和preferredDuringSchedulingIgnoredDuringExecution
中,topologyKey
也不允许为空(Empty topologyKey is not allowed
)。 - 对于
requiredDuringSchedulingIgnoredDuringExecution
要求的pod反亲和性,准入控制器LimitPodHardAntiAffinityTopology
被引入以确保topologyKey
只能是kubernetes.io/hostname
,如果希望topologyKey
也可以用于其他定制拓扑逻辑,可以更改准入控制器或禁用。 - 除上述情况外,
topologyKey
可以是任何合法的标签键。
3.1.2 pod亲和
先创建nginx测试pod
kind: Deployment apiVersion: apps/v1 metadata: labels: app: python-nginx-deployment-label name: python-nginx-deployment namespace: web spec: replicas: 1 selector: matchLabels: app: python-nginx-selector template: metadata: labels: app: python-nginx-selector project: python spec: containers: - name: python-nginx-container image: nginx:1.20.2-alpine imagePullPolicy: Always ports: - containerPort: 80 protocol: TCP name: http
查看label
[root@k8s-deploy Affinit-case]#kubectl get pod python-nginx-deployment-7ff6fd89f8-s27k4 -n web --show-labels -owide NAME READY STATUS RESTARTS AGE IP NODE LABELS python-nginx-deployment-7ff6fd89f8-s27k4 1/1 Running 0 30s 10.200.169.189 10.0.0.42 app=python-nginx-selector,pod-template-hash=7ff6fd89f8,project=python
硬亲和与软亲和,示例:
kind: Deployment apiVersion: apps/v1 metadata: labels: app: web-tomcat-app2-deployment-label name: web-tomcat-app2-deployment namespace: web spec: replicas: 1 selector: matchLabels: app: web-tomcat-app2-selector template: metadata: labels: app: web-tomcat-app2-selector spec: containers: - name: web-tomcat-app2-container image: tomcat:7.0.94-alpine imagePullPolicy: IfNotPresent ports: - containerPort: 8080 protocol: TCP name: http affinity: podAffinity: #Pod亲和 #requiredDuringSchedulingIgnoredDuringExecution: #硬亲和,必须匹配成功才调度,如果匹配失败则拒绝调度。 preferredDuringSchedulingIgnoredDuringExecution: #软亲和,能匹配成功就调度到一个topology,匹配不成功会由kubernetes自行调度。 - weight: 100 podAffinityTerm: labelSelector: #标签选择 matchExpressions: #正则匹配 - key: project operator: In values: - python topologyKey: kubernetes.io/hostname namespaces: - web
硬亲和:后创建的tomcat pod只调度到pod标签中key为project,value值在python的node上,即与nginx调度到同一个node上。
软亲和:后创建的tomcat pod尽可能调度到pod标签中key为project,value值在python的node上,即与nginx调度到同一个node上,若无法匹配,则有kubernetes自行调度。
3.1.3 pod反亲和
硬反亲和与软反亲和,示例
kind: Deployment apiVersion: apps/v1 metadata: labels: app: web-tomcat-app2-deployment-label name: web-tomcat-app2-deployment namespace: web spec: replicas: 1 selector: matchLabels: app: web-tomcat-app2-selector template: metadata: labels: app: web-tomcat-app2-selector spec: containers: - name: web-tomcat-app2-container image: tomcat:7.0.94-alpine imagePullPolicy: IfNotPresent #imagePullPolicy: Always ports: - containerPort: 8080 protocol: TCP name: http affinity: podAntiAffinity: # pod反亲和 requiredDuringSchedulingIgnoredDuringExecution: # 硬反亲和 - labelSelector: matchExpressions: - key: project operator: In values: - python topologyKey: "kubernetes.io/hostname" namespaces: - web preferredDuringSchedulingIgnoredDuringExecution: # 软反亲和 - weight: 100 podAffinityTerm: labelSelector: matchExpressions: - key: project operator: In values: - pythonx topologyKey: kubernetes.io/hostname namespaces: - web
硬亲和:后创建的tomcat pod不调度到pod标签中key为project,value值在python的node上,即不与nginx调度到同一个node上。
软亲和:后创建的tomcat pod尽可能不调度到pod标签中key为project,value值在pythonx的node上,即与不nginx调度到同一个node上。
3.2 污点与容忍
https://kubernetes.io/zh-cn/docs/concepts/scheduling-eviction/taint-and-toleration/
污点(Taint)用于node节点排斥pod调度,与亲和的作用是完全相反的,即taint的node和pod是排斥调度关系。
容忍(Toleration) 是应用于 Pod 上的。容忍度允许调度器调度带有对应污点的 Pod。 容忍度允许调度但并不保证调度:作为其功能的一部分, 调度器也会评估其他参数。
污点和容忍(Toleration)相互配合,可以用来避免 Pod 被分配到不合适的节点上。 每个节点上都可以应用一个或多个污点,这表示对于那些不能容忍这些污点的 Pod, 是不会被该节点接受的。
3.2.1 污点(Taint)
污点三种类型
-
NoScheduler
表示k8s不会将pod调度具有该污点的Node上
-
PreferNoScheduler
表示k8s尽量避免将pod调度到具有该污点的node上
-
NoExecute
表示k8s不会将pod调度到具有该污点的node上,同时会将Node上已经存在的pod强制驱逐出去
污点设置方法
# 添加污点 [root@k8s-deploy ~]#kubectl taint node 10.0.0.41 key1=value1:NoSchedule node/10.0.0.41 tainted # 查看污点 [root@k8s-deploy ~]#kubectl describe node 10.0.0.41 | grep Taint Taints: key1=value1:NoSchedule # 取消污点 [root@k8s-deploy ~]#kubectl taint node 10.0.0.41 key1:NoSchedule- node/10.0.0.41 untainted
3.2.2 容忍(Toleration)
定义pod的容忍度(即可以接收node的哪些污点),容忍后可以将pod调度至含有该污点的node。
基于operator的污点匹配:
- 如果operator是Equal,则需要指定value并且value的值需要等于tolerations的key。
- 如果operator的Exists,则容忍度不需要value而是直接匹配污点类型
示例如下:
apiVersion: v1 kind: Pod metadata: name: nginx labels: env: test spec: containers: - name: nginx image: nginx imagePullPolicy: IfNotPresent # 规则1 tolerations: - key: "key1" operator: "Equal" # Equal需要指定value value: "value1" effect: "NoSchedule" # 规则2 - key: "key2" operator: "Exists" # Exists不需要value effect: "NoSchedule"
operator
的默认值是 Equal
。
一个容忍度和一个污点相“匹配”是指它们有一样的键名和效果,并且:
- 如果
operator
是Exists
(此时容忍度不能指定value
),或者 - 如果
operator
是Equal
,则它们的value
应该相等
说明:存在两种特殊情况:
如果一个容忍度的 key
为空且 operator
为 Exists
, 表示这个容忍度与任意的 key、value 和 effect 都匹配,即这个容忍度能容忍任何污点。
如果 effect
为空,则可以与所有键名 key1
的效果相匹配。
3.3 驱逐
https://kubernetes.io/zh-cn/docs/concepts/scheduling-eviction/node-pressure-eviction/
节点压力驱逐是 kubelet 主动终止 Pod, 以回收节点上内存、磁盘空间等资源的过程。kubelet 监控集群节点的内存、磁盘空间和文件系统的 inode 等资源。 当这些资源中的一个或者多个达到特定的消耗水平, kubelet 可以主动地使节点上一个或者多个 Pod 强制驱逐,以防止当前node节点资源无法正常分配而引发的OOM(OutOfMemory)。
3.3.1 驱逐信号
驱逐信号是特定资源在特定时间点的当前状态。 kubelet 使用驱逐信号,通过将信号与驱逐条件进行比较来做出驱逐决定, 驱逐条件是节点上应该可用资源的最小量。
kubelet 使用以下驱逐信号:
-
memory.available
node节点可用内存,默认<100M
-
nodefs
是节点的主要文件系统,用于保存本地磁盘卷、emptyDir、日志存储等数据,默认是/var/lib/kubelet/,或者是kubelet通过--root-dir指定的磁盘挂载目录
nodefs.available
:nodefs的可用空间,默认<10%
nodefs.inodesFree
:nodefs的可用inode,默认<5% -
imagefs
可选文件系统,用于给容器提供运行时存储容器镜像和容器可写层
imagefs.available
:imagefs的磁盘空间可用百分比,默认<15%
imagefs.inodesFree
:imagefs的inode可用百分比 -
pid.available
可用pid百分比
示例
[root@k8s-master1 /]#cat /var/lib/kubelet/config.yaml ... evictionHard: imagefs.inodesFree: 15% imagefs.available: 15% memory.available: 300Mi nodefs.available: 10% nodefs.inodesFree: 5% pid.available: 5%
3.3.2 驱逐条件
可以为 kubelet 指定自定义驱逐条件,以便在作出驱逐决定时使用。驱逐条件的形式为 [eviction-signal][operator][quantity]
,其中:
-
eviction-signal
节点驱逐触发信号,进行判断是否驱逐,如通过cgroupfs获取memory.available
的值来进行下一步匹配。 -
operator
操作符,通过操作符对比条件匹配资源量是否触发驱逐, 比如<
(小于)。 -
quantity
使用量,例如1Gi
。quantity
的值必须与 Kubernetes 使用的数量表示相匹配,可以使用文字值或百分比(%
)。
例如,如果一个节点的总内存为 10Gi 并且你希望在可用内存低于 1Gi 时触发驱逐, 则可以将驱逐条件定义为 memory.available<10%
或 memory.available< 1G
,但两者不能同时使用。
软驱逐
软驱逐不会立即驱逐pod,可以自定义宽限期,在条件持续到宽限期还没有恢复,kubelet再强制杀死pod并触发驱逐。
可以使用以下标志来配置软驱逐条件:
-
eviction-soft
:软驱逐触发条件,如memory.available<1.5Gi
, 如果驱逐条件持续时长超过指定的宽限期,可以触发 Pod 驱逐。 -
eviction-soft-grace-period
:软驱逐宽限期, 如memory.available=1m30s
,定义软驱逐条件在触发 Pod 驱逐之前必须保持多长时间。 -
eviction-max-pod-grace-period
:在满足软驱逐条件而终止 Pod 时使用的最大允许宽限期(以秒为单位)。
硬驱逐
硬驱逐条件没有宽限期。当达到硬驱逐条件时, kubelet 会立即杀死 pod并驱逐。
可以使用 eviction-hard
标志来配置硬驱逐条件, 例如 memory.available<1Gi
。
kubelet 具有以下默认硬驱逐条件:
-
memory.available<100Mi
-
nodefs.available<10%
-
imagefs.available<15%
-
nodefs.inodesFree<5%
(Linux 节点)
kubelet默认配置
[root@k8s-master1 /]#cat /var/lib/kubelet/config.yaml ... evictionHard: imagefs.available: 15% memory.available: 300Mi nodefs.available: 10% nodefs.inodesFree: 5%
只有在没有更改任何参数的情况下,硬驱逐阈值才会被设置成这些默认值。 如果更改了任何参数的值,则其他参数的取值不会继承其默认值设置,而将被设置为零。 为了提供自定义值,应该分别设置所有阈值。
3.3.3 驱逐顺序
用于当node节点资源不足的时候自动将pod进行强制驱逐,以保证当前node节点的正常运行。
kubelet按QoS(服务质量等级)顺序排列和驱逐Pod:
https://kubernetes.io/zh-cn/docs/tasks/configure-pod-container/quality-service-pod/
- 首先考虑资源使用量超过其请求的
BestEffort
或Burstable
Pod。 这些 Pod 会根据它们的优先级以及它们的资源使用级别超过其请求的程度被逐出。 - 资源使用量少于请求量的
Guaranteed
Pod 和Burstable
Pod 根据其优先级被最后驱逐。
目前QoS等级包括以下三个,示例:
Guaranteed:limits和requests值相等,等级最高,最后被驱逐
apiVersion: v1 kind: Pod metadata: name: nginx labels: env: test spec: containers: - name: nginx image: nginx imagePullPolicy: IfNotPresent resources: requests: cpu: 500m memory: 256Mi limits: cpu: 500m memory: 512Mi
Burstable:limits和requests值不相等,等级折中,中间被驱逐
apiVersion: v1 kind: Pod metadata: name: nginx2 labels: env: test2 spec: containers: - name: nginx image: nginx imagePullPolicy: IfNotPresent resources: requests: cpu: 300m memory: 256Mi limits: cpu: 500m memory: 512Mi
BestEffort:没有限制,limits和requests值为空,等级最低,最先被驱逐
apiVersion: v1 kind: Pod metadata: name: nginx3 labels: env: test3 spec: containers: - name: nginx image: nginx imagePullPolicy: IfNotPresent
四、搭建ELK及kafka日志收集环境
日志收集流程

ELK架构

主机
类型 | IP | 主机名 | VIP |
---|---|---|---|
Elasticsearch-node1/Kibana |
10.0.0.51 |
es1 |
10.0.0.50 |
Elasticsearch-node2 | 10.0.0.52 | es2 | |
Elasticsearch-node3 | 10.0.0.53 | es3 | |
Logstash | 10.0.0.54 | logstash1 | —— |
Kafka1/zookeeper1 | 10.0.0.56 | kafka1 | 10.0.0.55 |
Kafka2/zookeeper2 | 10.0.0.57 | kafka2 | |
Kafka3/zookeeper3 | 10.0.0.58 | kafka3 |
准备环境
- 2c/4g内存(1c1g内存)/20g硬盘(该配置仅测试用)
- 最小化安装
Ubuntu 20.04 server
- 配置基础网络、更新源、SSH登录等
- 主机名、iptables、防火墙、内核参数和资源限制等系统配置
# 配置本地域名解析 echo "`hostname -I` `hostname`">> /etc/hosts
4.1 部署ELK
官网: https://www.elastic.co/cn/what-is/elk-stack
ELK是Elasticsearch、Logstash和Kibana这三个开源项目的首字母缩写。Elasticsearch是一个搜索和分析引擎。Logstash是服务端数据处理管道,能够同时从多个来源采集数据,转换数据,然后将数据发送到Elasticsearch等“存储库”中。Kibana则可以让用户在Elasticsearch中使用图形和图表对数据进行可视化。
Elastic Stack是ELK Stack的更新换代产品。
4.1.1 Elasticsearch
官网:https://www.elastic.co/cn/elasticsearch/
https://www.elastic.co/guide/en/elasticsearch/reference/current/deb.html#deb-repo
4.1.1.2 下载Elasticsearch
Elasticsearch依赖java环境,与jdk版本对应关系参考: https://www.elastic.co/cn/support/matrix#matrix_jvm
国内镜像下载地址: https://mirrors.tuna.tsinghua.edu.cn/elasticstack/
# 下载带jdk环境的安装包 wget https://mirrors.tuna.tsinghua.edu.cn/elasticstack/7.x/apt/pool/main/e/elasticsearch/elasticsearch-7.12.1-amd64.deb # 安装 dpkg -i elasticsearch-7.12.1-amd64.deb
4.1.1.3 修改配置
elasticsearch.yml
配置文件说明:https://www.ibm.com/docs/zh/bpm/8.5.6?topic=service-elasticsearch-configuration-properties
[root@es1 opt]#cat /etc/elasticsearch/elasticsearch.yml # ---------------------------------- Cluster ----------------------------------- # ELK集群名称,名称相同即属于同一个集群 cluster.name: ELK-Cluster # ------------------------------------ Node ------------------------------------ # 当前节点名称 node.name: node1 # ----------------------------------- Paths ------------------------------------ # ES数据保存目录 path.data: /elk/data # ES日志保存目录 path.logs: /elk/logs # ----------------------------------- Memory ----------------------------------- # 服务启动时锁定足够的内存,防止数据写入swap # bootstrap.memory_lock: true # ---------------------------------- Network ----------------------------------- # 监听IP network.host: 10.0.0.51 # 监听端口 http.port: 9200 # --------------------------------- Discovery ---------------------------------- # 集群中node节点发现列表 discovery.seed_hosts: ["10.0.0.51", "10.0.0.52","10.0.0.53"] # 集群初始化可以被选举为master节点列表 cluster.initial_master_nodes: ["10.0.0.51", "10.0.0.52","10.0.0.53"] # ---------------------------------- Various ----------------------------------- # 一个集群中的N个节点启动后,才允许进行数据恢复处理,默认为1 gateway.recover_after_nodes: 2 # 设置是否可以通过正则或者_all删除或关闭索引库,默认true表示必须需要显示指定索引库名称,生产环境建议设置true,删除索引库 # 必须指定,否则可能会误删索引库中的其他索引。 action.destructive_requires_name: true
【可选】生产环境可配置锁定内存参数
-
elasticsearch.yml
配置参数设置bootstrap.memory_lock: ture
-
修改锁定内存大小,建议最小和最大内存设置相同
[root@es1 opt]#cat /etc/elasticsearch/jvm.options|grep Xm -Xms4g #最小内存限制 -Xmx4g #最大内存限制
4.1.1.4 创建数据和日志目录,并授权
mkdir -p /elk/data /elk/logs chown -R elasticsearch:elasticsearch /elk/data /elk/logs
4.1.1.5 启动服务
systemctl enable elasticsearch.service systemctl start elasticsearch.service
查看服务状态
[root@es1 opt]<span class="hljs-comment">#systemctl is-active elasticsearch.service </span> active
4.1.1.6 验证监听端口
[root@es1 opt]#netstat -ntlp Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name ...... tcp6 0 0 10.0.0.51:9200 :::* LISTEN 11346/java tcp6 0 0 10.0.0.51:9300 :::* LISTEN 11346/java
4.1.1.7 配置VIP
#配置VIP [root@k8s-ha1 ~]#cat /etc/keepalived/keepalived.conf ... vrrp_instance k8s-master { ... virtual_ipaddress { ... 10.0.0.50/24 dev eth0 label eth0:3 # 添加VIP } ... } # 配置负责均衡 [root@k8s-ha1 ~]#cat /etc/haproxy/haproxy.cfg ... listen elasticsearch_http_9200 bind 10.0.0.50:9200 mode tcp server 10.0.0.51 10.0.0.51:9200 check inter 3s fall 3 rise 5 server 10.0.0.52 10.0.0.52:9200 check inter 3s fall 3 rise 5 server 10.0.0.53 10.0.0.53:9200 check inter 3s fall 3 rise 5
验证端口状态
[root@k8s-ha1 ~]#netstat -ntlp|grep 10.0.0.50 tcp 0 0 10.0.0.50:9200 0.0.0.0:* LISTEN 1548153/haproxy
4.1.1.8 浏览器访问

4.1.1.9 安装ElasticsearchHead插件
- 进入谷歌商店

- 添加插件

- 选择扩展程序->Elasticsearch Head

- 新建连接,输入Elasticsearch IP:端口

- 查看Elasticsearch集群

4.1.2 Kibana
官网: https://www.elastic.co/cn/kibana/
国内镜像下载地址: https://mirrors.tuna.tsinghua.edu.cn/elasticstack/
4.1.2.1 下载Kibana并安装
# 下载 wget https://mirrors.tuna.tsinghua.edu.cn/elasticstack/7.x/apt/pool/main/k/kibana/kibana-7.12.1-amd64.deb # 安装 dpkg -i kibana-7.12.1-amd64.deb
4.1.2.2 修改配置
[root@es1 opt]#cat /etc/kibana/kibana.yml # 监听端口 server.port: 5601 # 监听地址 server.host: 10.0.0.51 # Elasticsearch地址(VIP) elasticsearch.hosts: ["http://10.0.0.50:9200"] # 系统语言:en(英文),zh-CN(中文) i18n.locale: "zh-CN"
4.1.2.3 启动服务
systemctl enable kibana.service systemctl start kibana.service [root@es1 opt]#systemctl is-active kibana.service active
4.1.2.4 查看状态


4.1.2.5 验证Elasticsearch head插件数据
Kibana自动生成隐藏索引

4.1.3 Logstash
https://www.elastic.co/cn/logstash/
国内镜像下载地址: https://mirrors.tuna.tsinghua.edu.cn/elasticstack/
Logstash日志收集格式参考:https://www.elastic.co/guide/en/logstash/7.12/input-plugins.html
Logstash日志输出格式参考: https://www.elastic.co/guide/en/logstash/7.12/output-plugins.html
4.1.3.1 下载Logstash并安装
# 带java环境 wget https://mirrors.tuna.tsinghua.edu.cn/elasticstack/7.x/apt/pool/main/l/logstash/logstash-7.12.1-amd64.deb dpkg -i logstash-7.12.1-amd64.deb
4.1.3.2 修改配置
暂时不配置,后面根据实际进行配置
[root@logstash logstash]#cat /etc/logstash/conf.d/sample.conf # Sample Logstash configuration for creating a simple # Beats -> Logstash -> Elasticsearch pipeline. input { beats { port => 5044 } } output { elasticsearch { hosts => ["http://localhost:9200"] index => "%{[@metadata][beat]}-%{[@metadata][version]}-%{+YYYY.MM.dd}" #user => "elastic" #password => "changeme" } }
4.1.3.3 启动服务
systemctl enable logstash.service systemctl start logstash.service
查看服务状态
[root@logstash logstash]#systemctl is-active logstash.service active
4.2 部署kafka
4.2.1 安装zookeeper
官网:https://zookeeper.apache.org/
安装说明:https://zookeeper.apache.org/doc/r3.7.1/zookeeperAdmin.html
4.2.1.1 安装java
zookeeper依赖jdk环境
# 安装jdk 1.8 apt update apt install -y openjdk-8-jdk
4.2.1.2 下载zookeeper并解压
wget https://dlcdn.apache.org/zookeeper/zookeeper-3.7.1/apache-zookeeper-3.7.1-bin.tar.gz mkdir -p /apps tar xvf apache-zookeeper-3.7.1-bin.tar.gz -C /apps ln -s /apps/apache-zookeeper-3.7.1-bin /apps/zookeeper
4.2.1.3 修改配置
cd /apps/zookeeper/conf/ cp zoo_sample.cfg zoo.cfg # cat zoo.cfg tickTime=2000 initLimit=10 syncLimit=5 dataDir=/data/zookeeper/ clientPort=2181 server.1=10.0.0.56:2888:3888 server.2=10.0.0.57:2888:3888 server.3=10.0.0.58:2888:3888 # 分别在不同服务器上创建myid,分别为1、2、3 mkdir -p /data/zookeeper echo '1' > /data/zookeeper/myid
4.2.1.4 启动服务
zookeeper集群需要在20s内完成启动
/apps/zookeeper/bin/zkServer.sh start
4.2.1.5 验证集群
节点2为leader,其余节点为follower,集群状态正常
# zk1 [root@kafka1 conf]#/apps/zookeeper/bin/zkServer.sh status /usr/bin/java ZooKeeper JMX enabled by default Using config: /apps/zookeeper/bin/../conf/zoo.cfg Client port found: 2181. Client address: localhost. Client SSL: false. Mode: follower [root@kafka2 conf]#/apps/zookeeper/bin/zkServer.sh status /usr/bin/java ZooKeeper JMX enabled by default Using config: /apps/zookeeper/bin/../conf/zoo.cfg Client port found: 2181. Client address: localhost. Client SSL: false. Mode: leader [root@kafka3 conf]#/apps/zookeeper/bin/zkServer.sh status /usr/bin/java ZooKeeper JMX enabled by default Using config: /apps/zookeeper/bin/../conf/zoo.cfg Client port found: 2181. Client address: localhost. Client SSL: false. Mode: follower
4.2.2 安装kafka
安装说明:https://kafka.apache.org/documentation/
4.2.2.1 下载kafka并解压
wget https://archive.apache.org/dist/kafka/3.2.0/kafka_2.13-3.2.0.tgz tar xvf kafka_2.13-3.2.0.tgz -C /apps/ ln -s /apps/kafka_2.13-3.2.0 /apps/kafka
4.2.2.2 修改配置
[root@kafka1 config]#egrep -v "^#|^$" /apps/kafka/config/server.properties broker.id=56 # 每个broker节点不相同 listeners=PLAINTEXT://10.0.0.56:9092 # 设置监听本地IP、端口 num.network.threads=3 num.io.threads=8 socket.send.buffer.bytes=102400 socket.receive.buffer.bytes=102400 socket.request.max.bytes=104857600 log.dirs=/data/kafka-logs # kafka数据目录 num.partitions=1 num.recovery.threads.per.data.dir=1 offsets.topic.replication.factor=1 transaction.state.log.replication.factor=1 transaction.state.log.min.isr=1 log.retention.hours=168 # 日志保存时间(h) log.segment.bytes=1073741824 log.retention.check.interval.ms=300000 zookeeper.connect=10.0.0.56:2181,10.0.0.57:2181,10.0.0.58:2181 # zookeeper集群地址 zookeeper.connection.timeout.ms=18000 # zookeeper连接超时时间(ms) group.initial.rebalance.delay.ms=0
创建数据目录
mkdir -p /data/kafka-logs
4.2.2.3 启动服务
# 以后台方式运行 /apps/kafka/bin/kafka-server-start.sh -daemon /apps/kafka/config/server.properties
4.2.2.5 验证端口
[root@kafka1 /]#netstat -ntlp|grep 9092 tcp6 0 0 10.0.0.56:9092 :::* LISTEN 12503/java [root@kafka2 opt]#netstat -ntlp|grep 9092 tcp6 0 0 10.0.0.57:9092 :::* LISTEN 26474/java [root@kafka3 opt]#netstat -ntlp|grep 9092 tcp6 0 0 10.0.0.58:9092 :::* LISTEN 26427/java
4.2.2.6 验证元数据
使用kafka图形化工具连接查看,下载地址:https://www.kafkatool.com/download2/offsetexplorer_64bit.exe
-
安装offsetexplorer工具
-
新建连接
配置kafka版本,zookeeper信息

配置kafka信息

- 查看信息

五、实现daemonset和sidcar日志收集
5.1 daemonset日志收集
daemonset日志收集架构

5.1.1 构建Logstash镜像
下载Logstash官方镜像至本地仓库
docker pull logstash:7.12.1 docker tag logstash:7.12.1 harbor.chu.net/baseimages/logstash:7.12.1 docker push harbor.chu.net/baseimages/logstash:7.12.1
5.1.1.1 编写Dockerfile文件
# Logstash版本号要与Elasticsearch、Kibana等版本号保持一致 FROM harbor.chu.net/baseimages/logstash:7.12.1 USER root WORKDIR /usr/share/logstash ADD logstash.yml /usr/share/logstash/config/logstash.yml ADD logstash.conf /usr/share/logstash/pipeline/logstash.conf
5.1.1.2 准备配置文件
- logstash.yml
http.host: "0.0.0.0" #xpack.monitoring.elasticsearch.hosts: [ "http://elasticsearch:9200" ]
- logstash.conf
# 收集容器日志 input { file { #path => "/var/lib/docker/containers/*/*-json.log" # docker容器路径日志 path => "/var/log/pods/*/*/*.log" # containerd容器日志路径 start_position => "beginning" # 第一次从日志文件开头收集,之后从新添加的日志收集 type => "jsonfile-daemonset-applog" # 自定义日志索引类型 } file { path => "/var/log/*.log" # 宿主机日志 start_position => "beginning" type => "jsonfile-daemonset-syslog" } } # 容器日志输出到kafka output { if [type] == "jsonfile-daemonset-applog" { kafka { bootstrap_servers => "${KAFKA_SERVER}" # kafka集群,创建pod时进行设置环境变量 topic_id => "${TOPIC_ID}" # topic id,创建pod时进行设置环境变量 batch_size => 16384 # logstash每次向ES传输的数据量大小,单位为字节 codec => "${CODEC}" # 输出日志编解码格式,通常设置json格式 } } if [type] == "jsonfile-daemonset-syslog" { kafka { bootstrap_servers => "${KAFKA_SERVER}" topic_id => "${TOPIC_ID}" batch_size => 16384 codec => "${CODEC}" # 系统日志不是json格式 }} }
5.1.1.3 构建镜像
# 执行构建 docker build -t harbor.chu.net/baseimages/logstash:v7.12.1-json-file-log-v1 . # 上传至本地仓库 docker push harbor.chu.net/baseimages/logstash:v7.12.1-json-file-log-v1
5.1.2 创建daemonset收集任务
apiVersion: apps/v1 kind: DaemonSet metadata: name: logstash-elasticsearch namespace: kube-system labels: k8s-app: logstash-logging spec: selector: matchLabels: name: logstash-elasticsearch template: metadata: labels: name: logstash-elasticsearch spec: tolerations: - key: node-role.kubernetes.io/master operator: Exists effect: NoSchedule containers: - name: logstash-elasticsearch image: harbor.chu.net/baseimages/logstash:v7.12.1-json-file-log-v1 env: - name: "KAFKA_SERVER" value: "10.0.0.56:9092,10.0.0.57:9092,10.0.0.58:9092" #kafka集群地址 - name: "TOPIC_ID" value: "jsonfile-log-topic" - name: "CODEC" value: "json" volumeMounts: - name: varlog #定义宿主机系统日志挂载路径 mountPath: /var/log #宿主机系统日志挂载点 - name: varlibdockercontainers #定义容器日志挂载路径,和logstash配置文件中的收集路径保持一直 #mountPath: /var/lib/docker/containers #docker挂载路径 mountPath: /var/log/pods #containerd挂载路径,此路径与logstash的日志收集路径必须一致 readOnly: false terminationGracePeriodSeconds: 30 volumes: - name: varlog hostPath: path: /var/log #宿主机系统日志 - name: varlibdockercontainers hostPath: #path: /var/lib/docker/containers #docker的宿主机日志路径 path: /var/log/pods #containerd的宿主机日志路径
查看pod
[root@k8s-deploy 1.daemonset-logstash]#kubectl get pod -n kube-system NAME READY STATUS RESTARTS AGE ... logstash-elasticsearch-7cj86 1/1 Running 0 64s logstash-elasticsearch-9hfbw 1/1 Running 0 64s logstash-elasticsearch-bjf8d 1/1 Running 0 64s logstash-elasticsearch-jlffm 1/1 Running 0 64s logstash-elasticsearch-qx9j5 1/1 Running 0 64s logstash-elasticsearch-wpjrx 1/1 Running 0 64s
5.1.3 验证kafka数据
登录kafka工具,查看Logstash收集日志
- 修改类型为Sting

- 查看Logstash收集日志内容

5.1.4 配置Logstash服务器
- 修改配置文件
[root@logstash logstash]#cat /etc/logstash/conf.d/logstash.conf input { kafka { bootstrap_servers => "10.0.0.56:9092,10.0.0.57:9092,10.0.0.58:9092" topics => ["jsonfile-log-topic"] codec => "json" } } output { if [type] == "jsonfile-daemonset-applog" { elasticsearch { hosts => ["10.0.0.51:9200","10.0.0.52:9200","10.0.0.53:9200"] index => "jsonfile-daemonset-applog-%{+YYYY.MM.dd}" }} if [type] == "jsonfile-daemonset-syslog" { elasticsearch { hosts => ["10.0.0.51:9200","10.0.0.52:9200","10.0.0.53:9200"] index => "jsonfile-daemonset-syslog-%{+YYYY.MM.dd}" }} }
- 检查配置文件语法
/usr/share/logstash/bin/logstash -f /etc/logstash/conf.d/logstash.conf
- 重启服务
systemctl restart logstash.service
5.1.5 验证Elasticsearch数据
Elasticsearch Head插件查看索引

5.1.6 创建Kibana索引
- Stack Management

- Kibana索引
创建索引

- 创建索引名称
创建所有日志索引,命令索引名称

选择索引模式

- 查看设置索引

5.1.7 Kibana展示收集日志
选择Analytics-->Discover

展示查询日志

5.2 sidcar日志收集
sidcar日志收集架构

5.2.1 构建sidcar镜像
5.2.1.1 编写Dockerfile文件
FROM logstash:7.12.1 USER root WORKDIR /usr/share/logstash ADD logstash.yml /usr/share/logstash/config/logstash.yml ADD logstash.conf /usr/share/logstash/pipeline/logstash.conf
5.2.1.2 准备配置文件
- logstash.yml
http.host: "0.0.0.0" #xpack.monitoring.elasticsearch.hosts: [ "http://elasticsearch:9200" ]
- logstash.conf
input { file { path => "/var/log/applog/catalina.out" start_position => "beginning" type => "app1-sidecar-catalina-log" } file { path => "/var/log/applog/localhost_access_log.*.txt" start_position => "beginning" type => "app1-sidecar-access-log" } } output { if [type] == "app1-sidecar-catalina-log" { kafka { bootstrap_servers => "${KAFKA_SERVER}" topic_id => "${TOPIC_ID}" batch_size => 16384 # logstash每次向ES传输的数据量大小,单位为字节 codec => "${CODEC}" } } if [type] == "app1-sidecar-access-log" { kafka { bootstrap_servers => "${KAFKA_SERVER}" topic_id => "${TOPIC_ID}" batch_size => 16384 codec => "${CODEC}" }} }
5.2.1.3 构建镜像
docker build -t harbor.chu.net/baseimages/logstash:v7.12.1-sidecar . docker push harbor.chu.net/baseimages/logstash:v7.12.1-sidecar
5.2.2 构建tomcat业务镜像
参考: https://www.cnblogs.com/areke/p/17086791.html#:~:text=5.1-,Tomcat,-5.1.1 JDK基础
生成镜像为harbor.chu.net/web/tomcat-app1:v1
5.2.3 运行web服务
- 编写业务yaml文件
kind: Deployment apiVersion: apps/v1 metadata: labels: app: web-tomcat-app1-deployment-label name: web-tomcat-app1-deployment #当前版本的deployment 名称 namespace: web spec: replicas: 3 selector: matchLabels: app: web-tomcat-app1-selector template: metadata: labels: app: web-tomcat-app1-selector spec: containers: - name: sidecar-container image: harbor.chu.net/baseimages/logstash:v7.12.1-sidecar # sidcar镜像 imagePullPolicy: IfNotPresent #imagePullPolicy: Always env: - name: "KAFKA_SERVER" value: "10.0.0.56:9092,10.0.0.57:9092,10.0.0.58:9092" # kafka集群地址 - name: "TOPIC_ID" value: "tomcat-app1-topic" # topic id - name: "CODEC" value: "json" # json格式日志 volumeMounts: - name: applogs mountPath: /var/log/applog - name: web-tomcat-app1-container image: harbor.chu.net/web/tomcat-app1:v1 # tomcat业务镜像 imagePullPolicy: IfNotPresent #imagePullPolicy: Always ports: - containerPort: 8080 protocol: TCP name: http env: - name: "password" value: "123456" - name: "age" value: "18" resources: limits: cpu: 1 memory: "512Mi" requests: cpu: 500m memory: "512Mi" volumeMounts: - name: applogs mountPath: /apps/tomcat/logs startupProbe: httpGet: path: /myapp/index.html port: 8080 initialDelaySeconds: 5 #首次检测延迟5s failureThreshold: 3 #从成功转为失败的次数 periodSeconds: 3 #探测间隔周期 readinessProbe: httpGet: #path: /monitor/monitor.html path: /myapp/index.html port: 8080 initialDelaySeconds: 5 periodSeconds: 3 timeoutSeconds: 5 successThreshold: 1 failureThreshold: 3 livenessProbe: httpGet: #path: /monitor/monitor.html path: /myapp/index.html port: 8080 initialDelaySeconds: 5 periodSeconds: 3 timeoutSeconds: 5 successThreshold: 1 failureThreshold: 3 volumes: - name: applogs #定义通过emptyDir实现业务容器与sidecar容器的日志共享,以让sidecar收集业务容器中的日志 emptyDir: {}
- 编写service yaml文件
--- kind: Service apiVersion: v1 metadata: labels: app: web-tomcat-app1-service-label name: web-tomcat-app1-service namespace: web spec: type: NodePort ports: - name: http port: 80 protocol: TCP targetPort: 8080 nodePort: 40080 selector: app: web-tomcat-app1-selector
- 验证
查看状态
# 查看pod状态 [root@k8s-deploy 2.sidecar-logstash]#kubectl get pod -n web NAME READY STATUS RESTARTS AGE web-tomcat-app1-deployment-6f448b7685-4jpfn 2/2 Running 0 7s web-tomcat-app1-deployment-6f448b7685-bqszs 2/2 Running 0 7s web-tomcat-app1-deployment-6f448b7685-xglkj 2/2 Running 0 7s # 查看service [root@k8s-deploy 2.sidecar-logstash]#kubectl get svc -n web NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE web-tomcat-app1-service NodePort 10.100.245.111 <none> 80:40080/TCP 5s
访问测试

5.2.4 验证kafka日志数据

5.2.5 配置Logstash服务器
- 修改配置文件
[root@logstash ~]# /etc/logstash/conf.d/logstash-sidecar-kafka-to-es.conf input { kafka { bootstrap_servers => "10.0.0.56:9092,10.0.0.57:9092,10.0.0.58:9092" topics => ["tomcat-app1-topic"] codec => "json" } } output { if [type] == "app1-sidecar-access-log" { elasticsearch { hosts => ["10.0.0.51:9200","10.0.0.52:9200","10.0.0.53:9200"] index => "sidecar-app1-accesslog-%{+YYYY.MM.dd}" } } if [type] == "app1-sidecar-catalina-log" { elasticsearch { hosts => ["10.0.0.51:9200","10.0.0.52:9200","10.0.0.53:9200"] index => "sidecar-app1-catalinalog-%{+YYYY.MM.dd}" } } }
说明:其中type值与sidcar镜像中logstash.conf配置文件里面自定义type值进行匹配。
- 检查配置文件语法
/usr/share/logstash/bin/logstash -f /etc/logstash/conf.d/logstash-sidecar-kafka-to-es.conf
- 重启服务
systemctl restart logstash.service
5.2.6 验证Elasticsearch日志数据

5.2.7 Kibana展示日志
- 创建索引

- 展示日志
accesslog

catalinalog

【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?