• k8s核心资源之namespace与pod污点容忍度生命周期进阶篇(四)



    注:因编辑器的问题yaml语法格式可能显示不正确

    1、命名空间namespace

    1.1 什么是命名空间?

    Kubernetes 支持多个虚拟集群,它们底层依赖于同一个物理集群。 这些虚拟集群被称为命名空间。
    命名空间namespace是k8s集群级别的资源,可以给不同的用户、租户、环境或项目创建对应的命名空间,例如,可以为test、devlopment、production环境分别创建各自的命名空间。

    1.2 namespace应用场景

    命名空间适用于存在很多跨多个团队或项目的用户的场景。对于只有几到几十个用户的集群,根本不需要创建或考虑命名空间。

    1、查看名称空间及其资源对象
    k8s集群默认提供了几个名称空间用于特定目的,例如,kube-system主要用于运行系统级资源,存放k8s一些组件的。而default则为那些未指定名称空间的资源操作提供一个默认值。

    使用kubectl get namespace可以查看namespace资源,使用kubectl describe namespace $NAME可以查看特定的名称空间的详细信息。

    2、管理namespace资源
    namespace资源属性较少,通常只需要指定名称即可创建,如“kubectl create namespace qa”。namespace资源的名称仅能由字母、数字、下划线、连接线等字符组成。删除namespace资源会级联删除其包含的所有其他资源对象。

    1.3 namespacs常用指令

    ① 创建一个test命名空间
    # kubectl create ns test
    
    ② 切换命名空间
    # kubectl  config set-context --current --namespace=kube-system
    #切换命名空间后,kubectl get pods 如果不指定-n,查看的就是kube-system命名空间的资源了。 
    #查看哪些资源属于命名空间级别的
    

    image

    1.4 namespace资源限额

    namespace是命名空间,里面有很多资源,那么我们可以对命名空间资源做个限制,防止该命名空间部署的资源超过限制。
    如何对namespace资源做限额呢?

    # vim namespace-quota.yaml
    apiVersion: v1
    kind: ResourceQuota
    metadata:
      name: mem-cpu-quota
      namespace: test
    spec:
      hard:
        requests.cpu: "2"
        requests.memory: 2Gi
        limits.cpu: "4"
        limits.memory: 4Gi
    
    #创建的ResourceQuota对象将在test名字空间中添加以下限制:
      每个容器必须设置内存请求(memory request),内存限额(memory limit),cpu请求(cpu request)和cpu限额(cpu limit)。
        所有容器的内存请求总额不得超过2GiB。
        所有容器的内存限额总额不得超过4 GiB。
        所有容器的CPU请求总额不得超过2 CPU。
        所有容器的CPU限额总额不得超过4CPU。
    
    #创建pod时候必须设置资源限额,否则创建失败,如下:
    # vim pod-test.yaml
    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-test
      namespace: test
      labels:
        app: tomcat-pod-test
    spec:
      containers:
      - name:  tomcat-test
        ports:
        - containerPort: 8080
        image: hxu/tomcat-8.5-jre8:v1
        imagePullPolicy: IfNotPresent
    
    [root@k8s-master1 ~]# kubectl apply -f pod-test.yaml
    [root@k8s-master1 kubenetes-han]# kubectl get pod -n test
    NAME       READY   STATUS    RESTARTS   AGE
    pod-test   1/1     Running   0          67s
    

    2、标签

    2.1 什么是标签?

    标签其实就一对 key/value ,被关联到对象上,比如Pod,标签的使用我们倾向于能够表示对象的特殊特点,就是一眼就看出了这个Pod是干什么的,标签可以用来划分特定的对象(比如版本,服务类型等),标签可以在创建一个对象的时候直接定义,也可以在后期随时修改,每一个对象可以拥有多个标签,但是,key值必须是唯一的。创建标签之后也可以方便我们对资源进行分组管理。如果对pod打标签,之后就可以使用标签来查看、删除指定的pod。
    在k8s中,大部分资源都可以打标签。

    2.2 如何给pod资源打标签

    显示如下,显示如下,说明标签达成功了;

    [root@k8s-master1 kubenetes-han]# kubectl get pods pod-test -n test --show-labels
    NAME       READY   STATUS    RESTARTS   AGE     LABELS
    pod-test   1/1     Running   0          3m55s   app=tomcat-pod-test
    # 1、对已经存在的pod打标签,release=v1 
    [root@k8s-master1 kubenetes-han]# kubectl label pods pod-test  release=v1 -n test
    pod/pod-test labeled
    # 2、查看标签是否打成功:
    [root@k8s-master1 kubenetes-han]# kubectl get pods pod-test -n test --show-labels
    NAME       READY   STATUS    RESTARTS   AGE     LABELS
    pod-test   1/1     Running   0          4m40s   app=tomcat-pod-test,release=v1
    

    2.3 查看资源标签

    查看默认名称空间下所有pod资源的标签

    [root@k8s-master1 kubenetes-han]#  kubectl get pods --show-labels 
    NAME                          READY   STATUS    RESTARTS   AGE   LABELS
    demo-pod                      1/1     Running   0          15d   app=myapp,env=dev
    nginx-test-57f9f5b6d7-8zt7x   1/1     Running   0          38m   app=nginx,pod-template-hash=57f9f5b6d7
    nginx-test-57f9f5b6d7-fxsw5   1/1     Running   0          8d    app=nginx,pod-template-hash=57f9f5b6d7
    test-nginx-67b6d886b6-ccrvd   1/1     Running   0          13d   k8s-app=test-nginx,pod-template-hash=67b6d886b6
    test-nginx-67b6d886b6-qgjdh   1/1     Running   0          13d   k8s-app=test-nginx,pod-template-hash=67b6d886b6
    

    用法示例:

    # 查看默认名称空间下指定pod具有的所有标签
    kubectl get pods pod-first --show-labels
    # 列出默认名称空间下标签key是release的pod,不显示标签
    kubectl get pods -l release
    
    #列出默认名称空间下标签key是release、值是v1的pod,不显示标签
    kubectl get pods -l release=v1
    
    #列出默认名称空间下标签key是release的所有pod,并打印对应的标签值
    kubectl get pods -L release
    
    #查看所有名称空间下的所有pod的标签
    kubectl get pods --all-namespaces --show-labels
    kubectl get pods -l release=v1 -L release
    

    3、node节点选择器

    我们在创建pod资源的时候,pod会根据schduler进行调度,那么默认会调度到随机的一个工作节点,如果我们想要pod调度到指定节点或者调度到一些具有相同特点的node节点,怎么办呢?
    可以使用pod中的nodeName或者nodeSelector字段指定要调度到的node节点

    1、nodeName:
    指定pod节点运行在哪个具体node上
    先来编写一个yaml文件,指定调度节点为k8s-node1节点,我这里只有一个虚拟机所以看不出来效果,如有多个虚拟机可以指定其他的node节点的主机名!

    [root@k8s-master1 kubenetes-han]# cat pod-node.yaml 
    apiVersion: v1
    kind: Pod
    metadata:
      name: demo-pod
      namespace: default
      labels:
        app: myapp
        env: dev
    spec:
      nodeName: k8s-node1  #此处指定调度到哪个节点,写node的主机名
      containers:
      - name:  tomcat-pod-java
        ports:
        - containerPort: 8080
        image: tomcat:8.5-jre8-alpine
        imagePullPolicy: IfNotPresent
      - name: busybox
        image: busybox:latest
        command:
        - "/bin/sh"
        - "-c"
        - "sleep 3600"
    
    # kubectl apply -f pod-node.yaml
    查看pod调度到哪个节点
    [root@k8s-master1 kubenetes-han]# kubectl get pods  -o wide
    NAME                          READY   STATUS    RESTARTS   AGE   IP            NODE        NOMINATED NODE   READINESS GATES
    demo-pod                      2/2     Running   0          2m    10.244.1.21   k8s-node1   <none>           <none>
    

    4、nodeSelector:

    指定pod调度到具有哪些标签的node节点上

    给node节点打标签,打个具有disk=ceph的标签
    # kubectl label nodes k8s-node2 disk=ceph
    node/k8s-node2 labeled
    # 定义pod的时候指定要调度到具有disk=ceph标签的node上
    

    编辑一个yaml文件

    [root@k8s-master1 kubenetes-han]# cat pod-1.yaml
    apiVersion: v1
    kind: Pod
    metadata:
      name: demo-pod-1
      namespace: default
      labels:
        app: myapp
        env: dev
    spec:
      nodeSelector:     # 加上nodeselector
        disk: ceph      # 値
      containers:
      - name:  tomcat-pod-java
        ports:
        - containerPort: 8080
        image: tomcat:8.5-jre8-alpine
        imagePullPolicy: IfNotPresent
    

    读取yaml文件并创建pod

    [root@k8s-master1 kubenetes-han]# kubectl apply -f pod-1.yaml
    pod/demo-pod-1 created
    [root@k8s-master1 kubenetes-han]# kubectl get pods  -o wide
    NAME                          READY   STATUS              RESTARTS   AGE     IP            NODE        NOMINATED NODE   READINESS GATES
    demo-pod                      2/2     Running             68         2d21h   10.244.1.21   k8s-node1   <none>           <none>
    demo-pod-1                    0/1     ContainerCreating   0          3s      <none>        k8s-node2  # 调度到了node2上 <none>           <none>
    nginx-test-57f9f5b6d7-8zt7x   1/1     Running             0          2d22h   10.244.1.19   k8s-node1   <none>           <none>
    nginx-test-57f9f5b6d7-fxsw5   1/1     Running             0          11d     10.244.1.16   k8s-node1   <none>           <none>
    test-nginx-67b6d886b6-ccrvd   1/1     Running             0          15d     10.244.1.8    k8s-node1   <none>           <none>
    test-nginx-67b6d886b6-qgjdh   1/1     Running             0          15d     10.244.1.9    k8s-node1   <none>           <none>
    

    5、亲和性

    5.1 node节点亲和性

    node节点亲和性调度:nodeAffinity

    # kubectl explain pods.spec.affinity 
    KIND:     Pod
    VERSION:  v1
    RESOURCE: affinity <Object>
    DESCRIPTION:
         If specified, the pod's scheduling constraints
        Affinity is a group of affinity scheduling rules.
    FIELDS:
       nodeAffinity	<Object>
       podAffinity	<Object>
       podAntiAffinity	<Object>
    
    #  kubectl explain  pods.spec.affinity.nodeAffinity
    KIND:     Pod
    VERSION:  v1
    RESOURCE: nodeAffinity <Object>
    DESCRIPTION:
         Describes node affinity scheduling rules for the pod.
         Node affinity is a group of node affinity scheduling rules.
    FIELDS:
       preferredDuringSchedulingIgnoredDuringExecution	<[]Object>
       requiredDuringSchedulingIgnoredDuringExecution	<Object>
    
    prefered表示有节点尽量满足这个位置定义的亲和性,这不是一个必须的条件,软亲和性
    require表示必须有节点满足这个位置定义的亲和性,这是个硬性条件,硬亲和性
    
    
    # kubectl explain pods.spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution
    KIND:     Pod
    VERSION:  v1
    RESOURCE: requiredDuringSchedulingIgnoredDuringExecution <Object>
    DESCRIPTION:
    FIELDS:
       nodeSelectorTerms	<[]Object> -required-
         Required. A list of node selector terms. The terms are ORed.
    
    # kubectl explain pods.spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms
    KIND:     Pod
    VERSION:  v1
    RESOURCE: nodeSelectorTerms <[]Object>
    DESCRIPTION:
         Required. A list of node selector terms. The terms are ORed.
         A null or empty node selector term matches no objects. The requirements of
         them are ANDed. The TopologySelectorTerm type implements a subset of the
         NodeSelectorTerm.
    FIELDS:
       matchExpressions	<[]Object>
       matchFields	<[]Object>
    matchExpressions:匹配表达式的
    matchFields: 匹配字段的
    
    # kubectl explain pods.spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms.matchFields
    KIND:     Pod
    VERSION:  v1
    RESOURCE: matchFields <[]Object>
    DESCRIPTION:
    
    FIELDS:
       key	<string> -required-
       values	<[]string>
    
    # kubectl explain pods.spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms.matchExpressions
    KIND:     Pod
    VERSION:  v1
    RESOURCE: matchExpressions <[]Object>
    DESCRIPTION:
    FIELDS:
       key	<string> -required-
       operator	<string> -required-
       values	<[]string>
    key:检查label
    operator:做等值选则还是不等值选则
    values:给定值
    

    5.1.1 硬亲和性

    例1:使用requiredDuringSchedulingIgnoredDuringExecution硬亲和性
    #把myapp-v1.tar.gz上传到两个node主机中并load -i
    在master节点编辑yaml文件

    # cat pod-nodeaffinity-demo.yaml 
    apiVersion: v1
    kind: Pod
    metadata:
            name: pod-node-affinity-demo
            namespace: default
            labels:
                app: myapp
                tier: frontend
    spec:
        containers:
        - name: myapp
          image: ikubernetes/myapp:v1
        affinity:
            nodeAffinity:
                requiredDuringSchedulingIgnoredDuringExecution:
                       nodeSelectorTerms:
                       - matchExpressions:
                         - key: zone
                           operator: In
                           values:
                           - foo
                           - bar
    

    我们检查当前节点中有任意一个节点拥有zone标签的值是foo或者bar,就可以把pod调度到这个node节点的foo或者bar标签上的节点上

    [root@k8s-master1 kubenetes-han]# kubectl apply -f pod-nodeaffinity-demo.yaml
    pod/pod-node-affinity-demo created
    [root@k8s-master1 kubenetes-han]# kubectl get pods -o wide | grep pod-node
    pod-node-affinity-demo        0/1     Pending   0          2s      <none>
    

    status的状态是pending,上面说明没有完成调度,因为没有一个拥有zone的标签的值是foo或者bar,而且使用的是硬亲和性,必须满足条件才能完成调度

    [root@k8s-master1 kubenetes-han]# kubectl label nodes k8s-node2  zone=foo
    node/k8s-node2 labeled
    [root@k8s-master1 kubenetes-han]# kubectl get pods -o wide | grep pod-node
    pod-node-affinity-demo        1/1     Running   0          78s     10.244.2.3    k8s-node2
    

    5.1.2 软亲和性

    例2:使用preferredDuringSchedulingIgnoredDuringExecution软亲和性

    # cat pod-nodeaffinity-demo-2.yaml 
    apiVersion: v1
    kind: Pod
    metadata:
            name: pod-node-affinity-demo-2
            namespace: default
            labels:
                app: myapp
                tier: frontend
    spec:
        containers:
        - name: myapp
          image: ikubernetes/myapp:v1
        affinity:
            nodeAffinity:
                preferredDuringSchedulingIgnoredDuringExecution:
                - preference:
                   matchExpressions:
                   - key: zone1
                     operator: In
                     values:
                     - foo1
                     - bar1
                  weight: 60
    [root@k8s-master1 kubenetes-han]#  kubectl apply -f pod-nodeaffinity-demo-2.yaml
    pod/pod-node-affinity-demo-2 created
    [root@k8s-master1 kubenetes-han]#  kubectl get pods -o wide |grep demo-2
    pod-node-affinity-demo-2      1/1     Running   0          2s      10.244.2.4    k8s-node2
    

    上面说明软亲和性是可以运行这个pod的,尽管没有运行这个pod的节点定义的zone1标签

    Node节点亲和性针对的是pod和node的关系,Pod调度到node节点的时候匹配的条件

    5.2 Pod节点亲和性

    pod自身的亲和性调度有两种表示形式
    podaffinity:pod和pod更倾向腻在一起,把相近的pod结合到相近的位置,如同一区域,同一机架,这样的话pod和pod之间更好通信,比方说有两个机房,这两个机房部署的集群有1000台主机,那么我们希望把nginx和tomcat都部署同一个地方的node节点上,可以提高通信效率;

    podunaffinity:pod和pod更倾向不腻在一起,如果部署两套程序,那么这两套程序更倾向于反亲和性,这样相互之间不会有影响。

    第一个pod随机选则一个节点,做为评判后续的pod能否到达这个pod所在的节点上的运行方式,这就称为pod亲和性;我们怎么判定哪些节点是相同位置的,哪些节点是不同位置的;我们在定义pod亲和性时需要有一个前提,哪些pod在同一个位置,哪些pod不在同一个位置,这个位置是怎么定义的,标准是什么?以节点名称为标准,这个节点名称相同的表示是同一个位置,节点名称不相同的表示不是一个位置。
    帮助:

    # kubectl explain pods.spec.affinity.podAffinity
    KIND:     Pod
    VERSION:  v1
    RESOURCE: podAffinity <Object>
    DESCRIPTION:
         Describes pod affinity scheduling rules (e.g. co-locate this pod in the
         same node, zone, etc. as some other pod(s)).
         Pod affinity is a group of inter pod affinity scheduling rules.
    FIELDS:
       preferredDuringSchedulingIgnoredDuringExecution	<[]Object>
       requiredDuringSchedulingIgnoredDuringExecution	<[]Object>
       
    requiredDuringSchedulingIgnoredDuringExecution: 硬亲和性
    preferredDuringSchedulingIgnoredDuringExecution:软亲和性
    
    # kubectl explain pods.spec.affinity.podAffinity.requiredDuringSchedulingIgnoredDuringExecution
    KIND:     Pod
    VERSION:  v1
    RESOURCE: requiredDuringSchedulingIgnoredDuringExecution <[]Object>
    DESCRIPTION:
    FIELDS:
       labelSelector	<Object>
       namespaces	<[]string>
       topologyKey	<string> -required-
    
    
    topologyKey:
    位置拓扑的键,这个是必须字段
    怎么判断是不是同一个位置:
    rack=rack1
    row=row1
    使用rack的键是同一个位置
    使用row的键是同一个位置
    labelSelector:
    我们要判断pod跟别的pod亲和,跟哪个pod亲和,需要靠labelSelector,通过labelSelector选则一组能作为亲和对象的pod资源
    namespace:
    labelSelector需要选则一组资源,那么这组资源是在哪个名称空间中呢,通过namespace指定,如果不指定namespaces,那么就是当前创建pod的名称空间
    
    # kubectl explain pods.spec.affinity.podAffinity.requiredDuringSchedulingIgnoredDuringExecution.labelSelector 
    KIND:     Pod
    VERSION:  v1
    RESOURCE: labelSelector <Object>
    DESCRIPTION:
         A label query over a set of resources, in this case pods.
         A label selector is a label query over a set of resources. The result of
         matchLabels and matchExpressions are ANDed. An empty label selector matches
         all objects. A null label selector matches no objects.
    FIELDS:
       matchExpressions	<[]Object>
       matchLabels	<map[string]string>
    

    5.2.1 pod节点亲和性

    例1:
    定义两个pod,第一个pod做为基准,第二个pod跟着它走

    # cat pod-required-affinity-demo.yaml 
    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-first
      labels:
        app2: myapp2
        tier: frontend
    spec:
        containers:
        - name: myapp
          image: ikubernetes/myapp:v1
    ---
    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-second
      labels:
        app: backend
        tier: db
    spec:
        containers:
        - name: busybox
          image: busybox:latest
          imagePullPolicy: IfNotPresent
          command: ["sh","-c","sleep 3600"]
        affinity:
          podAffinity:
             requiredDuringSchedulingIgnoredDuringExecution:
             - labelSelector:
                  matchExpressions:
                  - {key: app2, operator: In, values: ["myapp2"]}
               topologyKey: kubernetes.io/hostname
    
    #上面表示创建的pod必须与拥有app=myapp标签的pod在一个节点上
    # kubectl apply -f pod-required-affinity-demo.yaml 
    kubectl get pods -o wide 显示如下:
    [root@k8s-master1 kubenetes-han]# kubectl get pods -o wide
    NAME                          READY   STATUS    RESTARTS   AGE     IP            NODE        NOMINATED NODE   READINESS GATES
    pod-first                     1/1     Running   0          24s     10.244.2.5    k8s-node2 
    pod-second                    1/1     Running   0          24s     10.244.2.6    k8s-node2   
    

    上面说明第一个pod调度到哪,第二个pod也调度到哪,这就是pod节点亲和性
    注意:查看node节点的标签可以使用--show-labels查看
    kubectl get nodes --show-labels

    5.2.2 Pod节点反亲和性

    例2:
    定义两个pod,第一个pod做为基准,第二个pod跟它调度节点相反
    在master节点编辑yaml文件

    # cat pod-required-anti-affinity-demo.yaml
    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-first
      labels:
        app1: myapp1
        tier: frontend
    spec:
        containers:
        - name: myapp
          image: ikubernetes/myapp:v1
    ---
    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-second
      labels:
        app: backend
        tier: db
    spec:
        containers:
        - name: busybox
          image: busybox:latest
          imagePullPolicy: IfNotPresent
          command: ["sh","-c","sleep 3600"]
        affinity:
          podAntiAffinity:
             requiredDuringSchedulingIgnoredDuringExecution:
             - labelSelector:
                  matchExpressions:
                  - {key: app1, operator: In, values: ["myapp1"]}
               topologyKey: kubernetes.io/hostname
    # kubectl apply -f pod-required-anti-affinity-demo.yaml
    # 显示两个pod不在一个node节点上,这就是pod节点反亲和性
    [root@k8s-master1 kubenetes-han]# kubectl get pods -o wide
    pod-first                     1/1     Running   0          4s      10.244.2.7    k8s-node2
    pod-second                    1/1     Running   0          4s      10.244.1.22   k8s-node1 
    # kubectl delete -f pod-required-anti-affinity-demo.yaml
    

    例3:换一个topologykey

    [root@k8s-master1 kubenetes-han]# kubectl label nodes k8s-node1  zone=foo
    [root@k8s-master1 kubenetes-han]# kubectl label nodes k8s-node2  zone=foo --overwrite
    # cat pod-first-required-anti-affinity-demo-1.yaml
    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-first
      labels:
        app3: myapp3
        tier: frontend
    spec:
        containers:
        - name: myapp
          image: ikubernetes/myapp:v1
    
    # cat pod-second-required-anti-affinity-demo-1.yaml 
    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-second
      labels:
        app: backend
        tier: db
    spec:
        containers:
        - name: busybox
          image: busybox:latest
          imagePullPolicy: IfNotPresent
          command: ["sh","-c","sleep 3600"]
        affinity:
          podAntiAffinity:
             requiredDuringSchedulingIgnoredDuringExecution:
             - labelSelector:
                  matchExpressions:
                  - {key: app3 ,operator: In, values: ["myapp3"]}
               topologyKey:  zone
    
    [root@k8s-master1 kubenetes-han]# kubectl apply -f  pod-first-required-anti-affinity-demo-1.yaml
    pod/pod-first created
    [root@k8s-master1 kubenetes-han]# kubectl apply -f  pod-second-required-anti-affinity-demo-1.yaml
    pod/pod-second created
    

    [root@k8s-master1 kubenetes-han]# kubectl get pod -o wide显示如下:
    image

    第二个pod现在是pending,因为两个节点是同一个位置,现在没有不是同一个位置的了,而且我们要求反亲和性,所以就会处于pending状态,如果在反亲和性这个位置把required改成preferred,那么也会运行。
    podaffinity:pod节点亲和性,pod倾向于哪个pod
    nodeaffinity:node节点亲和性,pod倾向于哪个node

    6、污点、容忍度

    给了节点选则的主动权,我们给节点打一个污点,不容忍的pod就运行不上来,污点就是定义在节点上的键值属性数据,可以定决定拒绝那些pod;
    taints是键值数据,用在节点上,定义污点;
    tolerations是键值数据,用在pod上,定义容忍度,能容忍哪些污点
    pod亲和性是pod属性;但是污点是节点的属性,污点定义在nodeSelector上

    # kubectl describe nodes k8s-master1
    Taints: node-role.kubernetes.io/master:NoSchedule

    # kubectl explain node.spec.taints
    KIND:     Node
    VERSION:  v1
    RESOURCE: taints <[]Object>
    DESCRIPTION:
         If specified, the node's taints.
         The node this Taint is attached to has the "effect" on any pod that does
         not tolerate the Taint.
    FIELDS:
       effect	<string> -required-
       key	<string> -required-
       timeAdded	<string>
       value	<string>
    

    taints的effect用来定义对pod对象的排斥等级(效果):

    NoSchedule:
    仅影响pod调度过程,当pod能容忍这个节点污点,就可以调度到当前节点,后来这个节点的污点改了,加了一个新的污点,使得之前调度的pod不能容忍了,那这个pod会怎么处理,对现存的pod对象不产生影响

    NoExecute:
    既影响调度过程,又影响现存的pod对象,如果现存的pod不能容忍节点后来加的污点,这个pod就会被驱逐

    PreferNoSchedule:
    最好不,也可以,是NoSchedule的柔性版本

    在pod对象定义容忍度的时候支持两种操作:
    1.等值密钥:key和value上完全匹配
    2.存在性判断:key和effect必须同时匹配,value可以是空
    在pod上定义的容忍度可能不止一个,在节点上定义的污点可能多个,需要琢个检查容忍度和污点能否匹配,每一个污点都能被容忍,才能完成调度,如果不能容忍怎么办,那就需要看pod的容忍度了
    # kubectl describe nodes k8s-master1
    查看master这个节点是否有污点,显示如下:
    image
    上面可以看到master这个节点的污点是Noschedule

    所以我们创建的pod都不会调度到master上,因为我们创建的pod没有容忍度
    kubectl describe pods kube-apiserver-k8s-master1 -n kube-system
    显示如下
    image
    可以看到这个pod的容忍度是NoExecute,则可以调度到k8s-master1上

    6.1 管理节点污点

    [root@k8s-master1]# kubectl taint –help

    例1:把k8s-node2当成是生产环境专用的,其他node是测试的,先打个污点
    [root@k8s-master1 taint]# kubectl taint node k8s-node2 node-type=production:NoSchedule
    node/k8s-node2 tainted

    [root@k8s-master1 taint]# cat pod-taint.yaml 
    apiVersion: v1
    kind: Pod
    metadata:
      name: taint-pod
      namespace: default
      labels:
        tomcat:  tomcat-pod
    spec:
      containers:
      - name:  taint-pod
        ports:
        - containerPort: 8080
        image: tomcat:8.5-jre8-alpine
        imagePullPolicy: IfNotPresent
    

    查看一下pod呗调度到哪个节点了

    [root@k8s-master1 taint]# kubectl get pods -o wide
    NAME                          READY   STATUS    RESTARTS   AGE     IP            NODE
    taint-pod                     1/1     Running   0          20s     10.244.1.23   k8s-node1
    

    可以看到都被调度到k8s-node1上了,因为k8s-node2这个节点打了污点,而我们在创建pod的时候没有容忍度,所以k8s-node2上不会有pod调度上去的

    例2:给k8s-node1也打上污点

    [root@k8s-master1 taint]# kubectl taint node k8s-node1  node-type=dev:NoExecute
    node/k8s-node1 tainted
    [root@k8s-master1 taint]# kubectl get pods -o wide
    

    如下图,可以看到node1已经存在的pod节点都被撵走了
    image

    [root@k8s-master1 taint]# cat pod-demo-1.yaml 
    apiVersion: v1
    kind: Pod
    metadata:
      name: myapp-deploy
      namespace: default
      labels:
        app: myapp
        release: canary
    spec:
          containers:
          - name: myapp
            image: ikubernetes/myapp:v1
            ports:
            - name: http
              containerPort: 80
          tolerations:
          - key: "node-type"
            operator: "Equal"
            value: "production"
            effect: "NoExecute"
            tolerationSeconds: 3600
    

    myapp-deploy 1/1 Pending 0 11s k8s-node2

    还是显示pending,因为我们使用的是equal(等值匹配),所以key和value,effect必须和node节点定义的污点完全匹配才可以,把上面配置effect: "NoExecute"变成
    effect: "NoSchedule"成;
    tolerationSeconds: 3600这行去掉
    先delete掉上面创建的pod后在修改,修改后重新应用。
    上面就可以调度到k8s-node2上了,因为在pod中定义的容忍度能容忍node节点上的污点

    例3:再次修改
    修改如下部分:

    tolerations:
    - key: "node-type"
    operator: "Exists"
    value: ""
    effect: "NoSchedule"
    

    只要对应的键是存在的,exists,其值被自动定义成通配符
    # kubectl delete -f pod-demo-1.yaml
    # kubectl apply -f pod-demo-1.yaml
    # kubectl get pods
    发现还是调度到k8s-node2上
    myapp-deploy 1/1 running 0 11s k8s-node2

    最后删除污点:
    [root@k8s-master1 taint]#kubectl taint nodes k8s-node1 node-type:NoExecute-
    node/k8s-node1 untainted
    [root@k8s-master1 taint]# kubectl taint nodes k8s-node2 node-type:NoSchedule-
    node/k8s-node2 untainted

    7、Pod常见的状态和重启策略

    7.1 常见的pod状态

    Pod的status定义在PodStatus对象中,其中有一个phase字段。它简单描述了Pod在其生命周期的阶段。熟悉Pod的各种状态对我们理解如何设置Pod的调度策略、重启策略是很有必要的。下面是 phase 可能的值,也就是pod常见的状态:
    挂起(Pending):我们在请求创建pod时,条件不满足,调度没有完成,没有任何一个节点能满足调度条件,已经创建了pod但是没有适合它运行的节点叫做挂起,调度没有完成,处于pending的状态会持续一段时间:包括调度Pod的时间和通过网络下载镜像的时间。
    运行中(Running):Pod已经绑定到了一个节点上,Pod 中所有的容器都已被创建。至少有一个容器正在运行,或者正处于启动或重启状态。
    成功(Succeeded):Pod 中的所有容器都被成功终止,并且不会再重启。
    失败(Failed):Pod 中的所有容器都已终止了,并且至少有一个容器是因为失败终止。也就是说,容器以非0状态退出或者被系统终止。
    未知(Unknown):未知状态,所谓pod是什么状态是apiserver和运行在pod节点的kubelet进行通信获取状态信息的,如果节点之上的kubelet本身出故障,那么apiserver就连不上kubelet,得不到信息了,就会看Unknown

    扩展:还有其他状态,如下:
    Evicted状态:出现这种情况,多见于系统内存或硬盘资源不足,可df-h查看docker存储所在目录的资源使用情况,如果百分比大于85%,就要及时清理下资源,尤其是一些大文件、docker镜像。
    CrashLoopBackOff:容器曾经启动了,但可能又异常退出了
    Error 状态:Pod 启动过程中发生了错误

    7.2 pod重启策略

    Pod的重启策略(RestartPolicy)应用于Pod内的所有容器,并且仅在Pod所处的Node上由kubelet进行判断和重启操作。当某个容器异常退出或者健康检查失败时,kubelet将根据 RestartPolicy 的设置来进行相应的操作。

    Pod的重启策略包括 Always、OnFailure和Never,默认值为Always。
    Always:当容器失败时,由kubelet自动重启该容器。
    OnFailure:当容器终止运行且退出码不为0时,由kubelet自动重启该容器。
    Never:不论容器运行状态如何,kubelet都不会重启该容器。

    [root@k8s-master1 ~]# vim pod.yaml
    apiVersion: v1
    kind: Pod
    metadata:
    name: demo-pod
    namespace: default
    labels:
    app: myapp
    spec:
    restartPolicy: Always
    containers:

    • name: tomcat-pod-java
      ports:
      • containerPort: 8080
        image: tomcat:8.5-jre8-alpine
        imagePullPolicy: IfNotPresent

    8、Pod生命周期

    概念图
    image

    8.1 Init容器

    Pod 里面可以有一个或者多个容器,部署应用的容器可以称为主容器,在创建Pod时候,Pod 中可以有一个或多个先于主容器启动的Init容器,这个init容器就可以成为初始化容器,初始化容器一旦执行完,它从启动开始到初始化代码执行完就退出了,它不会一直存在,所以在主容器启动之前执行初始化,初始化容器可以有多个,多个初始化容器是要串行执行的,先执行初始化容器1,在执行初始化容器2等,等初始化容器执行完初始化就退出了,然后再执行主容器,主容器一退出,pod就结束了,主容器退出的时间点就是pod的结束点,它俩时间轴是一致的;

    Init容器就是做初始化工作的容器。可以有一个或多个,如果多个按照定义的顺序依次执行,只有所有的初始化容器执行完后,主容器才启动。由于一个Pod里的存储卷是共享的,所以Init Container里产生的数据可以被主容器使用到,Init Container可以在多种K8S资源里被使用到,如Deployment、DaemonSet, StatefulSet、Job等,但都是在Pod启动时,在主容器启动前执行,做初始化工作。

    Init容器与普通的容器区别是:
    1、Init 容器不支持 Readiness,因为它们必须在Pod就绪之前运行完成
    2、每个Init容器必须运行成功,下一个才能够运行
    3、如果 Pod 的 Init 容器失败,Kubernetes 会不断地重启该 Pod,直到 Init 容器成功为止,然而,如果Pod对应的restartPolicy值为 Never,它不会重新启动。

    初始化容器的官方地址:
    https://kubernetes.io/docs/concepts/workloads/pods/init-containers/#init-containers-in-use
    访问官方文档
    在本机也编辑一个yaml

    [root@k8s-master1 init-pod]# cat init-pod.yaml
    apiVersion: v1
    kind: Pod
    metadata:
      name: myapp-pod
      labels:
        app: myapp
    spec:
      containers:
      - name: myapp-container
        image: busybox:1.28
        command: ['sh', '-c', 'echo The app is running! && sleep 3600']
      initContainers:
      - name: init-myservice
        image: busybox:1.28
        command: ['sh', '-c', "until nslookup myservice.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for myservice; sleep 2; done"]
      - name: init-mydb
        image: busybox:1.28
        command: ['sh', '-c', "until nslookup mydb.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for mydb; sleep 2; done"]
    

    更新yaml文件,此文件一直处于初始化阶段

    [root@k8s-master1 init-pod]# kubectl get pod 
    NAME                          READY   STATUS             RESTARTS   AGE
    myapp-pod                     0/1     Init:0/2           0          3m17s
    

    可以通过kubectl describe pod myapp-pod查看一下pod相关信息
    有两个容器需要先进行初始化后才能继续,
    kubectl logs myapp-pod -c init-myservice # Inspect the first init container
    kubectl logs myapp-pod -c init-mydb # Inspect the second init container
    yaml文件中定义的需要解析db的服务,因为我们还没有创建所以一直卡在这,按着官网文档先进行创建一个

    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: myservice
    spec:
      ports:
      - protocol: TCP
        port: 80
        targetPort: 9376
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: mydb
    spec:
      ports:
      - protocol: TCP
        port: 80
        targetPort: 9377
    

    8.2 主容器

    1、容器钩子
    初始化容器启动之后,开始启动主容器,在主容器启动之前有一个post start hook(容器启动后钩子)和pre stop hook(容器结束前钩子),无论启动后还是结束前所做的事我们可以把它放两个钩子,这个钩子就表示用户可以用它来钩住一些命令,来执行它,做开场前的预设,结束前的清理,如awk有begin,end,和这个效果类似;
    postStart:该钩子在容器被创建后立刻触发,通知容器它已经被创建。如果该钩子对应的hook handler执行失败,则该容器会被杀死,并根据该容器的重启策略决定是否要重启该容器,这个钩子不需要传递任何参数。
    preStop:该钩子在容器被删除前触发,其所对应的hook handler必须在删除该容器的请求发送给Docker daemon之前完成。在该钩子对应的hook handler完成后不论执行的结果如何,Docker daemon会发送一个SGTERN信号量给Docker daemon来删除该容器,这个钩子不需要传递任何参数。

    在k8s中支持两类对pod的检测,第一类叫做livenessprobe(pod存活性探测)
    存活探针主要作用是,用指定的方式检测pod中的容器应用是否正常运行,如果检测失败,则认为容器不健康,那么Kubelet将根据Pod中设置的 restartPolicy来判断Pod 是否要进行重启操作,如果容器配置中没有配置 livenessProbe,Kubelet 将认为存活探针探测一直为成功状态。
    第二类是状态检readinessprobe(pod就绪性探测):用于判断容器中应用是否启动完成,当探测成功后才使Pod对外提供网络访问,设置容器Ready状态为true,如果探测失败,则设置容器的Ready状态为false。

    8.3 创建pod需要经过哪些阶段?

    当用户创建pod时,这个请求给apiserver,apiserver把创建请求的状态保存在etcd中;
    接下来apiserver会请求scheduler来完成调度,如果调度成功,会把调度的结果(如调度到哪个节点上了,运行在哪个节点上了,把它更新到etcd的pod资源状态中)保存在etcd中,一旦存到etcd中并且完成更新以后,如调度到k8s-master1上,那么k8s-master1节点上的kubelet通过apiserver当中的状态变化知道有一些任务被执行了,所以此时此kubelet会拿到用户创建时所提交的清单,这个清单会在当前节点上运行或者启动这个pod,如果创建成功或者失败会有一个当前状态,当前这个状态会发给apiserver,apiserver在存到etcd中;在这个过程中,etcd和apiserver一直在打交道,不停的交互,scheduler也参与其中,负责调度pod到合适的node节点上,这个就是pod的创建过程

    pod在整个生命周期中有非常多的用户行为:
    1、初始化容器完成初始化
    2、主容器启动后可以做启动后钩子
    3、主容器结束前可以做结束前钩子
    4、在主容器运行中可以做一些健康检测,如liveness probe,readness probe

    9、容器钩子

    Pod容器探测和钩子
    10.1 容器钩子:postStart和preStop
    postStart:容器创建成功后,运行前的任务,用于资源部署、环境准备等。
    preStop:在容器被终止前的任务,用于优雅关闭应用程序、通知其他系统等。

    演示postStart和preStop用法
    ......
    containers:

    • image: sample:v2
      name: war
      lifecycle:
      postStart:
      exec:
      command:
      - “cp”
      - “/sample.war”
      - “/app”
      prestop:
      httpGet:
      host: monitor.com
      path: /waring
      port: 8080
      scheme: HTTP
      ......

    以上示例中,定义了一个Pod,包含一个JAVA的web应用容器,其中设置了PostStart和PreStop回调函数。即在容器创建成功后,复制/sample.war到/app文件夹中。而在容器终止之前,发送HTTP请求到http://monitor.com:8080/waring,即向监控系统发送警告。

    优雅的删除资源对象
    当用户请求删除含有pod的资源对象时(如RC、deployment等),K8S为了让应用程序优雅关闭(即让应用程序完成正在处理的请求后,再关闭软件),K8S提供两种信息通知:
    1)、默认:K8S通知node执行docker stop命令,docker会先向容器中PID为1的进程发送系统信号SIGTERM,然后等待容器中的应用程序终止执行,如果等待时间达到设定的超时时间,或者默认超时时间(30s),会继续发送SIGKILL的系统信号强行kill掉进程。

    2)、使用pod生命周期(利用PreStop回调函数),它执行在发送终止信号之前。
    默认情况下,所有的删除操作的优雅退出时间都在30秒以内。kubectl delete命令支持--grace-period=的选项,以运行用户来修改默认值。0表示删除立即执行,并且立即从API中删除pod。在节点上,被设置了立即结束的的pod,仍然会给一个很短的优雅退出时间段,才会开始被强制杀死。如下:
    spec:
    containers:
    - name: nginx-demo
    image: centos:nginx
    lifecycle:
    preStop:
    exec:
    # nginx -s quit gracefully terminate while SIGTERM triggers a quick exit
    command: ["/usr/local/nginx/sbin/nginx","-s","quit"]
    ports:
    - name: http
    containerPort: 80

    10.2 存活性探测livenessProbe和就绪性探测readinessProbe
    livenessProbe:存活性探测
    许多应用程序经过长时间运行,最终过渡到无法运行的状态,除了重启,无法恢复。通常情况下,K8S会发现应用程序已经终止,然后重启应用程序pod。有时应用程序可能因为某些原因(后端服务故障等)导致暂时无法对外提供服务,但应用软件没有终止,导致K8S无法隔离有故障的pod,调用者可能会访问到有故障的pod,导致业务不稳定。K8S提供livenessProbe来检测容器是否正常运行,并且对相应状况进行相应的补救措施。

    readinessProbe:就绪性探测
    在没有配置readinessProbe的资源对象中,pod中的容器启动完成后,就认为pod中的应用程序可以对外提供服务,该pod就会加入相对应的service,对外提供服务。但有时一些应用程序启动后,需要较长时间的加载才能对外服务,如果这时对外提供服务,执行结果必然无法达到预期效果,影响用户体验。比如使用tomcat的应用程序来说,并不是简单地说tomcat启动成功就可以对外提供服务的,还需要等待spring容器初始化,数据库连接上等等。

    目前LivenessProbe和ReadinessProbe两种探针都支持下面三种探测方法:
    1、ExecAction:在容器中执行指定的命令,如果执行成功,退出码为 0 则探测成功。
    2、TCPSocketAction:通过容器的 IP 地址和端口号执行 TCP 检 查,如果能够建立 TCP 连接,则表明容器健康。
    3、HTTPGetAction:通过容器的IP地址、端口号及路径调用 HTTP Get方法,如果响应的状态码大于等于200且小于400,则认为容器健康

    探针探测结果有以下值:
    1、Success:表示通过检测。
    2、Failure:表示未通过检测。
    3、Unknown:表示检测没有正常进行。

    Pod探针相关的属性:
    探针(Probe)有许多可选字段,可以用来更加精确的控制Liveness和Readiness两种探针的行为
    initialDelaySeconds: Pod启动后首次进行检查的等待时间,单位“秒”。
    periodSeconds: 检查的间隔时间,默认为10s,单位“秒”。
    timeoutSeconds: 探针执行检测请求后,等待响应的超时时间,默认为1s,单位“秒”。
    successThreshold:连续探测几次成功,才认为探测成功,默认为 1,在 Liveness 探针中必须为1,最小值为1。

    failureThreshold: 探测失败的重试次数,重试一定次数后将认为失败,在 readiness 探针中,Pod会被标记为未就绪,默认为 3,最小值为 1

    两种探针区别:
    ReadinessProbe 和 livenessProbe 可以使用相同探测方式,只是对 Pod 的处置方式不同:
    readinessProbe 当检测失败后,将 Pod 的 IP:Port 从对应的 EndPoint 列表中删除。
    livenessProbe 当检测失败后,将杀死容器并根据 Pod 的重启策略来决定作出对应的措施。

    Pod探针使用示例:
    1、LivenessProbe 探针使用示例
    (1)、通过exec方式做健康探测
    示例文件 liveness-exec.yaml
    apiVersion: v1
    kind: Pod
    metadata:
    name: liveness-exec
    labels:
    app: liveness
    spec:
    containers:

    • name: liveness
      image: busybox
      args: #创建测试探针探测的文件
      • /bin/sh
      • -c
      • touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
        livenessProbe:
        initialDelaySeconds: 10 #延迟检测时间
        periodSeconds: 5 #检测时间间隔
        exec:
        command:
        • cat
        • /tmp/healthy

    容器启动设置执行的命令:
    /bin/sh -c "touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600"
    容器在初始化后,首先创建一个 /tmp/healthy 文件,然后执行睡眠命令,睡眠 30 秒,到时间后执行删除 /tmp/healthy 文件命令。而设置的存活探针检检测方式为执行 shell 命令,用 cat 命令输出 healthy 文件的内容,如果能成功执行这条命令,存活探针就认为探测成功,否则探测失败。在前 30 秒内,由于文件存在,所以存活探针探测时执行 cat /tmp/healthy 命令成功执行。30 秒后 healthy 文件被删除,所以执行命令失败,Kubernetes 会根据 Pod 设置的重启策略来判断,是否重启 Pod。

    (2)、通过HTTP方式做健康探测
    示例文件 liveness-http.yaml
    apiVersion: v1
    kind: Pod
    metadata:
    name: liveness-http
    labels:
    test: liveness
    spec:
    containers:

    • name: liveness
      image: mydlqclub/springboot-helloworld:0.0.1
      livenessProbe:
      initialDelaySeconds: 20 #延迟加载时间
      periodSeconds: 5 #重试时间间隔
      timeoutSeconds: 10 #超时时间设置
      httpGet:
      scheme: HTTP
      port: 8081
      path: /actuator/health

    上面 Pod 中启动的容器是一个 SpringBoot 应用,其中引用了 Actuator 组件,提供了 /actuator/health 健康检查地址,存活探针可以使用 HTTPGet 方式向服务发起请求,请求 8081 端口的 /actuator/health 路径来进行存活判断:

    任何大于或等于200且小于400的代码表示探测成功。
    任何其他代码表示失败。

    如果探测失败,则会杀死 Pod 进行重启操作。

    httpGet探测方式有如下可选的控制字段:
    scheme: 用于连接host的协议,默认为HTTP。
    host:要连接的主机名,默认为Pod IP,可以在http request head中设置host头部。
    port:容器上要访问端口号或名称。
    path:http服务器上的访问URI。
    httpHeaders:自定义HTTP请求headers,HTTP允许重复headers。

    (3)、通过TCP方式做健康探测
    示例文件 liveness-tcp.yaml
    apiVersion: v1
    kind: Pod
    metadata:
    name: liveness-tcp
    labels:
    app: liveness
    spec:
    containers:

    • name: liveness
      image: nginx
      livenessProbe:
      initialDelaySeconds: 15
      periodSeconds: 20
      tcpSocket:
      port: 80
      TCP 检查方式和 HTTP 检查方式非常相似,在容器启动 initialDelaySeconds 参数设定的时间后,kubelet 将发送第一个 livenessProbe 探针,尝试连接容器的 80 端口,如果连接失败则将杀死 Pod 重启容器。

    2、ReadinessProbe 探针使用示例

    Pod 的ReadinessProbe 探针使用方式和 LivenessProbe 探针探测方法一样,也是支持三种,只是一个是用于探测应用的存活,一个是判断是否对外提供流量的条件。这里用一个 Springboot 项目,设置 ReadinessProbe 探测 SpringBoot 项目的 8081 端口下的 /actuator/health 接口,如果探测成功则代表内部程序以及启动,就开放对外提供接口访问,否则内部应用没有成功启动,暂不对外提供访问,直到就绪探针探测成功。
    示例文件 readiness-exec.yaml
    apiVersion: v1
    kind: Service
    metadata:
    name: springboot
    labels:
    app: springboot
    spec:
    type: NodePort
    ports:

    • name: server
      port: 8080
      targetPort: 8080
      nodePort: 31180
    • name: management
      port: 8081
      targetPort: 8081
      nodePort: 31181
      selector:
      app: springboot

    apiVersion: v1
    kind: Pod
    metadata:
    name: springboot
    labels:
    app: springboot
    spec:
    containers:

    • name: springboot
      image: mydlqclub/springboot-helloworld:0.0.1
      ports:
      • name: server
        containerPort: 8080
      • name: management
        containerPort: 8081
        readinessProbe:
        initialDelaySeconds: 20
        periodSeconds: 5
        timeoutSeconds: 10
        httpGet:
        scheme: HTTP
        port: 8081
        path: /actuator/health

    3、ReadinessProbe + LivenessProbe 配合使用示例
    一般程序中需要设置两种探针结合使用,并且也要结合实际情况,来配置初始化检查时间和检测间隔,下面列一个简单的 SpringBoot 项目的 Deployment 例子。

    apiVersion: v1
    kind: Service
    metadata:
    name: springboot
    labels:
    app: springboot
    spec:
    type: NodePort
    ports:

    • name: server
      port: 8080
      targetPort: 8080
      nodePort: 31180
    • name: management
      port: 8081
      targetPort: 8081
      nodePort: 31181
      selector:
      app: springboot

    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: springboot
    labels:
    app: springboot
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: springboot
    template:
    metadata:
    name: springboot
    labels:
    app: springboot
    spec:
    containers:
    - name: readiness
    image: mydlqclub/springboot-helloworld:0.0.1
    ports:
    - name: server
    containerPort: 8080
    - name: management
    containerPort: 8081
    readinessProbe:
    initialDelaySeconds: 20
    periodSeconds: 5
    timeoutSeconds: 10
    httpGet:
    scheme: HTTP
    port: 8081
    path: /actuator/health
    livenessProbe:
    initialDelaySeconds: 30
    periodSeconds: 10
    timeoutSeconds: 5
    httpGet:
    scheme: HTTP
    port: 8081
    path: /actuator/health

    我有梦,有远方,我会为了它们奔跑,奋斗,直到成为连我自己都佩服的人。
  • 相关阅读:
    WinForm事件中的Object sender和EventArgs e参数
    Day 25:Python 模块 collections 3 个常用类
    Day 23:Python 中的一些高频面试题及解答小记
    Day 22:Python 迭代器和生成器小记
    Day 21:Python 多线程和协程
    Day 20:python中几个常用的内置函数
    Day 19:Python 函数五类参数
    遍历容器auto方法
    JS鼠标提示框效果
    数据库(二)
  • 原文地址:https://www.cnblogs.com/fengdejiyixx/p/15265265.html
Copyright © 2020-2023  润新知