K8S应用笔记

K8S资源

  1. Namespace(命名空间):

    k8s通过命名空间实现资源隔离, 简称为ns

    我们在获取任何资源时,如果不明确指定命名空间那么会缺省的使用default命名空间

  2. Pod:

    K8s的最小运行单元(不太恰当的比喻,一个pod就详单与用docker启动了一个容器)

  3. Controller(控制器):

    一般来说生产环境都是通过各种Controller来管理pod的生命周期。在控制器中每一个副本都是一个pod 通过控制副本数来控制pod的创建和销毁

    常用的Controller有Deployment、DaemonSet、StatefulSet、Job、CronJob

    最常用的是 Deployment(无状态应用)

  4. replicas:副本数

    Deployment通过replicas属性来指定副本数 简称rs

  5. service: 不论单独创建的pod 还是通过deployment创建的pod 都是一个在k8s集群中的资源,他们的访问只能通过集群内ip访问,不能通过外网访问 所以需要通过service来暴露pod访问

    service常用的四种网络

    1. ClusterIP:集群内部访问,分配了一个稳定的虚拟ip。
    2. NodePort:集群外部访问,会在每个node节点上分配固定的端口(端口范围30000-32767),流量可以从外部转发到service,
    3. LoadBalancer:集群外部访问,主要在云环境中使用。他为service创建一个外部的负载均衡器,可以通过云环境的负载均衡器将流量打到service上
    4. ExternalName:通过返回一个CNAME记录,可以将服务映射到集群外部的服务。
  6. endpoints(v1 Endpoints is deprecated in v1.33+; use discovery.k8s.io/v1 EndpointSlice): 1.33以后被弃用

  7. EndpointSlice: 用于描述Service的实际访问点。它包含了提供服务的Pod的IP地址和端口信息。

    这些信息是Kubernetes实现服务发现和流量分发的关键依据

增(创建)

  1. 新增命名空间

    
    kubectl create ns ${nsName}
    
    #新建一个deployment
    #                         应用名             镜像地址                               副本数
    
  2. 创建(运行)一个pod

    kubectl run ${podName} --image=${镜像名称}:${镜像tag}
    
  3. 心中一个deployment 应用

    kubectl create deployment ${deploymentName} --image=${镜像名称}:${镜像tag} --replicas=${副本数}
    
  4. 创建一个deployment的配置文件

    kubectl create deployment nginx --image=nginx --dry-run=client -o yaml > nginx.yaml
    
    apiVersion: apps/v1
    # 资源类型
    kind: Deployment
    # 元数据,其中name是必填项
    metadata:
      creationTimestamp: null
      labels:
        app: nginx
      name: nginx
    # 规格说明
    spec:
      # 副本数
      replicas: 1
      selector:
        matchLabels:
          app: nginx
      strategy: {}
      # 定义 Pod 的模板,这是配置文件的重要部分
      template:
        # metadata 定义 Pod 的元数据,至少要定义一个 label。label 的 key 和 value 可以任意指定
        metadata:
          creationTimestamp: null
          labels:
            app: nginx
        # spec 描述 Pod 的规格,此部分定义 Pod 中每一个容器的属性,name 和 image 是必需的
        spec:
          containers:
          - image: nginx
            name: nginx
            resources: {}
    status: {}
    
  5. 创建一个service (svc)

    # 创建一个新的service, 将流量从端口5000 映射到 pod 80 端口
    kubectl create service clusterip new-nginx --tcp=5000:80 
    
    # 基于一个已存在的(名为new-nginx的)deployment 创建一个svc 网络模式为NodePort --port 是svc对外端口
    kubectl expose deployment new-nginx --type=NodePort --port=81 --target-port=80 --dry-run=client -o yaml > nginx-svc.yaml
    
    apiVersion: v1       # <<<<<<  v1 是 Service 的 apiVersion
    kind: Service        # <<<<<<  指明当前资源的类型为 Service
    metadata:
      creationTimestamp: null
      labels:
        app: new-nginx
      name: new-nginx       # <<<<<<  Service 的名字为 nginx
    spec:
      ports:
      - port: 81        # <<<<<<  将 Service 的 80 端口映射到 Pod 的 80 端口,使用 TCP 协议
        protocol: TCP
        targetPort: 80
      selector:
        app: new-nginx     # <<<<<<  selector 指明挑选那些 label 为 run: nginx 的 Pod 作为 Service 的后端
    status:
      loadBalancer: {}
    
  6. 给命名空间添加tls证书

kubectl -n <namespace> create secret tls boge-com-tls(这相当于证书的名称) --key boge.key(私钥路径) --cert boge.csr(证书路径)

  1. 通用表达式

    # 删除资源时 资源类型和名称必填
    kubectl delete ${资源类型} ${资源名称}
    

删除命名空间时,命名空间一直terminating

deleteK8sNamespace() {
    set -eo pipefail

    die() { echo "$*" 1>&2 ; exit 1; }

    need() {
            which "$1" &>/dev/null || die "Binary '$1' is missing but required"
    }

    # checking pre-reqs

    need "jq"
    need "curl"
    need "kubectl"

    PROJECT="$1"
    shift

    test -n "$PROJECT" || die "Missing arguments: kill-ns <namespace>"

    kubectl proxy &>/dev/null &
    PROXY_PID=$!
    killproxy () {
            kill $PROXY_PID
    }
    trap killproxy EXIT

    sleep 1 # give the proxy a second

    kubectl get namespace "$PROJECT" -o json | jq 'del(.spec.finalizers[] | select("kubernetes"))' | curl -s -k -H "Content-Type: application/json" -X PUT -o /dev/null --data-binary @- http://localhost:8001/api/v1/namespaces/$PROJECT/finalize && echo "Killed namespace: $PROJECT"

}
deleteK8sNamespace 要被删除的命名空间

  1. 修改deployment pod 的副本数

    kubectl scale deployment nginx --replicas=2
    
  2. 修改deployment 镜像地址

    root@k8s-192-168-0-17:/home/node1# kubectl get deployment -o wide
    NAME    READY   UP-TO-DATE   AVAILABLE   AGE   CONTAINERS   IMAGES                           SELECTOR
    nginx   2/2     2            2           11m   nginx        docker.io/library/nginx:1.25.1   app=nginx
    # ${deploymentName}值得是上面结果中NAME列 ${containerName}为CONTAINERS列的值
    kubectl set image deployments/${deploymentName} ${containerName}=${镜像地址}:${镜像版本}
    # 所以本次修改镜像的命令为
    # 原来 kubectl set image ...... --replicas (--replicas 标志在 Kubernetes v1.18.0 后已弃用)
    # 1. 先修改镜像 
    kubectl set image deployments/nginx nginx=nginx:1.21.6
    # 2. 再添加注释
    kubectl annotate deployment/nginx kubernetes.io/change-cause="image updated to 1.21.6"
    
  3. 回滚

    # 1. 先查询发布历史
    kubectl rollout history deployment ${deploymentName}
    deployment.apps/nginx 
    REVISION  CHANGE-CAUSE
    1         <none>
    2         image updated to 1.25.1
    3         image updated to 1.21.6
    # 2. 根据历史索引进行回滚
    kubectl rollout undo deployment ${deploymentName} --to-revision=2
    
  4. 修改svc的网络模式

    kubectl patch svc ${svcName} -p '{"spec":{"type":"NodePort"}}'
    
  5. 给节点打标签(label)

    #kubectl label node 10.0.1.201 apps/nginx=true
    kubectl label node ${nodeName} ${labelName}=${labelValue}
    kubectl label ${资源名称} ${nodeName} ${labelName}=${labelValue}
    
  6. 给节点删除一个标签(Label)

    kubectl label node ${nodeName} ${labelName}-
    

  1. 返回一种资源类型列表

    ## 返回一种资源类型列表,资源名称选填
    ## 除了查询命名空间意外,其他资源类型都可以添加 -n 参数指定命名空间。
    kubectl (-n ${nsName}) get ${资源类型} (${资源名称}) -o wide
    
  2. 返回多种资源类型列表

    kubectl (-n ${nsName}) get ${资源类型},${资源类型}  -o wide
    

    kubectl describe ${资源类型} ${资源名称}

  3. 进入到某个pod容器内

    kubectl -it exec ${podName} -- bash(一般用bash|sh,也可以执行其他命令)
    
  4. 查看当前某种资源的yaml

    sudo kubectl get ${资源类型} ${资源名称} -o yaml
    
  5. 查看标签(label)

    kubectl get node ${nodeName} --show-labels
    
  6. 查看集群的证书

健康检查

当容器退出时返回码非0 则认为容器发生了故障,k8s会根据restartPolicy策略来执行后续操作

restartPolicy的美剧

Always:总是重启 OnFailure:容器退出时返回码非0,则重启 Never:不重启

k8s的pod可以通过 Liveness 和 Readiness 来检查pod是否健康

  1. 通过Liveness进行健康检查

    apiVersion: v1
    kind: Pod
    metadata:
    labels:
        test: liveness
    name: liveness
    spec:
    restartPolicy: OnFailure
    containers:
    - name: liveness
        image: registry.cn-hangzhou.aliyuncs.com/acs/busybox:v1.29.2
        # 容器启动后立即创建/tmp/healthy 文件,30s后 删除
        args:
        - /bin/sh
        - -c
        - touch /tmp/healthy; sleep 30; rm -f /tmp/healthy; sleep 600
        livenessProbe:
        exec:
            command:
            - cat
            - /tmp/healthy
        # 容器启动 10 秒后开始检测
        initialDelaySeconds: 10   # 容器启动 10 秒之后开始检测
        # 每5s检查执行一次 cat /tmp/healthy
        periodSeconds: 5          # 每隔 5 秒再检测一次
    
  2. 通过Readiness进行健康检查

    我们可以通过Readiness检测来告诉K8s什么时候可以将pod加入到服务Service的负载均衡池中,对外提供服务,这个在生产场景服务发布新版本时非常重要,当我们上线的新版本发生程序错误时,Readiness会通过检测发布,从而不导入流量到pod内,将服务的故障控制在内部,在生产场景中,建议这个是必加的,Liveness不加都可以,因为有时候我们需要保留服务出错的现场来查询日志,定位问题,告之开发来修复程序。

    Liveness 检测和 Readiness 检测是两种 Health Check 机制,如果不特意配置,Kubernetes 将对两种检测采取相同的默认行为,即通过判断容器启动进程的返回值是否为零来判断检测是否成功。

    两种检测的配置方法完全一样,支持的配置参数也一样。

    不同之处在于检测失败后的行为:Liveness 检测是重启容器;Readiness 检测则是将容器设置为不可用,不接收 Service 转发的请求。

    Liveness 检测和 Readiness 检测是独立执行的,二者之间没有依赖,所以可以单独使用,也可以同时使用。

    用 Liveness 检测判断容器是否需要重启以实现自愈;用 Readiness 检测判断容器是否已经准备好对外提供服务。

    Readiness 检测的配置语法与 Liveness 检测完全一样

    apiVersion: v1
    kind: Pod
    metadata:
      labels:
        test: readiness
      name: readiness
    spec:
      restartPolicy: OnFailure
      containers:
      - name: readiness
        image: registry.cn-hangzhou.aliyuncs.com/acs/busybox:v1.29.2
        args:
        - /bin/sh
        - -c
        - touch /tmp/healthy; sleep 30; rm -f /tmp/healthy; sleep 600
        readinessProbe:    # 这里将livenessProbe换成readinessProbe即可,其它配置都一样
          exec:
            command:
            - cat
            - /tmp/healthy
          #initialDelaySeconds: 10   # 容器启动 10 秒之后开始检测
          periodSeconds: 5          # 每隔 5 秒再检测一次
        startupProbe:  # 启动探针,更灵活,完美代替initialDelaySeconds强制等待时间配置,启动时每3秒检测一次,一共检测100次
          exec:
            command:
            - cat
            - /tmp/healthy
          failureThreshold: 100
          periodSeconds: 3
          timeoutSeconds: 1
    

部署一个生产环境下的Ingress-Nginx 控制器

打开yaml文件

必看:

  1. 在大规模生产集群上,ingree-nginx 独占一台节点,他就只跑 ingree-nginx 不要再跑其他pod了

  2. kind: ConfigMap 段落的data.worker-processes = 实际服务器ingress-nginx-controller 所在的pod的那个节点的服务器的cpu核数(最好比实际核心数-1)

  3. kind: ConfigMap 段落的data.worker-cpu-affinity 目前配置是空, 留空就行

  4. kind: DaemonSet 如果是自建集群使用DaemonSet类型的控制器。 他会把容器端口映射到宿主机上这样就不用再使用NodePort映射了如果是是云上比如阿里云的ack 集群,使用Deployment类型的控制器,因为ack的pod使用的是云主机的弹性网卡他可以和你的云主机在同一个网络(网段)所以在这一段的内容中默认用了kind: DaemonSet 如果要用kind: Deployment 那么需要检查 “Deployment need” 和 “DaemonSet need"跟随的一些配置项

  5. 基于kind: DaemonSet|Deployment的resources(资源配置)如果limits分配的资源和requests分配的资源是一致的,那么这个pod在k8s集群中的优先级是最高的。当我们集群资源不够时, k8s会驱逐一些优先级低的pod。保证高优先级

  6. 如果日志报错提示 “mount: mounting rw on /proc/sys failed: Permission denied”, 那么就打开 privileged: true、procMount: Default、runAsUser: 0 这三条注释的内容,如果不报错就不用管他

  7. 给对应节点打标签

      nodeSelector:
        boge/ingress-controller-ready: "true"

打标签的方法 kubectl label node ${节点的hostname} boge/ingress-controller-ready=true
查看标签的方法 kubectl get node –show-labels
删除标签的方法 kubectl label node ${节点的hostname} boge/ingress-controller-ready-

  1. 基于ingress-nginx独立一台节点部署的情况。 给这个节点打上标签后。最好再给这个节点标记上污点
    打污点的方法是 kubectl taint nodes xx.xx.xx.xx boge/ingress-controller-ready=“true”:NoExecute
    去掉污点的方法是 kubectl taint nodes xx.xx.xx.xx boge/ingress-controller-ready:NoExecute-
    如果给节点打上了污点需要把下面这段注释打开,
    tolerations:
    - effect: NoExecute # effect: NoExecute:表示节点污点的驱逐效果,会驱逐已运行但不耐受的Pod
    key: boge/ingress-controller-ready
    operator: Equal # 要求value必须完全匹配(若为Exists则只需key存在)
    value: "true"

他的作用是Kubernetes中Pod的容忍度(Toleration)定义,用于控制Pod能否调度到带有特定污点(Taint)的节点上
所以上面这段的配置含义是

  • 允许Pod被调度到带有boge/ingress-controller-ready=true:NoExecute污点的节点上,确保它们只运行在特定节点。