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

https://kubernetes.io/zh-cn/docs/tasks/administer-cluster/manage-resources/quota-memory-cpu-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亲和

https://kubernetes.io/zh-cn/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity

​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(反亲和)的功能更加强大。

  1. 亲和与反亲和对目的标签的选择匹配不仅仅支持and,还支持In,NotIn,Exists,DoesNotExist,Gt,Lt。
  • In

    标签的值存在匹配列表中(匹配成功就调度到目的node,实现node亲和)

  • NotIn

    标签的值不存在指定的匹配列表中(不会调度到目的node,实现反亲和)

  • Gt

    标签的值大于某个值(字符串)

  • Lt

    标签的值小于某个值(字符串)

  • Exists

    指定的标签存在

  1. 可以设置软匹配和硬匹配,在软匹配下,如果调度器无法匹配节点,仍然将pod调度到其他不符合条件的节点。

  2. 还可以对pod定义亲和策略,比如允许哪些pod可以或者不可以被调度至同一个node。

‍

三、pod亲和与反亲和、污点与容忍、驱逐

3.1 pod亲和与反亲和

https://kubernetes.io/zh-cn/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity

  • 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​​​

‍

  1. pod亲和性与反亲和性的合法操作符(operator)有In、NotIn、Exists、DoesNotExist
  2. 在pod亲和性配置中,在requiredDuringSchedulingIgnoredDuringExecution​​和preferredDuringSchedulingIgnoredDuringExecution​​中,topologyKey​​不允许为空(Empty topologyKey is not allowed​​)。
  3. 在pod反亲和性配置中,requiredDuringSchedulingIgnoredDuringExecution​和preferredDuringSchedulingIgnoredDuringExecution​中,topologyKey​也不允许为空(Empty topologyKey is not allowed​)。
  4. 对于requiredDuringSchedulingIgnoredDuringExecution​要求的pod反亲和性,准入控制器LimitPodHardAntiAffinityTopology​被引入以确保topologyKey​只能是kubernetes.io/hostname​,如果希望topologyKey​也可以用于其他定制拓扑逻辑,可以更改准入控制器或禁用。
  5. 除上述情况外,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)

污点三种类型

  1. NoScheduler

    表示k8s不会将pod调度具有该污点的Node上

  2. PreferNoScheduler

    表示k8s尽量避免将pod调度到具有该污点的node上

  3. 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/

  1. 首先考虑资源使用量超过其请求的 BestEffort​ 或 Burstable​ Pod。 这些 Pod 会根据它们的优先级以及它们的资源使用级别超过其请求的程度被逐出。
  2. 资源使用量少于请求量的 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

‍

准备环境

  1. 2c/4g内存(1c1g内存)/20g硬盘(该配置仅测试用)
  2. 最小化安装Ubuntu 20.04 server​
  3. 配置基础网络、更新源、SSH登录等
  4. 主机名、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

【可选】生产环境可配置锁定内存参数

  1. ​elasticsearch.yml​配置参数设置bootstrap.memory_lock: ture​

  2. 修改锁定内存大小,建议最小和最大内存设置相同

    [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 查看状态

浏览器访问http://10.0.0.51/status

‍

进入主页http://10.0.0.51:5601/

‍

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/

安装说明: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

  1. 安装offsetexplorer工具

  2. 新建连接

配置kafka版本,zookeeper信息

配置kafka信息

​

  1. 查看信息

‍

五、实现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索引

  1. Stack Management

​

  1. Kibana索引

创建索引

‍

  1. 创建索引名称

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

​

选择索引模式

​

  1. 查看设置索引

‍

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%20JDK%E5%9F%BA%E7%A1%80

生成镜像为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展示日志

‍

  1. 创建索引

​

  1. 展示日志

accesslog

catalinalog

posted @ 2023-02-12 13:55  areke  阅读(259)  评论(0)    收藏  举报