部分配置文件示例

  • [[#DePloyment 无状态应用]]

  • [[#Service]]

  • [[#Ingress]]

  • [[#DaemonSet]]

  • [[#StatefulSet 有状态应用]]

  • [[#PV、PVC和StorageClass]]

  • [[#pod亲和反亲和]]

  • [[#ConfigMap]]

  • [[#Cronjob定时任务]]

  • [[#Secret]]

  • [[#配置存活、就绪和启动探测器]]


DePloyment 无状态应用

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: APP_NAME
  namespace: kube-system
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: APP_NAME
    spec:
      containers:
      - name: CONTAINER_NAME_1
        image: docker.io/image_name_1:latest
        env:
          - name: TZ    # 设置容器的时区为东八区
            value: Asia/Shanghai
		imagePullPolicy: Always
        restartPolicy: Always
        resources:
          requests:
            memory: "512Mi"
            cpu: "200m"
          limits:
            memory: "1024Mi"
            cpu: "800m"
        volumeMounts:
           - name: weblog
             mountPath: /logs
        ports:
        - containerPort: 8091
      - name: CONTAINER_NAME_2
        image: docker.io/image_name_2:latest
		imagePullPolicy: IfNotPresent
        restartPolicy: Always
		readinessProbe:
          httpGet:
            path: /favicon.ico
            port: 80
            scheme: HTTP
          initialDelaySeconds: 15    
          failureThreshold: 3
          periodSeconds: 30
          timeoutSeconds: 20
        livenessProbe:
          httpGet:
            path: /favicon.ico
            port: 80
            scheme: HTTP
          initialDelaySeconds: 15    
          failureThreshold: 3
          periodSeconds: 30
          timeoutSeconds: 20

        volumeMounts:
           - name: web
             mountPath: /www
        ports:
        - containerPort: 443
      volumes:
        - name: weblog
          nfs:
            server: 1.1.1.1
            path: "/path/logs"
        - name: web
          nfs:
            server: 1.1.1.1
            path: "/path/web"

pod亲和反亲和

pod 反亲和

k8s pod 经常挤在同一节点或因为没配置资源限制某一节点内存已经接近临界值仍不会迁移。而且在手动删除容器重启时还会出现再次在该节点重建的情况,可以通过pod的反亲和特性来使其在手动删除pod时不会在同一节点重建

kind: Deployment

...

spec:

    ...
    
    spec:
    
      ...
      
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - topologyKey: kubernetes.io/hostname
            labelSelector:
              matchLabels:
                app: NAME	# 这里配置成本pod名称,重建时就不会在同一节点出现两个同名pod

pod 亲和

kind: Deployment

...

spec:

    ...
    
    spec:
    
      ...
      
      affinity:
		podAffinity:	# 只是这里跟反亲和不一样
		  requiredDuringSchedulingIgnoredDuringExecution:
		  
		  # 这里可以改成标签名,这样就可以先匹配这个标签的节点,再匹配下面的app名称
		  - topologyKey: kubernetes.io/hostname	
		    labelSelector:
			  matchLabels:
			    app: NAME	# 这里配置成想要在同一节点的pod名称

DaemonSet

apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
  name: promtail
  namespace: ma
spec: 
  template:
    metadata:
      labels:
        app: promtail
      name: promtail
    spec:
      containers:
      - name: promtail
        image: grafana/promtail:latest
        imagePullPolicy: IfNotPresent
        args: ["-config.file=/mnt/config/promtail-config.yaml"]
        resources:
          requests:
            memory: "512Mi"
            cpu: "200m"
          limits:
            memory: "4096Mi"
            cpu: "1000m"
        volumeMounts:
        - mountPath: /mnt/config
          name: promtail-config
        - mountPath: /mnt/zoo-logs
          name: zoo-logs
        - mountPath: /mnt/projectlogs
          name: projectlogs
        ports:
        - containerPort: 9093
      volumes:
      - name: promtail-config
        configMap:
          name: loki-promtail-config
      - name: zoo-logs
        hostPath:
          path: /jpdata/data
      - name: projectlogs
        hostPath:
          path: /jpdata/log

StatefulSet 有状态应用

StatefulSet的特性:

  • StatefulSet给每个Pod提供固定名称,Pod名称增加从0-N的固定后缀,Pod重新调度后Pod名称和HostName不变。

  • StatefulSet通过Headless Service给每个Pod提供固定的访问域名,Service的概念会在Service中详细介绍。

  • StatefulSet通过创建固定标识的PVC保证Pod重新调度后还是能访问到相同的持久化数据。

创建Statefulset需要一个Headless Service用于Pod访问,Service的概念会在Service中详细介绍,这里先介绍Headless Service的创建方法。

使用如下文件描述Headless Service,其中:

  • spec.clusterIP:必须设置为None,表示Headless Service。

  • spec.ports.port:Pod间通信端口号。

  • spec.ports.name:Pod间通信端口名称。

创建Headless Service

apiVersion: v1
kind: Service       # 对象类型为Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
    - name: nginx     # Pod间通信的端口名称
      port: 80        # Pod间通信的端口号
  selector:
    app: nginx        # 选择标签为app:nginx的Pod
  clusterIP: None     # 必须设置为None,表示Headless Service

创建Statefulset

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: nginx
spec:
  serviceName: nginx # headless service的名称
  replicas: 3
  # 更新时允许的最大不可用Pod数量,它决定了应用更新的速度和可用性之间的权衡。
  maxUnavailable: 1

  # Pod的更新策略设置为"RollingUpdate"。这意味着StatefulSet会在更新期间逐个地迁移Pod,而不是同时关闭所有Pod。
  updateStrategy:
    type: RollingUpdate
    rollingUpdate:
	  # partition策略可以实现分区。 如果声明了一个分区,当 StatefulSet 的 .spec.template 被更新时,
	  # 所有序号大于等于该分区序号的 Pod 都会被更新。 所有序号小于该分区序号的 Pod 都不会被更新,
	  # 并且,即使它们被删除也会依据之前的版本进行重建。 这里表示如果更新,则更新除编号为0的pod
      partition: 1
      # "OrderedReady"策略只适用于StatefulSet的按顺序驱逐Pod驱逐操作,而不适用于Pod的滚动更新操作。
      # 如果要进行滚动更新操作,需要使用"RollingUpdate"策略
      updateOrder: OrderedReady

  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: container-0
          image: nginx:alpine
          resources:
            limits:
              cpu: 100m
              memory: 200Mi
            requests:
              cpu: 100m
              memory: 200Mi
          volumeMounts:                           # Pod挂载的存储
          - name:  data
            mountPath:  /usr/share/nginx/html     # 存储挂载到/usr/share/nginx/html
      imagePullSecrets:
        - name: default-secret
  # PVC模板
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes:
      - ReadWriteMany
      resources:
        requests:
          storage: 1Gi
      storageClassName: csi-nas                   # 持久化存储的类型

initContainers

Init 容器是一种特殊容器,在 Pod 内的应用容器启动之前运行。Init 容器可以包括一些应用镜像中不存在的实用工具和安装脚本。

其特点和作用可以归纳如下:

  • initContainers在主容器启动之前按顺序执行,全部执行成功后才会启动主容器。

  • initContainers和主容器在不同的容器中隔离运行,有不同的文件系统视图。

  • initContainers可以有多个,之间是串行执行的关系。

  • 如果任何一个initContainer失败了,都会导致Pod启动失败。

  • initContainers可以用于执行一些预初始化工作,如数据或文件拉取,配置或验证等。

  • initContainers提供了一种类似故障漏斗的机制,必须前置任务成功后才能继续后续任务。

  • initContainers可以在Pod重启的时候再次执行 Initialization 逻辑。

这里是一个使用 initContainers 的示例:

apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app: myapp
spec:
  containers:
  - name: myapp-container
    image: busybox
    command: ['sh', '-c', 'echo The app is running! && sleep 3600']
  initContainers:
  - name: init-myservice
    image: busybox
    command: ['sh', '-c', 'until nslookup myservice; do echo waiting for myservice; sleep 2; done;']
  - name: init-properties
    image: busybox
    command: ['sh', '-c', 'until test -f /mnt/properties/app.properties; do echo waiting for properties; sleep 2; done;']
    volumeMounts:
    - name: config-volume
      mountPath: /mnt/properties
  volumes:
  - name: config-volume
    configMap:
      name: app-configmap

这个示例中有两个 initContainers:

  1. init-myservice 容器会等待 myservice 这个服务准备就绪

  2. init-properties 容器会等待 app.properties 配置文件准备就绪

只有两个 init 容器都执行成功了,myapp-container 主容器才会启动。

init容器与主容器之间主要通过两种方式进行交互和数据传递:

通过volume挂载Volume来传递数据 init容器和主容器可以挂载同一个Volume,init容器在Volume中写入数据,主容器启动后可以读取这些数据,以此实现两者之间的数据传递。

通过文件或目录的写操作 由于init容器与主容器共享一个Pod上下文和文件系统视图,init容器对Pod内目录的修改操作,也会反映到主容器中。

举个例子,init容器在某目录下创建了一个文件,那么主容器就可以读取到这个文件,两者实现了交互。

需要注意的是,init容器必须保证操作的文件或目录不会被主容器inplace覆盖。另外,如果Volume支持可写(ReadWriteMany),也可以在Volume中进行交互。

如果init容器修改了基础镜像中的某个配置的值,而这个配置对主容器也是可见的,那么主容器中这个配置的值会是init容器修改后的值。

主要原因是:

init容器与主容器共享同一个Pod的文件系统视图。 当init容器启动时,它实际上启动的是基础镜像的一个容器实例。init容器对镜像中文件所做的任何修改,都会反映到文件系统视图中。 主容器启动时,它看到的文件系统视图已经被init容器做过修改。


PV、PVC和StorageClass

如果要求Pod重新调度后仍然能使用之前读写过的数据,就只能使用网络存储了,网络存储种类非常多且有不同的使用方法,通常一个云服务提供商至少有块存储、文件存储、对象存储三种,如华为云的EVS、SFS和OBS。Kubernetes解决这个问题的方式是抽象了PV(PersistentVolume)和PVC(PersistentVolumeClaim)来解耦这个问题,从而让使用者不用关心具体的基础设施,当需要存储资源的时候,只要像CPU和内存一样,声明要多少即可。

  • PV:PV描述的是持久化存储卷,主要定义的是一个持久化存储在宿主机上的目录,比如一个NFS的挂载目录。

  • PVC:PVC描述的是Pod所希望使用的持久化存储的属性,比如,Volume存储的大小、可读写权限等等。 Kubernetes管理员设置好网络存储的类型,提供对应的PV描述符配置到Kubernetes,使用者需要存储的时候只需要创建PVC,然后在Pod中使用Volume关联PVC,即可让Pod使用到存储资源,它们之间的关系如下图所示。

如果使用网络存储,则直接使用对应的协议即可。如果要使用k8s的CSI来进行调度存储,可以用本地存储类或第三方存储类(如rook、ceph等)来管理存储。需要弄清楚几个问题

  • StorageClass :存储类,只有安装了存储类,才能创建跟存存储类一样的PV,PVC。一般用本地存储

  • PV:先在宿主机上划出一个指定大小的空间用来准备给PVC申请,

  • PVC:在已经划分出来的PV空间中申请可用的存储空间,一般一个PV尽量只给一个PVC。

StorageClass 存储类

存储类分静态和动态两种

动态存储类(Dynamic Provisioning): 动态存储类允许Kubernetes自动创建PersistentVolume(PV)以满足PersistentVolumeClaim(PVC)的需求。当PVC被创建并指定使用某个动态存储类时,Kubernetes会按照存储类的定义自动创建一个符合要求的PV。这样,集群管理员无需手动创建PV,而是让Kubernetes根据需要动态创建。

以下是一个动态存储类的示例:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: standard
provisioner: kubernetes.io/aws-ebs
parameters:
  type: gp2

上面的示例使用了AWS的EBS(Elastic Block Store)作为存储后端,当PVC使用该存储类时,Kubernetes会自动创建对应的EBS卷。

静态存储类(Static Provisioning): 静态存储类是手动创建的,它与预先存在的PersistentVolume相关联。在这种情况下,集群管理员需要手动创建PV,并为它们定义一个静态存储类。PVC可以选择使用这个静态存储类,并将其绑定到预先创建的PV上。

以下是一个静态存储类的示例:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true

总结: 动态存储类: 允许Kubernetes在需要时自动创建PV,无需手动干预。它通常与云服务提供商的存储解决方案一起使用。

静态存储类: 需要手动创建PV,并将其绑定到静态存储类。这种方法通常用于本地存储或其他不支持动态创建PV的场景。

CSI

Kubernetes提供了CSI接口(Container Storage Interface,容器存储接口),基于CSI这套接口,可以开发定制出CSI插件,从而支持特定的存储,达到解耦的目的。

pv示例

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-0		# PV的名称
spec:
  capacity:
    storage: 5Gi	# 定义PV的大小
  accessModes:
  - ReadWriteOnce		# 读写模式
  persistentVolumeReclaimPolicy: Retain
  storageClassName: local-storage # 这个就是上面的StorageClass创建时定义的存储类名称
  hostPath:				 # 存储类型为本机存储
    path: "/data/data0"

---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-example			# PV的名称,下面的PVC中要调用
spec:
  accessModes:
  - ReadWriteMany                      # 读写模式
  capacity:
    storage: 10Gi                      # 定义PV的大小
  
  # 以下内容需要对应平台的插件支持,这里拿华为云为例,其它云平台请参考帮助文档
  csi:
    driver: nas.csi.everest.io         # 声明使用的驱动                   
 fsType: nfs                        # 存储类型为NFS存储
 volumeAttributes:
 everest.io/share-export-location: sfs-nas01.cn-north-4b.myhuaweicloud.com:/share-96314776 # 挂载地址
 volumeHandle: 68e4a4fd-d759-444b-8265-20dc66c8c502                                        # 存储ID

pvc示例

调用静态存储类

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-example
spec:
  accessModes:
  - ReadWriteMany
  resources:
    requests:
      storage: 10Gi              # 声明存储的大小
  storageClassName: local-storage # 这个就是上面的StorageClass创建时定义的存储类名称
  volumeName: pv-example         # 上面PV的名称,要匹配

在StatefulSet 还可以通过定义PVC模板,自动创建pod所需的PVC,前提是如果使用的静态存储类,则需要先手动创建足够数量的匹配PV,该PVC模板才会生效,否则PVC会一直处于pending状态

下面是在StatefulSet中的PVC模板配置

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: my-statefulset
spec:
  serviceName: "my-service"
  replicas: 3
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - name: mycontainer
        image: myimage
        volumeMounts:
        - name: mypvc
          mountPath: /data
  # PVC模板配置,定义名称为mypvc的模板,存储类名称manual-storage,存储大小1G,
  # 部署该服务时会自动根据replicas数量创建所需的PVC,如果是静态存储类,需要提前创建足够数量的PV。
  volumeClaimTemplates:
  - metadata:
      name: mypvc
    spec:
      accessModes:
        - ReadWriteOnce
      storageClassName: manual-storage
      resources:
        requests:
          storage: 1Gi

ConfigMap

ConfigMap 是一种 API 对象,用来将非机密性的数据保存到键值对中。使用时, Pods 可以将其用作环境变量、命令行参数或者存储卷中的配置文件。

注意: ConfigMap 并不提供保密或者加密功能。 如果你想存储的数据是机密的,请使用 Secret, 或者使用其他第三方工具来保证你的数据的私密性,而不是用 ConfigMap。

ConfigMap 配置示例

apiVersion: v1
kind: ConfigMap
metadata: 
  name: configmap_name
data:
  # 属性类;每一个键映射一个值
  app_ver: "1.0.2"
  config_file_name: "config.yaml"
    
  # 文件类
  config.yaml: |
    "str"

ConfigMap调用示例

apiVersion: v1
kind: Pod
metadata: 
  name: test-app
spec: 
  containers:
    - name: test-app
	  image: alpine
	  command: ['sleep', '3600']
	  env: 
	    # 定义环境变量
		- name: ENV_NAME
		  valueFrom: 
		    configMapKeyRef: 
			  name: configmap_name # 这个值来自ConfigMap
			  key: app_ver	# 对应ConfigMap中要取值的键
	    - name: ENV_NAME_2
		  valueFrom: 
		    configMapKeyRef:
			  name: configmap_name
			  key: config.yaml	# 对应ConfigMap中的配置文件
      volumeMounts:
      - name: app-config
        mountPath: "/config"	# 容器内的位置
    	  readOnly: true
  volumes: 
  - name: app-config
    configMap:
	  # 配置要挂载的ConfigMap名字,例如上面的ConfigMap名称为configmap_name
	  name: configmap_name
	  

当卷中使用的 ConfigMap 被更新时,所投射的键最终也会被更新。 kubelet 组件会在每次周期性同步时检查所挂载的 ConfigMap 是否为最新。 不过,kubelet 使用的是其本地的高速缓存来获得 ConfigMap 的当前值。 高速缓存的类型可以通过 KubeletConfiguration 结构ConfigMapAndSecretChangeDetectionStrategy 字段来配置。

配置不可变更的ConfigMap

对于大量使用 ConfigMap 的 集群(至少有数万个各不相同的 ConfigMap 给 Pod 挂载)而言,禁止更改 ConfigMap 的数据有以下好处:

  • 保护应用,使之免受意外(不想要的)更新所带来的负面影响。

  • 通过大幅降低对 kube-apiserver 的压力提升集群性能,这是因为系统会关闭 对已标记为不可变更的 ConfigMap 的监视操作。

此功能特性由 ImmutableEphemeralVolumes 特性门控 来控制。你可以通过将 immutable 字段设置为 true 创建不可变更的 ConfigMap。

示例如下:

apiVersion: v1
kind: ConfigMap
metadata:
  ...
 data:
   ...
 immutable: true	
# 创建、查看ConfigMap
kubectl create configmap CONFIGMAP_NAME --from-file=/path/file	# 指定单个文件
kubectl create configmap CONFIGMAP_NAME --from-file=/path/		# 将路径下的所有文件创建成一个ConfigMap

kubectl get configmap CONFIGMAP_NAME -o yaml 	# 查看创建后的内容

Secret

Secret 对象类型用来保存敏感信息,例如密码、OAuth 令牌和 SSH 密钥。 将这些信息放在 secret 中比放在 Pod 的定义或者 容器镜像 中来说更加安全和灵活。

注意: Kubernetes Secret 默认情况下存储为 base64-编码的、非加密的字符串。 默认情况下,能够访问 API 的任何人,或者能够访问 Kubernetes 下层数据存储(etcd) 的任何人都可以以明文形式读取这些数据。 为了能够安全地使用 Secret,我们建议你(至少):

  1. 为 Secret 启用静态加密

  2. 启用 或配置 RBAC 规则来限制对 Secret 的读写操作。 要注意,任何被允许创建 Pod 的人都默认地具有读取 Secret 的权限。

内置类型
用法

Opaque

用户定义的任意数据

kubernetes.io/service-account-token

服务账号令牌

kubernetes.io/dockercfg

~/.dockercfg 文件的序列化形式

kubernetes.io/dockerconfigjson

~/.docker/config.json 文件的序列化形式

kubernetes.io/basic-auth

用于基本身份认证的凭据

kubernetes.io/ssh-auth

用于 SSH 身份认证的凭据

kubernetes.io/tls

用于 TLS 客户端或者服务器端的数据

bootstrap.kubernetes.io/token

启动引导令牌数据

通过为 Secret 对象的 type 字段设置一个非空的字符串值,你也可以定义并使用自己 Secret 类型。如果 type 值为空字符串,则被视为 Opaque 类型。 Kubernetes 并不对类型的名称作任何限制。不过,如果你要使用内置类型之一, 则你必须满足为该类型所定义的所有要求。

TLS Secret

apiVersion: v1
kind: Secret
metadata:
  name: www-ssl # secret名称
  namespace: NAMESPACES
  annotations:
    description: www.domain.com SSL # 描述
data:
  tls.crt: >-
    LS0tLS1CRUdJTiBDRVUtLS0tLQo=	# 内容base64编码后,方法:echo -n "待编码内容" | base64
  tls.key: >-
    LS0tLS1CRUdJTSBLRVktLS0tLQo=	# 内容base64编码后,方法:echo -n "待编码内容" | base64
type: IngressTLS	# 类型

服务账号令牌Secret

apiVersion: v1
kind: Secret
metadata:
  name: secret-sa-sample
  annotations:
    kubernetes.io/service-account.name: "sa-name"
type: kubernetes.io/service-account-token
data:
  # 你可以像 Opaque Secret 一样在这里添加额外的键/值偶对
  extra: YmFyCg==

基本身份认证 Secret

apiVersion: v1
kind: Secret
metadata:
  name: secret-basic-auth
type: kubernetes.io/basic-auth
stringData:
  username: admin
  password: t0p-Secret

SSH 身份认证 Secret

apiVersion: v1
kind: Secret
metadata:
  name: secret-ssh-auth
type: kubernetes.io/ssh-auth
data:
  # 此例中的实际数据被截断
  ssh-privatekey: |
          MIIEpQIBAAKCAQEAulqb/Y ...

Secret调用

kind: Ingress
apiVersion: extensions/v1beta1
metadata:
  name: demo-web
  namespace: demo
  annotations:
    kubernetes.io/elb.id: 81ab96dd-d0b9-4897-9daa-de2b7d14bd72  # ELB ID,可以在华为云ELB中查看
    kubernetes.io/elb.ip: 122.112.x.x  # ELB 公网IP地址
    kubernetes.io/elb.port: '443'
spec:
  tls:  # SSL证书调用
    - secretName: www-ssl   # 对应# secret名称
  rules:
    - host: www.domain.com
      http:
        paths:
          - path: /
            backend:
              serviceName: pro-mml-jp-jsjxz-web-1
              servicePort: 80
            property:
              ingress.beta.kubernetes.io/url-match-mode: STARTS_WITH

Service

apiVersion: v1
kind: Service
metadata:
  name: loki
  namespace: ma
spec:
  selector:
    app: loki
  type: ClusterIP
  sessionAffinity: None
  ports:
  - name: "3100"
    port: 3100
    targetPort: 3100

Ingress

kind: Ingress
apiVersion: extensions/v1beta1
metadata:
  name: grafana
  namespace: ma
  annotations:
    kubernetes.io/elb.id: 81ab96dd-d0b9-4897-9daa-de2b7d14bd72
    kubernetes.io/elb.ip: 122.112.x.x
    kubernetes.io/elb.port: '80'
spec:
  rules:
    - host: log.domain.com
      http:
        paths:
          - path: /
            backend:
              serviceName: grafana
              servicePort: 3000
            property:
              ingress.beta.kubernetes.io/url-match-mode: STARTS_WITH

配置存活、就绪和启动探测器

  • readinessProbe:就绪探针,用于判断容器应用是否启动完成

  • livenessProbe:存活探针,用于判断容器中的应用是否正常运行

  • initialDelaySeconds:pod启动后首次进行检查的等待时间,单位“秒”

  • failureThreshold:探测失败的重试次数

  • periodSeconds:探测的间隔时间,单位“秒”

  • timeoutSeconds:探测请求等待响应的超时时间,单位“秒”

定义存活命令

periodSeconds 字段指定了 kubelet 应该每 5 秒执行一次存活探测。 initialDelaySeconds 字段告诉 kubelet 在执行第一次探测前应该等待 5 秒。 kubelet 在容器内执行命令 cat /tmp/healthy 来进行探测。 如果命令执行成功并且返回值为 0,kubelet 就会认为这个容器是健康存活的。 如果这个命令返回非 0 值,kubelet 会杀死这个容器并重新启动它。

apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-exec
spec:
  containers:
  - name: liveness
    image: k8s.gcr.io/busybox
    args:
    - /bin/sh
    - -c
    - touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
    livenessProbe:
      exec:
        command:
        - cat
        - /tmp/healthy
      initialDelaySeconds: 5
      periodSeconds: 5

定义一个存活态 HTTP 请求接口

periodSeconds 字段指定了 kubelet 每隔 3 秒执行一次存活探测。 initialDelaySeconds 字段告诉 kubelet 在执行第一次探测前应该等待 3 秒。 kubelet 会向容器内运行的服务(服务会监听 8080 端口)发送一个 HTTP GET 请求来执行探测。 如果服务器上 /healthz 路径下的处理程序返回成功代码,则 kubelet 认为容器是健康存活的。 如果处理程序返回失败代码,则 kubelet 会杀死这个容器并且重新启动它。

官方示例

apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-http
spec:
  containers:
  - name: liveness
    image: k8s.gcr.io/liveness
    args:
    - /server
    livenessProbe:
      httpGet:
        path: /healthz
        port: 8080
        httpHeaders:
        - name: Custom-Header
          value: Awesome
      initialDelaySeconds: 3
      periodSeconds: 3

示例2 periodSeconds 字段指定了 kubelet 每隔 30 秒执行一次存活探测。 initialDelaySeconds 字段告诉 kubelet 在执行第一次探测前应该等待 15 秒。 failureThreshold字段告诉kubelet可以失败几次。kubelet 会向容器内运行的服务(服务会监听 8080 端口)发送一个 HTTP GET 请求来执行探测。 如果服务器上 /ver.txt 路径下的处理程序返回成功代码,则 kubelet 认为容器是健康存活的。 如果处理程序返回失败代码,则 kubelet 会杀死这个容器并且重新启动它。

apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-http
spec:
  containers:
  - name: liveness
    image: k8s.gcr.io/liveness
    readinessProbe:
      httpGet:
        path: /ver.txt 
        port: 8080 
        scheme: HTTP
      initialDelaySeconds: 15 
      failureThreshold: 3
      periodSeconds: 30
      timeoutSeconds: 5
    livenessProbe:
      httpGet:
        path: /ver.txt 
        port: 8080
        scheme: HTTP
      initialDelaySeconds: 15 
      failureThreshold: 3
      periodSeconds: 30
      timeoutSeconds: 5

定义 TCP 的存活探测

第三种类型的存活探测是使用 TCP 套接字。 通过配置,kubelet 会尝试在指定端口和容器建立套接字链接。 如果能建立连接,这个容器就被看作是健康的,如果不能则这个容器就被看作是有问题的。

apiVersion: v1
kind: Pod
metadata:
  name: goproxy
  labels:
    app: goproxy
spec:
  containers:
  - name: goproxy
    image: k8s.gcr.io/goproxy:0.1
    ports:
    - containerPort: 8080
    readinessProbe:
      tcpSocket:
        port: 8080
      initialDelaySeconds: 5
      periodSeconds: 10
    livenessProbe:
      tcpSocket:
        port: 8080
      initialDelaySeconds: 15
      periodSeconds: 20

定义健康检查脚本

health.sh 脚本,将以下内容写入health.sh脚本,并打包进镜像内

#! /bin/bash
if [[ $(netstat -apnt | grep LISTEN | grep 9876 | wc -l) -gt 0 ]];then exit 0;else exit 1;fi

健康脚本应用示例,在deployment.yaml文件中如下区域添加以下配置

      containers:
	  ...
        image: image:latest
        imagePullPolicy: Always
        resources:
          requests:
            memory: "2048Mi"
            #cpu: "200m"
          limits:
            memory: "4096Mi"
            #cpu: "800m"
        readinessProbe:
          exec:
            command: 
            - sh
            - /tmp/health.sh	# 脚本容器中的位置
          initialDelaySeconds: 15    
          failureThreshold: 3
          periodSeconds: 10
          timeoutSeconds: 5
        livenessProbe:
          exec:
            command: 
            - sh
            - /tmp/health.sh
          initialDelaySeconds: 15    
          failureThreshold: 3
          periodSeconds: 10
          timeoutSeconds: 5        

Cronjob定时任务

apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: ossimport-1
spec:
  schedule: "0 1 * * *"
  concurrencyPolicy: Forbid # 禁用并发策略
  failedJobsHistoryLimit: 1 # 保留失败历史个数,默认1
  successfulJobsHistoryLimit: 1 # 保留成功历史个数,默认3
  jobTemplate:
    spec:
	  backoffLimit: 0  # pod失败后的重试次数,0表示失败后不重试
      template:
        spec:
          containers:
          - name: ossimport-1
            image: rsyncoss:v3
            imagePullPolicy: IfNotPresent
            volumeMounts:
              - name: appdir
                mountPath: /usr/local/ossimport-2.3.4
          restartPolicy: Never

          volumes:
            - name: appdir
              nfs:
                server: 172.25.0.1
                path: "/data/ossimport-1"

注意事项: 如果未能在调度时间内创建 CronJob,则计为错过。 例如,如果 concurrencyPolicy (并发策略)被设置为 Forbid(禁止),并且当前有一个调度仍在运行的情况下, 试图调度的 CronJob 将被计算为错过。

有时我们的定时任务脚本在完成后并不是正常退出,而是通过kill等方式强制结束时,k8s会自动重建pod试图恢复服务,如果不希望k8s恢复本次pod,就可以通过配置restartPolicy: NeverbackoffLimit: 0来防止其重建pod。

最后更新于